def interface_speed(speed):
    return to_ele("""
        <speed>
            <ethernet-{0}/>
        </speed>
    """.format(speed)) def interface_replace(interface_name, *ether_options):
    content = to_ele("""
        <interface operation=\"replace\">
            <name>{0}</name>
            <ether-options>
            </ether-options>
        </interface>
    """.format(interface_name))

    ether_options_node = first(content.xpath("//ether-options"))
    map(ether_options_node.append, ether_options)

    return content def interface_speed_update(interface_name, speed):
    return to_ele("""
        <interface>
            <name>{0}</name>
            <ether-options>
                <speed>
                    <ethernet-{1}/>
                </speed>
            </ether-options>
        </interface>
    """.format(interface_name, speed)) def first_text(node):
    return node[0].text if node else None def bond_name(number):
    return "ae{0}".format(number) class Juniper(SwitchBase):

    def __init__(self, switch_descriptor, custom_strategies,
                 timeout=300):
        super(Juniper, self).__init__(switch_descriptor)
        self.timeout = timeout
        self.custom_strategies = custom_strategies
        self.netconf = None

        self.in_transaction = False

    def _connect(self):
        params = dict(
            host=self.switch_descriptor.hostname,
            username=self.switch_descriptor.username,
            password=self.switch_descriptor.password,
            hostkey_verify=False,
            device_params={'name': 'junos'},
            timeout=self.timeout
        )

        if self.switch_descriptor.port:
            params["port"] = self.switch_descriptor.port

        self.netconf = manager.connect(**params)

    def _disconnect(self):
        try:
            self.netconf.close_session()
        except TimeoutExpiredError:
            pass

    def start_transaction(self):
        try:
            self.netconf.lock(target="candidate")
        except RPCError as e:
            if "configuration database modified" in e.message:
                self.rollback_transaction()
                self.netconf.lock(target="candidate")
            elif "Configuration database is already open" in e.message:
                raise LockedSwitch()
            else:
                raise
        self.in_transaction = True

    def end_transaction(self):
        self.in_transaction = False
        self.netconf.unlock(target="candidate")

    def rollback_transaction(self):
        self.netconf.discard_changes()

    def commit_transaction(self):
        try:
            self.netconf.commit()
        except RPCError as e:
            self.logger.info("An RPCError was raised : {}".format(e))
            raise OperationNotCompleted(str(e).strip())

    def get_vlans(self):
        config = self.query(self.custom_strategies.all_vlans, all_interfaces)

        vlan_list = []
        for vlan_node in self.custom_strategies.vlan_nodes(config):
            vlan = self.get_vlan_from_node(vlan_node, config)
            if vlan is not None:
                vlan_list.append(vlan)

        return vlan_list

    def get_vlan(self, number):
        config = self.query(self.custom_strategies.all_vlans, all_interfaces)
        vlan_node = self.custom_strategies.vlan_node(config, number)
        return self.get_vlan_from_node(vlan_node, config)

    def get_vlan_from_node(self, vlan_node, config):
        vlan_id_node = first(vlan_node.xpath("vlan-id"))

        vlan = None
        if vlan_id_node is not None:
            vlan = Vlan(number=int(vlan_id_node.text), icmp_redirects=True)

            description_node = first(vlan_node.xpath("description"))
            if description_node is not None:
                vlan.name = description_node.text

            l3_if_type, l3_if_name = self.custom_strategies.get_l3_interface(vlan_node)
            if l3_if_name is not None:
                interface_vlan_node = first(config.xpath("data/configuration/interfaces/interface/name[text()=\"{}\"]/.."
                                                         "/unit/name[text()=\"{}\"]/..".format(l3_if_type, l3_if_name)))
                if interface_vlan_node is not None:
                    vlan.ips = parse_ips(interface_vlan_node)
                    vlan.access_groups[IN] = parse_inet_filter(interface_vlan_node, "input")
                    vlan.access_groups[OUT] = parse_inet_filter(interface_vlan_node, "output")
                    vlan.vrrp_groups = self.custom_strategies.parse_vrrp_groups(interface_vlan_node)
                    vlan.icmp_redirects = self.custom_strategies.parse_icmp_redirects(interface_vlan_node)
        return vlan

    def get_interfaces(self):
        physical_interfaces = self._list_physical_interfaces()
        config = self.query(all_interfaces, self.custom_strategies.all_vlans)

        interface_list = []
        for phys_int in physical_interfaces:
            if not phys_int.name.startswith("ae"):
                interface_node = first(config.xpath("data/configuration/interfaces/interface/name[text()=\"{}\"]/.."
                                                    .format(phys_int.name)))
                if interface_node is not None:
                    interface_list.append(self.node_to_interface(interface_node, config))
                else:
                    interface_list.append(phys_int.to_interface())

        return interface_list

    def add_vlan(self, number, name=None):
        config = self.query(self.custom_strategies.all_vlans)

        try:
            self.custom_strategies.vlan_node(config, number)
            raise VlanAlreadyExist(number)
        except UnknownVlan:
            pass

        update = Update()
        self.custom_strategies.add_update_vlans(update, number, name)

        try:
            self._push(update)
        except RPCError as e:
            self.custom_strategies.manage_update_vlan_exception(e.message, number)
            raise

    def remove_vlan(self, number):
        config = self.query(self.custom_strategies.all_vlans, all_interfaces)

        vlan_node = self.custom_strategies.vlan_node(config, number)
        vlan_name = first(vlan_node.xpath("name")).text

        update = Update()
        self.custom_strategies.remove_update_vlans(update, vlan_name)

        l3_if_type, l3_if_name = self.custom_strategies.get_l3_interface(vlan_node)
        if l3_if_name is not None:
            update.add_interface(interface_unit_interface_removal(l3_if_type, l3_if_name))

        for interface_node in config.xpath("data/configuration/interfaces/interface"):
            members_modifications = self.custom_strategies.craft_members_modification_to_remove_vlan(interface_node, vlan_name, number)

            if len(members_modifications) > 0:
                update.add_interface(self.custom_strategies.interface_vlan_members_update(
                    first(interface_node.xpath("name")).text,
                    first(interface_node.xpath("unit/name")).text,
                    members_modifications)
                )

        self._push(update)

    def set_access_mode(self, interface_id):
        update_attributes = []

        config = self.query(all_interfaces, self.custom_strategies.all_vlans)

        interface_node = self.get_interface_config(interface_id, config)

        interface = self.node_to_interface(interface_node, config)

        if self.get_port_mode(interface_node) in (TRUNK, None):
            update_attributes.append(self.custom_strategies.get_interface_port_mode_update_element("access"))

        if len(interface.trunk_vlans) > 0:
            update_attributes.append(self.custom_strategies.get_delete_trunk_vlan_element())

        if interface.trunk_native_vlan is not None:
            update_attributes.append(to_ele('<native-vlan-id operation="delete" />'))

        if len(update_attributes) > 0:
            update = Update()
            update.add_interface(self.custom_strategies.interface_update(interface_id, "0", update_attributes))
            self._push_interface_update(interface_id, update)

    def set_trunk_mode(self, interface_id):
        update_attributes = []

        config = self.query(one_interface(interface_id), self.custom_strategies.all_vlans)
        interface_node = self.get_interface_config(interface_id, config)
        interface = self.node_to_interface(interface_node, config)

        if interface.port_mode is ACCESS or interface.port_mode is None:
            update_attributes.append(self.custom_strategies.get_interface_port_mode_update_element("trunk"))

        if interface.access_vlan is not None:
            update_attributes.append(self.custom_strategies.get_delete_vlan_element())

        if len(update_attributes) > 0:
            update = Update()
            update.add_interface(self.custom_strategies.interface_update(interface_id, "0", update_attributes))

            self._push_interface_update(interface_id, update)

    def set_access_vlan(self, interface_id, vlan):
        update_attributes = []
        update_vlan_members = []

        config = self.query(all_interfaces, self.custom_strategies.all_vlans)

        self.custom_strategies.vlan_node(config, vlan)

        interface_node = self.get_interface_config(interface_id, config)
        interface = self.node_to_interface(interface_node, config)

        if interface.port_mode == TRUNK:
            raise InterfaceInWrongPortMode("trunk")
        elif self.get_port_mode(interface_node) is None:
            update_attributes.append(self.custom_strategies.get_interface_port_mode_update_element("access"))

        if interface.access_vlan != vlan:
            self.custom_strategies.update_vlan_members(interface_node, update_vlan_members, vlan)

        if update_attributes or update_vlan_members:
            update = Update()
            update.add_interface(self.custom_strategies.interface_update(interface_id, "0", update_attributes, update_vlan_members))

            try:
                self._push_interface_update(interface_id, update)
            except RPCError as e:
                if "No vlan matches vlan tag" in e.message:
                    raise UnknownVlan(vlan)
                raise

    def unset_interface_access_vlan(self, interface_id):
        config = self.query(one_interface(interface_id), self.custom_strategies.all_vlans)
        interface_node = self.get_interface_config(interface_id, config)
        interface = self.node_to_interface(interface_node, config)

        if interface.port_mode == TRUNK:
            raise InterfaceInWrongPortMode("trunk")

        if interface.access_vlan is not None:
            update = Update()
            update.add_interface(self.custom_strategies.interface_update(interface_id, "0", [to_ele('<vlan operation="delete" />')]))

            self._push(update)
        else:
            raise AccessVlanNotSet(interface_id)

    def set_interface_native_vlan(self, interface_id, vlan):
        port_mode_node = None
        native_vlan_id_node = None

        config = self.query(all_interfaces, self.custom_strategies.all_vlans)

        self.custom_strategies.vlan_node(config, vlan)

        interface_node = self.get_interface_config(interface_id, config)

        interface = self.node_to_interface(interface_node, config)

        actual_port_mode = self.get_port_mode(interface_node)
        if actual_port_mode is ACCESS:
            raise InterfaceInWrongPortMode("access")
        elif actual_port_mode is None:
            port_mode_node = self.custom_strategies.get_interface_port_mode_update_element("trunk")

        if vlan in interface.trunk_vlans:
            raise VlanAlreadyInTrunk(vlan)
        elif interface.trunk_native_vlan != vlan:
            native_vlan_id_node = to_ele("<native-vlan-id>{}</native-vlan-id>".format(vlan))

        if native_vlan_id_node is not None:

            interface = self.custom_strategies.interface_update(interface_id, "0", [port_mode_node] if port_mode_node is not None else [])
            self.custom_strategies.set_native_vlan_id_node(interface, native_vlan_id_node)
            update = Update()
            update.add_interface(interface)

            try:
                self._push_interface_update(interface_id, update)
            except RPCError as e:
                if "No vlan matches vlan tag" in e.message:
                    raise UnknownVlan(vlan)
                raise

    def set_interface_auto_negotiation_state(self, interface_id, negotiation_state):
        content = to_ele("""
            <interface>
                <name>{0}</name>
            </interface>
            """.format(interface_id))
        if negotiation_state == ON:
            content.append(to_ele("<ether-options><auto-negotiation/></ether-options>"))
        else:
            content.append(to_ele("<ether-options><no-auto-negotiation/></ether-options>"))
        update = Update()
        update.add_interface(content)

        self._push_interface_update(interface_id, update)

    def unset_interface_auto_negotiation_state(self, interface_id):
        config = self.query(one_interface(interface_id))
        interface_node = self.get_interface_config(interface_id, config)

        if interface_node is None:
            self._get_physical_interface(interface_id)
            return

        auto_negotiation_present = first(interface_node.xpath('ether-options/auto-negotiation')) is not None
        no_auto_negotiation_present = first(interface_node.xpath('ether-options/no-auto-negotiation')) is not None

        if auto_negotiation_present or no_auto_negotiation_present:
            content = to_ele("""
                <interface>
                    <name>{0}</name>
                </interface>
            """.format(interface_id))
            ether_options = to_ele("<ether-options/>")
            if auto_negotiation_present:
                ether_options.append(to_ele("<auto-negotiation operation=\"delete\"/>"))
            elif no_auto_negotiation_present:
                ether_options.append(to_ele("<no-auto-negotiation operation=\"delete\"/>"))
            update = Update()

            content.append(ether_options)
            update.add_interface(content)

            self._push_interface_update(interface_id, update)

    def reset_interface(self, interface_id):
        content = to_ele("""
            <interface operation=\"delete\">
                <name>{0}</name>
            </interface>
        """.format(interface_id))
        update = Update()
        update.add_interface(content)

        self._push_interface_update(interface_id, update)

    def unset_interface_native_vlan(self, interface_id):
        config = self.query(one_interface(interface_id), self.custom_strategies.all_vlans)
        interface_node = self.get_interface_config(interface_id, config)
        interface = self.node_to_interface(interface_node, config)

        if interface.trunk_native_vlan is None:
            raise NativeVlanNotSet(interface_id)

        update = Update()
        update.add_interface(self.custom_strategies.interface_update(interface_id, "0", [to_ele("<native-vlan-id operation=\"delete\" />")]))

        self._push(update)

    def add_trunk_vlan(self, interface_id, vlan):
        config = self.query(all_interfaces, self.custom_strategies.all_vlans)

        self.custom_strategies.vlan_node(config, vlan)

        interface_node = self.get_interface_config(interface_id, config)

        interface = self.node_to_interface(interface_node, config)

        actual_port_mode = self.get_port_mode(interface_node)

        if actual_port_mode is ACCESS or interface.access_vlan is not None:
            raise InterfaceInWrongPortMode("access")

        if vlan not in interface.trunk_vlans:
            update = Update()
            update.add_interface(self.custom_strategies.interface_update(
                interface_id, "0",
                [self.custom_strategies.get_interface_port_mode_update_element("trunk")] if actual_port_mode is None else None,
                [self.custom_strategies.get_vlan_member_update_element(vlan)]
            ))

            self._push_interface_update(interface_id, update)

    def remove_trunk_vlan(self, interface_id, vlan):
        config = self.query(all_interfaces, self.custom_strategies.all_vlans)
        interface_node = self.get_interface_config(interface_id, config)
        if interface_node is None:
            raise UnknownInterface(interface_id)

        interface = self.node_to_interface(interface_node, config)

        if interface.port_mode is ACCESS:
            raise InterfaceInWrongPortMode("access")

        vlan_node = self.custom_strategies.vlan_node(config, vlan)
        vlan_name = first(vlan_node.xpath("name")).text

        modifications = self.custom_strategies.craft_members_modification_to_remove_vlan(interface_node, vlan_name, vlan)
        if len(modifications) == 0:
            raise TrunkVlanNotSet(interface_id)

        update = Update()
        update.add_interface(self.custom_strategies.interface_update(interface_id, "0", vlan_members=modifications))

        self._push(update)

    def set_interface_description(self, interface_id, description):
        update = Update()
        update.add_interface(interface_main_update(interface_id, [
            to_ele("<description>{}</description>".format(description))
        ]))

        try:
            self._push(update)
        except RPCError as e:
            self.logger.info("actual setting error was {}".format(e))
            raise UnknownInterface(interface_id)

    def unset_interface_description(self, interface_id):
        update = Update()
        update.add_interface(interface_main_update(interface_id, [
            to_ele("<description operation=\"delete\" />")
        ]))

        try:
            self._push(update)
        except RPCError as e:
            if e.severity != "warning":
                raise UnknownInterface(interface_id)

    def set_interface_mtu(self, interface_id, size):
        update = Update()
        update.add_interface(interface_main_update(interface_id, [
            to_ele("<mtu>{}</mtu>".format(size))
        ]))

        try:
            self._push(update)
        except RPCError as e:
            self.logger.info("actual setting error was {}".format(e))
            if "Value {} is not within range".format(size) in str(e):
                raise InvalidMtuSize(str(e))

            raise UnknownInterface(interface_id)

    def unset_interface_mtu(self, interface_id):
        update = Update()
        update.add_interface(interface_main_update(interface_id, [
            to_ele("<mtu operation=\"delete\" />")
        ]))

        try:
            self._push(update)
        except RPCError as e:
            if e.severity != "warning":
                raise UnknownInterface(interface_id)

    def edit_interface_spanning_tree(self, interface_id, edge=None):
        config = self.query(one_interface(interface_id),
                            one_protocol_interface("rstp", self._for_protocol(interface_id)))

        if edge is not None:
            modifications = _compute_edge_state_modifications(interface_id, edge, config)

            if modifications:
                update = Update()
                update.add_protocol_interface("rstp", to_ele("""
                   <interface>
                     <name>{}</name>
                     {}
                   </interface>
                """.format(interface_id, "".join(modifications))))

                self._push_interface_update(interface_id, update)

    def set_interface_state(self, interface_id, state):
        update = Update()
        update.add_interface(interface_state_update(interface_id, state))

        try:
            self._push(update)
        except RPCError as e:
            self.logger.info("actual setting error was {}".format(e))
            # When sending a "delete operation" on a nonexistent element <disable />, this is the error that is thrown.
            # It's ignored because the result of this operation would be the same as if the command was successful.
            if "statement not found" not in e.message:
                raise UnknownInterface(interface_id)

    def unset_interface_state(self, interface_id):
        self.set_interface_state(interface_id, state=ON)

    def set_interface_lldp_state(self, interface_id, enabled):
        config = self.query(one_interface(interface_id),
                            one_protocol_interface("lldp", self._for_protocol(interface_id)))
        self.get_interface_config(interface_id, config)

        update_ele = None
        disabled_node = first(config.xpath("data/configuration/protocols/lldp/interface/name"
                                           "[text()=\"{0:s}\"]/../disable".format(self._for_protocol(interface_id))))
        if enabled:
            update_ele = protocol_interface_update(interface_id)
            if disabled_node is not None:
                update_ele.append(to_ele('<disable operation="delete"/>'))
        elif not enabled and disabled_node is None:
            update_ele = protocol_interface_update(interface_id)
            update_ele.append(to_ele('<disable/>'))

        if update_ele is not None:
            update = Update()
            update.add_protocol_interface("lldp", update_ele)

            self._push_interface_update(interface_id, update)

    def add_bond(self, number):
        config = self.query(one_interface(bond_name(number)))
        if len(config.xpath("data/configuration/interfaces/interface")) > 0:
            raise BondAlreadyExist(number)

        update = Update()
        update.add_interface(bond_update(number, bond_lacp_options()))

        try:
            self._push(update)
        except RPCError as e:
            if "device value outside range" in e.message:
                raise BadBondNumber()

            raise

    def remove_bond(self, number):
        config = self.query(all_interfaces, one_protocol_interface("rstp", self._for_protocol(bond_name(number))))
        self.get_bond_config(number, config)

        update = Update()
        update.add_interface(interface_removal(bond_name(number)))

        rstp_node = first(config.xpath("data/configuration/protocols/rstp/interface/name[text()=\"{0:s}\"]/.."
                                       .format(self._for_protocol(bond_name(number)))))
        if rstp_node is not None:
            update.add_protocol_interface("rstp", rstp_interface_removal(bond_name(number)))

        for interface_node in self.get_bond_slaves_config(number, config):
            interface_name = first(interface_node.xpath("name")).text
            update.add_interface(free_from_bond_operation(interface_name))

        self._push(update)

    def add_interface_to_bond(self, interface, bond_id):
        config = self.query(all_interfaces, self.custom_strategies.all_vlans, rstp_protocol_interfaces)
        bond = self.node_to_bond(self.get_bond_config(bond_id, config), config)

        update = Update()
        self.custom_strategies.add_enslave_to_bond_operations(update, interface, bond)

        rstp_node = first(config.xpath("data/configuration/protocols/rstp/interface/name[text()=\"{0:s}\"]"
                                       .format(self._for_protocol(interface))))
        if rstp_node is not None:
            update.add_protocol_interface("rstp", rstp_interface_removal(interface))

        self._push_interface_update(interface, update)

    def remove_interface_from_bond(self, interface):
        try:
            update = Update()
            update.add_interface(free_from_bond_operation(interface))
            self._push(update)
        except RPCError:
            self._get_physical_interface(interface)

            raise InterfaceNotInBond()

    def set_bond_link_speed(self, number, speed):
        config = self.query(all_interfaces)
        self.get_bond_config(number, config)

        update = Update()
        update.add_interface(bond_update(number, bond_link_speed(speed)))

        self.custom_strategies.add_update_bond_members_speed_operations(
            update,
            self.get_bond_slaves_config(number, config),
            speed
        )

        self._push(update)

    def get_bond(self, number):
        config = self.query(all_interfaces, self.custom_strategies.all_vlans)

        bond_node = self.get_bond_config(number, config)
        return self.node_to_bond(bond_node, config, self.get_bond_slaves_config(
            value_of(bond_node.xpath("name"), transformer=bond_number), config))

    def get_bonds(self):
        config = self.query(all_interfaces, self.custom_strategies.all_vlans)
        bond_nodes = config.xpath("data/configuration/interfaces/interface/aggregated-ether-options/..")
        return [
            self.node_to_bond(node, config, self.get_bond_slaves_config(
                value_of(node.xpath("name"), transformer=bond_number), config))
            for node in bond_nodes]

    def set_bond_description(self, number, description):
        return self.set_interface_description(bond_name(number), description)

    def unset_bond_description(self, number):
        return self.unset_interface_description(bond_name(number))

    def set_bond_mtu(self, number, size):
        return self.set_interface_mtu(bond_name(number), size)

    def unset_bond_mtu(self, number):
        return self.unset_interface_mtu(bond_name(number))

    def set_bond_trunk_mode(self, number):
        return self.set_trunk_mode(bond_name(number))

    def set_bond_access_mode(self, number):
        return self.set_access_mode(bond_name(number))

    def add_bond_trunk_vlan(self, number, vlan):
        return self.add_trunk_vlan(bond_name(number), vlan)

    def remove_bond_trunk_vlan(self, number, vlan):
        return self.remove_trunk_vlan(bond_name(number), vlan)

    def set_bond_native_vlan(self, number, vlan):
        return self.set_interface_native_vlan(bond_name(number), vlan)

    def unset_bond_native_vlan(self, number):
        return self.unset_interface_native_vlan(bond_name(number))

    def edit_bond_spanning_tree(self, number, edge=None):
        return self.edit_interface_spanning_tree(bond_name(number), edge=edge)

    def _push_interface_update(self, interface_id, configuration):
        try:
            self._push(configuration)
        except RPCError as e:
            if "port value outside range" in e.message \
                    or "invalid interface type" in e.message \
                    or "device value outside range" in e.message:
                raise UnknownInterface(interface_id)
            raise

    def _push(self, configuration):
        config = new_ele('config')
        config.append(configuration.root)

        self.logger.info("Sending edit : {}".format(to_xml(config)))
        try:
            self.netconf.edit_config(target="candidate", config=config)
        except RPCError as e:
            self.logger.info("An RPCError was raised : {}".format(e))
            raise

    def query(self, *args):
        filter_node = new_ele("filter")
        conf = sub_ele(filter_node, "configuration")
        for arg in args:
            conf.append(arg())
        return self.netconf.get_config(source="candidate" if self.in_transaction else "running", filter=filter_node)

    def get_interface(self, interface_id):
        config = self.query(one_interface(interface_id), self.custom_strategies.all_vlans)
        interface_node = self.get_interface_config(interface_id, config)
        if interface_node is not None:
            return self.node_to_interface(interface_node, config)

        return self._get_physical_interface(interface_id).to_interface()

    def get_interface_config(self, interface_id, config=None):
        config = config or self.query(one_interface(interface_id))
        interface_node = first(config.xpath(
            "data/configuration/interfaces/interface/name[text()=\"{0:s}\"]/..".format(interface_id)))
        return interface_node

    def get_bond_config(self, number, config):
        interface_node = first(config.xpath("data/configuration/interfaces/interface/name[text()=\"{}\"]/..".format(bond_name(number))))
        if interface_node is None:
            raise UnknownBond(number)
        return interface_node

    def get_bond_slaves_config(self, bond_id, config=None):
        config = config or self.query(all_interfaces)

        return config.xpath(
            'data/configuration/interfaces/interface/ether-options/'
            'ieee-802.3ad/bundle[text()=\"{0}\"]/../../..'.format(
                bond_name(bond_id)))

    def get_port_mode(self, interface_node):
        if interface_node is None:
            return None

        if get_bond_master(interface_node) is not None:
            return BOND_MEMBER
        actual_port_mode_node = first(self.custom_strategies.get_port_mode_node_in_inteface_node(interface_node))
        if actual_port_mode_node is None:
            return None
        else:
            return {"access": ACCESS, "trunk": TRUNK}[actual_port_mode_node.text]

    def fill_interface_from_node(self, interface, interface_node, config):
        if interface_node is not None:
            interface.port_mode = self.get_port_mode(interface_node) or ACCESS
            vlans = self.custom_strategies.list_vlan_members(interface_node, config)
            if interface.port_mode is ACCESS:
                interface.access_vlan = first(vlans)
            else:
                interface.trunk_vlans = vlans
            interface.trunk_native_vlan = value_of(self.custom_strategies.get_interface_trunk_native_vlan_id_node(interface_node), transformer=int)
            interface.shutdown = first(interface_node.xpath("disable")) is not None
            interface.mtu = value_of(interface_node.xpath("mtu"), transformer=int)
            if first(interface_node.xpath('ether-options/auto-negotiation')) is not None:
                interface.auto_negotiation = True
            elif first(interface_node.xpath('ether-options/no-auto-negotiation')) is not None:
                interface.auto_negotiation = False
        return interface

    def node_to_interface(self, interface_node, config):
        interface = Interface()
        if interface_node is not None:
            interface.name = value_of(interface_node.xpath("name"))
            interface.bond_master = get_bond_master(interface_node)
        self.fill_interface_from_node(interface, interface_node, config)
        return interface

    def node_to_bond(self, bond_node, config, member_nodes=None):
        member_nodes = member_nodes or []
        bond = Bond(
            number=value_of(bond_node.xpath("name"), transformer=bond_number),
            link_speed=first_text(bond_node.xpath("aggregated-ether-options/link-speed")),
            members=[first_text(member.xpath('name')) for member in member_nodes]
        )
        self.fill_interface_from_node(bond, bond_node, config)
        return bond

    def get_vlan_interfaces(self, vlan_number):
        config = self.query(self.custom_strategies.one_vlan_by_vlan_id(vlan_number), all_interfaces)

        vlan_node = self.custom_strategies.vlan_node(config, vlan_number)
        interface_nodes = config.xpath("data/configuration/interfaces/interface")
        return self.get_vlan_interfaces_from_node(vlan_node, interface_nodes)

    def get_vlan_interfaces_from_node(self, vlan_node, interface_nodes):
        vlan_name = first(vlan_node.xpath("name")).text
        vlan_number = int(first(vlan_node.xpath("vlan-id")).text)
        interfaces = []
        for interface in interface_nodes:
            native_vlan_id_node = self.custom_strategies.get_interface_trunk_native_vlan_id_node(interface)
            if len(native_vlan_id_node) == 1 and int(first(native_vlan_id_node).text) == vlan_number:
                interfaces.append(first(interface.xpath("name")).text)

            members = [members.text for members in interface.xpath("unit/family/ethernet-switching/vlan/members")]
            if _is_vlan_in_interface_members(vlan_number, vlan_name, members):
                interfaces.append(first(interface.xpath("name")).text)
        return interfaces

    def _get_physical_interface(self, interface_id):
        try:
            return next(i for i in self._list_physical_interfaces() if i.name == interface_id)
        except StopIteration:
            raise UnknownInterface(interface_id)

    def _list_physical_interfaces(self):
        terse = self.netconf.rpc(to_ele("""
            <get-interface-information>
              <terse/>
            </get-interface-information>
        """))

        return [_PhysicalInterface(i.xpath("name")[0].text.strip(),
                                   shutdown=i.xpath("admin-status")[0].text.strip() == "down")
                for i in terse.xpath("interface-information/physical-interface")]

    def _for_protocol(self, interface_name):
        return self.custom_strategies.get_protocols_interface_name(interface_name)

    def get_mac_addresses(self):
        mac_table = self.netconf.rpc(to_ele("""
            <get-ethernet-switching-table-information>
                <detail/>
            </get-ethernet-switching-table-information>
        """))

        return self.custom_strategies.parse_mac_address_table(mac_table) def first(node):
    return node[0] if node else None def value_of(xpath_result, transformer=None):
    node = first(xpath_result)
    if node is not None:
        return node.text if transformer is None else transformer(node.text)
    else:
        return None def parse_range(r):
    if regex.match("(\d+)-(\d+)", r):
        return range(int(regex[0]), int(regex[1]) + 1)
    elif regex.match("(\d+)", r):
        return [int(regex[0])]
    return [] def to_range(number_list):
    if len(number_list) > 1:
        return "{}-{}".format(number_list[0], number_list[-1])
    else:
        return str(number_list[0])
from ncclient.xml_ import to_ele, new_ele
from netman.adapters.switches.juniper.base import interface_speed, interface_replace, interface_speed_update, \
    first_text, bond_name, Juniper, first, value_of, parse_range, to_range
from netman.core.objects.exceptions import BadVlanName, BadVlanNumber, VlanAlreadyExist, UnknownVlan
from netman.core.objects.mac_address import MacAddress
# Copyright 2015 Internap.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.



def netconf(switch_descriptor, *args, **kwargs):
    return Juniper(switch_descriptor, custom_strategies=JuniperCustomStrategies(), *args, **kwargs)


class JuniperCustomStrategies(object):
    def get_interface_port_mode_update_element(self, mode):
        return to_ele("<port-mode>{}</port-mode>".format(mode))

    def get_vlan_member_update_element(self, vlan):
        return to_ele("<members>{}</members>".format(vlan))

    def get_port_mode_node_in_inteface_node(self, interface_node):
        return interface_node.xpath("unit/family/ethernet-switching/port-mode")

    def add_enslave_to_bond_operations(self, update, interface, bond):
        ether_options = [
            to_ele("""
                <ieee-802.3ad>
                    <bundle>{0}</bundle>
                </ieee-802.3ad>
            """.format(bond_name(bond.number)))]

        if bond.link_speed is not None:
            ether_options.append(interface_speed(bond.link_speed))


        update.add_interface(interface_replace(interface, *ether_options))