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:
parent
e3e0dbca96
commit
00134a8e31
@ -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"
|
@ -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")),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user