class ObjectGroup(Model): ALLOWED_TYPES = [ 'icmp-type', 'network', 'protocol', 'security', 'service', 'user' ] description = StrField() group_objects = ListField(default=[], list_type=StrField()) icmp_objects = ListField(default=[], list_type=StrField()) name = StrField() network_objects = ListField(default=[], list_type=AllowedField([ IPAddressField(), IPNetworkField(), StrField() ])) port_objects = ListField(default=[], list_type=StrField()) protocol_groups = ListField(default=[], list_type=StrField()) security_groups = ListField(default=[], list_type=StrField()) service_objects = ListField(default=[], list_type=DictField()) service_protocol = StrField() type = StrField(allowed=ALLOWED_TYPES) def __repr__(self): return "ObjectGroup('{}')".format(self.name) def __str__(self): return self.name def parse_network_object_config(self, line): if line[1] not in ['host', 'object']: if '.' in line[1] or ':' in line[1]: network = '/'.join(line[1:3]) return network if network != '' else None return " ".join(line[1:]) if line[1] == 'host': if '.' in line[2] or ':' in line[2]: return line[2] return 'host {}'.format(line[2]) return " ".join(line[1:]) def parse_service_object_config(self, line): source = [] destination = [] for item in line[2:]: if item in ['destination', 'source']: item_type = item else: eval(item_type).append(item) return { 'protocol': line[1], 'source': tuple(source) if len(source) > 0 else None, 'destination': tuple(destination) if len(destination) > 0 else None } def load_config(self, config_str): config_lines = [(l[1:] if l.startswith(' ') else l) for l in config_str.splitlines()] config = {} for line in config_lines: line = line.split(' ') field = line[0] if field in COMMENTS: continue if field == 'object-group': config['type'] = line[1] config['name'] = line[2] if line[1] == 'service' and len(line) == 4: config['service_protocol'] = line[3] continue if field == 'description': config[field] = ' '.join(line[1:]) continue field = field.replace('-', '_') + 's' if field not in config: config[field] = [] if field == 'network_objects': config[field].append(self.parse_network_object_config(line)) continue if field == 'service_objects': config[field].append(self.parse_service_object_config(line)) continue config[field].append(' '.join(line[1:])) self.load_dict(**config) @property def is_valid(self): return all([self.name, self.type in self.ALLOWED_TYPES]) @property def config(self): if not self.is_valid: return None config = "object-group {} {}".format(self.type, self.name) if self.service_protocol: config += " {}".format(self.service_protocol) string_lists = [ 'group_objects', 'icmp_objects', 'port_objects', 'protocol_groups', 'security_groups' ] for item_type in string_lists: for item in getattr(self, item_type): config += "\n {} {}".format( item_type.replace('_', '-')[:-1], item) for item in self.network_objects: if '/' in str(item) and '.' in str(item): item = "{} {}".format(item.network_address, item.netmask) elif ' ' not in str(item) and '.' in str(item) or ':' in str(item): item = "host {}".format(str(item)) elif ' ' not in str(item): item = "object {}".format(str(item)) config += "\n network-object {}".format(str(item)) for item in self.service_objects: config += "\n service-object {}".format(item['protocol']) source = item['source'] destination = item['destination'] if source: config += " source {} {}".format(source[0], source[1]) if destination: config += " destination {} {}".format(destination[0], destination[1]) return config @property def remove_config(self): if not self.config: return None return "no " + self.config.splitlines()[0]
class AccessControlEntry(Model): ALLOWED_PROTOCOLS = [ "ah", "eigrp", "esp", "gre", "icmp", "icmp6", "igmp", "igrp", "ip", "ipinip", "ipsec", "nos", "ospf", "pcp", "pim", "pptp", "snp", "tcp", "udp", "url" ] ALLOWED_TYPES = ['extended', 'remark', 'standard', 'webtype'] ALLOWED_ACTIONS = ['deny', 'permit'] PORT_OPERATORS = ['eq', 'gt', 'lt', 'neq', 'range'] WEBTYPE_URL_PROTOCOLS = [ "cifs", "citrix", "citrixs", "ftp", "http", "https", "imap4", "pop3", "smtp", "smart-tunnel", "nfs", "post", "vnc", "ssh", "telnet", "rdp" ] name = StrField(readonly=True) input_line = StrField(readonly=True) type = StrField(allowed=ALLOWED_TYPES, readonly=True) action = StrField(allowed=ALLOWED_ACTIONS, readonly=True) protocol = AllowedField([ IntField(low=0, high=255), StrField(allowed=ALLOWED_PROTOCOLS), IPAddressField(), IPNetworkField(), StrField() ]) remark = StrField() source = AllowedField([IPAddressField(), IPNetworkField(), StrField()]) source_port = StrField() destination = AllowedField( [IPAddressField(), IPNetworkField(), StrField()]) destination_port = StrField() inactive = BoolField(default=False) log = BoolField(default=False) time_range = StrField() def __str__(self): return self.config def __repr__(self): return f"AccessControlEntry('{self.name}')" def parse_chunks(self, line): if isinstance(line, str): line = line.split(' ') chunk = '' chunks = [] for word in line: just_one = any([ 'any' in word, len(chunks) == 0 and word.isdigit(), word in ['log', 'inactive'], word in self.ALLOWED_PROTOCOLS ]) if just_one: chunks.append(word) chunk = '' continue if chunk in ['', 'security-group', 'range']: if chunk != '': chunk += ' ' chunk += word else: chunks.append(f"{chunk} {word}") chunk = '' if chunk != '': chunks.append(chunk) return chunks @staticmethod def parse_standard_config(config, line): if line[0] == 'host': config['destination'] = line[1] else: config['destination'] = " ".join(line[0:]) return config def parse_extended_config(self, config, line): chunks = self.parse_chunks(line) if chunks[0].split(' ')[0] == 'object' and len(chunks) == 1: config['protocol'] = " ".join(chunks[0:]) return config keys = [ 'protocol', 'source', 'source_port', 'destination', 'destination_port', 'bad_data_entered' ] port_value_keys_allowed = ['object-group'] + self.PORT_OPERATORS k = 0 for chunk in chunks: chunk = chunk.split(' ') value_key = chunk[0] value = " ".join(chunk[1:]) if len(chunk) > 1 else None needs_more_context = all([ config.get('protocol') in ['tcp', 'udp', '6', '17'], any([ len(chunks) in [3, 4] and 'object-group' in chunks[2], len(chunks) == 4 and 'object-group' in chunks[3] ]) ]) if needs_more_context: return None if value_key in ['log', 'inactive', 'time-range']: if value_key == 'time-range': config['time_range'] = value else: config[value_key] = True continue skip_optional = all([ 'port' in keys[k], any([ len(chunks) <= 3, config.get('protocol') and 'object-group' in config['protocol'], value_key not in port_value_keys_allowed ]) ]) if skip_optional: k += 1 if k > len(keys) - 1: k = len(keys) - 1 key = keys[k] only_one = any([ 'any' in value_key, key == 'protocol' and value_key in self.ALLOWED_PROTOCOLS, key == 'protocol' and value_key.isdigit() ]) if value_key == 'host': config[key] = value elif only_one: config[key] = value_key else: config[key] = f"{value_key} {value}" k += 1 return config def parse_webtype_config(self, config, line): chunks = self.parse_chunks(line) keys = [ 'protocol', 'destination', 'destination_port', 'bad_data_entered' ] k = 0 for chunk in chunks: chunk = chunk.split(' ') value_key = chunk[0] value = " ".join(chunk[1:]) if len(chunk) > 1 else None if value_key in ['log', 'inactive', 'time-range']: if value_key == 'time-range': config['time_range'] = value else: config[value_key] = True continue if k > len(keys) - 1: k = len(keys) - 1 key = keys[k] if value_key == 'host': config[key] = value elif 'any' in value_key or not value: config[key] = value_key else: config[key] = f"{value_key} {value}" k += 1 return config def load_config(self, config_str): line = config_str.split(' ') is_valid = all([ line[0] == 'access-list', line[1] not in ['alert-interval', 'deny-flow-max'] ]) if not is_valid: return None config = {'name': line[1], 'input_line': config_str} line = line[2:] new_config = None if line[0] in self.ALLOWED_TYPES: config['type'] = line[0] line = line[1:] if not config.get('type'): config['type'] = 'standard' if config['type'] == 'remark': config['remark'] = " ".join(line) elif config['type'] == 'standard': config['action'] = line[0] new_config = self.parse_standard_config(config, line[1:]) elif config['type'] == 'extended': config['action'] = line[0] new_config = self.parse_extended_config(config, line[1:]) elif config['type'] == 'webtype': config['action'] = line[0] new_config = self.parse_webtype_config(config, line[1:]) config = new_config if new_config else config self.load_dict(**config) def load_dict(self, *args, **kwargs): super().load_dict(**kwargs) if not self.input_line: self.input_line = self.config @staticmethod def _validate(values=None): return all([ values.get('name'), values.get('type'), any([ values.get('action') and any([values.get('source'), values.get('destination')]), values.get('type') == 'remark' and values.get('remark') ]) ]) @property def is_valid(self): return self._validate(self.values) @staticmethod def generate_network_config(value): if isinstance(value, ipaddress.IPv4Network): return f"{value.network_address} {value.netmask}" return str(value) def generate_config(self, values=None): if not values: values = self.values config = f"access-list {values['name']} {values['type']}" if values['type'] == 'remark': config += f" {values.get('remark', '')}" return config config += f" {values['action']}" if values.get('protocol'): config += f" {values['protocol']}" if values.get('source'): config += f" {self.generate_network_config(values['source'])}" if values.get('source_port'): config += f" {values['source_port']}" if values.get('destination'): config += f" {self.generate_network_config(values['destination'])}" if values.get('destination_port'): config += f" {values['destination_port']}" if values.get('inactive'): config += " inactive" if values.get('log'): config += " log" if values.get('time_range'): config += f" time-range {values['time_range']}" return config @property def config(self): if not self.is_valid: return self.input_line if self.input_line and not self.onlyshowchanges else None return self.generate_config(self.values) @property def initial_config(self): if not self._validate(self.initial_values): input_line = self.initial_values.get('input_line') return input_line return self.generate_config(self.initial_values)
class Nat(Model): ALLOWED_TYPES = ['static', 'dynamic'] after_auto = BoolField(default=False) description = StrField() destination = BoolField(default=False) destination_if_name = StrField() destination_mapped = AllowedField( [IPAddressField(), IPNetworkField(), StrField()]) destination_real = AllowedField( [IPAddressField(), IPNetworkField(), StrField()]) dns = BoolField(default=False) inactive = BoolField(default=False) interface = BoolField(default=False) ipv6 = BoolField(default=False) no_proxy_arp = BoolField(default=False) position = IntField(low=1, high=2147483647) route_lookup = BoolField(default=False) service = StrField() source = BoolField(default=False) source_if_name = StrField() source_mapped = AllowedField( [IPAddressField(), IPNetworkField(), StrField()]) source_real = AllowedField( [IPAddressField(), IPNetworkField(), StrField()]) type = StrField(allowed=ALLOWED_TYPES) unidirectional = BoolField(default=False) def __repr__(self): return "Translation('{}')".format(self.destination_mapped or self.destination_if_name) def __str__(self): if self.destination_mapped: return str(self.destination_mapped) elif self.destination_if_name: return str(self.destination_if_name) return '' def load_config(self, config_str): config_items = config_str.split( ' ') if config_str[0] != ' ' else config_str[1:].split(' ') config = {} if config_items[0] != 'nat': return None config_items = config_items[1:] i = 0 mode = None while i < len(config_items): add = 1 item = config_items[i] if '(' in item: interfaces = item.replace('(', '').replace(')', '').split(',') config['source_if_name'] = interfaces[0] config['destination_if_name'] = interfaces[1] elif item in ['source', 'destination']: config[item] = True mode = item elif item in ['service', 'description']: config['service'] = config_items[i + 1] add = 2 elif item in self.ALLOWED_TYPES: config['type'] = item if mode: config["{}_real".format(mode)] = config_items[i + 1] add = 2 if config_items[i + 2] not in dir(self) or not hasattr( self, config_items[i + 2]): config["{}_mapped".format(mode)] = config_items[i + 2] add = 3 mode = None else: config["destination_mapped".format(mode)] = config_items[i + 1] add = 2 elif item.isdigit(): config['position'] = item else: config[item.replace('-', '_')] = True i += add make_any = '::/0' if 'ipv6' in config else '0.0.0.0/0' for field in [ 'source_real', 'source_mapped', 'destination_real', 'destination_mapped' ]: if field in config: config[field] = config[field].replace('any', make_any) self.load_dict(**config) @property def is_valid(self): return all([ self.source_if_name, self.destination_if_name, self.type, any([ self.type == 'static' and self.destination_mapped, self.type == 'dynamic' and any([ self.source_real and self.source_mapped, self.source_real and self.interface ]) ]) ]) @property def config(self): if not self.is_valid: return None config = 'nat ({},{}) '.format(self.source_if_name, self.destination_if_name) if self.source: source_real = self.source_real source_mapped = self.source_mapped if '/' in str(source_real) and source_real.prefixlen == 0: source_real = 'any' if '/' in str(source_mapped) and source_mapped.prefixlen == 0: source_mapped = 'any' config += 'source {} {} {} '.format(self.type, source_real, source_mapped) if self.destination: destination_real = self.destination_real destination_mapped = self.destination_mapped if '/' in str( destination_real) and destination_real.prefixlen == 0: destination_real = 'any' if '/' in str( destination_mapped) and destination_mapped.prefixlen == 0: destination_mapped = 'any' config += 'destination {} {} {} '.format(self.type, destination_real, destination_mapped) if not self.source and not self.destination: destination_mapped = self.destination_mapped if '/' in str( destination_mapped) and destination_mapped.prefixlen == 0: destination_mapped = 'any' config += '{} {} '.format(self.type, destination_mapped) bool_types = [ 'after_auto', 'dns', 'inactive', 'interface', 'no_proxy_arp', 'route_lookup', 'unidirectional' ] for bool_type in bool_types: setting = getattr(self, bool_type) if setting: config += bool_type.replace('_', '-') + ' ' return config[:-1] @property def remove_config(self): if not self.config: return None config = 'no ' + self.config if 'parent_object' in self.values: config = self.parent_object.config.splitlines()[0] + '\n' + config return config
class Route(Model): method = StrField(default='static') if_name = StrField() route = IPNetworkField() next_hop = IPAddressField() distance = IntField(low=0) def __repr__(self): return "Route('{}')".format(self.next_hop) def __str__(self): return self.config def load_config(self, config_str): config_str = config_str.split(' ') is_valid = any([ all([ config_str[0] == 'route', len(config_str) >= 5]), all([ " ".join(config_str[0:1]) == 'ipv6 route', len(config_str) >= 6])]) if not is_valid: return None config = {} if config_str[0] == 'route': config['if_name'] = config_str[1] config['route'] = " ".join(config_str[2:4]) config['next_hop'] = config_str[4] if len(config_str) == 6: config['distance'] = config_str[5] elif config_str[0] == 'ipv6': config['if_name'] = config_str[2] config['route'] = " ".join(config_str[3:5]) config['next_hop'] = config_str[5] if len(config_str) == 7: config['distance'] = config_str[6] is_valid = all([ 'if_name' in config, 'route' in config and 'next_hop' in config]) if not is_valid: return None self.load_dict(**config) @property def is_valid(self): return all([ self.if_name, self.route and self.next_hop, self.distance]) @property def config(self): if not self.is_valid or self.method != 'static': return None route_str = "route " + self.if_name if '.' in str(self.next_hop): network_str = "{} {}".format( self.route.network_address, self.route.netmask, self.next_hop) else: route_str = "ipv6 {}".route_str network_str = "{} {}".format(self.route, self.next_hop) return "{} {} {}".format(route_str, network_str, self.distance) @property def remove_config(self): if not self.config: return None return "no " + self.config
class Interface(Model): authentication_keys = ListField(default=[], list_type=DictField()) authentication_modes = ListField(default=[], list_type=DictField()) channel_group = IntField() channel_group_mode = StrField(allowed=['active', 'on', 'passive']) channel_group_vss_id = IntField() ddns = StrField() delay = IntField() description = StrField() dhcp_client_route_distance = IntField() dhcp_client_route_track = IntField() dhcp_client_update_dns_server = StrField(allowed=['both', 'none']) dhcprelay_information_trusted = BoolField() dhcprelay_interface_timeout = IntField(low=0) dhcprelay_server = AllowedField([IPAddressField(), StrField()]) duplex = StrField(default='auto', allowed=['auto', 'half', 'full']) flowcontrol = DictField() hello_intervals = ListField(default=[], list_type=DictField()) hold_times = ListField(default=[], list_type=DictField()) igmp = DictField() interface = IntField(low=0, readonly=True) ipv4_address = AllowedField([IPAddressField(), StrField()]) ipv4_dhcp = BoolField() ipv4_subnet_mask = StrField() ipv4_pppoe = BoolField() ipv4_standby = AllowedField([IPAddressField(), StrField()]) ipv6_address = AllowedField([IPAddressField(), StrField()]) ipv6_enable = BoolField(default=False) ipv6_link_local_address = AllowedField([IPAddressField(), StrField()]) ipv6_link_local_network = IPNetworkField(strict=False) ipv6_link_local_standby = AllowedField([IPAddressField(), StrField()]) ipv6_nd = DictField() ipv6_network = IPNetworkField(strict=False) ipv6_ospf = DictField() ipv6_standby = AllowedField([IPAddressField(), StrField()]) lacp_port_priority = IntField(low=1, high=65535) line_status = StrField(allowed=['down', 'up']) mac_address = MACAddressField(splitter=".", split_at=4) mac_address_cluster_pool = StrField() management_only = BoolField() member_interfaces = ListField(default=[], list_type=StrField()) mfib_forwarding = BoolField() multicast_boundaries = ListField(default=[], list_type=DictField()) nameif = StrField() ospf = DictField() pim = DictField() pppoe_client_route_distance = IntField() pppoe_client_route_track = IntField() pppoe_client_secondary_track = IntField() pppoe_client_vpdn_group = StrField() rip_authentication_key = StrField() rip_authentication_mode = StrField() rip_receive_version = TupleField(default=(None, None), tuple_type=IntField(low=1, high=2)) rip_send_version = TupleField(default=(None, None), tuple_type=IntField(low=1, high=2)) security_level = IntField(low=0, high=100) shutdown = BoolField(default=False) slot = IntField(low=0, readonly=True) speed = AllowedField( [IntField(low=0), StrField(default='auto', allowed=['auto'])]) split_horizons = ListField(default=[], list_type=DictField()) summary_addresses = ListField(default=[], list_type=DictField()) type = StrField(readonly=True) vlan = VLANField(readonly=True) def __repr__(self): return f"Interface('{self.interface_name}')" def __str__(self): return self.interface_name @staticmethod def parse_authentication_config(config, line): if line[1] == 'key': if 'authentication_keys' not in config: config['authentication_keys'] = [] config['authentication_keys'].append({ 'eigrp': line[3], 'key': line[4], 'key-id': line[6] }) elif line[1] == 'mode': if 'authentication_modes' not in config: config['authentication_modes'] = [] config['authentication_modes'].append({ 'eigrp': line[3], 'md5': 'md5' in line }) return config @staticmethod def parse_channel_group_config(config, line): config['channel_group'] = line[1] if 'mode' in line: config['channel_group_mode'] = line[3] if 'vss-id' in line: config['channel_group_vss_id'] = line[5] return config @staticmethod def parse_dhcp_config(config, line): key_items = [ 'client', 'route', 'update', 'distance', 'track', 'dns', 'server' ] key = 'dhcp' for item in line[1:]: if item in key_items: key += '_' + item else: config[key] = item return config @staticmethod def parse_dhcprelay_config(config, line): key_items = [ 'information', 'trusted', 'interface', 'timeout', 'server' ] key = 'dhcprelay' for item in line[1:]: if item in key_items: key += '_' + item if item == 'trusted': config[key] = True else: config[key] = item return config @staticmethod def parse_flowcontrol_config(config, line): if 'flowcontrol' not in config: config['flowcontrol'] = {} if 'noconfirm' in line: config['flowcontrol']['noconfirm'] = True line = [i for i in line[1:] if i not in ['noconfirm', 'send', 'on']] if len(line) > 2: config['flowcontrol']['low_water'] = line[0] config['flowcontrol']['high_water'] = line[1] config['flowcontrol']['pause_time'] = line[2] return config @staticmethod def parse_hello_interval_config(config, line): if line[0] == 'hello-interval': if 'hello_intervals' not in config: config['hello_intervals'] = [] config['hello_intervals'].append({ 'eigrp': line[2], 'seconds': line[3] }) return config @staticmethod def parse_hold_time_config(config, line): if line[0] == 'hold-time': if 'hold_times' not in config: config['hold_times'] = [] config['hold_times'].append({'eigrp': line[2], 'seconds': line[3]}) return config @staticmethod def parse_igmp_config(config, line): if 'igmp' not in config: config['igmp'] = {} config['igmp'][line[1]] = line[2] return config @staticmethod def parse_ip_config(config, line): line = line[2:] for item in line: if item in ['dhcp', 'pppoe']: config['ipv4_' + item] = True continue if '.' in item and 'ipv4_address' not in config: config['ipv4_address'] = item elif '.' in item and 'ipv4_subnet_mask' not in config: config['ipv4_subnet_mask'] = item elif '.' in item and 'standby' in line and 'ipv4_standby' not in config: config['ipv4_standby'] = item return config @staticmethod def parse_ipv6_config(config, line): key = 'ipv6_' if line[1] == 'address': key = key if 'link-local' not in line else key + 'link_local_' config[key + 'address'] = line[2].split('/')[0] config[key + 'network'] = line[2] if 'standby' in line: config[key + 'standby'] = line[-1] return config if line[1] in ['nd', 'ospf']: key += line[1] if key not in config: config[key] = {} if len(line[2:]) > 1: config[key][line[2]] = " ".join(line[3:]) else: config[key][line[2]] = True return config @staticmethod def parse_lacp_config(config, line): config['lacp_port_priority'] = line[2] return config @staticmethod def parse_mac_address_config(config, line): if line[1] == 'cluster-pool': config['mac_address_cluster_pool'] = line[2] else: config['mac_address'] = line[1] return config @staticmethod def parse_member_interface_config(config, line): if 'member_interfaces' not in config: config['member_interfaces'] = [] config['member_interfaces'].append(line[1]) return config @staticmethod def parse_mfib_config(config, line): config['mfib_forwarding'] = True return config @staticmethod def parse_multicast_config(config, line): if 'multicast_boundaries' not in config: config['multicast_boundaries'] = [] config['multicast_boundaries'].append({ 'acl': line[2], 'filter-autorp': 'filter-autorp' in line }) return config @staticmethod def parse_ospf_config(config, line): if 'ospf' not in config: config['ospf'] = {} if line[1] == 'mtu-ignore': config[line[1]] = True else: config['ospf'][line[1]] = line[2:] return config @staticmethod def parse_pim_config(config, line): if 'pim' not in config: config['pim'] = {} config['pim'][line[1]] = line[2:] return config @staticmethod def parse_pppoe_config(config, line): key_items = [ 'client', 'route', 'secondary', 'distance', 'track', 'vdpn', 'group' ] key = 'pppoe' for item in line[1:]: if item in key_items: key += '_' + item else: config[key] = item return config @staticmethod def parse_rip_config(config, line): key = "_".join(line[0:3]) config[key] = " ".join(line[3:]) return config @staticmethod def parse_split_horizon_config(config, line): if 'split_horizons' not in config: config['split_horizons'] = [] config['split_horizons'].append({'eigrp': line[2]}) return config @staticmethod def parse_summary_address_config(config, line): if 'summary_addresses' not in config: config['summary_addresses'] = [] config['summary_addresses'].append({ 'eigrp': line[2], 'address': line[3], 'mask': line[4], 'distance': line[5] }) return config def load_config(self, config_str): config_lines = [(line[1:] if line.startswith(' ') else line) for line in config_str.splitlines()] config = {} if 'interface' not in config_lines[0]: return None parsers = [ 'authentication', 'channel_group', 'dhcp', 'dhcprelay', 'flowcontrol', 'hello_interval', 'hold_time', 'igmp', 'ip', 'ipv6', 'lacp', 'mac_address', 'member_interface' 'mfib', 'multicast', 'ospf', 'pim', 'pppoe', 'rip', 'split_horizon', 'summary_address' ] for line in config_lines: line = line.split(' ') field = line[0].replace('-', '_') if field in COMMENTS: continue if field == 'interface': if 'Ethernet' in line[1] or 'Management' in line[1]: if 'Ethernet' in line[1]: config['type'] = line[1].split( 'Ethernet')[0] + 'Ethernet' name = line[1].split('Ethernet')[1] else: config['type'] = line[1].split( 'Management')[0] + 'Management' name = line[1].split('Management')[1] config['slot'] = name.split('/')[0] config['interface'] = name.split('/')[1].split('.')[0] config['vlan'] = name.split('/')[1].split( '.')[1] if '.' in name else None else: config['type'] = line[1] config['interface'] = line[2] continue if field in parsers: func = 'parse_' + field + '_config' config = getattr(self, func)(config, line) continue if len(line) > 1 and field != 'no': if field in config and isinstance(config[field], list): config[field].append(" ".join(line[1:])) else: config[field] = " ".join(line[1:]) elif field != 'no': config[field] = True self.load_dict(**config) @property def interface_name(self): line = "" valid_ethernet = all([ any([ self.type and 'Ethernet' in self.type, self.type and 'Management' in self.type ]), isinstance(self.slot, int), isinstance(self.interface, int) ]) valid_other = all([ self.type, any([ self.type and 'redundant' in self.type, self.type and 'port-channel' in self.type ]), isinstance(self.interface, int) ]) if valid_ethernet: line = f"{self.type}{self.slot}/{self.interface}" if self.vlan: line += f'.{self.vlan}' elif valid_other: line = f"{self.type} {self.interface}" return line @property def is_valid(self): return self.interface_name != "" @property def generate_authentication_keys_config(self): if len(self.authentication_keys) == 0: return None config = [] for values in self.authentication_keys: valid_config = all( [values.get('eigrp'), values.get('key'), values.get('key-id')]) if valid_config: config.append( f" authentication key eigrp {values['eigrp']} {values['key']} key-id {values['key-id']}" ) if len(config) == 0: return None return "\n".join(config) @property def generate_authentication_modes_config(self): if len(self.authentication_modes) == 0: return None config = [] for values in self.authentication_modes: valid_config = all([values['eigrp'], len(values) == 2]) if valid_config: line = f" authentication mode eigrp {values['eigrp']}" for key in values: if key != 'eigrp': line += f" {key}" config.append(line) if len(config) == 0: return None return "\n".join(config) @property def generate_channel_group_config(self): if not all([self.channel_group, self.channel_group_mode]): return None line = f" channel-group {self.channel_group} mode {self.channel_group_mode}" if self.channel_group_vss_id: line += f" vss-id {self.channel_group_vss_id}" return line @property def generate_dhcp_config(self): if self.dhcp_client_route_distance is not None: return f" dhcp client route distance {self.dhcp_client_route_distance}" if self.dhcp_client_route_track is not None: return f" dhcp client route track {self.dhcp_client_route_track}" if self.dhcp_client_update_dns_server is not None: return f" dhcp client update dns server {self.dhcp_client_update_dns_server}" return None @property def generate_dhcprelay_config(self): if self.dhcprelay_information_trusted: return " dhcprelay information trusted" if self.dhcprelay_interface_timeout is not None: return f" dhcprelay interface timeout {self.dhcprelay_interface_timeout}" if self.dhcprelay_server is not None: return f" dhcprelay server {self.dhcprelay_server}" return None @property def generate_flowcontrol_config(self): valid_flowcontrol = all([ self.flowcontrol and 'low_water' in self.flowcontrol, self.flowcontrol and 'high_water' in self.flowcontrol, self.flowcontrol and 'pause_time' in self.flowcontrol ]) if not valid_flowcontrol: return None line = f" flowcontrol {self.flowcontrol['low_water']}" line += f" {self.flowcontrol['high_water']}" line += f" {self.flowcontrol['pause_time']}" if self.flowcontrol.get('noconfirm'): line += " noconfirm" return line @property def generate_hello_intervals_config(self): if len(self.hello_intervals) == 0: return None config = [] for values in self.hello_intervals: valid_config = all(['eigrp' in values, 'seconds' in values]) if valid_config: config.append( f" hello-interval eigrp {values['eigrp']} {values['seconds']}" ) if len(config) == 0: return None return "\n".join(config) @property def generate_hold_times_config(self): if len(self.hold_times) == 0: return None config = [] for values in self.hold_times: valid_config = all(['eigrp' in values, 'seconds' in values]) if valid_config: config.append( f" hold-time eigrp {values['eigrp']} {values['seconds']}") if len(config) == 0: return None return "\n".join(config) @property def generate_igmp_config(self): if not self.igmp: return None config = [] for key, value in self.igmp.items(): config.append(f" igmp {key} {value}") if len(config) == 0: return None return "\n".join(config) @property def generate_ipv4_config(self): line = " ip address" if not all([self.ipv4_address, self.ipv4_subnet_mask]): if self.onlyshowchanges: return None return " no" + line line += f" {self.ipv4_address} {self.ipv4_subnet_mask}" if self.ipv4_standby: line += f" standby {self.ipv4_standby}" return line @property def generate_ipv6_config(self): lines = [] if self.ipv6_enable: lines.append(' ipv6 enable') if all([self.ipv6_link_local_address, self.ipv6_link_local_network]): line = f" ipv6 {self.ipv6_link_local_address} {self.ipv6_link_local_network.prefixlen}" if self.ipv6_link_local_standby: line += f" standby {self.ipv6_link_local_standby}" lines.append(line) if all([self.ipv6_address, self.ipv6_network]): line = f" ipv6 {self.ipv6_address}/{self.ipv6_network.prefixlen}" if self.ipv6_standby: line += f" standby {self.ipv6_standby}" lines.append(line) for field in ['nd', 'ospf']: if f'ipv6_{field}' in self.values: values = self.values[f'ipv6_{field}'] for key in values: value = values[key] if not value: continue line = f" ipv6 {field} {key}" if not isinstance(value, bool): line += f" {value}" lines.append(line) if len(lines) == 0: return None return "\n".join(lines) @property def generate_lacp_port_priority_config(self): if not self.lacp_port_priority: return None return f" lacp port-priority {self.lacp_port_priority}" @property def generate_mac_address_config(self): if not self.mac_address: return None return f" mac-address {self.mac_address}" @property def generate_mac_address_cluster_pool_config(self): if not self.mac_address_cluster_pool: return None return f" mac-address cluster-pool {self.mac_address_cluster_pool}" @property def generate_member_interfaces_config(self): if len(self.member_interfaces) == 0: return None config = [] for interface in self.member_interfaces: config.append(f" member-interface {interface}") if len(config) == 0: return None return "\n".join(config) @property def generate_mfib_forwarding_config(self): if not self.mfib_forwarding: return None return " mfib forwarding" @property def generate_multicast_boundaries_config(self): if len(self.multicast_boundaries) == 0: return None config = [] for boundary in self.multicast_boundaries: if 'acl' not in boundary: continue line = f" multicast boundary {boundary['acl']}" if boundary.get('filter-autorp'): line += ' filter-autorp' config.append(line) if len(config) == 0: return None return "\n".join(config) @property def generate_ospf_config(self): if not self.ospf: return None config = [] for key, value in self.ospf.items(): line = f" ospf {key}" if not isinstance(value, bool): line += f" {value}" config.append(line) if len(config) == 0: return None return "\n".join(config) @property def generate_pim_config(self): if not self.pim: return None config = [] for key, value in self.pim.items(): config.append(f" pim {key} {value}") if len(config) == 0: return None return "\n".join(config) @property def generate_pppoe_config(self): config = [] if self.pppoe_client_route_distance: config.append( f" pppoe client route distance {self.pppoe_client_route_distance}" ) if self.pppoe_client_route_track: config.append( f" pppoe client route track {self.pppoe_client_route_track}") if self.pppoe_client_secondary_track: config.append( f" pppoe client secondary track {self.pppoe_client_secondary_track}" ) if self.pppoe_client_vpdn_group: config.append( f" pppoe client vpdn group {self.pppoe_client_vpdn_group}") if len(config) == 0: return None return "\n".join(config) @property def generate_rip_config(self): config = [] if self.rip_authentication_key: config.append( f" rip authentication key {self.rip_authentication_key}") if self.rip_authentication_mode: config.append( f" rip authentication mode {self.rip_authentication_mode}") if self.rip_receive_version and self.rip_receive_version != (None, None): rip_recieve_version = " ".join( [i for i in self.rip_receive_version if i]) config.append(f" rip receive version {rip_recieve_version}") if self.rip_send_version and self.rip_send_version != (None, None): rip_send_version = " ".join( [i for i in self.rip_send_version if i]) config.append(f" rip send version {rip_send_version}") if len(config) == 0: return None return "\n".join(config) @property def generate_split_horizons_config(self): if len(self.split_horizons) == 0: return None config = [] for values in self.split_horizons: value = values.get('eigrp') if value: config.append(f" split-horizon eigrp {value}") if len(config) == 0: return None return "\n".join(config) @property def generate_summary_addresses_config(self): if len(self.summary_addresses) == 0: return None config = [] for values in self.summary_addresses: eigrp = values.get('eigrp') address = values.get('address') mask = values.get('mask') distance = values.get('distance') if all([eigrp, address, mask, distance]): config.append( f" summary-address eigrp {eigrp} {address} {mask} {distance}" ) if len(config) == 0: return None return "\n".join(config) @property def config(self): config = f'interface {self.interface_name}' generators = [ 'authentication_keys', 'authentication_modes', 'channel_group', 'dhcp', 'dhcprelay', 'flowcontrol', 'hello_intervals', 'hold_times', 'igmp', 'ipv4', 'ipv6', 'lacp_port_priority', 'mac_address', 'mac_address_cluster_pool', 'member_interfaces', 'mfib_forwarding', 'multicast_boundaries', 'ospf', 'pim', 'pppoe', 'rip', 'split_horizons', 'summary_addresses' ] order = [ 'description', 'shutdown', 'vlan', 'channel_group', 'speed', 'duplex', 'nameif', 'security_level', 'mac_address', 'mac_address_cluster_pool', 'ipv4', 'ipv6', 'authentication_keys' 'authentication_modes', 'dhcp', 'dhcprelay', 'flowcontrol', 'hello_intervals', 'hold_times', 'igmp', 'lacp_port_priority', 'member_interfaces', 'mfib_forwarding', 'multicast_boundaries', 'ospf', 'pim', 'pppoe', 'rip', 'split_horizons', 'summary_addresses' ] for field in order: if field in generators: func = 'generate_' + field + '_config' line = getattr(self, func) if line: config += f"\n{line}" continue value = self.values.get(field) field = field.replace('_', '-') if value is None: if field in ['nameif', 'security-level' ] and not self.onlyshowchanges: config += f"\n no {field}" continue config += f"\n {field}" if not isinstance(value, bool): config += f" {str(value)}" if config == f'interface {self.interface_name}': return "" return config
class Object(Model): ALLOWED_TYPES = ['network', 'service'] BOOL_FIELDS = [ 'call_home', 'internal', 'password_recovery', 'resetoutside' ] call_home = BoolField(default=False) description = StrField() fqdn = StrField() host = IPAddressField() internal = BoolField(default=False) name = StrField() nat = ModelField('cisco.asa.nat.Nat', 'parent_object') password_recovery = BoolField(default=False) range = TupleField(tuple_type=IPAddressField()) resetinbound_interface = StrField() resetoutbound_interface = StrField() resetoutside = BoolField(default=False) service = DictField() subnet = IPNetworkField() type = StrField(allowed=ALLOWED_TYPES) def __repr__(self): return "Object('{}')".format(self.name) def __str__(self): return self.name def parse_service_config(self, line): source = [] destination = [] for item in line[2:]: if item in ['destination', 'source']: item_type = item else: eval(item_type).append(item) return { 'protocol': line[1], 'source': tuple(source) if len(source) > 0 else None, 'destination': tuple(destination) if len(destination) > 0 else None } def load_config(self, config_str): config_lines = [(l[1:] if l.startswith(' ') else l) for l in config_str.splitlines()] config = {} if config_lines[0].split(' ')[0] != 'object': return None for line in config_lines: line = line.split(' ') field = line[0].replace('-', '_') if field in COMMENTS: continue if field == 'object': config['type'] = line[1] config['name'] = line[2] continue if field == 'service': config[field] = self.parse_service_config(line) continue if isinstance(getattr(self, field), BoolField): config[field] = True continue if field in ['resetinbound', 'resetoutbound']: config["{}_interface".format(field)] = line[2] continue if field == 'nat': config[field] = " ".join(line) continue config[field] = " ".join(line[1:]) self.load_dict(**config) @property def is_valid(self): return all([ self.name, any([ self.type and self.type == 'network' and any( [self.fqdn, self.host, self.nat, self.range, self.subnet]), self.type and self.type == 'service' and self.service and any([self.service['destination'], self.service['source']]) ]) ]) @property def config(self): if not self.is_valid: return None config = "object {} {}".format(self.type, self.name) if self.fqdn: config += "\n fqdn {}".format(self.fqdn) if self.host: config += "\n host {}".format(self.host) if self.nat: config += "\n " + self.nat.config if self.range: config += "\n range {} {}".format(self.range[0], self.range[1]) if self.subnet: if ':' in str(self.subnet): network_str = str(self.subnet) else: network_str = "{} {}".format(self.subnet.network_address, self.subnet.netmask) config += "\n subnet {}".format(network_str) if self.type == 'service': config += "\n service " if self.service: config += self.service['protocol'] if self.service['source']: config += " source {} {}".format(self.service['source'][0], self.service['source'][1]) if self.service['destination']: config += " destination {} {}".format( self.service['destination'][0], self.service['destination'][1]) if self.resetinbound_interface: config += "\n service resetinbound interface {}".format( self.resetinbound_interface) if self.resetoutbound_interface: config += "\n service resetoutbound interface {}".format( self.resetoutbound_interface) for setting in self.BOOL_FIELDS: if getattr(self, setting): config += "\n {}".format(setting.replace('_', '-')) return config @property def remove_config(self): if not self.config: return None return "no " + self.config.splitlines()[0] @property def remove_nat_config(self): if not self.config: return None line1 = self.config.splitlines()[0] return "{}\n no {}".format(line1, self.nat.config)
class Failover(Model): failover = BoolField(default=False) interface_active_ip = IPAddressField() interface_ip_name = StrField() interface_network = IPNetworkField(strict=False) interface_policy = AllowedField([IntField(low=1, high=110), StrField()]) interface_standby_ip = IPAddressField() ipsec_pre_shared_key = StrField() ipsec_pre_shared_key_encrypted = BoolField(default=False) key = StrField() key_encrypted = BoolField(default=False) lan_interface = StrField() lan_name = StrField() lan_unit = StrField(allowed=['primary', 'secondary'], default='primary') link_interface = StrField() link_name = StrField() mac_address_active = MACAddressField() mac_address_name = StrField() mac_address_standy = MACAddressField() polltime_interface = IntField(low=1, high=15, default=1) polltime_interface_msec = IntField(low=500, high=999) polltime_interface_holdtime = IntField(low=5, high=75) polltime_unit = IntField(low=1, high=15, default=1) polltime_unit_msec = IntField(low=200, high=999) polltime_unit_holdtime = IntField(low=1, high=45) polltime_unit_holdtime_msec = IntField(low=800, high=999) replication_http = BoolField(default=False) timeout = TimeField('%H:%M:%S') def __repr__(self): return "Failover('{}')".format(self.lan_unit) def __str__(self): return self.config def _parse_encryption_config(self, line, config): if line[1] not in ['ipsec', 'key']: return config key = line[1] value_in = 2 if line[2] == 'pre-shared-key': key += '_pre_shared_key' value_in += 1 if line[value_in] in ['0', '8']: if line[value_in] == '8': config[key + '_encrypted'] = True value_in += 1 config[key] = line[value_in] return config def _parse_polltime_config(self, line, config): if line[1] != 'polltime': return config key = 'polltime_interface' if line[ 2] == 'interface' else 'polltime_unit' value = 3 if line[2] in ['interface', 'unit'] else 2 holdtime_key = key + '_holdtime' if line[value] == 'msec': value += 1 key += '_{}'.format('msec') config[key] = line[value] if line[value + 2] == 'msec': holdtime_key += "_msec" config[holdtime_key] = line[value + 3] else: config[holdtime_key] = line[value + 2] return config def load_config(self, config_str): config_lines = config_str.splitlines() config = {} if 'failover' not in config_lines[0]: return None for line in config_lines: line = line.split(' ') if len(line) == 1 and line[0] == 'failover': config['failover'] = True elif all([len(line) == 2, " ".join(line) == 'no failover']): config['failover'] = False elif line[1] == 'lan' and line[2] == 'unit': config['lan_unit'] = line[3] elif line[1] == 'link': config['link_interface'] = line[2] config['link_name'] = line[3] elif line[1] == 'interface' and line[2] == 'ip': config['interface_ip_name'] = line[3] if '.' in line[4]: config['interface_active_ip'] = line[4] config['interface_network'] = "{}/{}".format( line[4], line[5]) config['interface_standby_ip'] = line[7] elif ':' in line[4]: config['interface_active_ip'] = line[4].split('/')[0] config['interface_network'] = line[4] config['interface_standby_ip'] = line[6] elif line[1] == 'interface-policy': config['interface_policy'] = line[2] elif line[1] in ['ipsec', 'key']: config = self._parse_encryption_config(line, config) elif line[1] == 'mac' and line[2] == 'address': config['mac_address_active'] = line[4] config['mac_address_standy'] = line[5] config['mac_address_name'] = line[3] elif line[1] == 'polltime': config = self._parse_polltime_config(line, config) elif line[1] == 'replication' and line[2] == 'http': config['replication_http'] = True elif line[1] == 'timeout': config['timeout'] = line[2] if config == {}: return None self.load_dict(**config) @property def is_valid(self): return all([self.link_name, self.link_interface]) def _generate_key_config(self): config = "" if self.key and not self.ipsec_pre_shared_key: config += "failover key{} {}\n".format( " 8" if self.key_encrytped else "", self.key) return config def _generate_ipsec_config(self): config = "" if self.ipsec_pre_shared_key: config += "failover ipsec pre-shared-key{} {}\n".format( " 8" if self.ipsec_pre_shared_key_encrypted else "", self.ipsec_pre_shared_key) return config def _generate_polltime_config(self): config = "" key_parts = ['_unit', '_interface'] for key_part in key_parts: key = "polltime{}".format(key_part) polltime = self.values.get(key) holdtime = self.values.get(key + "_holdtime") if not polltime or polltime == 1: msec = self.values.get(key + "_msec") polltime = "msec {}".format(msec) if msec else polltime if not holdtime: msec = self.values.get(key + "_holdtime_msec") holdtime = "msec {}".format(msec) if msec else None if polltime and holdtime: config += "failover polltime{} {} holdtime {}\n".format( key_part.replace('_', ' '), polltime, holdtime) return config @property def config(self): if not self.is_valid: return None config = "failover\n" if self.failover else "no failover\n" if self.lan_unit: config += "failover lan unit {}\n".format(self.lan_unit) if self.interface_policy: config += "failover interface-policy {}\n".format( self.interface_policy) config += self._generate_polltime_config() config += self._generate_key_config() config += "failover replication http\n" if self.replication_http else "" config += "failover timeout {}\n".format( self.timeout) if self.timeout else "" if self.link_name: config += "failover link {} {}\n".format(self.link_name, self.link_interface) interface = all([ self.interface_active_ip, self.interface_ip_name, self.interface_network, self.interface_standby_ip ]) if all([interface, ':' in str(self.interface_active_ip)]): config += "failover interface ip {} {} standby {}\n".format( self.interface_ip_name, str(self.interface_active_ip) + "/" + str(self.interface_network.prefixlen), self.interface_standby_ip) elif interface: config += "failover interface ip {} {} {} standby {}\n".format( self.interface_ip_name, self.interface_active_ip, self.interface_network.netmask, self.interface_standby_ip) if all([ self.mac_address_name, self.mac_address_active, self.mac_address_standy ]): config += "failover mac address {} {} {}\n".format( self.mac_address_name, self.mac_address_active, self.mac_address_standy) config += self._generate_ipsec_config() return config[:-1]