diff --git a/etc/neutron/plugins/ml2/ml2_conf_cisco.ini b/etc/neutron/plugins/ml2/ml2_conf_cisco.ini index e7a09af1083..cb88d88bf3b 100644 --- a/etc/neutron/plugins/ml2/ml2_conf_cisco.ini +++ b/etc/neutron/plugins/ml2/ml2_conf_cisco.ini @@ -221,3 +221,41 @@ # SR-IOV and VM-FEX vendors supported by this plugin # xxxx:yyyy represents vendor_id:product_id # supported_pci_devs = ['2222:3333', '4444:5555'] + +[ml2_cisco_n1kv] + +# (StrOpt) Name of the policy profile to be associated with a port when no +# policy profile is specified during port creates. +# default_policy_profile = default-pp + +# (StrOpt) Name of the VLAN network profile to be associated with a network. +# default_vlan_network_profile = default-vlan-np + +# (StrOpt) Name of the VXLAN network profile to be associated with a network. +# default_vxlan_network_profile = default-vxlan-np + +# (IntOpt) Time in seconds for which the plugin polls the VSM for updates in +# policy profiles. +# poll_duration = 60 + +# (IntOpt) Timeout duration in seconds for the http request +# http_timeout = 15 + +# (BoolOpt) Specify whether tenants are restricted from accessing all the +# policy profiles. +# Default value: False, indicating all tenants can access all policy profiles. +# +# restrict_policy_profiles = False + +# Describe Cisco N1KV VSM connectivity +# In this section you can specify connectivity details in order for plugin +# to connect to N1KV Virtual Supervisor Module (VSM). +# +# n1kv_vsm_ips =,,.... +# username = +# password = +# +# An example would be: +# n1kv_vsm_ips = 1.1.1.1,1.1.1.2 +# username = user +# password = password diff --git a/neutron/db/migration/alembic_migrations/versions/589f9237ca0e_cisco_n1kv_ml2_driver_tables.py b/neutron/db/migration/alembic_migrations/versions/589f9237ca0e_cisco_n1kv_ml2_driver_tables.py new file mode 100644 index 00000000000..6c091ce5d27 --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/589f9237ca0e_cisco_n1kv_ml2_driver_tables.py @@ -0,0 +1,114 @@ +# Copyright 2015 Cisco Systems, 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. +# + +"""Cisco N1kv ML2 driver tables + +Revision ID: 589f9237ca0e +Revises: 51c54792158e +Create Date: 2014-08-13 13:31:43.537460 + +""" + +# revision identifiers, used by Alembic. +revision = '589f9237ca0e' +down_revision = '51c54792158e' + + +from alembic import op +import sqlalchemy as sa + +network_profile_type = sa.Enum('vlan', 'vxlan', + name='network_profile_type') +profile_type = sa.Enum('network', 'policy', name='profile_type') + + +def upgrade(): + + op.create_table( + 'cisco_ml2_n1kv_policy_profiles', + sa.Column('id', sa.String(length=36), nullable=False), + sa.Column('name', sa.String(length=255), nullable=False), + sa.Column('vsm_ip', sa.String(length=16), nullable=False), + sa.PrimaryKeyConstraint('id', 'vsm_ip'), + ) + + op.create_table( + 'cisco_ml2_n1kv_network_profiles', + sa.Column('id', sa.String(length=36), nullable=False), + sa.Column('name', sa.String(length=255), nullable=False), + sa.Column('segment_type', network_profile_type, nullable=False), + sa.Column('segment_range', sa.String(length=255), nullable=True), + sa.Column('multicast_ip_index', sa.Integer(), nullable=True), + sa.Column('multicast_ip_range', sa.String(length=255), nullable=True), + sa.Column('sub_type', sa.String(length=255), nullable=True), + sa.Column('physical_network', sa.String(length=255), nullable=True), + sa.PrimaryKeyConstraint('id'), + ) + + op.create_table( + 'cisco_ml2_n1kv_port_bindings', + sa.Column('port_id', sa.String(length=36), nullable=False), + sa.Column('profile_id', sa.String(length=36), nullable=False), + sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), + sa.PrimaryKeyConstraint('port_id'), + ) + + op.create_table( + 'cisco_ml2_n1kv_network_bindings', + sa.Column('network_id', sa.String(length=36), nullable=False), + sa.Column('network_type', sa.String(length=32), nullable=False), + sa.Column('segmentation_id', sa.Integer(), autoincrement=False), + sa.Column('profile_id', sa.String(length=36), nullable=False), + sa.ForeignKeyConstraint(['network_id'], ['networks.id'], + ondelete='CASCADE'), + sa.ForeignKeyConstraint(['profile_id'], + ['cisco_ml2_n1kv_network_profiles.id']), + sa.PrimaryKeyConstraint('network_id') + ) + + op.create_table( + 'cisco_ml2_n1kv_vxlan_allocations', + sa.Column('vxlan_id', sa.Integer(), autoincrement=False, + nullable=False), + sa.Column('allocated', sa.Boolean(), nullable=False), + sa.Column('network_profile_id', sa.String(length=36), nullable=False), + sa.ForeignKeyConstraint(['network_profile_id'], + ['cisco_ml2_n1kv_network_profiles.id'], + ondelete='CASCADE'), + sa.PrimaryKeyConstraint('vxlan_id') + ) + + op.create_table( + 'cisco_ml2_n1kv_vlan_allocations', + sa.Column('physical_network', sa.String(length=64), nullable=False), + sa.Column('vlan_id', sa.Integer(), autoincrement=False, + nullable=False), + sa.Column('allocated', sa.Boolean(), autoincrement=False, + nullable=False), + sa.Column('network_profile_id', sa.String(length=36), nullable=False), + sa.ForeignKeyConstraint(['network_profile_id'], + ['cisco_ml2_n1kv_network_profiles.id'], + ondelete='CASCADE'), + sa.PrimaryKeyConstraint('physical_network', 'vlan_id') + ) + + op.create_table( + 'cisco_ml2_n1kv_profile_bindings', + sa.Column('profile_type', profile_type, nullable=True), + sa.Column('tenant_id', sa.String(length=36), nullable=False, + server_default='tenant_id_not_set'), + sa.Column('profile_id', sa.String(length=36), nullable=False), + sa.PrimaryKeyConstraint('tenant_id', 'profile_id') + ) diff --git a/neutron/db/migration/alembic_migrations/versions/HEAD b/neutron/db/migration/alembic_migrations/versions/HEAD index a5f702c7f64..d21c061a43b 100644 --- a/neutron/db/migration/alembic_migrations/versions/HEAD +++ b/neutron/db/migration/alembic_migrations/versions/HEAD @@ -1 +1 @@ -51c54792158e +589f9237ca0e diff --git a/neutron/db/migration/models/head.py b/neutron/db/migration/models/head.py index c63304f0a6c..407c5b08902 100644 --- a/neutron/db/migration/models/head.py +++ b/neutron/db/migration/models/head.py @@ -54,6 +54,7 @@ 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.apic import apic_model # noqa +from neutron.plugins.ml2.drivers.cisco.n1kv import n1kv_models # noqa from neutron.plugins.ml2.drivers.cisco.nexus import ( # noqa nexus_models_v2 as ml2_nexus_models_v2) from neutron.plugins.ml2.drivers import type_flat # noqa diff --git a/neutron/plugins/ml2/drivers/cisco/n1kv/__init__.py b/neutron/plugins/ml2/drivers/cisco/n1kv/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/plugins/ml2/drivers/cisco/n1kv/extensions/__init__.py b/neutron/plugins/ml2/drivers/cisco/n1kv/extensions/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/plugins/ml2/drivers/cisco/n1kv/extensions/n1kv.py b/neutron/plugins/ml2/drivers/cisco/n1kv/extensions/n1kv.py new file mode 100644 index 00000000000..7cd4f8cbd83 --- /dev/null +++ b/neutron/plugins/ml2/drivers/cisco/n1kv/extensions/n1kv.py @@ -0,0 +1,57 @@ +# Copyright 2014 Cisco Systems, 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. + +from networking_cisco.plugins.ml2.drivers.cisco.n1kv import constants + +from neutron.api import extensions +from neutron.api.v2 import attributes + + +PROFILE = constants.N1KV_PROFILE +EXTENDED_ATTRIBUTES_2_0 = { + 'ports': {PROFILE: { + 'allow_post': True, + 'allow_put': False, + 'default': attributes.ATTR_NOT_SPECIFIED, + 'is_visible': True}}} + + +class N1kv(extensions.ExtensionDescriptor): + + @classmethod + def get_name(cls): + return "Cisco Nexus1000V Profile Extension" + + @classmethod + def get_alias(cls): + return "n1kv" + + @classmethod + def get_description(cls): + return _("Add new policy profile attribute to port resource.") + + @classmethod + def get_namespace(cls): + return "http://docs.openstack.org/ext/neutron/n1kv/api/v2.0" + + @classmethod + def get_updated(cls): + return "2014-11-23T13:33:25-00:00" + + def get_extended_resources(self, version): + if version == "2.0": + return EXTENDED_ATTRIBUTES_2_0 + else: + return {} diff --git a/neutron/plugins/ml2/drivers/cisco/n1kv/mech_cisco_n1kv.py b/neutron/plugins/ml2/drivers/cisco/n1kv/mech_cisco_n1kv.py new file mode 100644 index 00000000000..04e83a78a51 --- /dev/null +++ b/neutron/plugins/ml2/drivers/cisco/n1kv/mech_cisco_n1kv.py @@ -0,0 +1,24 @@ +# Copyright 2015 Cisco Systems, 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. + +""" +ML2 Mechanism Driver for Cisco Nexus1000V distributed virtual switches. +""" + +from networking_cisco.plugins.ml2.drivers.cisco.n1kv import mech_cisco_n1kv + + +class N1KVMechanismDriver(mech_cisco_n1kv.N1KVMechanismDriver): + pass diff --git a/neutron/plugins/ml2/drivers/cisco/n1kv/n1kv_ext_driver.py b/neutron/plugins/ml2/drivers/cisco/n1kv/n1kv_ext_driver.py new file mode 100644 index 00000000000..eb118c99862 --- /dev/null +++ b/neutron/plugins/ml2/drivers/cisco/n1kv/n1kv_ext_driver.py @@ -0,0 +1,101 @@ +# Copyright 2015 Cisco Systems, 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. + +"""Extensions Driver for Cisco Nexus1000V.""" + +from oslo_config import cfg +from oslo_log import log + +from networking_cisco.plugins.ml2.drivers.cisco.n1kv import ( + constants) +from networking_cisco.plugins.ml2.drivers.cisco.n1kv import ( + exceptions as n1kv_exc) +from networking_cisco.plugins.ml2.drivers.cisco.n1kv import ( + n1kv_db) + +from neutron.api import extensions as api_extensions +from neutron.api.v2 import attributes +from neutron.i18n import _LE +from neutron.openstack.common import uuidutils +from neutron.plugins.ml2.common import exceptions as ml2_exc +from neutron.plugins.ml2 import driver_api as api +from neutron.plugins.ml2.drivers.cisco.n1kv import extensions + +LOG = log.getLogger(__name__) + + +class CiscoN1kvExtensionDriver(api.ExtensionDriver): + """Cisco N1KV ML2 Extension Driver.""" + + # List of supported extensions for cisco Nexus1000V. + _supported_extension_alias = "n1kv" + + def initialize(self): + api_extensions.append_api_extensions_path(extensions.__path__) + + @property + def extension_alias(self): + """ + Supported extension alias. + + :returns: alias identifying the core API extension supported + by this driver + """ + return self._supported_extension_alias + + def process_create_port(self, context, data, result): + """Implementation of abstract method from ExtensionDriver class.""" + port_id = result.get('id') + policy_profile_attr = data.get(constants.N1KV_PROFILE) + if not attributes.is_attr_set(policy_profile_attr): + policy_profile_attr = (cfg.CONF.ml2_cisco_n1kv. + default_policy_profile) + with context.session.begin(subtransactions=True): + try: + n1kv_db.get_policy_binding(port_id, context.session) + except n1kv_exc.PortBindingNotFound: + if not uuidutils.is_uuid_like(policy_profile_attr): + policy_profile = n1kv_db.get_policy_profile_by_name( + policy_profile_attr, + context.session) + if policy_profile: + policy_profile_attr = policy_profile.id + else: + LOG.error(_LE("Policy Profile %(profile)s does " + "not exist."), + {"profile": policy_profile_attr}) + raise ml2_exc.MechanismDriverError() + elif not (n1kv_db.get_policy_profile_by_uuid( + context.session, + policy_profile_attr)): + LOG.error(_LE("Policy Profile %(profile)s does not " + "exist."), + {"profile": policy_profile_attr}) + raise ml2_exc.MechanismDriverError() + n1kv_db.add_policy_binding(port_id, + policy_profile_attr, + context.session) + result[constants.N1KV_PROFILE] = policy_profile_attr + + def extend_port_dict(self, session, model, result): + """Implementation of abstract method from ExtensionDriver class.""" + port_id = result.get('id') + with session.begin(subtransactions=True): + try: + res = n1kv_db.get_policy_binding(port_id, session) + result[constants.N1KV_PROFILE] = res.profile_id + except n1kv_exc.PortBindingNotFound: + # Do nothing if the port binding is not found. + pass diff --git a/neutron/plugins/ml2/drivers/cisco/n1kv/n1kv_models.py b/neutron/plugins/ml2/drivers/cisco/n1kv/n1kv_models.py new file mode 100644 index 00000000000..bfbbb51f41b --- /dev/null +++ b/neutron/plugins/ml2/drivers/cisco/n1kv/n1kv_models.py @@ -0,0 +1,138 @@ +# Copyright 2015 Cisco Systems, 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 sqlalchemy as sa +from sqlalchemy import orm + +from neutron.db import model_base +from neutron.db import models_v2 +from neutron.plugins.common import constants + + +class PolicyProfile(model_base.BASEV2): + + """ + Nexus1000V Policy Profiles + + Both 'profile_id' and 'name' are populated from Nexus1000V switch. + """ + __tablename__ = 'cisco_ml2_n1kv_policy_profiles' + + id = sa.Column(sa.String(36), nullable=False, primary_key=True) + name = sa.Column(sa.String(255), nullable=False) + vsm_ip = sa.Column(sa.String(16), nullable=False, primary_key=True) + + +class NetworkProfile(model_base.BASEV2, models_v2.HasId): + + """Nexus1000V Network Profiles created on the VSM.""" + __tablename__ = 'cisco_ml2_n1kv_network_profiles' + + name = sa.Column(sa.String(255), nullable=False) + segment_type = sa.Column(sa.Enum(constants.TYPE_VLAN, + constants.TYPE_VXLAN, + name='segment_type'), + nullable=False) + sub_type = sa.Column(sa.String(255)) + segment_range = sa.Column(sa.String(255)) + multicast_ip_index = sa.Column(sa.Integer, default=0) + multicast_ip_range = sa.Column(sa.String(255)) + physical_network = sa.Column(sa.String(255)) + + +class N1kvPortBinding(model_base.BASEV2): + + """Represents binding of ports to policy profile.""" + __tablename__ = 'cisco_ml2_n1kv_port_bindings' + + port_id = sa.Column(sa.String(36), + sa.ForeignKey('ports.id', ondelete="CASCADE"), + primary_key=True) + profile_id = sa.Column(sa.String(36), + nullable=False) + # Add a relationship to the Port model in order to instruct SQLAlchemy to + # eagerly load port bindings + port = orm.relationship( + models_v2.Port, + backref=orm.backref("n1kv_port_binding", + lazy='joined', uselist=False, + cascade='delete')) + + +class N1kvNetworkBinding(model_base.BASEV2): + + """Represents binding of virtual network to network profiles.""" + __tablename__ = 'cisco_ml2_n1kv_network_bindings' + + network_id = sa.Column(sa.String(36), + sa.ForeignKey('networks.id', ondelete="CASCADE"), + primary_key=True) + network_type = sa.Column(sa.String(32), nullable=False) + segmentation_id = sa.Column(sa.Integer) + profile_id = sa.Column(sa.String(36), + sa.ForeignKey('cisco_ml2_n1kv_network_profiles.id'), + nullable=False) + + +class N1kvVlanAllocation(model_base.BASEV2): + + """Represents allocation state of vlan_id on physical network.""" + __tablename__ = 'cisco_ml2_n1kv_vlan_allocations' + + physical_network = sa.Column(sa.String(64), + nullable=False, + primary_key=True) + vlan_id = sa.Column(sa.Integer, nullable=False, primary_key=True, + autoincrement=False) + allocated = sa.Column(sa.Boolean, nullable=False, default=False) + network_profile_id = sa.Column(sa.String(36), + sa.ForeignKey( + 'cisco_ml2_n1kv_network_profiles.id', + ondelete="CASCADE"), + nullable=False) + + +class N1kvVxlanAllocation(model_base.BASEV2): + + """Represents allocation state of vxlan_id.""" + __tablename__ = 'cisco_ml2_n1kv_vxlan_allocations' + + vxlan_id = sa.Column(sa.Integer, nullable=False, primary_key=True, + autoincrement=False) + allocated = sa.Column(sa.Boolean, nullable=False, default=False) + network_profile_id = sa.Column(sa.String(36), + sa.ForeignKey( + 'cisco_ml2_n1kv_network_profiles.id', + ondelete="CASCADE"), + nullable=False) + + +class ProfileBinding(model_base.BASEV2): + + """ + Represents a binding of Network Profile + or Policy Profile to tenant_id + """ + __tablename__ = 'cisco_ml2_n1kv_profile_bindings' + + profile_type = sa.Column(sa.Enum('network', 'policy', + name='profile_type'), + nullable=True) + tenant_id = sa.Column(sa.String(36), + primary_key=True, + nullable=False, + default='tenant_id_not_set', + server_default='tenant_id_not_set') + profile_id = sa.Column(sa.String(36), primary_key=True, nullable=False) diff --git a/neutron/plugins/ml2/drivers/cisco/requirements.txt b/neutron/plugins/ml2/drivers/cisco/requirements.txt new file mode 100644 index 00000000000..ef631a3f2b9 --- /dev/null +++ b/neutron/plugins/ml2/drivers/cisco/requirements.txt @@ -0,0 +1 @@ +networking-cisco diff --git a/setup.cfg b/setup.cfg index b3d8824c824..93a0b3a8124 100644 --- a/setup.cfg +++ b/setup.cfg @@ -179,6 +179,7 @@ neutron.ml2.mechanism_drivers = cisco_ncs = neutron.plugins.ml2.drivers.cisco.ncs.driver:NCSMechanismDriver cisco_nexus = neutron.plugins.ml2.drivers.cisco.nexus.mech_cisco_nexus:CiscoNexusMechanismDriver cisco_apic = neutron.plugins.ml2.drivers.cisco.apic.mechanism_apic:APICMechanismDriver + cisco_n1kv = neutron.plugins.ml2.drivers.cisco.n1kv.mech_cisco_n1kv:N1KVMechanismDriver l2population = neutron.plugins.ml2.drivers.l2pop.mech_driver:L2populationMechanismDriver bigswitch = neutron.plugins.ml2.drivers.mech_bigswitch.driver:BigSwitchMechanismDriver ofagent = neutron.plugins.ml2.drivers.ofagent.driver:OfagentMechanismDriver @@ -194,6 +195,7 @@ neutron.ml2.extension_drivers = test = neutron.tests.unit.ml2.drivers.ext_test:TestExtensionDriver testdb = neutron.tests.unit.ml2.drivers.ext_test:TestDBExtensionDriver port_security = neutron.plugins.ml2.extensions.port_security:PortSecurityExtensionDriver + cisco_n1kv_ext = neutron.plugins.ml2.drivers.cisco.n1kv.n1kv_ext_driver:CiscoN1kvExtensionDriver neutron.openstack.common.cache.backends = memory = neutron.openstack.common.cache._backends.memory:MemoryBackend # These are for backwards compat with Icehouse notification_driver configuration values