vmware-nsx/vmware_nsx/db/nsx_portbindings_db.py
Roey Chen 065ec89b91 NSXv port-binding support
The current implementation doesn't correctly process some port-binding
attributes such as 'portbinding:profile' and 'portbinding:vif_details'.
This patch add the required support to process and persist the missing
port-binding information.
The new fields are modified and queried by nova, and will allow us to support
for SR-IOV passthrough networking.

In order to avoid DB migrations, this implementation will utilize the
existing 'ml2_port_bindings' table to hold the extra port binding information,
current tables that contains partial information (e.g - 'portbindingports'
for port's 'binding:host_id') will be kept and maintained by the plugin to
preserve backward compatibility.

Change-Id: I779b577737565860a53461114c9822d7b3908cb3
2017-09-05 07:44:33 -07:00

161 lines
6.8 KiB
Python

# Copyright 2017 VMware, 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 oslo_log import log as logging
from oslo_serialization import jsonutils
from neutron_lib.api.definitions import port as port_def
from neutron_lib.api.definitions import portbindings as pbin
from neutron_lib.api.definitions import provider_net as pnet
from neutron_lib.api import validators
from neutron_lib import constants
from neutron_lib import exceptions
from neutron_lib.plugins import directory
from neutron.db import _resource_extend as resource_extend
from neutron.db import api as db_api
from neutron.db import portbindings_db as pbin_db
from neutron.plugins.ml2 import models as pbin_model
from vmware_nsx._i18n import _
from vmware_nsx.common import nsx_constants
from vmware_nsx.common import utils as c_utils
from vmware_nsx.db import nsxv_db
LOG = logging.getLogger(__name__)
FLAT_VLAN = 0
SUPPORTED_VNIC_TYPES = (pbin.VNIC_NORMAL,
pbin.VNIC_DIRECT,
pbin.VNIC_DIRECT_PHYSICAL)
VNIC_TYPES_DIRECT_PASSTHROUGH = (pbin.VNIC_DIRECT, pbin.VNIC_DIRECT_PHYSICAL)
@resource_extend.has_resource_extenders
class NsxPortBindingMixin(pbin_db.PortBindingMixin):
def _validate_port_vnic_type(self, context, port_data, network_id):
vnic_type = port_data.get(pbin.VNIC_TYPE)
if vnic_type and vnic_type not in SUPPORTED_VNIC_TYPES:
err_msg = _("Invalid port vnic-type '%(vnic_type)s'."
"Supported vnic-types are %(valid_types)s."
) % {'vnic_type': vnic_type,
'valid_types': SUPPORTED_VNIC_TYPES}
raise exceptions.InvalidInput(error_message=err_msg)
direct_vnic_type = vnic_type in VNIC_TYPES_DIRECT_PASSTHROUGH
if direct_vnic_type:
self._validate_vnic_type_direct_passthrough_for_network(
context, network_id)
return direct_vnic_type
def _validate_vnic_type_direct_passthrough_for_network(self,
context,
network_id):
supported_network_types = (c_utils.NsxVNetworkTypes.VLAN,
c_utils.NsxVNetworkTypes.FLAT,
c_utils.NsxVNetworkTypes.PORTGROUP)
if not self._validate_network_type(context, network_id,
supported_network_types):
msg_info = {
'vnic_types': VNIC_TYPES_DIRECT_PASSTHROUGH,
'networks': supported_network_types}
err_msg = _("%(vnic_types)s port vnic-types are only supported "
"for ports on networks of types "
"%(networks)s.") % msg_info
raise exceptions.InvalidInput(error_message=err_msg)
def _process_portbindings_create_and_update(self, context, port, port_res):
super(NsxPortBindingMixin,
self)._process_portbindings_create_and_update(
context, port, port_res)
port_id = port_res['id']
org_vnic_type = nsxv_db.get_nsxv_ext_attr_port_vnic_type(
context.session, port_id)
vnic_type = port.get(pbin.VNIC_TYPE, org_vnic_type)
cap_port_filter = (port.get(pbin.VNIC_TYPE, org_vnic_type)
== pbin.VNIC_NORMAL)
vif_details = {pbin.CAP_PORT_FILTER: cap_port_filter}
network = self.get_network(context, port_res['network_id'])
if network.get(pnet.NETWORK_TYPE) == c_utils.NsxVNetworkTypes.FLAT:
vif_details[pbin.VIF_DETAILS_VLAN] = FLAT_VLAN
elif network.get(pnet.NETWORK_TYPE) == c_utils.NsxVNetworkTypes.VLAN:
vif_details[pbin.VIF_DETAILS_VLAN] = network[pnet.SEGMENTATION_ID]
with db_api.context_manager.writer.using(context):
port_binding = context.session.query(
pbin_model.PortBinding).filter_by(port_id=port_id).first()
if not port_binding:
port_binding = pbin_model.PortBinding(
port_id=port_id,
vif_type=nsx_constants.VIF_TYPE_DVS)
context.session.add(port_binding)
port_binding.host = port_res[pbin.HOST_ID] or ''
port_binding.vnic_type = vnic_type
port_binding.vif_details = jsonutils.dumps(vif_details)
nsxv_db.update_nsxv_port_ext_attributes(
context.session, port_id, vnic_type)
profile = port.get(pbin.PROFILE, constants.ATTR_NOT_SPECIFIED)
if validators.is_attr_set(profile) or profile is None:
port_binding.profile = (jsonutils.dumps(profile)
if profile else "")
port_res[pbin.VNIC_TYPE] = vnic_type
self.extend_port_portbinding(port_res, port_binding)
def extend_port_portbinding(self, port_res, binding):
port_res[pbin.PROFILE] = self._get_profile(binding)
port_res[pbin.VIF_TYPE] = binding.vif_type
port_res[pbin.VIF_DETAILS] = self._get_vif_details(binding)
def _get_vif_details(self, binding):
if binding.vif_details:
try:
return jsonutils.loads(binding.vif_details)
except Exception:
LOG.error("Serialized vif_details DB value '%(value)s' "
"for port %(port)s is invalid",
{'value': binding.vif_details,
'port': binding.port_id})
return {}
def _get_profile(self, binding):
if binding.profile:
try:
return jsonutils.loads(binding.profile)
except Exception:
LOG.error("Serialized profile DB value '%(value)s' for "
"port %(port)s is invalid",
{'value': binding.profile,
'port': binding.port_id})
return {}
@staticmethod
@resource_extend.extends([port_def.COLLECTION_NAME])
def _extend_port_portbinding(port_res, port_db):
plugin = directory.get_plugin()
plugin.extend_port_dict_binding(port_res, port_db)
if port_db.nsx_port_attributes:
port_res[pbin.VNIC_TYPE] = port_db.nsx_port_attributes.vnic_type
if port_db.port_binding:
plugin.extend_port_portbinding(port_res, port_db.port_binding)