armando-migliaccio e23910725d Encapsulate some port properties in the PortContext
Bindings to host or status may need further encapsulation
to avoid exposing mechanism drivers to underlying DB model
details. This was particularly true in the case of the
l2pop mech driver.

As a result, some docstrings were reworded, and the newly
introduced properties used directly in the mech drivers.

Partially-implements: blueprint neutron-ovs-dvr
Supports blueprint: ml2-hierarchical-port-binding

Change-Id: If2a373ef04d19b164585fb4bde4fe6e0cfaf43be
2014-07-24 16:40:59 -07:00

219 lines
9.1 KiB
Python

# Copyright 2013 OpenStack Foundation
# 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 Nexus platforms.
"""
from oslo.config import cfg
from neutron.common import constants as n_const
from neutron.extensions import portbindings
from neutron.openstack.common import log as logging
from neutron.plugins.common import constants as p_const
from neutron.plugins.ml2 import driver_api as api
from neutron.plugins.ml2.drivers.cisco.nexus import config as conf
from neutron.plugins.ml2.drivers.cisco.nexus import exceptions as excep
from neutron.plugins.ml2.drivers.cisco.nexus import nexus_db_v2 as nxos_db
from neutron.plugins.ml2.drivers.cisco.nexus import nexus_network_driver
LOG = logging.getLogger(__name__)
class CiscoNexusMechanismDriver(api.MechanismDriver):
"""Cisco Nexus ML2 Mechanism Driver."""
def initialize(self):
# Create ML2 device dictionary from ml2_conf.ini entries.
conf.ML2MechCiscoConfig()
# Extract configuration parameters from the configuration file.
self._nexus_switches = conf.ML2MechCiscoConfig.nexus_dict
LOG.debug(_("nexus_switches found = %s"), self._nexus_switches)
self.driver = nexus_network_driver.CiscoNexusDriver()
def _valid_network_segment(self, segment):
return (cfg.CONF.ml2_cisco.managed_physical_network is None or
cfg.CONF.ml2_cisco.managed_physical_network ==
segment[api.PHYSICAL_NETWORK])
def _get_vlanid(self, segment):
if (segment and segment[api.NETWORK_TYPE] == p_const.TYPE_VLAN and
self._valid_network_segment(segment)):
return segment.get(api.SEGMENTATION_ID)
def _is_deviceowner_compute(self, port):
return port['device_owner'].startswith('compute')
def _is_status_active(self, port):
return port['status'] == n_const.PORT_STATUS_ACTIVE
def _get_switch_info(self, host_id):
host_connections = []
for switch_ip, attr in self._nexus_switches:
if str(attr) == str(host_id):
port_id = self._nexus_switches[switch_ip, attr]
if ':' in port_id:
intf_type, port = port_id.split(':')
else:
intf_type, port = 'ethernet', port_id
host_connections.append((switch_ip, intf_type, port))
if host_connections:
return host_connections
else:
raise excep.NexusComputeHostNotConfigured(host=host_id)
def _configure_nxos_db(self, vlan_id, device_id, host_id):
"""Create the nexus database entry.
Called during update precommit port event.
"""
host_connections = self._get_switch_info(host_id)
for switch_ip, intf_type, nexus_port in host_connections:
port_id = '%s:%s' % (intf_type, nexus_port)
nxos_db.add_nexusport_binding(port_id, str(vlan_id), switch_ip,
device_id)
def _configure_switch_entry(self, vlan_id, device_id, host_id):
"""Create a nexus switch entry.
if needed, create a VLAN in the appropriate switch/port and
configure the appropriate interfaces for this VLAN.
Called during update postcommit port event.
"""
vlan_name = cfg.CONF.ml2_cisco.vlan_name_prefix + str(vlan_id)
host_connections = self._get_switch_info(host_id)
for switch_ip, intf_type, nexus_port in host_connections:
# Check to see if this is the first binding to use this vlan on the
# switch/port. Configure switch accordingly.
bindings = nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
if len(bindings) == 1:
LOG.debug(_("Nexus: create & trunk vlan %s"), vlan_name)
self.driver.create_and_trunk_vlan(
switch_ip, vlan_id, vlan_name, intf_type, nexus_port)
else:
LOG.debug(_("Nexus: trunk vlan %s"), vlan_name)
self.driver.enable_vlan_on_trunk_int(switch_ip, vlan_id,
intf_type, nexus_port)
def _delete_nxos_db(self, vlan_id, device_id, host_id):
"""Delete the nexus database entry.
Called during delete precommit port event.
"""
try:
rows = nxos_db.get_nexusvm_bindings(vlan_id, device_id)
for row in rows:
nxos_db.remove_nexusport_binding(
row.port_id, row.vlan_id, row.switch_ip, row.instance_id)
except excep.NexusPortBindingNotFound:
return
def _delete_switch_entry(self, vlan_id, device_id, host_id):
"""Delete the nexus switch entry.
By accessing the current db entries determine if switch
configuration can be removed.
Called during update postcommit port event.
"""
host_connections = self._get_switch_info(host_id)
for switch_ip, intf_type, nexus_port in host_connections:
# if there are no remaining db entries using this vlan on this
# nexus switch port then remove vlan from the switchport trunk.
port_id = '%s:%s' % (intf_type, nexus_port)
try:
nxos_db.get_port_vlan_switch_binding(port_id, vlan_id,
switch_ip)
except excep.NexusPortBindingNotFound:
self.driver.disable_vlan_on_trunk_int(switch_ip, vlan_id,
intf_type, nexus_port)
# if there are no remaining db entries using this vlan on this
# nexus switch then remove the vlan.
try:
nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
except excep.NexusPortBindingNotFound:
self.driver.delete_vlan(switch_ip, vlan_id)
def _is_vm_migration(self, context):
if not context.bound_segment and context.original_bound_segment:
return context.host != context.original_host
def _port_action(self, port, segment, func):
"""Verify configuration and then process event."""
device_id = port.get('device_id')
host_id = port.get(portbindings.HOST_ID)
vlan_id = self._get_vlanid(segment)
if vlan_id and device_id and host_id:
func(vlan_id, device_id, host_id)
else:
fields = "vlan_id " if not vlan_id else ""
fields += "device_id " if not device_id else ""
fields += "host_id" if not host_id else ""
raise excep.NexusMissingRequiredFields(fields=fields)
def update_port_precommit(self, context):
"""Update port pre-database transaction commit event."""
# if VM migration is occurring then remove previous database entry
# else process update event.
if self._is_vm_migration(context):
self._port_action(context.original,
context.original_bound_segment,
self._delete_nxos_db)
else:
if (self._is_deviceowner_compute(context.current) and
self._is_status_active(context.current)):
self._port_action(context.current,
context.bound_segment,
self._configure_nxos_db)
def update_port_postcommit(self, context):
"""Update port non-database commit event."""
# if VM migration is occurring then remove previous nexus switch entry
# else process update event.
if self._is_vm_migration(context):
self._port_action(context.original,
context.original_bound_segment,
self._delete_switch_entry)
else:
if (self._is_deviceowner_compute(context.current) and
self._is_status_active(context.current)):
self._port_action(context.current,
context.bound_segment,
self._configure_switch_entry)
def delete_port_precommit(self, context):
"""Delete port pre-database commit event."""
if self._is_deviceowner_compute(context.current):
self._port_action(context.current,
context.bound_segment,
self._delete_nxos_db)
def delete_port_postcommit(self, context):
"""Delete port non-database commit event."""
if self._is_deviceowner_compute(context.current):
self._port_action(context.current,
context.bound_segment,
self._delete_switch_entry)