diff --git a/etc/neutron/plugins/ml2/ml2_conf_fslsdn.ini b/etc/neutron/plugins/ml2/ml2_conf_fslsdn.ini new file mode 100644 index 00000000000..6ee4a4e0091 --- /dev/null +++ b/etc/neutron/plugins/ml2/ml2_conf_fslsdn.ini @@ -0,0 +1,52 @@ +# Defines Configuration options for FSL SDN OS Mechanism Driver +# Cloud Resource Discovery (CRD) authorization credentials +[ml2_fslsdn] +#(StrOpt) User name for authentication to CRD. +# e.g.: user12 +# +# crd_user_name = + +#(StrOpt) Password for authentication to CRD. +# e.g.: secret +# +# crd_password = + +#(StrOpt) Tenant name for CRD service. +# e.g.: service +# +# crd_tenant_name = + +#(StrOpt) CRD auth URL. +# e.g.: http://127.0.0.1:5000/v2.0/ +# +# crd_auth_url = + +#(StrOpt) URL for connecting to CRD Service. +# e.g.: http://127.0.0.1:9797 +# +# crd_url= + +#(IntOpt) Timeout value for connecting to CRD service +# in seconds, e.g.: 30 +# +# crd_url_timeout= + +#(StrOpt) Region name for connecting to CRD in +# admin context, e.g.: RegionOne +# +# crd_region_name= + +#(BoolOpt)If set, ignore any SSL validation issues (boolean value) +# e.g.: False +# +# crd_api_insecure= + +#(StrOpt)Authorization strategy for connecting to CRD in admin +# context, e.g.: keystone +# +# crd_auth_strategy= + +#(StrOpt)Location of CA certificates file to use for CRD client +# requests. +# +# crd_ca_certificates_file= diff --git a/neutron/plugins/ml2/drivers/README.fslsdn b/neutron/plugins/ml2/drivers/README.fslsdn new file mode 100644 index 00000000000..09017284cb1 --- /dev/null +++ b/neutron/plugins/ml2/drivers/README.fslsdn @@ -0,0 +1,102 @@ +===================================================== +Freescale SDN Mechanism Driver for Neutron ML2 plugin +===================================================== + +Introduction +============ + +Freescale SDN (FSL-SDN) Mechanism Driver is an add-on support for ML2 plugin +for Neutron. + +It supports the Cloud Resource Discovery (CRD) service by updating +Network, Subnet and Port Create/Update/Delete data into the CRD database. + +CRD service manages network nodes, virtual network appliances and openflow +controller based network applications. + +Basic work flow +--------------- + +:: + + +---------------------------------+ + | | + | Neutron Server | + | (with ML2 plugin) | + | | + | +-------------------------------+ + | | Freescale SDN | + | | Mechanism Driver | + +-+--------+----------------------+ + | + | ReST API + | + +----------+-------------+ + | CRD server | + +------------------------+ + + + +How does Freescale SDN Mechanism Driver work? +=========================================== + +- Freescale Mechanism driver handles the following postcommit operations. + - Network create/update/delete + - Subnet create/update/delete + - Port create/delete + +Sequence diagram : create_network +--------------------------------- + +:: + + create_network + { + neutron -> ML2_plugin + ML2_plugin -> FSL-SDN-MD + FSL-SDN-MD -> crd_service + FSL-SDN-MD <-- crd_service + ML2_plugin <-- FSL-SDN-MD + neutron <-- ML2_plugin + } + +- Supported network types by FSL OF Controller include vlan and vxlan. + +- Freescale SDN mechanism driver handles VM port binding within in the + mechanism driver (like ODL MD). + +- 'bind_port' function verifies the supported network types (vlan,vxlan) + and calls context.set_binding with binding details. + +- Flow management in OVS is handled by Freescale Openflow Controller. + + +How to use Freescale SDN Mechanism Driver? +========================================== + +Configuring ML2 Plugin +---------------------- + +In [ml2] section of /etc/neutron/plugins/ml2/ml2_conf.ini, +modify 'mechanism_drivers' attributes as: + +:: + + mechanism_drivers = fslsdn + +Configuring FSLSDN Mechanism Driver +----------------------------------- + +Update /etc/neutron/plugins/ml2/ml2_conf_fslsdn.ini, as below. + +:: + + [ml2_fslsdn] + crd_auth_strategy = keystone + crd_url = http://127.0.0.1:9797 + crd_auth_url = http://127.0.0.1:5000/v2.0/ + crd_tenant_name = service + crd_password = <-service-password-> + crd_user_name = <-service-username-> + +CRD service must be running in the controller. diff --git a/neutron/plugins/ml2/drivers/mechanism_fslsdn.py b/neutron/plugins/ml2/drivers/mechanism_fslsdn.py new file mode 100755 index 00000000000..514fd9b86c9 --- /dev/null +++ b/neutron/plugins/ml2/drivers/mechanism_fslsdn.py @@ -0,0 +1,288 @@ +# Copyright (c) 2014 Freescale Semiconductor +# +# 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. + +# @author: Trinath Somanchi, Freescale, Inc + + +from neutronclient.v2_0 import client +from oslo.config import cfg + +from neutron.common import constants as n_const +from neutron.common import log +from neutron.extensions import portbindings +from neutron.openstack.common import log as logging +from neutron.plugins.common import constants +from neutron.plugins.ml2 import driver_api as api + + +LOG = logging.getLogger(__name__) + +# CRD service options required for FSL SDN OS Mech Driver +ml2_fslsdn_opts = [ + cfg.StrOpt('crd_user_name', default='crd', + help=_("CRD service Username")), + cfg.StrOpt('crd_password', default='password', + secret='True', + help=_("CRD Service Password")), + cfg.StrOpt('crd_tenant_name', default='service', + help=_("CRD Tenant Name")), + cfg.StrOpt('crd_auth_url', + default='http://127.0.0.1:5000/v2.0/', + help=_("CRD Auth URL")), + cfg.StrOpt('crd_url', + default='http://127.0.0.1:9797', + help=_("URL for connecting to CRD service")), + cfg.IntOpt('crd_url_timeout', + default=30, + help=_("Timeout value for connecting to " + "CRD service in seconds")), + cfg.StrOpt('crd_region_name', + default='RegionOne', + help=_("Region name for connecting to " + "CRD Service in admin context")), + cfg.BoolOpt('crd_api_insecure', + default=False, + help=_("If set, ignore any SSL validation issues")), + cfg.StrOpt('crd_auth_strategy', + default='keystone', + help=_("Auth strategy for connecting to " + "neutron in admin context")), + cfg.StrOpt('crd_ca_certificates_file', + help=_("Location of ca certificates file to use for " + "CRD client requests.")), +] + +# Register the configuration option for crd service +# required for FSL SDN OS Mechanism driver +cfg.CONF.register_opts(ml2_fslsdn_opts, "ml2_fslsdn") + +# shortcut +FSLCONF = cfg.CONF.ml2_fslsdn + +SERVICE_TYPE = 'crd' + + +class FslsdnMechanismDriver(api.MechanismDriver): + + """Freescale SDN OS Mechanism Driver for ML2 Plugin.""" + + @log.log + def initialize(self): + """Initialize the Mechanism driver.""" + + self.vif_type = portbindings.VIF_TYPE_OVS + self.vif_details = {portbindings.CAP_PORT_FILTER: True} + LOG.info(_("Initializing CRD client... ")) + crd_client_params = { + 'username': FSLCONF.crd_user_name, + 'tenant_name': FSLCONF.crd_tenant_name, + 'region_name': FSLCONF.crd_region_name, + 'password': FSLCONF.crd_password, + 'auth_url': FSLCONF.crd_auth_url, + 'auth_strategy': FSLCONF.crd_auth_strategy, + 'endpoint_url': FSLCONF.crd_url, + 'timeout': FSLCONF.crd_url_timeout, + 'insecure': FSLCONF.crd_api_insecure, + 'service_type': SERVICE_TYPE, + 'ca_cert': FSLCONF.crd_ca_certificates_file, + } + self._crdclient = client.Client(**crd_client_params) + + # Network Management + @staticmethod + @log.log + def _prepare_crd_network(network, segments): + """Helper function to create 'network' data.""" + + return {'network': + {'network_id': network['id'], + 'tenant_id': network['tenant_id'], + 'name': network['name'], + 'status': network['status'], + 'admin_state_up': network['admin_state_up'], + 'segments': segments, + }} + + def create_network_postcommit(self, context): + """Send create_network data to CRD service.""" + + network = context.current + segments = context.network_segments + body = self._prepare_crd_network(network, segments) + self._crdclient.create_network(body=body) + LOG.debug("create_network update sent to CRD Server: %s", body) + + def update_network_postcommit(self, context): + """Send update_network data to CRD service.""" + + network = context.current + segments = context.network_segments + body = self._prepare_crd_network(network, segments) + self._crdclient.update_network(network['id'], body=body) + LOG.debug("update_network update sent to CRD Server: %s", body) + + def delete_network_postcommit(self, context): + """Send delete_network data to CRD service.""" + + network = context.current + self._crdclient.delete_network(network['id']) + LOG.debug( + "delete_network update sent to CRD Server: %s", + network['id']) + + # Port Management + @staticmethod + def _prepare_crd_port(port): + """Helper function to prepare 'port' data.""" + + crd_subnet_id = '' + crd_ipaddress = '' + crd_sec_grps = '' + # Since CRD accepts one Fixed IP, + # so handle only one fixed IP per port. + if len(port['fixed_ips']) > 1: + LOG.debug("More than one fixed IP exists - using first one.") + # check empty fixed_ips list, move on if one or more exists + if len(port['fixed_ips']) != 0: + crd_subnet_id = port['fixed_ips'][0]['subnet_id'] + crd_ipaddress = port['fixed_ips'][0]['ip_address'] + LOG.debug("Handling fixed IP {subnet_id:%(subnet)s, " + "ip_address:%(ip)s}", + {'subnet': crd_subnet_id, 'ip': crd_ipaddress}) + else: + LOG.debug("No fixed IPs found.") + if 'security_groups' in port: + crd_sec_grps = ','.join(port['security_groups']) + return {'port': + {'port_id': port['id'], + 'tenant_id': port['tenant_id'], + 'name': port['name'], + 'network_id': port['network_id'], + 'subnet_id': crd_subnet_id, + 'mac_address': port['mac_address'], + 'device_id': port['device_id'], + 'ip_address': crd_ipaddress, + 'admin_state_up': port['admin_state_up'], + 'status': port['status'], + 'device_owner': port['device_owner'], + 'security_groups': crd_sec_grps, + }} + + def create_port_postcommit(self, context): + """Send create_port data to CRD service.""" + + port = context.current + body = self._prepare_crd_port(port) + self._crdclient.create_port(body=body) + LOG.debug("create_port update sent to CRD Server: %s", body) + + def delete_port_postcommit(self, context): + """Send delete_port data to CRD service.""" + + port = context.current + self._crdclient.delete_port(port['id']) + LOG.debug("delete_port update sent to CRD Server: %s", port['id']) + + # Subnet Management + @staticmethod + @log.log + def _prepare_crd_subnet(subnet): + """Helper function to prepare 'subnet' data.""" + + crd_allocation_pools = '' + crd_dns_nameservers = '' + crd_host_routes = '' + # Handling Allocation IPs + if 'allocation_pools' in subnet: + a_pools = subnet['allocation_pools'] + crd_allocation_pools = ','.join(["%s-%s" % (p['start'], + p['end']) + for p in a_pools]) + # Handling Host Routes + if 'host_routes' in subnet: + crd_host_routes = ','.join(["%s-%s" % (r['destination'], + r['nexthop']) + for r in subnet['host_routes']]) + # Handling DNS Nameservers + if 'dns_nameservers' in subnet: + crd_dns_nameservers = ','.join(subnet['dns_nameservers']) + # return Subnet Data + return {'subnet': + {'subnet_id': subnet['id'], + 'tenant_id': subnet['tenant_id'], + 'name': subnet['name'], + 'network_id': subnet['network_id'], + 'ip_version': subnet['ip_version'], + 'cidr': subnet['cidr'], + 'gateway_ip': subnet['gateway_ip'], + 'dns_nameservers': crd_dns_nameservers, + 'allocation_pools': crd_allocation_pools, + 'host_routes': crd_host_routes, + }} + + def create_subnet_postcommit(self, context): + """Send create_subnet data to CRD service.""" + + subnet = context.current + body = self._prepare_crd_subnet(subnet) + self._crdclient.create_subnet(body=body) + LOG.debug("create_subnet update sent to CRD Server: %s", body) + + def update_subnet_postcommit(self, context): + """Send update_subnet data to CRD service.""" + + subnet = context.current + body = self._prepare_crd_subnet(subnet) + self._crdclient.update_subnet(subnet['id'], body=body) + LOG.debug("update_subnet update sent to CRD Server: %s", body) + + def delete_subnet_postcommit(self, context): + """Send delete_subnet data to CRD service.""" + + subnet = context.current + self._crdclient.delete_subnet(subnet['id']) + LOG.debug("delete_subnet update sent to CRD Server: %s", subnet['id']) + + def bind_port(self, context): + """Set porting binding data for use with nova.""" + + LOG.debug("Attempting to bind port %(port)s on " + "network %(network)s", + {'port': context.current['id'], + 'network': context.network.current['id']}) + # Prepared porting binding data + for segment in context.network.network_segments: + if self.check_segment(segment): + context.set_binding(segment[api.ID], + self.vif_type, + self.vif_details, + status=n_const.PORT_STATUS_ACTIVE) + LOG.debug("Bound using segment: %s", segment) + return + else: + LOG.debug("Refusing to bind port for segment ID %(id)s, " + "segment %(seg)s, phys net %(physnet)s, and " + "network type %(nettype)s", + {'id': segment[api.ID], + 'seg': segment[api.SEGMENTATION_ID], + 'physnet': segment[api.PHYSICAL_NETWORK], + 'nettype': segment[api.NETWORK_TYPE]}) + + @log.log + def check_segment(self, segment): + """Verify a segment is valid for the FSL SDN MechanismDriver.""" + + return segment[api.NETWORK_TYPE] in [constants.TYPE_VLAN, + constants.TYPE_VXLAN] diff --git a/neutron/tests/unit/ml2/test_mechanism_fslsdn.py b/neutron/tests/unit/ml2/test_mechanism_fslsdn.py new file mode 100644 index 00000000000..d39e5973f7b --- /dev/null +++ b/neutron/tests/unit/ml2/test_mechanism_fslsdn.py @@ -0,0 +1,293 @@ +# Copyright (c) 2014 Freescale, Inc. +# +# 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. + +# @author: Trinath Somanchi, Freescale, Inc. + +import mock +from oslo.config import cfg + +from neutron.extensions import portbindings +from neutron.plugins.ml2.drivers import mechanism_fslsdn +from neutron.tests import base +from neutron.tests.unit import test_db_plugin + + +"""Unit testing for Freescale SDN mechanism driver.""" + + +def setup_driver_config(): + """Mechanism Driver specific configuration.""" + + # Configure mechanism driver as 'fslsdn' + cfg.CONF.set_override('mechanism_drivers', ['fslsdn'], 'ml2') + # Configure FSL SDN Mechanism driver specific options + cfg.CONF.set_override('crd_user_name', 'crd', 'ml2_fslsdn') + cfg.CONF.set_override('crd_password', 'CRD_PASS', 'ml2_fslsdn') + cfg.CONF.set_override('crd_tenant_name', 'service', 'ml2_fslsdn') + cfg.CONF.set_override('crd_auth_url', + 'http://127.0.0.1:5000/v2.0', 'ml2_fslsdn') + cfg.CONF.set_override('crd_url', + 'http://127.0.0.1:9797', 'ml2_fslsdn') + cfg.CONF.set_override('crd_auth_strategy', 'keystone', 'ml2_fslsdn') + + +class TestFslSdnMechDriverV2(test_db_plugin.NeutronDbPluginV2TestCase): + + """Testing mechanism driver with ML2 plugin.""" + + def setUp(self): + setup_driver_config() + + def mocked_fslsdn_init(self): + # Mock CRD client, since it requires CRD service running. + self._crdclieint = mock.Mock() + + with mock.patch.object(mechanism_fslsdn.FslsdnMechanismDriver, + 'initialize', new=mocked_fslsdn_init): + super(TestFslSdnMechDriverV2, self).setUp() + + +class TestFslSdnMechDriverNetworksV2(test_db_plugin.TestNetworksV2, + TestFslSdnMechDriverV2): + pass + + +class TestFslSdnMechDriverPortsV2(test_db_plugin.TestPortsV2, + TestFslSdnMechDriverV2): + VIF_TYPE = portbindings.VIF_TYPE_OVS + CAP_PORT_FILTER = True + + +class TestFslSdnMechDriverSubnetsV2(test_db_plugin.TestSubnetsV2, + TestFslSdnMechDriverV2): + pass + + +class TestFslSdnMechanismDriver(base.BaseTestCase): + + """Testing FSL SDN Mechanism driver.""" + + def setUp(self): + super(TestFslSdnMechanismDriver, self).setUp() + setup_driver_config() + self.driver = mechanism_fslsdn.FslsdnMechanismDriver() + self.driver.initialize() + self.client = self.driver._crdclient = mock.Mock() + + def test_create_update_delete_network_postcommit(self): + """Testing create/update/delete network postcommit operations.""" + + tenant_id = 'test' + network_id = '123' + segmentation_id = 456 + expected_seg = [{'segmentation_id': segmentation_id}] + expected_crd_network = {'network': + {'network_id': network_id, + 'tenant_id': tenant_id, + 'name': 'FakeNetwork', + 'status': 'ACTIVE', + 'admin_state_up': True, + 'segments': expected_seg}} + network_context = self._get_network_context(tenant_id, network_id, + segmentation_id) + network = network_context.current + segments = network_context.network_segments + net_id = network['id'] + req = self.driver._prepare_crd_network(network, segments) + # test crd network dict + self.assertEqual(expected_crd_network, req) + # test create_network. + self.driver.create_network_postcommit(network_context) + self.client.create_network.assert_called_once_with(body=req) + # test update_network. + self.driver.update_network_postcommit(network_context) + self.client.update_network.assert_called_once_with(net_id, body=req) + # test delete_network. + self.driver.delete_network_postcommit(network_context) + self.client.delete_network.assert_called_once_with(net_id) + + def test_create_update_delete_subnet_postcommit(self): + """Testing create/update/delete subnet postcommit operations.""" + + tenant_id = 'test' + network_id = '123' + subnet_id = '122' + cidr = '192.0.0.0/8' + gateway_ip = '192.0.0.1' + expected_crd_subnet = {'subnet': + {'subnet_id': subnet_id, 'tenant_id': tenant_id, + 'name': 'FakeSubnet', 'network_id': network_id, + 'ip_version': 4, 'cidr': cidr, + 'gateway_ip': gateway_ip, + 'dns_nameservers': '', + 'allocation_pools': '', + 'host_routes': ''}} + subnet_context = self._get_subnet_context(tenant_id, network_id, + subnet_id, cidr, gateway_ip) + subnet = subnet_context.current + subnet_id = subnet['id'] + req = self.driver._prepare_crd_subnet(subnet) + # test crd subnet dict + self.assertEqual(expected_crd_subnet, req) + # test create_subnet. + self.driver.create_subnet_postcommit(subnet_context) + self.client.create_subnet.assert_called_once_with(body=req) + # test update_subnet. + self.driver.update_subnet_postcommit(subnet_context) + self.client.update_subnet.assert_called_once_with(subnet_id, body=req) + # test delete_subnet. + self.driver.delete_subnet_postcommit(subnet_context) + self.client.delete_subnet.assert_called_once_with(subnet_id) + + def test_create_delete_port_postcommit(self): + """Testing create/delete port postcommit operations.""" + + tenant_id = 'test' + network_id = '123' + port_id = '453' + expected_crd_port = {'port': + {'port_id': port_id, 'tenant_id': tenant_id, + 'name': 'FakePort', 'network_id': network_id, + 'subnet_id': '', 'mac_address': 'aabb', + 'device_id': '1234', 'ip_address': '', + 'admin_state_up': True, 'status': 'ACTIVE', + 'device_owner': 'compute', + 'security_groups': ''}} + # Test with empty fixed IP + port_context = self._get_port_context(tenant_id, network_id, port_id) + port = port_context.current + req = self.driver._prepare_crd_port(port) + # Test crd port dict + self.assertEqual(expected_crd_port, req) + # test create_port. + self.driver.create_port_postcommit(port_context) + self.client.create_port.assert_called_once_with(body=req) + # Test delete_port + self.driver.delete_port_postcommit(port_context) + self.client.delete_port.assert_called_once_with(port['id']) + + def test_prepare_port_with_single_fixed_ip(self): + """Test _prepare_crd_port with single fixed_ip.""" + + tenant_id = 'test' + network_id = '123' + port_id = '453' + fips = [{"subnet_id": "sub-1", "ip_address": "10.0.0.1"}] + expected_crd_port = {'port': + {'port_id': port_id, 'tenant_id': tenant_id, + 'name': 'FakePort', 'network_id': network_id, + 'subnet_id': '', 'mac_address': 'aabb', + 'device_id': '1234', 'ip_address': '', + 'admin_state_up': True, 'status': 'ACTIVE', + 'device_owner': 'compute', + 'security_groups': ''}} + port_context = self._get_port_context(tenant_id, network_id, port_id, + fips) + port = port_context.current + req = self.driver._prepare_crd_port(port) + expected_crd_port['port']['subnet_id'] = 'sub-1' + expected_crd_port['port']['ip_address'] = '10.0.0.1' + self.assertEqual(expected_crd_port, req) + + def test_prepare_port_with_multiple_fixed_ips(self): + """Test _prepare_crd_port with multiple fixed_ips.""" + + tenant_id = 'test' + network_id = '123' + port_id = '453' + multiple_fips = [{"subnet_id": "sub-1", "ip_address": "10.0.0.1"}, + {"subnet_id": "sub-1", "ip_address": "10.0.0.4"}] + expected_crd_port = {'port': + {'port_id': port_id, 'tenant_id': tenant_id, + 'name': 'FakePort', 'network_id': network_id, + 'subnet_id': '', 'mac_address': 'aabb', + 'device_id': '1234', 'ip_address': '', + 'admin_state_up': True, 'status': 'ACTIVE', + 'device_owner': 'compute', + 'security_groups': ''}} + port_context = self._get_port_context(tenant_id, network_id, port_id, + multiple_fips) + port = port_context.current + req = self.driver._prepare_crd_port(port) + expected_crd_port['port']['subnet_id'] = 'sub-1' + expected_crd_port['port']['ip_address'] = '10.0.0.1' + self.assertEqual(expected_crd_port, req) + + def _get_subnet_context(self, tenant_id, net_id, subnet_id, cidr, + gateway_ip): + # sample data for testing purpose only. + subnet = {'tenant_id': tenant_id, + 'network_id': net_id, + 'id': subnet_id, + 'cidr': cidr, + 'name': 'FakeSubnet', + 'ip_version': 4, + 'gateway_ip': gateway_ip, + } + return FakeContext(subnet) + + def _get_port_context(self, tenant_id, net_id, port_id, + fixed_ips=[]): + # sample data for testing purpose only + port = {'device_id': '1234', + 'name': 'FakePort', + 'mac_address': 'aabb', + 'device_owner': 'compute', + 'tenant_id': tenant_id, + 'id': port_id, + 'fixed_ips': fixed_ips, + 'admin_state_up': True, + 'status': 'ACTIVE', + 'network_id': net_id} + return FakeContext(port) + + def _get_network_context(self, tenant_id, net_id, seg_id): + # sample data for testing purpose only. + network = {'id': net_id, + 'tenant_id': tenant_id, + 'admin_state_up': True, + 'status': 'ACTIVE', + 'name': 'FakeNetwork', } + segments = [{'segmentation_id': seg_id}] + return FakeNetworkContext(network, segments) + + +class FakeNetworkContext(object): + + """To generate network context for testing purposes only.""" + + def __init__(self, network, segments): + self._network = network + self._segments = segments + + @property + def current(self): + return self._network + + @property + def network_segments(self): + return self._segments + + +class FakeContext(object): + + """To generate context for testing purposes only.""" + + def __init__(self, record): + self._record = record + + @property + def current(self): + return self._record diff --git a/setup.cfg b/setup.cfg index a0676633449..2e8f7fb3433 100644 --- a/setup.cfg +++ b/setup.cfg @@ -70,6 +70,7 @@ data_files = etc/neutron/plugins/ml2/ml2_conf_ncs.ini etc/neutron/plugins/ml2/ml2_conf_odl.ini etc/neutron/plugins/ml2/ml2_conf_ofa.ini + etc/neutron/plugins/ml2/ml2_conf_fslsdn.ini etc/neutron/plugins/mlnx = etc/neutron/plugins/mlnx/mlnx_conf.ini etc/neutron/plugins/nec = etc/neutron/plugins/nec/nec.ini etc/neutron/plugins/nuage = etc/neutron/plugins/nuage/nuage_plugin.ini @@ -163,6 +164,7 @@ neutron.ml2.mechanism_drivers = ofagent = neutron.plugins.ml2.drivers.mech_ofagent:OfagentMechanismDriver mlnx = neutron.plugins.ml2.drivers.mlnx.mech_mlnx:MlnxMechanismDriver brocade = neutron.plugins.ml2.drivers.brocade.mechanism_brocade:BrocadeMechanism + fslsdn = neutron.plugins.ml2.drivers.mechanism_fslsdn:FslsdnMechanismDriver neutron.openstack.common.cache.backends = memory = neutron.openstack.common.cache._backends.memory:MemoryBackend