3904eed63658a711226fe5b5ccf8dce9c73f2da1
[time-slider.git] / usr / share / time-slider / lib / plugin / rsync / trigger.py
1 #!/usr/bin/python2
2 #
3 # CDDL HEADER START
4 #
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
8 #
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
13 #
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 #
20 # CDDL HEADER END
21 #
22
23 import os
24 import sys
25 import subprocess
26 import syslog
27
28 import rsyncsmf
29 from time_slider import util, smf, zfs
30
31 # Set to True if SMF property value of "plugin/command" is "true"
32 verboseprop = "plugin/verbose"
33 propbasename = "org.opensolaris:time-slider-plugin"
34
35
36 def main(argv):
37     # Check that appropriate environment variables have been
38     # provided by time-sliderd
39     #
40     # The label used for the snapshot set just taken, ie. the
41     # component proceeding the "@" in the snapshot name
42     snaplabel = os.getenv("AUTOSNAP_LABEL")
43     # The SMF fmri of the auto-snapshot instance corresponding to
44     # the snapshot set just taken.
45     snapfmri = os.getenv("AUTOSNAP_FMRI")
46     # The SMF fmri of the time-slider plugin instance associated with
47     # this command.
48     pluginfmri = os.getenv("PLUGIN_FMRI")
49
50     if pluginfmri == None:
51         sys.stderr.write("No time-slider plugin SMF instance FMRI defined. " \
52                          "This plugin does not support command line "
53                          "execution. Exiting\n")
54         sys.exit(-1)
55     syslog.openlog(pluginfmri, 0, syslog.LOG_DAEMON)
56
57     cmd = [smf.SVCPROPCMD, "-p", verboseprop, pluginfmri]
58     outdata,errdata = util.run_command(cmd)
59     if outdata.rstrip() == "true":
60         verbose = True
61     else:
62         verbose = False
63
64     if snaplabel == None:
65         log_error(syslog.LOG_ERR,
66                   "No snapshot label provided. Exiting")
67         sys.exit(-1)
68     if snapfmri == None:
69         log_error(syslog.LOG_ERR,
70                   "No auto-snapshot SMF instance FMRI provided. Exiting")
71         sys.exit(-1)
72
73     schedule = snapfmri.rsplit(':', 1)[1]
74     plugininstance = pluginfmri.rsplit(':', 1)[1]
75
76     # The user property/tag used when tagging and holding zfs datasets
77     propname = "%s:%s" % (propbasename, plugininstance)
78
79     # Identifying snapshots is a 3 stage process.
80     #
81     # First: identify all snapshots matching the AUTOSNAP_LABEL
82     # value passed in by the time-slider daemon.
83     #
84         # Second: Filter out snapshots of volumes, since rsync can only
85     # back up filesystems.
86     #
87     # Third: we need to filter the results and ensure that the
88     # filesystem corresponding to each snapshot is actually
89     # tagged with the property (com.sun:auto-snapshot<:schedule>)
90     #
91     # This is necessary to avoid confusion whereby a snapshot might
92     # have been sent|received from one zpool to another on the same
93     # system. The received snapshot will show up in the first pass
94     # results but is not actually part of the auto-snapshot set
95     # created by time-slider. It also avoids incorrectly placing
96     # zfs holds on the imported snapshots.
97
98
99     datasets = zfs.Datasets()
100     candidates = datasets.list_snapshots(snaplabel)
101     autosnapsets = datasets.list_auto_snapshot_sets(schedule)
102     autosnapfs = [name for [name,mount] in datasets.list_filesystems() \
103                    if name in autosnapsets]
104     snappeddatasets = []
105     snapnames = [name for [name,ctime] in candidates \
106                  if name.split('@',1)[0] in autosnapfs]
107
108     # Mark the snapshots with a user property. Doing this instead of
109     # placing a physical hold on the snapshot allows time-slider to
110     # expire the snapshots naturally or destroy them if a zpool fills
111     # up and triggers a remedial cleanup.
112     # It also prevents the possiblity of leaving snapshots lying around
113     # indefinitely on the system if the plugin SMF instance becomes 
114     # disabled or having to release a pile of held snapshots.
115     # We set org.opensolaris:time-slider-plugin:<instance> to "pending",
116     # indicate
117     snapshots = []
118     for snap in snapnames:
119         snapshot = zfs.Snapshot(snap)
120         fs = zfs.Filesystem(snapshot.fsname)
121         if fs.get_user_property(rsyncsmf.RSYNCFSTAG) == "true":
122             if fs.is_mounted() == True:
123                 snapshot.set_user_property(propname, "pending")
124                 util.debug("Marking %s as pending rsync" % (snap), verbose)
125             else:
126                 util.debug("Ignoring snapshot of unmounted fileystem: %s" \
127                            % (snap), verbose)
128
129 def maintenance(svcfmri):
130     log_error(syslog.LOG_ERR,
131               "Placing plugin into maintenance state")
132     cmd = [smf.SVCADMCMD, "mark", "maintenance", svcfmri]
133     subprocess.Popen(cmd, close_fds=True)
134
135 def log_error(loglevel, message):
136     syslog.syslog(loglevel, message + '\n')
137     sys.stderr.write(message + '\n')
138