def cli(self, commands): output = {} try: for cmd in commands: output[cmd] = self.device.send_command(cmd) return output except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e))
def _check_expert_mode(self) -> bool: # will break if PS1 is altered - not everything possible should be done...... try: rhostname = self.device.find_prompt() regex = r'\[Expert@.*$' if re.search(regex, rhostname): return True else: return False except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e)) except Exception as e: raise RuntimeError(e)
def _send_command(self, command): """ Wrapper for self.device.send.command(). If command is a list will iterate through commands until valid command. """ try: if isinstance(command, list): for cmd in command: output = self.device.send_command_timing(cmd) if 'Command fail. Return code' not in output: break else: output = self.device.send_command_timing(command) return output except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e))
def send_clish_cmd(self, cmd: str) -> str: """ send clish command :param cmd: str :return: (str) """ try: output = self.device.send_command(cmd) return output except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e)) except Exception as e: raise RuntimeError(e)
def _check_vsx_state(self) -> bool: """ :return: bool """ vsx_regex = r'^\|.\d+\|' command = 'cpstat -f stat vsx' try: output = self.device.send_command(command) if re.search(vsx_regex, output, re.M): return True else: return False except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e))
def is_alive(self): """Check if device connection is alive.""" """check if session cookie is still valid Returns: True - Session cookie is still valid None - There's an error """ url = self._api_url + 'system' endpoint = self._apisession.get(url) if endpoint.status_code == 200: "Session cookie is still valid" return {"is_alive": True} else: raise ConnectionClosedException("HTTP session is closed")
def get_interfaces_ip(self): """ | Get interface ip details. | Returns a dict of dicts :return: dict example:: { u'FastEthernet8': { 'ipv4': { u'10.66.43.169': { 'prefix_length': 22}}}, u'Loopback555': { 'ipv4': { u'192.168.1.1': { 'prefix_length': 24}}, 'ipv6': { u'1::1': { 'prefix_length': 64}}}, u'Tunnel0': { 'ipv4': { u'10.63.100.9': { 'prefix_length': 24}}}, u'Tunnel1': { 'ipv4': { u'10.63.101.9': { 'prefix_length': 24}}}, u'Vlan100': { 'ipv4': { u'10.65.0.1': { 'prefix_length': 24}}}, u'Vlan200': { 'ipv4': { u'10.63.176.57': { 'prefix_length': 29}}}} """ RE_NICDATA = {'ipv4': re.compile('\s+ipv4-ad\w+\s([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3})/([0-9]{1,3})$'), 'ipv6': re.compile(r'\s+ipv6-(\w+-\w+-)?\w+\s(.*)/([0-9]{1,3})$') } interface_table = {} try: self.device.send_command('set clienv rows 0') output = self.device.send_command('show interfaces all') except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e)) output = str(output).split('Interface ') for item in output: if len(item) == 0: output.remove(item) for item in output: item = str(item).splitlines() # set entry defaults for line in item: if re.match(RE_NICDATA['ipv4'], line) is not None: if item[0] not in interface_table: interface_table[item[0]] = {} ip_addr = re.findall(RE_NICDATA['ipv4'], line) interface_table[item[0]]['ipv4'] = {ip_addr[0][0]: {'prefix_length': ip_addr[0][1]}} if re.match(RE_NICDATA['ipv6'], line) is not None: if item[0] not in interface_table: interface_table[item[0]] = {} ip_addr = re.findall(RE_NICDATA['ipv6'], line) if len(ip_addr[0]) == 2: interface_table[item[0]]['ipv6'] = {ip_addr[0][0]: {'prefix_length': ip_addr[0][1]}} else: interface_table[item[0]]['ipv6'] = {ip_addr[0][1]: {'prefix_length': ip_addr[0][2]}} return interface_table
def _send_command(self, command): """Wrapper for self.device.send.command(). If command is a list will iterate through commands until valid command. """ try: if isinstance(command, list): for cmd in command: output = self.device.send_command(cmd) if "bad command" not in output: break else: output = self.device.send_command(command) return self._send_command_postprocess(output) except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e))
def _enter_expert_mode(self) -> bool: """ :return: bool """ try: if self._check_expert_mode() is False: self.device.send_command('expert', expect_string=r':') output = self.device.send_command_timing(self.expert_password) if r']#' in output: self.device.set_base_prompt(r'#') self.device.send_command(r'unset TMOUT') return self._check_expert_mode() except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e)) except Exception as e: raise RuntimeError(e)
def _validate_ping_source(self, source: str): source_interfaces = [] try: output = self.device.send_command_timing('show interfaces\t') except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e)) interface_list = output.split() for interface in interface_list: output = self.device.send_command('show interface {0} ipv4-address'.format(interface)) mobj = re.match(r'.*ipv4-address\s*(.*)/.*', output) if mobj is not None: source_interfaces.append(mobj.group(1)) output = self.device.send_command('show interface {0} ipv6-address'.format(interface)) mobj = re.match(r'.*ipv6-address\s*(.*)/.*', output) source_interfaces.append(interface) if source not in source_interfaces: raise ValueError('invalid source')
def _exit_expert_mode(self) -> bool: """ :return: bool """ try: if self._check_expert_mode() is True: self.device.send_command('exit', expect_string=r'>') if self._check_expert_mode() is False: return True else: return False else: return True except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e)) except Exception as e: raise RuntimeError(e)
def get_virtual_systems(self) -> dict: """ | Get virtual systems information. | Returns a dictionary with configured virtual systems. | The keys of the main dictionary represents virtual system ID. | The values represent the detail of the virtual system, | represeted by the following keys. | * type (str) | * name (str) | * policy (str) | * sic (str) :return: dict example:: { 0: {' 'type': 'VSX Gateway', 'name': 'dummy_vsx_gw', 'policy': 'dummy_policy' 'sic': 'Trust established' } } """ try: if self._check_vsx_state() is True: vs_regex = r'\|(.\d+)\|([A-z0-9-_]+\s.\w+)+(?:\s+\||\|)+([A-z0-9-_]+)+' \ r'(?:\s+\||\|)+([A-z0-9_-]+)+(?:\s+\||\|)+(.*)(?:\|)' command = 'cpstat -f stat vsx' output = self.device.send_command(command) vs = {} for match in re.finditer(vs_regex, output, re.M): vs[match.group(1)] = { 'type': match.group(2), 'name': match.group(3), 'policy': match.group(4), 'sic': match.group(5) } return vs else: raise ValidationException('VSX not enabled') except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e))
def cli(self, commands: list) -> dict: """ | Will execute a list of commands and return the output in a dictionary format. | Works only for cli.sh commands. Example:: { 'show version product': 'Product version Check Point Gaia R80.20', 'show route': ''' Codes: C - Connected, S - Static, R - RIP, B - BGP (D - Default), O - OSPF IntraArea (IA - InterArea, E - External, N - NSSA) A - Aggregate, K - Kernel Remnant, H - Hidden, P - Suppressed, U - Unreachable, i - Inactive S 0.0.0.0/0 via 172.16.10.1, eth0, cost 0, age 57785 C 127.0.0.0/8 is directly connected, lo C 172.16.10.0/26 is directly connected, eth0''' } """ output = {} try: if isinstance(commands, list): for cmd in commands: if isinstance(cmd, str): output[cmd] = self.device.send_command(cmd) else: raise TypeError( 'Expected <class \'str\'> not a {}'.format( type(cmd) )) else: raise TypeError( 'Expected <class \'list\'> not a {}'.format( type(commands) ) ) return output except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e))
def _set_virtual_system(self, vsid: int) -> bool: """ | Switch to VSX context. Raises RuntimeError if failed. :return: bool """ try: if self._check_vsx_state() is True: if self._check_expert_mode() is True: command = 'vsenv {}'.format(vsid) else: command = 'set virtual-system {}'.format(vsid) vsid_regex = r'(?<=:){}'.format(vsid) expect_regex = r'(?<=:)\d+' output = self.device.send_command(command, expect_regex) if re.search(vsid_regex, output): return True else: raise CommandErrorException('cannot access virtual-system') else: raise ValidationException('VSX not enabled') except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e))
def get_config(self, retrieve='all') -> dict: """ | Get host configuration. Returns a string delimited with a '\n' for further parsing. | Configuration can be retrieved at once or as logical part. :return: dict For example:: device.get_config(retrieve='user') { 'running': ' set user admin shell /etc/cli.sh set user admin password-hash $1$aWTXGUmr$1r1Ls428oJg2gFwMcKJdO0 set user monitor shell /etc/cli.sh set user monitor password-hash * ', 'candidate': '', 'startup' : '' } Retrieve options:: all - display full configuration aaa - display aaa configuration commands aggregate - Display Route Aggregation configuration commands allowed-client - Displays Allowed Clients configuration arp - Display ARP configuration commands as - Show Autonomous System Number configuration commands backup-scheduled - Display scheduled backup configuration commands bgp - Display BGP configuration commands bonding - display bonding configuration commands bootp - Show BOOTP/DHCP Relay configuration commands bridging - display bridging configuration commands clienv - display CLI environment configuration commands command - extended commands configuration commands core-dump - Display core-dump configuration commands cron - display cron configuration commands dhcp-client - display dhcp client configuration commands dhcp-server - display dhcp configuration commands dns - display dns configuration commands domainname - display domainname configuration commands edition - display edition configuration commands expert-password - Displays expert password configuration format - display format configuration commands group - display group configuration commands host - Display host configuration commands hostname - Display hostname configuration commands igmp - Display IGMP configuration commands inbound-route-filter - Display Inbound Route Filter configuration commands installer - installer configuration commands interface - interface configuration commands interface-name - Interface naming configuration commands iphelper - Display IP Broadcast Helper configuration commands ipv6 - Display IPv6 routing configuration commands ipv6-state - Display IPv6 configuration commands kernel-routes - Show configuration commands for kernel routes lcd - display lcd configuration commands mail-notification - display format configuration commands management - management configuration commands max-path-splits - Show max-path-splits configuration commands message - Display message configuration commands net-access - Displays network access configuration netflow - netflow configuration commands ntp - display ntp configuration commands ospf - Display OSPFv2 configuration commands password-controls - display password-controls configuration commands pim - Display PIM configuration commands ping - Display ping (for static routes) configuration commands protocol-rank - Show protocol ranks configuration commands proxy - display proxy configuration commands rba - Display rba configuration commands rdisc - Display ICMP Router Discovery configuration commands rip - Display RIP configuration commands route-redistribution - Display route redistribution configuration commands routedsyslog - Show Routing Daemon syslog configuration commands routemap - Display configuration commands for a specific Route Map routemaps - Display Route Map configuration commands router-id - Show Router ID configuration commands router-options - Show Router Options configuration commands snmp - SNMP configuration commands static-mroute - Display static multicast route configuration commands static-route - Display IPv4 static route configuration commands syslog - Display syslog configuration commands timezone - Timezone configuration commands trace - Show Trace configuration commands tracefile - Show Tracefile configuration commands user - Display user configuration commands vpnt - Display VPN tunnel configuration web - Displays Web configuration """ opt = retrieve.lower() command = 'show configuration' if opt != 'all': command += ' {}'.format(opt) try: output = self.device.send_command(command) retdict = {'running': output, 'candidate': '', 'startup': ''} except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e)) except Exception as e: RuntimeError(e) return retdict
def get_firewall_policy(self, interfaces=False) -> dict: """ | Gets firewall policy information. Returns a dict with the following keys. | * name (str) | * install_time (str) | * current_conns (int) | * peak_conns (int) | * conns_limit (int) | | With optional parameter 'interfaces' returns nested dict with the following keys. | * iftab32 (dict) | * iftab64 (dict) | * <interface name> (dict) | * in (dict) | * accept (int) | * drop (int) | * reject (int) | * log (int) | * out (dict) | * accept (int) | * drop (int) | * reject (int) | * log (int) :param interfaces: bool :return: dict example:: { 'name': 'policy', 'install_time': 'Wed Mar 1 00:00:00 2020', 'current_conns': '0', 'peak_conns': '0', 'conns_limit': '0', 'if_tab_32': { 'bond0': { 'in': { 'accept': '0', 'drop': '0', 'reject': '0', 'log': '0' }, 'out': { 'accept': '0', 'drop': '0', 'reject': '0', 'log': '0' } } 'if_tab_64': { 'bond0': { 'in': { 'accept': '0', 'drop': '0', 'reject': '0', 'log': '0' }, 'out': { 'accept': '0', 'drop': '0', 'reject': '0', 'log': '0' } } } """ regex = r'.*\snot a FireWall-1 module' command = 'fw stat' output = self.device.send_command(command) if re.match(regex, output) is not None: raise ValueError('firewall module not enabled') try: policy_regex = r'([A-z. ]+)(?:\:)(?:\s+)([A-z0-9-_:\ ]+)' policy_if_regex = r'^(?:\|)([A-z0-9.]+)(?:\s+\||\|)([A-z]+)' \ r'(?:\s+\||\|)(?:\s+|)(\d+)(?:\s+\||\|\s+|\|)' \ r'(\d+)(?:\s+\||\|\s+|\|)(\d+)(?:\s+\||\|\s+|\|)(\d+)' command = 'cpstat -f policy fw' output = self.device.send_command(command) policy_list = [] for match in re.finditer(policy_regex, output, re.M): policy_list.append(match.group(2)) policy = { 'name': str(policy_list[1]), 'install_time': str(policy_list[2]), 'current_conns': int(policy_list[3]), 'peak_conns': int(policy_list[4]), 'conns_limit': int(policy_list[5]) } if interfaces is True: for match in re.finditer(policy_if_regex, output, re.M): counters = { 'accept': int(match.group(4)), 'drop': int(match.group(5)), 'reject': int(match.group(6)), 'log': int(match.group(7)) } if match.group(1) is None: if match.group(2) not in policy[iftab]: policy[iftab][match.group(2)] = {} policy[iftab][match.group(2)][match.group(3)] = counters else: iftab = 'iftab64' if re.sub(r'\D', '', match.group(1)) == '64' else 'iftab32' policy[iftab] = {} return policy except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e)) except Exception as e: raise RuntimeError(str(e))
def get_users(self, **kwargs) -> dict: """ | Returns a dictionary with the configured users. | The keys of the main dictionary represents the username. | Checkpoint uses RBAC and does not know about privilege levels | therefore level always returns a level of 15 | instead a field 'privileges' was added containing additional user-role information | | ssh-keys will only fetched with option retrieve='all' (requires expert password) | otherwise ssh-keys will return a list containing an empty string | The values represent the details of the user, | represented by the following keys: * uid: int * gid: int * homedir: str * shell: str * name: str * privileges: str * sshkeys: list[str,] * level : 15, :return: dict example:: { 'admin': { 'uid': '0', 'gid': '0', 'homedir': '/home/admin', 'shell': '/etc/cli.sh', 'name': 'n/a', 'privileges': 'Access to Expert features'}, 'level' : 15, 'password' : '$1$aWTXGUmr$1r1Ls428oJg2gFwMcKJdO0' 'sshkeys' : ['',]} 'monitor': { 'uid': '102', 'gid': '100', 'homedir': '/home/monitor', 'shell': '/etc/cli.sh', 'name': 'Monitor', 'privileges': 'None', 'level' : 15, 'password' : '*' 'sshkeys' : ['',]} } """ username_regex = ( r'^([A-z0-9_-]{1,32})\s+(\d+)\s+(\d+)\s+([/A-z0-9_-]{1,})\s+' r'([./A-z0-9_-]{1,})\s+((?:[\/A-z0-9_-]+\s?)+[\/A-z0-9_-])\s+' r'((?:[\/A-z0-9_-]+\s?)+[\/A-z0-9_-]).+?$' ) pwdhash_regex = ( r'^[a-z]{3}\s[a-z]{4}\s([A-z][A-z0-9_-]+)\s.*hash\s(.*)$' ) users = {} command = 'show users' try: output = self.device.send_command(command) for match in re.finditer(username_regex, output, re.M): users[match.group(1)] = { 'uid': match.group(2), 'gid': match.group(3), 'homedir': match.group(4), 'shell': match.group(5), 'name': match.group(6), 'privileges': match.group(7), } command = 'show configuration user' output = self.device.send_command(command) for match in re.finditer(pwdhash_regex, output, re.M): users[match.group(1)]['password'] = match.group(2) if 'retrieve' in kwargs: if kwargs['retrieve'] == 'all': if self._enter_expert_mode() is True: files = ['authorized_keys', 'authorized_keys2'] for user in users: users[user]['sshkeys'] = [] i = False for file in files: command = r'cat /home/{0}/.ssh/{1}'.format(user, file) output = self.device.send_command(command) if re.match(r'cat.*$', output) is None and re.match(r'$', output) is None: users[user]['sshkeys'].append(str(output.split('\n'))) i = True else: pass if i is False: users[user]['sshkeys'].append('') else: raise RuntimeError('unable to enter expert-mode') else: for user in users: users[user]['sshkeys'] = [''] return users except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e)) except Exception as e: raise RuntimeError(e)
def ping(self, destination: str, **opts) -> dict: """ | ping destination from device | vsx/vrf is currently not supported, neither is setting timeout :param destination: str :param opts: dict | opts: * source: (<interface|ip-address>: str) * ttl: (1-255: int) * timeout: None * vrf: None * size: (8-65507: int) * count: (1-1000: int) example:: { 'source': 'eth0', 'ttl': 30, 'timeout': None, 'size': 1500, 'count': 10, 'vrf': None } :return: dict example:: { 'success': { 'probes_sent': 5, 'packet_loss': 0, 'rtt_min': 72.158, 'rtt_max': 72.433, 'rtt_avg': 72.268, 'rtt_stddev': 0.094, 'results': [ { 'ip_address': u'1.1.1.1', 'rtt': 72.248 }, { 'ip_address': u'1.1.1.1', 'rtt': 72.299 } ] } } OR:: { 'error': 'unknown host 8.8.8.8' } """ try: self.device.send_command('\t') except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e)) if self._is_valid_hostname(destination) is True: command = r'ping {0}'.format(destination) if 'source' in opts: self._validate_ping_source(opts['source']) command += ' -I {0}'.format(opts['source']) if 'ttl' in opts: self._validate_ping_ttl(opts['ttl']) command += ' -t {0}'.format(opts['ttl']) if 'size' in opts: self._validate_ping_size(opts['size']) command += ' -s {0}'.format(opts['size']) if 'count' in opts: self._validate_ping_count(opts['count']) command += ' -c {0}'.format(opts['count']) else: command += ' -c 5' output = self.device.send_command(command, delay_factor=10) output = str(output).split('\n') values = [] re_output_rtt = r'(\d+).*time=(.*)\sms' re_output_rtt_unreachable = r'(.*[Uu]nreachable)' re_stats_rtt = r'.*=\s(.*)/(.*)/(.*)/(.*)\sms' re_unreachable = r'.*100%\spacket\sloss.*' mobj = re.match(re_unreachable, output[-2]) packets_sent = 0 packets_lost = 0 if mobj is not None: return {'error': 'unknown host {0}'.format(destination)} else: for line in output: mobj = re.match(re_output_rtt, line) if mobj is not None: val = float(mobj.group(2)) values.append({ 'ip-address': destination, 'rtt': val}) packets_sent += 1 mobj = re.match(re_output_rtt_unreachable, line) if mobj is not None: values.append({'ip-address': destination, 'rtt': None}) packets_sent += 1 packets_lost += 1 response = {} response['success'] = {} response['success']['results'] = [] rttstats = re.match(re_stats_rtt, output[-1]) response = { 'probes_sent': packets_sent, 'packet_loss': packets_lost, 'rtt_min': rttstats.group(1), 'rtt_max': rttstats.group(3), 'rtt_avg': rttstats.group(2), 'rtt_stddev': rttstats.group(4), 'results' : values } return response else: raise ValueError('invalid host format')
def get_interfaces(self) -> dict: """ | Get interface details. | last_flapped is not implemented and will return -1. | Virtual interfaces speed will return 0. :return: dict example:: {u'Vlan1': {'description': u'N/A', 'is_enabled': True, 'is_up': True, 'last_flapped': -1.0, 'mac_address': u'a493.4cc1.67a7', 'speed': 100, 'mtu': 1500}, u'Vlan100': {'description': u'Data Network', 'is_enabled': True, 'is_up': True, 'last_flapped': -1.0, 'mac_address': u'a493.4cc1.67a7', 'speed': 100, 'mtu': 65536}, u'Vlan200': {'description': u'Voice Network', 'is_enabled': True, 'is_up': True, 'last_flapped': -1.0, 'mac_address': u'a493.4cc1.67a7', 'speed': 100, 'mtu': 1500 }} """ RE_NICDATA = {'description': re.compile('\s+comments(.*|$)'), 'is_enabled': re.compile('\s+state\s(.*)$'), 'is_up': re.compile('\s+[Ll]ink-[Ss]tate\s[Ll]ink\s(.*)$'), 'mac_address': re.compile('\s+[Mm]ac-[Aa]ddr\s(.*)$'), 'speed': re.compile('\s+[Ll]ink-[Ss]peed\s([0-9]+).*$'), 'mtu': re.compile('\s+[Mm]tu\s(.*)$') } interface_table = {} try: self.device.send_command('set clienv rows 0') output = self.device.send_command('show interfaces all') except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e)) output = str(output).split('Interface ') for item in output: if len(item) == 0: output.remove(item) for item in output: item = str(item).splitlines() # set entry defaults interface_table[item[0]] = {} interface_table[item[0]]['is_enabled'] = False interface_table[item[0]]['is_up'] = False interface_table[item[0]]['mtu'] = 0 interface_table[item[0]]['description'] = '' interface_table[item[0]]['mac_address'] = 'Not configured' interface_table[item[0]]['speed'] = 0 for line in item: if re.match(RE_NICDATA['is_enabled'], line) is not None: if re.findall(RE_NICDATA['is_enabled'], line) == 'off': interface_table[item[0]]['is_enabled'] = False else: interface_table[item[0]]['is_enabled'] = True if re.match(RE_NICDATA['is_up'], line) is not None: if re.findall(RE_NICDATA['is_up'], line) == 'down': interface_table[item[0]]['is_up'] = False else: interface_table[item[0]]['is_up'] = True if re.match(RE_NICDATA['mtu'], line) is not None: interface_table[item[0]]['mtu'] = re.findall(RE_NICDATA['mtu'], line)[0] if re.match(RE_NICDATA['description'], line) is not None: interface_table[item[0]]['description'] = re.findall(RE_NICDATA['description'], line)[0] if re.match(r'^\s+$', interface_table[item[0]]['description']): interface_table[item[0]]['description'] = '' if re.match(RE_NICDATA['mac_address'], line) is not None: interface_table[item[0]]['mac_address'] = re.findall(RE_NICDATA['mac_address'], line)[0] if re.match(RE_NICDATA['mac_address'], line) is not None: interface_table[item[0]]['mac_address'] = re.findall(RE_NICDATA['mac_address'], line)[0] if re.match(RE_NICDATA['speed'], line) is not None: interface_table[item[0]]['speed'] = re.findall(RE_NICDATA['speed'], line)[0] return interface_table