Parcourir la source

Add option to create default route to router VRFs

This change adds the option to create default route for routers that
have a default gateway defined. This is controled by setting
vrf_default_route option in the configution

Change-Id: I16ae7feacb04730111a4b5eb61d2c3ae0fb3f041
(cherry picked from commit 06d6342a44)
tags/2018.1.11
Alin Iorga il y a 3 mois
Parent
révision
2c274907d9
7 fichiers modifiés avec 482 ajouts et 55 suppressions
  1. +3
    -0
      devstack/plugin.sh
  2. +6
    -0
      etc/ml2_conf_arista.ini
  3. +8
    -0
      networking_arista/common/config.py
  4. +64
    -0
      networking_arista/common/db_lib.py
  5. +189
    -21
      networking_arista/l3Plugin/arista_l3_driver.py
  6. +7
    -1
      networking_arista/l3Plugin/l3_arista.py
  7. +205
    -33
      networking_arista/tests/unit/l3Plugin/test_arista_l3_driver.py

+ 3
- 0
devstack/plugin.sh Voir le fichier

@@ -58,6 +58,9 @@ function configure_arista() {
if [ -n "${ARISTA_USE_VRF+x}" ]; then
iniset $ARISTA_ML2_CONF_FILE l3_arista use_vrf $ARISTA_USE_VRF
fi
if [ -n "${ARISTA_VRF_DEFAULT_ROUTE+x}" ]; then
iniset $ARISTA_ML2_CONF_FILE l3_arista vrf_default_route $ARISTA_VRF_DEFAULT_ROUTE
fi
if [ -n "${ARISTA_L3_SYNC_INTERVAL+x}" ]; then
iniset $ARISTA_ML2_CONF_FILE l3_arista l3_sync_interval $ARISTA_L3_SYNC_INTERVAL
fi


+ 6
- 0
etc/ml2_conf_arista.ini Voir le fichier

@@ -55,6 +55,12 @@
# If not set, a value of "False" is assumed. (boolean value)
#use_vrf = false

# A "True" value for this flag indicates to create a default route in
# VRF. This setting is valid only when used with the use_vrf=True. If
# not set, all routers are created without default gateway. This is
# optional. If not set, a value of "False" is assumed.
#vrf_default_route = false

# Sync interval in seconds between L3 Service plugin and EOS. This
# interval defines how often the synchronization is performed. This is
# an optional field. If not set, a value of 180 seconds is assumed


+ 8
- 0
networking_arista/common/config.py Voir le fichier

@@ -165,6 +165,14 @@ ARISTA_L3_PLUGIN = [
'in default VRF. '
'This is optional. If not set, a value of "False" '
'is assumed.')),
cfg.BoolOpt('vrf_default_route',
default=False,
help=_('A "True" value for this flag indicates to create a '
'default route in VRF. This setting is valid only '
'when used with the use_vrf=True. If not set, '
'all routers are created without default gateway.'
'This is optional. If not set, a value of "False" '
'is assumed.')),
cfg.IntOpt('l3_sync_interval',
default=180,
help=_('Sync interval in seconds between L3 Service plugin '


+ 64
- 0
networking_arista/common/db_lib.py Voir le fichier

@@ -524,3 +524,67 @@ def get_baremetal_sg_bindings():
.group_by(sg_binding_model.port_id)
.having(func.count(sg_binding_model.port_id) == 1))
return sg_bindings


def get_subnet_gateway_ipv4(subnet_id):
"""Returns the IPv4 gateway of the router if exists"""
session = db.get_reader_session()
with session.begin():
subnet_model = models_v2.Subnet
router_model = l3_models.Router
port_model = models_v2.Port
ip_allocation_model = models_v2.IPAllocation
result = (session
.query(router_model.name,
router_model.id,
subnet_model.gateway_ip.label('gip'),
subnet_model.cidr,
subnet_model.ip_version,
port_model.network_id,
ip_allocation_model.ip_address)
.filter(and_(router_model.gw_port_id == port_model.id,
port_model.network_id ==
subnet_model.network_id,
ip_allocation_model.network_id ==
subnet_model.network_id,
subnet_model.ip_version == 4,
subnet_model.id == subnet_id)).first())
return _format_gateway_result(result)


def get_network_gateway_ipv4(network_id):
"""Returns all the routers and IPv4 gateway that have network as gateway"""
session = db.get_reader_session()
with session.begin():
subnet_model = models_v2.Subnet
router_model = l3_models.Router
port_model = models_v2.Port
ip_allocation_model = models_v2.IPAllocation
result = (session
.query(router_model.name,
router_model.id,
subnet_model.gateway_ip.label('gip'),
subnet_model.cidr,
subnet_model.ip_version,
port_model.network_id,
ip_allocation_model.ip_address)
.filter(and_(router_model.gw_port_id == port_model.id,
port_model.network_id ==
subnet_model.network_id,
ip_allocation_model.network_id ==
subnet_model.network_id,
port_model.network_id == network_id,
subnet_model.ip_version == 4)).first())
return _format_gateway_result(result)


def _format_gateway_result(db_result):
"""This function formats result as needed by add_router_interface"""
if not db_result:
return None
result = {
k: db_result[i]
for i, k in enumerate(
('name', 'id', 'gip', 'cidr', 'ip_version', 'network_id',
'fixed_ip'))}
return result

+ 189
- 21
networking_arista/l3Plugin/arista_l3_driver.py Voir le fichier

@@ -16,12 +16,18 @@ import hashlib
import socket
import struct

from neutron.plugins.ml2.driver_context import NetworkContext # noqa
from neutron_lib.callbacks import events
from neutron_lib.callbacks import registry
from neutron_lib.callbacks import resources
from neutron_lib import constants as const
from neutron_lib import context as nctx
from oslo_config import cfg
from oslo_log import log as logging

from networking_arista._i18n import _, _LI
from networking_arista.common import api
from networking_arista.common import db_lib
from networking_arista.common import exceptions as arista_exc

LOG = logging.getLogger(__name__)
@@ -39,10 +45,10 @@ IPV6_BITS = 128
router_in_vrf_v1 = {
'router': {'create': ['vrf definition {0}',
'rd {1}',
'exit'],
'exit',
'ip routing vrf {0}'],
'delete': ['no vrf definition {0}']},
'interface': {'add': ['ip routing vrf {1}',
'vlan {0}',
'interface': {'add': ['vlan {0}',
'exit',
'interface vlan {0}',
'vrf forwarding {1}',
@@ -52,10 +58,10 @@ router_in_vrf_v1 = {
router_in_vrf_v2 = {
'router': {'create': ['vrf instance {0}',
'rd {1}',
'exit'],
'exit',
'ip routing vrf {0}'],
'delete': ['no vrf instance {0}']},
'interface': {'add': ['ip routing vrf {1}',
'vlan {0}',
'interface': {'add': ['vlan {0}',
'exit',
'interface vlan {0}',
'vrf {1}',
@@ -99,6 +105,12 @@ additional_cmds_for_mlag_v6 = {
'interface': {'add': ['ipv6 virtual-router address {0}'],
'remove': []}}

additional_cmds_for_default_route = {
'add': ['ip route vrf {0} 0.0.0.0/0 {1}'],
# Remove is used when updating a network to remove default gateway
# when deleting an interface we don't need to delete the route
'remove': ['no ip route vrf {0} 0.0.0.0/0']}


class AristaL3Driver(object):
"""Wraps Arista JSON RPC.
@@ -119,6 +131,12 @@ class AristaL3Driver(object):
self._vrf_syntax_v2_supported = None
self._router_in_vrf = router_in_vrf_v2
self._use_vrf = cfg.CONF.l3_arista.use_vrf
self._vrf_default_route = False
if self._use_vrf:
self._vrf_default_route = cfg.CONF.l3_arista.vrf_default_route
if self._vrf_default_route:
# only subscribe for events if vrf default route is enabled
self.subscribe()
if self._mlag_configured:
host = cfg.CONF.l3_arista.secondary_l3_host
self._hosts.append(host)
@@ -137,6 +155,61 @@ class AristaL3Driver(object):
self._protected_vlans = self._parse_protected_vlans(
cfg.CONF.l3_arista.protected_vlans)

def subscribe(self):
# Subscribe to the events related to networks and subnets
registry.subscribe(self.update_subnet, resources.SUBNET,
events.AFTER_UPDATE)
registry.subscribe(self.update_network, resources.NETWORK,
events.AFTER_UPDATE)

def update_subnet(self, resource, event, trigger, **kwargs):
subnet_info = kwargs['subnet']

if subnet_info['ip_version'] == 6:
LOG.info('IPv6 networks not supported with L3 plugin')
return
ctx = nctx.get_admin_context()
ml2_db = NetworkContext(self, ctx, {'id': subnet_info['network_id']})
seg_id = ml2_db.network_segments[0]['segmentation_id']
router_info = db_lib.get_subnet_gateway_ipv4(subnet_info['id'])
if router_info:
router_info['seg_id'] = seg_id
router_name = self._arista_router_name(router_info['id'],
router_info['name'])
self._delete_default_gateway(router_name)
self.add_router_interface(ctx, router_info)
self._setup_default_gateway(ctx, router_info)

def update_network(self, resource, event, trigger, **kwargs):
network_info = kwargs['network']
self._add_network_default_gateway(network_info['id'])

def _prepare_network_default_gateway(self, network_id):
router_info = db_lib.get_network_gateway_ipv4(network_id)
if not router_info:
return

ip_version = router_info['ip_version']
if ip_version == 6:
LOG.info('IPv6 networks not supported with L3 plugin')
return

ctx = nctx.get_admin_context()
ml2_db = NetworkContext(self, ctx, {'id': network_id})
seg_id = ml2_db.network_segments[0]['segmentation_id']
router_info['seg_id'] = seg_id
return router_info

def _add_network_default_gateway(self, network_id):
router_info = self._prepare_network_default_gateway(network_id)
if router_info:
ctx = nctx.get_admin_context()
router_name = self._arista_router_name(router_info['id'],
router_info['name'])
self._delete_default_gateway(router_name)
self.add_router_interface(ctx, router_info)
self._setup_default_gateway(router_info)

@staticmethod
def _raise_invalid_protected_vlans(vlan_string):
msg = '%s is not a valid vlan or vlan range' % vlan_string
@@ -270,8 +343,8 @@ class AristaL3Driver(object):
if self._use_vrf:
if ipv == 6:
msg = (_('IPv6 subnets are not supported with VRFs'))
LOG.exception(msg)
raise arista_exc.AristaServicePluginRpcError(msg=msg)
LOG.info(msg)
self._interfaceDict = self._router_in_vrf['interface']
else:
if ipv == 6:
@@ -285,12 +358,13 @@ class AristaL3Driver(object):
additional_cmds_for_mlag['interface'])

def add_interface_to_router(self, segment_id,
router_name, gip, router_ip, mask, server):
router_name, fixed_ip, router_ip, mask,
server):
"""Adds an interface to existing HW router on Arista HW device.

:param segment_id: VLAN Id associated with interface that is added
:param router_name: globally unique identifier for router/VRF
:param gip: Gateway IP associated with the subnet
:param fixed_ip: Fixed IP associated with the port
:param router_ip: IP address of the router
:param mask: subnet mask to be used
:param server: Server endpoint on the Arista switch to be configured
@@ -301,14 +375,15 @@ class AristaL3Driver(object):
cmds = []
for c in self._interfaceDict['add']:
if self._mlag_configured:
# In VARP config, use router ID else, use gateway IP address.
# In VARP config, use router ID else, use fixed IP.
# If fixed Ip was not set this will be gateway IP address.
ip = router_ip
else:
ip = gip + '/' + mask
ip = fixed_ip + '/' + mask
cmds.append(c.format(segment_id, router_name, ip))
if self._mlag_configured:
for c in self._additionalInterfaceCmdsDict['add']:
cmds.append(c.format(gip))
cmds.append(c.format(fixed_ip))

self._run_config_cmds(cmds, server)

@@ -348,7 +423,7 @@ class AristaL3Driver(object):
mlag_peer_failed = False
except Exception:
if self._mlag_configured and not mlag_peer_failed:
# In paied switch, it is OK to fail on one switch
# In paired switch, it is OK to fail on one switch
mlag_peer_failed = True
else:
msg = (_('Failed to create router %s on EOS') %
@@ -356,6 +431,13 @@ class AristaL3Driver(object):
LOG.exception(msg)
raise arista_exc.AristaServicePluginRpcError(msg=msg)

if self._vrf_default_route:
ext_gateway = router.get('external_gateway_info')
if ext_gateway:
network_id = ext_gateway.get('network_id')
if network_id:
self._add_network_default_gateway(network_id)

def delete_router(self, context, router_id, router):
"""Deletes a router from Arista Switch."""

@@ -368,7 +450,7 @@ class AristaL3Driver(object):
mlag_peer_failed = False
except Exception:
if self._mlag_configured and not mlag_peer_failed:
# In paied switch, it is OK to fail on one switch
# In paired switch, it is OK to fail on one switch
mlag_peer_failed = True
else:
msg = (_('Failed to create router %s on EOS') %
@@ -379,9 +461,82 @@ class AristaL3Driver(object):
def update_router(self, context, router_id, original_router, new_router):
"""Updates a router which is already created on Arista Switch.

TODO: (Sukhdev) - to be implemented in next release.
"""
pass
if not self._vrf_default_route or not new_router:
return

ext_gateway = new_router.get('external_gateway_info')
if ext_gateway is None:
# Remove default gateway if it exists
orig_ext_gateway = original_router.get('external_gateway_info')
if orig_ext_gateway is None:
# External gateway did not change
return
network_id = orig_ext_gateway['network_id']
ml2_db = NetworkContext(self, context, {'id': network_id})
seg_id = ml2_db.network_segments[0]['segmentation_id']
new_router['seg_id'] = seg_id
new_router['ip_version'] = 4
self.remove_router_interface(context, new_router,
delete_gateway=True)
return

network_id = ext_gateway.get('network_id')
if network_id:
self._add_network_default_gateway(network_id)

def _setup_default_gateway(self, router_info):
mlag_peer_failed = False
gip = router_info['gip']
router_name = self._arista_router_name(router_info['id'],
router_info['name'])

for s in self._servers:
try:
self._setup_switch_default_gateway(router_name, gip, s)
mlag_peer_failed = False
except Exception:
if self._mlag_configured and not mlag_peer_failed:
# In paired switch, it is OK to fail on one switch
mlag_peer_failed = True
else:
msg = (_('Failed to setup router gateway %s on EOS') %
router_name)
LOG.exception(msg)
raise arista_exc.AristaServicePluginRpcError(msg=msg)

def _setup_switch_default_gateway(self, router_name, gip, server):
cmds = [
c.format(router_name, gip)
for c in additional_cmds_for_default_route['add']
]

self._run_config_cmds(cmds, server)

def _delete_default_gateway(self, router_name):
mlag_peer_failed = False

for s in self._servers:
try:
self._delete_switch_default_gateway(router_name, s)
mlag_peer_failed = False
except Exception:
if self._mlag_configured and not mlag_peer_failed:
# In paired switch, it is OK to fail on one switch
mlag_peer_failed = True
else:
msg = (_('Failed to delete router gateway %s on EOS') %
router_name)
LOG.exception(msg)
raise arista_exc.AristaServicePluginRpcError(msg=msg)

def _delete_switch_default_gateway(self, router_name, server):
cmds = [
c.format(router_name)
for c in additional_cmds_for_default_route['remove']
]

self._run_config_cmds(cmds, server)

def add_router_interface(self, context, router_info):
"""Adds an interface to a router created on Arista HW router.
@@ -389,6 +544,11 @@ class AristaL3Driver(object):
This deals with both IPv6 and IPv4 configurations.
"""
if router_info:
if router_info['ip_version'] == 6 and self._use_vrf:
# For the moment we ignore the interfaces to be added
# on IPv6 subnets.
LOG.info('Using VRFs. Ignoring IPv6 interface')
return
self._select_dicts(router_info['ip_version'])
cidr = router_info['cidr']
subnet_mask = cidr.split('/')[1]
@@ -405,7 +565,7 @@ class AristaL3Driver(object):
try:
self.add_interface_to_router(router_info['seg_id'],
router_name,
router_info['gip'],
router_info['fixed_ip'],
router_ip, subnet_mask,
server)
mlag_peer_failed = False
@@ -423,20 +583,28 @@ class AristaL3Driver(object):
for s in self._servers:
self.add_interface_to_router(router_info['seg_id'],
router_name,
router_info['gip'],
router_info['fixed_ip'],
None, subnet_mask, s)

def remove_router_interface(self, context, router_info):
def remove_router_interface(self, context, router_info,
delete_gateway=False):
"""Removes previously configured interface from router on Arista HW.

This deals with both IPv6 and IPv4 configurations.
"""
if router_info:
if router_info['ip_version'] == 6 and self._use_vrf:
# For the moment we ignore the interfaces to be added
# on IPv6 subnets.
LOG.info('Using VRFs. Ignoring IPv6 interface')
return
router_name = self._arista_router_name(router_info['id'],
router_info['name'])
mlag_peer_failed = False
for s in self._servers:
try:
if delete_gateway:
self._delete_switch_default_gateway(router_name, s)
self.delete_interface_from_router(router_info['seg_id'],
router_name, s)
if self._mlag_configured:
@@ -445,7 +613,7 @@ class AristaL3Driver(object):
if self._mlag_configured and not mlag_peer_failed:
mlag_peer_failed = True
else:
msg = (_('Failed to add interface to router '
msg = (_('Failed to remove interface to router '
'%s on EOS') % router_name)
LOG.exception(msg)
raise arista_exc.AristaServicePluginRpcError(msg=msg)


+ 7
- 1
networking_arista/l3Plugin/l3_arista.py Voir le fichier

@@ -98,6 +98,7 @@ class AristaL3SyncWorker(worker.BaseWorker):
router_interface['seg_id'] = seg_id
router_interface['cidr'] = subnet['cidr']
router_interface['gip'] = subnet['gateway_ip']
router_interface['fixed_ip'] = p['fixed_ips'][0]['ip_address']
router_interface['ip_version'] = subnet['ip_version']
router_interface['subnet_id'] = subnet_id
router_interfaces.append(router_interface)
@@ -301,9 +302,13 @@ class AristaL3ServicePlugin(service_base.ServicePluginBase,
add_by_port, add_by_sub = self._validate_interface_info(interface_info)
if add_by_sub:
subnet = core.get_subnet(context, interface_info['subnet_id'])
# If we add by subnet and we have no port allocated, assigned
# gateway IP for the interface
fixed_ip = subnet['gateway_ip']
elif add_by_port:
port = core.get_port(context, interface_info['port_id'])
subnet_id = port['fixed_ips'][0]['subnet_id']
fixed_ip = port['fixed_ips'][0]['ip_address']
subnet = core.get_subnet(context, subnet_id)
network_id = subnet['network_id']

@@ -319,6 +324,7 @@ class AristaL3ServicePlugin(service_base.ServicePluginBase,
router_info['name'] = router['name']
router_info['cidr'] = subnet['cidr']
router_info['gip'] = subnet['gateway_ip']
router_info['fixed_ip'] = fixed_ip
router_info['ip_version'] = subnet['ip_version']

try:
@@ -358,7 +364,7 @@ class AristaL3ServicePlugin(service_base.ServicePluginBase,
router_info = copy.deepcopy(router_to_del)
router_info['seg_id'] = seg_id
router_info['name'] = router['name']
router_info['ip_version'] = subnet['ip_version']
try:
self.driver.remove_router_interface(context, router_info)
return router_to_del


+ 205
- 33
networking_arista/tests/unit/l3Plugin/test_arista_l3_driver.py Voir le fichier

@@ -30,11 +30,14 @@ from networking_arista.l3Plugin import l3_arista
from networking_arista.tests.unit import utils


def setup_arista_config(value='', vrf=False, mlag=False):
def setup_arista_config(value='', vrf=False, mlag=False, vrf_gateway=False):
cfg.CONF.set_override('primary_l3_host', value, "l3_arista")
cfg.CONF.set_override('primary_l3_host_username', value, "l3_arista")
if vrf:
cfg.CONF.set_override('use_vrf', vrf, "l3_arista")
if vrf_gateway:
cfg.CONF.set_override('vrf_default_route', vrf_gateway,
"l3_arista")
if mlag:
cfg.CONF.set_override('secondary_l3_host', value, "l3_arista")
cfg.CONF.set_override('mlag_config', mlag, "l3_arista")
@@ -81,15 +84,16 @@ class AristaL3DriverTestCasesDefaultVrf(base.BaseTestCase):
router_name = 'test-router-1'
segment_id = '123'
router_ip = '10.10.10.10'
gw_ip = '10.10.10.1'
mask = '255.255.255.0'
fixed_ip = '10.10.10.15'

self.drv.add_interface_to_router(segment_id, router_name, gw_ip,
router_ip, mask, self.drv._servers[0])
self.drv.add_interface_to_router(segment_id, router_name, fixed_ip,
router_ip, mask,
self.drv._servers[0])
cmds = ['enable', 'configure', 'ip routing',
'vlan %s' % segment_id, 'exit',
'interface vlan %s' % segment_id,
'ip address %s/%s' % (gw_ip, mask), 'exit']
'ip address %s/%s' % (fixed_ip, mask), 'exit']

self.drv._servers[0].execute.assert_called_once_with(cmds,
keep_alive=True)
@@ -140,7 +144,8 @@ class AristaL3DriverTestCasesUsingVRFs(base.BaseTestCase):

cmds = ['enable', 'configure',
'vrf instance %s' % r,
'rd %(rd)s:%(rd)s' % {'rd': d}, 'exit', 'exit']
'rd %(rd)s:%(rd)s' % {'rd': d}, 'exit',
'ip routing vrf %s' % r, 'exit']

self.drv._servers[0].execute.assert_called_with(cmds,
keep_alive=True)
@@ -159,7 +164,8 @@ class AristaL3DriverTestCasesUsingVRFs(base.BaseTestCase):

cmds = ['enable', 'configure',
'vrf definition %s' % r,
'rd %(rd)s:%(rd)s' % {'rd': d}, 'exit', 'exit']
'rd %(rd)s:%(rd)s' % {'rd': d}, 'exit',
'ip routing vrf %s' % r, 'exit']

self.drv._servers[0].execute.assert_called_with(cmds,
keep_alive=True)
@@ -200,8 +206,8 @@ class AristaL3DriverTestCasesUsingVRFs(base.BaseTestCase):
router_name = 'test-router-1'
segment_id = '123'
router_ip = '10.10.10.10'
gw_ip = '10.10.10.1'
mask = '255.255.255.0'
fixed_ip = '10.10.10.15'

with mock.patch.object(self.drv, '_check_vrf_syntax_v2_support') as c:
c.return_value = False
@@ -209,14 +215,14 @@ class AristaL3DriverTestCasesUsingVRFs(base.BaseTestCase):
self.drv._select_dicts(4)
c.assert_called_once_with(self.drv._servers[0], keep_alive=True)

self.drv.add_interface_to_router(segment_id, router_name, gw_ip,
router_ip, mask, self.drv._servers[0])
self.drv.add_interface_to_router(segment_id, router_name, fixed_ip,
router_ip, mask,
self.drv._servers[0])
cmds = ['enable', 'configure',
'ip routing vrf %s' % router_name,
'vlan %s' % segment_id, 'exit',
'interface vlan %s' % segment_id,
'vrf forwarding %s' % router_name,
'ip address %s/%s' % (gw_ip, mask), 'exit']
'ip address %s/%s' % (fixed_ip, mask), 'exit']

self.drv._servers[0].execute.assert_called_once_with(cmds,
keep_alive=True)
@@ -225,8 +231,8 @@ class AristaL3DriverTestCasesUsingVRFs(base.BaseTestCase):
router_name = 'test-router-1'
segment_id = '123'
router_ip = '10.10.10.10'
gw_ip = '10.10.10.1'
mask = '255.255.255.0'
fixed_ip = '10.10.10.15'

with mock.patch.object(self.drv, '_check_vrf_syntax_v2_support') as c:
c.return_value = True
@@ -234,14 +240,170 @@ class AristaL3DriverTestCasesUsingVRFs(base.BaseTestCase):
self.drv._select_dicts(4)
c.assert_called_once_with(self.drv._servers[0], keep_alive=True)

self.drv.add_interface_to_router(segment_id, router_name, gw_ip,
router_ip, mask, self.drv._servers[0])
self.drv.add_interface_to_router(segment_id, router_name, fixed_ip,
router_ip, mask,
self.drv._servers[0])
cmds = ['enable', 'configure',
'ip routing vrf %s' % router_name,
'vlan %s' % segment_id, 'exit',
'interface vlan %s' % segment_id,
'vrf %s' % router_name,
'ip address %s/%s' % (gw_ip, mask), 'exit']
'ip address %s/%s' % (fixed_ip, mask), 'exit']

self.drv._servers[0].execute.assert_called_once_with(cmds,
keep_alive=True)

def test_delete_interface_from_router_on_eos(self):
router_name = 'test-router-1'
segment_id = '123'

self.drv.delete_interface_from_router(segment_id, router_name,
self.drv._servers[0])
cmds = ['enable', 'configure', 'no interface vlan %s' % segment_id,
'exit']

self.drv._servers[0].execute.assert_called_once_with(cmds,
keep_alive=True)


class AristaL3DriverTestCasesVRFDefaultGateway(base.BaseTestCase):
"""Test cases to test the RPC between Arista Driver and EOS.

Tests all methods used to send commands between Arista L3 Driver and EOS
to program routing functions using multiple VRFs.
Note that the configuration commands are different when VRFs are used.
"""

def setUp(self):
super(AristaL3DriverTestCasesVRFDefaultGateway, self).setUp()
setup_arista_config('value', vrf=True, vrf_gateway=True)
self.drv = arista.AristaL3Driver()
self.drv._servers = []
self.drv._servers.append(mock.MagicMock())

def test_no_exception_on_correct_configuration(self):
self.assertIsNotNone(self.drv)

def test_create_router_on_eos_v2_syntax(self):
max_vrfs = 5
routers = ['testRouterV2-%s' % n for n in range(max_vrfs)]
domains = ['20%s' % n for n in range(max_vrfs)]

with mock.patch.object(self.drv, '_check_vrf_syntax_v2_support') as c:
c.return_value = True
self.drv._update_vrf_commands()
c.assert_called_once_with(self.drv._servers[0], keep_alive=True)

for (r, d) in zip(routers, domains):
self.drv.create_router_on_eos(r, d, self.drv._servers[0])

cmds = ['enable', 'configure',
'vrf instance %s' % r,
'rd %(rd)s:%(rd)s' % {'rd': d}, 'exit',
'ip routing vrf %s' % r, 'exit']

self.drv._servers[0].execute.assert_called_with(cmds,
keep_alive=True)

def test_create_router_on_eos_v1_syntax(self):
max_vrfs = 5
routers = ['testRouterV1-%s' % n for n in range(max_vrfs)]
domains = ['10%s' % n for n in range(max_vrfs)]
with mock.patch.object(self.drv, '_check_vrf_syntax_v2_support') as c:
c.return_value = False
self.drv._update_vrf_commands()
c.assert_called_once_with(self.drv._servers[0], keep_alive=True)

for (r, d) in zip(routers, domains):
self.drv.create_router_on_eos(r, d, self.drv._servers[0])

cmds = ['enable', 'configure',
'vrf definition %s' % r,
'rd %(rd)s:%(rd)s' % {'rd': d}, 'exit',
'ip routing vrf %s' % r, 'exit']

self.drv._servers[0].execute.assert_called_with(cmds,
keep_alive=True)

def test_delete_router_from_eos_v1_syntax(self):
max_vrfs = 5
routers = ['testRouter-%s' % n for n in range(max_vrfs)]

with mock.patch.object(self.drv, '_check_vrf_syntax_v2_support') as c:
c.return_value = False
self.drv._update_vrf_commands()
c.assert_called_once_with(self.drv._servers[0], keep_alive=True)

for r in routers:
self.drv.delete_router_from_eos(r, self.drv._servers[0])
cmds = ['enable', 'configure', 'no vrf definition %s' % r, 'exit']

self.drv._servers[0].execute.assert_called_with(cmds,
keep_alive=True)

def test_delete_router_from_eos_v2_syntax(self):
max_vrfs = 5
routers = ['testRouter-%s' % n for n in range(max_vrfs)]

with mock.patch.object(self.drv, '_check_vrf_syntax_v2_support') as c:
c.return_value = True
self.drv._update_vrf_commands()
c.assert_called_once_with(self.drv._servers[0], keep_alive=True)

for r in routers:
self.drv.delete_router_from_eos(r, self.drv._servers[0])
cmds = ['enable', 'configure', 'no vrf instance %s' % r, 'exit']

self.drv._servers[0].execute.assert_called_with(cmds,
keep_alive=True)

def test_add_interface_to_router_on_eos_v1(self):
router_name = 'test-router-1'
segment_id = '123'
router_ip = '10.10.10.10'
mask = '255.255.255.0'
fixed_ip = '10.10.10.15'

with mock.patch.object(self.drv, '_check_vrf_syntax_v2_support') as c:
c.return_value = False
self.drv._update_vrf_commands()
self.drv._select_dicts(4)
c.assert_called_once_with(self.drv._servers[0], keep_alive=True)

self.drv.add_interface_to_router(segment_id, router_name, fixed_ip,
router_ip, mask,
self.drv._servers[0])
cmds = ['enable', 'configure',
'vlan %s' % segment_id, 'exit',
'interface vlan %s' % segment_id,
'vrf forwarding %s' % router_name,
'ip address %s/%s' % (fixed_ip, mask),
'exit']

self.drv._servers[0].execute.assert_called_once_with(cmds,
keep_alive=True)

def test_add_interface_to_router_on_eos_v2(self):
router_name = 'test-router-1'
segment_id = '123'
router_ip = '10.10.10.10'
mask = '255.255.255.0'
fixed_ip = '10.10.10.15'

with mock.patch.object(self.drv, '_check_vrf_syntax_v2_support') as c:
c.return_value = True
self.drv._update_vrf_commands()
self.drv._select_dicts(4)
c.assert_called_once_with(self.drv._servers[0], keep_alive=True)

self.drv.add_interface_to_router(segment_id, router_name, fixed_ip,
router_ip, mask,
self.drv._servers[0])
cmds = ['enable', 'configure',
'vlan %s' % segment_id, 'exit',
'interface vlan %s' % segment_id,
'vrf %s' % router_name,
'ip address %s/%s' % (fixed_ip, mask),
'exit']

self.drv._servers[0].execute.assert_called_once_with(cmds,
keep_alive=True)
@@ -304,17 +466,17 @@ class AristaL3DriverTestCasesMlagConfig(base.BaseTestCase):
router_name = 'test-router-1'
segment_id = '123'
router_ip = '10.10.10.10'
gw_ip = '10.10.10.1'
mask = '255.255.255.0'
fixed_ip = '10.10.10.15'

for s in self.drv._servers:
self.drv.add_interface_to_router(segment_id, router_name, gw_ip,
self.drv.add_interface_to_router(segment_id, router_name, fixed_ip,
router_ip, mask, s)
cmds = ['enable', 'configure', 'ip routing',
'vlan %s' % segment_id, 'exit',
'interface vlan %s' % segment_id,
'ip address %s' % router_ip,
'ip virtual-router address %s' % gw_ip, 'exit']
'ip virtual-router address %s' % fixed_ip, 'exit']

s.execute.assert_called_once_with(cmds, keep_alive=True)

@@ -364,6 +526,7 @@ class AristaL3DriverTestCasesMlagVRFConfig(base.BaseTestCase):
'vrf instance %s' % r,
'rd %(rd)s:%(rd)s' % {'rd': d},
'exit',
'ip routing vrf %s' % r,
'ip virtual-router mac-address %s' % router_mac,
'exit']
s.execute.assert_called_with(cmds, keep_alive=True)
@@ -383,8 +546,8 @@ class AristaL3DriverTestCasesMlagVRFConfig(base.BaseTestCase):
def test_add_interface_to_router_on_eos_v1(self):
router_name = 'test-router-1'
segment_id = '123'
fixed_ip = '10.10.10.15'
router_ip = '10.10.10.10'
gw_ip = '10.10.10.1'
mask = '255.255.255.0'

with mock.patch.object(self.drv, '_check_vrf_syntax_v2_support') as c:
@@ -394,15 +557,14 @@ class AristaL3DriverTestCasesMlagVRFConfig(base.BaseTestCase):
c.assert_called_once_with(self.drv._servers[0], keep_alive=True)

for s in self.drv._servers:
self.drv.add_interface_to_router(segment_id, router_name, gw_ip,
self.drv.add_interface_to_router(segment_id, router_name, fixed_ip,
router_ip, mask, s)
cmds = ['enable', 'configure',
'ip routing vrf %s' % router_name,
'vlan %s' % segment_id, 'exit',
'interface vlan %s' % segment_id,
'vrf forwarding %s' % router_name,
'ip address %s' % router_ip,
'ip virtual-router address %s' % gw_ip,
'ip virtual-router address %s' % fixed_ip,
'exit']

s.execute.assert_called_once_with(cmds, keep_alive=True)
@@ -411,8 +573,8 @@ class AristaL3DriverTestCasesMlagVRFConfig(base.BaseTestCase):
router_name = 'test-router-1'
segment_id = '123'
router_ip = '10.10.10.10'
gw_ip = '10.10.10.1'
mask = '255.255.255.0'
fixed_ip = '10.10.10.15'

with mock.patch.object(self.drv, '_check_vrf_syntax_v2_support') as c:
c.return_value = True
@@ -421,15 +583,14 @@ class AristaL3DriverTestCasesMlagVRFConfig(base.BaseTestCase):
c.assert_called_once_with(self.drv._servers[0], keep_alive=True)

for s in self.drv._servers:
self.drv.add_interface_to_router(segment_id, router_name, gw_ip,
self.drv.add_interface_to_router(segment_id, router_name, fixed_ip,
router_ip, mask, s)
cmds = ['enable', 'configure',
'ip routing vrf %s' % router_name,
'vlan %s' % segment_id, 'exit',
'interface vlan %s' % segment_id,
'vrf %s' % router_name,
'ip address %s' % router_ip,
'ip virtual-router address %s' % gw_ip,
'ip virtual-router address %s' % fixed_ip,
'exit']

s.execute.assert_called_once_with(cmds, keep_alive=True)
@@ -467,6 +628,7 @@ class AristaL3DriverTestCases_v4(base.BaseTestCase):
def test_add_v4_interface_to_router(self):
gateway_ip = '10.10.10.1'
cidrs = ['10.10.10.0/24', '10.11.11.0/24']
fixed_id = '10.10.10.15'

# Add couple of IPv4 subnets to router
for cidr in cidrs:
@@ -476,13 +638,15 @@ class AristaL3DriverTestCases_v4(base.BaseTestCase):
'seg_id': '123',
'cidr': "%s" % cidr,
'gip': "%s" % gateway_ip,
'ip_version': 4}
'ip_version': 4,
'fixed_ip': fixed_id}

self.assertFalse(self.drv.add_router_interface(None, router))

def test_delete_v4_interface_from_router(self):
gateway_ip = '10.10.10.1'
cidrs = ['10.10.10.0/24', '10.11.11.0/24']
fixed_ip = '10.10.10.15'

# remove couple of IPv4 subnets from router
for cidr in cidrs:
@@ -492,7 +656,8 @@ class AristaL3DriverTestCases_v4(base.BaseTestCase):
'seg_id': '123',
'cidr': "%s" % cidr,
'gip': "%s" % gateway_ip,
'ip_version': 4}
'ip_version': 4,
'fixed_id': fixed_ip}

self.assertFalse(self.drv.remove_router_interface(None, router))

@@ -517,6 +682,7 @@ class AristaL3DriverTestCases_v6(base.BaseTestCase):
def test_add_v6_interface_to_router(self):
gateway_ip = '3FFE::1'
cidrs = ['3FFE::/16', '2001::/16']
fixed_ip = '3FFE::5'

# Add couple of IPv6 subnets to router
for cidr in cidrs:
@@ -526,13 +692,15 @@ class AristaL3DriverTestCases_v6(base.BaseTestCase):
'seg_id': '123',
'cidr': "%s" % cidr,
'gip': "%s" % gateway_ip,
'ip_version': 6}
'ip_version': 6,
'fixed_ip': fixed_ip}

self.assertFalse(self.drv.add_router_interface(None, router))

def test_delete_v6_interface_from_router(self):
gateway_ip = '3FFE::1'
cidrs = ['3FFE::/16', '2001::/16']
fixed_ip = '3FFE::5'

# remove couple of IPv6 subnets from router
for cidr in cidrs:
@@ -542,7 +710,8 @@ class AristaL3DriverTestCases_v6(base.BaseTestCase):
'seg_id': '123',
'cidr': "%s" % cidr,
'gip': "%s" % gateway_ip,
'ip_version': 6}
'ip_version': 6,
'fixed_ip': fixed_ip}

self.assertFalse(self.drv.remove_router_interface(None, router))

@@ -568,6 +737,7 @@ class AristaL3DriverTestCases_MLAG_v6(base.BaseTestCase):
def test_add_v6_interface_to_router(self):
gateway_ip = '3FFE::1'
cidrs = ['3FFE::/16', '2001::/16']
fixed_ip = '3FFE::5'

# Add couple of IPv6 subnets to router
for cidr in cidrs:
@@ -577,7 +747,8 @@ class AristaL3DriverTestCases_MLAG_v6(base.BaseTestCase):
'seg_id': '123',
'cidr': "%s" % cidr,
'gip': "%s" % gateway_ip,
'ip_version': 6}
'ip_version': 6,
'fixed_ip': fixed_ip}

self.assertFalse(self.drv.add_router_interface(None, router))

@@ -647,6 +818,7 @@ class AristaL3DriverTestCasesMlag_one_switch_failed(base.BaseTestCase):
router['ip_version'] = 4
router['cidr'] = '10.10.10.0/24'
router['gip'] = '10.10.10.1'
router['fixed_ip'] = '10.10.10.15'

# Make one of the switches throw an exception - i.e. fail
self.drv._servers[1].execute = mock.Mock(side_effect=Exception)


Chargement…
Annuler
Enregistrer