blob: 30db80c7d8d1af9ffd5d16ee6feee59c77bb3ca1 [file] [log] [blame]
Norman Jamesb4914ad2015-10-26 07:14:29 -05001#!/usr/bin/python
2
3import sys
Norman Jamesb4914ad2015-10-26 07:14:29 -05004import dbus
Andrew Jeffery0b6883d2017-10-11 22:08:55 +10305import argparse
Norman Jamesb4914ad2015-10-26 07:14:29 -05006
Andrew Jefferyde4ed022017-10-12 01:26:55 +10307from dbus.mainloop.glib import DBusGMainLoop
8import gobject
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +10309import os
10import signal
11import time
12from subprocess import Popen
Andrew Jefferyde4ed022017-10-12 01:26:55 +103013
Andrew Jeffery1be4fab2017-10-16 14:13:53 +103014import obmc_system_config
15import obmc.system
16
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +103017descriptors = {
Andrew Geisslercae038e2016-12-19 15:29:10 -060018 'power': {
19 'bus_name': 'org.openbmc.control.Power',
20 'object_name': '/org/openbmc/control/power0',
21 'interface_name': 'org.openbmc.control.Power'
22 },
Andrew Geissler2cbbb672017-05-17 10:52:49 -050023 'chassison': {
24 'bus_name': 'xyz.openbmc_project.State.Chassis',
25 'object_name': '/xyz/openbmc_project/state/chassis0',
26 'interface_name': 'xyz.openbmc_project.State.Chassis',
27 'property': 'RequestedPowerTransition',
Andrew Jefferyde4ed022017-10-12 01:26:55 +103028 'value': 'xyz.openbmc_project.State.Chassis.Transition.On',
29 'monitor': 'obmc-chassis-poweron@0.target',
Andrew Geissler2cbbb672017-05-17 10:52:49 -050030 },
31 'chassisoff': {
32 'bus_name': 'xyz.openbmc_project.State.Chassis',
33 'object_name': '/xyz/openbmc_project/state/chassis0',
34 'interface_name': 'xyz.openbmc_project.State.Chassis',
35 'property': 'RequestedPowerTransition',
Andrew Jefferyde4ed022017-10-12 01:26:55 +103036 'value': 'xyz.openbmc_project.State.Chassis.Transition.Off',
37 'monitor': 'obmc-chassis-hard-poweroff@0.target',
Andrew Geissler2cbbb672017-05-17 10:52:49 -050038 },
Andrew Geisslercae038e2016-12-19 15:29:10 -060039 'poweron': {
Andrew Geissler25b9a852016-12-19 16:14:09 -060040 'bus_name': 'xyz.openbmc_project.State.Host',
41 'object_name': '/xyz/openbmc_project/state/host0',
42 'interface_name': 'xyz.openbmc_project.State.Host',
43 'property': 'RequestedHostTransition',
Andrew Jefferyde4ed022017-10-12 01:26:55 +103044 'value': 'xyz.openbmc_project.State.Host.Transition.On',
45 'monitor': 'obmc-host-start@0.target',
Andrew Geisslercae038e2016-12-19 15:29:10 -060046 },
47 'poweroff': {
Andrew Geissler25b9a852016-12-19 16:14:09 -060048 'bus_name': 'xyz.openbmc_project.State.Host',
49 'object_name': '/xyz/openbmc_project/state/host0',
50 'interface_name': 'xyz.openbmc_project.State.Host',
51 'property': 'RequestedHostTransition',
Andrew Jefferyde4ed022017-10-12 01:26:55 +103052 'value': 'xyz.openbmc_project.State.Host.Transition.Off',
53 'monitor': 'obmc-host-stop@0.target',
Andrew Geisslercae038e2016-12-19 15:29:10 -060054 },
Andrew Geissler8f8cbce2017-02-20 16:10:26 -060055 'bmcstate': {
56 'bus_name': 'xyz.openbmc_project.State.BMC',
57 'object_name': '/xyz/openbmc_project/state/bmc0',
58 'interface_name': 'xyz.openbmc_project.State.BMC',
59 'property': 'CurrentBMCState',
60 },
61 'chassisstate': {
62 'bus_name': 'xyz.openbmc_project.State.Chassis',
63 'object_name': '/xyz/openbmc_project/state/chassis0',
64 'interface_name': 'xyz.openbmc_project.State.Chassis',
65 'property': 'CurrentPowerState',
66 },
67 'hoststate': {
68 'bus_name': 'xyz.openbmc_project.State.Host',
69 'object_name': '/xyz/openbmc_project/state/host0',
70 'interface_name': 'xyz.openbmc_project.State.Host',
71 'property': 'CurrentHostState',
Andrew Geisslercae038e2016-12-19 15:29:10 -060072 },
73 'bootprogress': {
George Keishing3be09952017-08-18 09:48:03 -050074 'bus_name': 'xyz.openbmc_project.State.Host',
75 'object_name': '/xyz/openbmc_project/state/host0',
76 'interface_name': 'xyz.openbmc_project.State.Boot.Progress',
77 'property': 'BootProgress',
Andrew Geisslercae038e2016-12-19 15:29:10 -060078 },
Andrew Jeffery0b6883d2017-10-11 22:08:55 +103079 'state' : ['bmcstate', 'chassisstate', 'hoststate']
Norman Jamesb4914ad2015-10-26 07:14:29 -050080}
81
Andrew Jefferyde4ed022017-10-12 01:26:55 +103082def run_set_property(dbus_bus, dbus_iface, descriptor, args):
83 mainloop = gobject.MainLoop()
84
85 iface = descriptor['interface_name']
86 prop = descriptor['property']
87
88 if 'monitor' not in descriptor:
89 dbus_iface.Set(iface, prop, descriptor['value'])
90 return True
91
92 def property_listener(job, path, unit, state):
93 if descriptor['monitor'] != unit:
94 return
95
96 property_listener.success = (state == 'done')
97 mainloop.quit()
98
99 property_listener.success = True
100
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030101 if args.wait and args.verbose:
102 pid = Popen(["/bin/journalctl", "-f", "--no-pager"]).pid
103
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030104 if args.wait:
105 sig_match = dbus_bus.add_signal_receiver(property_listener, "JobRemoved")
106
107 dbus_iface.Set(iface, prop, descriptor['value'])
108
109 if args.wait:
110 mainloop.run()
111 sig_match.remove()
112
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030113 if args.wait and args.verbose:
114 # wait some time for the journal output
115 time.sleep(args.wait_tune)
116 os.kill(pid, signal.SIGTERM)
117
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030118 return property_listener.success
119
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030120def get_dbus_obj(dbus_bus, bus, obj, args):
121 if not args.wait:
122 return dbus_bus.get_object(bus, obj)
123
124 mainloop = gobject.MainLoop()
125
126 def property_listener(job, path, unit, state):
127 if 'obmc-standby.target' == unit:
128 mainloop.quit()
129
130 sig_match = dbus_bus.add_signal_receiver(property_listener, "JobRemoved")
131 try:
132 return dbus_bus.get_object(bus, obj)
133 except dbus.exceptions.DBusException as e:
134 if args.verbose:
135 pid = Popen(["/bin/journalctl", "-f", "--no-pager"]).pid
136
137 mainloop.run()
138
139 if args.verbose:
140 os.kill(pid, signal.SIGTERM)
141 finally:
142 sig_match.remove()
143
144 return dbus_bus.get_object(bus, obj)
145
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030146def run_one_command(dbus_bus, descriptor, args):
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030147 bus = descriptor['bus_name']
148 obj = descriptor['object_name']
149 iface = descriptor['interface_name']
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030150 dbus_obj = get_dbus_obj(dbus_bus, bus, obj, args)
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030151 result = None
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030152
Andrew Jeffery08e149b2017-10-12 01:37:07 +1030153 if 'property' in descriptor:
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030154 dbus_iface = dbus.Interface(dbus_obj, "org.freedesktop.DBus.Properties")
Andrew Jeffery08e149b2017-10-12 01:37:07 +1030155 if 'value' in descriptor:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030156 result = run_set_property(dbus_bus, dbus_iface, descriptor, args)
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030157 else:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030158 prop = descriptor['property']
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030159 dbus_prop = dbus_iface.Get(iface, prop)
160 print '{:<20}: {}'.format(prop, str(dbus_prop))
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030161 result = True
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030162 else:
163 dbus_iface = dbus.Interface(dbus_obj, "org.freedesktop.DBus.Properties")
164 props = dbus_iface.GetAll(iface)
165 for p in props:
Andrew Jefferybba82bd2017-10-11 22:20:30 +1030166 print "{} = {}".format(p, str(props[p]))
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030167 result = True
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030168
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030169 return result
170
171def run_all_commands(dbus_bus, recipe, args):
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030172 if isinstance(recipe, dict):
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030173 return run_one_command(dbus_bus, recipe, args)
Andrew Jeffery7b376e72017-10-11 18:12:05 +1030174
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030175 assert isinstance(recipe, list)
176 for command in recipe:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030177 descriptor = descriptors[command]
178 if not run_one_command(dbus_bus, descriptor, args):
179 print "Failed to execute command: {}".format(descriptor)
180 return False
181
182 return True
Andrew Jeffery7b376e72017-10-11 18:12:05 +1030183
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030184def gpio_set_value(gpio_name, active_low, asserted):
185 gpio_id = obmc.system.convertGpio(gpio_name)
186 gpio_value_path = "/sys/class/gpio/gpio{}/value".format(gpio_id)
187
188 with open(gpio_value_path, 'w') as gpio:
189 # Inversion behaviour needs to change with the resolution of
190 # https://github.com/openbmc/openbmc/issues/2489, where properly
191 # configuring the kernel will allow it to handle the inversion for us.
192 gpio.write(str(int(asserted ^ active_low)))
193
194 return True
195
196def gpio_deassert(gpio_name, active_low, args):
197 # Deal with silly python2 exception handling as outlined in main
198 if args.verbose:
199 return gpio_set_value(gpio_name, active_low, False)
200
201 try:
202 return gpio_set_value(gpio_name, active_low, False)
203 except IOError as e:
204 print >> sys.stderr, "Failed to access GPIO {}: {}".format(gpio_name, e.message)
205 return False
206
207def run_chassiskill(args):
208 # We shouldn't be able to invoke run_chassiskill() unless it's been
209 # explicitly added as a valid command to argparse in main()
210 assert can_chassiskill()
211
212 # Multi-dimensional fetch is now exception-safe
213 gpios = obmc_system_config.GPIO_CONFIGS['power_config']['power_up_outs']
214
215 gc = obmc_system_config.GPIO_CONFIG
216
217 for gpio in gpios:
218 function = gpio[0]
219
220 if function not in gc or 'gpio_pin' not in gc[function]:
221 print >> sys.stderr, "Missing or invalid definition for '{}' in system GPIO_CONFIG".format(function)
222 continue
223
224 name = gc[function]['gpio_pin']
225
226 # The second element of the tuples stashed in 'power_up_outs'
227 # represents the boolean condition of the statement 'active high'. To
228 # mirror the code at [1] we instead need the condition of the statement
229 # 'active low', thus we negate gpio[1].
230 #
231 # [1] https://github.com/openbmc/skeleton/blob/93b84e42834893313616f96c70743369f26a7190/op-pwrctl/power_control_obj.c#L283
232 active_low = not gpio[1]
233
234 if not gpio_deassert(name, active_low, args):
235 return False
236
237 return True
238
239def can_chassiskill():
240 gcs = obmc_system_config.GPIO_CONFIGS
241
242 if 'power_config' in gcs:
243 if 'power_up_outs' in gcs['power_config']:
244 # Just to be sure
245 return len(gcs['power_config']) > 0
246
247 return False
248
Andrew Jeffery44e6d9c2017-10-11 16:57:31 +1030249def main():
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030250 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
251
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030252 # Conditionally add the `chassiskill` commmand based on whether the
253 # required GPIO configuration is present in the system description.
254 if can_chassiskill():
255 descriptors['chassiskill'] = None
256
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030257 parser = argparse.ArgumentParser()
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030258 parser.add_argument('--verbose', '-v', action='store_true',
259 help="Verbose output")
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030260 parser.add_argument('--wait', '-w', action='store_true',
261 help='Block until the state transition succeeds or fails')
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030262 parser.add_argument('--wait-tune', '-t', nargs='?', default=8, type=float,
263 # help='Seconds to wait for journal output to complete after receiving DBus signal',
264 help=argparse.SUPPRESS)
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030265 parser.add_argument('recipe', choices=sorted(descriptors.keys()))
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030266 args = parser.parse_args()
Andrew Geissler8f8cbce2017-02-20 16:10:26 -0600267
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030268 # This is a special case: directly pull the power, don't do any D-Bus
269 # related stuff
270 if args.recipe == "chassiskill":
271 return run_chassiskill(args)
272
Andrew Jefferybdb74352017-10-11 17:29:48 +1030273 dbus_bus = dbus.SystemBus()
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030274
275 # The only way to get a sensible backtrace with python 2 without stupid
276 # hoops is to let the uncaught exception handler do the work for you.
277 # Catching and binding an exception appears to overwrite the stack trace at
278 # the point of bind.
279 #
280 # So, if we're in verbose mode, don't try to catch the DBus exception. That
281 # way we can understand where it originated.
282 if args.verbose:
283 return run_all_commands(dbus_bus, descriptors[args.recipe], args)
284
285 # Otherwise, we don't care about the traceback. Just catch it and print the
286 # error message.
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030287 try:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030288 return run_all_commands(dbus_bus, descriptors[args.recipe], args)
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030289 except dbus.exceptions.DBusException as e:
290 print >> sys.stderr, "DBus error occurred: {}".format(e.get_dbus_message())
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030291 finally:
292 dbus_bus.close()
Andrew Jeffery44e6d9c2017-10-11 16:57:31 +1030293
294if __name__ == "__main__":
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030295 sys.exit(0 if main() else 1)