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
This commit is contained in:
parent
1401b25294
commit
065ec89b91
160
vmware_nsx/db/nsx_portbindings_db.py
Normal file
160
vmware_nsx/db/nsx_portbindings_db.py
Normal file
@ -0,0 +1,160 @@
|
||||
# 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)
|
@ -870,6 +870,15 @@ def add_nsxv_port_ext_attributes(session, port_id,
|
||||
return binding
|
||||
|
||||
|
||||
def get_nsxv_ext_attr_port_vnic_type(session, port_id):
|
||||
try:
|
||||
binding = session.query(nsxv_models.NsxvPortExtAttributes).filter_by(
|
||||
port_id=port_id).one()
|
||||
return binding['vnic_type']
|
||||
except exc.NoResultFound:
|
||||
return pbin.VNIC_NORMAL
|
||||
|
||||
|
||||
def update_nsxv_port_ext_attributes(session, port_id,
|
||||
vnic_type=pbin.VNIC_NORMAL):
|
||||
try:
|
||||
|
@ -21,7 +21,6 @@ from neutron_lib.api.definitions import extra_dhcp_opt as ext_edo
|
||||
from neutron_lib.api.definitions import network as net_def
|
||||
from neutron_lib.api.definitions import port as port_def
|
||||
from neutron_lib.api.definitions import port_security as psec
|
||||
from neutron_lib.api.definitions import portbindings as pbin
|
||||
from neutron_lib.api.definitions import provider_net as pnet
|
||||
from neutron_lib.api.definitions import subnet as subnet_def
|
||||
from neutron_lib.api import validators
|
||||
@ -67,7 +66,6 @@ from neutron.db import l3_gwmode_db
|
||||
from neutron.db.models import l3 as l3_db_models
|
||||
from neutron.db.models import securitygroup as securitygroup_model # noqa
|
||||
from neutron.db import models_v2
|
||||
from neutron.db import portbindings_db
|
||||
from neutron.db import portsecurity_db
|
||||
from neutron.db import quota_db # noqa
|
||||
from neutron.db import securitygroups_db
|
||||
@ -98,7 +96,6 @@ from vmware_nsx.common import exceptions as nsx_exc
|
||||
from vmware_nsx.common import l3_rpc_agent_api
|
||||
from vmware_nsx.common import locking
|
||||
from vmware_nsx.common import managers as nsx_managers
|
||||
from vmware_nsx.common import nsx_constants
|
||||
from vmware_nsx.common import nsxv_constants
|
||||
from vmware_nsx.common import utils as c_utils
|
||||
from vmware_nsx.db import (
|
||||
@ -107,6 +104,7 @@ from vmware_nsx.db import (
|
||||
routertype as rt_rtr)
|
||||
from vmware_nsx.db import db as nsx_db
|
||||
from vmware_nsx.db import extended_security_group as extended_secgroup
|
||||
from vmware_nsx.db import nsx_portbindings_db as pbin_db
|
||||
from vmware_nsx.db import nsxv_db
|
||||
from vmware_nsx.db import vnic_index_db
|
||||
from vmware_nsx.extensions import (
|
||||
@ -157,7 +155,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
extradhcpopt_db.ExtraDhcpOptMixin,
|
||||
router_az_db.RouterAvailabilityZoneMixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin,
|
||||
portbindings_db.PortBindingMixin,
|
||||
pbin_db.NsxPortBindingMixin,
|
||||
portsecurity_db.PortSecurityDbMixin,
|
||||
extend_sg_rule.ExtendedSecurityGroupRuleMixin,
|
||||
securitygroups_db.SecurityGroupDbMixin,
|
||||
@ -1666,27 +1664,6 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
context, port['port'], created_port)
|
||||
return created_port
|
||||
|
||||
def _validate_port_direct_vnic_type(self, context, port_data):
|
||||
vnic_type = port_data.get(pbin.VNIC_TYPE)
|
||||
has_vnic_type = validators.is_attr_set(vnic_type)
|
||||
if has_vnic_type and vnic_type in [pbin.VNIC_DIRECT,
|
||||
pbin.VNIC_DIRECT_PHYSICAL]:
|
||||
if not self._validate_network_type(
|
||||
context, port_data['network_id'],
|
||||
[c_utils.NsxVNetworkTypes.VLAN,
|
||||
c_utils.NsxVNetworkTypes.FLAT,
|
||||
c_utils.NsxVNetworkTypes.PORTGROUP]):
|
||||
err_msg = _("'%s' vnic-type is only supported"
|
||||
"for networks of type 'vlan', 'flat' or "
|
||||
"'portgroup'.") % vnic_type
|
||||
raise n_exc.InvalidInput(error_message=err_msg)
|
||||
return vnic_type
|
||||
elif has_vnic_type and vnic_type != pbin.VNIC_NORMAL:
|
||||
err_msg = _("Invalid vnic-type %s."
|
||||
"Supported vnic-types are 'normal', 'direct' and "
|
||||
"'direct-physical'.") % vnic_type
|
||||
raise n_exc.InvalidInput(error_message=err_msg)
|
||||
|
||||
def _validate_extra_dhcp_options(self, opts):
|
||||
if not opts:
|
||||
return
|
||||
@ -1719,6 +1696,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
self._validate_extra_dhcp_options(dhcp_opts)
|
||||
self._validate_max_ips_per_port(port_data.get('fixed_ips', []),
|
||||
port_data.get('device_owner'))
|
||||
direct_vnic_type = self._validate_port_vnic_type(
|
||||
context, port_data, port_data['network_id'])
|
||||
|
||||
with db_api.context_manager.writer.using(context):
|
||||
# First we allocate port in neutron database
|
||||
@ -1726,9 +1705,6 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
self._extension_manager.process_create_port(
|
||||
context, port_data, neutron_db)
|
||||
|
||||
direct_vnic_type = self._validate_port_direct_vnic_type(context,
|
||||
port_data)
|
||||
|
||||
# Port port-security is decided based on port's vnic_type and ports
|
||||
# network port-security state (unless explicitly requested
|
||||
# differently by the user).
|
||||
@ -1757,6 +1733,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
self._process_port_port_security_create(
|
||||
context, port_data, neutron_db)
|
||||
|
||||
self._process_portbindings_create_and_update(
|
||||
context, port_data, neutron_db)
|
||||
|
||||
# Update fields obtained from neutron db (eg: MAC address)
|
||||
port["port"].update(neutron_db)
|
||||
has_ip = self._ip_on_port(neutron_db)
|
||||
@ -1786,15 +1765,6 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
port_data,
|
||||
ssgids)
|
||||
|
||||
if direct_vnic_type:
|
||||
nsxv_db.update_nsxv_port_ext_attributes(
|
||||
session=context.session,
|
||||
port_id=port_data['id'],
|
||||
vnic_type=direct_vnic_type)
|
||||
|
||||
self._process_portbindings_create_and_update(context,
|
||||
port['port'],
|
||||
port_data)
|
||||
neutron_db[addr_pair.ADDRESS_PAIRS] = (
|
||||
self._process_create_allowed_address_pairs(
|
||||
context, neutron_db,
|
||||
@ -2013,30 +1983,28 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
comp_owner_update = ('device_owner' in port_data and
|
||||
port_data['device_owner'].startswith('compute:'))
|
||||
|
||||
direct_vnic_type = self._validate_port_vnic_type(
|
||||
context, port_data, original_port['network_id'])
|
||||
if direct_vnic_type and has_port_security:
|
||||
err_msg = _("Security features are not supported for "
|
||||
"ports with direct/direct-physical VNIC type.")
|
||||
raise n_exc.InvalidInput(error_message=err_msg)
|
||||
|
||||
with db_api.context_manager.writer.using(context):
|
||||
ret_port = super(NsxVPluginV2, self).update_port(
|
||||
context, id, port)
|
||||
self._extension_manager.process_update_port(
|
||||
context, port_data, ret_port)
|
||||
|
||||
self._process_portbindings_create_and_update(
|
||||
context, port_data, ret_port)
|
||||
|
||||
# copy values over - except fixed_ips as
|
||||
# they've already been processed
|
||||
updates_fixed_ips = port['port'].pop('fixed_ips', [])
|
||||
ret_port.update(port['port'])
|
||||
has_ip = self._ip_on_port(ret_port)
|
||||
|
||||
direct_vnic_type = self._validate_port_direct_vnic_type(context,
|
||||
ret_port)
|
||||
if direct_vnic_type and has_port_security:
|
||||
err_msg = _("Security features are not supported for "
|
||||
"ports with direct/direct-physical VNIC type.")
|
||||
raise n_exc.InvalidInput(error_message=err_msg)
|
||||
|
||||
if direct_vnic_type:
|
||||
nsxv_db.update_nsxv_port_ext_attributes(
|
||||
session=context.session,
|
||||
port_id=ret_port['id'],
|
||||
vnic_type=direct_vnic_type)
|
||||
|
||||
# checks that if update adds/modify security groups,
|
||||
# then port has ip and port-security
|
||||
if not (has_ip and has_port_security):
|
||||
@ -2056,11 +2024,6 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
pvd_sg_changed = self._process_port_update_provider_security_group(
|
||||
context, port, original_port, ret_port)
|
||||
|
||||
LOG.debug("Updating port: %s", port)
|
||||
self._process_portbindings_create_and_update(context,
|
||||
port['port'],
|
||||
ret_port)
|
||||
|
||||
update_assigned_addresses = False
|
||||
if addr_pair.ADDRESS_PAIRS in attrs:
|
||||
update_assigned_addresses = self.update_address_pairs_on_port(
|
||||
@ -2293,20 +2256,6 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
|
||||
self._delete_dhcp_static_binding(context, neutron_db_port)
|
||||
|
||||
@staticmethod
|
||||
@resource_extend.extends([port_def.COLLECTION_NAME])
|
||||
def _extend_nsx_port_dict_binding(result, portdb):
|
||||
result[pbin.VIF_TYPE] = nsx_constants.VIF_TYPE_DVS
|
||||
port_attr = portdb.get('nsx_port_attributes')
|
||||
if port_attr:
|
||||
result[pbin.VNIC_TYPE] = port_attr.vnic_type
|
||||
else:
|
||||
result[pbin.VNIC_TYPE] = pbin.VNIC_NORMAL
|
||||
result[pbin.VIF_DETAILS] = {
|
||||
# TODO(rkukura): Replace with new VIF security details
|
||||
# security-groups extension supported by this plugin
|
||||
pbin.CAP_PORT_FILTER: True}
|
||||
|
||||
def base_delete_subnet(self, context, subnet_id):
|
||||
with locking.LockManager.get_lock('neutron-base-subnet'):
|
||||
super(NsxVPluginV2, self).delete_subnet(context, subnet_id)
|
||||
|
Loading…
Reference in New Issue
Block a user