def device_recovery(start, device, console_activity_pattern, golden_image=None, break_count=10, timeout=600, recovery_password=None, tftp_boot=None, item=None, **kwargs): ''' A method for starting Spawns and handling the device statements during recovery Args: device ('obj'): Device object start ('obj'): Start method under device object console_activity_pattern ('str'): Pattern to send the break at golden_image ('dict'): information to load golden image on the device break_count ('int'): Number of sending break times timeout ('int'): Recovery process timeout recovery_password ('str'): Device password after recovery Returns: None ''' # Set a target for each recovery session # so it's easier to distinguish expect debug logs on the console. device.instantiate(connection_timeout=timeout) # Get device console port information last_word_in_start_match = re.match('.*\s(\S+)$', start) last_word_in_start = last_word_in_start_match.group(1) \ if last_word_in_start_match else "" # Set target target = "{}_{}".format(device.hostname, last_word_in_start) logfile = log.handlers[1].logfile if len(log.handlers) >= 2 else None spawn = Spawn(spawn_command=start, settings=device.cli.settings, target=target, log=log, logfile=logfile) break_dialog = BreakBootDialog() break_dialog.add_statement(Statement(pattern=console_activity_pattern, action=sendbrk_handler, args={'break_count': break_count}, loop_continue=True, continue_timer=False), pos=0) break_dialog.dialog.process(spawn, timeout=timeout) dialog = RommonDialog() dialog.dialog.process(spawn, timeout=timeout, context={ 'boot_image': golden_image[0], 'break_count': break_count, 'password': recovery_password }) spawn.close()
def golden_recovery(start, device, console_activity_pattern, golden_image=None, break_count=10, timeout=600, recovery_password=None, tftp_boot=None, item=None): ''' A method for starting Spawns and handling the device statements during recovery Args: device ('obj'): Device object start ('obj'): Start method under device object console_activity_pattern ('str'): Pattern to send the break at golden_image ('str'): Image to load the device with break_count ('int'): Number of sending break times timeout ('int'): Recovery process timeout recovery_password ('str'): Device password after recovery Returns: None ''' break_dialog = BreakBootDialog() break_dialog.add_statement(Statement(pattern=console_activity_pattern, action=sendbrk_handler, args={'break_count': break_count}, loop_continue=True, continue_timer=False), pos=0) # Set a target for each recovery session # so it's easier to distinguish expect debug logs on the console. device.instantiate(connection_timeout=timeout) # Get device console port information last_word_in_start_match = re.match('.*\s(\S+)$', start) last_word_in_start = last_word_in_start_match.group(1) \ if last_word_in_start_match else "" # Set target target = "{}_{}".format(device.hostname, last_word_in_start) spawn = Spawn(start, settings=device.cli.settings, target=target, log=log, logfile=log.handlers[1].logfile) if 'system' not in golden_image: raise Exception("System image has not been provided in the " "'device_recovery' section of the clean YAML") dialog = RommonDialog() dialog.dialog.process(spawn, context={ 'sys': golden_image.get('system'), 'password': recovery_password }, timeout=timeout) spawn.close()
def tftp_recovery_worker(start, device, console_activity_pattern, tftp_boot=None, break_count=10, timeout=600, recovery_password=None, golden_image=None, item=None): ''' A method for starting Spawns and handling the device statements during recovery Args: device ('obj'): Device object start ('obj'): Start method under device object console_activity_pattern ('str'): Pattern to send the break at tftp_boot ('dict'): Tftp boot information break_count ('int'): Number of sending break times timeout ('int'): Recovery process timeout recovery_password ('str'): Device password after recovery Returns: None ''' log.info('Set the device in rommon and load the device with tftp boot') break_dialog = BreakBootDialog() break_dialog.add_statement(Statement(pattern=console_activity_pattern, action=sendbrk_handler, args={'break_count':break_count}, loop_continue=True, continue_timer=False), pos=0) # Set a target for each recovery session # so it's easier to distinguish expect debug logs on the console. device.instantiate(connection_timeout=timeout) # Get device console port information last_word_in_start_match = re.match('.*\s(\S+)$', start) last_word_in_start = last_word_in_start_match.group(1) \ if last_word_in_start_match else "" # Set target target = "{}_{}".format(device.hostname, last_word_in_start) spawn = Spawn(spawn_command=start, settings=device.cli.settings, target=target, log=log, logfile=log.handlers[1].logfile) rommon_dialog = TftpRommonDialog() rommon_dialog.hostname_statement(device.hostname) rommon_dialog.dialog.process(spawn, timeout=timeout, context={'device_name': device.name, 'ip': tftp_boot['ip_address'][item], 'password': recovery_password, 'subnet_mask': tftp_boot['subnet_mask'], 'gateway': tftp_boot['gateway'], 'image': tftp_boot['image'], 'tftp_server': tftp_boot['tftp_server'], 'hostname': device.hostname}) spawn.close()
def recovery_worker(start, device, console_activity_pattern, break_count=10, timeout=600, *args, **kwargs): """ Starts a Spawn and processes device dialogs during recovery of a device Args: start (obj): Start method under device object device (obj): Device object console_activity_pattern (str): Pattern to send the break at break_count (int, optional): Number of break commands to send. Defaults to 10. timeout (int, optional): Recovery process timeout. Defaults to 600. Returns: None """ def breakboot(spawn, break_count): """ Breaks the booting process on a device Args: spawn (obj): Spawn connection object break_count (int): Number of break commands to send Returns: None """ log.info("Found the console_activity_pattern! " "Breaking the boot process") for _ in range(break_count): # '\x03' == <ctrl>+C spawn.send("\x03") time.sleep(1) # Set a target for each recovery session # so it's easier to distinguish expect debug logs on the console. device.instantiate(connection_timeout=timeout) # Get device console port information last_word_in_start_match = re.match('.*\s(\S+)$', start) last_word_in_start = last_word_in_start_match.group(1) \ if last_word_in_start_match else "" # Set target target = "{}_{}".format(device.hostname, last_word_in_start) if len(log.handlers) >= 2: logfile = log.handlers[1].logfile else: logfile = None spawn = Spawn(spawn_command=start, settings=device.cli.settings, target=target, log=log, logfile=logfile) # Stop the device from booting break_dialog = BreakBootDialog() break_dialog.add_statement(Statement(pattern=console_activity_pattern, action=breakboot, args={'break_count': break_count}, loop_continue=True, continue_timer=False), pos=0) break_dialog.dialog.process(spawn, timeout=timeout) # Recover the device using the specified method if kwargs.get('golden_image'): device_recovery(spawn, timeout, *args, **kwargs) elif kwargs.get('tftp_boot'): tftp_device_recovery(spawn, timeout, device, *args, **kwargs) spawn.close()
# this is first entry hence we need to send login password. session.flag = True spawn.sendline(loginpw) else: # if we come here that means it is second entry and here. # we need to send the enable password. spawn.sendline(enablepw) # construct the dialog. # here we see how shorthand notation can make the code look leaner. d = Dialog([ [r'enter to continue \.\.\.', 'sendline()', None, True, False], [r'username:\s?$', "sendline(admin)", None, True, False], [ r'password:\s?$', send_passwd, { 'enablepw': 'lablab', 'loginpw': 'lab' }, True, False ], [disable_prompt, 'sendline(enable)', None, True, False], [enable_prompt, None, None, False, False], ]) s = Spawn(router_command) # at this stage we are anticipating the program to wait for a new line d.process(s) s.close()
def is_available(host, port, user=DEFAULT_USERNAME, pwd=DEFAULT_PASSWORD, prompt='firepower login:'******'telnet'): """Checks whether device is available. :param host: Ip of the device/console :param port: console port :param user: username :param pwd: password :param prompt: expected prompt :param access: type of access: telnet or ssh :return: True if device is available, False if it's not """ if user == DEFAULT_USERNAME: user = get_username(user) if pwd == DEFAULT_PASSWORD: pwd = get_password(pwd) VALID_PROMPTS.append(prompt) if access == 'telnet': spawn_id = Spawn('telnet {} {}\n'.format(host, port)) try: spawn_id.expect( "Connected to.*Escape character is '\^\]'\..*Username: "******"Connected to.*Escape character is '\^\]'\..*Username: "******"Password: "******"Password OK.*") except TimeoutError: LOGGER.debug("'Password OK' message did not appear ... continue") spawn_id.sendline('') try: __wait_for_rommon(spawn_id, 900) except: LOGGER.info("\nFailed to get a valid prompt") spawn_id.close() return False LOGGER.info('%s on port %d is available' % (host, port)) spawn_id.close() elif access == 'ssh': try: if port is not 22: clear_line(host=host, port=port % 100, access='ssh', user=user, pwd=pwd, en_password=pwd) spawn_id = Spawn('ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l {} -p {} {}'. format(user, port, host)) d1 = Dialog([ ['continue connecting (yes/no)?', 'sendline(yes)', None, True, False], ['(P|p)assword:', 'sendline({})'.format(pwd), None, False, False], ['Connection refused', None, None, False, False], ]) d1.process(spawn_id, timeout=60) try: spawn_id.expect("Password OK.*") except: pass spawn_id.sendline() time.sleep(10) try: __wait_for_rommon(spawn_id, 900) except: LOGGER.info("\nFailed to get a valid prompt") spawn_id.close() return False LOGGER.info('%s on port %d is available' % (host, port)) spawn_id.close() except: return False else: raise RuntimeError('Device can be accessed only by telnet or ssh') return True
def clear_line(host, port, user=DEFAULT_USERNAME, pwd=DEFAULT_PASSWORD, prompt='#', access='telnet', en_password=DEFAULT_ENPASSWORD, timeout=None): """Clear line corresponding to a device; this is required because only a single console connection is available. If somebody or some process failed to close the connection, it should be cleared explicitly. This function accepts only ssh and telnet connections. :param host: ip address of terminal server :param port: device line number in terminal server to be cleared for example, if port 2005 is mapped to line 5, port=5 :param user: username :param pwd: password :param prompt: expected prompt after logging in :param access: ssh or telnet; default is set to telnet :param en_password: enable password to switch to line configuration mode :param timeout: how long the connection and authentication would take in seconds; if not provided, default is 60s :return: None """ if user == DEFAULT_USERNAME: user = get_username(user) if pwd == DEFAULT_PASSWORD: pwd = get_password(pwd) if en_password == DEFAULT_ENPASSWORD: en_password = get_password(en_password) if not timeout: timeout = DEFAULT_TIMEOUT d1 = None spawn = None # establish a connection to the terminal server if access == 'telnet': spawn = Spawn('telnet {} {}'.format(host, '23')) d1 = Dialog([ ["Connected to.*Escape character is '\^\]'\.", 'sendline()', None, True, False], ['.*Username:'******'sendline({})'.format(user), None, True, False], ['(p|P)assword:', 'sendline({})'.format(pwd), None, True, True], [prompt, 'sendline()', None, False, False], ['>', 'sendline()', None, False, False], ]) elif access == 'ssh': spawn = Spawn('ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no ' '-l {} -p {} {}'.format(user, '22', host)) d1 = Dialog([ ['continue connecting (yes/no)?', 'sendline(yes)', None, True, False], ['(p|P)assword:', 'sendline({})'.format(pwd), None, False, False], ]) else: LOGGER.error('Unknown protocol: Telnet or ssh supported only') try: LOGGER.info('Trying to connect to {}'.format(host)) d1.process(spawn, timeout=timeout) try: spawn.expect("Password OK.*") except TimeoutError: LOGGER.info("'Password OK' message didn't appear") pass spawn.sendline() except TimeoutError: LOGGER.error('Failed to connect to terminal server') raise Exception('Failed to connect to terminal server') # clear port section try: spawn.expect('#') except: # expect > spawn.sendline('en') try: spawn.expect('Password:'******'detected line number for clearing: {} from port {}'. format(line_id, port)) if line_id: spawn.sendline('clear line {}'.format(line_id)) spawn.expect('[confirm]') spawn.sendline('') spawn.expect('[OK]') LOGGER.info('line: {} was cleared'.format(port)) spawn.close() except TimeoutError: spawn.close() LOGGER.error('Line: {} was not cleared'.format(port)) raise Exception('Line {} was NOT cleared'.format(port))