NSX|P provider networks support

Supporting provider networks including:
- Creating a base plugin for T & P plugins (separate from V)
- Moving some nsx-T code to the common plugin after stripping
  it from v3 specific code
- Reusing the nsx-T DB table for keeping the provider network
  configurations
- Support for vlan transparent
- Support for update network action

Also adding unit tests for provider networks and enabing tempest
tests for networks

Change-Id: I505ef65f2ee6bfba78dadff62c4c06463ebffff0
This commit is contained in:
Adit Sarfaty 2018-10-29 15:36:29 +02:00
parent e3e0dbca96
commit 00134a8e31
8 changed files with 1186 additions and 622 deletions

View File

@ -21,5 +21,5 @@
# Begin list of exclusions.
#r="^(?!.*)"
r="$r(tempest\.api\.network\.test_security_groups).*$"
r="$r(tempest\.api\.network\.test_security_groups|tempest\.api\.network\.test_networks|tempest\.api\.network\.test_networks_negative).*$"
export DEVSTACK_GATE_TEMPEST_REGEX="$r"

View File

@ -344,6 +344,12 @@ nsx_v3_and_p = [
default=False,
help=_("(Optional) Indicates whether distributed-firewall "
"security-groups rules are logged.")),
cfg.ListOpt('network_vlan_ranges',
default=[],
help=_("List of <TZ UUID>:<vlan_min>:<vlan_max> "
"specifying Transport Zone UUID usable for VLAN "
"provider networks, as well as ranges of VLAN "
"tags on each available for allocation to networks.")),
]
nsx_v3_opts = nsx_v3_and_p + [
@ -456,12 +462,6 @@ nsx_v3_opts = nsx_v3_and_p + [
default=False,
help=_("(Optional) Indicates whether ENS transport zones can "
"be used")),
cfg.ListOpt('network_vlan_ranges',
default=[],
help=_("List of <TZ UUID>:<vlan_min>:<vlan_max> "
"specifying Transport Zone UUID usable for VLAN "
"provider networks, as well as ranges of VLAN "
"tags on each available for allocation to networks.")),
cfg.BoolOpt('disable_port_security_for_ens',
default=False,
help=_("When True, port security will be set to False for "
@ -489,6 +489,22 @@ nsx_p_opts = nsx_v3_and_p + [
"configuring external networks. If only one tier0 "
" router is present on backend, it will be assumed "
"as default unless this value is provided")),
cfg.StrOpt('default_overlay_tz',
help=_("This is the name or UUID of the default NSX overlay "
"transport zone that will be used for creating "
"tunneled isolated Neutron networks. It needs to be "
"created in NSX before starting Neutron with the NSX "
"plugin. If only one overlay transport zone is present "
"on backend, it will be assumed as default unless this "
"value is provided")),
cfg.StrOpt('default_vlan_tz',
help=_("(Optional) Only required when creating VLAN or flat "
"provider networks. Name or UUID of default NSX VLAN "
"transport zone that will be used for bridging between "
"Neutron networks, if no physical network has been "
"specified. If only one VLAN transport zone is present "
"on backend, it will be assumed as default unless this "
"value is provided")),
]

View File

@ -63,7 +63,7 @@ class NsxVNetworkTypes(object):
PORTGROUP = 'portgroup'
# Allowed network types for the NSXv3 Plugin
# Allowed network types for the NSXv3 and NSX-Policy Plugin
class NsxV3NetworkTypes(object):
"""Allowed provider network types for the NSXv3 Plugin."""
FLAT = 'flat'

View File

@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from neutron.db import _resource_extend as resource_extend
@ -24,10 +23,8 @@ from neutron.db import l3_db
from neutron.db import models_v2
from neutron_lib.api.definitions import address_scope as ext_address_scope
from neutron_lib.api.definitions import availability_zone as az_def
from neutron_lib.api.definitions import external_net as extnet_apidef
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 subnet as subnet_def
from neutron_lib.api import validators
from neutron_lib.api.validators import availability_zone as az_validator
@ -39,15 +36,11 @@ from neutron_lib import context as n_context
from neutron_lib.db import api as db_api
from neutron_lib import exceptions as n_exc
from neutron_lib.plugins import directory
from neutron_lib.services.qos import constants as qos_consts
from neutron_lib.utils import net
from neutron_lib.utils import net as nl_net_utils
from vmware_nsx._i18n import _
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.common import utils
from vmware_nsx.extensions import maclearning as mac_ext
from vmware_nsx.services.qos.common import utils as qos_com_utils
from vmware_nsx.services.vpnaas.nsxv3 import ipsec_utils
LOG = logging.getLogger(__name__)
@ -55,7 +48,7 @@ LOG = logging.getLogger(__name__)
@resource_extend.has_resource_extenders
class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
address_scope_db.AddressScopeDbMixin):
"""Common methods for NSX-V and NSX-V3 plugins"""
"""Common methods for NSX-V, NSX-V3 and NSX-P plugins"""
@property
def plugin_type(self):
@ -308,7 +301,7 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
cannot add multiple static dhcp bindings with the same port
"""
if (device_owner and
net.is_port_trusted({'device_owner': device_owner})):
nl_net_utils.is_port_trusted({'device_owner': device_owner})):
return
if validators.is_attr_set(fixed_ip_list) and len(fixed_ip_list) > 1:
@ -392,203 +385,15 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
if qos_policy_id:
qos_com_utils.validate_policy_accessable(context, qos_policy_id)
def _validate_create_network(self, context, net_data):
"""Validate the parameters of the new network to be created
This method includes general validations that does not depend on
provider attributes, or plugin specific configurations
"""
external = net_data.get(extnet_apidef.EXTERNAL)
is_external_net = validators.is_attr_set(external) and external
with_qos = validators.is_attr_set(
net_data.get(qos_consts.QOS_POLICY_ID))
if with_qos:
self._validate_qos_policy_id(
context, net_data.get(qos_consts.QOS_POLICY_ID))
if is_external_net:
raise nsx_exc.QoSOnExternalNet()
def _validate_update_netowrk(self, context, id, original_net, net_data):
"""Validate the updated parameters of a network
This method includes general validations that does not depend on
provider attributes, or plugin specific configurations
"""
extern_net = self._network_is_external(context, id)
with_qos = validators.is_attr_set(
net_data.get(qos_consts.QOS_POLICY_ID))
# Do not allow QoS on external networks
if with_qos and extern_net:
raise nsx_exc.QoSOnExternalNet()
# Do not support changing external/non-external networks
if (extnet_apidef.EXTERNAL in net_data and
net_data[extnet_apidef.EXTERNAL] != extern_net):
err_msg = _("Cannot change the router:external flag of a network")
raise n_exc.InvalidInput(error_message=err_msg)
def _assert_on_illegal_port_with_qos(self, device_owner):
# Prevent creating/update port with QoS policy
# on router-interface/network-dhcp ports.
if ((device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF or
device_owner == constants.DEVICE_OWNER_DHCP)):
err_msg = _("Unable to create or update %s port with a QoS "
"policy") % device_owner
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
def _assert_on_external_net_with_compute(self, port_data):
# Prevent creating port with device owner prefix 'compute'
# on external networks.
device_owner = port_data.get('device_owner')
if (device_owner is not None and
device_owner.startswith(constants.DEVICE_OWNER_COMPUTE_PREFIX)):
err_msg = _("Unable to update/create a port with an external "
"network")
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
def _validate_create_port(self, context, port_data):
self._validate_max_ips_per_port(port_data.get('fixed_ips', []),
port_data.get('device_owner'))
is_external_net = self._network_is_external(
context, port_data['network_id'])
qos_selected = validators.is_attr_set(port_data.get(
qos_consts.QOS_POLICY_ID))
device_owner = port_data.get('device_owner')
# QoS validations
if qos_selected:
self._validate_qos_policy_id(
context, port_data.get(qos_consts.QOS_POLICY_ID))
self._assert_on_illegal_port_with_qos(device_owner)
if is_external_net:
raise nsx_exc.QoSOnExternalNet()
# External network validations:
if is_external_net:
self._assert_on_external_net_with_compute(port_data)
self._assert_on_port_admin_state(port_data, device_owner)
def _assert_on_vpn_port_change(self, port_data):
if port_data['device_owner'] == ipsec_utils.VPN_PORT_OWNER:
msg = _('Can not update/delete VPNaaS port %s') % port_data['id']
raise n_exc.InvalidInput(error_message=msg)
def _assert_on_lb_port_fixed_ip_change(self, port_data, orig_dev_own):
if orig_dev_own == constants.DEVICE_OWNER_LOADBALANCERV2:
if "fixed_ips" in port_data and port_data["fixed_ips"]:
msg = _('Can not update Loadbalancer port with fixed IP')
raise n_exc.InvalidInput(error_message=msg)
def _assert_on_device_owner_change(self, port_data, orig_dev_own):
"""Prevent illegal device owner modifications
"""
if orig_dev_own == constants.DEVICE_OWNER_LOADBALANCERV2:
if ("allowed_address_pairs" in port_data and
port_data["allowed_address_pairs"]):
msg = _('Loadbalancer port can not be updated '
'with address pairs')
raise n_exc.InvalidInput(error_message=msg)
if 'device_owner' not in port_data:
return
new_dev_own = port_data['device_owner']
if new_dev_own == orig_dev_own:
return
err_msg = (_("Changing port device owner '%(orig)s' to '%(new)s' is "
"not allowed") % {'orig': orig_dev_own,
'new': new_dev_own})
# Do not allow changing nova <-> neutron device owners
if ((orig_dev_own.startswith(constants.DEVICE_OWNER_COMPUTE_PREFIX) and
new_dev_own.startswith(constants.DEVICE_OWNER_NETWORK_PREFIX)) or
(orig_dev_own.startswith(constants.DEVICE_OWNER_NETWORK_PREFIX) and
new_dev_own.startswith(constants.DEVICE_OWNER_COMPUTE_PREFIX))):
raise n_exc.InvalidInput(error_message=err_msg)
# Do not allow removing the device owner in some cases
if orig_dev_own == constants.DEVICE_OWNER_DHCP:
raise n_exc.InvalidInput(error_message=err_msg)
def _assert_on_port_sec_change(self, port_data, device_owner):
"""Do not allow enabling port security/mac learning of some ports
Trusted ports are created with port security and mac learning disabled
in neutron, and it should not change.
"""
if net.is_port_trusted({'device_owner': device_owner}):
if port_data.get(psec.PORTSECURITY) is True:
err_msg = _("port_security_enabled=True is not supported for "
"trusted ports")
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
mac_learning = port_data.get(mac_ext.MAC_LEARNING)
if (validators.is_attr_set(mac_learning) and mac_learning is True):
err_msg = _("mac_learning_enabled=True is not supported for "
"trusted ports")
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
def _validate_update_port(self, context, id, original_port, port_data):
qos_selected = validators.is_attr_set(port_data.get
(qos_consts.QOS_POLICY_ID))
is_external_net = self._network_is_external(
context, original_port['network_id'])
device_owner = (port_data['device_owner']
if 'device_owner' in port_data
else original_port.get('device_owner'))
# QoS validations
if qos_selected:
self._validate_qos_policy_id(
context, port_data.get(qos_consts.QOS_POLICY_ID))
if is_external_net:
raise nsx_exc.QoSOnExternalNet()
self._assert_on_illegal_port_with_qos(device_owner)
# External networks validations:
if is_external_net:
self._assert_on_external_net_with_compute(port_data)
# Device owner validations:
orig_dev_owner = original_port.get('device_owner')
self._assert_on_device_owner_change(port_data, orig_dev_owner)
self._assert_on_port_admin_state(port_data, device_owner)
self._assert_on_port_sec_change(port_data, device_owner)
self._validate_max_ips_per_port(
port_data.get('fixed_ips', []), device_owner)
self._assert_on_vpn_port_change(original_port)
self._assert_on_lb_port_fixed_ip_change(port_data, orig_dev_owner)
def _get_dhcp_port_name(self, net_name, net_id):
return utils.get_name_and_uuid('%s-%s' % ('dhcp',
net_name or 'network'),
net_id)
def _build_port_name(self, context, port_data):
device_owner = port_data.get('device_owner')
device_id = port_data.get('device_id')
if device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF and device_id:
router = self._get_router(context, device_id)
name = utils.get_name_and_uuid(
router['name'] or 'router', port_data['id'], tag='port')
elif device_owner == constants.DEVICE_OWNER_DHCP:
network = self.get_network(context, port_data['network_id'])
name = self._get_dhcp_port_name(network['name'],
network['id'])
elif device_owner.startswith(constants.DEVICE_OWNER_COMPUTE_PREFIX):
name = utils.get_name_and_uuid(
port_data['name'] or 'instance-port', port_data['id'])
else:
name = port_data['name']
return name
def _get_interface_network(self, context, interface_info):
is_port, is_sub = self._validate_interface_info(interface_info)
if is_port:
net_id = self.get_port(context,
interface_info['port_id'])['network_id']
elif is_sub:
net_id = self.get_subnet(context,
interface_info['subnet_id'])['network_id']
return net_id
def _process_extra_attr_router_create(self, context, router_db, r):
for extra_attr in l3_attrs_db.get_attr_info().keys():
@ -597,12 +402,6 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
self.set_extra_attr_value(context, router_db,
extra_attr, r[extra_attr])
def _validate_ipv4_address_pairs(self, address_pairs):
for pair in address_pairs:
ip = pair.get('ip_address')
if not utils.is_ipv4_ip_address(ip):
raise nsx_exc.InvalidIPAddress(ip_address=ip)
def get_housekeeper(self, context, name, fields=None):
# run the job in readonly mode and get the results
self.housekeeper.run(context, name, readonly=True)

View File

@ -14,28 +14,57 @@
# under the License.
from oslo_config import cfg
from oslo_log import log as logging
from six import moves
from neutron.db import l3_db
from neutron.extensions import securitygroup as ext_sg
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
from neutron_lib.api.definitions import external_net as extnet_apidef
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 import validators
from neutron_lib import constants
from neutron_lib.db import api as db_api
from neutron_lib.db import utils as db_utils
from neutron_lib import exceptions as n_exc
from neutron_lib.exceptions import allowedaddresspairs as addr_exc
from neutron_lib.exceptions import port_security as psec_exc
from neutron_lib.plugins import utils as plugin_utils
from neutron_lib.services.qos import constants as qos_consts
from neutron_lib.utils import net as nl_net_utils
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.common import nsx_constants
from vmware_nsx.common import utils
from vmware_nsx.db import db as nsx_db
from vmware_nsx.db import extended_security_group as extended_sec
from vmware_nsx.db import nsx_portbindings_db as pbin_db
from vmware_nsx.extensions import maclearning as mac_ext
from vmware_nsx.extensions import providersecuritygroup as provider_sg
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as sg_prefix
from vmware_nsx.plugins.common import plugin
from vmware_nsx.services.qos.common import utils as qos_com_utils
from vmware_nsx.services.vpnaas.nsxv3 import ipsec_utils
from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
from vmware_nsxlib.v3 import nsx_constants as nsxlib_consts
LOG = logging.getLogger(__name__)
class NsxPluginV3Base(plugin.NsxPluginBase,
extended_sec.ExtendedSecurityGroupPropertiesMixin):
"""Common methods for NSX-V3 plugins"""
extended_sec.ExtendedSecurityGroupPropertiesMixin,
pbin_db.NsxPortBindingMixin):
"""Common methods for NSX-V3 plugins (NSX-V3 & Policy)"""
def __init__(self):
super(NsxPluginV3Base, self).__init__()
plugin_cfg = getattr(cfg.CONF, self.cfg_group)
self._network_vlans = plugin_utils.parse_network_vlan_ranges(
plugin_cfg.network_vlan_ranges)
def _get_interface_network(self, context, interface_info):
is_port, is_sub = self._validate_interface_info(interface_info)
@ -127,3 +156,525 @@ class NsxPluginV3Base(plugin.NsxPluginBase,
port_data[ext_sg.SECURITYGROUPS] = (
self._get_security_groups_on_port(context, port))
return port_security, has_ip, sgids, psgids
def _validate_create_network(self, context, net_data):
"""Validate the parameters of the new network to be created
This method includes general validations that does not depend on
provider attributes, or plugin specific configurations
"""
external = net_data.get(extnet_apidef.EXTERNAL)
is_external_net = validators.is_attr_set(external) and external
with_qos = validators.is_attr_set(
net_data.get(qos_consts.QOS_POLICY_ID))
if with_qos:
self._validate_qos_policy_id(
context, net_data.get(qos_consts.QOS_POLICY_ID))
if is_external_net:
raise nsx_exc.QoSOnExternalNet()
def _validate_update_network(self, context, id, original_net, net_data):
"""Validate the updated parameters of a network
This method includes general validations that does not depend on
provider attributes, or plugin specific configurations
"""
extern_net = self._network_is_external(context, id)
with_qos = validators.is_attr_set(
net_data.get(qos_consts.QOS_POLICY_ID))
# Do not allow QoS on external networks
if with_qos and extern_net:
raise nsx_exc.QoSOnExternalNet()
# Do not support changing external/non-external networks
if (extnet_apidef.EXTERNAL in net_data and
net_data[extnet_apidef.EXTERNAL] != extern_net):
err_msg = _("Cannot change the router:external flag of a network")
raise n_exc.InvalidInput(error_message=err_msg)
def _assert_on_illegal_port_with_qos(self, device_owner):
# Prevent creating/update port with QoS policy
# on router-interface/network-dhcp ports.
if ((device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF or
device_owner == constants.DEVICE_OWNER_DHCP)):
err_msg = _("Unable to create or update %s port with a QoS "
"policy") % device_owner
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
def _assert_on_external_net_with_compute(self, port_data):
# Prevent creating port with device owner prefix 'compute'
# on external networks.
device_owner = port_data.get('device_owner')
if (device_owner is not None and
device_owner.startswith(constants.DEVICE_OWNER_COMPUTE_PREFIX)):
err_msg = _("Unable to update/create a port with an external "
"network")
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
def _validate_create_port(self, context, port_data):
self._validate_max_ips_per_port(port_data.get('fixed_ips', []),
port_data.get('device_owner'))
is_external_net = self._network_is_external(
context, port_data['network_id'])
qos_selected = validators.is_attr_set(port_data.get(
qos_consts.QOS_POLICY_ID))
device_owner = port_data.get('device_owner')
# QoS validations
if qos_selected:
self._validate_qos_policy_id(
context, port_data.get(qos_consts.QOS_POLICY_ID))
self._assert_on_illegal_port_with_qos(device_owner)
if is_external_net:
raise nsx_exc.QoSOnExternalNet()
# External network validations:
if is_external_net:
self._assert_on_external_net_with_compute(port_data)
self._assert_on_port_admin_state(port_data, device_owner)
def _assert_on_vpn_port_change(self, port_data):
if port_data['device_owner'] == ipsec_utils.VPN_PORT_OWNER:
msg = _('Can not update/delete VPNaaS port %s') % port_data['id']
raise n_exc.InvalidInput(error_message=msg)
def _assert_on_lb_port_fixed_ip_change(self, port_data, orig_dev_own):
if orig_dev_own == constants.DEVICE_OWNER_LOADBALANCERV2:
if "fixed_ips" in port_data and port_data["fixed_ips"]:
msg = _('Can not update Loadbalancer port with fixed IP')
raise n_exc.InvalidInput(error_message=msg)
def _assert_on_device_owner_change(self, port_data, orig_dev_own):
"""Prevent illegal device owner modifications
"""
if orig_dev_own == constants.DEVICE_OWNER_LOADBALANCERV2:
if ("allowed_address_pairs" in port_data and
port_data["allowed_address_pairs"]):
msg = _('Loadbalancer port can not be updated '
'with address pairs')
raise n_exc.InvalidInput(error_message=msg)
if 'device_owner' not in port_data:
return
new_dev_own = port_data['device_owner']
if new_dev_own == orig_dev_own:
return
err_msg = (_("Changing port device owner '%(orig)s' to '%(new)s' is "
"not allowed") % {'orig': orig_dev_own,
'new': new_dev_own})
# Do not allow changing nova <-> neutron device owners
if ((orig_dev_own.startswith(constants.DEVICE_OWNER_COMPUTE_PREFIX) and
new_dev_own.startswith(constants.DEVICE_OWNER_NETWORK_PREFIX)) or
(orig_dev_own.startswith(constants.DEVICE_OWNER_NETWORK_PREFIX) and
new_dev_own.startswith(constants.DEVICE_OWNER_COMPUTE_PREFIX))):
raise n_exc.InvalidInput(error_message=err_msg)
# Do not allow removing the device owner in some cases
if orig_dev_own == constants.DEVICE_OWNER_DHCP:
raise n_exc.InvalidInput(error_message=err_msg)
def _assert_on_port_sec_change(self, port_data, device_owner):
"""Do not allow enabling port security/mac learning of some ports
Trusted ports are created with port security and mac learning disabled
in neutron, and it should not change.
"""
if nl_net_utils.is_port_trusted({'device_owner': device_owner}):
if port_data.get(psec.PORTSECURITY) is True:
err_msg = _("port_security_enabled=True is not supported for "
"trusted ports")
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
mac_learning = port_data.get(mac_ext.MAC_LEARNING)
if (validators.is_attr_set(mac_learning) and mac_learning is True):
err_msg = _("mac_learning_enabled=True is not supported for "
"trusted ports")
LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg)
def _validate_update_port(self, context, id, original_port, port_data):
qos_selected = validators.is_attr_set(port_data.get
(qos_consts.QOS_POLICY_ID))
is_external_net = self._network_is_external(
context, original_port['network_id'])
device_owner = (port_data['device_owner']
if 'device_owner' in port_data
else original_port.get('device_owner'))
# QoS validations
if qos_selected:
self._validate_qos_policy_id(
context, port_data.get(qos_consts.QOS_POLICY_ID))
if is_external_net:
raise nsx_exc.QoSOnExternalNet()
self._assert_on_illegal_port_with_qos(device_owner)
# External networks validations:
if is_external_net:
self._assert_on_external_net_with_compute(port_data)
# Device owner validations:
orig_dev_owner = original_port.get('device_owner')
self._assert_on_device_owner_change(port_data, orig_dev_owner)
self._assert_on_port_admin_state(port_data, device_owner)
self._assert_on_port_sec_change(port_data, device_owner)
self._validate_max_ips_per_port(
port_data.get('fixed_ips', []), device_owner)
self._assert_on_vpn_port_change(original_port)
self._assert_on_lb_port_fixed_ip_change(port_data, orig_dev_owner)
def _get_dhcp_port_name(self, net_name, net_id):
return utils.get_name_and_uuid('%s-%s' % ('dhcp',
net_name or 'network'),
net_id)
def _build_port_name(self, context, port_data):
device_owner = port_data.get('device_owner')
device_id = port_data.get('device_id')
if device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF and device_id:
router = self._get_router(context, device_id)
name = utils.get_name_and_uuid(
router['name'] or 'router', port_data['id'], tag='port')
elif device_owner == constants.DEVICE_OWNER_DHCP:
network = self.get_network(context, port_data['network_id'])
name = self._get_dhcp_port_name(network['name'],
network['id'])
elif device_owner.startswith(constants.DEVICE_OWNER_COMPUTE_PREFIX):
name = utils.get_name_and_uuid(
port_data['name'] or 'instance-port', port_data['id'])
else:
name = port_data['name']
return name
def _validate_external_net_create(self, net_data, default_tier0_router,
tier0_validator=None):
"""Validate external network configuration
Returns a tuple of:
- Boolean is provider network (always True)
- Network type (always L3_EXT)
- tier 0 router id
- vlan id
"""
if not validators.is_attr_set(net_data.get(pnet.PHYSICAL_NETWORK)):
tier0_uuid = default_tier0_router
else:
tier0_uuid = net_data[pnet.PHYSICAL_NETWORK]
if ((validators.is_attr_set(net_data.get(pnet.NETWORK_TYPE)) and
net_data.get(pnet.NETWORK_TYPE) != utils.NetworkTypes.L3_EXT and
net_data.get(pnet.NETWORK_TYPE) != utils.NetworkTypes.LOCAL) or
validators.is_attr_set(net_data.get(pnet.SEGMENTATION_ID))):
msg = (_("External network cannot be created with %s provider "
"network or segmentation id") %
net_data.get(pnet.NETWORK_TYPE))
raise n_exc.InvalidInput(error_message=msg)
if tier0_validator:
tier0_validator(tier0_uuid)
return (True, utils.NetworkTypes.L3_EXT, tier0_uuid, 0)
def _extend_network_dict_provider(self, context, network, bindings=None):
"""Add network provider fields to the network dict from the DB"""
if 'id' not in network:
return
if not bindings:
bindings = nsx_db.get_network_bindings(context.session,
network['id'])
# With NSX plugin, "normal" overlay networks will have no binding
if bindings:
# Network came in through provider networks API
network[pnet.NETWORK_TYPE] = bindings[0].binding_type
network[pnet.PHYSICAL_NETWORK] = bindings[0].phy_uuid
network[pnet.SEGMENTATION_ID] = bindings[0].vlan_id
def _extend_get_network_dict_provider(self, context, network):
self._extend_network_dict_provider(context, network)
network[qos_consts.QOS_POLICY_ID] = (qos_com_utils.
get_network_policy_id(context, network['id']))
def get_network(self, context, id, fields=None):
with db_api.CONTEXT_READER.using(context):
# Get network from Neutron database
network = self._get_network(context, id)
# Don't do field selection here otherwise we won't be able to add
# provider networks fields
net = self._make_network_dict(network, context=context)
self._extend_get_network_dict_provider(context, net)
return db_utils.resource_fields(net, fields)
def get_networks(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
# Get networks from Neutron database
filters = filters or {}
with db_api.CONTEXT_READER.using(context):
networks = super(NsxPluginV3Base, self).get_networks(
context, filters, fields, sorts,
limit, marker, page_reverse)
# Add provider network fields
for net in networks:
self._extend_get_network_dict_provider(context, net)
return (networks if not fields else
[db_utils.resource_fields(network,
fields) for network in networks])
def _assert_on_ens_with_qos(self, net_data):
qos_id = net_data.get(qos_consts.QOS_POLICY_ID)
if validators.is_attr_set(qos_id):
err_msg = _("Cannot configure QOS on ENS networks")
raise n_exc.InvalidInput(error_message=err_msg)
def _ens_psec_supported(self):
"""Should be implemented by each plugin"""
pass
def _get_nsx_net_tz_id(self, nsx_net):
"""Should be implemented by each plugin"""
pass
def _validate_ens_net_portsecurity(self, net_data):
"""Validate/Update the port security of the new network for ENS TZ
Should be implemented by the plugin if necessary
"""
pass
def _generate_segment_id(self, context, physical_network, net_data):
bindings = nsx_db.get_network_bindings_by_phy_uuid(
context.session, physical_network)
vlan_ranges = self._network_vlans.get(physical_network, [])
if vlan_ranges:
vlan_ids = set()
for vlan_min, vlan_max in vlan_ranges:
vlan_ids |= set(moves.range(vlan_min, vlan_max + 1))
else:
vlan_min = constants.MIN_VLAN_TAG
vlan_max = constants.MAX_VLAN_TAG
vlan_ids = set(moves.range(vlan_min, vlan_max + 1))
used_ids_in_range = set([binding.vlan_id for binding in bindings
if binding.vlan_id in vlan_ids])
free_ids = list(vlan_ids ^ used_ids_in_range)
if len(free_ids) == 0:
raise n_exc.NoNetworkAvailable()
net_data[pnet.SEGMENTATION_ID] = free_ids[0]
return net_data[pnet.SEGMENTATION_ID]
def _validate_provider_create(self, context, network_data,
default_vlan_tz_uuid,
default_overlay_tz_uuid,
nsxlib_tz, nsxlib_network,
transparent_vlan=False):
"""Validate the parameters of a new provider network
raises an error if illegal
returns a dictionary with the relevant processed data:
- is_provider_net: boolean
- net_type: provider network type or None
- physical_net: the uuid of the relevant transport zone or None
- vlan_id: vlan tag, 0 or None
- switch_mode: standard ot ENS
"""
is_provider_net = any(
validators.is_attr_set(network_data.get(f))
for f in (pnet.NETWORK_TYPE,
pnet.PHYSICAL_NETWORK,
pnet.SEGMENTATION_ID))
physical_net = network_data.get(pnet.PHYSICAL_NETWORK)
if not validators.is_attr_set(physical_net):
physical_net = None
vlan_id = network_data.get(pnet.SEGMENTATION_ID)
if not validators.is_attr_set(vlan_id):
vlan_id = None
if vlan_id and transparent_vlan:
err_msg = (_("Segmentation ID cannot be set with transparent "
"vlan!"))
raise n_exc.InvalidInput(error_message=err_msg)
err_msg = None
net_type = network_data.get(pnet.NETWORK_TYPE)
tz_type = nsxlib_consts.TRANSPORT_TYPE_VLAN
switch_mode = nsxlib_consts.HOST_SWITCH_MODE_STANDARD
if validators.is_attr_set(net_type):
if net_type == utils.NsxV3NetworkTypes.FLAT:
if vlan_id is not None:
err_msg = (_("Segmentation ID cannot be specified with "
"%s network type") %
utils.NsxV3NetworkTypes.FLAT)
else:
if not transparent_vlan:
# Set VLAN id to 0 for flat networks
vlan_id = '0'
if physical_net is None:
physical_net = default_vlan_tz_uuid
elif net_type == utils.NsxV3NetworkTypes.VLAN:
# Use default VLAN transport zone if physical network not given
if physical_net is None:
physical_net = default_vlan_tz_uuid
if not transparent_vlan:
# Validate VLAN id
if not vlan_id:
vlan_id = self._generate_segment_id(context,
physical_net,
network_data)
elif not plugin_utils.is_valid_vlan_tag(vlan_id):
err_msg = (_('Segmentation ID %(seg_id)s out of '
'range (%(min_id)s through %(max_id)s)') %
{'seg_id': vlan_id,
'min_id': constants.MIN_VLAN_TAG,
'max_id': constants.MAX_VLAN_TAG})
else:
# Verify VLAN id is not already allocated
bindings = nsx_db.\
get_network_bindings_by_vlanid_and_physical_net(
context.session, vlan_id, physical_net)
if bindings:
raise n_exc.VlanIdInUse(
vlan_id=vlan_id, physical_network=physical_net)
elif net_type == utils.NsxV3NetworkTypes.GENEVE:
if vlan_id:
err_msg = (_("Segmentation ID cannot be specified with "
"%s network type") %
utils.NsxV3NetworkTypes.GENEVE)
tz_type = nsxlib_consts.TRANSPORT_TYPE_OVERLAY
elif net_type == utils.NsxV3NetworkTypes.NSX_NETWORK:
# Linking neutron networks to an existing NSX logical switch
if not physical_net:
err_msg = (_("Physical network must be specified with "
"%s network type") % net_type)
# Validate the logical switch existence
else:
try:
nsx_net = nsxlib_network.get(physical_net)
tz_id = self._get_nsx_net_tz_id(nsx_net)
switch_mode = nsxlib_tz.get_host_switch_mode(tz_id)
except nsx_lib_exc.ResourceNotFound:
err_msg = (_('Logical switch %s does not exist') %
physical_net)
# make sure no other neutron network is using it
bindings = (
nsx_db.get_network_bindings_by_vlanid_and_physical_net(
context.elevated().session, 0, physical_net))
if bindings:
err_msg = (_('Logical switch %s is already used by '
'another network') % physical_net)
else:
err_msg = (_('%(net_type_param)s %(net_type_value)s not '
'supported') %
{'net_type_param': pnet.NETWORK_TYPE,
'net_type_value': net_type})
elif is_provider_net:
# FIXME: Ideally provider-network attributes should be checked
# at the NSX backend. For now, the network_type is required,
# so the plugin can do a quick check locally.
err_msg = (_('%s is required for creating a provider network') %
pnet.NETWORK_TYPE)
else:
net_type = None
if physical_net is None:
# Default to transport type overlay
physical_net = default_overlay_tz_uuid
# validate the transport zone existence and type
if (not err_msg and physical_net and
net_type != utils.NsxV3NetworkTypes.NSX_NETWORK):
if is_provider_net:
try:
backend_type = nsxlib_tz.get_transport_type(
physical_net)
except nsx_lib_exc.ResourceNotFound:
err_msg = (_('Transport zone %s does not exist') %
physical_net)
else:
if backend_type != tz_type:
err_msg = (_('%(tz)s transport zone is required for '
'creating a %(net)s provider network') %
{'tz': tz_type, 'net': net_type})
if not err_msg:
switch_mode = nsxlib_tz.get_host_switch_mode(physical_net)
if err_msg:
raise n_exc.InvalidInput(error_message=err_msg)
if (switch_mode == nsxlib_consts.HOST_SWITCH_MODE_ENS):
if not self._allow_ens_networks():
raise NotImplementedError(_("ENS support is disabled"))
self._assert_on_ens_with_qos(network_data)
self._validate_ens_net_portsecurity(network_data)
return {'is_provider_net': is_provider_net,
'net_type': net_type,
'physical_net': physical_net,
'vlan_id': vlan_id,
'switch_mode': switch_mode}
def _network_is_nsx_net(self, context, network_id):
bindings = nsx_db.get_network_bindings(context.session, network_id)
if not bindings:
return False
return (bindings[0].binding_type ==
utils.NsxV3NetworkTypes.NSX_NETWORK)
def _vif_type_by_vnic_type(self, direct_vnic_type):
return (nsx_constants.VIF_TYPE_DVS if direct_vnic_type
else pbin.VIF_TYPE_OVS)
def _get_network_segmentation_id(self, context, neutron_id):
bindings = nsx_db.get_network_bindings(context.session, neutron_id)
if bindings:
return bindings[0].vlan_id
def _extend_nsx_port_dict_binding(self, context, port_data):
# Not using the register api for this because we need the context
# Some attributes were already initialized by _extend_port_portbinding
if pbin.VIF_TYPE not in port_data:
port_data[pbin.VIF_TYPE] = pbin.VIF_TYPE_OVS
if pbin.VNIC_TYPE not in port_data:
port_data[pbin.VNIC_TYPE] = pbin.VNIC_NORMAL
if 'network_id' in port_data:
net_id = port_data['network_id']
if pbin.VIF_DETAILS not in port_data:
port_data[pbin.VIF_DETAILS] = {}
port_data[pbin.VIF_DETAILS][pbin.OVS_HYBRID_PLUG] = False
if (port_data.get('device_owner') ==
constants.DEVICE_OWNER_FLOATINGIP):
# floatingip belongs to an external net without nsx-id
port_data[pbin.VIF_DETAILS]['nsx-logical-switch-id'] = None
else:
port_data[pbin.VIF_DETAILS]['nsx-logical-switch-id'] = (
self._get_network_nsx_id(context, net_id))
if port_data[pbin.VNIC_TYPE] != pbin.VNIC_NORMAL:
port_data[pbin.VIF_DETAILS]['segmentation-id'] = (
self._get_network_segmentation_id(context, net_id))
def fix_direct_vnic_port_sec(self, direct_vnic_type, port_data):
if direct_vnic_type:
if validators.is_attr_set(port_data.get(psec.PORTSECURITY)):
# 'direct' and 'direct-physical' vnic types ports requires
# port-security to be disabled.
if port_data[psec.PORTSECURITY]:
err_msg = _("Security features are not supported for "
"ports with direct/direct-physical VNIC "
"type")
raise n_exc.InvalidInput(error_message=err_msg)
else:
# Implicitly disable port-security for direct vnic types.
port_data[psec.PORTSECURITY] = False
def _validate_network_type(self, context, network_id, net_types):
net = self.get_network(context, network_id)
if net.get(pnet.NETWORK_TYPE) in net_types:
return True
return False

View File

@ -45,6 +45,7 @@ from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
from neutron_lib.api.definitions import external_net
from neutron_lib.api.definitions import l3 as l3_apidef
from neutron_lib.api.definitions import port_security as psec
from neutron_lib.api.definitions import vlantransparent as vlan_apidef
from neutron_lib.api import faults
from neutron_lib.api import validators
from neutron_lib.callbacks import events
@ -129,7 +130,8 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
"subnet_allocation",
"security-group-logging",
"provider-security-group",
"port-security-groups-filtering"]
"port-security-groups-filtering",
"vlan-transparent"]
@resource_registry.tracked_resources(
network=models_v2.Network,
@ -148,6 +150,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
extension_drivers = cfg.CONF.nsx_extension_drivers
self._extension_manager = managers.ExtensionManager(
extension_drivers=extension_drivers)
self.cfg_group = 'nsx_p' # group name for nsx_p section in nsx.ini
super(NsxPolicyPlugin, self).__init__()
# Bind the dummy L3 notifications
self.l3_rpc_notifier = l3_rpc_agent_api.L3NotifyAPI()
@ -160,8 +163,6 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
nsxlib_utils.set_inject_headers_callback(v3_utils.inject_headers)
self._validate_nsx_policy_version()
self.cfg_group = 'nsx_p' # group name for nsx_p section in nsx.ini
self._init_default_config()
self._prepare_default_rules()
@ -173,11 +174,14 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# NOTE(annak): we may need to generalize this for API calls
# requiring path ids
def _init_default_resource(self, resource_api, name_or_id):
def _init_default_resource(self, resource_api, name_or_id,
filter_list_results=None):
if not name_or_id:
# If not specified, the system will auto-configure
# in case only single resource is present
resources = resource_api.list()
if filter_list_results:
resources = filter_list_results(resources)
if len(resources) == 1:
return resources[0]['id']
else:
@ -195,6 +199,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return None
def _init_default_config(self):
# Default Tier0 router
self.default_tier0_router = self._init_default_resource(
self.nsxpolicy.tier0,
cfg.CONF.nsx_p.default_tier0_router)
@ -203,6 +208,24 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
raise cfg.RequiredOptError("default_tier0_router",
group=cfg.OptGroup('nsx_p'))
# Default overlay transport zone
self.default_overlay_tz = self._init_default_resource(
self.nsxpolicy.transport_zone,
cfg.CONF.nsx_p.default_overlay_tz,
filter_list_results=lambda tzs: [
tz for tz in tzs if tz['tz_type'].startswith('OVERLAY')])
if not self.default_overlay_tz:
raise cfg.RequiredOptError("default_overlay_tz",
group=cfg.OptGroup('nsx_p'))
# Default VLAN transport zone (not mandatory)
self.default_vlan_tz = self._init_default_resource(
self.nsxpolicy.transport_zone,
cfg.CONF.nsx_p.default_vlan_tz,
filter_list_results=lambda tzs: [
tz for tz in tzs if tz['tz_type'].startswith('VLAN')])
def _validate_nsx_policy_version(self):
self._nsx_version = self.nsxpolicy.get_version()
LOG.info("NSX Version: %s", self._nsx_version)
@ -252,48 +275,107 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
webob.exc.HTTPBadRequest,
})
def _create_network_on_backend(self, context, net_data):
# TODO(annak): provider network
def _create_network_on_backend(self, context, net_data,
transparent_vlan,
provider_data):
net_data['id'] = net_data.get('id') or uuidutils.generate_uuid()
# update the network name to indicate the neutron id too.
net_name = utils.get_name_and_uuid(net_data['name'] or 'network',
net_data['id'])
tags = self.nsxpolicy.build_v3_api_version_project_tag(
context.tenant_name)
tags = self.nsxpolicy.build_v3_tags_payload(
net_data, resource_type='os-neutron-net-id',
project_name=context.tenant_name)
# TODO(annak): admin state config is missing on policy
# should we not create networks that are down?
# alternative - configure status on manager for time being
# admin_state = net_data.get('admin_state_up', True)
admin_state = net_data.get('admin_state_up', True)
LOG.debug('create_network: %(net_name)s, %(physical_net)s, '
'%(tags)s, %(admin_state)s, %(vlan_id)s',
{'net_name': net_name,
'physical_net': provider_data['physical_net'],
'tags': tags,
'admin_state': admin_state,
'vlan_id': provider_data['vlan_id']})
if transparent_vlan:
# all vlan tags are allowed for guest vlan
vlan_ids = ["0-%s" % const.MAX_VLAN_TAG]
elif provider_data['vlan_id']:
vlan_ids = [provider_data['vlan_id']]
else:
vlan_ids = None
self.nsxpolicy.segment.create_or_overwrite(
net_name,
segment_id=net_data['id'],
description=net_data.get('description'),
vlan_ids=vlan_ids,
transport_zone_id=provider_data['physical_net'],
tags=tags)
def _validate_external_net_create(self, net_data):
#TODO(asarfaty): implement
pass
def _tier0_validator(self, tier0_uuid):
# Fail of the tier0 uuid was not found on the BSX
self.nsxpolicy.tier0.get(tier0_uuid)
def _get_nsx_net_tz_id(self, nsx_net):
return nsx_net['transport_zone_path'].split('/')[-1]
def _allow_ens_networks(self):
return True
def _ens_psec_supported(self):
"""ENS security features are always enabled on NSX versions which
the policy plugin supports.
"""
return True
def create_network(self, context, network):
net_data = network['network']
#TODO(asarfaty): network validation
#TODO(asarfaty): add ENS support
external = net_data.get(external_net.EXTERNAL)
is_external_net = validators.is_attr_set(external) and external
tenant_id = net_data['tenant_id']
self._ensure_default_security_group(context, tenant_id)
vlt = vlan_apidef.get_vlan_transparent(net_data)
self._validate_create_network(context, net_data)
if is_external_net:
self._validate_external_net_create(net_data)
is_provider_net, net_type, physical_net, vlan_id = (
self._validate_external_net_create(
net_data, self.default_tier0_router,
self._tier0_validator))
provider_data = {'is_provider_net': is_provider_net,
'net_type': net_type,
'physical_net': physical_net,
'vlan_id': vlan_id}
is_backend_network = False
else:
provider_data = self._validate_provider_create(
context, net_data,
self.default_vlan_tz,
self.default_overlay_tz,
self.nsxpolicy.transport_zone,
self.nsxpolicy.segment,
transparent_vlan=vlt)
if (provider_data['is_provider_net'] and
provider_data['net_type'] ==
utils.NsxV3NetworkTypes.NSX_NETWORK):
is_backend_network = False
else:
is_backend_network = True
# Create the neutron network
with db_api.CONTEXT_WRITER.using(context):
# Create network in Neutron
created_net = super(NsxPolicyPlugin, self).create_network(
context, network)
super(NsxPolicyPlugin, self).update_network(context,
created_net['id'],
{'network': {'vlan_transparent': vlt}})
self._extension_manager.process_create_network(
context, net_data, created_net)
if psec.PORTSECURITY not in net_data:
@ -302,10 +384,21 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
context, net_data, created_net)
self._process_l3_create(context, created_net, net_data)
if provider_data['is_provider_net']:
# Save provider network fields, needed by get_network()
net_bindings = [nsx_db.add_network_binding(
context.session, created_net['id'],
provider_data['net_type'],
provider_data['physical_net'],
provider_data['vlan_id'])]
self._extend_network_dict_provider(context, created_net,
bindings=net_bindings)
# Create the backend NSX network
if not is_external_net:
if is_backend_network:
try:
self._create_network_on_backend(context, created_net)
self._create_network_on_backend(
context, created_net, vlt, provider_data)
except Exception as e:
LOG.exception("Failed to create NSX network network: %s", e)
with excutils.save_and_reraise_exception():
@ -316,24 +409,30 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# latest db model for the extension functions
net_model = self._get_network(context, created_net['id'])
resource_extend.apply_funcs('networks', created_net, net_model)
return created_net
def delete_network(self, context, network_id):
is_nsx_net = self._network_is_nsx_net(context, network_id)
is_external_net = self._network_is_external(context, network_id)
with db_api.CONTEXT_WRITER.using(context):
self._process_l3_delete(context, network_id)
super(NsxPolicyPlugin, self).delete_network(
context, network_id)
if not self._network_is_external(context, network_id):
if not is_external_net and not is_nsx_net:
self.nsxpolicy.segment.delete(network_id)
else:
# TODO(asarfaty): for NSX network we may need to delete DHCP conf
pass
def update_network(self, context, id, network):
original_net = super(NsxPolicyPlugin, self).get_network(context, id)
def update_network(self, context, network_id, network):
original_net = super(NsxPolicyPlugin, self).get_network(
context, network_id)
net_data = network['network']
LOG.debug("Updating network %s %s->%s", id, original_net, net_data)
# Neutron does not support changing provider network values
providernet._raise_if_updates_provider_attributes(net_data)
extern_net = self._network_is_external(context, id)
extern_net = self._network_is_external(context, network_id)
is_nsx_net = self._network_is_nsx_net(context, network_id)
# Do not support changing external/non-external networks
if (external_net.EXTERNAL in net_data and
@ -341,41 +440,34 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
err_msg = _("Cannot change the router:external flag of a network")
raise n_exc.InvalidInput(error_message=err_msg)
updated_net = super(NsxPolicyPlugin, self).update_network(context, id,
network)
# Update the neutron network
updated_net = super(NsxPolicyPlugin, self).update_network(
context, network_id, network)
self._extension_manager.process_update_network(context, net_data,
updated_net)
self._process_l3_update(context, updated_net, network['network'])
self._extend_network_dict_provider(context, updated_net)
#TODO(asarfaty): update the Policy manager
# Update the backend segment
if (not extern_net and not is_nsx_net and
('name' in net_data or 'description' in net_data)):
# TODO(asarfaty): handle admin state changes as well
net_name = utils.get_name_and_uuid(
updated_net['name'] or 'network', network_id)
try:
self.nsxpolicy.segment.update(
network_id,
name=net_name,
description=net_data.get('description'))
except nsx_lib_exc.ManagerError:
LOG.exception("Unable to update NSX backend, rolling "
"back changes on neutron")
with excutils.save_and_reraise_exception():
super(NsxPolicyPlugin, self).update_network(
context, network_id, {'network': original_net})
return updated_net
def get_network(self, context, id, fields=None):
with db_api.CONTEXT_READER.using(context):
# Get network from Neutron database
network = self._get_network(context, id)
# Don't do field selection here otherwise we won't be able to add
# provider networks fields
net = self._make_network_dict(network, context=context)
return db_utils.resource_fields(net, fields)
def get_networks(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
# Get networks from Neutron database
filters = filters or {}
with db_api.CONTEXT_READER.using(context):
networks = (
super(NsxPolicyPlugin, self).get_networks(
context, filters, fields, sorts,
limit, marker, page_reverse))
# TODO(asarfaty) Add plugin/provider network fields
return (networks if not fields else
[db_utils.resource_fields(network,
fields) for network in networks])
def create_subnet(self, context, subnet):
self._validate_host_routes_input(subnet)
created_subnet = super(
@ -423,6 +515,20 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return address_bindings
def _get_network_nsx_id(self, context, network_id):
"""Return the NSX segment ID matching the neutron network id
Usually the NSX ID is the same as the neutron ID. The exception is
when this is a provider NSX_NETWORK, which means the network already
existed on the NSX backend, and it is being consumed by the plugin.
"""
bindings = nsx_db.get_network_bindings(context.session, network_id)
if (bindings and
bindings[0].binding_type == utils.NsxV3NetworkTypes.NSX_NETWORK):
# return the ID of the NSX network
return bindings[0].phy_uuid
return network_id
def _build_port_tags(self, port_data):
sec_groups = port_data.get(ext_sg.SECURITYGROUPS, [])
sec_groups += port_data.get(provider_sg.PROVIDER_SECURITYGROUPS, [])
@ -453,22 +559,16 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
tags.extend(self.nsxpolicy.build_v3_api_version_project_tag(
context.tenant_name))
segment_id = self._get_network_nsx_id(
context, port_data['network_id'])
self.nsxpolicy.segment_port.create_or_overwrite(
name,
port_data['network_id'],
name, segment_id,
port_id=port_data['id'],
description=port_data.get('description'),
address_bindings=address_bindings,
vif_id=vif_id,
tags=tags)
def _cleanup_port(self, context, port_id, lport_id):
super(NsxPolicyPlugin, self).delete_port(context, port_id)
port_data = self.get_port(context, port_id)
self.nsxpolicy.segment_port.delete(
port_data['network_id'], port_data['id'])
def base_create_port(self, context, port):
neutron_db = super(NsxPolicyPlugin, self).create_port(context, port)
self._extension_manager.process_create_port(
@ -480,6 +580,11 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._validate_max_ips_per_port(port_data.get('fixed_ips', []),
port_data.get('device_owner'))
# Validate the vnic type (the same types as for the NSX-T plugin)
direct_vnic_type = self._validate_port_vnic_type(
context, port_data, port_data['network_id'],
projectpluginmap.NsxPlugins.NSX_T)
with db_api.CONTEXT_WRITER.using(context):
is_external_net = self._network_is_external(
context, port_data['network_id'])
@ -489,10 +594,14 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
neutron_db = self.base_create_port(context, port)
port["port"].update(neutron_db)
self.fix_direct_vnic_port_sec(direct_vnic_type, port_data)
(is_psec_on, has_ip, sgids, psgids) = (
self._create_port_preprocess_security(context, port,
port_data, neutron_db,
False))
self._process_portbindings_create_and_update(
context, port['port'], port_data,
vif_type=self._vif_type_by_vnic_type(direct_vnic_type))
self._process_port_create_security_group(context, port_data, sgids)
self._process_port_create_provider_security_group(
@ -506,26 +615,31 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
LOG.error('Failed to create port %(id)s on NSX '
'backend. Exception: %(e)s',
{'id': neutron_db['id'], 'e': e})
self._cleanup_port(context, neutron_db['id'], None)
super(NsxPolicyPlugin, self).delete_port(
context, neutron_db['id'])
# this extra lookup is necessary to get the
# latest db model for the extension functions
port_model = self._get_port(context, port_data['id'])
resource_extend.apply_funcs('ports', port_data, port_model)
self._extend_nsx_port_dict_binding(context, port_data)
self._remove_provider_security_groups_from_list(port_data)
kwargs = {'context': context, 'port': neutron_db}
registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)
return neutron_db
return port_data
def delete_port(self, context, port_id,
l3_port_check=True, l2gw_port_check=True,
force_delete_dhcp=False,
force_delete_vpn=False):
port_data = self.get_port(context, port_id)
segment_id = self._get_network_nsx_id(
context, port_data['network_id'])
if not self._network_is_external(context, port_data['network_id']):
try:
self.nsxpolicy.segment_port.delete(
port_data['network_id'], port_data['id'])
self.nsxpolicy.segment_port.delete(segment_id, port_data['id'])
except Exception as ex:
LOG.error("Failed to delete port %(id)s on NSX backend "
"due to %(e)s",
@ -593,6 +707,8 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
if 'id' in port:
port_model = self._get_port(context, port['id'])
resource_extend.apply_funcs('ports', port, port_model)
self._extend_nsx_port_dict_binding(context, port)
self._remove_provider_security_groups_from_list(port)
return db_utils.resource_fields(port, fields)
def get_ports(self, context, filters=None, fields=None,
@ -617,6 +733,8 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
"process, and is being skipped", port['id'])
ports.remove(port)
continue
self._extend_nsx_port_dict_binding(context, port)
self._remove_provider_security_groups_from_list(port)
return (ports if not fields else
[db_utils.resource_fields(port, fields) for port in ports])
@ -703,11 +821,12 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._validate_interface_address_scope(context, router_db, info)
subnet = self.get_subnet(context, info['subnet_ids'][0])
segment_id = self._get_network_nsx_id(context, network_id)
# TODO(annak): Validate TZ
try:
# This is always an overwrite call
# NOTE: Connecting network to multiple routers is not supported
self.nsxpolicy.segment.create_or_overwrite(network_id,
self.nsxpolicy.segment.create_or_overwrite(segment_id,
tier1_id=router_id)
except Exception as ex:
with excutils.save_and_reraise_exception():
@ -745,9 +864,6 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
fip_id, router_id, port_id)
if router_id:
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
router_id)
if nsx_router_id:
#TODO(asarfaty): Update the NSX router
pass
@ -764,9 +880,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
router_id = new_fip['router_id']
new_port_id = new_fip['port_id']
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
router_id)
if nsx_router_id:
if router_id:
#TODO(asarfaty): Update the NSX router
LOG.debug("Updating floating IP %s. Router %s, Port %s "
"(old port %s)",
@ -784,9 +898,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
for fip_db in fip_dbs:
if not fip_db.router_id:
continue
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
fip_db.router_id)
if nsx_router_id:
if fip_db.router_id:
# TODO(asarfaty): Update the NSX logical router
pass
self.update_floatingip_status(context, fip_db.id,

View File

@ -63,7 +63,6 @@ from neutron.extensions import providernet
from neutron.extensions import securitygroup as ext_sg
from neutron.quota import resource_registry
from neutron_lib.api.definitions import extra_dhcp_opt as ext_edo
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 vlantransparent as vlan_apidef
from neutron_lib.api import validators
@ -74,7 +73,6 @@ from neutron_lib.callbacks import resources
from neutron_lib import constants as const
from neutron_lib import context as q_context
from neutron_lib import exceptions as n_exc
from neutron_lib.plugins import utils as plugin_utils
from neutron_lib.utils import helpers
from oslo_config import cfg
from oslo_db import exception as db_exc
@ -82,7 +80,6 @@ from oslo_log import log
from oslo_utils import excutils
from oslo_utils import importutils
from oslo_utils import uuidutils
from six import moves
from sqlalchemy import exc as sql_exc
import webob.exc
@ -99,7 +96,6 @@ from vmware_nsx.common import utils
from vmware_nsx.db import db as nsx_db
from vmware_nsx.db import extended_security_group_rule as extend_sg_rule
from vmware_nsx.db import maclearning as mac_db
from vmware_nsx.db import nsx_portbindings_db as pbin_db
from vmware_nsx.dhcp_meta import rpc as nsx_rpc
from vmware_nsx.extensions import advancedserviceproviders as as_providers
from vmware_nsx.extensions import housekeeper as hk_ext
@ -168,7 +164,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
extraroute_db.ExtraRoute_db_mixin,
router_az_db.RouterAvailabilityZoneMixin,
l3_gwmode_db.L3_NAT_db_mixin,
pbin_db.NsxPortBindingMixin,
portbindings_db.PortBindingMixin,
portsecurity_db.PortSecurityDbMixin,
extradhcpopt_db.ExtraDhcpOptMixin,
@ -231,6 +226,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
extension_drivers = cfg.CONF.nsx_extension_drivers
self._extension_manager = managers.ExtensionManager(
extension_drivers=extension_drivers)
self.cfg_group = 'nsx_v3' # group name for nsx_v3 section in nsx.ini
super(NsxV3Plugin, self).__init__()
# Bind the dummy L3 notifications
self.l3_rpc_notifier = l3_rpc_agent_api.L3NotifyAPI()
@ -255,11 +251,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._nsx_version = self.nsxlib.get_version()
LOG.info("NSX Version: %s", self._nsx_version)
self.cfg_group = 'nsx_v3' # group name for nsx_v3 section in nsx.ini
self.tier0_groups_dict = {}
self._network_vlans = plugin_utils.parse_network_vlan_ranges(
cfg.CONF.nsx_v3.network_vlan_ranges)
# Initialize the network availability zones, which will be used only
# when native_dhcp_metadata is True
self.init_availability_zones()
@ -632,33 +625,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
for az in self.get_azs_list():
az.translate_configured_names_to_uuids(self.nsxlib)
def _get_network_segmentation_id(self, context, neutron_id):
bindings = nsx_db.get_network_bindings(context.session, neutron_id)
if bindings:
return bindings[0].vlan_id
def _extend_nsx_port_dict_binding(self, context, port_data):
# Not using the register api for this because we need the context
# Some attributes were already initialized by _extend_port_portbinding
if pbin.VIF_TYPE not in port_data:
port_data[pbin.VIF_TYPE] = pbin.VIF_TYPE_OVS
if pbin.VNIC_TYPE not in port_data:
port_data[pbin.VNIC_TYPE] = pbin.VNIC_NORMAL
if 'network_id' in port_data:
net_id = port_data['network_id']
if pbin.VIF_DETAILS not in port_data:
port_data[pbin.VIF_DETAILS] = {}
port_data[pbin.VIF_DETAILS][pbin.OVS_HYBRID_PLUG] = False
if port_data.get('device_owner') == const.DEVICE_OWNER_FLOATINGIP:
# floatingip belongs to an external net without nsx-id
port_data[pbin.VIF_DETAILS]['nsx-logical-switch-id'] = None
else:
port_data[pbin.VIF_DETAILS]['nsx-logical-switch-id'] = (
self._get_network_nsx_id(context, net_id))
if port_data[pbin.VNIC_TYPE] != pbin.VNIC_NORMAL:
port_data[pbin.VIF_DETAILS]['segmentation-id'] = (
self._get_network_segmentation_id(context, net_id))
@nsxlib_utils.retry_upon_exception(
Exception, max_attempts=cfg.CONF.nsx_v3.retries)
def _init_default_section_nsgroup(self):
@ -917,179 +883,25 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return self.conn.consume_in_threads()
def _validate_provider_create(self, context, network_data, az,
transparent_vlan):
is_provider_net = any(
validators.is_attr_set(network_data.get(f))
for f in (pnet.NETWORK_TYPE,
pnet.PHYSICAL_NETWORK,
pnet.SEGMENTATION_ID))
physical_net = network_data.get(pnet.PHYSICAL_NETWORK)
if not validators.is_attr_set(physical_net):
physical_net = None
vlan_id = network_data.get(pnet.SEGMENTATION_ID)
if not validators.is_attr_set(vlan_id):
vlan_id = None
if vlan_id and transparent_vlan:
err_msg = (_("Segmentation ID cannot be set with transparent "
"vlan!"))
raise n_exc.InvalidInput(error_message=err_msg)
err_msg = None
net_type = network_data.get(pnet.NETWORK_TYPE)
nsxlib_tz = self.nsxlib.transport_zone
tz_type = nsxlib_tz.TRANSPORT_TYPE_VLAN
switch_mode = nsxlib_tz.HOST_SWITCH_MODE_STANDARD
if validators.is_attr_set(net_type):
if net_type == utils.NsxV3NetworkTypes.FLAT:
if vlan_id is not None:
err_msg = (_("Segmentation ID cannot be specified with "
"%s network type") %
utils.NsxV3NetworkTypes.FLAT)
else:
if not transparent_vlan:
# Set VLAN id to 0 for flat networks
vlan_id = '0'
if physical_net is None:
physical_net = az._default_vlan_tz_uuid
elif (net_type == utils.NsxV3NetworkTypes.VLAN and
not transparent_vlan):
# Use default VLAN transport zone if physical network not given
if physical_net is None:
physical_net = az._default_vlan_tz_uuid
# Validate VLAN id
if not vlan_id:
vlan_id = self._generate_segment_id(context,
physical_net,
network_data)
elif not plugin_utils.is_valid_vlan_tag(vlan_id):
err_msg = (_('Segmentation ID %(segmentation_id)s out of '
'range (%(min_id)s through %(max_id)s)') %
{'segmentation_id': vlan_id,
'min_id': const.MIN_VLAN_TAG,
'max_id': const.MAX_VLAN_TAG})
else:
# Verify VLAN id is not already allocated
bindings = (
nsx_db.get_network_bindings_by_vlanid_and_physical_net(
context.session, vlan_id, physical_net)
)
if bindings:
raise n_exc.VlanIdInUse(
vlan_id=vlan_id, physical_network=physical_net)
elif (net_type == utils.NsxV3NetworkTypes.VLAN and
transparent_vlan):
# Use default VLAN transport zone if physical network not given
if physical_net is None:
physical_net = az._default_vlan_tz_uuid
elif net_type == utils.NsxV3NetworkTypes.GENEVE:
if vlan_id:
err_msg = (_("Segmentation ID cannot be specified with "
"%s network type") %
utils.NsxV3NetworkTypes.GENEVE)
tz_type = nsxlib_tz.TRANSPORT_TYPE_OVERLAY
elif net_type == utils.NsxV3NetworkTypes.NSX_NETWORK:
# Linking neutron networks to an existing NSX logical switch
if physical_net is None:
err_msg = (_("Physical network must be specified with "
"%s network type") % net_type)
# Validate the logical switch existence
try:
nsx_net = self.nsxlib.logical_switch.get(physical_net)
switch_mode = nsxlib_tz.get_host_switch_mode(
nsx_net['transport_zone_id'])
except nsx_lib_exc.ResourceNotFound:
err_msg = (_('Logical switch %s does not exist') %
physical_net)
# make sure no other neutron network is using it
bindings = (
nsx_db.get_network_bindings_by_vlanid_and_physical_net(
context.elevated().session, 0, physical_net))
if bindings:
err_msg = (_('Logical switch %s is already used by '
'another network') % physical_net)
else:
err_msg = (_('%(net_type_param)s %(net_type_value)s not '
'supported') %
{'net_type_param': pnet.NETWORK_TYPE,
'net_type_value': net_type})
elif is_provider_net:
# FIXME: Ideally provider-network attributes should be checked
# at the NSX backend. For now, the network_type is required,
# so the plugin can do a quick check locally.
err_msg = (_('%s is required for creating a provider network') %
pnet.NETWORK_TYPE)
else:
net_type = None
if physical_net is None:
# Default to transport type overlay
physical_net = az._default_overlay_tz_uuid
# validate the transport zone existence and type
if (not err_msg and physical_net and
net_type != utils.NsxV3NetworkTypes.NSX_NETWORK):
if is_provider_net:
try:
backend_type = nsxlib_tz.get_transport_type(
physical_net)
except nsx_lib_exc.ResourceNotFound:
err_msg = (_('Transport zone %s does not exist') %
physical_net)
else:
if backend_type != tz_type:
err_msg = (_('%(tz)s transport zone is required for '
'creating a %(net)s provider network') %
{'tz': tz_type, 'net': net_type})
if not err_msg:
switch_mode = nsxlib_tz.get_host_switch_mode(physical_net)
if err_msg:
raise n_exc.InvalidInput(error_message=err_msg)
return {'is_provider_net': is_provider_net,
'net_type': net_type,
'physical_net': physical_net,
'vlan_id': vlan_id,
'switch_mode': switch_mode}
def _get_edge_cluster(self, tier0_uuid):
self.nsxlib.router.validate_tier0(self.tier0_groups_dict, tier0_uuid)
tier0_info = self.tier0_groups_dict[tier0_uuid]
return tier0_info['edge_cluster_uuid']
def _validate_external_net_create(self, net_data, az):
if not validators.is_attr_set(net_data.get(pnet.PHYSICAL_NETWORK)):
tier0_uuid = az._default_tier0_router
else:
tier0_uuid = net_data[pnet.PHYSICAL_NETWORK]
if ((validators.is_attr_set(net_data.get(pnet.NETWORK_TYPE)) and
net_data.get(pnet.NETWORK_TYPE) != utils.NetworkTypes.L3_EXT and
net_data.get(pnet.NETWORK_TYPE) != utils.NetworkTypes.LOCAL) or
validators.is_attr_set(net_data.get(pnet.SEGMENTATION_ID))):
msg = (_("External network cannot be created with %s provider "
"network or segmentation id") %
net_data.get(pnet.NETWORK_TYPE))
raise n_exc.InvalidInput(error_message=msg)
self.nsxlib.router.validate_tier0(self.tier0_groups_dict, tier0_uuid)
return (True, utils.NetworkTypes.L3_EXT, tier0_uuid, 0)
def _allow_ens_networks(self):
return cfg.CONF.nsx_v3.ens_support
def _create_network_at_the_backend(self, context, net_data, az,
transparent_vlan):
provider_data = self._validate_provider_create(context, net_data, az,
transparent_vlan)
provider_data = self._validate_provider_create(
context, net_data,
az._default_vlan_tz_uuid,
az._default_overlay_tz_uuid,
self.nsxlib.transport_zone,
self.nsxlib.logical_switch,
transparent_vlan=transparent_vlan)
neutron_net_id = net_data.get('id') or uuidutils.generate_uuid()
net_data['id'] = neutron_net_id
if (provider_data['switch_mode'] ==
self.nsxlib.transport_zone.HOST_SWITCH_MODE_ENS):
if not cfg.CONF.nsx_v3.ens_support:
raise NotImplementedError(_("ENS support is disabled"))
self._assert_on_ens_with_qos(net_data)
self._validate_ens_net_portsecurity(net_data)
if (provider_data['is_provider_net'] and
provider_data['net_type'] == utils.NsxV3NetworkTypes.NSX_NETWORK):
@ -1148,12 +960,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return (is_overlay or self.nsxlib.feature_supported(
nsxlib_consts.FEATURE_VLAN_ROUTER_INTERFACE)), net_type
def _validate_network_type(self, context, network_id, net_types):
net = self.get_network(context, network_id)
if net.get(pnet.NETWORK_TYPE) in net_types:
return True
return False
def _is_ddi_supported_on_network(self, context, network_id):
result, _ = self._is_ddi_supported_on_net_with_type(
context, network_id)
@ -1199,19 +1005,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self.nsxlib.transport_zone.TRANSPORT_TYPE_OVERLAY)
return False
def _extend_network_dict_provider(self, context, network, bindings=None):
if 'id' not in network:
return
if not bindings:
bindings = nsx_db.get_network_bindings(context.session,
network['id'])
# With NSX plugin, "normal" overlay networks will have no binding
if bindings:
# Network came in through provider networks API
network[pnet.NETWORK_TYPE] = bindings[0].binding_type
network[pnet.PHYSICAL_NETWORK] = bindings[0].phy_uuid
network[pnet.SEGMENTATION_ID] = bindings[0].vlan_id
def get_subnets(self, context, filters=None, fields=None, sorts=None,
limit=None, marker=None, page_reverse=False):
filters = filters or {}
@ -1227,38 +1020,17 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return super(NsxV3Plugin, self).get_subnets(
context, filters, fields, sorts, limit, marker, page_reverse)
def _network_is_nsx_net(self, context, network_id):
bindings = nsx_db.get_network_bindings(context.session, network_id)
if not bindings:
return False
return (bindings[0].binding_type ==
utils.NsxV3NetworkTypes.NSX_NETWORK)
def _generate_segment_id(self, context, physical_network, net_data):
bindings = nsx_db.get_network_bindings_by_phy_uuid(
context.session, physical_network)
vlan_ranges = self._network_vlans.get(physical_network, [])
if vlan_ranges:
vlan_ids = set()
for vlan_min, vlan_max in vlan_ranges:
vlan_ids |= set(moves.range(vlan_min, vlan_max + 1))
else:
vlan_min = const.MIN_VLAN_TAG
vlan_max = const.MAX_VLAN_TAG
vlan_ids = set(moves.range(vlan_min, vlan_max + 1))
used_ids_in_range = set([binding.vlan_id for binding in bindings
if binding.vlan_id in vlan_ids])
free_ids = list(vlan_ids ^ used_ids_in_range)
if len(free_ids) == 0:
raise n_exc.NoNetworkAvailable()
net_data[pnet.SEGMENTATION_ID] = free_ids[0]
return net_data[pnet.SEGMENTATION_ID]
def _get_mdproxy_port_name(self, net_name, net_id):
return utils.get_name_and_uuid('%s-%s' % ('mdproxy',
net_name or 'network'),
net_id)
def _tier0_validator(self, tier0_uuid):
self.nsxlib.router.validate_tier0(self.tier0_groups_dict, tier0_uuid)
def _get_nsx_net_tz_id(self, nsx_net):
return nsx_net['transport_zone_id']
def create_network(self, context, network):
net_data = network['network']
external = net_data.get(extnet_apidef.EXTERNAL)
@ -1283,7 +1055,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
if is_external_net:
is_provider_net, net_type, physical_net, vlan_id = (
self._validate_external_net_create(net_data, az))
self._validate_external_net_create(
net_data, az._default_tier0_router,
self._tier0_validator))
nsx_net_id = None
is_backend_network = False
else:
@ -1389,12 +1163,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return created_net
def _assert_on_ens_with_qos(self, net_data):
qos_id = net_data.get(qos_consts.QOS_POLICY_ID)
if validators.is_attr_set(qos_id):
err_msg = _("Cannot configure QOS on ENS networks")
raise n_exc.InvalidInput(error_message=err_msg)
def _ens_psec_supported(self):
return self.nsxlib.feature_supported(
nsxlib_consts.FEATURE_ENS_WITH_SEC)
@ -1405,8 +1173,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
if cfg.CONF.nsx_v3.disable_port_security_for_ens:
# Override the port-security to False
if net_data[psec.PORTSECURITY]:
LOG.warning("Disabling port security for network %s",
net_data['id'])
LOG.warning("Disabling port security for bew network")
# Set the port security to False
net_data[psec.PORTSECURITY] = False
@ -1521,7 +1288,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
is_ens_net = self._is_ens_tz_net(context, id)
# Validate the updated parameters
self._validate_update_netowrk(context, id, original_net, net_data)
self._validate_update_network(context, id, original_net, net_data)
# add some plugin specific validations
if is_ens_net:
self._assert_on_ens_with_qos(net_data)
@ -2205,38 +1972,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return address_bindings
def _extend_get_network_dict_provider(self, context, network):
self._extend_network_dict_provider(context, network)
network[qos_consts.QOS_POLICY_ID] = (qos_com_utils.
get_network_policy_id(context, network['id']))
def get_network(self, context, id, fields=None):
with db_api.CONTEXT_READER.using(context):
# Get network from Neutron database
network = self._get_network(context, id)
# Don't do field selection here otherwise we won't be able to add
# provider networks fields
net = self._make_network_dict(network, context=context)
self._extend_get_network_dict_provider(context, net)
return db_utils.resource_fields(net, fields)
def get_networks(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
# Get networks from Neutron database
filters = filters or {}
with db_api.CONTEXT_READER.using(context):
networks = (
super(NsxV3Plugin, self).get_networks(
context, filters, fields, sorts,
limit, marker, page_reverse))
# Add provider network fields
for net in networks:
self._extend_get_network_dict_provider(context, net)
return (networks if not fields else
[db_utils.resource_fields(network,
fields) for network in networks])
def _get_qos_profile_id(self, context, policy_id):
switch_profile_id = nsx_db.get_switch_profile_by_qos_policy(
context.session, policy_id)
@ -2820,10 +2555,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
context, port['port'], neutron_db)
return neutron_db
def _vif_type_by_vnic_type(self, direct_vnic_type):
return (nsx_constants.VIF_TYPE_DVS if direct_vnic_type
else pbin.VIF_TYPE_OVS)
def _validate_ens_create_port(self, context, port_data):
qos_selected = validators.is_attr_set(port_data.get(
qos_consts.QOS_POLICY_ID))
@ -2862,19 +2593,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
neutron_db = self.base_create_port(context, port)
port["port"].update(neutron_db)
if direct_vnic_type:
if validators.is_attr_set(port_data.get(psec.PORTSECURITY)):
# 'direct' and 'direct-physical' vnic types ports requires
# port-security to be disabled.
if port_data[psec.PORTSECURITY]:
err_msg = _("Security features are not supported for "
"ports with direct/direct-physical VNIC "
"type")
raise n_exc.InvalidInput(error_message=err_msg)
else:
# Implicitly disable port-security for direct vnic types.
port_data[psec.PORTSECURITY] = False
self.fix_direct_vnic_port_sec(direct_vnic_type, port_data)
(is_psec_on, has_ip, sgids, psgids) = (
self._create_port_preprocess_security(context, port,
port_data, neutron_db,

View File

@ -15,13 +15,35 @@
import mock
from oslo_config import cfg
from oslo_utils import uuidutils
from webob import exc
from neutron.tests.unit.db import test_db_base_plugin_v2
from neutron.tests.unit.extensions import test_securitygroup
from neutron_lib.api.definitions import external_net as extnet_apidef
from neutron_lib.api.definitions import port_security as psec
from neutron_lib.api.definitions import portbindings
from neutron_lib.api.definitions import provider_net as pnet
from neutron_lib.api.definitions import vlantransparent as vlan_apidef
from neutron_lib import context
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
from vmware_nsxlib.v3 import nsx_constants
PLUGIN_NAME = 'vmware_nsx.plugin.NsxPolicyPlugin'
NSX_OVERLAY_TZ_NAME = 'OVERLAY_TZ'
NSX_VLAN_TZ_NAME = 'VLAN_TZ'
DEFAULT_TIER0_ROUTER_UUID = "efad0078-9204-4b46-a2d8-d4dd31ed448f"
def _return_id_key(*args, **kwargs):
return {'id': uuidutils.generate_uuid()}
def _return_id_key_list(*args, **kwargs):
return [{'id': uuidutils.generate_uuid()}]
class NsxPPluginTestCaseMixin(
@ -56,14 +78,38 @@ class NsxPPluginTestCaseMixin(
return_value=-1).start()
def setup_conf_overrides(self):
#TODO(asarfaty): will be needed in the future
#cfg.CONF.set_override('default_overlay_tz', NSX_TZ_NAME, 'nsx_p')
#cfg.CONF.set_override('native_dhcp_metadata', False, 'nsx_p')
#cfg.CONF.set_override('dhcp_profile',
# NSX_DHCP_PROFILE_ID, 'nsx_p')
#cfg.CONF.set_override('metadata_proxy',
# NSX_METADATA_PROXY_ID, 'nsx_p')
pass
cfg.CONF.set_override('default_overlay_tz', NSX_OVERLAY_TZ_NAME,
'nsx_p')
cfg.CONF.set_override('default_vlan_tz', NSX_VLAN_TZ_NAME, 'nsx_p')
def _create_network(self, fmt, name, admin_state_up,
arg_list=None, providernet_args=None,
set_context=False, tenant_id=None,
**kwargs):
tenant_id = tenant_id or self._tenant_id
data = {'network': {'name': name,
'admin_state_up': admin_state_up,
'tenant_id': tenant_id}}
# Fix to allow the router:external attribute and any other
# attributes containing a colon to be passed with
# a double underscore instead
kwargs = dict((k.replace('__', ':'), v) for k, v in kwargs.items())
if extnet_apidef.EXTERNAL in kwargs:
arg_list = (extnet_apidef.EXTERNAL, ) + (arg_list or ())
if providernet_args:
kwargs.update(providernet_args)
for arg in (('admin_state_up', 'tenant_id', 'shared',
'availability_zone_hints') + (arg_list or ())):
# Arg must be present
if arg in kwargs:
data['network'][arg] = kwargs[arg]
network_req = self.new_create_request('networks', data, fmt)
if set_context and tenant_id:
# create a specific auth context for this request
network_req.environ['neutron.context'] = context.Context(
'', tenant_id)
return network_req.get_response(self.api)
class NsxPTestNetworks(test_db_base_plugin_v2.TestNetworksV2,
@ -78,6 +124,220 @@ class NsxPTestNetworks(test_db_base_plugin_v2.TestNetworksV2,
def tearDown(self):
super(NsxPTestNetworks, self).tearDown()
def test_create_provider_flat_network(self):
providernet_args = {pnet.NETWORK_TYPE: 'flat'}
with mock.patch('vmware_nsxlib.v3.policy_resources.'
'NsxPolicySegmentApi.create_or_overwrite',
side_effect=_return_id_key) as nsx_create, \
mock.patch('vmware_nsxlib.v3.policy_resources.NsxPolicySegmentApi.'
'delete') as nsx_delete, \
mock.patch('vmware_nsxlib.v3.policy_resources.'
'NsxPolicyTransportZoneApi.get_transport_type',
return_value=nsx_constants.TRANSPORT_TYPE_VLAN), \
self.network(name='flat_net',
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE, )) as net:
self.assertEqual('flat', net['network'].get(pnet.NETWORK_TYPE))
# make sure the network is created at the backend
nsx_create.assert_called_once()
# Delete the network and make sure it is deleted from the backend
req = self.new_delete_request('networks', net['network']['id'])
res = req.get_response(self.api)
self.assertEqual(exc.HTTPNoContent.code, res.status_int)
nsx_delete.assert_called_once()
def test_create_provider_flat_network_with_physical_net(self):
physical_network = DEFAULT_TIER0_ROUTER_UUID
providernet_args = {pnet.NETWORK_TYPE: 'flat',
pnet.PHYSICAL_NETWORK: physical_network}
with mock.patch(
'vmware_nsxlib.v3.policy_resources.NsxPolicyTransportZoneApi.'
'get_transport_type',
return_value=nsx_constants.TRANSPORT_TYPE_VLAN), \
self.network(name='flat_net',
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE,
pnet.PHYSICAL_NETWORK)) as net:
self.assertEqual('flat', net['network'].get(pnet.NETWORK_TYPE))
def test_create_provider_flat_network_with_vlan(self):
providernet_args = {pnet.NETWORK_TYPE: 'flat',
pnet.SEGMENTATION_ID: 11}
with mock.patch(
'vmware_nsxlib.v3.policy_resources.NsxPolicyTransportZoneApi.'
'get_transport_type',
return_value=nsx_constants.TRANSPORT_TYPE_VLAN):
result = self._create_network(fmt='json', name='bad_flat_net',
admin_state_up=True,
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE,
pnet.SEGMENTATION_ID))
data = self.deserialize('json', result)
# should fail
self.assertEqual('InvalidInput', data['NeutronError']['type'])
def test_create_provider_geneve_network(self):
providernet_args = {pnet.NETWORK_TYPE: 'geneve'}
with mock.patch('vmware_nsxlib.v3.policy_resources.'
'NsxPolicySegmentApi.create_or_overwrite',
side_effect=_return_id_key) as nsx_create, \
mock.patch('vmware_nsxlib.v3.policy_resources.NsxPolicySegmentApi.'
'delete') as nsx_delete, \
self.network(name='geneve_net',
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE, )) as net:
self.assertEqual('geneve', net['network'].get(pnet.NETWORK_TYPE))
# make sure the network is created at the backend
nsx_create.assert_called_once()
# Delete the network and make sure it is deleted from the backend
req = self.new_delete_request('networks', net['network']['id'])
res = req.get_response(self.api)
self.assertEqual(exc.HTTPNoContent.code, res.status_int)
nsx_delete.assert_called_once()
def test_create_provider_geneve_network_with_physical_net(self):
physical_network = DEFAULT_TIER0_ROUTER_UUID
providernet_args = {pnet.NETWORK_TYPE: 'geneve',
pnet.PHYSICAL_NETWORK: physical_network}
with mock.patch(
'vmware_nsxlib.v3.policy_resources.NsxPolicyTransportZoneApi.'
'get_transport_type',
return_value=nsx_constants.TRANSPORT_TYPE_OVERLAY),\
self.network(name='geneve_net',
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE, )) as net:
self.assertEqual('geneve', net['network'].get(pnet.NETWORK_TYPE))
def test_create_provider_geneve_network_with_vlan(self):
providernet_args = {pnet.NETWORK_TYPE: 'geneve',
pnet.SEGMENTATION_ID: 11}
with mock.patch(
'vmware_nsxlib.v3.policy_resources.NsxPolicyTransportZoneApi.'
'get_transport_type',
return_value=nsx_constants.TRANSPORT_TYPE_OVERLAY):
result = self._create_network(fmt='json', name='bad_geneve_net',
admin_state_up=True,
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE,
pnet.SEGMENTATION_ID))
data = self.deserialize('json', result)
# should fail
self.assertEqual('InvalidInput', data['NeutronError']['type'])
def test_create_provider_vlan_network(self):
providernet_args = {pnet.NETWORK_TYPE: 'vlan',
pnet.SEGMENTATION_ID: 11}
with mock.patch('vmware_nsxlib.v3.policy_resources.'
'NsxPolicySegmentApi.create_or_overwrite',
side_effect=_return_id_key) as nsx_create, \
mock.patch('vmware_nsxlib.v3.policy_resources.NsxPolicySegmentApi.'
'delete') as nsx_delete, \
mock.patch('vmware_nsxlib.v3.policy_resources.'
'NsxPolicyTransportZoneApi.get_transport_type',
return_value=nsx_constants.TRANSPORT_TYPE_VLAN), \
self.network(name='vlan_net',
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE,
pnet.SEGMENTATION_ID)) as net:
self.assertEqual('vlan', net['network'].get(pnet.NETWORK_TYPE))
# make sure the network is created at the backend
nsx_create.assert_called_once()
# Delete the network and make sure it is deleted from the backend
req = self.new_delete_request('networks', net['network']['id'])
res = req.get_response(self.api)
self.assertEqual(exc.HTTPNoContent.code, res.status_int)
nsx_delete.assert_called_once()
def test_create_provider_nsx_network(self):
physical_network = 'Fake logical switch'
providernet_args = {pnet.NETWORK_TYPE: 'nsx-net',
pnet.PHYSICAL_NETWORK: physical_network}
with mock.patch(
'vmware_nsxlib.v3.policy_resources.NsxPolicySegmentApi.'
'create_or_overwrite',
side_effect=nsxlib_exc.ResourceNotFound) as nsx_create, \
mock.patch('vmware_nsxlib.v3.policy_resources.NsxPolicySegmentApi.'
'delete') as nsx_delete, \
self.network(name='nsx_net',
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE,
pnet.PHYSICAL_NETWORK)) as net:
self.assertEqual('nsx-net', net['network'].get(pnet.NETWORK_TYPE))
self.assertEqual(physical_network,
net['network'].get(pnet.PHYSICAL_NETWORK))
# make sure the network is NOT created at the backend
nsx_create.assert_not_called()
# Delete the network. It should NOT deleted from the backend
req = self.new_delete_request('networks', net['network']['id'])
res = req.get_response(self.api)
self.assertEqual(exc.HTTPNoContent.code, res.status_int)
nsx_delete.assert_not_called()
def test_create_provider_bad_nsx_network(self):
physical_network = 'Bad logical switch'
providernet_args = {pnet.NETWORK_TYPE: 'nsx-net',
pnet.PHYSICAL_NETWORK: physical_network}
with mock.patch(
"vmware_nsxlib.v3.policy_resources.NsxPolicySegmentApi.get",
side_effect=nsxlib_exc.ResourceNotFound):
result = self._create_network(fmt='json', name='bad_nsx_net',
admin_state_up=True,
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE,
pnet.PHYSICAL_NETWORK))
data = self.deserialize('json', result)
# should fail
self.assertEqual('InvalidInput', data['NeutronError']['type'])
def test_create_transparent_vlan_network(self):
providernet_args = {vlan_apidef.VLANTRANSPARENT: True}
with mock.patch('vmware_nsxlib.v3.policy_resources.'
'NsxPolicyTransportZoneApi.get_transport_type',
return_value=nsx_constants.TRANSPORT_TYPE_OVERLAY), \
self.network(name='vt_net',
providernet_args=providernet_args,
arg_list=(vlan_apidef.VLANTRANSPARENT, )) as net:
self.assertTrue(net['network'].get(vlan_apidef.VLANTRANSPARENT))
def test_create_provider_vlan_network_with_transparent(self):
providernet_args = {pnet.NETWORK_TYPE: 'vlan',
vlan_apidef.VLANTRANSPARENT: True}
with mock.patch('vmware_nsxlib.v3.policy_resources.'
'NsxPolicyTransportZoneApi.get_transport_type',
return_value=nsx_constants.TRANSPORT_TYPE_VLAN):
result = self._create_network(fmt='json', name='badvlan_net',
admin_state_up=True,
providernet_args=providernet_args,
arg_list=(
pnet.NETWORK_TYPE,
pnet.SEGMENTATION_ID,
vlan_apidef.VLANTRANSPARENT))
data = self.deserialize('json', result)
self.assertEqual('vlan', data['network'].get(pnet.NETWORK_TYPE))
def test_network_update_external_failure(self):
data = {'network': {'name': 'net1',
'router:external': 'True',
'tenant_id': 'tenant_one',
'provider:physical_network': 'stam'}}
network_req = self.new_create_request('networks', data)
network = self.deserialize(self.fmt,
network_req.get_response(self.api))
ext_net_id = network['network']['id']
# should fail to update the network to non-external
args = {'network': {'router:external': 'False'}}
req = self.new_update_request('networks', args,
ext_net_id, fmt='json')
res = self.deserialize('json', req.get_response(self.api))
self.assertEqual('InvalidInput',
res['NeutronError']['type'])
class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
NsxPPluginTestCaseMixin):
@ -147,18 +407,125 @@ class NsxPTestSecurityGroup(NsxPPluginTestCaseMixin,
for k, v, in keys:
self.assertEqual(rule['security_group_rule'][k], v)
def _test_create_direct_network(self, vlan_id=0):
net_type = vlan_id and 'vlan' or 'flat'
name = 'direct_net'
providernet_args = {pnet.NETWORK_TYPE: net_type,
pnet.PHYSICAL_NETWORK: 'tzuuid'}
if vlan_id:
providernet_args[pnet.SEGMENTATION_ID] = vlan_id
mock_tt = mock.patch('vmware_nsxlib.v3'
'.policy_resources.NsxPolicyTransportZoneApi'
'.get_transport_type',
return_value=nsx_constants.TRANSPORT_TYPE_VLAN)
mock_tt.start()
return self.network(name=name,
providernet_args=providernet_args,
arg_list=(pnet.NETWORK_TYPE,
pnet.PHYSICAL_NETWORK,
pnet.SEGMENTATION_ID))
def _test_create_port_vnic_direct(self, vlan_id):
with mock.patch('vmware_nsxlib.v3.policy_resources.'
'NsxPolicyTransportZoneApi.get_transport_type',
return_value=nsx_constants.TRANSPORT_TYPE_VLAN),\
self._test_create_direct_network(vlan_id=vlan_id) as network:
# Check that port security conflicts
kwargs = {portbindings.VNIC_TYPE: portbindings.VNIC_DIRECT,
psec.PORTSECURITY: True}
net_id = network['network']['id']
res = self._create_port(self.fmt, net_id=net_id,
arg_list=(portbindings.VNIC_TYPE,
psec.PORTSECURITY),
**kwargs)
self.assertEqual(res.status_int, exc.HTTPBadRequest.code)
# Check that security group conflicts
kwargs = {portbindings.VNIC_TYPE: portbindings.VNIC_DIRECT,
'security_groups': [
'4cd70774-cc67-4a87-9b39-7d1db38eb087'],
psec.PORTSECURITY: False}
net_id = network['network']['id']
res = self._create_port(self.fmt, net_id=net_id,
arg_list=(portbindings.VNIC_TYPE,
psec.PORTSECURITY),
**kwargs)
self.assertEqual(res.status_int, exc.HTTPBadRequest.code)
# All is kosher so we can create the port
kwargs = {portbindings.VNIC_TYPE: portbindings.VNIC_DIRECT}
net_id = network['network']['id']
res = self._create_port(self.fmt, net_id=net_id,
arg_list=(portbindings.VNIC_TYPE,),
**kwargs)
port = self.deserialize('json', res)
self.assertEqual("direct", port['port'][portbindings.VNIC_TYPE])
self.assertEqual("dvs", port['port'][portbindings.VIF_TYPE])
self.assertEqual(
vlan_id,
port['port'][portbindings.VIF_DETAILS]['segmentation-id'])
# try to get the same port
req = self.new_show_request('ports', port['port']['id'], self.fmt)
sport = self.deserialize(self.fmt, req.get_response(self.api))
self.assertEqual("dvs", sport['port'][portbindings.VIF_TYPE])
self.assertEqual("direct", sport['port'][portbindings.VNIC_TYPE])
self.assertEqual(
vlan_id,
sport['port'][portbindings.VIF_DETAILS]['segmentation-id'])
def test_create_port_vnic_direct_flat(self):
self._test_create_port_vnic_direct(0)
def test_create_port_vnic_direct_vlan(self):
self._test_create_port_vnic_direct(10)
def test_create_port_vnic_direct_invalid_network(self):
with self.network(name='not vlan/flat') as net:
kwargs = {portbindings.VNIC_TYPE: portbindings.VNIC_DIRECT,
psec.PORTSECURITY: False}
net_id = net['network']['id']
res = self._create_port(self.fmt, net_id=net_id,
arg_list=(portbindings.VNIC_TYPE,
psec.PORTSECURITY),
**kwargs)
self.assertEqual(exc.HTTPBadRequest.code, res.status_int)
def test_update_vnic_direct(self):
with self._test_create_direct_network(vlan_id=7) as network:
with self.subnet(network=network) as subnet:
with self.port(subnet=subnet) as port:
# need to do two updates as the update for port security
# disabled requires that it can only change 2 items
data = {'port': {psec.PORTSECURITY: False,
'security_groups': []}}
req = self.new_update_request('ports',
data, port['port']['id'])
res = self.deserialize('json', req.get_response(self.api))
self.assertEqual(portbindings.VNIC_NORMAL,
res['port'][portbindings.VNIC_TYPE])
data = {'port': {portbindings.VNIC_TYPE:
portbindings.VNIC_DIRECT}}
req = self.new_update_request('ports',
data, port['port']['id'])
res = self.deserialize('json', req.get_response(self.api))
self.assertEqual(portbindings.VNIC_DIRECT,
res['port'][portbindings.VNIC_TYPE])
def test_port_invalid_vnic_type(self):
with self._test_create_direct_network(vlan_id=7) as network:
kwargs = {portbindings.VNIC_TYPE: 'invalid',
psec.PORTSECURITY: False}
net_id = network['network']['id']
res = self._create_port(self.fmt, net_id=net_id,
arg_list=(portbindings.VNIC_TYPE,
psec.PORTSECURITY),
**kwargs)
self.assertEqual(res.status_int, exc.HTTPBadRequest.code)
# Temporarily skip all port related tests until the plugin supports it
def test_create_port_with_no_security_groups(self):
self.skipTest('Temporarily not supported')
def test_create_delete_security_group_port_in_use(self):
self.skipTest('Temporarily not supported')
def test_create_port_with_multiple_security_groups(self):
self.skipTest('Temporarily not supported')
def test_list_ports_security_group(self):
self.skipTest('Temporarily not supported')
def test_update_port_with_security_group(self):
self.skipTest('Temporarily not supported')