import os from unicon.eal.expect import Spawn, TimeoutError from unicon.eal.dialogs import Statement, Dialog router_command = os.path.join(os.getcwd(), 'router.sh') prompt = 'sim-router' enable_prompt = prompt + '#' disable_prompt = prompt + '>' # construct the dialog d = Dialog([ [r'enter to continue \.\.\.', lambda spawn: spawn.sendline(), None, True, False], [r'username:\s?$', lambda spawn: spawn.sendline("admin"), None, True, False], [r'password:\s?$', lambda spawn: spawn.sendline("lab"), None, True, False], [disable_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 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) 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) 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 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))
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 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 ('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 ''' # 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(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) if 'kickstart' not in golden_image or 'system' not in golden_image: raise Exception( "Either Kickstart or System image has not been provided " "in the 'device_recovery' section of clean YAML") dialog = RommonDialog() dialog.dialog.process(spawn, context={ 'kick': golden_image.get('kickstart'), 'sys': golden_image.get('system'), 'password': recovery_password }, timeout=timeout) 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) # TODO: We need to fix that handlers[1]... # Make it stronger. # For now, if it doesnt exists, then just get out try: logfile = log.handlers[1].logfile except IndexError as e: raise Exception('Could not recover the device') spawn = Spawn(start, settings=device.cli.settings, target=target, log=log, logfile=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()
from unicon.eal.dialogs import Statement, Dialog from unicon.eal.dialog_processor import DialogProcessor from inspect import getfullargspec as spec from unicon.eal.expect import Spawn from unicon.utils import AttributeDict start1 = 'ls' start2 = 'ps' s1 = Spawn(start1) # s2 = Spawn(start2) pattern = '^pattern$' def cb1(): print('cb with no args') def cb2(spawn, context): print('spawn=%s; context=%s' % (spawn, context)) def cb3(spawn, context, session, name): session.check = 'yes it works' session.num = 10 print('spawn=%s; context=%s, session=%s; name=%s' % (spawn, context, session, name)) def cb4(spawn, city='bangalore'): print('spawn=%s; city=%s' % (spawn, city))
def recovery_worker(start, device, console_activity_pattern=None, console_breakboot_char=None, grub_activity_pattern=None, grub_breakboot_char=None, 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 console_breakboot_char (str): Character to send when console_activity_pattern is matched grub_activity_pattern (str): Break pattern on the device for grub boot mode grub_breakboot_char (str): Character to send when grub_activity_pattern is matched 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 console_breakboot(spawn, break_count, break_char): """ Breaks the booting process on a device Args: spawn (obj): Spawn connection object break_count (int): Number of break commands to send break_char (str): Char to send Returns: None """ log.info( f"Found the console_activity_pattern! Breaking the boot process.") for _ in range(break_count): log.info(f"Sending {repr(break_char)}") spawn.send(break_char) time.sleep(1) def grub_breakboot(spawn, break_char): """ Breaks the booting process on a device Args: spawn (obj): Spawn connection object break_char (str): Char to send Returns: None """ log.info(f"Found the grub_activity_pattern! Breaking the boot process " f"by sending {repr(break_char)}") spawn.send(break_char) # 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() if console_activity_pattern and console_breakboot_char: break_dialog.add_statement(Statement(pattern=console_activity_pattern, action=console_breakboot, args={ 'break_count': break_count, 'break_char': console_breakboot_char }, loop_continue=True, continue_timer=False), pos=0) if grub_activity_pattern and grub_breakboot_char: break_dialog.add_statement(Statement( pattern=grub_activity_pattern, action=grub_breakboot, args={'break_char': grub_breakboot_char}, 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()
import os from unicon.eal.expect import Spawn from time import sleep sec = 0.1 print('spawning processes .....') s1 = Spawn("telnet 10.64.70.24 2020") s2 = Spawn("telnet 10.64.70.24 2019") def waitpid(): print('\ns1> waitpid for s1') ret = os.waitpid(s1.pid, os.WNOHANG) print(ret) print('s2> waitpid for s2') ret = os.waitpid(s2.pid, os.WNOHANG) print(ret) def process(): print('\ns1> ps command output') os.system("ps -eaf | grep -i %s| grep -v grep" % s1.pid) print('s2> ps command output') os.system("ps -eaf | grep -i %s| grep -v grep" % s2.pid) print('sleeping for %s seconds ....' % sec) sleep(sec) print('\ns1> expect output ....') s1.expect(r'.*')