blob: 6e0e9c3b3ed0be1b546603725514fbe37f79a6f7 [file] [log] [blame]
Michael Walsh70369fd2016-11-22 11:25:57 -06001#!/usr/bin/env python
2
3r"""
4This module contains functions having to do with machine state: get_state,
5check_state, wait_state, etc.
6
7The 'State' is a composite of many pieces of data. Therefore, the functions
8in this module define state as an ordered dictionary. Here is an example of
9some test output showing machine state:
10
Michael Walsh341c21e2017-01-17 16:25:20 -060011default_state:
Michael Walsh65b12542017-02-03 15:34:38 -060012 default_state[chassis]: On
Michael Walsh01975fa2017-08-20 20:51:36 -050013 default_state[boot_progress]: OSStart
Michael Walsh56749222017-09-29 15:26:07 -050014 default_state[operating_system]: BootComplete
Michael Walsh65b12542017-02-03 15:34:38 -060015 default_state[host]: Running
Michael Walsh341c21e2017-01-17 16:25:20 -060016 default_state[os_ping]: 1
17 default_state[os_login]: 1
18 default_state[os_run_cmd]: 1
Michael Walsh70369fd2016-11-22 11:25:57 -060019
20Different users may very well have different needs when inquiring about
Michael Walsh8fae6ea2017-02-20 16:14:44 -060021state. Support for new pieces of state information may be added to this
22module as needed.
Michael Walsh70369fd2016-11-22 11:25:57 -060023
24By using the wait_state function, a caller can start a boot and then wait for
25a precisely defined state to indicate that the boot has succeeded. If
26the boot fails, they can see exactly why by looking at the current state as
27compared with the expected state.
28"""
29
30import gen_print as gp
Michael Walsh70369fd2016-11-22 11:25:57 -060031import gen_valid as gv
Michael Walsh16cbb7f2017-02-02 15:54:16 -060032import gen_robot_utils as gru
Michael Walsh8fae6ea2017-02-20 16:14:44 -060033import gen_cmd as gc
Michael Walsh6a9bd142018-07-24 16:10:29 -050034import bmc_ssh_utils as bsu
Michael Walsh70369fd2016-11-22 11:25:57 -060035
Michael Walsh70369fd2016-11-22 11:25:57 -060036from robot.libraries.BuiltIn import BuiltIn
Michael Walsh341c21e2017-01-17 16:25:20 -060037from robot.utils import DotDict
Michael Walsh70369fd2016-11-22 11:25:57 -060038
39import re
Michael Walsh341c21e2017-01-17 16:25:20 -060040import os
Michael Walsh56749222017-09-29 15:26:07 -050041import sys
42import imp
43
Michael Walsh70369fd2016-11-22 11:25:57 -060044
Michael Walsh5a5868a2018-10-31 15:20:04 -050045# NOTE: Avoid importing utils.robot because utils.robot imports state.py
46# (indirectly) which will cause failures.
47gru.my_import_resource("rest_client.robot")
Michael Walsh70369fd2016-11-22 11:25:57 -060048
Michael Walsh56749222017-09-29 15:26:07 -050049base_path = os.path.dirname(os.path.dirname(
50 imp.find_module("gen_robot_print")[1])) + os.sep
51sys.path.append(base_path + "data/")
Michael Walsh56749222017-09-29 15:26:07 -050052
Michael Walsh940d6912017-10-27 12:32:33 -050053# Previously, I had this coded:
54# import variables as var
55# However, we ran into a problem where a robot program did this...
56# Variables ../../lib/ras/variables.py
57# Prior to doing this...
58# Library ../lib/state.py
59
60# This caused the wrong variables.py file to be selected. Attempts to fix this
61# have failed so far. For the moment, we will hard-code the value we need from
62# the file.
63
64SYSTEM_STATE_URI = "/xyz/openbmc_project/state/"
Michael Walsh56749222017-09-29 15:26:07 -050065
Michael Walsh8fae6ea2017-02-20 16:14:44 -060066# The BMC code has recently been changed as far as what states are defined and
67# what the state values can be. This module now has a means of processing both
68# the old style state (i.e. OBMC_STATES_VERSION = 0) and the new style (i.e.
Michael Walsh16cbb7f2017-02-02 15:54:16 -060069# OBMC_STATES_VERSION = 1).
Michael Walsh341c21e2017-01-17 16:25:20 -060070# The caller can set environment variable OBMC_STATES_VERSION to dictate
71# whether we're processing old or new style states. If OBMC_STATES_VERSION is
Michael Walsh8fae6ea2017-02-20 16:14:44 -060072# not set it will default to 1.
Michael Walsh341c21e2017-01-17 16:25:20 -060073
Michael Walsh619aa332017-04-12 15:56:51 -050074# As of the present moment, OBMC_STATES_VERSION of 0 is for cold that is so old
75# that it is no longer worthwhile to maintain. The OBMC_STATES_VERSION 0 code
76# is being removed but the OBMC_STATES_VERSION value will stay for now in the
77# event that it is needed in the future.
78
Michael Walsh8fae6ea2017-02-20 16:14:44 -060079OBMC_STATES_VERSION = int(os.environ.get('OBMC_STATES_VERSION', 1))
Michael Walsh341c21e2017-01-17 16:25:20 -060080
Michael Sheposda40c1d2020-12-01 22:30:00 -060081redfish_support_trans_state = int(os.environ.get('REDFISH_SUPPORT_TRANS_STATE', 0)) or \
82 int(BuiltIn().get_variable_value("${REDFISH_SUPPORT_TRANS_STATE}", default=0))
83
George Keishing1e2fbee2021-03-19 11:19:29 -050084platform_arch_type = BuiltIn().get_variable_value("${PLATFORM_ARCH_TYPE}", default="power")
85
Michael Walsh8fae6ea2017-02-20 16:14:44 -060086# valid_os_req_states and default_os_req_states are used by the os_get_state
87# function.
88# valid_os_req_states is a list of state information supported by the
89# get_os_state function.
90valid_os_req_states = ['os_ping',
91 'os_login',
92 'os_run_cmd']
Michael Sheposbdd1dce2020-12-10 11:51:58 -060093
Michael Walsh8fae6ea2017-02-20 16:14:44 -060094# When a user calls get_os_state w/o specifying req_states,
95# default_os_req_states is used as its value.
96default_os_req_states = ['os_ping',
97 'os_login',
98 'os_run_cmd']
99
100# Presently, some BMCs appear to not keep time very well. This environment
101# variable directs the get_state function to use either the BMC's epoch time
102# or the local epoch time.
103USE_BMC_EPOCH_TIME = int(os.environ.get('USE_BMC_EPOCH_TIME', 0))
Michael Walsh341c21e2017-01-17 16:25:20 -0600104
Michael Walsh619aa332017-04-12 15:56:51 -0500105# Useful state constant definition(s).
Michael Sheposda40c1d2020-12-01 22:30:00 -0600106if not redfish_support_trans_state:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600107 # When a user calls get_state w/o specifying req_states, default_req_states
108 # is used as its value.
109 default_req_states = ['rest',
110 'chassis',
111 'bmc',
112 'boot_progress',
113 'operating_system',
114 'host',
115 'os_ping',
116 'os_login',
117 'os_run_cmd']
118
119 # valid_req_states is a list of sub states supported by the get_state function.
120 # valid_req_states, default_req_states and master_os_up_match are used by the
121 # get_state function.
122
123 valid_req_states = ['ping',
124 'packet_loss',
125 'uptime',
126 'epoch_seconds',
127 'elapsed_boot_time',
128 'rest',
129 'chassis',
130 'requested_chassis',
131 'bmc',
132 'requested_bmc',
133 'boot_progress',
134 'operating_system',
135 'host',
136 'requested_host',
137 'attempts_left',
138 'os_ping',
139 'os_login',
140 'os_run_cmd']
141
Michael Sheposda40c1d2020-12-01 22:30:00 -0600142 # default_state is an initial value which may be of use to callers.
143 default_state = DotDict([('rest', '1'),
144 ('chassis', 'On'),
145 ('bmc', 'Ready'),
146 ('boot_progress', 'OSStart'),
147 ('operating_system', 'BootComplete'),
148 ('host', 'Running'),
149 ('os_ping', '1'),
150 ('os_login', '1'),
151 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500152
Michael Sheposda40c1d2020-12-01 22:30:00 -0600153 # A match state for checking that the system is at "standby".
154 standby_match_state = DotDict([('rest', '^1$'),
155 ('chassis', '^Off$'),
156 ('bmc', '^Ready$'),
157 ('boot_progress', '^Off|Unspecified$'),
158 ('operating_system', '^Inactive$'),
159 ('host', '^Off$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500160
Michael Sheposda40c1d2020-12-01 22:30:00 -0600161 # A match state for checking that the system is at "os running".
162 os_running_match_state = DotDict([('chassis', '^On$'),
163 ('bmc', '^Ready$'),
164 ('boot_progress',
165 'FW Progress, Starting OS|OSStart'),
166 ('operating_system', 'BootComplete'),
167 ('host', '^Running$'),
168 ('os_ping', '^1$'),
169 ('os_login', '^1$'),
170 ('os_run_cmd', '^1$')])
171
172 # A master dictionary to determine whether the os may be up.
173 master_os_up_match = DotDict([('chassis', '^On$'),
Michael Walsh7dc885b2018-03-14 17:51:59 -0500174 ('bmc', '^Ready$'),
175 ('boot_progress',
176 'FW Progress, Starting OS|OSStart'),
177 ('operating_system', 'BootComplete'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600178 ('host', '^Running|Quiesced$')])
Michael Walsh7dc885b2018-03-14 17:51:59 -0500179
Michael Sheposda40c1d2020-12-01 22:30:00 -0600180 invalid_state_match = DotDict([('rest', '^$'),
181 ('chassis', '^$'),
182 ('bmc', '^$'),
183 ('boot_progress', '^$'),
184 ('operating_system', '^$'),
185 ('host', '^$')])
186else:
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600187 # When a user calls get_state w/o specifying req_states, default_req_states
188 # is used as its value.
189 default_req_states = ['redfish',
190 'chassis',
191 'bmc',
192 'boot_progress',
193 'host',
194 'os_ping',
195 'os_login',
196 'os_run_cmd']
197
198 # valid_req_states is a list of sub states supported by the get_state function.
199 # valid_req_states, default_req_states and master_os_up_match are used by the
200 # get_state function.
201
202 valid_req_states = ['ping',
203 'packet_loss',
204 'uptime',
205 'epoch_seconds',
206 'elapsed_boot_time',
207 'redfish',
208 'chassis',
209 'requested_chassis',
210 'bmc',
211 'requested_bmc',
212 'boot_progress',
213 'host',
214 'requested_host',
215 'attempts_left',
216 'os_ping',
217 'os_login',
218 'os_run_cmd']
219
Michael Sheposda40c1d2020-12-01 22:30:00 -0600220 # default_state is an initial value which may be of use to callers.
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600221 default_state = DotDict([('redfish', '1'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600222 ('chassis', 'On'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600223 ('bmc', 'Enabled'),
224 ('boot_progress', 'SystemHardwareInitializationComplete|OSRunning'),
225 ('host', 'Enabled'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600226 ('os_ping', '1'),
227 ('os_login', '1'),
228 ('os_run_cmd', '1')])
Michael Walsh619aa332017-04-12 15:56:51 -0500229
Michael Sheposda40c1d2020-12-01 22:30:00 -0600230 # A match state for checking that the system is at "standby".
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600231 standby_match_state = DotDict([('redfish', '^1$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600232 ('chassis', '^Off$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600233 ('bmc', '^Enabled$'),
234 ('boot_progress', '^None$'),
235 ('host', '^Disabled$')])
Michael Sheposda40c1d2020-12-01 22:30:00 -0600236
237 # A match state for checking that the system is at "os running".
238 os_running_match_state = DotDict([('chassis', '^On$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600239 ('bmc', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600240 ('boot_progress',
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600241 'SystemHardwareInitializationComplete|OSRunning'),
242 ('host', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600243 ('os_ping', '^1$'),
244 ('os_login', '^1$'),
245 ('os_run_cmd', '^1$')])
246
247 # A master dictionary to determine whether the os may be up.
248 master_os_up_match = DotDict([('chassis', '^On$'),
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600249 ('bmc', '^Enabled$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600250 ('boot_progress',
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600251 'SystemHardwareInitializationComplete|OSRunning'),
252 ('host', '^Enabled$')])
Michael Sheposda40c1d2020-12-01 22:30:00 -0600253
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600254 invalid_state_match = DotDict([('redfish', '^$'),
Michael Sheposda40c1d2020-12-01 22:30:00 -0600255 ('chassis', '^$'),
256 ('bmc', '^$'),
257 ('boot_progress', '^$'),
258 ('host', '^$')])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500259
George Keishing1e2fbee2021-03-19 11:19:29 -0500260# Filter the states based on platform type.
261if platform_arch_type == "x86":
262 default_req_states.remove("operating_system")
263 default_req_states.remove("boot_progress")
264 valid_req_states.remove("operating_system")
265 valid_req_states.remove("boot_progress")
266 del default_state["operating_system"]
267 del default_state["boot_progress"]
268 del standby_match_state["operating_system"]
269 del standby_match_state["boot_progress"]
270 del os_running_match_state["operating_system"]
271 del os_running_match_state["boot_progress"]
272 del master_os_up_match["operating_system"]
273 del master_os_up_match["boot_progress"]
274 del invalid_state_match["operating_system"]
275 del invalid_state_match["boot_progress"]
276
Michael Walsh341c21e2017-01-17 16:25:20 -0600277
George Keishing36efbc02018-12-12 10:18:23 -0600278def return_state_constant(state_name='default_state'):
Michael Walsh619aa332017-04-12 15:56:51 -0500279 r"""
Michael Walsh7dc885b2018-03-14 17:51:59 -0500280 Return the named state dictionary constant.
Michael Walsh619aa332017-04-12 15:56:51 -0500281 """
282
George Keishing36efbc02018-12-12 10:18:23 -0600283 return eval(state_name)
Michael Walsh619aa332017-04-12 15:56:51 -0500284
Michael Walsh619aa332017-04-12 15:56:51 -0500285
Michael Walsh70369fd2016-11-22 11:25:57 -0600286def anchor_state(state):
Michael Walsh70369fd2016-11-22 11:25:57 -0600287 r"""
288 Add regular expression anchors ("^" and "$") to the beginning and end of
289 each item in the state dictionary passed in. Return the resulting
290 dictionary.
291
Michael Walsh2a0df682019-09-27 17:19:27 -0500292 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600293 state A dictionary such as the one returned by the get_state()
294 function.
295 """
296
Michael Walsh2ce067a2017-02-27 14:24:07 -0600297 anchored_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500298 for key in anchored_state.keys():
Michael Walsh70369fd2016-11-22 11:25:57 -0600299 anchored_state[key] = "^" + str(anchored_state[key]) + "$"
300
301 return anchored_state
302
Michael Walsh70369fd2016-11-22 11:25:57 -0600303
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600304def strip_anchor_state(state):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600305 r"""
306 Strip regular expression anchors ("^" and "$") from the beginning and end
307 of each item in the state dictionary passed in. Return the resulting
308 dictionary.
309
Michael Walsh2a0df682019-09-27 17:19:27 -0500310 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600311 state A dictionary such as the one returned by the get_state()
312 function.
313 """
314
Michael Walsh2ce067a2017-02-27 14:24:07 -0600315 stripped_state = state.copy()
Michael Walsh2a0df682019-09-27 17:19:27 -0500316 for key in stripped_state.keys():
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600317 stripped_state[key] = stripped_state[key].strip("^$")
318
319 return stripped_state
320
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600321
Michael Walsh2a0df682019-09-27 17:19:27 -0500322def expressions_key():
323 r"""
324 Return expressions key constant.
325 """
326 return '<expressions>'
327
328
Michael Walsh70369fd2016-11-22 11:25:57 -0600329def compare_states(state,
Michael Walsh45ca6e42017-09-14 17:29:12 -0500330 match_state,
331 match_type='and'):
Michael Walsh70369fd2016-11-22 11:25:57 -0600332 r"""
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600333 Compare 2 state dictionaries. Return True if they match and False if they
Michael Walsh70369fd2016-11-22 11:25:57 -0600334 don't. Note that the match_state dictionary does not need to have an entry
335 corresponding to each entry in the state dictionary. But for each entry
336 that it does have, the corresponding state entry will be checked for a
337 match.
338
Michael Walsh2a0df682019-09-27 17:19:27 -0500339 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600340 state A state dictionary such as the one returned by the
341 get_state function.
342 match_state A dictionary whose key/value pairs are "state field"/
343 "state value". The state value is interpreted as a
344 regular expression. Every value in this dictionary is
Michael Walsh45ca6e42017-09-14 17:29:12 -0500345 considered. When match_type is 'and', if each and every
346 comparison matches, the two dictionaries are considered to
347 be matching. If match_type is 'or', if any two of the
348 elements compared match, the two dictionaries are
349 considered to be matching.
Michael Walsh2a0df682019-09-27 17:19:27 -0500350
Michael Walsh7dc885b2018-03-14 17:51:59 -0500351 This value may also be any string accepted by
Michael Walsh2a0df682019-09-27 17:19:27 -0500352 return_state_constant (e.g. "standby_match_state"). In
353 such a case this function will call return_state_constant
354 to convert it to a proper dictionary as described above.
355
356 Finally, one special value is accepted for the key field:
357 expression_key(). If such an entry exists, its value is
358 taken to be a list of expressions to be evaluated. These
359 expressions may reference state dictionary entries by
360 simply coding them in standard python syntax (e.g.
361 state['key1']). What follows is an example expression:
362
363 "int(float(state['uptime'])) < int(state['elapsed_boot_time'])"
364
365 In this example, if the state dictionary's 'uptime' entry
366 is less than its 'elapsed_boot_time' entry, it would
367 qualify as a match.
Michael Walsh45ca6e42017-09-14 17:29:12 -0500368 match_type This may be 'and' or 'or'.
Michael Walsh70369fd2016-11-22 11:25:57 -0600369 """
370
Michael Walsh2a0df682019-09-27 17:19:27 -0500371 error_message = gv.valid_value(match_type, valid_values=['and', 'or'])
Michael Walsh45ca6e42017-09-14 17:29:12 -0500372 if error_message != "":
373 BuiltIn().fail(gp.sprint_error(error_message))
374
George Keishing36efbc02018-12-12 10:18:23 -0600375 try:
Michael Walsh7dc885b2018-03-14 17:51:59 -0500376 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600377 except TypeError:
378 pass
Michael Walsh7dc885b2018-03-14 17:51:59 -0500379
Michael Walsh45ca6e42017-09-14 17:29:12 -0500380 default_match = (match_type == 'and')
Michael Walsh70369fd2016-11-22 11:25:57 -0600381 for key, match_state_value in match_state.items():
Michael Walsh97df71c2017-03-27 14:33:24 -0500382 # Blank match_state_value means "don't care".
383 if match_state_value == "":
384 continue
Michael Walsh2a0df682019-09-27 17:19:27 -0500385 if key == expressions_key():
386 for expr in match_state_value:
387 # Use python interpreter to evaluate the expression.
388 match = eval(expr)
389 if match != default_match:
390 return match
391 else:
392 try:
393 match = (re.match(match_state_value, str(state[key])) is not None)
394 except KeyError:
395 match = False
396 if match != default_match:
397 return match
Michael Walsh45ca6e42017-09-14 17:29:12 -0500398
399 return default_match
Michael Walsh70369fd2016-11-22 11:25:57 -0600400
Michael Walsh70369fd2016-11-22 11:25:57 -0600401
Michael Walsh70369fd2016-11-22 11:25:57 -0600402def get_os_state(os_host="",
403 os_username="",
404 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600405 req_states=default_os_req_states,
406 os_up=True,
Michael Walsh70369fd2016-11-22 11:25:57 -0600407 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600408 r"""
409 Get component states for the operating system such as ping, login,
410 etc, put them into a dictionary and return them to the caller.
411
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600412 Note that all substate values are strings.
413
Michael Walsh2a0df682019-09-27 17:19:27 -0500414 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600415 os_host The DNS name or IP address of the operating system.
416 This defaults to global ${OS_HOST}.
417 os_username The username to be used to login to the OS.
418 This defaults to global ${OS_USERNAME}.
419 os_password The password to be used to login to the OS.
420 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600421 req_states This is a list of states whose values are being requested by
422 the caller.
423 os_up If the caller knows that the os can't possibly be up, it can
424 improve performance by passing os_up=False. This function
425 will then simply return default values for all requested os
426 sub states.
Michael Walsh70369fd2016-11-22 11:25:57 -0600427 quiet Indicates whether status details (e.g. curl commands) should
428 be written to the console.
429 Defaults to either global value of ${QUIET} or to 1.
430 """
431
Michael Walsh619aa332017-04-12 15:56:51 -0500432 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600433
434 # Set parm defaults where necessary and validate all parms.
435 if os_host == "":
436 os_host = BuiltIn().get_variable_value("${OS_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500437 error_message = gv.valid_value(os_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600438 if error_message != "":
439 BuiltIn().fail(gp.sprint_error(error_message))
440
441 if os_username == "":
442 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500443 error_message = gv.valid_value(os_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600444 if error_message != "":
445 BuiltIn().fail(gp.sprint_error(error_message))
446
447 if os_password == "":
448 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500449 error_message = gv.valid_value(os_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600450 if error_message != "":
451 BuiltIn().fail(gp.sprint_error(error_message))
452
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600453 invalid_req_states = [sub_state for sub_state in req_states
454 if sub_state not in valid_os_req_states]
455 if len(invalid_req_states) > 0:
456 error_message = "The following req_states are not supported:\n" +\
457 gp.sprint_var(invalid_req_states)
458 BuiltIn().fail(gp.sprint_error(error_message))
Michael Walsh70369fd2016-11-22 11:25:57 -0600459
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600460 # Initialize all substate values supported by this function.
461 os_ping = 0
462 os_login = 0
463 os_run_cmd = 0
Michael Walsh70369fd2016-11-22 11:25:57 -0600464
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600465 if os_up:
466 if 'os_ping' in req_states:
467 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600468 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + os_host,
469 print_output=0, show_err=0,
470 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600471 if rc == 0:
472 os_ping = 1
Michael Walsh70369fd2016-11-22 11:25:57 -0600473
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600474 # Programming note: All attributes which do not require an ssh login
475 # should have been processed by this point.
476 master_req_login = ['os_login', 'os_run_cmd']
477 req_login = [sub_state for sub_state in req_states if sub_state in
478 master_req_login]
Michael Walsh97df71c2017-03-27 14:33:24 -0500479 must_login = (len(req_login) > 0)
Michael Walsh70369fd2016-11-22 11:25:57 -0600480
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600481 if must_login:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500482 output, stderr, rc = bsu.os_execute_command("uptime", quiet=quiet,
Michael Walsh7fc33972018-08-07 14:55:03 -0500483 ignore_err=1,
Michael Shepos3390c852021-02-11 00:12:31 -0600484 time_out=20,
485 os_host=os_host,
486 os_username=os_username,
487 os_password=os_password)
Michael Walsh6a9bd142018-07-24 16:10:29 -0500488 if rc == 0:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600489 os_login = 1
Michael Walsh6a9bd142018-07-24 16:10:29 -0500490 os_run_cmd = 1
Michael Walsh3eb50022017-03-21 11:27:30 -0500491 else:
Michael Walsh6a9bd142018-07-24 16:10:29 -0500492 gp.dprint_vars(output, stderr)
493 gp.dprint_vars(rc, 1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600494
495 os_state = DotDict()
496 for sub_state in req_states:
497 cmd_buf = "os_state['" + sub_state + "'] = str(" + sub_state + ")"
498 exec(cmd_buf)
Michael Walsh70369fd2016-11-22 11:25:57 -0600499
500 return os_state
501
Michael Walsh70369fd2016-11-22 11:25:57 -0600502
Michael Walsh70369fd2016-11-22 11:25:57 -0600503def get_state(openbmc_host="",
504 openbmc_username="",
505 openbmc_password="",
506 os_host="",
507 os_username="",
508 os_password="",
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600509 req_states=default_req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600510 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600511 r"""
Michael Walsh619aa332017-04-12 15:56:51 -0500512 Get component states such as chassis state, bmc state, etc, put them into a
Michael Walsh70369fd2016-11-22 11:25:57 -0600513 dictionary and return them to the caller.
514
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600515 Note that all substate values are strings.
516
Michael Walsh2a0df682019-09-27 17:19:27 -0500517 Note: If elapsed_boot_time is included in req_states, it is the caller's
518 duty to call set_start_boot_seconds() in order to set global
519 start_boot_seconds. elapsed_boot_time is the current time minus
520 start_boot_seconds.
521
522 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600523 openbmc_host The DNS name or IP address of the BMC.
524 This defaults to global ${OPENBMC_HOST}.
525 openbmc_username The username to be used to login to the BMC.
526 This defaults to global ${OPENBMC_USERNAME}.
527 openbmc_password The password to be used to login to the BMC.
528 This defaults to global ${OPENBMC_PASSWORD}.
529 os_host The DNS name or IP address of the operating system.
530 This defaults to global ${OS_HOST}.
531 os_username The username to be used to login to the OS.
532 This defaults to global ${OS_USERNAME}.
533 os_password The password to be used to login to the OS.
534 This defaults to global ${OS_PASSWORD}.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600535 req_states This is a list of states whose values are being requested
536 by the caller.
Michael Walsh70369fd2016-11-22 11:25:57 -0600537 quiet Indicates whether status details (e.g. curl commands)
538 should be written to the console.
539 Defaults to either global value of ${QUIET} or to 1.
540 """
541
Michael Walsh619aa332017-04-12 15:56:51 -0500542 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600543
544 # Set parm defaults where necessary and validate all parms.
545 if openbmc_host == "":
546 openbmc_host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500547 error_message = gv.valid_value(openbmc_host, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600548 if error_message != "":
549 BuiltIn().fail(gp.sprint_error(error_message))
550
551 if openbmc_username == "":
552 openbmc_username = BuiltIn().get_variable_value("${OPENBMC_USERNAME}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500553 error_message = gv.valid_value(openbmc_username, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600554 if error_message != "":
555 BuiltIn().fail(gp.sprint_error(error_message))
556
557 if openbmc_password == "":
558 openbmc_password = BuiltIn().get_variable_value("${OPENBMC_PASSWORD}")
Michael Walsh2a0df682019-09-27 17:19:27 -0500559 error_message = gv.valid_value(openbmc_password, invalid_values=[None, ""])
Michael Walsh70369fd2016-11-22 11:25:57 -0600560 if error_message != "":
561 BuiltIn().fail(gp.sprint_error(error_message))
562
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600563 # NOTE: OS parms are optional.
Michael Walsh70369fd2016-11-22 11:25:57 -0600564 if os_host == "":
565 os_host = BuiltIn().get_variable_value("${OS_HOST}")
566 if os_host is None:
567 os_host = ""
568
569 if os_username is "":
570 os_username = BuiltIn().get_variable_value("${OS_USERNAME}")
571 if os_username is None:
572 os_username = ""
573
574 if os_password is "":
575 os_password = BuiltIn().get_variable_value("${OS_PASSWORD}")
576 if os_password is None:
577 os_password = ""
578
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600579 invalid_req_states = [sub_state for sub_state in req_states
580 if sub_state not in valid_req_states]
581 if len(invalid_req_states) > 0:
582 error_message = "The following req_states are not supported:\n" +\
583 gp.sprint_var(invalid_req_states)
584 BuiltIn().fail(gp.sprint_error(error_message))
585
586 # Initialize all substate values supported by this function.
587 ping = 0
588 packet_loss = ''
589 uptime = ''
590 epoch_seconds = ''
Michael Walsh2a0df682019-09-27 17:19:27 -0500591 elapsed_boot_time = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500592 rest = ''
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600593 redfish = ''
Michael Walsh2b269de2017-10-09 11:18:11 -0500594 chassis = ''
595 requested_chassis = ''
596 bmc = ''
597 requested_bmc = ''
George Keishing18f15dd2021-03-09 11:17:35 -0600598 # BootProgress state will get populated when state logic enumerates the
599 # state URI. This is to prevent state dictionary boot_progress value
600 # getting empty when the BootProgress is NOT found, making it optional.
601 boot_progress = 'NA'
Michael Walsh2b269de2017-10-09 11:18:11 -0500602 operating_system = ''
603 host = ''
604 requested_host = ''
605 attempts_left = ''
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600606
Michael Walsh70369fd2016-11-22 11:25:57 -0600607 # Get the component states.
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600608 if 'ping' in req_states:
609 # See if the OS pings.
George Keishing36efbc02018-12-12 10:18:23 -0600610 rc, out_buf = gc.shell_cmd("ping -c 1 -w 2 " + openbmc_host,
611 print_output=0, show_err=0,
612 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600613 if rc == 0:
614 ping = 1
615
616 if 'packet_loss' in req_states:
617 # See if the OS pings.
618 cmd_buf = "ping -c 5 -w 5 " + openbmc_host +\
619 " | egrep 'packet loss' | sed -re 's/.* ([0-9]+)%.*/\\1/g'"
George Keishing36efbc02018-12-12 10:18:23 -0600620 rc, out_buf = gc.shell_cmd(cmd_buf,
621 print_output=0, show_err=0,
622 ignore_err=1)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600623 if rc == 0:
624 packet_loss = out_buf.rstrip("\n")
625
Michael Walshe53e47a2017-06-30 17:03:24 -0500626 if 'uptime' in req_states:
Michael Walshfa765932017-10-13 14:07:22 -0500627 # Sometimes reading uptime results in a blank value. Call with
628 # wait_until_keyword_succeeds to ensure a non-blank value is obtained.
629 remote_cmd_buf = "read uptime filler 2>/dev/null < /proc/uptime" +\
630 " && [ ! -z \"${uptime}\" ] && echo ${uptime}"
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500631 cmd_buf = ["BMC Execute Command",
Michael Walsh888d85a2019-04-18 11:03:28 -0500632 re.sub('\\$', '\\$', remote_cmd_buf), 'quiet=1',
George Keishing4361d8d2021-03-24 02:59:52 -0500633 'test_mode=0', 'time_out=5']
Michael Walsh2a0df682019-09-27 17:19:27 -0500634 gp.qprint_issuing(cmd_buf, 0)
635 gp.qprint_issuing(remote_cmd_buf, 0)
Michael Walshfa765932017-10-13 14:07:22 -0500636 try:
637 stdout, stderr, rc =\
George Keishing4361d8d2021-03-24 02:59:52 -0500638 BuiltIn().wait_until_keyword_succeeds("10 sec", "5 sec",
Michael Walshfa765932017-10-13 14:07:22 -0500639 *cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500640 if rc == 0 and stderr == "":
641 uptime = stdout
Michael Walshfa765932017-10-13 14:07:22 -0500642 except AssertionError as my_assertion_error:
643 pass
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600644
Michael Walsh2a0df682019-09-27 17:19:27 -0500645 if 'epoch_seconds' in req_states or 'elapsed_boot_time' in req_states:
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600646 date_cmd_buf = "date -u +%s"
647 if USE_BMC_EPOCH_TIME:
Michael Walshe53e47a2017-06-30 17:03:24 -0500648 cmd_buf = ["BMC Execute Command", date_cmd_buf, 'quiet=${1}']
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600649 if not quiet:
Michael Walshedb5c942019-03-28 12:40:50 -0500650 gp.print_issuing(cmd_buf)
Michael Walsh97df71c2017-03-27 14:33:24 -0500651 status, ret_values = \
652 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
653 if status == "PASS":
654 stdout, stderr, rc = ret_values
655 if rc == 0 and stderr == "":
656 epoch_seconds = stdout.rstrip("\n")
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600657 else:
658 shell_rc, out_buf = gc.cmd_fnc_u(date_cmd_buf,
Michael Walshfa765932017-10-13 14:07:22 -0500659 quiet=quiet,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600660 print_output=0)
661 if shell_rc == 0:
662 epoch_seconds = out_buf.rstrip("\n")
663
Michael Walsh2a0df682019-09-27 17:19:27 -0500664 if 'elapsed_boot_time' in req_states:
665 global start_boot_seconds
666 elapsed_boot_time = int(epoch_seconds) - start_boot_seconds
667
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600668 if not redfish_support_trans_state:
669 master_req_rest = ['rest', 'host', 'requested_host', 'operating_system',
670 'attempts_left', 'boot_progress', 'chassis',
671 'requested_chassis' 'bmc' 'requested_bmc']
Michael Walsh56749222017-09-29 15:26:07 -0500672
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600673 req_rest = [sub_state for sub_state in req_states if sub_state in
674 master_req_rest]
675 need_rest = (len(req_rest) > 0)
676 state = DotDict()
677 if need_rest:
678 cmd_buf = ["Read Properties", SYSTEM_STATE_URI + "enumerate",
679 "quiet=${" + str(quiet) + "}", "timeout=30"]
680 gp.dprint_issuing(cmd_buf)
681 status, ret_values = \
682 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
683 if status == "PASS":
684 state['rest'] = '1'
685 else:
686 state['rest'] = '0'
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600687
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600688 if int(state['rest']):
689 for url_path in ret_values:
George Keishing3adda952021-02-11 13:23:51 -0600690 # Skip conflicting "CurrentHostState" URL from the enum
691 # /xyz/openbmc_project/state/hypervisor0
692 if "hypervisor0" in url_path:
693 continue
694
Konstantin Aladyshev8c9cf9c2021-03-21 23:33:40 +0300695 if platform_arch_type == "x86":
696 # Skip conflicting "CurrentPowerState" URL from the enum
697 # /xyz/openbmc_project/state/chassis_system0
698 if "chassis_system0" in url_path:
699 continue
700
Michael Sheposbdd1dce2020-12-10 11:51:58 -0600701 for attr_name in ret_values[url_path]:
702 # Create a state key value based on the attr_name.
703 try:
704 ret_values[url_path][attr_name] = \
705 re.sub(r'.*\.', "",
706 ret_values[url_path][attr_name])
707 except TypeError:
708 pass
709 # Do some key name manipulations.
710 new_attr_name = re.sub(r'^Current|(State|Transition)$',
711 "", attr_name)
712 new_attr_name = re.sub(r'BMC', r'Bmc', new_attr_name)
713 new_attr_name = re.sub(r'([A-Z][a-z])', r'_\1',
714 new_attr_name)
715 new_attr_name = new_attr_name.lower().lstrip("_")
716 new_attr_name = re.sub(r'power', r'chassis', new_attr_name)
717 if new_attr_name in req_states:
718 state[new_attr_name] = ret_values[url_path][attr_name]
719 else:
720 master_req_rf = ['redfish', 'host', 'requested_host',
721 'attempts_left', 'boot_progress', 'chassis',
722 'requested_chassis' 'bmc' 'requested_bmc']
723
724 req_rf = [sub_state for sub_state in req_states if sub_state in
725 master_req_rf]
726 need_rf = (len(req_rf) > 0)
727 state = DotDict()
728 if need_rf:
729 cmd_buf = ["Redfish Get States"]
730 gp.dprint_issuing(cmd_buf)
731 status, ret_values = \
732 BuiltIn().run_keyword_and_ignore_error(*cmd_buf)
733 gp.dprint_vars(status, ret_values)
734 if status == "PASS":
735 state['redfish'] = '1'
736 else:
737 state['redfish'] = '0'
738
739 if int(state['redfish']):
740 state['chassis'] = ret_values['chassis']
741 state['boot_progress'] = ret_values['boot_progress']
742 state['host'] = ret_values['host']
743 state['bmc'] = ret_values['bmc']
Michael Walshb95eb542017-03-31 09:39:20 -0500744
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600745 for sub_state in req_states:
Michael Walsh56749222017-09-29 15:26:07 -0500746 if sub_state in state:
747 continue
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600748 if sub_state.startswith("os_"):
749 # We pass "os_" requests on to get_os_state.
750 continue
751 cmd_buf = "state['" + sub_state + "'] = str(" + sub_state + ")"
752 exec(cmd_buf)
753
754 if os_host == "":
755 # The caller has not specified an os_host so as far as we're concerned,
756 # it doesn't exist.
757 return state
758
759 os_req_states = [sub_state for sub_state in req_states
760 if sub_state.startswith('os_')]
761
762 if len(os_req_states) > 0:
763 # The caller has specified an os_host and they have requested
764 # information on os substates.
765
766 # Based on the information gathered on bmc, we'll try to make a
767 # determination of whether the os is even up. We'll pass the result
768 # of that assessment to get_os_state to enhance performance.
769 os_up_match = DotDict()
770 for sub_state in master_os_up_match:
771 if sub_state in req_states:
772 os_up_match[sub_state] = master_os_up_match[sub_state]
Michael Walsh70369fd2016-11-22 11:25:57 -0600773 os_up = compare_states(state, os_up_match)
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600774 os_state = get_os_state(os_host=os_host,
775 os_username=os_username,
776 os_password=os_password,
777 req_states=os_req_states,
778 os_up=os_up,
779 quiet=quiet)
780 # Append os_state dictionary to ours.
781 state.update(os_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600782
783 return state
784
Michael Walsh70369fd2016-11-22 11:25:57 -0600785
Michael Walshfd5a8682019-02-01 14:28:42 -0600786exit_wait_early_message = ""
787
788
789def set_exit_wait_early_message(value):
790 r"""
791 Set global exit_wait_early_message to the indicated value.
792
793 This is a mechanism by which the programmer can do an early exit from
794 wait_until_keyword_succeeds() based on some special condition.
795
796 Description of argument(s):
797 value The value to assign to the global
798 exit_wait_early_message.
799 """
800
801 global exit_wait_early_message
802 exit_wait_early_message = value
803
804
Michael Walsh70369fd2016-11-22 11:25:57 -0600805def check_state(match_state,
806 invert=0,
807 print_string="",
808 openbmc_host="",
809 openbmc_username="",
810 openbmc_password="",
811 os_host="",
812 os_username="",
813 os_password="",
814 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600815 r"""
816 Check that the Open BMC machine's composite state matches the specified
817 state. On success, this keyword returns the machine's composite state as a
818 dictionary.
819
Michael Walsh2a0df682019-09-27 17:19:27 -0500820 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600821 match_state A dictionary whose key/value pairs are "state field"/
822 "state value". The state value is interpreted as a
823 regular expression. Example call from robot:
Michael Walsh341c21e2017-01-17 16:25:20 -0600824 ${match_state}= Create Dictionary chassis=^On$
825 ... bmc=^Ready$
Michael Walsh01975fa2017-08-20 20:51:36 -0500826 ... boot_progress=^OSStart$
Michael Walsh70369fd2016-11-22 11:25:57 -0600827 ${state}= Check State &{match_state}
828 invert If this flag is set, this function will succeed if the
829 states do NOT match.
830 print_string This function will print this string to the console prior
831 to getting the state.
832 openbmc_host The DNS name or IP address of the BMC.
833 This defaults to global ${OPENBMC_HOST}.
834 openbmc_username The username to be used to login to the BMC.
835 This defaults to global ${OPENBMC_USERNAME}.
836 openbmc_password The password to be used to login to the BMC.
837 This defaults to global ${OPENBMC_PASSWORD}.
838 os_host The DNS name or IP address of the operating system.
839 This defaults to global ${OS_HOST}.
840 os_username The username to be used to login to the OS.
841 This defaults to global ${OS_USERNAME}.
842 os_password The password to be used to login to the OS.
843 This defaults to global ${OS_PASSWORD}.
844 quiet Indicates whether status details should be written to the
845 console. Defaults to either global value of ${QUIET} or
846 to 1.
847 """
848
Michael Walsh619aa332017-04-12 15:56:51 -0500849 quiet = int(gp.get_var_value(quiet, 0))
Michael Walsh70369fd2016-11-22 11:25:57 -0600850
Michael Walshedb5c942019-03-28 12:40:50 -0500851 gp.gp_print(print_string)
852
853 try:
854 match_state = return_state_constant(match_state)
855 except TypeError:
856 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600857
Michael Walsh2a0df682019-09-27 17:19:27 -0500858 req_states = list(match_state.keys())
859 # Remove special-case match key from req_states.
860 if expressions_key() in req_states:
861 req_states.remove(expressions_key())
Michael Walsh70369fd2016-11-22 11:25:57 -0600862 # Initialize state.
863 state = get_state(openbmc_host=openbmc_host,
864 openbmc_username=openbmc_username,
865 openbmc_password=openbmc_password,
866 os_host=os_host,
867 os_username=os_username,
868 os_password=os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600869 req_states=req_states,
Michael Walsh70369fd2016-11-22 11:25:57 -0600870 quiet=quiet)
871 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -0500872 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600873
Michael Walshfd5a8682019-02-01 14:28:42 -0600874 if exit_wait_early_message != "":
875 # The exit_wait_early_message has been set by a signal handler so we
876 # will exit "successfully". It is incumbent upon the calling function
877 # (e.g. wait_state) to check/clear this variable and to fail
878 # appropriately.
879 return state
880
Michael Walsh70369fd2016-11-22 11:25:57 -0600881 match = compare_states(state, match_state)
882
883 if invert and match:
884 fail_msg = "The current state of the machine matches the match" +\
885 " state:\n" + gp.sprint_varx("state", state)
886 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
887 elif not invert and not match:
888 fail_msg = "The current state of the machine does NOT match the" +\
889 " match state:\n" +\
890 gp.sprint_varx("state", state)
891 BuiltIn().fail("\n" + gp.sprint_error(fail_msg))
892
893 return state
894
Michael Walsh70369fd2016-11-22 11:25:57 -0600895
Michael Walshf893ba02017-01-10 10:28:05 -0600896def wait_state(match_state=(),
Michael Walsh70369fd2016-11-22 11:25:57 -0600897 wait_time="1 min",
898 interval="1 second",
899 invert=0,
900 openbmc_host="",
901 openbmc_username="",
902 openbmc_password="",
903 os_host="",
904 os_username="",
905 os_password="",
906 quiet=None):
Michael Walsh70369fd2016-11-22 11:25:57 -0600907 r"""
908 Wait for the Open BMC machine's composite state to match the specified
909 state. On success, this keyword returns the machine's composite state as
910 a dictionary.
911
Michael Walsh2a0df682019-09-27 17:19:27 -0500912 Description of argument(s):
Michael Walsh70369fd2016-11-22 11:25:57 -0600913 match_state A dictionary whose key/value pairs are "state field"/
914 "state value". See check_state (above) for details.
Michael Walsh619aa332017-04-12 15:56:51 -0500915 This value may also be any string accepted by
916 return_state_constant (e.g. "standby_match_state").
917 In such a case this function will call
918 return_state_constant to convert it to a proper
919 dictionary as described above.
Michael Walsh70369fd2016-11-22 11:25:57 -0600920 wait_time The total amount of time to wait for the desired state.
921 This value may be expressed in Robot Framework's time
922 format (e.g. 1 minute, 2 min 3 s, 4.5).
923 interval The amount of time between state checks.
924 This value may be expressed in Robot Framework's time
925 format (e.g. 1 minute, 2 min 3 s, 4.5).
926 invert If this flag is set, this function will for the state of
927 the machine to cease to match the match state.
928 openbmc_host The DNS name or IP address of the BMC.
929 This defaults to global ${OPENBMC_HOST}.
930 openbmc_username The username to be used to login to the BMC.
931 This defaults to global ${OPENBMC_USERNAME}.
932 openbmc_password The password to be used to login to the BMC.
933 This defaults to global ${OPENBMC_PASSWORD}.
934 os_host The DNS name or IP address of the operating system.
935 This defaults to global ${OS_HOST}.
936 os_username The username to be used to login to the OS.
937 This defaults to global ${OS_USERNAME}.
938 os_password The password to be used to login to the OS.
939 This defaults to global ${OS_PASSWORD}.
940 quiet Indicates whether status details should be written to the
941 console. Defaults to either global value of ${QUIET} or
942 to 1.
943 """
944
Michael Walsh619aa332017-04-12 15:56:51 -0500945 quiet = int(gp.get_var_value(quiet, 0))
946
George Keishing36efbc02018-12-12 10:18:23 -0600947 try:
Michael Walsh619aa332017-04-12 15:56:51 -0500948 match_state = return_state_constant(match_state)
George Keishing36efbc02018-12-12 10:18:23 -0600949 except TypeError:
950 pass
Michael Walsh70369fd2016-11-22 11:25:57 -0600951
952 if not quiet:
953 if invert:
954 alt_text = "cease to "
955 else:
956 alt_text = ""
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -0500957 gp.print_timen("Checking every " + str(interval) + " for up to "
958 + str(wait_time) + " for the state of the machine to "
959 + alt_text + "match the state shown below.")
Michael Walsh3eb50022017-03-21 11:27:30 -0500960 gp.print_var(match_state)
Michael Walsh70369fd2016-11-22 11:25:57 -0600961
Michael Walshf893ba02017-01-10 10:28:05 -0600962 if quiet:
Michael Walsh341c21e2017-01-17 16:25:20 -0600963 print_string = ""
Michael Walshf893ba02017-01-10 10:28:05 -0600964 else:
Michael Walsh341c21e2017-01-17 16:25:20 -0600965 print_string = "#"
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600966
967 debug = int(BuiltIn().get_variable_value("${debug}", "0"))
968 if debug:
969 # In debug we print state so no need to print the "#".
970 print_string = ""
971 check_state_quiet = 1 - debug
Michael Walsh70369fd2016-11-22 11:25:57 -0600972 cmd_buf = ["Check State", match_state, "invert=${" + str(invert) + "}",
Michael Walshf893ba02017-01-10 10:28:05 -0600973 "print_string=" + print_string, "openbmc_host=" + openbmc_host,
Michael Walsh70369fd2016-11-22 11:25:57 -0600974 "openbmc_username=" + openbmc_username,
975 "openbmc_password=" + openbmc_password, "os_host=" + os_host,
976 "os_username=" + os_username, "os_password=" + os_password,
Michael Walsh8fae6ea2017-02-20 16:14:44 -0600977 "quiet=${" + str(check_state_quiet) + "}"]
Michael Walshedb5c942019-03-28 12:40:50 -0500978 gp.dprint_issuing(cmd_buf)
Michael Walsh619aa332017-04-12 15:56:51 -0500979 try:
980 state = BuiltIn().wait_until_keyword_succeeds(wait_time, interval,
981 *cmd_buf)
982 except AssertionError as my_assertion_error:
983 gp.printn()
984 message = my_assertion_error.args[0]
985 BuiltIn().fail(message)
986
Michael Walshfd5a8682019-02-01 14:28:42 -0600987 if exit_wait_early_message:
988 # The global exit_wait_early_message was set by a signal handler
989 # indicating that we should fail.
990 message = exit_wait_early_message
991 # Clear the exit_wait_early_message variable for future use.
992 set_exit_wait_early_message("")
993 BuiltIn().fail(gp.sprint_error(message))
994
Michael Walsh70369fd2016-11-22 11:25:57 -0600995 if not quiet:
Michael Walsh3eb50022017-03-21 11:27:30 -0500996 gp.printn()
Michael Walsh70369fd2016-11-22 11:25:57 -0600997 if invert:
Michael Walsh3eb50022017-03-21 11:27:30 -0500998 gp.print_timen("The states no longer match:")
Michael Walsh70369fd2016-11-22 11:25:57 -0600999 else:
Michael Walsh3eb50022017-03-21 11:27:30 -05001000 gp.print_timen("The states match:")
1001 gp.print_var(state)
Michael Walsh70369fd2016-11-22 11:25:57 -06001002
1003 return state
1004
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001005
Michael Walsh2a0df682019-09-27 17:19:27 -05001006def set_start_boot_seconds(value=0):
1007 global start_boot_seconds
1008 start_boot_seconds = int(value)
1009
1010
1011set_start_boot_seconds(0)
1012
1013
Michael Walsh619aa332017-04-12 15:56:51 -05001014def wait_for_comm_cycle(start_boot_seconds,
1015 quiet=None):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001016 r"""
Michael Walsh2a0df682019-09-27 17:19:27 -05001017 Wait for the BMC uptime to be less than elapsed_boot_time.
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001018
Michael Walsh2a0df682019-09-27 17:19:27 -05001019 This function will tolerate an expected loss of communication to the BMC.
1020 This function is useful when some kind of reboot has been initiated by the
1021 caller.
1022
1023 Description of argument(s):
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001024 start_boot_seconds The time that the boot test started. The format is the
1025 epoch time in seconds, i.e. the number of seconds since
1026 1970-01-01 00:00:00 UTC. This value should be obtained
1027 from the BMC so that it is not dependent on any kind of
1028 synchronization between this machine and the target BMC
1029 This will allow this program to work correctly even in
1030 a simulated environment. This value should be obtained
1031 by the caller prior to initiating a reboot. It can be
1032 obtained as follows:
1033 state = st.get_state(req_states=['epoch_seconds'])
1034 """
1035
Michael Walsh619aa332017-04-12 15:56:51 -05001036 quiet = int(gp.get_var_value(quiet, 0))
1037
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001038 # Validate parms.
Michael Walsh2a0df682019-09-27 17:19:27 -05001039 error_message = gv.valid_integer(start_boot_seconds)
1040 if error_message:
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001041 BuiltIn().fail(gp.sprint_error(error_message))
1042
Michael Walsh2a0df682019-09-27 17:19:27 -05001043 # Wait for uptime to be less than elapsed_boot_time.
1044 set_start_boot_seconds(start_boot_seconds)
1045 expr = 'int(float(state[\'uptime\'])) < int(state[\'elapsed_boot_time\'])'
Joy Onyerikwu004ad3c2018-06-11 16:29:56 -05001046 match_state = DotDict([('uptime', '^[0-9\\.]+$'),
Michael Walsh2a0df682019-09-27 17:19:27 -05001047 ('elapsed_boot_time', '^[0-9]+$'),
1048 (expressions_key(), [expr])])
David Shawe9192562020-09-28 11:00:45 -05001049 wait_state(match_state, wait_time="12 mins", interval="5 seconds")
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001050
Michael Sheposbdd1dce2020-12-10 11:51:58 -06001051 gp.qprint_timen("Verifying that REST/Redfish API interface is working.")
1052 if not redfish_support_trans_state:
1053 match_state = DotDict([('rest', '^1$')])
1054 else:
1055 match_state = DotDict([('redfish', '^1$')])
Michael Walsh8fae6ea2017-02-20 16:14:44 -06001056 state = wait_state(match_state, wait_time="5 mins", interval="2 seconds")