Skip to content

Commit

Permalink
Arista bond trunk support
Browse files Browse the repository at this point in the history
  • Loading branch information
jsoissan authored and lindycoder committed Nov 6, 2018
1 parent 8c6d0b7 commit 0837fff
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 3 deletions.
38 changes: 36 additions & 2 deletions netman/adapters/switches/arista.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from netman import regex
from netman.adapters.switches.util import split_on_dedent
from netman.core.objects.exceptions import VlanAlreadyExist, UnknownVlan, BadVlanNumber, BadVlanName, \
IPAlreadySet, IPNotAvailable, UnknownIP, DhcpRelayServerAlreadyExists, UnknownDhcpRelayServer, UnknownInterface
IPAlreadySet, IPNotAvailable, UnknownIP, UnknownInterface, UnknownBond, DhcpRelayServerAlreadyExists, \
UnknownDhcpRelayServer
from netman.core.objects.interface import Interface
from netman.core.objects.interface_states import OFF, ON
from netman.core.objects.port_modes import ACCESS, TRUNK
Expand Down Expand Up @@ -223,6 +224,18 @@ def remove_trunk_vlan(self, interface_id, vlan):
]
self.node.config(commands)

def set_bond_trunk_mode(self, number):
with NamedBond(number) as bond:
return self.set_trunk_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 add_dhcp_relay_server(self, vlan_number, ip_address):
vlan = self.get_vlan(vlan_number)

Expand Down Expand Up @@ -252,7 +265,8 @@ def _apply_interface_vlan_data(self, vlans):
try:
vlan.dhcp_relay_servers.append(IPAddress(regex[0]))
except AddrFormatError:
self.logger.warning('Unsupported IP Helper address found in Vlan {} : {}'.format(vlan.number, regex[0]))
self.logger.warning(
'Unsupported IP Helper address found in Vlan {} : {}'.format(vlan.number, regex[0]))

def _fetch_interface_vlans_config(self, vlans):
all_interface_vlans = sorted('Vlan{}'.format(vlan.number) for vlan in vlans)
Expand Down Expand Up @@ -314,6 +328,26 @@ def parse_range(single_range):
return [int(single_range)]


def bond_name(number):
return "Port-Channel{}".format(number)


class NamedBond(object):
def __init__(self, number):
self.number = number

@property
def name(self):
return bond_name(self.number)

def __enter__(self):
return self

def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is UnknownInterface:
raise UnknownBond(self.number), None, exc_tb


def _extract_vlans(vlans_info):
vlan_list = []
for id, vlan in vlans_info['vlans'].items():
Expand Down
79 changes: 78 additions & 1 deletion tests/adapters/switches/arista_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from netman.adapters.switches import arista
from netman.adapters.switches.arista import Arista, parse_vlan_ranges
from netman.core.objects.exceptions import BadVlanNumber, VlanAlreadyExist, BadVlanName, UnknownVlan, \
UnknownIP, IPNotAvailable, IPAlreadySet, UnknownInterface, UnknownDhcpRelayServer, DhcpRelayServerAlreadyExists
UnknownIP, IPNotAvailable, IPAlreadySet, UnknownInterface, UnknownBond, UnknownDhcpRelayServer, \
DhcpRelayServerAlreadyExists
from netman.core.objects.interface import Interface
from netman.core.objects.interface_states import OFF, ON
from netman.core.objects.port_modes import TRUNK
Expand Down Expand Up @@ -597,6 +598,82 @@ def test_remove_trunk_vlan_no_port_mode_still_working(self):

self.switch.remove_trunk_vlan("Ethernet1", 800)

def test_set_bond_trunk_mode_initial(self):
self.switch.node.should_receive("config").with_args([
"interface Port-Channel1",
"switchport mode trunk",
"switchport trunk allowed vlan none"
]).once()

self.switch.set_bond_trunk_mode(1)

def test_set_bond_trunk_mode_initial_invalid_interface_raises(self):
self.switch.node.should_receive("config") \
.with_args(["interface Port-Channel9999",
"switchport mode trunk",
"switchport trunk allowed vlan none"]) \
.and_raise(CommandError(1002, "CLI command 3 of 6 'interface Port-Channel9999' failed: invalid command",
command_error="Invalid input (at token 1: 'Port-Channel9999')"))

with self.assertRaises(UnknownBond):
self.switch.set_bond_trunk_mode(9999)

def test_add_bond_trunk_vlan(self):
self.switch = flexmock(self.switch)
self.switch.should_receive("get_vlan").with_args(800).and_return(Vlan(800))
self.switch.node.should_receive("config") \
.with_args(["interface Port-Channel1",
"switchport trunk allowed vlan add 800"]).once()

self.switch.add_bond_trunk_vlan(1, vlan=800)

def test_add_bond_trunk_vlan_invalid_vlan_raises(self):
self.switch = flexmock(self.switch)
self.switch.should_receive("get_vlan").with_args(800).and_raise(UnknownVlan(800))

with self.assertRaises(UnknownVlan):
self.switch.add_bond_trunk_vlan(1, vlan=800)

def test_add_bond_trunk_vlan_invalid_interface_raises(self):
self.switch = flexmock(self.switch)
self.switch.should_receive("get_vlan").with_args(800).and_return(Vlan(800))
self.switch.node.should_receive("config") \
.with_args(["interface Port-Channel9999",
"switchport trunk allowed vlan add 800"]) \
.and_raise(CommandError(1002, "CLI command 3 of 6 'interface Port-Channel9999' failed: invalid command",
command_error="Invalid input (at token 1: 'Port-Channel9999')"))

with self.assertRaises(UnknownBond):
self.switch.add_bond_trunk_vlan(9999, vlan=800)

def test_remove_bond_trunk_vlan(self):
self.switch = flexmock(self.switch)
self.switch.should_receive("get_interface") \
.with_args("Port-Channel1") \
.and_return(Interface(name="Port-Channel1", trunk_vlans=[800], port_mode="trunk"))
self.switch.node.should_receive("config") \
.with_args(["interface Port-Channel1", "switchport trunk allowed vlan remove 800"])

self.switch.remove_bond_trunk_vlan(1, 800)

def test_remove_bond_trunk_vlan_invalid_vlan_raises(self):
self.switch = flexmock(self.switch)
self.switch.should_receive("get_interface") \
.with_args("Port-Channel1") \
.and_return(Interface(name="Port-Channel1", trunk_vlans=[], port_mode="trunk"))

with self.assertRaises(UnknownVlan):
self.switch.remove_bond_trunk_vlan(1, vlan=800)

def test_remove_bond_trunk_vlan_invalid_interface_raises(self):
self.switch = flexmock(self.switch)
self.switch.should_receive("get_interface") \
.with_args("Port-Channel9999") \
.and_raise(UnknownInterface("Port-Channel9999"))

with self.assertRaises(UnknownBond):
self.switch.remove_bond_trunk_vlan(9999, vlan=800)

def test_add_dhcp_relay_server(self):
vlans_payload = {'vlans': {'123': vlan_data(name='Patate')}}

Expand Down

0 comments on commit 0837fff

Please sign in to comment.