From 50eb8041bdc8cc872d95e3005eed2c59c2c3290c Mon Sep 17 00:00:00 2001 From: Sukhdev Kapur Date: Thu, 30 Jul 2015 15:23:52 -0700 Subject: [PATCH] Arista Drivers decomposition part II As a part of vendor driver decomposition, this patch moves the remaining Arista specific code to openstack/networking-arista Change-Id: Ie16b5ed936b116043dea36ec967bb5ae9cdacbdf Partial-Implements: blueprint core-vendor-decomposition --- etc/neutron/plugins/ml2/ml2_conf_arista.ini | 100 ---- .../migration/alembic_migrations/external.py | 4 + neutron/db/migration/models/head.py | 1 - neutron/plugins/ml2/drivers/arista/README | 12 - neutron/plugins/ml2/drivers/arista/config.py | 128 ----- neutron/plugins/ml2/drivers/arista/db.py | 80 --- .../plugins/ml2/drivers/arista/exceptions.py | 35 -- .../ml2/drivers/arista/mechanism_arista.py | 470 ------------------ neutron/services/l3_router/l3_arista.py | 280 ----------- .../plugins/ml2/drivers/arista/__init__.py | 0 .../drivers/arista/test_mechanism_arista.py | 417 ---------------- setup.cfg | 2 - tox.ini | 1 - 13 files changed, 4 insertions(+), 1526 deletions(-) delete mode 100644 etc/neutron/plugins/ml2/ml2_conf_arista.ini delete mode 100644 neutron/plugins/ml2/drivers/arista/README delete mode 100644 neutron/plugins/ml2/drivers/arista/config.py delete mode 100644 neutron/plugins/ml2/drivers/arista/db.py delete mode 100644 neutron/plugins/ml2/drivers/arista/exceptions.py delete mode 100644 neutron/plugins/ml2/drivers/arista/mechanism_arista.py delete mode 100644 neutron/services/l3_router/l3_arista.py delete mode 100644 neutron/tests/unit/plugins/ml2/drivers/arista/__init__.py delete mode 100644 neutron/tests/unit/plugins/ml2/drivers/arista/test_mechanism_arista.py diff --git a/etc/neutron/plugins/ml2/ml2_conf_arista.ini b/etc/neutron/plugins/ml2/ml2_conf_arista.ini deleted file mode 100644 index abaf5bc7c..000000000 --- a/etc/neutron/plugins/ml2/ml2_conf_arista.ini +++ /dev/null @@ -1,100 +0,0 @@ -# Defines configuration options specific for Arista ML2 Mechanism driver - -[ml2_arista] -# (StrOpt) EOS IP address. This is required field. If not set, all -# communications to Arista EOS will fail -# -# eapi_host = -# Example: eapi_host = 192.168.0.1 -# -# (StrOpt) EOS command API username. This is required field. -# if not set, all communications to Arista EOS will fail. -# -# eapi_username = -# Example: arista_eapi_username = admin -# -# (StrOpt) EOS command API password. This is required field. -# if not set, all communications to Arista EOS will fail. -# -# eapi_password = -# Example: eapi_password = my_password -# -# (StrOpt) Defines if hostnames are sent to Arista EOS as FQDNs -# ("node1.domain.com") or as short names ("node1"). This is -# optional. If not set, a value of "True" is assumed. -# -# use_fqdn = -# Example: use_fqdn = True -# -# (IntOpt) Sync interval in seconds between Neutron plugin and EOS. -# This field defines how often the synchronization is performed. -# This is an optional field. If not set, a value of 180 seconds -# is assumed. -# -# sync_interval = -# Example: sync_interval = 60 -# -# (StrOpt) Defines Region Name that is assigned to this OpenStack Controller. -# This is useful when multiple OpenStack/Neutron controllers are -# managing the same Arista HW clusters. Note that this name must -# match with the region name registered (or known) to keystone -# service. Authentication with Keysotne is performed by EOS. -# This is optional. If not set, a value of "RegionOne" is assumed. -# -# region_name = -# Example: region_name = RegionOne - - -[l3_arista] - -# (StrOpt) primary host IP address. This is required field. If not set, all -# communications to Arista EOS will fail. This is the host where -# primary router is created. -# -# primary_l3_host = -# Example: primary_l3_host = 192.168.10.10 -# -# (StrOpt) Primary host username. This is required field. -# if not set, all communications to Arista EOS will fail. -# -# primary_l3_host_username = -# Example: arista_primary_l3_username = admin -# -# (StrOpt) Primary host password. This is required field. -# if not set, all communications to Arista EOS will fail. -# -# primary_l3_host_password = -# Example: primary_l3_password = my_password -# -# (StrOpt) IP address of the second Arista switch paired as -# MLAG (Multi-chassis Link Aggregation) with the first. -# This is optional field, however, if mlag_config flag is set, -# then this is a required field. If not set, all -# communications to Arista EOS will fail. If mlag_config is set -# to False, then this field is ignored -# -# seconadary_l3_host = -# Example: seconadary_l3_host = 192.168.10.20 -# -# (BoolOpt) Defines if Arista switches are configured in MLAG mode -# If yes, all L3 configuration is pushed to both switches -# automatically. If this flag is set, ensure that secondary_l3_host -# is set to the second switch's IP. -# This flag is Optional. If not set, a value of "False" is assumed. -# -# mlag_config = -# Example: mlag_config = True -# -# (BoolOpt) Defines if the router is created in default VRF or a -# a specific VRF. This is optional. -# If not set, a value of "False" is assumed. -# -# Example: use_vrf = True -# -# (IntOpt) Sync interval in seconds between Neutron plugin and EOS. -# This field defines how often the synchronization is performed. -# This is an optional field. If not set, a value of 180 seconds -# is assumed. -# -# l3_sync_interval = -# Example: l3_sync_interval = 60 diff --git a/neutron/db/migration/alembic_migrations/external.py b/neutron/db/migration/alembic_migrations/external.py index 0ac017f67..735e1f1a5 100644 --- a/neutron/db/migration/alembic_migrations/external.py +++ b/neutron/db/migration/alembic_migrations/external.py @@ -25,6 +25,10 @@ LBAAS_TABLES = ['vips', 'sessionpersistences', 'pools', 'healthmonitors', FWAAS_TABLES = ['firewall_rules', 'firewalls', 'firewall_policies'] DRIVER_TABLES = [ + # Arista ML2 driver Models moved to openstack/networking-arista + 'arista_provisioned_nets', + 'arista_provisioned_vms', + 'arista_provisioned_tenants', # Models moved to openstack/networking-cisco 'cisco_ml2_apic_contracts', 'cisco_ml2_apic_names', diff --git a/neutron/db/migration/models/head.py b/neutron/db/migration/models/head.py index 14be01961..43eb02ee4 100644 --- a/neutron/db/migration/models/head.py +++ b/neutron/db/migration/models/head.py @@ -52,7 +52,6 @@ from neutron.plugins.brocade.db import models as brocade_models # noqa from neutron.plugins.cisco.db.l3 import l3_models # noqa from neutron.plugins.cisco.db import n1kv_models_v2 # noqa from neutron.plugins.cisco.db import network_models_v2 # noqa -from neutron.plugins.ml2.drivers.arista import db # noqa from neutron.plugins.ml2.drivers.brocade.db import ( # noqa models as ml2_brocade_models) from neutron.plugins.ml2.drivers.cisco.n1kv import n1kv_models # noqa diff --git a/neutron/plugins/ml2/drivers/arista/README b/neutron/plugins/ml2/drivers/arista/README deleted file mode 100644 index 080e581f7..000000000 --- a/neutron/plugins/ml2/drivers/arista/README +++ /dev/null @@ -1,12 +0,0 @@ - -Arista Neutron ML2 Mechanism Driver - -This mechanism driver implements ML2 Driver API and is used to manage the virtual and physical networks using Arista Hardware. - -Note: Initial version of this driver support VLANs only. - -For more details on use please refer to: -https://wiki.openstack.org/wiki/Arista-neutron-ml2-driver - -The back-end of the driver is now moved to: -https://github.com/stackforge/networking-arista diff --git a/neutron/plugins/ml2/drivers/arista/config.py b/neutron/plugins/ml2/drivers/arista/config.py deleted file mode 100644 index c1534d2bf..000000000 --- a/neutron/plugins/ml2/drivers/arista/config.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# -# 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. - - -from oslo_config import cfg - - -# Arista ML2 Mechanism driver specific configuration knobs. -# -# Following are user configurable options for Arista ML2 Mechanism -# driver. The eapi_username, eapi_password, and eapi_host are -# required options. Region Name must be the same that is used by -# Keystone service. This option is available to support multiple -# OpenStack/Neutron controllers. - -ARISTA_DRIVER_OPTS = [ - cfg.StrOpt('eapi_username', - default='', - help=_('Username for Arista EOS. This is required field. ' - 'If not set, all communications to Arista EOS ' - 'will fail.')), - cfg.StrOpt('eapi_password', - default='', - secret=True, # do not expose value in the logs - help=_('Password for Arista EOS. This is required field. ' - 'If not set, all communications to Arista EOS ' - 'will fail.')), - cfg.StrOpt('eapi_host', - default='', - help=_('Arista EOS IP address. This is required field. ' - 'If not set, all communications to Arista EOS ' - 'will fail.')), - cfg.BoolOpt('use_fqdn', - default=True, - help=_('Defines if hostnames are sent to Arista EOS as FQDNs ' - '("node1.domain.com") or as short names ("node1"). ' - 'This is optional. If not set, a value of "True" ' - 'is assumed.')), - cfg.IntOpt('sync_interval', - default=180, - help=_('Sync interval in seconds between Neutron 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.')), - cfg.StrOpt('region_name', - default='RegionOne', - help=_('Defines Region Name that is assigned to this OpenStack ' - 'Controller. This is useful when multiple ' - 'OpenStack/Neutron controllers are managing the same ' - 'Arista HW clusters. Note that this name must match ' - 'with the region name registered (or known) to keystone ' - 'service. Authentication with Keysotne is performed by ' - 'EOS. This is optional. If not set, a value of ' - '"RegionOne" is assumed.')) -] - - -""" Arista L3 Service Plugin specific configuration knobs. - -Following are user configurable options for Arista L3 plugin -driver. The eapi_username, eapi_password, and eapi_host are -required options. -""" - -ARISTA_L3_PLUGIN = [ - cfg.StrOpt('primary_l3_host_username', - default='', - help=_('Username for Arista EOS. This is required field. ' - 'If not set, all communications to Arista EOS ' - 'will fail')), - cfg.StrOpt('primary_l3_host_password', - default='', - secret=True, # do not expose value in the logs - help=_('Password for Arista EOS. This is required field. ' - 'If not set, all communications to Arista EOS ' - 'will fail')), - cfg.StrOpt('primary_l3_host', - default='', - help=_('Arista EOS IP address. This is required field. ' - 'If not set, all communications to Arista EOS ' - 'will fail')), - cfg.StrOpt('secondary_l3_host', - default='', - help=_('Arista EOS IP address for second Switch MLAGed with ' - 'the first one. This an optional field, however, if ' - 'mlag_config flag is set, then this is required. ' - 'If not set, all communications to Arista EOS ' - 'will fail')), - cfg.BoolOpt('mlag_config', - default=False, - help=_('This flag is used indicate if Arista Switches are ' - 'configured in MLAG mode. If yes, all L3 config ' - 'is pushed to both the switches automatically. ' - 'If this flag is set to True, ensure to specify IP ' - 'addresses of both switches. ' - 'This is optional. If not set, a value of "False" ' - 'is assumed.')), - cfg.BoolOpt('use_vrf', - default=False, - help=_('A "True" value for this flag indicates to create a ' - 'router in VRF. If not set, all routers are created ' - 'in default VRF. ' - '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 ' - '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')) -] - -cfg.CONF.register_opts(ARISTA_L3_PLUGIN, "l3_arista") - -cfg.CONF.register_opts(ARISTA_DRIVER_OPTS, "ml2_arista") diff --git a/neutron/plugins/ml2/drivers/arista/db.py b/neutron/plugins/ml2/drivers/arista/db.py deleted file mode 100644 index ad6a570bd..000000000 --- a/neutron/plugins/ml2/drivers/arista/db.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# -# 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. - -import sqlalchemy as sa - -from neutron.db import model_base -from neutron.db import models_v2 - -UUID_LEN = 36 -STR_LEN = 255 - - -class AristaProvisionedNets(model_base.BASEV2, models_v2.HasId, - models_v2.HasTenant): - """Stores networks provisioned on Arista EOS. - - Saves the segmentation ID for each network that is provisioned - on EOS. This information is used during synchronization between - Neutron and EOS. - """ - __tablename__ = 'arista_provisioned_nets' - - network_id = sa.Column(sa.String(UUID_LEN)) - segmentation_id = sa.Column(sa.Integer) - - def eos_network_representation(self, segmentation_type): - return {u'networkId': self.network_id, - u'segmentationTypeId': self.segmentation_id, - u'segmentationType': segmentation_type} - - -class AristaProvisionedVms(model_base.BASEV2, models_v2.HasId, - models_v2.HasTenant): - """Stores VMs provisioned on Arista EOS. - - All VMs launched on physical hosts connected to Arista - Switches are remembered - """ - __tablename__ = 'arista_provisioned_vms' - - vm_id = sa.Column(sa.String(STR_LEN)) - host_id = sa.Column(sa.String(STR_LEN)) - port_id = sa.Column(sa.String(UUID_LEN)) - network_id = sa.Column(sa.String(UUID_LEN)) - - def eos_vm_representation(self): - return {u'vmId': self.vm_id, - u'host': self.host_id, - u'ports': {self.port_id: [{u'portId': self.port_id, - u'networkId': self.network_id}]}} - - def eos_port_representation(self): - return {u'vmId': self.vm_id, - u'host': self.host_id, - u'portId': self.port_id, - u'networkId': self.network_id} - - -class AristaProvisionedTenants(model_base.BASEV2, models_v2.HasId, - models_v2.HasTenant): - """Stores Tenants provisioned on Arista EOS. - - Tenants list is maintained for sync between Neutron and EOS. - """ - __tablename__ = 'arista_provisioned_tenants' - - def eos_tenant_representation(self): - return {u'tenantId': self.tenant_id} diff --git a/neutron/plugins/ml2/drivers/arista/exceptions.py b/neutron/plugins/ml2/drivers/arista/exceptions.py deleted file mode 100644 index 278ec7d4b..000000000 --- a/neutron/plugins/ml2/drivers/arista/exceptions.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# -# 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. - - -"""Exceptions used by Arista ML2 Mechanism Driver.""" - -from neutron.common import exceptions - - -class AristaRpcError(exceptions.NeutronException): - message = _('%(msg)s') - - -class AristaConfigError(exceptions.NeutronException): - message = _('%(msg)s') - - -class AristaServicePluginRpcError(exceptions.NeutronException): - message = _('%(msg)s') - - -class AristaServicePluginConfigError(exceptions.NeutronException): - message = _('%(msg)s') diff --git a/neutron/plugins/ml2/drivers/arista/mechanism_arista.py b/neutron/plugins/ml2/drivers/arista/mechanism_arista.py deleted file mode 100644 index 4bcf0e7e8..000000000 --- a/neutron/plugins/ml2/drivers/arista/mechanism_arista.py +++ /dev/null @@ -1,470 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# -# 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. - -import threading - -from networking_arista.common import db_lib -from networking_arista.ml2 import arista_ml2 -from oslo_config import cfg -from oslo_log import log as logging - -from neutron.common import constants as n_const -from neutron.i18n import _LI -from neutron.plugins.common import constants as p_const -from neutron.plugins.ml2.common import exceptions as ml2_exc -from neutron.plugins.ml2 import driver_api -from neutron.plugins.ml2.drivers.arista import config # noqa -from neutron.plugins.ml2.drivers.arista import db -from neutron.plugins.ml2.drivers.arista import exceptions as arista_exc - -LOG = logging.getLogger(__name__) - -EOS_UNREACHABLE_MSG = _('Unable to reach EOS') - - -class AristaDriver(driver_api.MechanismDriver): - """Ml2 Mechanism driver for Arista networking hardware. - - Remembers all networks and VMs that are provisioned on Arista Hardware. - Does not send network provisioning request if the network has already been - provisioned before for the given port. - """ - def __init__(self, rpc=None): - - self.rpc = rpc or arista_ml2.AristaRPCWrapper() - self.db_nets = db.AristaProvisionedNets() - self.db_vms = db.AristaProvisionedVms() - self.db_tenants = db.AristaProvisionedTenants() - self.ndb = db_lib.NeutronNets() - - confg = cfg.CONF.ml2_arista - self.segmentation_type = db_lib.VLAN_SEGMENTATION - self.timer = None - self.eos = arista_ml2.SyncService(self.rpc, self.ndb) - self.sync_timeout = confg['sync_interval'] - self.eos_sync_lock = threading.Lock() - - def initialize(self): - self.rpc.register_with_eos() - self._cleanup_db() - self.rpc.check_cli_commands() - # Registering with EOS updates self.rpc.region_updated_time. Clear it - # to force an initial sync - self.rpc.clear_region_updated_time() - self._synchronization_thread() - - def create_network_precommit(self, context): - """Remember the tenant, and network information.""" - - network = context.current - segments = context.network_segments - if segments[0][driver_api.NETWORK_TYPE] != p_const.TYPE_VLAN: - # If network type is not VLAN, do nothing - return - network_id = network['id'] - tenant_id = network['tenant_id'] - if not tenant_id: - tenant_id = context._plugin_context.tenant_id - segmentation_id = segments[0]['segmentation_id'] - with self.eos_sync_lock: - db_lib.remember_tenant(tenant_id) - db_lib.remember_network(tenant_id, - network_id, - segmentation_id) - - def create_network_postcommit(self, context): - """Provision the network on the Arista Hardware.""" - - network = context.current - network_id = network['id'] - network_name = network['name'] - tenant_id = network['tenant_id'] - if not tenant_id: - tenant_id = context._plugin_context.tenant_id - segments = context.network_segments - vlan_id = segments[0]['segmentation_id'] - shared_net = network['shared'] - with self.eos_sync_lock: - if db_lib.is_network_provisioned(tenant_id, network_id): - try: - network_dict = { - 'network_id': network_id, - 'segmentation_id': vlan_id, - 'network_name': network_name, - 'shared': shared_net} - self.rpc.create_network(tenant_id, network_dict) - except arista_exc.AristaRpcError: - LOG.info(EOS_UNREACHABLE_MSG) - raise ml2_exc.MechanismDriverError() - else: - LOG.info(_LI('Network %s is not created as it is not found in ' - 'Arista DB'), network_id) - - def update_network_precommit(self, context): - """At the moment we only support network name change - - Any other change in network is not supported at this time. - We do not store the network names, therefore, no DB store - action is performed here. - """ - new_network = context.current - orig_network = context.original - if new_network['name'] != orig_network['name']: - LOG.info(_LI('Network name changed to %s'), new_network['name']) - - def update_network_postcommit(self, context): - """At the moment we only support network name change - - If network name is changed, a new network create request is - sent to the Arista Hardware. - """ - new_network = context.current - orig_network = context.original - if ((new_network['name'] != orig_network['name']) or - (new_network['shared'] != orig_network['shared'])): - network_id = new_network['id'] - network_name = new_network['name'] - tenant_id = new_network['tenant_id'] - if not tenant_id: - tenant_id = context._plugin_context.tenant_id - vlan_id = new_network['provider:segmentation_id'] - shared_net = new_network['shared'] - with self.eos_sync_lock: - if db_lib.is_network_provisioned(tenant_id, network_id): - try: - network_dict = { - 'network_id': network_id, - 'segmentation_id': vlan_id, - 'network_name': network_name, - 'shared': shared_net} - self.rpc.create_network(tenant_id, network_dict) - except arista_exc.AristaRpcError: - LOG.info(EOS_UNREACHABLE_MSG) - raise ml2_exc.MechanismDriverError() - else: - LOG.info(_LI('Network %s is not updated as it is not found' - ' in Arista DB'), network_id) - - def delete_network_precommit(self, context): - """Delete the network infromation from the DB.""" - network = context.current - network_id = network['id'] - tenant_id = network['tenant_id'] - with self.eos_sync_lock: - if db_lib.is_network_provisioned(tenant_id, network_id): - db_lib.forget_network(tenant_id, network_id) - - def delete_network_postcommit(self, context): - """Send network delete request to Arista HW.""" - network = context.current - segments = context.network_segments - if segments[0][driver_api.NETWORK_TYPE] != p_const.TYPE_VLAN: - # If networtk type is not VLAN, do nothing - return - network_id = network['id'] - tenant_id = network['tenant_id'] - if not tenant_id: - tenant_id = context._plugin_context.tenant_id - with self.eos_sync_lock: - - # Succeed deleting network in case EOS is not accessible. - # EOS state will be updated by sync thread once EOS gets - # alive. - try: - self.rpc.delete_network(tenant_id, network_id) - # if necessary, delete tenant as well. - self.delete_tenant(tenant_id) - except arista_exc.AristaRpcError: - LOG.info(EOS_UNREACHABLE_MSG) - raise ml2_exc.MechanismDriverError() - - def create_port_precommit(self, context): - """Remember the infromation about a VM and its ports - - A VM information, along with the physical host information - is saved. - """ - port = context.current - device_id = port['device_id'] - device_owner = port['device_owner'] - host = context.host - - # device_id and device_owner are set on VM boot - is_vm_boot = device_id and device_owner - if host and is_vm_boot: - port_id = port['id'] - network_id = port['network_id'] - tenant_id = port['tenant_id'] - if not tenant_id: - tenant_id = context._plugin_context.tenant_id - with self.eos_sync_lock: - if not db_lib.is_network_provisioned(tenant_id, network_id): - # Ignore this request if network is not provisioned - return - db_lib.remember_tenant(tenant_id) - db_lib.remember_vm(device_id, host, port_id, - network_id, tenant_id) - - def create_port_postcommit(self, context): - """Plug a physical host into a network. - - Send provisioning request to Arista Hardware to plug a host - into appropriate network. - """ - port = context.current - device_id = port['device_id'] - device_owner = port['device_owner'] - host = context.host - - # device_id and device_owner are set on VM boot - is_vm_boot = device_id and device_owner - if host and is_vm_boot: - port_id = port['id'] - port_name = port['name'] - network_id = port['network_id'] - tenant_id = port['tenant_id'] - if not tenant_id: - tenant_id = context._plugin_context.tenant_id - with self.eos_sync_lock: - hostname = self._host_name(host) - vm_provisioned = db_lib.is_vm_provisioned(device_id, - host, - port_id, - network_id, - tenant_id) - # If network does not exist under this tenant, - # it may be a shared network. Get shared network owner Id - net_provisioned = ( - db_lib.is_network_provisioned(tenant_id, network_id) or - self.ndb.get_shared_network_owner_id(network_id) - ) - if vm_provisioned and net_provisioned: - try: - self.rpc.plug_port_into_network(device_id, - hostname, - port_id, - network_id, - tenant_id, - port_name, - device_owner) - except arista_exc.AristaRpcError: - LOG.info(EOS_UNREACHABLE_MSG) - raise ml2_exc.MechanismDriverError() - else: - LOG.info(_LI('VM %s is not created as it is not found in ' - 'Arista DB'), device_id) - - def update_port_precommit(self, context): - """Update the name of a given port. - - At the moment we only support port name change. - Any other change to port is not supported at this time. - We do not store the port names, therefore, no DB store - action is performed here. - """ - new_port = context.current - orig_port = context.original - if new_port['name'] != orig_port['name']: - LOG.info(_LI('Port name changed to %s'), new_port['name']) - new_port = context.current - device_id = new_port['device_id'] - device_owner = new_port['device_owner'] - host = context.host - - # device_id and device_owner are set on VM boot - is_vm_boot = device_id and device_owner - if host and host != orig_port['binding:host_id'] and is_vm_boot: - port_id = new_port['id'] - network_id = new_port['network_id'] - tenant_id = new_port['tenant_id'] - if not tenant_id: - tenant_id = context._plugin_context.tenant_id - with self.eos_sync_lock: - db_lib.update_vm_host(device_id, host, port_id, - network_id, tenant_id) - - def update_port_postcommit(self, context): - """Update the name of a given port in EOS. - - At the moment we only support port name change - Any other change to port is not supported at this time. - """ - port = context.current - orig_port = context.original - - device_id = port['device_id'] - device_owner = port['device_owner'] - host = context.host - is_vm_boot = device_id and device_owner - - if host and is_vm_boot: - port_id = port['id'] - port_name = port['name'] - network_id = port['network_id'] - tenant_id = port['tenant_id'] - if not tenant_id: - tenant_id = context._plugin_context.tenant_id - with self.eos_sync_lock: - hostname = self._host_name(host) - segmentation_id = db_lib.get_segmentation_id(tenant_id, - network_id) - vm_provisioned = db_lib.is_vm_provisioned(device_id, - host, - port_id, - network_id, - tenant_id) - # If network does not exist under this tenant, - # it may be a shared network. Get shared network owner Id - net_provisioned = ( - db_lib.is_network_provisioned(tenant_id, network_id, - segmentation_id) or - self.ndb.get_shared_network_owner_id(network_id) - ) - if vm_provisioned and net_provisioned: - try: - orig_host = orig_port['binding:host_id'] - if host != orig_host: - # The port moved to a different host. So delete the - # old port on the old host before creating a new - # port on the new host. - self._delete_port(port, orig_host, tenant_id) - self.rpc.plug_port_into_network(device_id, - hostname, - port_id, - network_id, - tenant_id, - port_name, - device_owner) - except arista_exc.AristaRpcError: - LOG.info(EOS_UNREACHABLE_MSG) - raise ml2_exc.MechanismDriverError() - else: - LOG.info(_LI('VM %s is not updated as it is not found in ' - 'Arista DB'), device_id) - - def delete_port_precommit(self, context): - """Delete information about a VM and host from the DB.""" - port = context.current - - host_id = context.host - device_id = port['device_id'] - tenant_id = port['tenant_id'] - if not tenant_id: - tenant_id = context._plugin_context.tenant_id - network_id = port['network_id'] - port_id = port['id'] - with self.eos_sync_lock: - if db_lib.is_vm_provisioned(device_id, host_id, port_id, - network_id, tenant_id): - db_lib.forget_vm(device_id, host_id, port_id, - network_id, tenant_id) - - def delete_port_postcommit(self, context): - """unPlug a physical host from a network. - - Send provisioning request to Arista Hardware to unplug a host - from appropriate network. - """ - port = context.current - host = context.host - tenant_id = port['tenant_id'] - if not tenant_id: - tenant_id = context._plugin_context.tenant_id - - with self.eos_sync_lock: - self._delete_port(port, host, tenant_id) - - def _delete_port(self, port, host, tenant_id): - """Deletes the port from EOS. - - param port: Port which is to be deleted - param host: The host on which the port existed - param tenant_id: The tenant to which the port belongs to. Some times - the tenant id in the port dict is not present (as in - the case of HA router). - """ - device_id = port['device_id'] - port_id = port['id'] - network_id = port['network_id'] - device_owner = port['device_owner'] - - try: - if not db_lib.is_network_provisioned(tenant_id, network_id): - # If we do not have network associated with this, ignore it - return - hostname = self._host_name(host) - if device_owner == n_const.DEVICE_OWNER_DHCP: - self.rpc.unplug_dhcp_port_from_network(device_id, - hostname, - port_id, - network_id, - tenant_id) - else: - self.rpc.unplug_host_from_network(device_id, - hostname, - port_id, - network_id, - tenant_id) - # if necessary, delete tenant as well. - self.delete_tenant(tenant_id) - except arista_exc.AristaRpcError: - LOG.info(EOS_UNREACHABLE_MSG) - raise ml2_exc.MechanismDriverError() - - def delete_tenant(self, tenant_id): - """delete a tenant from DB. - - A tenant is deleted only if there is no network or VM configured - configured for this tenant. - """ - objects_for_tenant = (db_lib.num_nets_provisioned(tenant_id) + - db_lib.num_vms_provisioned(tenant_id)) - if not objects_for_tenant: - db_lib.forget_tenant(tenant_id) - try: - self.rpc.delete_tenant(tenant_id) - except arista_exc.AristaRpcError: - LOG.info(EOS_UNREACHABLE_MSG) - raise ml2_exc.MechanismDriverError() - - def _host_name(self, hostname): - fqdns_used = cfg.CONF.ml2_arista['use_fqdn'] - return hostname if fqdns_used else hostname.split('.')[0] - - def _synchronization_thread(self): - with self.eos_sync_lock: - self.eos.do_synchronize() - - self.timer = threading.Timer(self.sync_timeout, - self._synchronization_thread) - self.timer.start() - - def stop_synchronization_thread(self): - if self.timer: - self.timer.cancel() - self.timer = None - - def _cleanup_db(self): - """Clean up any uncessary entries in our DB.""" - db_tenants = db_lib.get_tenants() - for tenant in db_tenants: - neutron_nets = self.ndb.get_all_networks_for_tenant(tenant) - neutron_nets_id = [] - for net in neutron_nets: - neutron_nets_id.append(net['id']) - db_nets = db_lib.get_networks(tenant) - for net_id in db_nets.keys(): - if net_id not in neutron_nets_id: - db_lib.forget_network(tenant, net_id) diff --git a/neutron/services/l3_router/l3_arista.py b/neutron/services/l3_router/l3_arista.py deleted file mode 100644 index ac5a1d41c..000000000 --- a/neutron/services/l3_router/l3_arista.py +++ /dev/null @@ -1,280 +0,0 @@ -# Copyright 2014 Arista Networks, Inc. All rights reserved. -# -# 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. - -import copy -import threading - -from networking_arista.common import db_lib -from networking_arista.l3Plugin import arista_l3_driver -from oslo_config import cfg -from oslo_log import helpers as log_helpers -from oslo_log import log as logging -from oslo_utils import excutils - -from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api -from neutron.api.rpc.handlers import l3_rpc -from neutron.common import constants as n_const -from neutron.common import rpc as n_rpc -from neutron.common import topics -from neutron import context as nctx -from neutron.db import db_base_plugin_v2 -from neutron.db import extraroute_db -from neutron.db import l3_agentschedulers_db -from neutron.db import l3_gwmode_db -from neutron.i18n import _LE, _LI -from neutron.plugins.common import constants -from neutron.plugins.ml2.driver_context import NetworkContext # noqa - -LOG = logging.getLogger(__name__) - - -class AristaL3ServicePlugin(db_base_plugin_v2.NeutronDbPluginV2, - extraroute_db.ExtraRoute_db_mixin, - l3_gwmode_db.L3_NAT_db_mixin, - l3_agentschedulers_db.L3AgentSchedulerDbMixin): - - """Implements L3 Router service plugin for Arista hardware. - - Creates routers in Arista hardware, manages them, adds/deletes interfaces - to the routes. - """ - - supported_extension_aliases = ["router", "ext-gw-mode", - "extraroute"] - - def __init__(self, driver=None): - - self.driver = driver or arista_l3_driver.AristaL3Driver() - self.ndb = db_lib.NeutronNets() - self.setup_rpc() - self.sync_timeout = cfg.CONF.l3_arista.l3_sync_interval - self.sync_lock = threading.Lock() - self._synchronization_thread() - - def setup_rpc(self): - # RPC support - self.topic = topics.L3PLUGIN - self.conn = n_rpc.create_connection(new=True) - self.agent_notifiers.update( - {n_const.AGENT_TYPE_L3: l3_rpc_agent_api.L3AgentNotifyAPI()}) - self.endpoints = [l3_rpc.L3RpcCallback()] - self.conn.create_consumer(self.topic, self.endpoints, - fanout=False) - self.conn.consume_in_threads() - - def get_plugin_type(self): - return constants.L3_ROUTER_NAT - - def get_plugin_description(self): - """Returns string description of the plugin.""" - return ("Arista L3 Router Service Plugin for Arista Hardware " - "based routing") - - def _synchronization_thread(self): - with self.sync_lock: - self.synchronize() - - self.timer = threading.Timer(self.sync_timeout, - self._synchronization_thread) - self.timer.start() - - def stop_synchronization_thread(self): - if self.timer: - self.timer.cancel() - self.timer = None - - @log_helpers.log_method_call - def create_router(self, context, router): - """Create a new router entry in DB, and create it Arista HW.""" - - tenant_id = self._get_tenant_id_for_create(context, router['router']) - - # Add router to the DB - with context.session.begin(subtransactions=True): - new_router = super(AristaL3ServicePlugin, self).create_router( - context, - router) - # create router on the Arista Hw - try: - self.driver.create_router(context, tenant_id, new_router) - return new_router - except Exception: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Error creating router on Arista HW router=%s "), - new_router) - super(AristaL3ServicePlugin, self).delete_router(context, - new_router['id']) - - @log_helpers.log_method_call - def update_router(self, context, router_id, router): - """Update an existing router in DB, and update it in Arista HW.""" - - with context.session.begin(subtransactions=True): - # Read existing router record from DB - original_router = super(AristaL3ServicePlugin, self).get_router( - context, router_id) - # Update router DB - new_router = super(AristaL3ServicePlugin, self).update_router( - context, router_id, router) - - # Modify router on the Arista Hw - try: - self.driver.update_router(context, router_id, - original_router, new_router) - return new_router - except Exception: - LOG.error(_LE("Error updating router on Arista HW router=%s "), - new_router) - - @log_helpers.log_method_call - def delete_router(self, context, router_id): - """Delete an existing router from Arista HW as well as from the DB.""" - - router = super(AristaL3ServicePlugin, self).get_router(context, - router_id) - tenant_id = router['tenant_id'] - - # Delete router on the Arista Hw - try: - self.driver.delete_router(context, tenant_id, router_id, router) - except Exception as e: - LOG.error(_LE("Error deleting router on Arista HW " - "router %(r)s exception=%(e)s"), - {'r': router, 'e': e}) - - with context.session.begin(subtransactions=True): - super(AristaL3ServicePlugin, self).delete_router(context, - router_id) - - @log_helpers.log_method_call - def add_router_interface(self, context, router_id, interface_info): - """Add a subnet of a network to an existing router.""" - - new_router = super(AristaL3ServicePlugin, self).add_router_interface( - context, router_id, interface_info) - - # Get network info for the subnet that is being added to the router. - # Check if the interface information is by port-id or subnet-id - add_by_port, add_by_sub = self._validate_interface_info(interface_info) - if add_by_sub: - subnet = self.get_subnet(context, interface_info['subnet_id']) - elif add_by_port: - port = self.get_port(context, interface_info['port_id']) - subnet_id = port['fixed_ips'][0]['subnet_id'] - subnet = self.get_subnet(context, subnet_id) - network_id = subnet['network_id'] - - # To create SVI's in Arista HW, the segmentation Id is required - # for this network. - ml2_db = NetworkContext(self, context, {'id': network_id}) - seg_id = ml2_db.network_segments[0]['segmentation_id'] - - # Package all the info needed for Hw programming - router = super(AristaL3ServicePlugin, self).get_router(context, - router_id) - router_info = copy.deepcopy(new_router) - router_info['seg_id'] = seg_id - router_info['name'] = router['name'] - router_info['cidr'] = subnet['cidr'] - router_info['gip'] = subnet['gateway_ip'] - router_info['ip_version'] = subnet['ip_version'] - - try: - self.driver.add_router_interface(context, router_info) - return new_router - except Exception: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Error Adding subnet %(subnet)s to " - "router %(router_id)s on Arista HW"), - {'subnet': subnet, 'router_id': router_id}) - super(AristaL3ServicePlugin, self).remove_router_interface( - context, - router_id, - interface_info) - - @log_helpers.log_method_call - def remove_router_interface(self, context, router_id, interface_info): - """Remove a subnet of a network from an existing router.""" - - new_router = ( - super(AristaL3ServicePlugin, self).remove_router_interface( - context, router_id, interface_info)) - - # Get network information of the subnet that is being removed - subnet = self.get_subnet(context, new_router['subnet_id']) - network_id = subnet['network_id'] - - # For SVI removal from Arista HW, segmentation ID is needed - ml2_db = NetworkContext(self, context, {'id': network_id}) - seg_id = ml2_db.network_segments[0]['segmentation_id'] - - router = super(AristaL3ServicePlugin, self).get_router(context, - router_id) - router_info = copy.deepcopy(new_router) - router_info['seg_id'] = seg_id - router_info['name'] = router['name'] - - try: - self.driver.remove_router_interface(context, router_info) - return new_router - except Exception as exc: - LOG.error(_LE("Error removing interface %(interface)s from " - "router %(router_id)s on Arista HW" - "Exception =(exc)s"), - {'interface': interface_info, 'router_id': router_id, - 'exc': exc}) - - def synchronize(self): - """Synchronizes Router DB from Neturon DB with EOS. - - Walks through the Neturon Db and ensures that all the routers - created in Netuton DB match with EOS. After creating appropriate - routers, it ensures to add interfaces as well. - Uses idempotent properties of EOS configuration, which means - same commands can be repeated. - """ - LOG.info(_LI('Syncing Neutron Router DB <-> EOS')) - ctx = nctx.get_admin_context() - - routers = super(AristaL3ServicePlugin, self).get_routers(ctx) - for r in routers: - tenant_id = r['tenant_id'] - ports = self.ndb.get_all_ports_for_tenant(tenant_id) - - try: - self.driver.create_router(self, tenant_id, r) - - except Exception: - continue - - # Figure out which interfaces are added to this router - for p in ports: - if p['device_id'] == r['id']: - net_id = p['network_id'] - subnet_id = p['fixed_ips'][0]['subnet_id'] - subnet = self.ndb.get_subnet_info(subnet_id) - ml2_db = NetworkContext(self, ctx, {'id': net_id}) - seg_id = ml2_db.network_segments[0]['segmentation_id'] - - r['seg_id'] = seg_id - r['cidr'] = subnet['cidr'] - r['gip'] = subnet['gateway_ip'] - r['ip_version'] = subnet['ip_version'] - - try: - self.driver.add_router_interface(self, r) - except Exception: - LOG.error(_LE("Error Adding interface %(subnet_id)s " - "to router %(router_id)s on Arista HW"), - {'subnet_id': subnet_id, 'router_id': r}) diff --git a/neutron/tests/unit/plugins/ml2/drivers/arista/__init__.py b/neutron/tests/unit/plugins/ml2/drivers/arista/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/tests/unit/plugins/ml2/drivers/arista/test_mechanism_arista.py b/neutron/tests/unit/plugins/ml2/drivers/arista/test_mechanism_arista.py deleted file mode 100644 index 5fb194105..000000000 --- a/neutron/tests/unit/plugins/ml2/drivers/arista/test_mechanism_arista.py +++ /dev/null @@ -1,417 +0,0 @@ -# Copyright (c) 2013 OpenStack Foundation -# -# 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. - -import sys - -import mock - -from neutron.extensions import portbindings -from neutron.tests.unit import testlib_api - -with mock.patch.dict(sys.modules, - {'networking_arista': mock.Mock(), - 'networking_arista.ml2': mock.Mock(), - 'networking_arista.common': mock.Mock()}): - from neutron.plugins.ml2.drivers.arista import mechanism_arista - - -class AristaDriverTestCase(testlib_api.SqlTestCase): - """Main test cases for Arista Mechanism driver. - - Tests all mechanism driver APIs supported by Arista Driver. It invokes - all the APIs as they would be invoked in real world scenarios and - verifies the functionality. - """ - def setUp(self): - super(AristaDriverTestCase, self).setUp() - self.fake_rpc = mock.MagicMock() - mechanism_arista.db_lib = self.fake_rpc - self.drv = mechanism_arista.AristaDriver(self.fake_rpc) - - def tearDown(self): - super(AristaDriverTestCase, self).tearDown() - self.drv.stop_synchronization_thread() - - def test_create_network_precommit(self): - tenant_id = 'ten-1' - network_id = 'net1-id' - segmentation_id = 1001 - - network_context = self._get_network_context(tenant_id, - network_id, - segmentation_id, - False) - self.drv.create_network_precommit(network_context) - - expected_calls = [ - mock.call.remember_tenant(tenant_id), - mock.call.remember_network(tenant_id, - network_id, - segmentation_id) - ] - - mechanism_arista.db_lib.assert_has_calls(expected_calls) - - def test_create_network_postcommit(self): - tenant_id = 'ten-1' - network_id = 'net1-id' - segmentation_id = 1001 - - network_context = self._get_network_context(tenant_id, - network_id, - segmentation_id, - False) - mechanism_arista.db_lib.is_network_provisioned.return_value = True - network = network_context.current - segments = network_context.network_segments - net_dict = { - 'network_id': network['id'], - 'segmentation_id': segments[0]['segmentation_id'], - 'network_name': network['name'], - 'shared': network['shared']} - - self.drv.create_network_postcommit(network_context) - - expected_calls = [ - mock.call.is_network_provisioned(tenant_id, network_id), - mock.call.create_network(tenant_id, net_dict), - ] - - mechanism_arista.db_lib.assert_has_calls(expected_calls) - - def test_delete_network_precommit(self): - tenant_id = 'ten-1' - network_id = 'net1-id' - segmentation_id = 1001 - - network_context = self._get_network_context(tenant_id, - network_id, - segmentation_id, - False) - mechanism_arista.db_lib.is_network_provisioned.return_value = True - mechanism_arista.db_lib.num_nets_provisioned.return_value = 0 - mechanism_arista.db_lib.num_vms_provisioned.return_value = 0 - self.drv.delete_network_precommit(network_context) - - expected_calls = [ - mock.call.is_network_provisioned(tenant_id, network_id), - mock.call.forget_network(tenant_id, network_id), - ] - - mechanism_arista.db_lib.assert_has_calls(expected_calls) - - def test_delete_network_postcommit(self): - tenant_id = 'ten-1' - network_id = 'net1-id' - segmentation_id = 1001 - - network_context = self._get_network_context(tenant_id, - network_id, - segmentation_id, - False) - - self.drv.delete_network_postcommit(network_context) - expected_calls = [ - mock.call.delete_network(tenant_id, network_id), - ] - - mechanism_arista.db_lib.assert_has_calls(expected_calls) - - def test_create_port_precommit(self): - tenant_id = 'ten-1' - network_id = 'net1-id' - segmentation_id = 1001 - vm_id = 'vm1' - - network_context = self._get_network_context(tenant_id, - network_id, - segmentation_id, - False) - - port_context = self._get_port_context(tenant_id, - network_id, - vm_id, - network_context) - host_id = port_context.current['binding:host_id'] - port_id = port_context.current['id'] - self.drv.create_port_precommit(port_context) - - expected_calls = [ - mock.call.remember_tenant(tenant_id), - mock.call.remember_vm(vm_id, host_id, port_id, - network_id, tenant_id) - ] - - mechanism_arista.db_lib.assert_has_calls(expected_calls) - - def test_create_port_postcommit(self): - tenant_id = 'ten-1' - network_id = 'net1-id' - segmentation_id = 1001 - vm_id = 'vm1' - - network_context = self._get_network_context(tenant_id, - network_id, - segmentation_id, - False) - port_context = self._get_port_context(tenant_id, - network_id, - vm_id, - network_context) - mechanism_arista.db_lib.is_vm_provisioned.return_value = True - mechanism_arista.db_lib.is_network_provisioned.return_value = True - mechanism_arista.db_lib.get_shared_network_owner_id.return_value = 1 - - port = port_context.current - device_id = port['device_id'] - device_owner = port['device_owner'] - host_id = port['binding:host_id'] - port_id = port['id'] - port_name = port['name'] - - self.drv.create_port_postcommit(port_context) - - expected_calls = [ - mock.call.is_vm_provisioned(device_id, host_id, port_id, - network_id, tenant_id), - mock.call.is_network_provisioned(tenant_id, network_id), - mock.call.plug_port_into_network(device_id, host_id, port_id, - network_id, tenant_id, - port_name, device_owner) - ] - - mechanism_arista.db_lib.assert_has_calls(expected_calls) - - # Now test the delete ports - def test_delete_port_precommit(self): - tenant_id = 'ten-1' - network_id = 'net1-id' - segmentation_id = 1001 - vm_id = 'vm1' - - network_context = self._get_network_context(tenant_id, - network_id, - segmentation_id, - False) - - port_context = self._get_port_context(tenant_id, - network_id, - vm_id, - network_context) - mechanism_arista.db_lib.is_vm_provisioned.return_value = True - mechanism_arista.db_lib.num_nets_provisioned.return_value = 0 - mechanism_arista.db_lib.num_vms_provisioned.return_value = 0 - self.drv.delete_port_precommit(port_context) - - host_id = port_context.current['binding:host_id'] - port_id = port_context.current['id'] - expected_calls = [ - mock.call.is_vm_provisioned(vm_id, host_id, port_id, - network_id, tenant_id), - mock.call.forget_vm(vm_id, host_id, port_id, - network_id, tenant_id), - ] - - mechanism_arista.db_lib.assert_has_calls(expected_calls) - - def test_delete_port_postcommit(self): - tenant_id = 'ten-1' - network_id = 'net1-id' - segmentation_id = 1001 - vm_id = 'vm1' - - network_context = self._get_network_context(tenant_id, - network_id, - segmentation_id, - False) - port_context = self._get_port_context(tenant_id, - network_id, - vm_id, - network_context) - port = port_context.current - device_id = port['device_id'] - host_id = port['binding:host_id'] - port_id = port['id'] - - self.drv.delete_port_postcommit(port_context) - - expected_calls = [ - mock.call.unplug_host_from_network(device_id, host_id, port_id, - network_id, tenant_id) - ] - - mechanism_arista.db_lib.assert_has_calls(expected_calls) - - def test_update_port_precommit(self): - tenant_id = 'ten-1' - network_id = 'net1-id' - segmentation_id = 1001 - vm_id = 'vm1' - - network_context = self._get_network_context(tenant_id, - network_id, - segmentation_id, - False) - - port_context = self._get_port_context(tenant_id, - network_id, - vm_id, - network_context) - host_id = port_context.current['binding:host_id'] - port_context.original['binding:host_id'] = 'ubuntu0' - port_id = port_context.current['id'] - self.drv.update_port_precommit(port_context) - - expected_calls = [ - mock.call.update_vm_host(vm_id, host_id, port_id, - network_id, tenant_id) - ] - - mechanism_arista.db_lib.assert_has_calls(expected_calls) - - def test_update_port_postcommit(self): - tenant_id = 'ten-1' - network_id = 'net1-id' - segmentation_id = 1001 - vm_id = 'vm1' - - network_context = self._get_network_context(tenant_id, - network_id, - segmentation_id, - False) - port_context = self._get_port_context(tenant_id, - network_id, - vm_id, - network_context) - - mechanism_arista.db_lib.is_vm_provisioned.return_value = True - mechanism_arista.db_lib.is_network_provisioned.return_value = True - mechanism_arista.db_lib.get_shared_network_owner_id.return_value = 1 - mechanism_arista.db_lib.get_segmentation_id.return_value = 1001 - mechanism_arista.db_lib.num_nets_provisioned.return_value = 1 - mechanism_arista.db_lib.num_vms_provisioned.return_value = 1 - - port = port_context.current - device_id = port['device_id'] - device_owner = port['device_owner'] - host_id = port['binding:host_id'] - orig_host_id = 'ubuntu0' - port_context.original['binding:host_id'] = orig_host_id - port_id = port['id'] - port_name = port['name'] - - self.drv.update_port_postcommit(port_context) - - expected_calls = [ - mock.call.NeutronNets(), - mock.call.get_segmentation_id(tenant_id, network_id), - mock.call.is_vm_provisioned(device_id, host_id, port_id, - network_id, tenant_id), - mock.call.is_network_provisioned(tenant_id, network_id, - segmentation_id), - mock.call.is_network_provisioned(tenant_id, network_id), - mock.call.unplug_host_from_network(device_id, orig_host_id, - port_id, network_id, tenant_id), - mock.call.num_nets_provisioned(tenant_id), - mock.call.num_vms_provisioned(tenant_id), - mock.call.plug_port_into_network(device_id, host_id, port_id, - network_id, tenant_id, - port_name, device_owner) - ] - - mechanism_arista.db_lib.assert_has_calls(expected_calls) - - def _get_network_context(self, tenant_id, net_id, seg_id, shared): - network = {'id': net_id, - 'tenant_id': tenant_id, - 'name': 'test-net', - 'shared': shared} - network_segments = [{'segmentation_id': seg_id, - 'network_type': 'vlan'}] - return FakeNetworkContext(network, network_segments, network) - - def _get_port_context(self, tenant_id, net_id, vm_id, network): - port = {'device_id': vm_id, - 'device_owner': 'compute', - 'binding:host_id': 'ubuntu1', - 'name': 'test-port', - 'tenant_id': tenant_id, - 'id': 101, - 'network_id': net_id - } - return FakePortContext(port, dict(port), network) - - -class fake_keystone_info_class(object): - """To generate fake Keystone Authentification token information - - Arista Driver expects Keystone auth info. This fake information - is for testing only - """ - auth_uri = 'abc://host:35357/v2.0/' - identity_uri = 'abc://host:5000' - admin_user = 'neutron' - admin_password = 'fun' - admin_tenant_name = 'tenant_name' - - -class FakeNetworkContext(object): - """To generate network context for testing purposes only.""" - - def __init__(self, network, segments=None, original_network=None): - self._network = network - self._original_network = original_network - self._segments = segments - - @property - def current(self): - return self._network - - @property - def original(self): - return self._original_network - - @property - def network_segments(self): - return self._segments - - -class FakePortContext(object): - """To generate port context for testing purposes only.""" - - def __init__(self, port, original_port, network): - self._port = port - self._original_port = original_port - self._network_context = network - - @property - def current(self): - return self._port - - @property - def original(self): - return self._original_port - - @property - def network(self): - return self._network_context - - @property - def host(self): - return self._port.get(portbindings.HOST_ID) - - @property - def original_host(self): - return self._original_port.get(portbindings.HOST_ID) diff --git a/setup.cfg b/setup.cfg index c0eee5195..34a094a94 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,7 +63,6 @@ data_files = etc/neutron/plugins/bigswitch/restproxy.ini etc/neutron/plugins/ml2/linuxbridge_agent.ini etc/neutron/plugins/ml2/ml2_conf.ini - etc/neutron/plugins/ml2/ml2_conf_arista.ini etc/neutron/plugins/ml2/ml2_conf_brocade.ini etc/neutron/plugins/ml2/ml2_conf_brocade_fi_ni.ini etc/neutron/plugins/ml2/ml2_conf_cisco.ini @@ -167,7 +166,6 @@ neutron.ml2.mechanism_drivers = linuxbridge = neutron.plugins.ml2.drivers.linuxbridge.mech_driver.mech_linuxbridge:LinuxbridgeMechanismDriver openvswitch = neutron.plugins.ml2.drivers.openvswitch.mech_driver.mech_openvswitch:OpenvswitchMechanismDriver hyperv = neutron.plugins.ml2.drivers.hyperv.mech_hyperv:HypervMechanismDriver - arista = neutron.plugins.ml2.drivers.arista.mechanism_arista:AristaDriver # Note: ncs and cisco_ncs point to the same driver entrypoint # TODO: The old name (ncs) can be dropped when it is no longer used ncs = neutron.plugins.ml2.drivers.cisco.ncs.driver:NCSMechanismDriver diff --git a/tox.ini b/tox.ini index fdc2dbe47..51b48a585 100644 --- a/tox.ini +++ b/tox.ini @@ -140,7 +140,6 @@ commands = python -m testtools.run \ neutron.tests.unit.plugins.ml2.drivers.test_type_vxlan \ neutron.tests.unit.plugins.ml2.drivers.test_type_gre \ neutron.tests.unit.plugins.ml2.drivers.test_helpers \ - neutron.tests.unit.plugins.ml2.drivers.arista.test_mechanism_arista \ neutron.tests.unit.plugins.ml2.drivers.test_type_local \ neutron.tests.unit.plugins.ml2.drivers.mechanism_logger \ neutron.tests.unit.plugins.ml2.drivers.test_type_flat \ -- 2.45.2