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.
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.
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]
26 from . import pluginsmf
28 from time_slider import smf, autosnapsmf, util
30 PLUGINBASEFMRI = "svc:/application/time-slider/plugin"
33 class Plugin(Exception):
35 def __init__(self, instanceName, debug=False):
37 util.debug("Instantiating plugin for:\t%s" % (instanceName), self.verbose)
38 self.smfInst = pluginsmf.PluginSMF(instanceName)
41 # Note that the associated plugin service's start method checks
42 # that the command is defined and executable. But SMF doesn't
43 # bother to do this for offline services until all dependencies
44 # (ie. time-slider) are brought online.
45 # So we also check the permissions here.
46 command = self.smfInst.get_trigger_command()
48 statinfo = os.stat(command)
49 other_x = (statinfo.st_mode & 0o1)
51 raise RuntimeError('Plugin: %s:\nConfigured trigger command is not ' \
53 % (self.smfInst.instanceName, command))
55 raise RuntimeError('Plugin: %s:\nCan not access the configured ' \
56 'plugin/trigger_command:\n%s' \
57 % (self.smfInst.instanceName, command))
60 def execute(self, schedule, label):
62 triggers = self.smfInst.get_trigger_list()
67 triggers.index(schedule)
71 # Skip if already running
72 if self.is_running() == True:
73 util.debug("Plugin: %s is already running. Skipping execution" \
74 % (self.smfInst.instanceName), \
77 # Skip if plugin FMRI has been disabled or placed into maintenance
78 cmd = [smf.SVCSCMD, "-H", "-o", "state", self.smfInst.instanceName]
79 outdata,errdata = util.run_command(cmd)
80 state = outdata.strip()
81 if state == "disabled" or state == "maintenance":
82 util.debug("Plugin: %s is in %s state. Skipping execution" \
83 % (self.smfInst.instanceName, state), \
87 cmd = self.smfInst.get_trigger_command()
88 util.debug("Executing plugin command: %s" % str(cmd), self.verbose)
89 svcFmri = "%s:%s" % (autosnapsmf.BASESVC, schedule)
91 os.putenv("AUTOSNAP_FMRI", svcFmri)
92 os.putenv("AUTOSNAP_LABEL", label)
94 os.putenv("PLUGIN_FMRI", self.smfInst.instanceName)
95 self._proc = subprocess.Popen(cmd,
96 stdout=subprocess.PIPE,
97 stderr=subprocess.PIPE,
99 except OSError as message:
100 raise RuntimeError("%s subprocess error:\n %s" % \
104 def is_running(self):
105 if self._proc == None:
106 util.debug("Plugin child process is not started", self.verbose)
110 if self._proc.returncode == None:
111 util.debug("Plugin child process is still running",
115 util.debug("Plugin child process has ended", self.verbose)
119 class PluginManager():
121 def __init__(self, debug=False):
125 def execute_plugins(self, schedule, label):
126 util.debug("Executing plugins for \"%s\" with label: \"%s\"" \
127 % (schedule, label), \
129 for plugin in self.plugins:
130 plugin.execute(schedule, label)
135 cmd = [smf.SVCSCMD, "-H", "-o", "state,FMRI", PLUGINBASEFMRI]
137 p = subprocess.Popen(cmd,
138 stdout=subprocess.PIPE,
139 stderr=subprocess.PIPE,
141 outdata,errdata = p.communicate()
144 self._refreshLock.release()
145 raise RuntimeError('%s failed with exit code %d\n%s' % \
146 (str(cmd), err, errdata))
147 for line in outdata.rstrip().split('\n'):
148 line = line.rstrip().split()
152 # Note that the plugins, being dependent on the time-slider service
153 # themselves will typically be in an offline state when enabled. They will
154 # transition to an "online" state once time-slider itself comes
155 # "online" to satisfy it's dependency
156 if state == "online" or state == "offline" or state == "degraded":
157 util.debug("Found enabled plugin:\t%s" % (fmri), self.verbose)
159 plugin = Plugin(fmri, self.verbose)
160 self.plugins.append(plugin)
161 except RuntimeError as message:
162 sys.stderr.write("Ignoring misconfigured plugin: %s\n" \
164 sys.stderr.write("Reason:\n%s\n" % (message))
166 util.debug("Found disabled plugin:\t%s" + fmri, self.verbose)