def test_changing_default_command_timeout(self):
        shell.base.default_command_timeout = 300

        ssh = SshClient(**self._get_some_credentials())
        self.assertEqual(300, ssh.command_timeout)

        shell.default_command_timeout = 600

        ssh2 = SshClient(**self._get_some_credentials())
        self.assertEqual(600, ssh2.command_timeout)
    def test_changing_default_connect_timeout(self, open_channel_method_mock):
        shell.default_connect_timeout = 60

        SshClient(**self._get_some_credentials())
        self.assertEqual(60, open_channel_method_mock.call_args[0][4])

        shell.default_connect_timeout = 120

        SshClient(**self._get_some_credentials())
        self.assertEqual(120, open_channel_method_mock.call_args[0][4])
Exemple #3
0
    def _connect(self):
        params = dict(
            host=self.switch_descriptor.hostname,
            username=self.switch_descriptor.username,
            password=self.switch_descriptor.password,
        )
        if self.switch_descriptor.port:
            params["port"] = self.switch_descriptor.port

        self.ssh = SshClient(**params)

        if self.ssh.get_current_prompt().endswith(">"):
            self.ssh.do("enable", wait_for=": ")
            self.ssh.do(self.switch_descriptor.password)

        self.ssh.do("terminal length 0")
        self.ssh.do("terminal width 0")
Exemple #4
0
    def _connect(self):
        params = dict(
            host=self.switch_descriptor.hostname,
            username=self.switch_descriptor.username,
            password=self.switch_descriptor.password,
        )
        if self.switch_descriptor.port:
            params["port"] = self.switch_descriptor.port

        self.ssh = SshClient(**params)

        if self.ssh.get_current_prompt().endswith(">"):
            self.ssh.do("enable", wait_for=": ")
            self.ssh.do(self.switch_descriptor.password)

        self.ssh.do("terminal length 0")
        self.ssh.do("terminal width 0")
Exemple #5
0
class Cisco(SwitchBase):

    def __init__(self, switch_descriptor):
        super(Cisco, self).__init__(switch_descriptor)
        self.ssh = None

    def _connect(self):
        params = dict(
            host=self.switch_descriptor.hostname,
            username=self.switch_descriptor.username,
            password=self.switch_descriptor.password,
        )
        if self.switch_descriptor.port:
            params["port"] = self.switch_descriptor.port

        self.ssh = SshClient(**params)

        if self.ssh.get_current_prompt().endswith(">"):
            self.ssh.do("enable", wait_for=": ")
            self.ssh.do(self.switch_descriptor.password)

        self.ssh.do("terminal length 0")
        self.ssh.do("terminal width 0")

    def _disconnect(self):
        self.ssh.quit("exit")
        self.logger.info(self.ssh.full_log)

    def _end_transaction(self):
        pass

    def _start_transaction(self):
        pass

    def commit_transaction(self):
        self.ssh.do("write memory")

    def rollback_transaction(self):
        pass

    def get_vlan(self, number):
        vlan = Vlan(int(number), icmp_redirects=True)
        apply_vlan_running_config_data(vlan, self._get_vlan_run_conf(number))
        apply_interface_running_config_data(
            vlan,
            self.ssh.do("show running-config interface vlan {} | begin interface".format(number))
        )
        return vlan

    def get_vlans(self):
        vlan_list = self.ssh.do("show vlan brief")

        vlans = {}
        for line in vlan_list:
            if regex.match('^(\d+)\s+(\S+).*', line):
                number, name = regex

                if name == ("VLAN{}".format(number)):
                    name = None

                vlans[number] = Vlan(int(number), name, icmp_redirects=True)

        for ip_interface_data in split_on_dedent(self.ssh.do("show ip interface")):
            if regex.match("^Vlan(\d+)\s.*", ip_interface_data[0]):
                current_vlan = vlans.get(regex[0])
                if current_vlan:
                    apply_interface_running_config_data(
                        current_vlan,
                        self.ssh.do("show running-config interface vlan {}".format(current_vlan.number))
                    )
        return vlans.values()

    def add_vlan(self, number, name=None):
        if self._show_run_vlan(number):
            raise VlanAlreadyExist(number)

        with self.config():
            result = self.ssh.do('vlan {}'.format(number))
            if len(result) > 0:
                raise BadVlanNumber()
            else:
                if name:
                    result = self.ssh.do('name {}'.format(name))

                self.ssh.do('exit')

                if len(result) > 0:
                    raise BadVlanName()

    def remove_vlan(self, number):
        self._get_vlan_run_conf(number)

        with self.config():
            self.ssh.do('no interface vlan {}'.format(number))
            self.ssh.do('no vlan {}'.format(number))

    def get_interfaces(self):
        interfaces = []
        for data in split_on_bang(self.ssh.do("show running-config | begin interface")):
            interface = parse_interface(data)
            if interface:
                interfaces.append(interface)
        return interfaces

    def set_access_vlan(self, interface_id, vlan):
        self._get_vlan_run_conf(vlan)

        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport access vlan {}'.format(vlan))

    def unset_interface_access_vlan(self, interface_id):
        with self.config(), self.interface(interface_id):
            self.ssh.do('no switchport access vlan')

    def set_access_mode(self, interface_id):
        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport mode access')
            self.ssh.do('no switchport trunk native vlan')
            self.ssh.do('no switchport trunk allowed vlan')

    def set_trunk_mode(self, interface_id):
        has_trunk_allowed = self.has_trunk_allowed_set(interface_id)
        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport mode trunk')
            if not has_trunk_allowed:
                self.ssh.do('switchport trunk allowed vlan none')
            self.ssh.do('no switchport access vlan')

    def add_trunk_vlan(self, interface_id, vlan):
        self._get_vlan_run_conf(vlan)

        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport trunk allowed vlan add {}'.format(vlan))

    def remove_trunk_vlan(self, interface_id, vlan):
        interface = self.get_interface(interface_id)
        if vlan not in interface.trunk_vlans:
            raise UnknownVlan(vlan)

        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport trunk allowed vlan remove {}'.format(vlan))

    def set_interface_state(self, interface_id, state):
        with self.config(), self.interface(interface_id):
            self.ssh.do('shutdown' if state is OFF else "no shutdown")

    def set_interface_native_vlan(self, interface_id, vlan):
        self._get_vlan_run_conf(vlan)

        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport trunk native vlan {}'.format(vlan))

    def unset_interface_native_vlan(self, interface_id):
        with self.config(), self.interface(interface_id):
            self.ssh.do('no switchport trunk native vlan')

    def add_ip_to_vlan(self, vlan_number, ip_network):
        vlan = self.get_vlan_interface_data(vlan_number)

        ip_found = next((ip for ip in vlan.ips if ip.ip == ip_network.ip), False)
        if not ip_found:
            has_ips = len(vlan.ips) > 0
            with self.config(), self.interface_vlan(vlan_number):
                if has_ips:
                    self.ssh.do('no ip redirects')
                result = self.ssh.do('ip address {} {}{}'.format(ip_network.ip, ip_network.netmask,
                                                             " secondary" if has_ips else ""))
                if len(result) > 0:
                    raise IPNotAvailable(ip_network, reason="; ".join(result))
        else:
            raise IPAlreadySet(ip_network, ip_found)

    def remove_ip_from_vlan(self, vlan_number, ip_network):
        vlan = self.get_vlan_interface_data(vlan_number)
        existing_ip = next((ip for ip in vlan.ips if ip.ip == ip_network.ip and ip.netmask == ip_network.netmask), False)

        if existing_ip:
            ip_index = vlan.ips.index(existing_ip)
            with self.config(), self.interface_vlan(vlan_number):
                if ip_index == 0:
                    if len(vlan.ips) == 1:
                        self.ssh.do('no ip address {} {}'.format(ip_network.ip, ip_network.netmask))
                    else:
                        self.ssh.do('ip address {} {}'.format(vlan.ips[1].ip, vlan.ips[1].netmask))
                else:
                    self.ssh.do('no ip address {} {} secondary'.format(ip_network.ip, ip_network.netmask))
        else:
            raise UnknownIP(ip_network)

    def has_trunk_allowed_set(self, interface_id):
        for line in self.ssh.do('show running-config interface {}'.format(interface_id)):
            if 'Invalid input detected' in line:
                raise UnknownInterface(interface_id)
            if 'switchport trunk allowed' in line:
                return True
        return False

    def set_vlan_access_group(self, vlan_number, direction, name):
        self.get_vlan_interface_data(vlan_number)

        with self.config(), self.interface_vlan(vlan_number):
            result = self.ssh.do("ip access-group {} {}".format(name, 'in' if direction == IN else 'out'))
            if len(result) > 0:
                raise InvalidAccessGroupName(name)

    def unset_vlan_access_group(self, vlan_number, direction):
        vlan = self.get_vlan_interface_data(vlan_number)

        if vlan.access_groups[direction] is None:
            raise UnknownAccessGroup(direction)
        else:
            with self.config(), self.interface_vlan(vlan_number):
                self.ssh.do("no ip access-group {}".format(('in' if direction == IN else 'out')))

    def set_vlan_vrf(self, vlan_number, vrf_name):
        self.get_vlan_interface_data(vlan_number)

        with self.config(), self.interface_vlan(vlan_number):
            result = self.ssh.do("ip vrf forwarding {}".format(vrf_name))
            if len(result) > 0:
                raise UnknownVrf(vrf_name)

    def unset_vlan_vrf(self, vlan_number):
        vlan = self.get_vlan_interface_data(vlan_number)

        if vlan.vrf_forwarding is None:
            raise VlanVrfNotSet(vlan_number)
        else:
            with self.config(), self.interface_vlan(vlan_number):
                self.ssh.do("no ip vrf forwarding")

    def add_dhcp_relay_server(self, vlan_number, ip_address):
        vlan = self.get_vlan_interface_data(vlan_number)

        if ip_address in vlan.dhcp_relay_servers:
            raise DhcpRelayServerAlreadyExists(vlan_number=vlan_number, ip_address=ip_address)

        with self.config(), self.interface_vlan(vlan_number):
            self.ssh.do("ip helper-address {}".format(ip_address))

    def remove_dhcp_relay_server(self, vlan_number, ip_address):
        vlan = self.get_vlan_interface_data(vlan_number)

        if ip_address not in vlan.dhcp_relay_servers:
            raise UnknownDhcpRelayServer(vlan_number=vlan_number, ip_address=ip_address)

        with self.config(), self.interface_vlan(vlan_number):
            self.ssh.do("no ip helper-address {}".format(ip_address))
            
    def get_vlan_interfaces(self, vlan_number):
        vlan_interfaces = get_vlan_interfaces_from_data(vlan_number, self.get_interfaces())
        if not vlan_interfaces:
            self.get_vlan(vlan_number)
        return vlan_interfaces

    def set_bond_trunk_mode(self, number):
        with NamedBond(number) as bond:
            return self.set_trunk_mode(bond.name)

    def set_bond_access_mode(self, number):
        with NamedBond(number) as bond:
            return self.set_access_mode(bond.name)

    def add_bond_trunk_vlan(self, number, vlan):
        with NamedBond(number) as bond:
            return self.add_trunk_vlan(bond.name, vlan)

    def remove_bond_trunk_vlan(self, number, vlan):
        with NamedBond(number) as bond:
            return self.remove_trunk_vlan(bond.name, vlan)

    def set_bond_native_vlan(self, number, vlan):
        with NamedBond(number) as bond:
            return self.set_interface_native_vlan(bond.name, vlan)

    def unset_bond_native_vlan(self, number):
        with NamedBond(number) as bond:
            return self.unset_interface_native_vlan(bond.name)

    def config(self):
        return SubShell(self.ssh, enter="configure terminal", exit_cmd='exit')

    def interface(self, interface_id):
        return SubShell(self.ssh, enter="interface {}".format(interface_id), exit_cmd='exit',
                        validate=no_output(UnknownInterface, interface_id))

    def interface_vlan(self, interface_id):
        return SubShell(self.ssh, enter=["interface vlan {}".format(interface_id), "no shutdown"], exit_cmd='exit')

    def _get_vlan_run_conf(self, vlan_number):
        run_config = self._show_run_vlan(vlan_number)
        if not run_config:
            raise UnknownVlan(vlan_number)
        return run_config

    def _show_run_vlan(self, vlan_number):
        return self.ssh.do('show running-config vlan {} | begin vlan'.format(vlan_number))

    def get_vlan_interface_data(self, vlan_number):
        run_int_vlan_data = self.ssh.do('show running-config interface vlan {}'.format(vlan_number))
        if not run_int_vlan_data[0].startswith("Building configuration..."):
            self._get_vlan_run_conf(vlan_number)

        vlan = Vlan(vlan_number)
        apply_interface_running_config_data(vlan, run_int_vlan_data)
        return vlan

    def get_interface(self, interface_id):
        data = self.ssh.do("show running-config interface {} | begin interface".format(interface_id))
        interface = parse_interface(data)
        if not interface:
            raise UnknownInterface(interface_id)
        return interface

    def add_vrrp_group(self, vlan_number, group_id, ips=None, priority=None, hello_interval=None, dead_interval=None,
                       track_id=None, track_decrement=None):
        if not (0 < group_id <= 255):
            raise BadVrrpGroupNumber(1, 255)

        vlan = self.get_vlan_interface_data(vlan_number)

        if [group for group in vlan.vrrp_groups if group.id == group_id]:
            raise VrrpAlreadyExistsForVlan(vlan=vlan_number, vrrp_group_id=group_id)

        with self.config(), self.interface_vlan(vlan_number):
            if hello_interval is not None and dead_interval is not None:
                result = self.ssh.do('standby {group_id} timers {hello_interval} {dead_interval}'.format(
                    group_id=group_id, hello_interval=hello_interval, dead_interval=dead_interval))
                if len(result) > 0:
                    raise BadVrrpTimers()

            if priority is not None:
                result = self.ssh.do('standby {group_id} priority {priority}'.format(group_id=group_id, priority=priority))
                if len(result) > 0:
                    raise BadVrrpPriorityNumber(1, 255)

            self.ssh.do('standby {group_id} preempt delay minimum 60'.format(group_id=group_id))
            self.ssh.do('standby {group_id} authentication {authentication}'.format(group_id=group_id,
                                                                                    authentication='VLAN{}'.format(vlan_number)))

            if track_id is not None and track_decrement is not None:
                result = self.ssh.do('standby {group_id} track {track_id} decrement {track_decrement}'.format(
                    group_id=group_id, track_id=track_id, track_decrement=track_decrement))
                if len(result) > 0:
                    raise BadVrrpTracking()

            for i, ip in enumerate(ips):
                result = self.ssh.do('standby {group_id} ip {ip}{secondary}'.format(
                    group_id=group_id, ip=ip, secondary=' secondary' if i > 0 else ''))
                if len(result) > 0:
                    raise IPNotAvailable(ip, reason="; ".join(result))

    def remove_vrrp_group(self, vlan_number, group_id):
        vlan = self.get_vlan_interface_data(vlan_number)

        if not [group for group in vlan.vrrp_groups if group.id == group_id]:
            raise VrrpDoesNotExistForVlan(vlan=vlan_number, vrrp_group_id=group_id)

        with self.config(), self.interface_vlan(vlan_number):
            result = self.ssh.do('no standby {group_id}'.format(group_id=group_id))
            if len(result) > 0:
                raise VrrpDoesNotExistForVlan(vlan=vlan_number, vrrp_group_id=group_id)

    def set_vlan_icmp_redirects_state(self, vlan_number, state):
        self.get_vlan_interface_data(vlan_number)

        with self.config(), self.interface_vlan(vlan_number):
            if state:
                self.ssh.do('ip redirects')
            else:
                self.ssh.do('no ip redirects')
Exemple #6
0
class Cisco(SwitchBase):
    def __init__(self, switch_descriptor):
        super(Cisco, self).__init__(switch_descriptor)
        self.ssh = None

    def _connect(self):
        params = dict(
            host=self.switch_descriptor.hostname,
            username=self.switch_descriptor.username,
            password=self.switch_descriptor.password,
        )
        if self.switch_descriptor.port:
            params["port"] = self.switch_descriptor.port

        self.ssh = SshClient(**params)

        if self.ssh.get_current_prompt().endswith(">"):
            self.ssh.do("enable", wait_for=": ")
            self.ssh.do(self.switch_descriptor.password)

        self.ssh.do("terminal length 0")
        self.ssh.do("terminal width 0")

    def _disconnect(self):
        self.ssh.quit("exit")
        self.logger.info(self.ssh.full_log)

    def _end_transaction(self):
        pass

    def _start_transaction(self):
        pass

    def commit_transaction(self):
        self.ssh.do("write memory")

    def rollback_transaction(self):
        pass

    def get_vlan(self, number):
        vlan = Vlan(int(number),
                    icmp_redirects=True,
                    arp_routing=True,
                    ntp=True)
        apply_vlan_running_config_data(vlan, self._get_vlan_run_conf(number))
        apply_interface_running_config_data(
            vlan,
            self.ssh.do(
                "show running-config interface vlan {} | begin interface".
                format(number)))
        return vlan

    def get_vlans(self):
        vlan_list = self.ssh.do("show vlan brief")

        vlans = {}
        for line in vlan_list:
            if regex.match('^(\d+)\s+(\S+).*', line):
                number, name = regex

                if name == ("VLAN{}".format(number)):
                    name = None

                vlans[number] = Vlan(int(number),
                                     name,
                                     icmp_redirects=True,
                                     arp_routing=True,
                                     ntp=True)

        for ip_interface_data in split_on_dedent(
                self.ssh.do("show ip interface")):
            if regex.match("^Vlan(\d+)\s.*", ip_interface_data[0]):
                current_vlan = vlans.get(regex[0])
                if current_vlan:
                    apply_interface_running_config_data(
                        current_vlan,
                        self.ssh.do(
                            "show running-config interface vlan {}".format(
                                current_vlan.number)))
        return vlans.values()

    def add_vlan(self, number, name=None):
        if self._show_run_vlan(number):
            raise VlanAlreadyExist(number)

        with self.config():
            result = self.ssh.do('vlan {}'.format(number))
            if len(result) > 0:
                raise BadVlanNumber()
            else:
                if name:
                    result = self.ssh.do('name {}'.format(name))

                self.ssh.do('exit')

                if len(result) > 0:
                    raise BadVlanName()

    def remove_vlan(self, number):
        self._get_vlan_run_conf(number)

        with self.config():
            self.ssh.do('no interface vlan {}'.format(number))
            self.ssh.do('no vlan {}'.format(number))

    def get_interfaces(self):
        interfaces = []
        for data in split_on_bang(
                self.ssh.do("show running-config | begin interface")):
            interface = parse_interface(data)
            if interface:
                interfaces.append(interface)
        return interfaces

    def set_access_vlan(self, interface_id, vlan):
        self._get_vlan_run_conf(vlan)

        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport access vlan {}'.format(vlan))

    def unset_interface_access_vlan(self, interface_id):
        with self.config(), self.interface(interface_id):
            self.ssh.do('no switchport access vlan')

    def set_access_mode(self, interface_id):
        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport mode access')
            self.ssh.do('no switchport trunk native vlan')
            self.ssh.do('no switchport trunk allowed vlan')

    def set_trunk_mode(self, interface_id):
        has_trunk_allowed = self.has_trunk_allowed_set(interface_id)
        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport mode trunk')
            if not has_trunk_allowed:
                self.ssh.do('switchport trunk allowed vlan none')
            self.ssh.do('no switchport access vlan')

    def add_trunk_vlan(self, interface_id, vlan):
        self._get_vlan_run_conf(vlan)

        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport trunk allowed vlan add {}'.format(vlan))

    def remove_trunk_vlan(self, interface_id, vlan):
        interface = self.get_interface(interface_id)
        if vlan not in interface.trunk_vlans:
            raise UnknownVlan(vlan)

        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport trunk allowed vlan remove {}'.format(vlan))

    def set_interface_state(self, interface_id, state):
        with self.config(), self.interface(interface_id):
            self.ssh.do('shutdown' if state is OFF else "no shutdown")

    def set_interface_native_vlan(self, interface_id, vlan):
        self._get_vlan_run_conf(vlan)

        with self.config(), self.interface(interface_id):
            self.ssh.do('switchport trunk native vlan {}'.format(vlan))

    def unset_interface_native_vlan(self, interface_id):
        with self.config(), self.interface(interface_id):
            self.ssh.do('no switchport trunk native vlan')

    def add_ip_to_vlan(self, vlan_number, ip_network):
        vlan = self.get_vlan_interface_data(vlan_number)

        ip_found = next((ip for ip in vlan.ips if ip.ip == ip_network.ip),
                        False)
        if ip_found:
            raise IPAlreadySet(ip_network, ip_found)

        has_ips = len(vlan.ips) > 0
        with self.config(), self.interface_vlan(vlan_number):
            if has_ips:
                self.ssh.do('no ip redirects')
            result = self.ssh.do('ip address {} {}{}'.format(
                ip_network.ip, ip_network.netmask,
                " secondary" if has_ips else ""))
            if len(result) > 0:
                raise IPNotAvailable(ip_network, reason="; ".join(result))

    def remove_ip_from_vlan(self, vlan_number, ip_network):
        vlan = self.get_vlan_interface_data(vlan_number)
        existing_ip = next(
            (ip for ip in vlan.ips
             if ip.ip == ip_network.ip and ip.netmask == ip_network.netmask),
            False)

        if existing_ip:
            ip_index = vlan.ips.index(existing_ip)
            with self.config(), self.interface_vlan(vlan_number):
                if ip_index == 0:
                    if len(vlan.ips) == 1:
                        self.ssh.do('no ip address {} {}'.format(
                            ip_network.ip, ip_network.netmask))
                    else:
                        self.ssh.do('ip address {} {}'.format(
                            vlan.ips[1].ip, vlan.ips[1].netmask))
                else:
                    self.ssh.do('no ip address {} {} secondary'.format(
                        ip_network.ip, ip_network.netmask))
        else:
            raise UnknownIP(ip_network)

    def has_trunk_allowed_set(self, interface_id):
        for line in self.ssh.do(
                'show running-config interface {}'.format(interface_id)):
            if 'Invalid input detected' in line:
                raise UnknownInterface(interface_id)
            if 'switchport trunk allowed' in line:
                return True
        return False

    def set_vlan_access_group(self, vlan_number, direction, name):
        self.get_vlan_interface_data(vlan_number)

        with self.config(), self.interface_vlan(vlan_number):
            result = self.ssh.do("ip access-group {} {}".format(
                name, 'in' if direction == IN else 'out'))
            if len(result) > 0:
                raise InvalidAccessGroupName(name)

    def unset_vlan_access_group(self, vlan_number, direction):
        vlan = self.get_vlan_interface_data(vlan_number)

        if vlan.access_groups[direction] is None:
            raise UnknownAccessGroup(direction)
        else:
            with self.config(), self.interface_vlan(vlan_number):
                self.ssh.do("no ip access-group {}".format(
                    ('in' if direction == IN else 'out')))

    def set_vlan_vrf(self, vlan_number, vrf_name):
        self.get_vlan_interface_data(vlan_number)

        with self.config(), self.interface_vlan(vlan_number):
            result = self.ssh.do("ip vrf forwarding {}".format(vrf_name))
            if len(result) > 0:
                raise UnknownVrf(vrf_name)

    def unset_vlan_vrf(self, vlan_number):
        vlan = self.get_vlan_interface_data(vlan_number)

        if vlan.vrf_forwarding is None:
            raise VlanVrfNotSet(vlan_number)
        else:
            with self.config(), self.interface_vlan(vlan_number):
                self.ssh.do("no ip vrf forwarding")

    def add_dhcp_relay_server(self, vlan_number, ip_address):
        vlan = self.get_vlan_interface_data(vlan_number)

        if ip_address in vlan.dhcp_relay_servers:
            raise DhcpRelayServerAlreadyExists(vlan_number=vlan_number,
                                               ip_address=ip_address)

        with self.config(), self.interface_vlan(vlan_number):
            self.ssh.do("ip helper-address {}".format(ip_address))

    def remove_dhcp_relay_server(self, vlan_number, ip_address):
        vlan = self.get_vlan_interface_data(vlan_number)

        if ip_address not in vlan.dhcp_relay_servers:
            raise UnknownDhcpRelayServer(vlan_number=vlan_number,
                                         ip_address=ip_address)

        with self.config(), self.interface_vlan(vlan_number):
            self.ssh.do("no ip helper-address {}".format(ip_address))

    def get_vlan_interfaces(self, vlan_number):
        vlan_interfaces = get_vlan_interfaces_from_data(
            vlan_number, self.get_interfaces())
        if not vlan_interfaces:
            self.get_vlan(vlan_number)
        return vlan_interfaces

    def set_bond_trunk_mode(self, number):
        with NamedBond(number) as bond:
            return self.set_trunk_mode(bond.name)

    def set_bond_access_mode(self, number):
        with NamedBond(number) as bond:
            return self.set_access_mode(bond.name)

    def add_bond_trunk_vlan(self, number, vlan):
        with NamedBond(number) as bond:
            return self.add_trunk_vlan(bond.name, vlan)

    def remove_bond_trunk_vlan(self, number, vlan):
        with NamedBond(number) as bond:
            return self.remove_trunk_vlan(bond.name, vlan)

    def set_bond_native_vlan(self, number, vlan):
        with NamedBond(number) as bond:
            return self.set_interface_native_vlan(bond.name, vlan)

    def unset_bond_native_vlan(self, number):
        with NamedBond(number) as bond:
            return self.unset_interface_native_vlan(bond.name)

    def config(self):
        return SubShell(self.ssh, enter="configure terminal", exit_cmd='exit')

    def interface(self, interface_id):
        return SubShell(self.ssh,
                        enter="interface {}".format(interface_id),
                        exit_cmd='exit',
                        validate=no_output(UnknownInterface, interface_id))

    def interface_vlan(self, interface_id):
        return SubShell(
            self.ssh,
            enter=["interface vlan {}".format(interface_id), "no shutdown"],
            exit_cmd='exit')

    def _get_vlan_run_conf(self, vlan_number):
        run_config = self._show_run_vlan(vlan_number)
        if not run_config:
            raise UnknownVlan(vlan_number)
        return run_config

    def _show_run_vlan(self, vlan_number):
        return self.ssh.do(
            'show running-config vlan {} | begin vlan'.format(vlan_number))

    def get_vlan_interface_data(self, vlan_number):
        run_int_vlan_data = self.ssh.do(
            'show running-config interface vlan {}'.format(vlan_number))
        if not run_int_vlan_data[0].startswith("Building configuration..."):
            self._get_vlan_run_conf(vlan_number)

        vlan = Vlan(vlan_number)
        apply_interface_running_config_data(vlan, run_int_vlan_data)
        return vlan

    def get_interface(self, interface_id):
        data = self.ssh.do(
            "show running-config interface {} | begin interface".format(
                interface_id))
        interface = parse_interface(data)
        if not interface:
            raise UnknownInterface(interface_id)
        return interface

    def add_vrrp_group(self,
                       vlan_number,
                       group_id,
                       ips=None,
                       priority=None,
                       hello_interval=None,
                       dead_interval=None,
                       track_id=None,
                       track_decrement=None):
        if not (0 < group_id <= 255):
            raise BadVrrpGroupNumber(1, 255)

        vlan = self.get_vlan_interface_data(vlan_number)

        if [group for group in vlan.vrrp_groups if group.id == group_id]:
            raise VrrpAlreadyExistsForVlan(vlan=vlan_number,
                                           vrrp_group_id=group_id)

        with self.config(), self.interface_vlan(vlan_number):
            if len(vlan.vrrp_groups) == 0:
                self.ssh.do('standby version 2')

            if hello_interval is not None and dead_interval is not None:
                result = self.ssh.do(
                    'standby {group_id} timers {hello_interval} {dead_interval}'
                    .format(group_id=group_id,
                            hello_interval=hello_interval,
                            dead_interval=dead_interval))
                if len(result) > 0:
                    raise BadVrrpTimers()

            if priority is not None:
                result = self.ssh.do(
                    'standby {group_id} priority {priority}'.format(
                        group_id=group_id, priority=priority))
                if len(result) > 0:
                    raise BadVrrpPriorityNumber(1, 255)

            self.ssh.do('standby {group_id} preempt delay minimum 60'.format(
                group_id=group_id))
            self.ssh.do(
                'standby {group_id} authentication {authentication}'.format(
                    group_id=group_id,
                    authentication='VLAN{}'.format(vlan_number)))

            if track_id is not None and track_decrement is not None:
                result = self.ssh.do(
                    'standby {group_id} track {track_id} decrement {track_decrement}'
                    .format(group_id=group_id,
                            track_id=track_id,
                            track_decrement=track_decrement))
                if len(result) > 0:
                    raise BadVrrpTracking()

            for i, ip in enumerate(ips):
                result = self.ssh.do(
                    'standby {group_id} ip {ip}{secondary}'.format(
                        group_id=group_id,
                        ip=ip,
                        secondary=' secondary' if i > 0 else ''))
                if len(result) > 0:
                    raise IPNotAvailable(ip, reason="; ".join(result))

    def remove_vrrp_group(self, vlan_number, group_id):
        vlan = self.get_vlan_interface_data(vlan_number)

        if not [group for group in vlan.vrrp_groups if group.id == group_id]:
            raise VrrpDoesNotExistForVlan(vlan=vlan_number,
                                          vrrp_group_id=group_id)

        with self.config(), self.interface_vlan(vlan_number):
            result = self.ssh.do(
                'no standby {group_id}'.format(group_id=group_id))
            if len(result) > 0:
                raise VrrpDoesNotExistForVlan(vlan=vlan_number,
                                              vrrp_group_id=group_id)

            if len(vlan.vrrp_groups) == 1:
                self.ssh.do('no standby version')

    def set_vlan_arp_routing_state(self, vlan_number, state):
        self.get_vlan_interface_data(vlan_number)

        with self.config(), self.interface_vlan(vlan_number):
            if state == ON:
                self.ssh.do('ip proxy-arp')
            else:
                self.ssh.do('no ip proxy-arp')

    def set_vlan_icmp_redirects_state(self, vlan_number, state):
        self.get_vlan_interface_data(vlan_number)

        with self.config(), self.interface_vlan(vlan_number):
            if state:
                self.ssh.do('ip redirects')
            else:
                self.ssh.do('no ip redirects')

    def set_vlan_ntp_state(self, vlan_number, state):
        self.get_vlan_interface_data(vlan_number)

        with self.config(), self.interface_vlan(vlan_number):
            self.ssh.do('{}ntp disable'.format('no ' if state else ''))

    def reset_interface(self, interface_id):
        with self.config():
            for line in self.ssh.do(
                    'default interface {}'.format(interface_id)):
                if 'Invalid input detected' in line:
                    raise UnknownInterface(interface_id)

    def get_versions(self):
        result = self.ssh.do('show version')

        versions = {}
        for i, line in enumerate(result):
            matches = re.match("^(.*)\s:\s(.*)$", line)
            if matches:
                values = matches.groups()
                versions[values[0].strip()] = values[1]

            matches = re.match(
                "^.*(\d+)\s+(\d+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s*$", line)
            if matches:
                values = matches.groups()
                if "units" not in versions:
                    versions["units"] = {}

                versions["units"][values[0]] = {
                    "Ports": values[1],
                    "Model": values[2],
                    "SW Version": values[3],
                    "SW Image": values[4]
                }

        return versions

    def set_vlan_unicast_rpf_mode(self, vlan_number, mode):
        operations = {STRICT: self._set_unicast_rpf_strict}

        if mode not in operations:
            raise InvalidUnicastRPFMode(mode)

        self.get_vlan_interface_data(vlan_number)

        with self.config(), self.interface_vlan(vlan_number):
            operations[mode]()

    def unset_vlan_unicast_rpf_mode(self, vlan_number):
        self.get_vlan_interface_data(vlan_number)

        with self.config(), self.interface_vlan(vlan_number):
            self.ssh.do('no ip verify unicast')

    def _set_unicast_rpf_strict(self):
        result = self.ssh.do('ip verify unicast source reachable-via rx')
        if len(result) > 0:
            raise UnsupportedOperation("Unicast RPF Mode Strict",
                                       "\n".join(result))