diff --git a/etc/neutron/plugins/ml2/ml2_conf_cisco.ini b/etc/neutron/plugins/ml2/ml2_conf_cisco.ini index cb88d88bf3b..fa4b5b0d529 100644 --- a/etc/neutron/plugins/ml2/ml2_conf_cisco.ini +++ b/etc/neutron/plugins/ml2/ml2_conf_cisco.ini @@ -207,20 +207,6 @@ # cidr_exposed=10.10.40.2/16 # gateway_ip=10.10.40.1 -[ml2_cisco_ucsm] - -# Cisco UCS Manager IP address -# ucsm_ip=1.1.1.1 - -# Username to connect to UCS Manager -# ucsm_username=user - -# Password to connect to UCS Manager -# ucsm_password=password - -# 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] @@ -259,3 +245,22 @@ # n1kv_vsm_ips = 1.1.1.1,1.1.1.2 # username = user # password = password + +[ml2_cisco_ucsm] + +# Cisco UCS Manager IP address +# ucsm_ip=1.1.1.1 + +# Username to connect to UCS Manager +# ucsm_username=user + +# Password to connect to UCS Manager +# ucsm_password=password + +# SR-IOV and VM-FEX vendors supported by this plugin +# xxxx:yyyy represents vendor_id:product_id +# supported_pci_devs = ['2222:3333', '4444:5555'] + +# Hostname to Service profile mapping for UCS Manager +# controlled compute hosts +# ucsm_host_list=Hostname1:Serviceprofile1, Hostname2:Serviceprofile2 diff --git a/neutron/db/migration/alembic_migrations/versions/20b99fd19d4f_cisco_ucs_manager_mechanism_driver.py b/neutron/db/migration/alembic_migrations/versions/20b99fd19d4f_cisco_ucs_manager_mechanism_driver.py new file mode 100644 index 00000000000..5090ef761b3 --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/20b99fd19d4f_cisco_ucs_manager_mechanism_driver.py @@ -0,0 +1,40 @@ +# 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. +# + +"""Cisco UCS Manager Mechanism Driver + +Revision ID: 20b99fd19d4f +Revises: 589f9237ca0e +Create Date: 2014-07-30 21:01:13.754637 + +""" + +# revision identifiers, used by Alembic. +revision = '20b99fd19d4f' +down_revision = '589f9237ca0e' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.create_table( + 'ml2_ucsm_port_profiles', + sa.Column('vlan_id', sa.Integer(), nullable=False), + sa.Column('profile_id', sa.String(length=64), nullable=False), + sa.Column('created_on_ucs', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('vlan_id') + ) diff --git a/neutron/db/migration/alembic_migrations/versions/HEAD b/neutron/db/migration/alembic_migrations/versions/HEAD index d21c061a43b..0dd06323d96 100644 --- a/neutron/db/migration/alembic_migrations/versions/HEAD +++ b/neutron/db/migration/alembic_migrations/versions/HEAD @@ -1 +1 @@ -589f9237ca0e +20b99fd19d4f diff --git a/neutron/db/migration/models/head.py b/neutron/db/migration/models/head.py index 407c5b08902..56de64239a7 100644 --- a/neutron/db/migration/models/head.py +++ b/neutron/db/migration/models/head.py @@ -57,6 +57,7 @@ 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.cisco.ucsm import ucsm_model # noqa from neutron.plugins.ml2.drivers import type_flat # noqa from neutron.plugins.ml2.drivers import type_gre # noqa from neutron.plugins.ml2.drivers import type_vlan # noqa diff --git a/neutron/plugins/ml2/drivers/cisco/ucsm/__init__.py b/neutron/plugins/ml2/drivers/cisco/ucsm/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/neutron/plugins/ml2/drivers/cisco/ucsm/mech_cisco_ucsm.py b/neutron/plugins/ml2/drivers/cisco/ucsm/mech_cisco_ucsm.py new file mode 100644 index 00000000000..8f88966bd6d --- /dev/null +++ b/neutron/plugins/ml2/drivers/cisco/ucsm/mech_cisco_ucsm.py @@ -0,0 +1,218 @@ +# 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. + +from networking_cisco.plugins.ml2.drivers.cisco.ucsm import constants as const +from networking_cisco.plugins.ml2.drivers.cisco.ucsm import ucsm_db +from networking_cisco.plugins.ml2.drivers.cisco.ucsm import ucsm_network_driver + +from oslo_log import log as logging + +from neutron.common import constants +from neutron.extensions import portbindings +from neutron.i18n import _LE, _LW +from neutron.plugins.common import constants as p_const +from neutron.plugins.ml2 import driver_api as api + +LOG = logging.getLogger(__name__) + + +class CiscoUcsmMechanismDriver(api.MechanismDriver): + + """ML2 Mechanism Driver for Cisco UCS Manager.""" + + def initialize(self): + self.vif_type = portbindings.VIF_TYPE_802_QBH + self.vif_details = {portbindings.CAP_PORT_FILTER: False} + self.driver = ucsm_network_driver.CiscoUcsmDriver() + self.ucsm_db = ucsm_db.UcsmDbModel() + + def _get_vlanid(self, context): + """Returns vlan_id associated with a bound VLAN segment.""" + segment = context.bottom_bound_segment + if segment and self.check_segment(segment): + return segment.get(api.SEGMENTATION_ID) + + def update_port_precommit(self, context): + """Adds port profile and vlan information to the DB. + + Assign a port profile to this port. To do that: + 1. Get the vlan_id associated with the bound segment + 2. Check if a port profile already exists for this vlan_id + 3. If yes, associate that port profile with this port. + 4. If no, create a new port profile with this vlan_id and + associate with this port + """ + LOG.debug("Inside update_port_precommit") + vnic_type = context.current.get(portbindings.VNIC_TYPE, + portbindings.VNIC_NORMAL) + + profile = context.current.get(portbindings.PROFILE, {}) + + if not self.driver.check_vnic_type_and_vendor_info(vnic_type, + profile): + LOG.debug("update_port_precommit encountered a non-SR-IOV port") + return + + # If this is an Intel SR-IOV vnic, then no need to create port + # profile on the UCS manager. So no need to update the DB. + if not self.driver.is_vmfex_port(profile): + LOG.debug("update_port_precommit has nothing to do for this " + "sr-iov port") + return + + vlan_id = self._get_vlanid(context) + + if not vlan_id: + LOG.warn(_LW("update_port_precommit: vlan_id is None.")) + return + + p_profile_name = self.make_profile_name(vlan_id) + LOG.debug("update_port_precommit: Profile: %s, VLAN_id: %d", + p_profile_name, vlan_id) + + # Create a new port profile entry in the db + self.ucsm_db.add_port_profile(p_profile_name, vlan_id) + + def update_port_postcommit(self, context): + """Creates a port profile on UCS Manager. + + Creates a Port Profile for this VLAN if it does not already + exist. + """ + LOG.debug("Inside update_port_postcommit") + vlan_id = self._get_vlanid(context) + + if not vlan_id: + LOG.warn(_LW("update_port_postcommit: vlan_id is None.")) + return + + # Check if UCS Manager needs to create a Port Profile. + # 1. Make sure this is a vm_fex_port.(Port profiles are created + # only for VM-FEX ports.) + # 2. Make sure update_port_precommit added an entry in the DB + # for this port profile + # 3. Make sure that the Port Profile hasn't already been created. + + profile = context.current.get(portbindings.PROFILE, {}) + vnic_type = context.current.get(portbindings.VNIC_TYPE, + portbindings.VNIC_NORMAL) + + if (self.driver.check_vnic_type_and_vendor_info(vnic_type, profile) and + self.driver.is_vmfex_port(profile)): + + LOG.debug("update_port_postcommit: VM-FEX port updated for " + "vlan_id %d", vlan_id) + + profile_name = self.ucsm_db.get_port_profile_for_vlan(vlan_id) + if self.ucsm_db.is_port_profile_created(vlan_id): + LOG.debug("update_port_postcommit: Port Profile %s for " + "vlan_id %d already exists. Nothing to do.", + profile_name, vlan_id) + return + + # Ask the UCS Manager driver to create the above Port Profile. + # Connection to the UCS Manager is managed from within the driver. + if self.driver.create_portprofile(profile_name, vlan_id, + vnic_type): + # Port profile created on UCS, record that in the DB. + self.ucsm_db.set_port_profile_created(vlan_id, profile_name) + return + + else: + # Enable vlan-id for this regular Neutron virtual port. + host_id = context.current.get(portbindings.HOST_ID) + LOG.debug("update_port_postcommit: Host_id is %s", host_id) + self.driver.update_serviceprofile(host_id, vlan_id) + + def delete_network_precommit(self, context): + """Delete entry corresponding to Network's VLAN in the DB.""" + + segments = context.network_segments + vlan_id = segments[0]['segmentation_id'] + + if vlan_id: + self.ucsm_db.delete_vlan_entry(vlan_id) + + def delete_network_postcommit(self, context): + """Delete all configuration added to UCS Manager for the vlan_id.""" + + segments = context.network_segments + vlan_id = segments[0]['segmentation_id'] + port_profile = self.make_profile_name(vlan_id) + + if vlan_id: + self.driver.delete_all_config_for_vlan(vlan_id, port_profile) + + def bind_port(self, context): + """Binds port to current network segment. + + Binds port only if the vnic_type is direct or macvtap and + the port is from a supported vendor. While binding port set it + in ACTIVE state and provide the Port Profile or Vlan Id as part + vif_details. + """ + vnic_type = context.current.get(portbindings.VNIC_TYPE, + portbindings.VNIC_NORMAL) + + LOG.debug("Attempting to bind port %(port)s with vnic_type " + "%(vnic_type)s on network %(network)s", + {'port': context.current['id'], + 'vnic_type': vnic_type, + 'network': context.network.current['id']}) + + profile = context.current.get(portbindings.PROFILE, {}) + + if not self.driver.check_vnic_type_and_vendor_info(vnic_type, + profile): + return + + for segment in context.network.network_segments: + if self.check_segment(segment): + vlan_id = segment[api.SEGMENTATION_ID] + + if not vlan_id: + LOG.warn(_LW("Bind port: vlan_id is None.")) + return + + LOG.debug("Port binding to Vlan_id: %s", str(vlan_id)) + + # Check if this is a Cisco VM-FEX port or Intel SR_IOV port + if self.driver.is_vmfex_port(profile): + profile_name = self.make_profile_name(vlan_id) + self.vif_details[ + const.VIF_DETAILS_PROFILEID] = profile_name + else: + self.vif_details[ + portbindings.VIF_DETAILS_VLAN] = str(vlan_id) + + context.set_binding(segment[api.ID], + self.vif_type, + self.vif_details, + constants.PORT_STATUS_ACTIVE) + return + + LOG.error(_LE("UCS Mech Driver: Failed binding port ID %(id)s " + "on any segment of network %(network)s"), + {'id': context.current['id'], + 'network': context.network.current['id']}) + + @staticmethod + def check_segment(segment): + network_type = segment[api.NETWORK_TYPE] + return network_type == p_const.TYPE_VLAN + + @staticmethod + def make_profile_name(vlan_id): + return const.PORT_PROFILE_NAME_PREFIX + str(vlan_id) diff --git a/neutron/plugins/ml2/drivers/cisco/ucsm/ucsm_model.py b/neutron/plugins/ml2/drivers/cisco/ucsm/ucsm_model.py new file mode 100644 index 00000000000..05b164805e4 --- /dev/null +++ b/neutron/plugins/ml2/drivers/cisco/ucsm/ucsm_model.py @@ -0,0 +1,29 @@ +# 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 neutron.db import model_base + + +class PortProfile(model_base.BASEV2): + + """Port profiles created on the UCS Manager.""" + + __tablename__ = 'ml2_ucsm_port_profiles' + + vlan_id = sa.Column(sa.Integer(), nullable=False, primary_key=True) + profile_id = sa.Column(sa.String(64), nullable=False) + created_on_ucs = sa.Column(sa.Boolean(), nullable=False) diff --git a/setup.cfg b/setup.cfg index 93a0b3a8124..c5af580b5ef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -180,6 +180,7 @@ neutron.ml2.mechanism_drivers = 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 + cisco_ucsm = neutron.plugins.ml2.drivers.cisco.ucsm.mech_cisco_ucsm:CiscoUcsmMechanismDriver 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