blob: 0aa40a9df3a86cd2154e2610ca976301eb379a55 [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
Matt Spinlerc4390de2018-08-08 11:16:40 -05009import json
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +103010import os
11import signal
12import time
Adriana Kobylak7e480382018-09-24 11:01:42 -050013from glob import glob
14from os.path import join
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +103015from subprocess import Popen
Andrew Jefferyde4ed022017-10-12 01:26:55 +103016
Andrew Jeffery1be4fab2017-10-16 14:13:53 +103017import obmc_system_config
Andrew Jeffery1be4fab2017-10-16 14:13:53 +103018
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +103019descriptors = {
Andrew Geisslercae038e2016-12-19 15:29:10 -060020 'power': {
21 'bus_name': 'org.openbmc.control.Power',
22 'object_name': '/org/openbmc/control/power0',
23 'interface_name': 'org.openbmc.control.Power'
24 },
Andrew Geissler2cbbb672017-05-17 10:52:49 -050025 'chassison': {
26 'bus_name': 'xyz.openbmc_project.State.Chassis',
27 'object_name': '/xyz/openbmc_project/state/chassis0',
28 'interface_name': 'xyz.openbmc_project.State.Chassis',
29 'property': 'RequestedPowerTransition',
Andrew Jefferyde4ed022017-10-12 01:26:55 +103030 'value': 'xyz.openbmc_project.State.Chassis.Transition.On',
31 'monitor': 'obmc-chassis-poweron@0.target',
Andrew Geissler2cbbb672017-05-17 10:52:49 -050032 },
33 'chassisoff': {
34 'bus_name': 'xyz.openbmc_project.State.Chassis',
35 'object_name': '/xyz/openbmc_project/state/chassis0',
36 'interface_name': 'xyz.openbmc_project.State.Chassis',
37 'property': 'RequestedPowerTransition',
Andrew Jefferyde4ed022017-10-12 01:26:55 +103038 'value': 'xyz.openbmc_project.State.Chassis.Transition.Off',
39 'monitor': 'obmc-chassis-hard-poweroff@0.target',
Andrew Geissler2cbbb672017-05-17 10:52:49 -050040 },
Andrew Geisslercae038e2016-12-19 15:29:10 -060041 'poweron': {
Andrew Geissler25b9a852016-12-19 16:14:09 -060042 'bus_name': 'xyz.openbmc_project.State.Host',
43 'object_name': '/xyz/openbmc_project/state/host0',
44 'interface_name': 'xyz.openbmc_project.State.Host',
45 'property': 'RequestedHostTransition',
Andrew Jefferyde4ed022017-10-12 01:26:55 +103046 'value': 'xyz.openbmc_project.State.Host.Transition.On',
47 'monitor': 'obmc-host-start@0.target',
Andrew Geisslercae038e2016-12-19 15:29:10 -060048 },
49 'poweroff': {
Andrew Geissler25b9a852016-12-19 16:14:09 -060050 'bus_name': 'xyz.openbmc_project.State.Host',
51 'object_name': '/xyz/openbmc_project/state/host0',
52 'interface_name': 'xyz.openbmc_project.State.Host',
53 'property': 'RequestedHostTransition',
Andrew Jefferyde4ed022017-10-12 01:26:55 +103054 'value': 'xyz.openbmc_project.State.Host.Transition.Off',
55 'monitor': 'obmc-host-stop@0.target',
Andrew Geisslercae038e2016-12-19 15:29:10 -060056 },
Andrew Geissler8f8cbce2017-02-20 16:10:26 -060057 'bmcstate': {
58 'bus_name': 'xyz.openbmc_project.State.BMC',
59 'object_name': '/xyz/openbmc_project/state/bmc0',
60 'interface_name': 'xyz.openbmc_project.State.BMC',
61 'property': 'CurrentBMCState',
62 },
63 'chassisstate': {
64 'bus_name': 'xyz.openbmc_project.State.Chassis',
65 'object_name': '/xyz/openbmc_project/state/chassis0',
66 'interface_name': 'xyz.openbmc_project.State.Chassis',
67 'property': 'CurrentPowerState',
68 },
69 'hoststate': {
70 'bus_name': 'xyz.openbmc_project.State.Host',
71 'object_name': '/xyz/openbmc_project/state/host0',
72 'interface_name': 'xyz.openbmc_project.State.Host',
73 'property': 'CurrentHostState',
Andrew Geisslercae038e2016-12-19 15:29:10 -060074 },
75 'bootprogress': {
George Keishing3be09952017-08-18 09:48:03 -050076 'bus_name': 'xyz.openbmc_project.State.Host',
77 'object_name': '/xyz/openbmc_project/state/host0',
78 'interface_name': 'xyz.openbmc_project.State.Boot.Progress',
79 'property': 'BootProgress',
Andrew Geisslercae038e2016-12-19 15:29:10 -060080 },
Joel Stanleya30ab9d2018-01-31 13:34:16 +103081 'state' : ['bmcstate', 'chassisstate', 'hoststate'],
82 'status' : ['bmcstate', 'chassisstate', 'hoststate'],
Norman Jamesb4914ad2015-10-26 07:14:29 -050083}
84
Matt Spinlerc4390de2018-08-08 11:16:40 -050085GPIO_DEFS_FILE = '/etc/default/obmc/gpio/gpio_defs.json'
86
Adriana Kobylak7e480382018-09-24 11:01:42 -050087
88def find_gpio_base(path="/sys/class/gpio/"):
89 pattern = "gpiochip*"
90 for gc in glob(join(path, pattern)):
91 with open(join(gc, "label")) as f:
92 label = f.readline().strip()
93 if label == "1e780000.gpio":
94 with open(join(gc, "base")) as f:
95 return int(f.readline().strip())
96 # trigger a file not found exception
97 open(join(path, "gpiochip"))
98
99
100GPIO_BASE = find_gpio_base()
101
102
103def convertGpio(name):
104 offset = int(''.join(list(filter(str.isdigit, name))))
105 port = list(filter(str.isalpha, name.upper()))
106 a = ord(port[-1]) - ord('A')
107 if len(port) > 1:
108 a += 26
109 base = a * 8 + GPIO_BASE
110 return base + offset
111
112
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030113def run_set_property(dbus_bus, dbus_iface, descriptor, args):
114 mainloop = gobject.MainLoop()
115
116 iface = descriptor['interface_name']
117 prop = descriptor['property']
118
119 if 'monitor' not in descriptor:
120 dbus_iface.Set(iface, prop, descriptor['value'])
121 return True
122
123 def property_listener(job, path, unit, state):
124 if descriptor['monitor'] != unit:
125 return
126
127 property_listener.success = (state == 'done')
128 mainloop.quit()
129
130 property_listener.success = True
131
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030132 if args.wait and args.verbose:
133 pid = Popen(["/bin/journalctl", "-f", "--no-pager"]).pid
134
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030135 if args.wait:
136 sig_match = dbus_bus.add_signal_receiver(property_listener, "JobRemoved")
137
138 dbus_iface.Set(iface, prop, descriptor['value'])
139
140 if args.wait:
141 mainloop.run()
142 sig_match.remove()
143
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030144 if args.wait and args.verbose:
145 # wait some time for the journal output
146 time.sleep(args.wait_tune)
147 os.kill(pid, signal.SIGTERM)
148
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030149 return property_listener.success
150
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030151def get_dbus_obj(dbus_bus, bus, obj, args):
152 if not args.wait:
153 return dbus_bus.get_object(bus, obj)
154
155 mainloop = gobject.MainLoop()
156
157 def property_listener(job, path, unit, state):
158 if 'obmc-standby.target' == unit:
159 mainloop.quit()
160
161 sig_match = dbus_bus.add_signal_receiver(property_listener, "JobRemoved")
162 try:
163 return dbus_bus.get_object(bus, obj)
164 except dbus.exceptions.DBusException as e:
165 if args.verbose:
166 pid = Popen(["/bin/journalctl", "-f", "--no-pager"]).pid
167
168 mainloop.run()
169
170 if args.verbose:
171 os.kill(pid, signal.SIGTERM)
172 finally:
173 sig_match.remove()
174
175 return dbus_bus.get_object(bus, obj)
176
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030177def run_one_command(dbus_bus, descriptor, args):
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030178 bus = descriptor['bus_name']
179 obj = descriptor['object_name']
180 iface = descriptor['interface_name']
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030181 dbus_obj = get_dbus_obj(dbus_bus, bus, obj, args)
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030182 result = None
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030183
Andrew Jeffery08e149b2017-10-12 01:37:07 +1030184 if 'property' in descriptor:
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030185 dbus_iface = dbus.Interface(dbus_obj, "org.freedesktop.DBus.Properties")
Andrew Jeffery08e149b2017-10-12 01:37:07 +1030186 if 'value' in descriptor:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030187 result = run_set_property(dbus_bus, dbus_iface, descriptor, args)
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030188 else:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030189 prop = descriptor['property']
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030190 dbus_prop = dbus_iface.Get(iface, prop)
191 print '{:<20}: {}'.format(prop, str(dbus_prop))
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030192 result = True
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030193 else:
194 dbus_iface = dbus.Interface(dbus_obj, "org.freedesktop.DBus.Properties")
195 props = dbus_iface.GetAll(iface)
196 for p in props:
Andrew Jefferybba82bd2017-10-11 22:20:30 +1030197 print "{} = {}".format(p, str(props[p]))
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030198 result = True
Andrew Jefferye4dcaef2017-10-11 18:08:01 +1030199
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030200 return result
201
202def run_all_commands(dbus_bus, recipe, args):
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030203 if isinstance(recipe, dict):
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030204 return run_one_command(dbus_bus, recipe, args)
Andrew Jeffery7b376e72017-10-11 18:12:05 +1030205
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030206 assert isinstance(recipe, list)
207 for command in recipe:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030208 descriptor = descriptors[command]
209 if not run_one_command(dbus_bus, descriptor, args):
210 print "Failed to execute command: {}".format(descriptor)
211 return False
212
213 return True
Andrew Jeffery7b376e72017-10-11 18:12:05 +1030214
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030215def gpio_set_value(gpio_name, active_low, asserted):
Adriana Kobylak7e480382018-09-24 11:01:42 -0500216 gpio_id = convertGpio(gpio_name)
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030217 gpio_value_path = "/sys/class/gpio/gpio{}/value".format(gpio_id)
218
219 with open(gpio_value_path, 'w') as gpio:
220 # Inversion behaviour needs to change with the resolution of
221 # https://github.com/openbmc/openbmc/issues/2489, where properly
222 # configuring the kernel will allow it to handle the inversion for us.
223 gpio.write(str(int(asserted ^ active_low)))
224
225 return True
226
227def gpio_deassert(gpio_name, active_low, args):
228 # Deal with silly python2 exception handling as outlined in main
229 if args.verbose:
230 return gpio_set_value(gpio_name, active_low, False)
231
232 try:
233 return gpio_set_value(gpio_name, active_low, False)
234 except IOError as e:
235 print >> sys.stderr, "Failed to access GPIO {}: {}".format(gpio_name, e.message)
236 return False
237
238def run_chassiskill(args):
239 # We shouldn't be able to invoke run_chassiskill() unless it's been
240 # explicitly added as a valid command to argparse in main()
241 assert can_chassiskill()
242
Matt Spinlerc4390de2018-08-08 11:16:40 -0500243 data = {}
244 with open(GPIO_DEFS_FILE, 'r') as json_input:
245 data = json.load(json_input)
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030246
Matt Spinlerc4390de2018-08-08 11:16:40 -0500247 gpios = data['gpio_configs']['power_config']['power_up_outs']
248 defs = data['gpio_definitions']
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030249
250 for gpio in gpios:
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030251
Matt Spinlerc4390de2018-08-08 11:16:40 -0500252 definition = filter(lambda g: g['name'] == gpio['name'], defs)
253 if len(definition) == 0:
254 print >> sys.stderr, "Missing definition for GPIO {}".format(gpio['name'])
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030255 continue
256
Matt Spinlerc4390de2018-08-08 11:16:40 -0500257 pin = str(definition[0]['pin'])
258 active_low = not gpio['polarity']
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030259
Matt Spinlerc4390de2018-08-08 11:16:40 -0500260 if not gpio_deassert(pin, active_low, args):
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030261 return False
262
263 return True
264
265def can_chassiskill():
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030266
Matt Spinlerc4390de2018-08-08 11:16:40 -0500267 try:
268 with open(GPIO_DEFS_FILE, 'r') as json_input:
269 data = json.load(json_input)
270 gpios = data['gpio_configs']['power_config']['power_up_outs']
271 return len(gpios) > 0
272 except:
273 pass
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030274
275 return False
276
Andrew Jeffery44e6d9c2017-10-11 16:57:31 +1030277def main():
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030278 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
279
Gunnar Millsaa4c3102017-10-25 20:50:47 -0500280 # Conditionally add the `chassiskill` command based on whether the
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030281 # required GPIO configuration is present in the system description.
282 if can_chassiskill():
283 descriptors['chassiskill'] = None
284
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030285 parser = argparse.ArgumentParser()
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030286 parser.add_argument('--verbose', '-v', action='store_true',
287 help="Verbose output")
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030288 parser.add_argument('--wait', '-w', action='store_true',
289 help='Block until the state transition succeeds or fails')
Andrew Jefferyb43f7ed2017-10-12 01:33:59 +1030290 parser.add_argument('--wait-tune', '-t', nargs='?', default=8, type=float,
291 # help='Seconds to wait for journal output to complete after receiving DBus signal',
292 help=argparse.SUPPRESS)
Andrew Jefferyc6b5deb2017-10-11 22:18:27 +1030293 parser.add_argument('recipe', choices=sorted(descriptors.keys()))
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030294 args = parser.parse_args()
Andrew Geissler8f8cbce2017-02-20 16:10:26 -0600295
Andrew Jeffery1be4fab2017-10-16 14:13:53 +1030296 # This is a special case: directly pull the power, don't do any D-Bus
297 # related stuff
298 if args.recipe == "chassiskill":
299 return run_chassiskill(args)
300
Andrew Jefferybdb74352017-10-11 17:29:48 +1030301 dbus_bus = dbus.SystemBus()
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030302
303 # The only way to get a sensible backtrace with python 2 without stupid
304 # hoops is to let the uncaught exception handler do the work for you.
305 # Catching and binding an exception appears to overwrite the stack trace at
306 # the point of bind.
307 #
308 # So, if we're in verbose mode, don't try to catch the DBus exception. That
309 # way we can understand where it originated.
310 if args.verbose:
311 return run_all_commands(dbus_bus, descriptors[args.recipe], args)
312
313 # Otherwise, we don't care about the traceback. Just catch it and print the
314 # error message.
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030315 try:
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030316 return run_all_commands(dbus_bus, descriptors[args.recipe], args)
Andrew Jefferycf6a7842017-10-12 14:47:43 +1030317 except dbus.exceptions.DBusException as e:
318 print >> sys.stderr, "DBus error occurred: {}".format(e.get_dbus_message())
Andrew Jeffery0b6883d2017-10-11 22:08:55 +1030319 finally:
320 dbus_bus.close()
Andrew Jeffery44e6d9c2017-10-11 16:57:31 +1030321
322if __name__ == "__main__":
Andrew Jefferyde4ed022017-10-12 01:26:55 +1030323 sys.exit(0 if main() else 1)