def test_fsm_action_is_class(self): """FSM: Test action is class""" class Ctrl(object): hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Device(object): ctrl = Mock(spec=Ctrl) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 events = "STATE1" class action1: pass transitions = [ ("STATE1", [0], -1, action1, 1), ] sm = FSM("FSM", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) with self.assertRaises(RuntimeWarning): sm.run()
def config(self, config_text, plane): """Apply config.""" nol = config_text.count('\n') config_lines = iter(config_text.splitlines()) events = [self.prompt_re, self.syntax_error_re] transitions = [(self.prompt_re, [0], 0, partial(a_send_line, config_lines), 10), (self.syntax_error_re, [0], -1, CommandSyntaxError("Configuration syntax error."), 0)] self.device.ctrl.send_command(self.config_cmd) fsm = FSM("CONFIG", self.device, events, transitions, timeout=10, max_transitions=nol + 5) fsm.run() # after the configuration the hostname may change. Need to detect it again try: self.device.send("", timeout=2) except CommandTimeoutError: prompt = self.device.ctrl.detect_prompt() self.device.prompt_re = self.make_dynamic_prompt(prompt) self.device.update_config_mode(prompt) if self.device.mode == "config": self.device.send("end") self.device.send("write memory") return "NO-COMMIT-ID"
def reload(self, reload_timeout=300, save_config=True): """Reload the device. CSM_DUT#reload System configuration has been modified. Save? [yes/no]: yes Building configuration... [OK] Proceed with reload? [confirm] """ response = "yes" if save_config else "no" events = [SAVE_CONFIG, PROCEED, pexpect.TIMEOUT, pexpect.EOF] transitions = [ (SAVE_CONFIG, [0], 1, partial(a_send_line, response), 60), (PROCEED, [0, 1], 2, partial(a_send, "\r"), 10), # if timeout try to send the reload command again (pexpect.TIMEOUT, [0], 0, partial(a_send_line, self.reload_cmd), 10), (pexpect.TIMEOUT, [2], -1, a_disconnect, 0), (pexpect.EOF, [0, 1, 2], -1, a_disconnect, 0) ] fsm = FSM("IOS-RELOAD", self.device, events, transitions, timeout=10, max_transitions=5) fsm.run()
def enable(self, enable_password): """Change to the privilege mode.""" if self.device.prompt[-1] == '#': self.log("Device is already in privileged mode") return events = [ self.password_re, self.device.prompt_re, pexpect.TIMEOUT, pexpect.EOF ] transitions = [ (self.password_re, [0], 1, partial(a_send_password, enable_password), 10), (self.password_re, [1], -1, ConnectionAuthenticationError("Incorrect enable password", self.device.hostname), 0), (self.device.prompt_re, [0, 1, 2, 3], -1, a_expected_prompt, 0), (pexpect.TIMEOUT, [0, 1, 2], -1, ConnectionAuthenticationError("Unable to get privileged mode", self.device.hostname), 0), (pexpect.EOF, [0, 1, 2], -1, ConnectionError("Device disconnected"), 0) ] self.device.ctrl.send_command(self.enable_cmd) fsm = FSM("IOS-ENABLE", self.device, events, transitions, timeout=10, max_transitions=5) fsm.run() if self.device.prompt[-1] != '#': raise ConnectionAuthenticationError("Privileged mode not set", self.device.hostname)
def test_fsm_action(self): """FSM: Test different actions""" class Ctrl(object): hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Connection(object): def log(self, msg): print(msg) class Chain(object): connection = Mock(spec=Connection) class Device(object): ctrl = Mock(spec=Ctrl) chain = Mock(spec=Chain) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 @action def action1(ctx): ctx.ctrl.expect.return_value = 1 self.assertEqual(str(ctx), "FSM Context:E=0,S=0,FI=False,M=''") return True @action def action2(ctx): """Action 2""" ctx.ctrl.expect.return_value = 2 self.assertEqual(str(ctx), "FSM Context:E=1,S=1,FI=False,M=''") return True events = ["STATE1", "STATE2", "STATE3"] transitions = [ ("STATE1", [0], 1, action1, 1), ("STATE2", [1], 2, action2, 1), ("STATE3", [2], 3, None, 1), ("STATE3", [3], -1, condoor.ConnectionTimeoutError("Error"), 0), ("UNKNOWN", [4], -1, None, 0) ] sm = FSM("FSM", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) with self.assertRaises(condoor.ConnectionTimeoutError): sm.run()
def config(self, config_text, plane): """Apply config.""" NO_CONFIGURATION_CHANGE = re.compile( "No configuration changes to commit") CONFIGURATION_FAILED = re.compile("show configuration failed") CONFIGURATION_INCONSITENCY = re.compile( "No configuration commits for this SDR will be allowed until " "a 'clear configuration inconsistency' command is performed.") self.enter_plane(plane) nol = config_text.count('\n') config_lines = iter(config_text.splitlines()) events = [self.prompt_re, self.syntax_error_re] transitions = [(self.prompt_re, [0], 0, partial(a_send_line, config_lines), 10), (self.syntax_error_re, [0], -1, CommandSyntaxError("Configuration syntax error."), 0)] self.device.ctrl.send_command(self.config_cmd) fsm = FSM("CONFIG", self.device, events, transitions, timeout=10, max_transitions=nol + 5) fsm.run() events = [ self.prompt_re, NO_CONFIGURATION_CHANGE, CONFIGURATION_FAILED ] transitions = [ (NO_CONFIGURATION_CHANGE, [0], -1, ConfigurationErrors("No configuration changes to commit."), 0), (CONFIGURATION_FAILED, [0], 2, a_capture_show_configuration_failed, 10), (CONFIGURATION_INCONSITENCY, [0], 2, a_configuration_inconsistency, 10), (self.prompt_re, [0], 1, partial(a_send_line, 'end'), 60), (self.prompt_re, [1], -1, None, 0) ] label = 'condoor-{}'.format(int(time.time())) self.device.ctrl.send_command(self.commit_cmd.format(label)) fsm = FSM("COMMIT", self.device, events, transitions, timeout=120, max_transitions=5) fsm.run() self.exit_plane() return label
def test_fsm_unknown_state(self): """FSM: Test unknown state transition""" class Ctrl(object): hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Device(object): ctrl = Mock(spec=Ctrl) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 @action def action1(value, ctx): """Action 1""" return True events = "STATE2" transitions = [ ("STATE1", [0], -1, action1, 1), ] sm = FSM("FSM", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) result = sm.run() self.assertFalse(result)
def test_fsm_action_is_partial(self): """FSM: Test action is partial""" class Ctrl(object): hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Device(object): ctrl = Mock(spec=Ctrl) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 events = ["STATE1"] @action def action1(value, ctx): """Action 1""" self.assertEqual(value, "test_value") return True transitions = [ ("STATE1", [0], -1, partial(action1, "test_value"), 1), ] sm = FSM("FSM", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) result = sm.run() self.assertTrue(result)
def test_fsm_event_not_list(self): """FSM: Test single event""" class Ctrl(object): hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Device(object): ctrl = Mock(spec=Ctrl) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 events = "STATE1" transitions = [ ("STATE1", [0], -1, None, 1), ] sm = FSM("FSM", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) result = sm.run() self.assertEqual(True, result)
def connect(self, driver): """Connect using the SSH protocol specific FSM.""" # 0 1 2 events = [driver.password_re, self.device.prompt_re, driver.unable_to_connect_re, # 3 4 5 6 7 NEWSSHKEY, KNOWN_HOSTS, HOST_KEY_FAILED, MODULUS_TOO_SMALL, PROTOCOL_DIFFER, # 8 9 driver.timeout_re, pexpect.TIMEOUT] transitions = [ (driver.password_re, [0, 1, 4, 5], -1, partial(a_save_last_pattern, self), 0), (self.device.prompt_re, [0], -1, partial(a_save_last_pattern, self), 0), # cover all messages indicating that connection was not set up (driver.unable_to_connect_re, [0], -1, a_unable_to_connect, 0), (NEWSSHKEY, [0], 1, partial(a_send_line, "yes"), 10), (KNOWN_HOSTS, [0, 1], 0, None, 0), (HOST_KEY_FAILED, [0], -1, ConnectionError("Host key failed", self.hostname), 0), (MODULUS_TOO_SMALL, [0], 0, self.fallback_to_sshv1, 0), (PROTOCOL_DIFFER, [0], 4, self.fallback_to_sshv1, 0), (PROTOCOL_DIFFER, [4], -1, ConnectionError("Protocol version differs", self.hostname), 0), (pexpect.TIMEOUT, [0], 5, partial(a_send, "\r\n"), 10), (pexpect.TIMEOUT, [5], -1, ConnectionTimeoutError("Connection timeout", self.hostname), 0), (driver.timeout_re, [0], -1, ConnectionTimeoutError("Connection timeout", self.hostname), 0), ] self.log("EXPECTED_PROMPT={}".format(pattern_to_str(self.device.prompt_re))) fsm = FSM("SSH-CONNECT", self.device, events, transitions, timeout=_C['connect_timeout'], searchwindowsize=160) return fsm.run()
def wait_for_string(self, expected_string, timeout=60): """Wait for string FSM.""" # 0 1 2 3 events = [self.syntax_error_re, self.connection_closed_re, expected_string, self.press_return_re, # 4 5 6 7 self.more_re, pexpect.TIMEOUT, pexpect.EOF, self.buffer_overflow_re] # add detected prompts chain events += self.device.get_previous_prompts() # without target prompt logger.debug("Expecting: {}".format(pattern_to_str(expected_string))) transitions = [ (self.syntax_error_re, [0], -1, CommandSyntaxError("Command unknown", self.device.hostname), 0), (self.connection_closed_re, [0], 1, a_connection_closed, 10), (pexpect.TIMEOUT, [0], -1, CommandTimeoutError("Timeout waiting for prompt", self.device.hostname), 0), (pexpect.EOF, [0, 1], -1, ConnectionError("Unexpected device disconnect", self.device.hostname), 0), (self.more_re, [0], 0, partial(a_send, " "), 10), (expected_string, [0, 1], -1, a_expected_prompt, 0), (self.press_return_re, [0], -1, a_stays_connected, 0), # TODO: Customize in XR driver (self.buffer_overflow_re, [0], -1, CommandSyntaxError("Command too long", self.device.hostname), 0) ] for prompt in self.device.get_previous_prompts(): transitions.append((prompt, [0, 1], 0, a_unexpected_prompt, 0)) fsm = FSM("WAIT-4-STRING", self.device, events, transitions, timeout=timeout) return fsm.run()
def authenticate(self, driver): """Authenticate using the SSH protocol specific FSM.""" # 0 1 2 3 events = [ driver.press_return_re, driver.password_re, self.device.prompt_re, pexpect.TIMEOUT ] transitions = [ (driver.press_return_re, [0, 1], 1, partial(a_send, "\r\n"), 10), (driver.password_re, [0], 1, partial(a_send_password, self._acquire_password()), _C['first_prompt_timeout']), (driver.password_re, [1], -1, a_authentication_error, 0), (self.device.prompt_re, [0, 1], -1, None, 0), (pexpect.TIMEOUT, [1], -1, ConnectionError("Error getting device prompt") if self.device.is_target else partial(a_send, "\r\n"), 0) ] self.log("EXPECTED_PROMPT={}".format( pattern_to_str(self.device.prompt_re))) fsm = FSM("SSH-AUTH", self.device, events, transitions, init_pattern=self.last_pattern, timeout=30) return fsm.run()
def test_fsm_event_not_list(self): """FSM: Test single event""" class Ctrl(object): hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Connection(object): def log(self, msg): print(msg) class Chain(object): connection = Mock(spec=Connection) class Device(object): ctrl = Mock(spec=Ctrl) chain = Mock(spec=Chain) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 events = "STATE1" transitions = [ ("STATE1", [0], -1, None, 1), ] sm = FSM("FSM", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) result = sm.run() self.assertEqual(True, result)
def connect(self, driver): """Connect using the SSH protocol specific FSM.""" # 0 1 2 events = [driver.password_re, self.device.prompt_re, driver.unable_to_connect_re, # 3 4 5 6 7 NEWSSHKEY, KNOWN_HOSTS, HOST_KEY_FAILED, MODULUS_TOO_SMALL, PROTOCOL_DIFFER, # 8 9 driver.timeout_re, pexpect.TIMEOUT] transitions = [ (driver.password_re, [0, 1, 4, 5], -1, partial(a_save_last_pattern, self), 0), (self.device.prompt_re, [0], -1, partial(a_save_last_pattern, self), 0), # cover all messages indicating that connection was not set up (driver.unable_to_connect_re, [0], -1, a_unable_to_connect, 0), (NEWSSHKEY, [0], 1, partial(a_send_line, "yes"), 10), (KNOWN_HOSTS, [0, 1], 0, None, 0), (HOST_KEY_FAILED, [0], -1, ConnectionError("Host key failed", self.hostname), 0), (MODULUS_TOO_SMALL, [0], 0, self.fallback_to_sshv1, 0), (PROTOCOL_DIFFER, [0], 4, self.fallback_to_sshv1, 0), (PROTOCOL_DIFFER, [4], -1, ConnectionError("Protocol version differs", self.hostname), 0), (pexpect.TIMEOUT, [0], 5, partial(a_send, "\r\n"), 10), (pexpect.TIMEOUT, [5], -1, ConnectionTimeoutError("Connection timeout", self.hostname), 0), (driver.timeout_re, [0], -1, ConnectionTimeoutError("Connection timeout", self.hostname), 0), ] logger.debug("EXPECTED_PROMPT={}".format(pattern_to_str(self.device.prompt_re))) fsm = FSM("SSH-CONNECT", self.device, events, transitions, timeout=_C['connect_timeout'], searchwindowsize=160) return fsm.run()
def connect(self, driver): """Connect using the Telnet protocol specific FSM.""" # 0 1 2 3 events = [ESCAPE_CHAR, driver.press_return_re, driver.standby_re, driver.username_re, # 4 5 6 7 driver.password_re, driver.more_re, self.device.prompt_re, driver.rommon_re, # 8 9 10 11 driver.unable_to_connect_re, driver.timeout_re, pexpect.TIMEOUT, PASSWORD_OK] transitions = [ (ESCAPE_CHAR, [0], 1, None, _C['esc_char_timeout']), (driver.press_return_re, [0, 1], 1, partial(a_send, "\r\n"), 10), (PASSWORD_OK, [0, 1], 1, partial(a_send, "\r\n"), 10), (driver.standby_re, [0, 5], -1, partial(a_standby_console), 0), (driver.username_re, [0, 1, 5, 6], -1, partial(a_save_last_pattern, self), 0), (driver.password_re, [0, 1, 5], -1, partial(a_save_last_pattern, self), 0), (driver.more_re, [0, 5], 7, partial(a_send, "q"), 10), # router sends it again to delete (driver.more_re, [7], 8, None, 10), # (prompt, [0, 1, 5], 6, partial(a_send, "\r\n"), 10), (self.device.prompt_re, [0, 1, 5], 0, None, 10), (self.device.prompt_re, [6, 8, 5], -1, partial(a_save_last_pattern, self), 0), (driver.rommon_re, [0, 1, 5], -1, partial(a_save_last_pattern, self), 0), (driver.unable_to_connect_re, [0, 1], -1, a_unable_to_connect, 0), (driver.timeout_re, [0, 1], -1, ConnectionTimeoutError("Connection Timeout", self.hostname), 0), (pexpect.TIMEOUT, [0, 1], 5, partial(a_send, "\r\n"), 10), (pexpect.TIMEOUT, [5], -1, ConnectionTimeoutError("Connection timeout", self.hostname), 0) ] logger.debug("EXPECTED_PROMPT={}".format(pattern_to_str(self.device.prompt_re))) fsm = FSM("TELNET-CONNECT", self.device, events, transitions, timeout=_C['connect_timeout'], init_pattern=self.last_pattern) return fsm.run()
def authenticate(self, driver): """Authenticate using the SSH protocol specific FSM.""" # 0 1 2 3 events = [driver.username_re, driver.password_re, self.device.prompt_re, driver.rommon_re, # 4 5 6 7 driver.unable_to_connect_re, driver.authentication_error_re, pexpect.TIMEOUT, pexpect.EOF] transitions = [ (driver.username_re, [0], 1, partial(a_send_username, self.username), 10), (driver.username_re, [1], 1, None, 10), (driver.password_re, [0, 1], 2, partial(a_send_password, self._acquire_password()), _C['first_prompt_timeout']), (driver.username_re, [2], -1, a_authentication_error, 0), (driver.password_re, [2], -1, a_authentication_error, 0), (driver.authentication_error_re, [1, 2], -1, a_authentication_error, 0), (self.device.prompt_re, [0, 1, 2], -1, None, 0), (driver.rommon_re, [0], -1, partial(a_send, "\r\n"), 0), (pexpect.TIMEOUT, [0], 1, partial(a_send, "\r\n"), 10), (pexpect.TIMEOUT, [2], -1, None, 0), (pexpect.TIMEOUT, [3, 7], -1, ConnectionTimeoutError("Connection Timeout", self.hostname), 0), (driver.unable_to_connect_re, [0, 1, 2], -1, a_unable_to_connect, 0), ] logger.debug("EXPECTED_PROMPT={}".format(pattern_to_str(self.device.prompt_re))) fsm = FSM("TELNET-AUTH", self.device, events, transitions, timeout=_C['connect_timeout'], init_pattern=self.last_pattern) return fsm.run()
def connect(self, driver): """Connect using console specific FSM.""" # 0 1 2 3 events = [ ESCAPE_CHAR, driver.press_return_re, driver.standby_re, driver.username_re, # 4 5 6 7 driver.password_re, driver.more_re, self.device.prompt_re, driver.rommon_re, # 8 9 10 11 driver.unable_to_connect_re, driver.timeout_re, pexpect.TIMEOUT, PASSWORD_OK ] transitions = [ (ESCAPE_CHAR, [0], 1, partial(a_send, "\r\n"), _C['esc_char_timeout']), (driver.press_return_re, [0, 1], 1, partial(a_send, "\r\n"), 10), (PASSWORD_OK, [0, 1], 1, partial(a_send, "\r\n"), 10), (driver.standby_re, [0, 5], -1, ConnectionError("Standby console", self.hostname), 0), (driver.username_re, [0, 1, 5, 6], -1, partial(a_save_last_pattern, self), 0), (driver.password_re, [0, 1, 5], -1, partial(a_save_last_pattern, self), 0), (driver.more_re, [0, 5], 7, partial(a_send, "q"), 10), # router sends it again to delete (driver.more_re, [7], 8, None, 10), # (prompt, [0, 1, 5], 6, partial(a_send, "\r\n"), 10), (self.device.prompt_re, [0, 5], 0, None, 10), (self.device.prompt_re, [1, 6, 8, 5], -1, partial(a_save_last_pattern, self), 0), (driver.rommon_re, [0, 1, 5], -1, partial(a_save_last_pattern, self), 0), (driver.unable_to_connect_re, [0, 1], -1, a_unable_to_connect, 0), (driver.timeout_re, [0, 1], -1, ConnectionTimeoutError("Connection Timeout", self.hostname), 0), (pexpect.TIMEOUT, [0, 1], 5, partial(a_send, "\r\n"), 10), (pexpect.TIMEOUT, [5], -1, ConnectionTimeoutError("Connection timeout", self.hostname), 0) ] logger.debug("EXPECTED_PROMPT={}".format( pattern_to_str(self.device.prompt_re))) fsm = FSM("CONSOLE-SERVER-CONNECT", self.device, events, transitions, timeout=_C['connect_timeout'], init_pattern=self.last_pattern) return fsm.run()
def test_fsm_action(self): """FSM: Test different actions""" class Ctrl(object): hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Device(object): ctrl = Mock(spec=Ctrl) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 @action def action1(ctx): ctx.ctrl.expect.return_value = 1 self.assertEqual(str(ctx), "FSM Context:E=0,S=0,FI=False,M=''") return True @action def action2(ctx): """Action 2""" ctx.ctrl.expect.return_value = 2 self.assertEqual(str(ctx), "FSM Context:E=1,S=1,FI=False,M=''") return True events = ["STATE1", "STATE2", "STATE3"] transitions = [ ("STATE1", [0], 1, action1, 1), ("STATE2", [1], 2, action2, 1), ("STATE3", [2], 3, None, 1), ("STATE3", [3], -1, condoor.ConnectionTimeoutError("Error"), 0), ("UNKNOWN", [4], -1, None, 0) ] sm = FSM("FSM", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) with self.assertRaises(condoor.ConnectionTimeoutError): sm.run()
def reload(self, reload_timeout, save_config): """Reload the device.""" RELOAD_PROMPT = re.compile(re.escape("Reload hardware module ? [no,yes]")) START_TO_BACKUP = re.compile("Status report.*START TO BACKUP") BACKUP_HAS_COMPLETED_SUCCESSFULLY = re.compile("Status report.*BACKUP HAS COMPLETED SUCCESSFULLY") DONE = re.compile(re.escape("[Done]")) CONSOLE = re.compile("ios con[0|1]/(?:RS?P)?[0-1]/CPU0 is now available") CONFIGURATION_COMPLETED = re.compile("SYSTEM CONFIGURATION COMPLETED") CONFIGURATION_IN_PROCESS = re.compile("SYSTEM CONFIGURATION IN PROCESS") BOOTING = re.compile("Booting IOS-XR 64 bit Boot previously installed image") # events = [RELOAD_NA, DONE, PROCEED, CONFIGURATION_IN_PROCESS, self.rommon_re, self.press_return_re, # # 6 7 8 9 10 11 # CONSOLE, CONFIGURATION_COMPLETED, RECONFIGURE_USERNAME_PROMPT, TIMEOUT, EOF, self.reload_cmd, # # 12 13 14 # ROOT_USERNAME_PROMPT, ROOT_PASSWORD_PROMPT, CANDIDATE_BOOT_IMAGE] events = [self.reload_cmd, RELOAD_PROMPT, START_TO_BACKUP, BACKUP_HAS_COMPLETED_SUCCESSFULLY, DONE, BOOTING, CONSOLE, self.press_return_re, CONFIGURATION_COMPLETED, CONFIGURATION_IN_PROCESS, EOF] transitions = [ # do I really need to clean the cmd (RELOAD_PROMPT, [0], 1, partial(a_send_line, "yes"), 30), (START_TO_BACKUP, [1], 2, a_message_callback, 60), (BACKUP_HAS_COMPLETED_SUCCESSFULLY, [2], 3, a_message_callback, 10), (DONE, [3], 4, None, 600), (self.rommon_re, [0, 4], 5, partial(a_send_boot, "boot"), 600), (BOOTING, [0, 4], 5, a_message_callback, 600), (CONSOLE, [0, 5], 6, None, 600), (self.press_return_re, [6], 7, partial(a_send, "\r"), 300), (CONFIGURATION_IN_PROCESS, [7], 8, None, 180), (CONFIGURATION_COMPLETED, [8], -1, a_reconnect, 0), (EOF, [0, 1, 2, 3, 4, 5], -1, ConnectionError("Device disconnected"), 0), # (RELOAD_NA, [1], -1, a_reload_na, 0), # (DONE, [1], 2, None, 120), # (PROCEED, [2], 3, partial(a_send, "\r"), reload_timeout), # (self.rommon_re, [0, 3], 4, partial(a_send_boot, "boot"), 600), # (CANDIDATE_BOOT_IMAGE, [0, 3], 4, a_message_callback, 600), # (CONSOLE, [0, 1, 3, 4], 5, None, 600), # (self.press_return_re, [5], 6, partial(a_send, "\r"), 300), # # configure root username and password the same as used for device connection. # (RECONFIGURE_USERNAME_PROMPT, [6, 7], 8, None, 10), # (ROOT_USERNAME_PROMPT, [8], 9, partial(a_send_username, self.device.node_info.username), 1), # (ROOT_PASSWORD_PROMPT, [9], 9, partial(a_send_password, self.device.node_info.password), 1), # (CONFIGURATION_IN_PROCESS, [6, 9], 7, None, 180), # (CONFIGURATION_COMPLETED, [7], -1, a_reconnect, 0), # (TIMEOUT, [0, 1, 2], -1, ConnectionAuthenticationError("Unable to reload"), 0), # (EOF, [0, 1, 2, 3, 4, 5], -1, ConnectionError("Device disconnected"), 0), # (TIMEOUT, [6], 7, partial(a_send, "\r"), 180), # (TIMEOUT, [7], -1, ConnectionAuthenticationError("Unable to reconnect after reloading"), 0), ] fsm = FSM("RELOAD", self.device, events, transitions, timeout=600) return fsm.run()
def enable(self, enable_password): """Change to the privilege mode.""" if self.device.prompt[-1] == '#': logger.debug("Device is already in privileged mode") return events = [self.password_re, self.device.prompt_re, pexpect.TIMEOUT, pexpect.EOF] transitions = [ (self.password_re, [0], 1, partial(a_send_password, enable_password), 10), (self.password_re, [1], -1, ConnectionAuthenticationError("Incorrect enable password", self.device.hostname), 0), (self.device.prompt_re, [0, 1, 2, 3], -1, a_expected_prompt, 0), (pexpect.TIMEOUT, [0, 1, 2], -1, ConnectionAuthenticationError("Unable to get privileged mode", self.device.hostname), 0), (pexpect.EOF, [0, 1, 2], -1, ConnectionError("Device disconnected"), 0) ] self.device.ctrl.send_command(self.enable_cmd) fsm = FSM("IOS-ENABLE", self.device, events, transitions, timeout=10, max_transitions=5) fsm.run() if self.device.prompt[-1] != '#': raise ConnectionAuthenticationError("Privileged mode not set", self.device.hostname)
def test_fsm_action_is_string(self): """FSM: Test action is string""" class Ctrl(object): hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Connection(object): def log(self, msg): print(msg) class Chain(object): connection = Mock(spec=Connection) class Device(object): ctrl = Mock(spec=Ctrl) chain = Mock(spec=Chain) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 events = "STATE1" action1 = "not callable string" transitions = [ ("STATE1", [0], -1, action1, 1), ] sm = FSM("FSM", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) with self.assertRaises(RuntimeWarning): sm.run()
def test_fsm_max_transitions(self): """FSM: Test the maximum transitions sentil""" class Ctrl(object): logger = None hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Connection(object): def log(self, msg): print(msg) class Chain(object): connection = Mock(spec=Connection) class Device(object): ctrl = Mock(spec=Ctrl) chain = Mock(spec=Chain) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 device.chain.connection.log.return_value = None @action def action1(ctx): self.assertEqual(ctx.event, 0) self.assertEqual(ctx.finished, False) self.assertEqual(ctx.fsm_name, "MAX-TR") self.assertEqual(ctx.msg, "") self.assertEqual(ctx.pattern, pexpect.TIMEOUT) self.assertEqual(ctx.state, 0) ctx.device.counter += 1 return True events = [pexpect.TIMEOUT] transitions = [ (pexpect.TIMEOUT, [0], 0, action1, 1) ] sm = FSM("MAX-TR", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) result = sm.run() self.assertEqual(device.counter, 5) self.assertFalse(result)
def authenticate(self, driver): """Authenticate using the SSH protocol specific FSM.""" # 0 1 2 3 events = [driver.press_return_re, driver.password_re, self.device.prompt_re, pexpect.TIMEOUT] transitions = [ (driver.press_return_re, [0, 1], 1, partial(a_send, "\r\n"), 10), (driver.password_re, [0], 1, partial(a_send_password, self._acquire_password()), _C['first_prompt_timeout']), (driver.password_re, [1], -1, a_authentication_error, 0), (self.device.prompt_re, [0, 1], -1, None, 0), (pexpect.TIMEOUT, [1], -1, ConnectionError("Error getting device prompt") if self.device.is_target else partial(a_send, "\r\n"), 0) ] logger.debug("EXPECTED_PROMPT={}".format(pattern_to_str(self.device.prompt_re))) fsm = FSM("SSH-AUTH", self.device, events, transitions, init_pattern=self.last_pattern, timeout=30) return fsm.run()
def reload(self, reload_timeout=300, save_config=True): """Reload the device. CSM_DUT#reload System configuration has been modified. Save? [yes/no]: yes Building configuration... [OK] Proceed with reload? [confirm] """ SAVE_CONFIG = re.compile( re.escape( "System configuration has been modified. Save? [yes/no]: ")) PROCEED = re.compile(re.escape("Proceed with reload? [confirm]")) IMAGE = re.compile("Passing control to the main image") BOOTSTRAP = re.compile("System Bootstrap") LOCATED = re.compile("Located .*") RETURN = re.compile(re.escape("Press RETURN to get started!")) response = "yes" if save_config else "no" # 0 1 2 3 4 events = [ SAVE_CONFIG, PROCEED, LOCATED, RETURN, self.username_re, self.password_re, BOOTSTRAP, IMAGE, TIMEOUT, EOF ] # 5 6 7 8 9 transitions = [ (SAVE_CONFIG, [0], 1, partial(a_send_line, response), 60), (PROCEED, [0, 1], 2, partial(a_send, "\r"), reload_timeout), (LOCATED, [2], 2, a_message_callback, reload_timeout), # if timeout try to send the reload command again (TIMEOUT, [0], 0, partial(a_send_line, self.reload_cmd), 10), (BOOTSTRAP, [2], -1, a_disconnect, reload_timeout), (IMAGE, [2], 3, a_message_callback, reload_timeout), (self.username_re, [3], -1, a_return_and_reconnect, 0), (self.password_re, [3], -1, a_return_and_reconnect, 0), (RETURN, [3], -1, a_return_and_reconnect, 0), (TIMEOUT, [2], -1, a_disconnect, 0), (EOF, [0, 1, 2, 3], -1, a_disconnect, 0) ] fsm = FSM("IOS-RELOAD", self.device, events, transitions, timeout=10) return fsm.run()
def authenticate(self, driver): """Authenticate using the Console Server protocol specific FSM.""" # 0 1 2 3 events = [ driver.username_re, driver.password_re, self.device.prompt_re, driver.rommon_re, # 4 5 6 7 8 driver.unable_to_connect_re, driver.authentication_error_re, pexpect.TIMEOUT, pexpect.EOF ] transitions = [ (driver.username_re, [0], 1, partial(a_send_username, self.username), 10), (driver.username_re, [1], 1, None, 10), (driver.password_re, [0, 1], 2, partial(a_send_password, self._acquire_password()), _C['first_prompt_timeout']), (driver.username_re, [2], -1, a_authentication_error, 0), (driver.password_re, [2], -1, a_authentication_error, 0), (driver.authentication_error_re, [1, 2], -1, a_authentication_error, 0), (self.device.prompt_re, [0, 1, 2], -1, None, 0), (driver.rommon_re, [0], -1, partial(a_send, "\r\n"), 0), (pexpect.TIMEOUT, [0], 1, partial(a_send, "\r\n"), 10), (pexpect.TIMEOUT, [2], -1, None, 0), (pexpect.TIMEOUT, [3, 7], -1, ConnectionTimeoutError("Connection Timeout", self.hostname), 0), (driver.unable_to_connect_re, [0, 1, 2], -1, a_unable_to_connect, 0), ] logger.debug("EXPECTED_PROMPT={}".format( pattern_to_str(self.device.prompt_re))) fsm = FSM("CONSOLE-SERVER-AUTH", self.device, events, transitions, timeout=_C['connect_timeout'], init_pattern=self.last_pattern) return fsm.run()
def wait_for_string(self, expected_string, timeout=60): """Wait for string FSM for XR 64 bit.""" # Big thanks to calvados developers for make this FSM such complex ;-) # 0 1 2 3 events = [self.syntax_error_re, self.connection_closed_re, expected_string, self.press_return_re, # 4 5 6 7 8 self.more_re, pexpect.TIMEOUT, pexpect.EOF, self.calvados_re, self.calvados_connect_re, # 9 self.calvados_term_length] # add detected prompts chain events += self.device.get_previous_prompts() # without target prompt logger.debug("Expecting: {}".format(pattern_to_str(expected_string))) logger.debug("Calvados prompt: {}".format(pattern_to_str(self.calvados_re))) transitions = [ (self.syntax_error_re, [0], -1, CommandSyntaxError("Command unknown", self.device.hostname), 0), (self.connection_closed_re, [0], 1, a_connection_closed, 10), (pexpect.TIMEOUT, [0, 2], -1, CommandTimeoutError("Timeout waiting for prompt", self.device.hostname), 0), (pexpect.EOF, [0, 1], -1, ConnectionError("Unexpected device disconnect", self.device.hostname), 0), (self.more_re, [0], 0, partial(a_send, " "), 10), (expected_string, [0, 1], -1, a_expected_prompt, 0), (self.calvados_re, [0], -1, a_expected_prompt, 0), (self.press_return_re, [0], -1, a_stays_connected, 0), (self.calvados_connect_re, [0], 2, None, 0), # admin command to switch to calvados (self.calvados_re, [2], 3, None, _C['calvados_term_wait_time']), # getting the prompt only (pexpect.TIMEOUT, [3], 0, partial(a_send, "\r"), 0), # term len (self.calvados_term_length, [3], 4, None, 0), # ignore for command start (self.calvados_re, [4], 5, None, 0), # ignore for command start (self.calvados_re, [5], 0, a_store_cmd_result, 0), ] for prompt in self.device.get_previous_prompts(): transitions.append((prompt, [0, 1], 0, a_unexpected_prompt, 0)) fsm = FSM("WAIT-4-STRING", self.device, events, transitions, timeout=timeout) return fsm.run()
def test_fsm_action_is_partial(self): """FSM: Test action is partial""" class Ctrl(object): hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Connection(object): def log(self, msg): print(msg) class Chain(object): connection = Mock(spec=Connection) class Device(object): ctrl = Mock(spec=Ctrl) chain = Mock(spec=Chain) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 events = ["STATE1"] @action def action1(value, ctx): """Action 1""" self.assertEqual(value, "test_value") return True transitions = [ ("STATE1", [0], -1, partial(action1, "test_value"), 1), ] sm = FSM("FSM", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) result = sm.run() self.assertTrue(result)
def test_fsm_max_transitions(self): """FSM: Test the maximum transitions sentil""" class Ctrl(object): logger = None hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Device(object): ctrl = Mock(spec=Ctrl) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 @action def action1(ctx): self.assertEqual(ctx.event, 0) self.assertEqual(ctx.finished, False) self.assertEqual(ctx.fsm_name, "MAX-TR") self.assertEqual(ctx.msg, "") self.assertEqual(ctx.pattern, pexpect.TIMEOUT) self.assertEqual(ctx.state, 0) ctx.device.counter += 1 return True events = [pexpect.TIMEOUT] transitions = [ (pexpect.TIMEOUT, [0], 0, action1, 1) ] sm = FSM("MAX-TR", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) result = sm.run() self.assertEqual(device.counter, 5) self.assertFalse(result)
def test_fsm_unknown_state(self): """FSM: Test unknown state transition""" class Ctrl(object): hostname = "hostname" def expect(self, events, searchwindowsize, timeout): pass class Connection(object): def log(self, msg): print(msg) class Chain(object): connection = Mock(spec=Connection) class Device(object): ctrl = Mock(spec=Ctrl) chain = Mock(spec=Chain) device = Mock(spec=Device) device.ctrl.expect.return_value = 0 device.counter = 0 @action def action1(value, ctx): """Action 1""" return True events = "STATE2" transitions = [ ("STATE1", [0], -1, action1, 1), ] sm = FSM("FSM", device, events=events, transitions=transitions, init_pattern=None, timeout=1, max_transitions=5) result = sm.run() self.assertFalse(result)
def reload(self, reload_timeout, save_config): """Reload the device.""" PROCEED = re.compile(re.escape("Proceed with reload? [confirm]")) DONE = re.compile(re.escape("[Done]")) CONFIGURATION_COMPLETED = re.compile("SYSTEM CONFIGURATION COMPLETED") CONFIGURATION_IN_PROCESS = re.compile( "SYSTEM CONFIGURATION IN PROCESS") # CONSOLE = re.compile("ios con[0|1]/RS?P[0-1]/CPU0 is now available") CONSOLE = re.compile( "ios con[0|1]/(?:RS?P)?[0-1]/CPU0 is now available") RECONFIGURE_USERNAME_PROMPT = "[Nn][Oo] root-system username is configured" ROOT_USERNAME_PROMPT = "Enter root-system username\: " ROOT_PASSWORD_PROMPT = "Enter secret( again)?\: " # BOOT=disk0:asr9k-os-mbi-6.1.1/0x100305/mbiasr9k-rsp3.vm,1; \ # disk0:asr9k-os-mbi-5.3.4/0x100305/mbiasr9k-rsp3.vm,2; # Candidate Boot Image num 0 is disk0:asr9k-os-mbi-6.1.1/0x100305/mbiasr9k-rsp3.vm # Candidate Boot Image num 1 is disk0:asr9k-os-mbi-5.3.4/0x100305/mbiasr9k-rsp3.vm CANDIDATE_BOOT_IMAGE = "Candidate Boot Image num 0 is .*vm" NOT_COMMITTED = re.compile( re.escape( "Some active software packages are not yet committed. Proceed?[confirm]" )) RELOAD_NA = re.compile( "Reload to the ROM monitor disallowed from a telnet line") # 0 1 2 3 4 5 events = [ RELOAD_NA, DONE, PROCEED, CONFIGURATION_IN_PROCESS, self.rommon_re, self.press_return_re, # 6 7 8 9 CONSOLE, CONFIGURATION_COMPLETED, RECONFIGURE_USERNAME_PROMPT, ROOT_USERNAME_PROMPT, # 10 11 12 13 14 15 ROOT_PASSWORD_PROMPT, self.username_re, TIMEOUT, EOF, self.reload_cmd, CANDIDATE_BOOT_IMAGE, # 16 NOT_COMMITTED ] transitions = [ (RELOAD_NA, [0], -1, a_reload_na, 0), # temp for testing (NOT_COMMITTED, [0], -1, a_not_committed, 10), (DONE, [0], 2, None, 120), (PROCEED, [2], 3, partial(a_send, "\r"), reload_timeout), # this needs to be verified (self.rommon_re, [0, 3], 3, partial(a_send_boot, "boot"), 600), (CANDIDATE_BOOT_IMAGE, [0, 3], 4, a_message_callback, 600), (CONSOLE, [0, 1, 3, 4], 5, None, 600), # This is required. Otherwise nothing more is displayed on the console (self.press_return_re, [5], 6, partial(a_send, "\r"), 300), # configure root username and password the same as used for device connection. (RECONFIGURE_USERNAME_PROMPT, [6, 7, 10], 8, None, 10), (ROOT_USERNAME_PROMPT, [8], 9, partial(a_send_username, self.device.node_info.username), 1), (ROOT_PASSWORD_PROMPT, [9], 9, partial(a_send_password, self.device.node_info.password), 1), (CONFIGURATION_IN_PROCESS, [6, 9], 10, None, 1200), (CONFIGURATION_COMPLETED, [10], -1, a_reconnect, 0), (self.username_re, [7, 9], -1, a_return_and_reconnect, 0), (TIMEOUT, [0, 1, 2], -1, ConnectionAuthenticationError("Unable to reload"), 0), (EOF, [0, 1, 2, 3, 4, 5], -1, ConnectionError("Device disconnected"), 0), (TIMEOUT, [6], 7, partial(a_send, "\r"), 180), (TIMEOUT, [7], -1, ConnectionAuthenticationError( "Unable to reconnect after reloading"), 0), (TIMEOUT, [10], -1, a_reconnect, 0), ] fsm = FSM("RELOAD", self.device, events, transitions, timeout=600) return fsm.run()
def wait_for_string(self, expected_string, timeout=60): """Wait for string FSM for XR 64 bit.""" # Big thanks to calvados developers for make this FSM such complex ;-) # 0 1 2 3 events = [ self.syntax_error_re, self.connection_closed_re, expected_string, self.press_return_re, # 4 5 6 7 8 self.more_re, pexpect.TIMEOUT, pexpect.EOF, self.calvados_re, self.calvados_connect_re, # 9 self.calvados_term_length ] # add detected prompts chain events += self.device.get_previous_prompts() # without target prompt logger.debug("Expecting: {}".format(pattern_to_str(expected_string))) logger.debug("Calvados prompt: {}".format( pattern_to_str(self.calvados_re))) transitions = [ (self.syntax_error_re, [0], -1, CommandSyntaxError("Command unknown", self.device.hostname), 0), (self.connection_closed_re, [0], 1, a_connection_closed, 10), (pexpect.TIMEOUT, [0, 2], -1, CommandTimeoutError("Timeout waiting for prompt", self.device.hostname), 0), (pexpect.EOF, [0, 1], -1, ConnectionError("Unexpected device disconnect", self.device.hostname), 0), (self.more_re, [0], 0, partial(a_send, " "), 10), (expected_string, [0, 1], -1, a_expected_prompt, 0), (self.calvados_re, [0], -1, a_expected_prompt, 0), (self.press_return_re, [0], -1, a_stays_connected, 0), (self.calvados_connect_re, [0], 2, None, 0), # admin command to switch to calvados (self.calvados_re, [2], 3, None, _C['calvados_term_wait_time']), # getting the prompt only (pexpect.TIMEOUT, [3], 0, partial(a_send, "\r"), 0), # term len (self.calvados_term_length, [3], 4, None, 0), # ignore for command start (self.calvados_re, [4], 5, None, 0), # ignore for command start (self.calvados_re, [5], 0, a_store_cmd_result, 0), ] for prompt in self.device.get_previous_prompts(): transitions.append((prompt, [0, 1], 0, a_unexpected_prompt, 0)) fsm = FSM("WAIT-4-STRING", self.device, events, transitions, timeout=timeout) return fsm.run()
def reload(self, reload_timeout, save_config): """Reload the device.""" RELOAD_PROMPT = re.compile( re.escape("Reload hardware module ? [no,yes]")) START_TO_BACKUP = re.compile("Status report.*START TO BACKUP") BACKUP_HAS_COMPLETED_SUCCESSFULLY = re.compile( "Status report.*BACKUP HAS COMPLETED SUCCESSFULLY") DONE = re.compile(re.escape("[Done]")) CONSOLE = re.compile( "ios con[0|1]/(?:RS?P)?[0-1]/CPU0 is now available") CONFIGURATION_COMPLETED = re.compile("SYSTEM CONFIGURATION COMPLETED") CONFIGURATION_IN_PROCESS = re.compile( "SYSTEM CONFIGURATION IN PROCESS") BOOTING = re.compile( "Booting IOS-XR 64 bit Boot previously installed image") # events = [RELOAD_NA, DONE, PROCEED, CONFIGURATION_IN_PROCESS, self.rommon_re, self.press_return_re, # # 6 7 8 9 10 11 # CONSOLE, CONFIGURATION_COMPLETED, RECONFIGURE_USERNAME_PROMPT, TIMEOUT, EOF, self.reload_cmd, # # 12 13 14 # ROOT_USERNAME_PROMPT, ROOT_PASSWORD_PROMPT, CANDIDATE_BOOT_IMAGE] events = [ self.reload_cmd, RELOAD_PROMPT, START_TO_BACKUP, BACKUP_HAS_COMPLETED_SUCCESSFULLY, DONE, BOOTING, CONSOLE, self.press_return_re, CONFIGURATION_COMPLETED, CONFIGURATION_IN_PROCESS, EOF ] transitions = [ # do I really need to clean the cmd (RELOAD_PROMPT, [0], 1, partial(a_send_line, "yes"), 30), (START_TO_BACKUP, [1], 2, a_message_callback, 60), (BACKUP_HAS_COMPLETED_SUCCESSFULLY, [2], 3, a_message_callback, 10), (DONE, [3], 4, None, 600), (self.rommon_re, [0, 4], 5, partial(a_send_boot, "boot"), 600), (BOOTING, [0, 4], 5, a_message_callback, 600), (CONSOLE, [0, 5], 6, None, 600), (self.press_return_re, [6], 7, partial(a_send, "\r"), 300), (CONFIGURATION_IN_PROCESS, [7], 8, None, 180), (CONFIGURATION_COMPLETED, [8], -1, a_reconnect, 0), (EOF, [0, 1, 2, 3, 4, 5], -1, ConnectionError("Device disconnected"), 0), # (RELOAD_NA, [1], -1, a_reload_na, 0), # (DONE, [1], 2, None, 120), # (PROCEED, [2], 3, partial(a_send, "\r"), reload_timeout), # (self.rommon_re, [0, 3], 4, partial(a_send_boot, "boot"), 600), # (CANDIDATE_BOOT_IMAGE, [0, 3], 4, a_message_callback, 600), # (CONSOLE, [0, 1, 3, 4], 5, None, 600), # (self.press_return_re, [5], 6, partial(a_send, "\r"), 300), # # configure root username and password the same as used for device connection. # (RECONFIGURE_USERNAME_PROMPT, [6, 7], 8, None, 10), # (ROOT_USERNAME_PROMPT, [8], 9, partial(a_send_username, self.device.node_info.username), 1), # (ROOT_PASSWORD_PROMPT, [9], 9, partial(a_send_password, self.device.node_info.password), 1), # (CONFIGURATION_IN_PROCESS, [6, 9], 7, None, 180), # (CONFIGURATION_COMPLETED, [7], -1, a_reconnect, 0), # (TIMEOUT, [0, 1, 2], -1, ConnectionAuthenticationError("Unable to reload"), 0), # (EOF, [0, 1, 2, 3, 4, 5], -1, ConnectionError("Device disconnected"), 0), # (TIMEOUT, [6], 7, partial(a_send, "\r"), 180), # (TIMEOUT, [7], -1, ConnectionAuthenticationError("Unable to reconnect after reloading"), 0), ] fsm = FSM("RELOAD", self.device, events, transitions, timeout=600) return fsm.run()
def reload(self, reload_timeout, save_config): """Reload the device.""" MAX_BOOT_TIME = 1800 # 30 minutes - TODO(klstanie): move to config RELOAD_PROMPT = re.compile( re.escape("Reload hardware module ? [no,yes]")) START_TO_BACKUP = re.compile("Status report.*START TO BACKUP") BACKUP_IN_PROGRESS = re.compile("Status report.*BACKUP INPROGRESS") BACKUP_HAS_COMPLETED_SUCCESSFULLY = re.compile( "Status report.*BACKUP HAS COMPLETED SUCCESSFULLY") DONE = re.compile(re.escape("[Done]")) STAND_BY = re.compile("Please stand by while rebooting the system") CONSOLE = re.compile( "ios con[0|1]/(?:RS?P)?[0-1]/CPU0 is now available") CONFIGURATION_COMPLETED = re.compile("SYSTEM CONFIGURATION COMPLETED") CONFIGURATION_IN_PROCESS = re.compile( "SYSTEM CONFIGURATION IN PROCESS") BOOTING = re.compile( "Booting IOS-XR 64 bit Boot previously installed image") # 0 1 2 3 4 5 events = [ RELOAD_PROMPT, START_TO_BACKUP, BACKUP_IN_PROGRESS, BACKUP_HAS_COMPLETED_SUCCESSFULLY, DONE, BOOTING, # 6 7 8 9 10 CONSOLE, self.press_return_re, CONFIGURATION_COMPLETED, CONFIGURATION_IN_PROCESS, self.username_re, # 11 12 13 14 EOF, pexpect.TIMEOUT, self.rommon_re, STAND_BY ] transitions = [ # do I really need to clean the cmd (RELOAD_PROMPT, [0], 1, partial(a_send_line, "yes"), MAX_BOOT_TIME), (START_TO_BACKUP, [0, 1], 2, a_message_callback, 60), (BACKUP_IN_PROGRESS, [0, 1, 2], 2, a_message_callback, 60), (BACKUP_HAS_COMPLETED_SUCCESSFULLY, [0, 1, 2], 3, a_message_callback, 10), (DONE, [1, 2, 3], 4, None, MAX_BOOT_TIME), (STAND_BY, [2, 3, 4], 5, a_message_callback, MAX_BOOT_TIME), (self.rommon_re, [0, 4], 5, partial(a_send_boot, "boot"), MAX_BOOT_TIME), (BOOTING, [0, 1, 2, 3, 4], 5, a_message_callback, MAX_BOOT_TIME), (CONSOLE, [0, 1, 2, 3, 4, 5], 6, None, 600), (self.press_return_re, [6], 7, partial(a_send, "\r"), 300), (CONFIGURATION_IN_PROCESS, [7], 8, None, 180), (CONFIGURATION_COMPLETED, [8], -1, a_reconnect, 0), (self.username_re, [9], -1, a_return_and_reconnect, 0), (EOF, [0, 1, 2, 3, 4, 5], -1, ConnectionError("Device disconnected"), 0), (pexpect.TIMEOUT, [7], 9, partial(a_send, "\r"), 180), (pexpect.TIMEOUT, [1, 5, 8], -1, ConnectionError( "Boot process took more than {}s".format(MAX_BOOT_TIME)), 0), (pexpect.TIMEOUT, [9], -1, ConnectionAuthenticationError( "Unable to reconnect after reloading"), 0) ] fsm = FSM("RELOAD", self.device, events, transitions, timeout=600) return fsm.run()
def wait_for_string(self, expected_string, timeout=60): """Wait for string FSM for XR 64 bit.""" ADMIN_USERNAME_PROMPT = re.compile("Admin Username:"******"Password:"******"Expecting: {}".format(pattern_to_str(expected_string))) self.log("Calvados prompt: {}".format(pattern_to_str( self.calvados_re))) transitions = [ (ADMIN_USERNAME_PROMPT, [0], 6, partial(a_send_username, self.device.node_info.username), 5), (ADMIN_PASSWORD_PROMPT, [0, 6], 0, partial(a_send_password, self.device.node_info.password), 5), (self.authentication_error_re, [0], -1, ConnectionAuthenticationError("Admin plane authentication failed", self.device.hostname), 0), (self.syntax_error_re, [0], -1, CommandSyntaxError("Command unknown", self.device.hostname), 0), (self.connection_closed_re, [0], 1, a_connection_closed, 10), (pexpect.TIMEOUT, [0, 2], -1, CommandTimeoutError("Timeout waiting for prompt", self.device.hostname), 0), (pexpect.EOF, [0, 1], -1, ConnectionError("Unexpected device disconnect", self.device.hostname), 0), (self.more_re, [0], 0, partial(a_send, " "), 10), (expected_string, [0, 1], -1, a_expected_prompt, 0), (self.calvados_re, [0], -1, a_expected_prompt, 0), (self.press_return_re, [0], -1, a_stays_connected, 0), (self.calvados_connect_re, [0], 2, None, 0), # admin command to switch to calvados (self.calvados_re, [2], 3, None, _C['calvados_term_wait_time']), # getting the prompt only (pexpect.TIMEOUT, [3], 0, partial(a_send, "\r\r"), timeout), # term len (self.calvados_term_length, [3], 4, None, 0), # ignore for command start (self.calvados_re, [4], 5, None, 0), # ignore for command start (self.calvados_re, [5], 0, a_store_cmd_result, 0), ] for prompt in self.device.get_previous_prompts(): transitions.append((prompt, [0, 1], 0, a_unexpected_prompt, 0)) fsm = FSM("WAIT-4-STRING", self.device, events, transitions, timeout=timeout) return fsm.run()
def connect(self, driver): """Connect using the Telnet protocol specific FSM.""" # 0 1 2 3 events = [ ESCAPE_CHAR, driver.press_return_re, driver.standby_re, driver.username_re, # 4 5 6 7 driver.password_re, driver.more_re, self.device.prompt_re, driver.rommon_re, # 8 9 10 11 12 driver.unable_to_connect_re, driver.timeout_re, pexpect.TIMEOUT, PASSWORD_OK, driver.syntax_error_re ] transitions = [ (ESCAPE_CHAR, [0], 1, None, _C['esc_char_timeout']), (driver.syntax_error_re, [0], -1, CommandSyntaxError("Command syntax error"), 0), (driver.press_return_re, [0, 1], 1, a_send_newline, 10), (PASSWORD_OK, [0, 1], 1, a_send_newline, 10), (driver.standby_re, [0, 5], -1, partial(a_standby_console), 0), (driver.username_re, [0, 1, 5, 6], -1, partial(a_save_last_pattern, self), 0), (driver.password_re, [0, 1, 5], -1, partial(a_save_last_pattern, self), 0), (driver.more_re, [0, 5], 7, partial(a_send, "q"), 10), # router sends it again to delete (driver.more_re, [7], 8, None, 10), # (prompt, [0, 1, 5], 6, a_send_newline, 10), (self.device.prompt_re, [0, 1, 5], 0, None, 10), (self.device.prompt_re, [6, 8, 5], -1, partial(a_save_last_pattern, self), 0), (driver.rommon_re, [0, 1, 5], -1, partial(a_save_last_pattern, self), 0), (driver.unable_to_connect_re, [0, 1, 5], -1, a_unable_to_connect, 0), (driver.timeout_re, [0, 1, 5], -1, ConnectionTimeoutError("Connection Timeout", self.hostname), 0), (pexpect.TIMEOUT, [0, 1], 5, a_send_newline, 10), (pexpect.TIMEOUT, [5], -1, ConnectionTimeoutError("Connection timeout", self.hostname), 0) ] self.log("EXPECTED_PROMPT={}".format( pattern_to_str(self.device.prompt_re))) # setting max_transitions to large number to swallow prompt like strings from prompt fsm = FSM("TELNET-CONNECT", self.device, events, transitions, timeout=_C['connect_timeout'], init_pattern=self.last_pattern, max_transitions=500) return fsm.run()