7d1e3d1cf434c7f315ada03e0e7638fbdd6a9e43
[time-slider.git] / usr / share / time-slider / lib / plugin / plugin.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 pluginsmf
27
28 from time_slider import smf, autosnapsmf, util
29
30 PLUGINBASEFMRI = "svc:/application/time-slider/plugin"
31
32
33 class Plugin(Exception):
34
35     def __init__(self, instanceName, debug=False):
36         self.verbose = debug
37         util.debug("Instantiating plugin for:\t%s" % (instanceName), self.verbose)
38         self.smfInst = pluginsmf.PluginSMF(instanceName)
39         self._proc = None
40
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()
47         try:
48             statinfo = os.stat(command)
49             other_x = (statinfo.st_mode & 01)
50             if other_x == 0:
51               raise RuntimeError, 'Plugin: %s:\nConfigured trigger command is not ' \
52                                   'executable:\n%s' \
53                                   % (self.smfInst.instanceName, command)  
54         except OSError:
55             raise RuntimeError, 'Plugin: %s:\nCan not access the configured ' \
56                                 'plugin/trigger_command:\n%s' \
57                                 % (self.smfInst.instanceName, command)      
58
59
60     def execute(self, schedule, label):
61
62         triggers = self.smfInst.get_trigger_list()
63         try:
64             triggers.index("all")
65         except ValueError:
66             try:
67                 triggers.index(schedule)
68             except ValueError:
69                 return
70
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), \
75                        self.verbose)
76             return
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), \
84                        self.verbose)
85             return
86
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)
90
91         os.putenv("AUTOSNAP_FMRI", svcFmri)
92         os.putenv("AUTOSNAP_LABEL", label)
93         try:
94             os.putenv("PLUGIN_FMRI", self.smfInst.instanceName) 
95             self._proc = subprocess.Popen(cmd,
96                                           stdout=subprocess.PIPE,
97                                           stderr=subprocess.PIPE,
98                                           close_fds=True)
99         except OSError, message:
100             raise RuntimeError, "%s subprocess error:\n %s" % \
101                                 (cmd, str(message))
102             self._proc = None
103
104     def is_running(self):
105         if self._proc == None:
106             util.debug("Plugin child process is not started", self.verbose)
107             return False
108         else:
109             self._proc.poll()
110             if self._proc.returncode == None:
111                 util.debug("Plugin child process is still running",
112                            self.verbose)
113                 return True
114             else:
115                 util.debug("Plugin child process has ended", self.verbose)
116                 return False
117
118
119 class PluginManager():
120
121     def __init__(self, debug=False):
122         self.plugins = []
123         self.verbose = debug
124
125     def execute_plugins(self, schedule, label):
126         util.debug("Executing plugins for \"%s\" with label: \"%s\"" \
127                    % (schedule, label), \
128                    self.verbose)
129         for plugin in self.plugins:
130             plugin.execute(schedule, label)
131
132
133     def refresh(self):
134         self.plugins = []
135         cmd = [smf.SVCSCMD, "-H", "-o", "state,FMRI", PLUGINBASEFMRI]
136
137         p = subprocess.Popen(cmd,
138                              stdout=subprocess.PIPE,
139                              stderr=subprocess.PIPE,
140                              close_fds=True)
141         outdata,errdata = p.communicate()
142         err = p.wait()
143         if err != 0:
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()
149             state = line[0]
150             fmri = line[1]
151
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)
158                 try:
159                     plugin = Plugin(fmri, self.verbose)
160                     self.plugins.append(plugin)
161                 except RuntimeError, message:
162                     sys.stderr.write("Ignoring misconfigured plugin: %s\n" \
163                                      % (fmri))
164                     sys.stderr.write("Reason:\n%s\n" % (message))
165             else:
166                 util.debug("Found disabled plugin:\t%s" + fmri, self.verbose)
167