23 # |
23 # |
24 # Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. |
24 # Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. |
25 # |
25 # |
26 |
26 |
27 import pkg.smf as smf |
27 import pkg.smf as smf |
|
28 import pkg.actions |
28 import os |
29 import os |
29 |
30 |
30 import pkg.misc |
31 import pkg.misc |
31 |
32 |
32 from pkg.client.debugvalues import DebugValues |
33 from pkg.client.debugvalues import DebugValues |
33 from pkg.client.imagetypes import IMG_USER, IMG_ENTIRE |
34 from pkg.client.imagetypes import IMG_USER, IMG_ENTIRE |
34 |
35 |
35 class GenericActuator(object): |
36 |
|
37 class Actuator(object): |
36 """Actuators are action attributes that cause side effects |
38 """Actuators are action attributes that cause side effects |
37 on live images when those actions are updated, installed |
39 on live images when those actions are updated, installed |
38 or removed. Since no side effects are caused when the |
40 or removed. Since no side effects are caused when the |
39 affected image isn't the current root image, the OS may |
41 affected image isn't the current root image, the OS may |
40 need to cause the equivalent effect during boot. |
42 need to cause the equivalent effect during boot. |
41 """ |
43 This is Solaris specific for now. """ |
42 |
|
43 actuator_attrs = set() |
|
44 |
|
45 def __init__(self): |
|
46 self.install = {} |
|
47 self.removal = {} |
|
48 self.update = {} |
|
49 |
|
50 def __nonzero__(self): |
|
51 return bool(self.install or self.removal or self.update) |
|
52 |
|
53 def scan_install(self, attrs): |
|
54 self.__scan(self.install, attrs) |
|
55 |
|
56 def scan_removal(self, attrs): |
|
57 self.__scan(self.removal, attrs) |
|
58 |
|
59 def scan_update(self, attrs): |
|
60 self.__scan(self.update, attrs) |
|
61 |
|
62 def __scan(self, dictionary, attrs): |
|
63 for a in set(attrs.keys()) & self.actuator_attrs: |
|
64 values = attrs[a] |
|
65 |
|
66 if not isinstance(values, list): |
|
67 values = [values] |
|
68 |
|
69 dictionary.setdefault(a, set()).update(values) |
|
70 |
|
71 def reboot_needed(self): |
|
72 return False |
|
73 |
|
74 def exec_prep(self, image): |
|
75 pass |
|
76 |
|
77 def exec_pre_actuators(self, image): |
|
78 pass |
|
79 |
|
80 def exec_post_actuators(self, image): |
|
81 pass |
|
82 |
|
83 def exec_fail_actuators(self, image): |
|
84 pass |
|
85 |
|
86 def __str__(self): |
|
87 return "Removals: %s\nInstalls: %s\nUpdates: %s\n" % \ |
|
88 (self.removal, self.install, self.update) |
|
89 |
|
90 |
|
91 class Actuator(GenericActuator): |
|
92 """Solaris specific Actuator implementation...""" |
|
93 |
44 |
94 actuator_attrs = set([ |
45 actuator_attrs = set([ |
95 "reboot-needed", # have to reboot to update this file |
46 "reboot-needed", # have to reboot to update this file |
|
47 "release-note", # conditionally include this file |
|
48 # in release notes |
96 "refresh_fmri", # refresh this service on any change |
49 "refresh_fmri", # refresh this service on any change |
97 "restart_fmri", # restart this service on any change |
50 "restart_fmri", # restart this service on any change |
98 "suspend_fmri", # suspend this service during update |
51 "suspend_fmri", # suspend this service during update |
99 "disable_fmri" # disable this service prior to removal |
52 "disable_fmri" # disable this service prior to removal |
100 ]) |
53 ]) |
102 __state__desc = { |
55 __state__desc = { |
103 "install": { |
56 "install": { |
104 "disable_fmri": set(), |
57 "disable_fmri": set(), |
105 "reboot-needed": set(), |
58 "reboot-needed": set(), |
106 "refresh_fmri": set(), |
59 "refresh_fmri": set(), |
|
60 "release-note": [(pkg.actions.generic.NSG, pkg.fmri.PkgFmri)], |
107 "restart_fmri": set(), |
61 "restart_fmri": set(), |
108 "suspend_fmri": set(), |
62 "suspend_fmri": set(), |
109 }, |
63 }, |
110 "removal": { |
64 "removal": { |
111 "disable_fmri": set(), |
65 "disable_fmri": set(), |
112 "reboot-needed": set(), |
66 "reboot-needed": set(), |
113 "refresh_fmri": set(), |
67 "refresh_fmri": set(), |
|
68 "release-note": [(pkg.actions.generic.NSG, pkg.fmri.PkgFmri)], |
114 "restart_fmri": set(), |
69 "restart_fmri": set(), |
115 "suspend_fmri": set(), |
70 "suspend_fmri": set(), |
116 }, |
71 }, |
117 "update": { |
72 "update": { |
118 "disable_fmri": set(), |
73 "disable_fmri": set(), |
119 "reboot-needed": set(), |
74 "reboot-needed": set(), |
120 "refresh_fmri": set(), |
75 "refresh_fmri": set(), |
|
76 "release-note": [(pkg.actions.generic.NSG, pkg.fmri.PkgFmri)], |
121 "restart_fmri": set(), |
77 "restart_fmri": set(), |
122 "suspend_fmri": set(), |
78 "suspend_fmri": set(), |
123 }, |
79 }, |
124 } |
80 } |
125 |
81 |
126 def __init__(self): |
82 def __init__(self): |
127 GenericActuator.__init__(self) |
83 self.install = {} |
|
84 self.removal = {} |
|
85 self.update = {} |
128 self.suspend_fmris = None |
86 self.suspend_fmris = None |
129 self.tmp_suspend_fmris = None |
87 self.tmp_suspend_fmris = None |
130 self.do_nothing = True |
88 self.do_nothing = True |
131 self.cmd_path = "" |
89 self.cmd_path = "" |
132 |
90 |
161 return rv |
119 return rv |
162 |
120 |
163 def __bool__(self): |
121 def __bool__(self): |
164 return self.install or self.removal or self.update |
122 return self.install or self.removal or self.update |
165 |
123 |
|
124 def __nonzero__(self): |
|
125 return bool(self.install or self.removal or self.update) |
|
126 |
|
127 # scan_* functions take ActionPlan arguments (see imageplan.py) |
|
128 def scan_install(self, ap): |
|
129 self.__scan(self.install, ap.dst, ap.p.destination_fmri) |
|
130 |
|
131 def scan_removal(self, ap): |
|
132 self.__scan(self.removal, ap.src, ap.p.origin_fmri) |
|
133 |
|
134 def scan_update(self, ap): |
|
135 if ap.src: |
|
136 self.__scan(self.update, ap.src, ap.p.destination_fmri) |
|
137 self.__scan(self.update, ap.dst, ap.p.destination_fmri) |
|
138 |
|
139 def __scan(self, dictionary, act, fmri): |
|
140 attrs = act.attrs |
|
141 for a in set(attrs.keys()) & self.actuator_attrs: |
|
142 if a != "release-note": |
|
143 values = attrs[a] |
|
144 if not isinstance(values, list): |
|
145 values = [values] |
|
146 dictionary.setdefault(a, set()).update(values) |
|
147 else: |
|
148 if act.name == "file": # ignore for non-files |
|
149 dictionary.setdefault(a, list()).append( |
|
150 (act, fmri)) |
|
151 |
166 def get_list(self): |
152 def get_list(self): |
167 """Returns a list of actuator value pairs, suitable for printing""" |
153 """Returns a list of actuator value pairs, suitable for printing""" |
168 def check_val(dfmri): |
154 def check_val(dfmri): |
169 # For actuators which are a single, global function that |
155 # For actuators which are a single, global function that |
170 # needs to get executed, simply print true. |
156 # needs to get executed, simply print true. |
171 if callable(dfmri): |
157 if callable(dfmri) or isinstance(dfmri, list): |
172 return [ "true" ] |
158 return [ "true" ] |
173 else: |
159 else: |
174 return dfmri |
160 return dfmri |
175 |
161 |
176 merge = {} |
162 merge = {} |
186 return [(fmri, smf) |
172 return [(fmri, smf) |
187 for fmri in merge |
173 for fmri in merge |
188 for smf in merge[fmri] |
174 for smf in merge[fmri] |
189 ] |
175 ] |
190 |
176 |
|
177 def get_release_note_info(self): |
|
178 """Returns a list of tuples of possible release notes""" |
|
179 return self.update.get("release-note", []) + \ |
|
180 self.install.get("release-note", []) |
|
181 |
191 def get_services_list(self): |
182 def get_services_list(self): |
192 """Returns a list of services that would be restarted""" |
183 """Returns a list of services that would be restarted""" |
193 return [(fmri, smf) for fmri, smf in self.get_list() |
184 return [(fmri, smf) for fmri, smf in self.get_list() |
194 if smf not in ["true", "false"]] |
185 if smf not in ["true", "false"]] |
195 |
186 |
208 environment.""" |
199 environment.""" |
209 |
200 |
210 return bool("true" in self.update.get("reboot-needed", [])) or \ |
201 return bool("true" in self.update.get("reboot-needed", [])) or \ |
211 bool("true" in self.removal.get("reboot-needed", [])) |
202 bool("true" in self.removal.get("reboot-needed", [])) |
212 |
203 |
213 def exec_prep(self, image): |
204 def exec_prep(self, image): |
214 if not image.is_liveroot(): |
205 if not image.is_liveroot(): |
215 # we're doing off-line pkg ops; we need |
206 # we're doing off-line pkg ops; we need |
216 # to support self-assembly milestone |
207 # to support self-assembly milestone |
217 # so create the necessary marker file |
208 # so create the necessary marker file |
218 |
209 |