Exemplo n.º 1
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)
Exemplo n.º 2
0
class Logging(Model):
    asdm = AllowedField([
        StrField(allowed=[v for k, v in LEVELS.items()]),
        StrField()])
    asdm_buffer_size = IntField(low=100, high=512)
    buffer_size = IntField(low=4096, high=1048576)
    buffered = AllowedField([
        StrField(allowed=[v for k, v in LEVELS.items()]),
        StrField()])

    # Holy crap, the level of inception here...
    # This creates the validators for the individual
    #  levels within the multilayer dict "classes"
    # Tuples within tuples within tuples,
    #  using list comprension to make the keys!
    classes = DictField(allowed=[(
        logging_class,
        DictField(allowed=[(
            level_type,
            StrField(allowed=[v for k, v in LEVELS.items()])
        ) for level_type in LEVEL_TYPES])
    ) for logging_class in CLASSES])

    console = AllowedField([
        StrField(allowed=[v for k, v in LEVELS.items()]),
        StrField()])
    debug_trace = BoolField(default=False)
    device_id = StrField()
    emblem = BoolField(default=False)
    enable = BoolField(default=False)
    facility = IntField(low=16, high=23, default=20)
    flash_bufferwrap = BoolField(default=False)
    flash_maximum_allocation = IntField(low=4, high=249592)
    flash_minimum_free = IntField(low=0, high=249592)
    flow_export_syslogs = StrField(allowed=['enable', 'disable'])
    from_address = StrField()
    ftp_bufferwrap = BoolField(default=False)
    ftp_server_address = AllowedField([IPAddressField(), StrField()])
    ftp_server_directory_path = StrField()
    ftp_server_username = StrField()
    ftp_server_password = StrField()
    ftp_server_password_encrypted = BoolField(default=False)
    hide_username = BoolField(default=True)
    history = AllowedField([
        StrField(allowed=[v for k, v in LEVELS.items()]),
        StrField()])

    # This craziness is also adding validation to a list of dicts
    hosts = ListField(
        list_type=DictField(
            allowed=[
                ('interface', StrField()),
                ('address', AllowedField([
                    IPAddressField(),
                    StrField()])),
                ('protocol', AllowedField([
                    IntField(low=6, high=6),  # TCP
                    IntField(low=17, high=17)])),  # UDP
                ('port', IntField(low=1, high=65535)),
                ('secure', BoolField(default=False)),
                ('format', StrField(allowed=['emblem']))]))

    # Some more of the same craziness that hosts is doing, but for lists!
    lists = ListField(
        list_type=DictField(
            allowed=[
                ('name', StrField()),
                ('level', StrField(allowed=[v for k, v in LEVELS.items()])),
                ('message', IntField(low=100000, high=999999)),
                ('class', StrField(c for c in CLASSES))]))

    mail = AllowedField([
        StrField(allowed=[v for k, v in LEVELS.items()]),
        StrField()])

    # Screw you, Cisco, for this level of complexity!
    # On a similar note, thank you, Cisco, for this level of customization!
    # Just more validation to a list of dicts
    messages = ListField(
        list_type=DictField(
            allowed=[
                ('level', StrField(allowed=[v for k, v in LEVELS.items()])),
                ('message', IntField(low=100000, high=999999)),
                ('no', BoolField(default=False))]))

    monitor = AllowedField([
        StrField(allowed=[v for k, v in LEVELS.items()]),
        StrField()])
    permit_hostdown = BoolField(default=False)
    queue = IntField(low=0, high=8192, default=512)

    # Understanding this logging stuff is love/hate
    # Yet more validation to a list of dicts
    rate_limits = ListField(
        list_type=DictField(
            allowed=[
                ('number', IntField(low=1, high=2147483647)),
                ('unlimited', BoolField(default=False)),
                ('interval', IntField(low=1, high=2147483647)),
                ('message', IntField(low=100000, high=999999)),
                ('level', IntField(low=0, high=7))]))

    # Because you can have multiple loggin levels sent to multiple email addresses,
    #  you need to be able to store multiple recipient-addresses. Which means
    #  this code you see has to validate this stuff, so here you go, more
    #  validator barf...
    recipient_addresses = ListField(
        list_type=DictField(
            allowed=[
                ('level', StrField(allowed=[v for k, v in LEVELS.items()])),
                ('email', EmailField())]))

    standby = BoolField(default=False)
    timestamp = BoolField(default=False)
    trap = AllowedField([
        StrField(allowed=[v for k, v in LEVELS.items()]),
        StrField()])

    def __repr__(self):
        enabled = 'Enabled' if self.enable else 'Not Enabled'
        return "Logging('{}')".format(enabled)

    def __str__(self):
        return self.config

    def parse_level_config(self, field, params, config):
        level = params[0]

        if level.isdigit():
            level = LEVELS.get(int(level)) or level

        config[field] = level

        return config

    def parse_class_config(self, params, config):
        class_type = params[0]
        level_type = params[1]
        level = LEVELS.get(int(params[2])) if params[2].isdigit() else params[2]

        if 'classes' not in config:
            config['classes'] = {}

        if class_type not in config['classes']:
            config['classes'][class_type] = {}

        config['classes'][class_type][level_type] = level

        return config

    def parse_list_config(self, params, config):
        list_item = {
            'name': params[0],
            'class': None,
            'level': None,
            'message': None}

        params = params[1:]
        changes = False

        while len(params) > 0:
            field = params[0]
            value = params[1]

            if field == 'level' and value.isdigit():
                value = LEVELS.get(int(value)) or value

            list_item[field] = value
            changes = True
            params = params[2:]

        if changes:
            if 'lists' not in config:
                config['lists'] = []

            config['lists'].append(list_item)

        return config

    def parse_ftp_server_config(self, params, config):
        config['ftp_server_address'] = params[0]
        config['ftp_server_directory_path'] = params[1]
        config['ftp_server_username'] = params[2]

        password = params[3]

        if params[3] in ['0', '8']:
            if params[3] == '8':
                config['ftp_server_password_encrypted'] = True
            else:
                config['ftp_server_password_encrypted'] = False

            password = params[4]

        if password != '*****':
            config['ftp_server_password'] = password

        return config

    def parse_host_config(self, params, config):
        host = {
            'interface': params[0],
            'address': params[1],
            'protocol': None,
            'port': None,
            'format': 'emblem' if 'emblem' in params else None,
            'secure': 'secure' in params}

        params = [p for p in params[2:] if p not in ['format', 'emblem', 'secure']]

        if len(params) > 0:
            protocol_and_port = params[0].split('/')
            protocol = protocol_and_port[0].lower()
            protocol = int(protocol) if protocol.isdigit() else protocol
            port = None

            if protocol in ['tcp', 'udp', 6, 17]:
                if protocol in ['tcp', 6]:
                    protocol = 6
                    port = 1470
                elif protocol in ['udp', 17]:
                    protocol = 17
                    port = 514

            if len(protocol_and_port) > 1 and protocol_and_port[1].isdigit():
                port = int(protocol_and_port[1])

            host['protocol'] = protocol
            host['port'] = port

        if 'hosts' not in config:
            config['hosts'] = []

        config['hosts'].append(host)

        return config

    def parse_message_config(self, params, enabled, config):
        message = {
            'no': not enabled,
            'level': None,
            'message': params[0]}

        if 'level' in params:
            level = params[-1]

            if level.isdigit():
                level = LEVELS.get(int(level)) or level

            message['level'] = level

        if 'messages' not in config:
            config['messages'] = []

        config['messages'].append(message)

        return config

    def parse_rate_limit_config(self, params, config):
        rate_limit = {
            'number': None,
            'unlimited': False,
            'interval': None,
            'message': None,
            'level': None}

        if params[0] == 'unlimited':
            rate_limit['unlimited'] = True
            params = params[1:]
        else:
            rate_limit['number'] = params[0]
            rate_limit['interval'] = params[1]
            params = params[2:]

        if params[0] == 'level':
            level = LEVELS.get(int(params[1])) if params[1].isdigit() else params[1]
            rate_limit['level'] = level
        elif params[0] == 'message':
            rate_limit['message'] = params[1]

        if 'rate_limits' not in config:
            config['rate_limits'] = []

        config['rate_limits'].append(rate_limit)

        return config

    def parse_recipient_address_config(self, params, config):
        level = LEVELS.get(int(params[2])) if params[2].isdigit() else params[2]

        recipient_address = {
            'email': params[0],
            'level': level}

        if 'recipient_addresses' not in config:
            config['recipient_addresses'] = []

        config['recipient_addresses'].append(recipient_address)

        return config

    def load_config(self, config_str):
        config_lines = [l for l in config_str.splitlines() if l.startswith('logging')]
        config = {}

        if len(config_lines) == 0:
            return None

        level_parsing = [
            'asdm', 'buffered', 'console', 'history',
            'mail', 'monitor', 'trap']

        for line in config_lines:
            line = line.split(' ')
            field = line[1].replace('-', '_')
            params = line[2:]
            enabled = True

            if line[0] == 'no':
                if line[1] != 'logging':
                    continue

                enabled = False
                field = line[2].replace('-', '_')
                params = line[3:]

            if field == 'hide':
                field += '_' + params[0]
                params = params[1:]

            if field in level_parsing:
                config = self.parse_level_config(field, params, config)
            elif field == 'class':
                config = self.parse_class_config(params, config)
            elif field == 'ftp_server':
                config = self.parse_ftp_server_config(params, config)
            elif field == 'host':
                config = self.parse_host_config(params, config)
            elif field == 'list':
                config = self.parse_list_config(params, config)
            elif field == 'message':
                config = self.parse_message_config(params, enabled, config)
            elif field == 'rate_limit':
                config = self.parse_rate_limit_config(params, config)
            elif field == 'recipient_address':
                config = self.parse_recipient_address_config(params, config)
            elif len(params) == 0:
                config[field] = enabled
            else:
                config[field] = " ".join(params)

        if config == {}:
            return None

        self.load_dict(**config)

    @property
    def is_valid(self):
        return True

    def generate_level_config(self, field):
        value = getattr(self, field)

        if not value:
            return ""

        return "logging {} {}".format(field, value)

    def generate_classes_config(self):
        if not self.classes or self.classes == {}:
            return ""

        lines = ""

        if self.onlyshowchanges:
            for class_type in self.initial_values['classes']:
                for level_type in self.initial_values['classes'][class_type]:
                    level = self.initial_values['classes'][class_type][level_type]

                    no = any([
                        class_type not in self.classes,
                        level_type not in self.classes[class_type]])

                    if no:
                        if lines != "":
                            lines += "\n"

                        lines += "no logging class {} {} {}".format(
                            class_type,
                            level_type,
                            level)

        for class_type in self.classes:
            for level_type in self.classes[class_type]:
                level = self.classes[class_type][level_type]

                if lines != "":
                    lines += "\n"

                lines += "logging class {} {} {}".format(
                    class_type,
                    level_type,
                    level)

        return lines

    def generate_ftp_server_config(self):
        if not self.ftp_server_address:
            if self.onlyshowchanges and self.initial_values['ftp_server_address']:
                password = self.initial_values['ftp_server_password'] or ""

                if password != "" and self.initial_values['ftp_server_password_encrypted']:
                    password = "******" + password

                return "no logging ftp-server {} {} {} {}".format(
                    self.initial_values['ftp_server_address'],
                    self.initial_values['ftp_server_directory_path'],
                    self.initial_values['ftp_server_username'],
                    password)

            return ""

        password = self.ftp_server_password or ""

        if password != "" and self.ftp_server_password_encrypted:
            password = "******" + password

        return "logging ftp-server {} {} {} {}".format(
            self.ftp_server_address,
            self.ftp_server_directory_path,
            self.ftp_server_username,
            password)

    def generate_hosts_config(self):
        if not self.hosts or len(self.hosts) == 0:
            return ""

        lines = ""

        for host in self.initial_values['hosts']:
            if host in self.hosts or not all([host['interface'], host['address']]):
                continue

            lines = lines + "\n" if lines != "" else lines
            lines += "no logging host {} {}".format(host['interface'], host['address'])

            if host['protocol']:
                protocol = str(host['protocol'])

                if host['port']:
                    protocol += "/{}".format(host['port'])

                lines += " {}".format(protocol)

            if host['secure'] and host['protocol'] == 6:
                lines += " secure"

            if host['format'] and host['protocol'] == 17:
                lines += " format {}".format(host['format'])

        for host in self.hosts:
            if not all([host['interface'], host['address']]):
                continue

            lines = lines + "\n" if lines != "" else lines
            lines += "logging host {} {}".format(host['interface'], host['address'])

            if host['protocol']:
                protocol = str(host['protocol'])

                if host['port']:
                    protocol += "/{}".format(host['port'])

                lines += " {}".format(protocol)

            if host['secure'] and host['protocol'] == 6:
                lines += " secure"

            if host['format'] and host['protocol'] == 17:
                lines += " format {}".format(host['format'])

        return lines

    def generate_lists_config(self):
        if not self.lists or len(self.lists) == 0:
            return ""

        lines = ""

        for item in self.lists:
            if not item['name']:
                continue

            line = "\n" if lines != "" else ""
            line += "logging list {}".format(item['name'])

            if item['message']:
                line += " message {}".format(item['message'])
                lines += line
                continue

            if not item['level']:
                continue

            line += " level {}".format(item['level'])

            if item['class']:
                line += " class {}".format(item['class'])

            lines += line

        return lines

    def generate_messages_config(self):
        if not self.messages or len(self.messages) == 0:
            return ""

        lines = ""

        for message in self.messages:
            if not message['message']:
                continue

            line = "\n" if lines != "" else ""
            line += "logging message {}".format(message['message'])

            if message['level']:
                line += " level {}".format(message['level'])
                lines += line
                continue

            if message['no']:
                line = "no {}".format(line)

            if not message['no'] and not self.onlyshowchanges:
                continue

            lines += line

        return lines

    def generate_rate_limits_config(self):
        if not self.rate_limits or len(self.rate_limits) == 0:
            return ""

        lines = ""

        for rate_limit in self.rate_limits:
            if not any([rate_limit['unlimited'], rate_limit['number']]):
                continue

            line = "\nlogging rate-limit" if lines != "" else "logging rate-limit"

            if rate_limit['unlimited']:
                line += " unlimited"
            else:
                line += " {}".format(rate_limit['number'])

            if rate_limit['interval']:
                line += " {}".format(rate_limit['interval'])

            if rate_limit['message']:
                line += " message {}".format(rate_limit['message'])
                lines += line
                continue

            if rate_limit['level']:
                line += " level {}".format(rate_limit['level'])
                lines += line

        return lines

    def generate_recipient_addresses_config(self):
        if not self.recipient_addresses or len(self.recipient_addresses) == 0:
            return ""

        lines = ""

        for recipient in self.recipient_addresses:
            if not recipient['email']:
                continue

            line = "\n" if lines != "" else ""
            line += "logging recipient-address {}".format(recipient['email'])

            if recipient['level']:
                line += " level {}".format(recipient['level'])

            lines += line

        return lines

    @property
    def config(self):
        config = ""

        order = [
            'enable', 'timestamp', 'standby', 'emblem', 'lists', 'buffer_size',
            'console', 'monitor', 'buffered', 'trap', 'history', 'asdm',
            'mail', 'from_address', 'recipient_addresses', 'facility',
            'queue', 'device_id', 'hosts', 'debug_trace', 'flash_bufferwrap',
            'flash_minimum_free', 'flash_maximum_allocation', 'ftp_bufferwrap',
            'ftp_server', 'permit_hostdown', 'classes', 'messages',
            'rate_limits']

        level_parsing = [
            'asdm', 'buffered', 'console', 'history',
            'mail', 'monitor', 'trap']

        for field in order:
            value = None

            if field not in ['ftp_server']:
                value = getattr(self, field)

            if value:
                try:
                    default = object.__getattribute__(self, field).default
                except Exception:
                    default = None

                if default and value == default:
                    continue

                initial = self.initial_values.get(field)

                if self.onlyshowchanges and initial and value == initial:
                    continue

            line = "\n" if config != "" else ""

            if field in level_parsing:
                line += self.generate_level_config(field)
            elif field == 'classes':
                line += self.generate_classes_config()
            elif field == 'ftp_server':
                line += self.generate_ftp_server_config()
            elif field == 'hosts':
                line += self.generate_hosts_config()
            elif field == 'lists':
                line += self.generate_lists_config()
            elif field == 'messages':
                line += self.generate_messages_config()
            elif field == 'rate_limits':
                line += self.generate_rate_limits_config()
            elif field == 'recipient_addresses':
                line += self.generate_recipient_addresses_config()
            elif isinstance(value, bool) and value:
                line += "logging {}".format(field.replace('_', '-'))
            elif not isinstance(value, bool) and value:
                line += "logging {} {}".format(field.replace('_', '-'), str(value))

            if line not in ["\n", ""]:
                config += line

        if config == "":
            return None

        return config
Exemplo n.º 3
0
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]
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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]
Exemplo n.º 7
0
class IPAudit(Model):
    interface = StrField()
    specification_name = StrField()
    name = StrField()
    type = StrField(allowed=['attack', 'info'])
    alarm = BoolField(default=False)
    drop = BoolField(default=False)
    reset = BoolField(default=False)
    signature = AllowedField([
        IntField(low=1000, high=1006),
        IntField(low=1100, high=1103),
        IntField(low=2000, high=2012),
        IntField(low=2150, high=2151),
        IntField(low=2154, high=2154),
        IntField(low=3040, high=3042),
        IntField(low=3153, high=3154),
        IntField(low=4050, high=4052),
        IntField(low=6050, high=6053),
        IntField(low=6100, high=6103),
        IntField(low=6150, high=6155),
        IntField(low=6175, high=6175),
        IntField(low=6180, high=6180),
        IntField(low=6190, high=6190)])

    def __repr__(self):
        if self.interface:
            string = "Interface {}".format(self.interface)

        elif self.signature:
            string = "Signature {}".format(self.signature)

        elif self.name:
            string = "{}.{}".format(self.name, self.type)

        else:
            string = self.type

        return "IPAudit('{}')".format(string)

    def __str__(self):
        return self.config

    def load_config(self, config_str):
        config_str = config_str.split(' ')

        is_valid = all([
            config_str[0] == 'ip',
            config_str[1] == 'audit'])

        if not is_valid:
            return None

        config_str = config_str[2:]
        config = {}

        if config_str[0] == 'interface':
            config['interface'] = config_str[1]
            config['specification_name'] = config_str[2]

            self.load_dict(**config)
            return

        if config_str[0] == 'signature' and config_str[2] == 'disable':
            config['signature'] = config_str[1]

            self.load_dict(**config)
            return

        if config_str[0] == 'name':
            config['name'] = config_str[1]
            config_str = config_str[2:]

        config['type'] = config_str[0]

        if 'alarm' in config_str:
            config['alarm'] = True

        if 'drop' in config_str:
            config['drop'] = True

        if 'reset' in config_str:
            config['reset'] = True

        self.load_dict(**config)

    @property
    def is_valid(self):
        return any([
            all([self.interface, self.specification_name]),
            self.signature,
            all([
                self.type,
                any([
                    self.alarm,
                    self.drop,
                    self.reset])])])

    @property
    def config(self):
        if not self.is_valid:
            return None

        config = "ip audit"

        if self.interface:
            return "{} interface {} {}".format(
                config,
                self.interface,
                self.specification_name)

        if self.signature:
            return "{} signature {} disable".format(
                config,
                self.signature)

        if self.name:
            config = "{} name {}".format(
                config,
                self.name)

        config = "{} {} action".format(
            config,
            self.type)

        config = "{} alarm".format(config) if self.alarm else config
        config = "{} drop".format(config) if self.drop else config
        config = "{} reset".format(config) if self.reset else config

        return config