blob: 8c3f6c953769e987a96fcd33b7618c0d6e2692f8 [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 },
Joel Stanleya30ab9d2018-01-31 13:34:16 +103079 'state' : ['bmcstate', 'chassisstate', 'hoststate'],
80 'status' : ['bmcstate', 'chassisstate', 'hoststate'],
Norman Jamesb4914ad2015-10-26 07:14:29 -050081}
82
Andrew Jefferyde4ed022017-10-12 01:26:55 +103083def run_set_property(dbus_bus, dbus_iface, descriptor, args):
84 mainloop = gobject.MainLoop()
85
86 iface = descriptor['interface_name']
87 prop = descriptor['property']
88
89 if 'monitor' not in descriptor:
90 dbus_iface.Set(iface, prop, descriptor['value'])
91 return True
92
93 def property_listener(job, path, unit, state):
94 if descriptor['monitor'] != unit:
95 return
96
97 property_listener.success = (state == 'done')
98 mainloop.quit()
99
100 property_listener.success = True
101
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030102 if args.wait and args.verbose:
103 pid = Popen(["/bin/journalctl", "-f", "--no-pager"]).pid
104
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030105 if args.wait:
106 sig_match = dbus_bus.add_signal_receiver(property_listener, "JobRemoved")
107
108 dbus_iface.Set(iface, prop, descriptor['value'])
109
110 if args.wait:
111 mainloop.run()
112 sig_match.remove()
113
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030114 if args.wait and args.verbose:
115 # wait some time for the journal output
116 time.sleep(args.wait_tune)
117 os.kill(pid, signal.SIGTERM)
118
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030119 return property_listener.success
120
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030121def get_dbus_obj(dbus_bus, bus, obj, args):
122 if not args.wait:
123 return dbus_bus.get_object(bus, obj)
124
125 mainloop = gobject.MainLoop()
126
127 def property_listener(job, path, unit, state):
128 if 'obmc-standby.target' == unit:
129 mainloop.quit()
130
131 sig_match = dbus_bus.add_signal_receiver(property_listener, "JobRemoved")
132 try:
133 return dbus_bus.get_object(bus, obj)
134 except dbus.exceptions.DBusException as e:
135 if args.verbose:
136 pid = Popen(["/bin/journalctl", "-f", "--no-pager"]).pid
137
138 mainloop.run()
139
140 if args.verbose:
141 os.kill(pid, signal.SIGTERM)
142 finally:
143 sig_match.remove()
144
145 return dbus_bus.get_object(bus, obj)
146
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030147def run_one_command(dbus_bus, descriptor, args):
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030148 bus = descriptor['bus_name']
149 obj = descriptor['object_name']
150 iface = descriptor['interface_name']
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030151 dbus_obj = get_dbus_obj(dbus_bus, bus, obj, args)
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030152 result = None
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030153
Andrew Jeffery08e149b2017-10-12 01:37:07 +1030154 if 'property' in descriptor:
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030155 dbus_iface = dbus.Interface(dbus_obj, "org.freedesktop.DBus.Properties")
Andrew Jeffery08e149b2017-10-12 01:37:07 +1030156 if 'value' in descriptor:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030157 result = run_set_property(dbus_bus, dbus_iface, descriptor, args)
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030158 else:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030159 prop = descriptor['property']
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030160 dbus_prop = dbus_iface.Get(iface, prop)
161 print '{:<20}: {}'.format(prop, str(dbus_prop))
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030162 result = True
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030163 else:
164 dbus_iface = dbus.Interface(dbus_obj, "org.freedesktop.DBus.Properties")
165 props = dbus_iface.GetAll(iface)
166 for p in props:
Andrew Jefferybba82bd2017-10-11 22:20:30 +1030167 print "{} = {}".format(p, str(props[p]))
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030168 result = True
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030169
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030170 return result
171
172def run_all_commands(dbus_bus, recipe, args):
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030173 if isinstance(recipe, dict):
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030174 return run_one_command(dbus_bus, recipe, args)
Andrew Jeffery7b376e72017-10-11 18:12:05 +1030175
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030176 assert isinstance(recipe, list)
177 for command in recipe:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030178 descriptor = descriptors[command]
179 if not run_one_command(dbus_bus, descriptor, args):
180 print "Failed to execute command: {}".format(descriptor)
181 return False
182
183 return True
Andrew Jeffery7b376e72017-10-11 18:12:05 +1030184
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030185def gpio_set_value(gpio_name, active_low, asserted):
186 gpio_id = obmc.system.convertGpio(gpio_name)
187 gpio_value_path = "/sys/class/gpio/gpio{}/value".format(gpio_id)
188
189 with open(gpio_value_path, 'w') as gpio:
190 # Inversion behaviour needs to change with the resolution of
191 # https://github.com/openbmc/openbmc/issues/2489, where properly
192 # configuring the kernel will allow it to handle the inversion for us.
193 gpio.write(str(int(asserted ^ active_low)))
194
195 return True
196
197def gpio_deassert(gpio_name, active_low, args):
198 # Deal with silly python2 exception handling as outlined in main
199 if args.verbose:
200 return gpio_set_value(gpio_name, active_low, False)
201
202 try:
203 return gpio_set_value(gpio_name, active_low, False)
204 except IOError as e:
205 print >> sys.stderr, "Failed to access GPIO {}: {}".format(gpio_name, e.message)
206 return False
207
208def run_chassiskill(args):
209 # We shouldn't be able to invoke run_chassiskill() unless it's been
210 # explicitly added as a valid command to argparse in main()
211 assert can_chassiskill()
212
213 # Multi-dimensional fetch is now exception-safe
214 gpios = obmc_system_config.GPIO_CONFIGS['power_config']['power_up_outs']
215
216 gc = obmc_system_config.GPIO_CONFIG
217
218 for gpio in gpios:
219 function = gpio[0]
220
221 if function not in gc or 'gpio_pin' not in gc[function]:
222 print >> sys.stderr, "Missing or invalid definition for '{}' in system GPIO_CONFIG".format(function)
223 continue
224
225 name = gc[function]['gpio_pin']
226
227 # The second element of the tuples stashed in 'power_up_outs'
228 # represents the boolean condition of the statement 'active high'. To
229 # mirror the code at [1] we instead need the condition of the statement
230 # 'active low', thus we negate gpio[1].
231 #
232 # [1] https://github.com/openbmc/skeleton/blob/93b84e42834893313616f96c70743369f26a7190/op-pwrctl/power_control_obj.c#L283
233 active_low = not gpio[1]
234
235 if not gpio_deassert(name, active_low, args):
236 return False
237
238 return True
239
240def can_chassiskill():
241 gcs = obmc_system_config.GPIO_CONFIGS
242
243 if 'power_config' in gcs:
244 if 'power_up_outs' in gcs['power_config']:
245 # Just to be sure
246 return len(gcs['power_config']) > 0
247
248 return False
249
Andrew Jeffery44e6d9c2017-10-11 16:57:31 +1030250def main():
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030251 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
252
Gunnar Millsaa4c3102017-10-25 20:50:47 -0500253 # Conditionally add the `chassiskill` command based on whether the
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030254 # required GPIO configuration is present in the system description.
255 if can_chassiskill():
256 descriptors['chassiskill'] = None
257
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030258 parser = argparse.ArgumentParser()
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030259 parser.add_argument('--verbose', '-v', action='store_true',
260 help="Verbose output")
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030261 parser.add_argument('--wait', '-w', action='store_true',
262 help='Block until the state transition succeeds or fails')
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030263 parser.add_argument('--wait-tune', '-t', nargs='?', default=8, type=float,
264 # help='Seconds to wait for journal output to complete after receiving DBus signal',
265 help=argparse.SUPPRESS)
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030266 parser.add_argument('recipe', choices=sorted(descriptors.keys()))
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030267 args = parser.parse_args()
Andrew Geissler8f8cbce2017-02-20 16:10:26 -0600268
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030269 # This is a special case: directly pull the power, don't do any D-Bus
270 # related stuff
271 if args.recipe == "chassiskill":
272 return run_chassiskill(args)
273
Andrew Jefferybdb74352017-10-11 17:29:48 +1030274 dbus_bus = dbus.SystemBus()
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030275
276 # The only way to get a sensible backtrace with python 2 without stupid
277 # hoops is to let the uncaught exception handler do the work for you.
278 # Catching and binding an exception appears to overwrite the stack trace at
279 # the point of bind.
280 #
281 # So, if we're in verbose mode, don't try to catch the DBus exception. That
282 # way we can understand where it originated.
283 if args.verbose:
284 return run_all_commands(dbus_bus, descriptors[args.recipe], args)
285
286 # Otherwise, we don't care about the traceback. Just catch it and print the
287 # error message.
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030288 try:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030289 return run_all_commands(dbus_bus, descriptors[args.recipe], args)
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030290 except dbus.exceptions.DBusException as e:
291 print >> sys.stderr, "DBus error occurred: {}".format(e.get_dbus_message())
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030292 finally:
293 dbus_bus.close()
Andrew Jeffery44e6d9c2017-10-11 16:57:31 +1030294
295if __name__ == "__main__":
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030296 sys.exit(0 if main() else 1)