class V2(Serializer):
class V1(Serializer):
    def to_api(self, bond):
    def to_core(self, api_bond):
    def to_api(self, bond):
    def to_core(self, api_bond): def to_api(vlan):
def to_core(serialized):
def deserialize_ip_network(ips):
def serialize_ip_network(ips): class SwitchApiBase(object):
    def __init__(self, switch_factory, sessions_manager):
        self.switch_factory = switch_factory
        self.sessions_manager = sessions_manager

    def hook_to(self, server):
        raise NotImplemented()

    @property
    def logger(self):
        return logging.getLogger(__name__)

    def _get_switch_descriptor_from_request_headers(self, hostname):
        headers_present = [h in request.headers for h in ['Netman-Model', 'Netman-Username', 'Netman-Password']]

        if not any(headers_present):
            return None

        if not all(headers_present):
            raise BadRequest('For anonymous switch usage, please specify headers: Netman-Model, Netman-Username and Netman-Password.')

        port = None
        if "Netman-Port" in request.headers:
            try:
                port = int(request.headers["Netman-Port"])
            except ValueError:
                raise BadRequest('Netman-Port optional header should be an integer')

        netman_server = request.headers.get("Netman-Proxy-Server", None)
        if netman_server is not None and "," in netman_server:
            netman_server = [e.strip() for e in netman_server.split(",")]

        self.logger.info("Anonymous Switch Access ({}) {}@{}".format(request.headers['Netman-Model'], request.headers['Netman-Username'], hostname))
        return SwitchDescriptor(
            hostname=hostname,
            model=request.headers['Netman-Model'],
            username=request.headers['Netman-Username'],
            password=request.headers['Netman-Password'],
            port=port,
            netman_server=netman_server
        )

    def resolve_switch(self, hostname):
        switch_descriptor = self._get_switch_descriptor_from_request_headers(hostname)

        if switch_descriptor:
            return self.switch_factory.get_switch_by_descriptor(switch_descriptor)
        return self.switch_factory.get_switch(hostname)

    def resolve_session(self, session_id):
        return self.sessions_manager.get_switch_for_session(session_id) class Switch:
    def __init__(self, switch_api):
        self.switch_api = switch_api
        self.is_session = False
        self.switch = None

    def process(self, parameters):
        hostname = parameters.pop('hostname')
        try:
            self.switch = self.switch_api.resolve_session(hostname)
            self.is_session = True
        except UnknownResource:
            self.switch = self.switch_api.resolve_switch(hostname)

    def __enter__(self):
        if not self.is_session:
            self.switch.connect()
        return self.switch

    def __exit__(self, *_):
        if not self.is_session:
            self.switch.disconnect() def is_boolean(option, **_):
    option = option.lower()
    if option not in ['true', 'false']:
        raise BadRequest('Unreadable content "{}". Should be either "true" or "false"'.format(option))

    return {'state': option == 'true'} def is_vlan_number(vlan_number, **_):
    try:
        vlan_int = int(vlan_number)
    except ValueError:
        logging.getLogger("netman.api").info("Rejected vlan content : {}".format(repr(vlan_number)))
        raise BadVlanNumber()

    if not 1 <= vlan_int <= 4094:
        logging.getLogger("netman.api").info("Rejected vlan number : {}".format(vlan_number))
        raise BadVlanNumber()

    return {'vlan_number': vlan_int} class Interface:
    def __init__(self, switch_api):
        self.switch_api = switch_api
        self.interface = None

    def process(self, parameters):
        self.interface = parameters.pop('interface_id')

    def __enter__(self):
        return self.interface

    def __exit__(self, *_):
        pass class Vlan:
    def __init__(self, switch_api):
        self.switch_api = switch_api
        self.vlan = None

    def process(self, parameters):
        self.vlan = is_vlan_number(parameters.pop('vlan_number'))['vlan_number']

    def __enter__(self):
        return self.vlan

    def __exit__(self, *_):
        pass def resource(*validators):

    def resource_decorator(fn):
        @wraps(fn)
        def wrapper(self, **kwargs):
            with MultiContext(self, kwargs, *validators) as ctxs:
                return fn(self, *ctxs, **kwargs)

        return wrapper

    return resource_decorator def content(validator_fn):

    def content_decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            kwargs.update(validator_fn(request.data))
            return fn(*args, **kwargs)

        return wrapper

    return content_decorator def is_ip_network(data, **_):
    try:
        try:
            json_addr = json.loads(data)
            ip = IPNetwork("{}/{}".format(json_addr["address"], json_addr["mask"]))
        except ValueError:
            ip = IPNetwork(data)
    except (KeyError, AddrFormatError):
        raise BadRequest('Malformed content, should be : x.x.x.x/xx or {"address": "x.x.x.x", "mask": "xx"}')

    return {'validated_ip_network': ip} class IPNetworkResource:
    def __init__(self, switch_api):
        self.switch_api = switch_api
        self.ip_network = None

    def process(self, parameters):
        try:
            self.ip_network = is_ip_network(parameters.pop('ip_network'))['validated_ip_network']
        except BadRequest:
            raise BadRequest('Malformed IP, should be : x.x.x.x/xx')

    def __enter__(self):
        return self.ip_network

    def __exit__(self, *_):
        pass def is_access_group_name(data, **_):
    if data == "" or " " in data:
        raise BadRequest('Malformed access group name')

    return {'access_group_name': data} class Direction:
    def __init__(self, switch_api):
        self.switch_api = switch_api
        self.direction = None

    def process(self, parameters):
        direction = parameters.pop('direction')
        if direction.lower() == 'in':
            self.direction = IN
        elif direction.lower() == 'out':
            self.direction = OUT
        else:
            raise UnknownResource("Unknown direction : {}".format(direction))

    def __enter__(self):
        return self.direction

    def __exit__(self, *_):
        pass def is_vlan(data, **_):
    try:
        json_data = json.loads(data)
    except ValueError:
        raise BadRequest("Malformed content, should be a JSON object")

    if "number" not in json_data:
        raise BadVlanNumber()

    name = json_data["name"] if "name" in json_data and len(json_data["name"]) > 0 else None
    if name and " " in name:
        raise BadVlanName()

    return {
        'number': is_vlan_number(json_data["number"])['vlan_number'],
        'name': name
    } def is_bond(data, **_):
    try:
        json_data = json.loads(data)
    except ValueError:
        raise BadRequest("Malformed content, should be a JSON object")

    if "number" not in json_data:
        raise BadBondNumber()

    return {
        'bond_number': is_bond_number(json_data["number"])['bond_number'],
    } class Bond:
    def __init__(self, switch_api):
        self.switch_api = switch_api
        self.bond = None

    def process(self, parameters):
        self.bond = is_bond_number(parameters.pop('bond_number'))['bond_number']

    def __enter__(self):
        return self.bond

    def __exit__(self, *_):
        pass def is_bond_link_speed(data, **_):
    if re.match(r'^\d+[mg]$', data):
        return {'bond_link_speed': data}

    raise BadBondLinkSpeed() def is_bond_number(bond_number, **_):
    try:
        bond_number_int = int(bond_number)
    except ValueError:
        logging.getLogger("netman.api").info("Rejected number content : {}".format(repr(bond_number)))
        raise BadBondNumber()

    return {'bond_number': bond_number_int} def is_description(description, **_):
    return {'description': description} def is_vrf_name(data, **_):
    if data == "" or " " in data:
        raise BadRequest('Malformed VRF name')

    return {'vrf_name': data} def is_vrrp_group(data, **_):
    try:
        data = json.loads(data)
    except ValueError:
        raise BadRequest("Malformed content, should be a JSON object")

    if data.get('id') is None:
        raise BadRequest("VRRP group id is mandatory")

    return dict(
        group_id=data.pop('id'),
        ips=[validate_ip_address(i) for i in data.pop('ips', [])],
        **data
    ) class VrrpGroup:
    def __init__(self, switch_api):
        self.switch_api = switch_api
        self.vrrp_group_id = None

    def process(self, parameters):
        try:
            self.vrrp_group_id = int(parameters.pop('vrrp_group_id'))
            if not 1 <= self.vrrp_group_id <= 255:
                raise BadVrrpGroupNumber()
        except (ValueError, KeyError):
            raise BadVrrpGroupNumber()

    def __enter__(self):
        return self.vrrp_group_id

    def __exit__(self, *_):
        pass def is_dict_with(**fields):
    def m(data, **_):
        try:
            result = json.loads(data)
        except ValueError:
            raise BadRequest("Malformed JSON request")
        for field, validator in fields.iteritems():
            validator(result, field)
        for field, validator in result.iteritems():
            if field not in fields:
                raise BadRequest("Unknown key: {}".format(field))
        return result

    return m def optional(sub_validator):
    def m(params, key):
        if key in params:
            sub_validator(params, key)
    return m def is_type(obj_type):
    def m(params, key):
        if not isinstance(params[key], obj_type):
            raise BadRequest('Expected "{}" type for key {}, got "{}"'.format(obj_type.__name__, key, type(params[key]).__name__))
    return m def is_int(number, **_):
    try:
        value = int(number)
    except ValueError:
        raise BadRequest('Expected integer content, got "{}"'.format(number))

    return {'value': value} def is_unincast_rpf_mode(data, **_):
    if data not in [STRICT]:
        raise BadRequest('Invalid unicast rpf mode')

    return {'mode': data} def is_valid_mpls_state(state):
    option = str(state).lower()
    if option not in ['true', 'false']:
        raise BadMplsIpState(state)
    return {'state': option == 'true'}
from flask import request
from netman.api.api_utils import BadRequest, to_response
from netman.api.objects import bond, interface, vlan
from netman.api.switch_api_base import SwitchApiBase
from netman.api.validators import Switch, is_boolean, is_vlan_number, Interface, Vlan, resource, content, is_ip_network, \
    IPNetworkResource, is_access_group_name, Direction, is_vlan, is_bond, Bond, \
    is_bond_link_speed, is_bond_number, is_description, is_vrf_name, \
    is_vrrp_group, VrrpGroup, is_dict_with, optional, is_type, is_int, is_unincast_rpf_mode
from netman.core.objects.interface_states import OFF, ON
from netman.core.validator import is_valid_mpls_state

        .. literalinclude:: ../doc_config/api_samples/get_switch_hostname_vlans.json
            :language: json

        """
        vlans = sorted(switch.get_vlans(), key=lambda x: x.number)

        return 200, [vlan.to_api(v) for v in vlans]

    @to_response
    @resource(Switch, Vlan)
    def get_vlan_interfaces(self, switch, vlan_number):
        """
        Displays interfaces use in a VLAN

        :arg str hostname: Hostname or IP of the switch
        :arg int vlan_number: Vlan number, between 1 and 4096
        :code 200 OK:

        Example output:

        .. literalinclude:: ../doc_config/api_samples/get_switch_hostname_vlans_vlan_interfaces.json
            :language: json

        """

        return 200, switch.get_vlan_interfaces(vlan_number)

    @to_response
    @resource(Switch, Vlan)
    def get_vlan(self, switch, vlan_number):
        """
        Displays informations about one VLAN

        :arg str hostname: Hostname or IP of the switch
        :arg int vlan_number: Vlan number, between 1 and 4096
        :code 200 OK:

        Example output:

        .. literalinclude:: ../doc_config/api_samples/get_switch_hostname_vlans_vlan.json
            :language: json

        """

        return 200, vlan.to_api(switch.get_vlan(vlan_number))

    @to_response
    @content(is_vlan)
    @resource(Switch)
    def add_vlan(self, switch, number, name):
        """
        Create an new VLAN

        :arg str hostname: Hostname or IP of the switch
        :body:
            Highlighted fields are mandatory

            .. literalinclude:: ../doc_config/api_samples/post_switch_hostname_vlans.json
                :language: json
                :emphasize-lines: 2

        """

        switch.add_vlan(number, name)
        return 201, None

    @to_response
    @resource(Switch, Vlan)
    def remove_vlan(self, switch, vlan_number):
        """
        Deletes a VLAN

        :arg str hostname: Hostname or IP of the switch
        :arg int vlan_number: Vlan number, between 1 and 4096

        """

        switch.remove_vlan(vlan_number)
        return 204, None

    @to_response
    @content(is_ip_network)
    @resource(Switch, Vlan)
    def add_ip(self, switch, vlan_number, validated_ip_network):
        """
        Adds an IP/Subnet to a vlan

        :arg str hostname: Hostname or IP of the switch
        :arg int vlan_number: Vlan number, between 1 and 4096
        :body:
            Highlighted fields are mandatory

            .. literalinclude:: ../doc_config/api_samples/post_switch_hostname_vlans_vlanid_ips.json
                :language: json
                :emphasize-lines: 2-3

            or

            .. literalinclude:: ../doc_config/api_samples/post_switch_hostname_vlans_vlanid_ips.txt
        """

        switch.add_ip_to_vlan(vlan_number, validated_ip_network)
        return 201, None

    @to_response
    @resource(Switch, Vlan, IPNetworkResource)
    def remove_ip(self, switch, vlan_number, ip_network):
        """
        Removes an IP/Subnet from a vlan

        :arg str hostname: Hostname or IP of the switch
        :arg int vlan_number: Vlan number, between 1 and 4096
        :arg str ip_network: IP/Subnet in the "x.x.x.x/xx" format
        """

        switch.remove_ip_from_vlan(vlan_number, ip_network)
        return 204, None

    @to_response

    @content(is_vrrp_group)