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.
|
# Begin list of exclusions.
|
||||||
#r="^(?!.*)"
|
#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"
|
export DEVSTACK_GATE_TEMPEST_REGEX="$r"
|
@ -344,6 +344,12 @@ nsx_v3_and_p = [
|
|||||||
default=False,
|
default=False,
|
||||||
help=_("(Optional) Indicates whether distributed-firewall "
|
help=_("(Optional) Indicates whether distributed-firewall "
|
||||||
"security-groups rules are logged.")),
|
"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 + [
|
nsx_v3_opts = nsx_v3_and_p + [
|
||||||
@ -456,12 +462,6 @@ nsx_v3_opts = nsx_v3_and_p + [
|
|||||||
default=False,
|
default=False,
|
||||||
help=_("(Optional) Indicates whether ENS transport zones can "
|
help=_("(Optional) Indicates whether ENS transport zones can "
|
||||||
"be used")),
|
"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',
|
cfg.BoolOpt('disable_port_security_for_ens',
|
||||||
default=False,
|
default=False,
|
||||||
help=_("When True, port security will be set to False for "
|
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 "
|
"configuring external networks. If only one tier0 "
|
||||||
" router is present on backend, it will be assumed "
|
" router is present on backend, it will be assumed "
|
||||||
"as default unless this value is provided")),
|
"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'
|
PORTGROUP = 'portgroup'
|
||||||
|
|
||||||
|
|
||||||
# Allowed network types for the NSXv3 Plugin
|
# Allowed network types for the NSXv3 and NSX-Policy Plugin
|
||||||
class NsxV3NetworkTypes(object):
|
class NsxV3NetworkTypes(object):
|
||||||
"""Allowed provider network types for the NSXv3 Plugin."""
|
"""Allowed provider network types for the NSXv3 Plugin."""
|
||||||
FLAT = 'flat'
|
FLAT = 'flat'
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from neutron.db import _resource_extend as resource_extend
|
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.db import models_v2
|
||||||
from neutron_lib.api.definitions import address_scope as ext_address_scope
|
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 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 network as net_def
|
||||||
from neutron_lib.api.definitions import port as port_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.definitions import subnet as subnet_def
|
||||||
from neutron_lib.api import validators
|
from neutron_lib.api import validators
|
||||||
from neutron_lib.api.validators import availability_zone as az_validator
|
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.db import api as db_api
|
||||||
from neutron_lib import exceptions as n_exc
|
from neutron_lib import exceptions as n_exc
|
||||||
from neutron_lib.plugins import directory
|
from neutron_lib.plugins import directory
|
||||||
from neutron_lib.services.qos import constants as qos_consts
|
from neutron_lib.utils import net as nl_net_utils
|
||||||
from neutron_lib.utils import net
|
|
||||||
|
|
||||||
from vmware_nsx._i18n import _
|
from vmware_nsx._i18n import _
|
||||||
from vmware_nsx.common import exceptions as nsx_exc
|
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.qos.common import utils as qos_com_utils
|
||||||
from vmware_nsx.services.vpnaas.nsxv3 import ipsec_utils
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -55,7 +48,7 @@ LOG = logging.getLogger(__name__)
|
|||||||
@resource_extend.has_resource_extenders
|
@resource_extend.has_resource_extenders
|
||||||
class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
|
class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
|
||||||
address_scope_db.AddressScopeDbMixin):
|
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
|
@property
|
||||||
def plugin_type(self):
|
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
|
cannot add multiple static dhcp bindings with the same port
|
||||||
"""
|
"""
|
||||||
if (device_owner and
|
if (device_owner and
|
||||||
net.is_port_trusted({'device_owner': device_owner})):
|
nl_net_utils.is_port_trusted({'device_owner': device_owner})):
|
||||||
return
|
return
|
||||||
|
|
||||||
if validators.is_attr_set(fixed_ip_list) and len(fixed_ip_list) > 1:
|
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:
|
if qos_policy_id:
|
||||||
qos_com_utils.validate_policy_accessable(context, qos_policy_id)
|
qos_com_utils.validate_policy_accessable(context, qos_policy_id)
|
||||||
|
|
||||||
def _validate_create_network(self, context, net_data):
|
def _get_interface_network(self, context, interface_info):
|
||||||
"""Validate the parameters of the new network to be created
|
is_port, is_sub = self._validate_interface_info(interface_info)
|
||||||
|
if is_port:
|
||||||
This method includes general validations that does not depend on
|
net_id = self.get_port(context,
|
||||||
provider attributes, or plugin specific configurations
|
interface_info['port_id'])['network_id']
|
||||||
"""
|
elif is_sub:
|
||||||
external = net_data.get(extnet_apidef.EXTERNAL)
|
net_id = self.get_subnet(context,
|
||||||
is_external_net = validators.is_attr_set(external) and external
|
interface_info['subnet_id'])['network_id']
|
||||||
with_qos = validators.is_attr_set(
|
return net_id
|
||||||
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 _process_extra_attr_router_create(self, context, router_db, r):
|
def _process_extra_attr_router_create(self, context, router_db, r):
|
||||||
for extra_attr in l3_attrs_db.get_attr_info().keys():
|
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,
|
self.set_extra_attr_value(context, router_db,
|
||||||
extra_attr, r[extra_attr])
|
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):
|
def get_housekeeper(self, context, name, fields=None):
|
||||||
# run the job in readonly mode and get the results
|
# run the job in readonly mode and get the results
|
||||||
self.housekeeper.run(context, name, readonly=True)
|
self.housekeeper.run(context, name, readonly=True)
|
||||||
|
@ -14,28 +14,57 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
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.extensions import securitygroup as ext_sg
|
||||||
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
|
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 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.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 allowedaddresspairs as addr_exc
|
||||||
from neutron_lib.exceptions import port_security as psec_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 exceptions as nsx_exc
|
||||||
|
from vmware_nsx.common import nsx_constants
|
||||||
from vmware_nsx.common import utils
|
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 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 providersecuritygroup as provider_sg
|
||||||
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as sg_prefix
|
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as sg_prefix
|
||||||
from vmware_nsx.plugins.common import plugin
|
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__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class NsxPluginV3Base(plugin.NsxPluginBase,
|
class NsxPluginV3Base(plugin.NsxPluginBase,
|
||||||
extended_sec.ExtendedSecurityGroupPropertiesMixin):
|
extended_sec.ExtendedSecurityGroupPropertiesMixin,
|
||||||
"""Common methods for NSX-V3 plugins"""
|
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):
|
def _get_interface_network(self, context, interface_info):
|
||||||
is_port, is_sub = self._validate_interface_info(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] = (
|
port_data[ext_sg.SECURITYGROUPS] = (
|
||||||
self._get_security_groups_on_port(context, port))
|
self._get_security_groups_on_port(context, port))
|
||||||
return port_security, has_ip, sgids, psgids
|
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 external_net
|
||||||
from neutron_lib.api.definitions import l3 as l3_apidef
|
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 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 faults
|
||||||
from neutron_lib.api import validators
|
from neutron_lib.api import validators
|
||||||
from neutron_lib.callbacks import events
|
from neutron_lib.callbacks import events
|
||||||
@ -129,7 +130,8 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
"subnet_allocation",
|
"subnet_allocation",
|
||||||
"security-group-logging",
|
"security-group-logging",
|
||||||
"provider-security-group",
|
"provider-security-group",
|
||||||
"port-security-groups-filtering"]
|
"port-security-groups-filtering",
|
||||||
|
"vlan-transparent"]
|
||||||
|
|
||||||
@resource_registry.tracked_resources(
|
@resource_registry.tracked_resources(
|
||||||
network=models_v2.Network,
|
network=models_v2.Network,
|
||||||
@ -148,6 +150,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
extension_drivers = cfg.CONF.nsx_extension_drivers
|
extension_drivers = cfg.CONF.nsx_extension_drivers
|
||||||
self._extension_manager = managers.ExtensionManager(
|
self._extension_manager = managers.ExtensionManager(
|
||||||
extension_drivers=extension_drivers)
|
extension_drivers=extension_drivers)
|
||||||
|
self.cfg_group = 'nsx_p' # group name for nsx_p section in nsx.ini
|
||||||
super(NsxPolicyPlugin, self).__init__()
|
super(NsxPolicyPlugin, self).__init__()
|
||||||
# Bind the dummy L3 notifications
|
# Bind the dummy L3 notifications
|
||||||
self.l3_rpc_notifier = l3_rpc_agent_api.L3NotifyAPI()
|
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)
|
nsxlib_utils.set_inject_headers_callback(v3_utils.inject_headers)
|
||||||
self._validate_nsx_policy_version()
|
self._validate_nsx_policy_version()
|
||||||
|
|
||||||
self.cfg_group = 'nsx_p' # group name for nsx_p section in nsx.ini
|
|
||||||
|
|
||||||
self._init_default_config()
|
self._init_default_config()
|
||||||
self._prepare_default_rules()
|
self._prepare_default_rules()
|
||||||
|
|
||||||
@ -173,11 +174,14 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
|
|
||||||
# NOTE(annak): we may need to generalize this for API calls
|
# NOTE(annak): we may need to generalize this for API calls
|
||||||
# requiring path ids
|
# 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 name_or_id:
|
||||||
# If not specified, the system will auto-configure
|
# If not specified, the system will auto-configure
|
||||||
# in case only single resource is present
|
# in case only single resource is present
|
||||||
resources = resource_api.list()
|
resources = resource_api.list()
|
||||||
|
if filter_list_results:
|
||||||
|
resources = filter_list_results(resources)
|
||||||
if len(resources) == 1:
|
if len(resources) == 1:
|
||||||
return resources[0]['id']
|
return resources[0]['id']
|
||||||
else:
|
else:
|
||||||
@ -195,6 +199,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def _init_default_config(self):
|
def _init_default_config(self):
|
||||||
|
# Default Tier0 router
|
||||||
self.default_tier0_router = self._init_default_resource(
|
self.default_tier0_router = self._init_default_resource(
|
||||||
self.nsxpolicy.tier0,
|
self.nsxpolicy.tier0,
|
||||||
cfg.CONF.nsx_p.default_tier0_router)
|
cfg.CONF.nsx_p.default_tier0_router)
|
||||||
@ -203,6 +208,24 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
raise cfg.RequiredOptError("default_tier0_router",
|
raise cfg.RequiredOptError("default_tier0_router",
|
||||||
group=cfg.OptGroup('nsx_p'))
|
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):
|
def _validate_nsx_policy_version(self):
|
||||||
self._nsx_version = self.nsxpolicy.get_version()
|
self._nsx_version = self.nsxpolicy.get_version()
|
||||||
LOG.info("NSX Version: %s", self._nsx_version)
|
LOG.info("NSX Version: %s", self._nsx_version)
|
||||||
@ -252,48 +275,107 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
webob.exc.HTTPBadRequest,
|
webob.exc.HTTPBadRequest,
|
||||||
})
|
})
|
||||||
|
|
||||||
def _create_network_on_backend(self, context, net_data):
|
def _create_network_on_backend(self, context, net_data,
|
||||||
# TODO(annak): provider network
|
transparent_vlan,
|
||||||
|
provider_data):
|
||||||
net_data['id'] = net_data.get('id') or uuidutils.generate_uuid()
|
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_name = utils.get_name_and_uuid(net_data['name'] or 'network',
|
||||||
net_data['id'])
|
net_data['id'])
|
||||||
tags = self.nsxpolicy.build_v3_api_version_project_tag(
|
tags = self.nsxpolicy.build_v3_tags_payload(
|
||||||
context.tenant_name)
|
net_data, resource_type='os-neutron-net-id',
|
||||||
|
project_name=context.tenant_name)
|
||||||
|
|
||||||
# TODO(annak): admin state config is missing on policy
|
# TODO(annak): admin state config is missing on policy
|
||||||
# should we not create networks that are down?
|
# should we not create networks that are down?
|
||||||
# alternative - configure status on manager for time being
|
# 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(
|
self.nsxpolicy.segment.create_or_overwrite(
|
||||||
net_name,
|
net_name,
|
||||||
segment_id=net_data['id'],
|
segment_id=net_data['id'],
|
||||||
description=net_data.get('description'),
|
description=net_data.get('description'),
|
||||||
|
vlan_ids=vlan_ids,
|
||||||
|
transport_zone_id=provider_data['physical_net'],
|
||||||
tags=tags)
|
tags=tags)
|
||||||
|
|
||||||
def _validate_external_net_create(self, net_data):
|
def _tier0_validator(self, tier0_uuid):
|
||||||
#TODO(asarfaty): implement
|
# Fail of the tier0 uuid was not found on the BSX
|
||||||
pass
|
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):
|
def create_network(self, context, network):
|
||||||
net_data = network['network']
|
net_data = network['network']
|
||||||
|
|
||||||
#TODO(asarfaty): network validation
|
#TODO(asarfaty): add ENS support
|
||||||
external = net_data.get(external_net.EXTERNAL)
|
external = net_data.get(external_net.EXTERNAL)
|
||||||
is_external_net = validators.is_attr_set(external) and external
|
is_external_net = validators.is_attr_set(external) and external
|
||||||
tenant_id = net_data['tenant_id']
|
tenant_id = net_data['tenant_id']
|
||||||
|
|
||||||
self._ensure_default_security_group(context, 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:
|
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
|
# Create the neutron network
|
||||||
with db_api.CONTEXT_WRITER.using(context):
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
# Create network in Neutron
|
# Create network in Neutron
|
||||||
created_net = super(NsxPolicyPlugin, self).create_network(
|
created_net = super(NsxPolicyPlugin, self).create_network(
|
||||||
context, network)
|
context, network)
|
||||||
|
super(NsxPolicyPlugin, self).update_network(context,
|
||||||
|
created_net['id'],
|
||||||
|
{'network': {'vlan_transparent': vlt}})
|
||||||
self._extension_manager.process_create_network(
|
self._extension_manager.process_create_network(
|
||||||
context, net_data, created_net)
|
context, net_data, created_net)
|
||||||
if psec.PORTSECURITY not in net_data:
|
if psec.PORTSECURITY not in net_data:
|
||||||
@ -302,10 +384,21 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
context, net_data, created_net)
|
context, net_data, created_net)
|
||||||
self._process_l3_create(context, created_net, net_data)
|
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
|
# Create the backend NSX network
|
||||||
if not is_external_net:
|
if is_backend_network:
|
||||||
try:
|
try:
|
||||||
self._create_network_on_backend(context, created_net)
|
self._create_network_on_backend(
|
||||||
|
context, created_net, vlt, provider_data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception("Failed to create NSX network network: %s", e)
|
LOG.exception("Failed to create NSX network network: %s", e)
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
@ -316,24 +409,30 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
# latest db model for the extension functions
|
# latest db model for the extension functions
|
||||||
net_model = self._get_network(context, created_net['id'])
|
net_model = self._get_network(context, created_net['id'])
|
||||||
resource_extend.apply_funcs('networks', created_net, net_model)
|
resource_extend.apply_funcs('networks', created_net, net_model)
|
||||||
|
|
||||||
return created_net
|
return created_net
|
||||||
|
|
||||||
def delete_network(self, context, network_id):
|
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):
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
self._process_l3_delete(context, network_id)
|
self._process_l3_delete(context, network_id)
|
||||||
super(NsxPolicyPlugin, self).delete_network(
|
super(NsxPolicyPlugin, self).delete_network(
|
||||||
context, network_id)
|
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)
|
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):
|
def update_network(self, context, network_id, network):
|
||||||
original_net = super(NsxPolicyPlugin, self).get_network(context, id)
|
original_net = super(NsxPolicyPlugin, self).get_network(
|
||||||
|
context, network_id)
|
||||||
net_data = network['network']
|
net_data = network['network']
|
||||||
LOG.debug("Updating network %s %s->%s", id, original_net, net_data)
|
|
||||||
# Neutron does not support changing provider network values
|
# Neutron does not support changing provider network values
|
||||||
providernet._raise_if_updates_provider_attributes(net_data)
|
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
|
# Do not support changing external/non-external networks
|
||||||
if (external_net.EXTERNAL in net_data and
|
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")
|
err_msg = _("Cannot change the router:external flag of a network")
|
||||||
raise n_exc.InvalidInput(error_message=err_msg)
|
raise n_exc.InvalidInput(error_message=err_msg)
|
||||||
|
|
||||||
updated_net = super(NsxPolicyPlugin, self).update_network(context, id,
|
# Update the neutron network
|
||||||
network)
|
updated_net = super(NsxPolicyPlugin, self).update_network(
|
||||||
|
context, network_id, network)
|
||||||
self._extension_manager.process_update_network(context, net_data,
|
self._extension_manager.process_update_network(context, net_data,
|
||||||
updated_net)
|
updated_net)
|
||||||
self._process_l3_update(context, updated_net, network['network'])
|
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
|
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):
|
def create_subnet(self, context, subnet):
|
||||||
self._validate_host_routes_input(subnet)
|
self._validate_host_routes_input(subnet)
|
||||||
created_subnet = super(
|
created_subnet = super(
|
||||||
@ -423,6 +515,20 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
|
|
||||||
return address_bindings
|
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):
|
def _build_port_tags(self, port_data):
|
||||||
sec_groups = port_data.get(ext_sg.SECURITYGROUPS, [])
|
sec_groups = port_data.get(ext_sg.SECURITYGROUPS, [])
|
||||||
sec_groups += port_data.get(provider_sg.PROVIDER_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(
|
tags.extend(self.nsxpolicy.build_v3_api_version_project_tag(
|
||||||
context.tenant_name))
|
context.tenant_name))
|
||||||
|
|
||||||
|
segment_id = self._get_network_nsx_id(
|
||||||
|
context, port_data['network_id'])
|
||||||
self.nsxpolicy.segment_port.create_or_overwrite(
|
self.nsxpolicy.segment_port.create_or_overwrite(
|
||||||
name,
|
name, segment_id,
|
||||||
port_data['network_id'],
|
|
||||||
port_id=port_data['id'],
|
port_id=port_data['id'],
|
||||||
description=port_data.get('description'),
|
description=port_data.get('description'),
|
||||||
address_bindings=address_bindings,
|
address_bindings=address_bindings,
|
||||||
vif_id=vif_id,
|
vif_id=vif_id,
|
||||||
tags=tags)
|
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):
|
def base_create_port(self, context, port):
|
||||||
neutron_db = super(NsxPolicyPlugin, self).create_port(context, port)
|
neutron_db = super(NsxPolicyPlugin, self).create_port(context, port)
|
||||||
self._extension_manager.process_create_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', []),
|
self._validate_max_ips_per_port(port_data.get('fixed_ips', []),
|
||||||
port_data.get('device_owner'))
|
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):
|
with db_api.CONTEXT_WRITER.using(context):
|
||||||
is_external_net = self._network_is_external(
|
is_external_net = self._network_is_external(
|
||||||
context, port_data['network_id'])
|
context, port_data['network_id'])
|
||||||
@ -489,10 +594,14 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
neutron_db = self.base_create_port(context, port)
|
neutron_db = self.base_create_port(context, port)
|
||||||
port["port"].update(neutron_db)
|
port["port"].update(neutron_db)
|
||||||
|
|
||||||
|
self.fix_direct_vnic_port_sec(direct_vnic_type, port_data)
|
||||||
(is_psec_on, has_ip, sgids, psgids) = (
|
(is_psec_on, has_ip, sgids, psgids) = (
|
||||||
self._create_port_preprocess_security(context, port,
|
self._create_port_preprocess_security(context, port,
|
||||||
port_data, neutron_db,
|
port_data, neutron_db,
|
||||||
False))
|
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_security_group(context, port_data, sgids)
|
||||||
self._process_port_create_provider_security_group(
|
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 '
|
LOG.error('Failed to create port %(id)s on NSX '
|
||||||
'backend. Exception: %(e)s',
|
'backend. Exception: %(e)s',
|
||||||
{'id': neutron_db['id'], 'e': e})
|
{'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
|
# this extra lookup is necessary to get the
|
||||||
# latest db model for the extension functions
|
# latest db model for the extension functions
|
||||||
port_model = self._get_port(context, port_data['id'])
|
port_model = self._get_port(context, port_data['id'])
|
||||||
resource_extend.apply_funcs('ports', port_data, port_model)
|
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}
|
kwargs = {'context': context, 'port': neutron_db}
|
||||||
registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)
|
registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)
|
||||||
return neutron_db
|
return port_data
|
||||||
|
|
||||||
def delete_port(self, context, port_id,
|
def delete_port(self, context, port_id,
|
||||||
l3_port_check=True, l2gw_port_check=True,
|
l3_port_check=True, l2gw_port_check=True,
|
||||||
force_delete_dhcp=False,
|
force_delete_dhcp=False,
|
||||||
force_delete_vpn=False):
|
force_delete_vpn=False):
|
||||||
port_data = self.get_port(context, port_id)
|
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']):
|
if not self._network_is_external(context, port_data['network_id']):
|
||||||
try:
|
try:
|
||||||
self.nsxpolicy.segment_port.delete(
|
self.nsxpolicy.segment_port.delete(segment_id, port_data['id'])
|
||||||
port_data['network_id'], port_data['id'])
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
LOG.error("Failed to delete port %(id)s on NSX backend "
|
LOG.error("Failed to delete port %(id)s on NSX backend "
|
||||||
"due to %(e)s",
|
"due to %(e)s",
|
||||||
@ -593,6 +707,8 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
if 'id' in port:
|
if 'id' in port:
|
||||||
port_model = self._get_port(context, port['id'])
|
port_model = self._get_port(context, port['id'])
|
||||||
resource_extend.apply_funcs('ports', port, port_model)
|
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)
|
return db_utils.resource_fields(port, fields)
|
||||||
|
|
||||||
def get_ports(self, context, filters=None, fields=None,
|
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'])
|
"process, and is being skipped", port['id'])
|
||||||
ports.remove(port)
|
ports.remove(port)
|
||||||
continue
|
continue
|
||||||
|
self._extend_nsx_port_dict_binding(context, port)
|
||||||
|
self._remove_provider_security_groups_from_list(port)
|
||||||
return (ports if not fields else
|
return (ports if not fields else
|
||||||
[db_utils.resource_fields(port, fields) for port in ports])
|
[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)
|
self._validate_interface_address_scope(context, router_db, info)
|
||||||
subnet = self.get_subnet(context, info['subnet_ids'][0])
|
subnet = self.get_subnet(context, info['subnet_ids'][0])
|
||||||
|
|
||||||
|
segment_id = self._get_network_nsx_id(context, network_id)
|
||||||
# TODO(annak): Validate TZ
|
# TODO(annak): Validate TZ
|
||||||
try:
|
try:
|
||||||
# This is always an overwrite call
|
# This is always an overwrite call
|
||||||
# NOTE: Connecting network to multiple routers is not supported
|
# 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)
|
tier1_id=router_id)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
@ -745,9 +864,6 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
fip_id, router_id, port_id)
|
fip_id, router_id, port_id)
|
||||||
|
|
||||||
if router_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
|
#TODO(asarfaty): Update the NSX router
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -764,9 +880,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
router_id = new_fip['router_id']
|
router_id = new_fip['router_id']
|
||||||
new_port_id = new_fip['port_id']
|
new_port_id = new_fip['port_id']
|
||||||
|
|
||||||
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
|
if router_id:
|
||||||
router_id)
|
|
||||||
if nsx_router_id:
|
|
||||||
#TODO(asarfaty): Update the NSX router
|
#TODO(asarfaty): Update the NSX router
|
||||||
LOG.debug("Updating floating IP %s. Router %s, Port %s "
|
LOG.debug("Updating floating IP %s. Router %s, Port %s "
|
||||||
"(old port %s)",
|
"(old port %s)",
|
||||||
@ -784,9 +898,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
for fip_db in fip_dbs:
|
for fip_db in fip_dbs:
|
||||||
if not fip_db.router_id:
|
if not fip_db.router_id:
|
||||||
continue
|
continue
|
||||||
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
|
if fip_db.router_id:
|
||||||
fip_db.router_id)
|
|
||||||
if nsx_router_id:
|
|
||||||
# TODO(asarfaty): Update the NSX logical router
|
# TODO(asarfaty): Update the NSX logical router
|
||||||
pass
|
pass
|
||||||
self.update_floatingip_status(context, fip_db.id,
|
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.extensions import securitygroup as ext_sg
|
||||||
from neutron.quota import resource_registry
|
from neutron.quota import resource_registry
|
||||||
from neutron_lib.api.definitions import extra_dhcp_opt as ext_edo
|
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 provider_net as pnet
|
||||||
from neutron_lib.api.definitions import vlantransparent as vlan_apidef
|
from neutron_lib.api.definitions import vlantransparent as vlan_apidef
|
||||||
from neutron_lib.api import validators
|
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 constants as const
|
||||||
from neutron_lib import context as q_context
|
from neutron_lib import context as q_context
|
||||||
from neutron_lib import exceptions as n_exc
|
from neutron_lib import exceptions as n_exc
|
||||||
from neutron_lib.plugins import utils as plugin_utils
|
|
||||||
from neutron_lib.utils import helpers
|
from neutron_lib.utils import helpers
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_db import exception as db_exc
|
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 excutils
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
from six import moves
|
|
||||||
from sqlalchemy import exc as sql_exc
|
from sqlalchemy import exc as sql_exc
|
||||||
import webob.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 db as nsx_db
|
||||||
from vmware_nsx.db import extended_security_group_rule as extend_sg_rule
|
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 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.dhcp_meta import rpc as nsx_rpc
|
||||||
from vmware_nsx.extensions import advancedserviceproviders as as_providers
|
from vmware_nsx.extensions import advancedserviceproviders as as_providers
|
||||||
from vmware_nsx.extensions import housekeeper as hk_ext
|
from vmware_nsx.extensions import housekeeper as hk_ext
|
||||||
@ -168,7 +164,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
extraroute_db.ExtraRoute_db_mixin,
|
extraroute_db.ExtraRoute_db_mixin,
|
||||||
router_az_db.RouterAvailabilityZoneMixin,
|
router_az_db.RouterAvailabilityZoneMixin,
|
||||||
l3_gwmode_db.L3_NAT_db_mixin,
|
l3_gwmode_db.L3_NAT_db_mixin,
|
||||||
pbin_db.NsxPortBindingMixin,
|
|
||||||
portbindings_db.PortBindingMixin,
|
portbindings_db.PortBindingMixin,
|
||||||
portsecurity_db.PortSecurityDbMixin,
|
portsecurity_db.PortSecurityDbMixin,
|
||||||
extradhcpopt_db.ExtraDhcpOptMixin,
|
extradhcpopt_db.ExtraDhcpOptMixin,
|
||||||
@ -231,6 +226,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
extension_drivers = cfg.CONF.nsx_extension_drivers
|
extension_drivers = cfg.CONF.nsx_extension_drivers
|
||||||
self._extension_manager = managers.ExtensionManager(
|
self._extension_manager = managers.ExtensionManager(
|
||||||
extension_drivers=extension_drivers)
|
extension_drivers=extension_drivers)
|
||||||
|
self.cfg_group = 'nsx_v3' # group name for nsx_v3 section in nsx.ini
|
||||||
super(NsxV3Plugin, self).__init__()
|
super(NsxV3Plugin, self).__init__()
|
||||||
# Bind the dummy L3 notifications
|
# Bind the dummy L3 notifications
|
||||||
self.l3_rpc_notifier = l3_rpc_agent_api.L3NotifyAPI()
|
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()
|
self._nsx_version = self.nsxlib.get_version()
|
||||||
LOG.info("NSX Version: %s", self._nsx_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.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
|
# Initialize the network availability zones, which will be used only
|
||||||
# when native_dhcp_metadata is True
|
# when native_dhcp_metadata is True
|
||||||
self.init_availability_zones()
|
self.init_availability_zones()
|
||||||
@ -632,33 +625,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
for az in self.get_azs_list():
|
for az in self.get_azs_list():
|
||||||
az.translate_configured_names_to_uuids(self.nsxlib)
|
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(
|
@nsxlib_utils.retry_upon_exception(
|
||||||
Exception, max_attempts=cfg.CONF.nsx_v3.retries)
|
Exception, max_attempts=cfg.CONF.nsx_v3.retries)
|
||||||
def _init_default_section_nsgroup(self):
|
def _init_default_section_nsgroup(self):
|
||||||
@ -917,179 +883,25 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
|
|
||||||
return self.conn.consume_in_threads()
|
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):
|
def _get_edge_cluster(self, tier0_uuid):
|
||||||
self.nsxlib.router.validate_tier0(self.tier0_groups_dict, tier0_uuid)
|
self.nsxlib.router.validate_tier0(self.tier0_groups_dict, tier0_uuid)
|
||||||
tier0_info = self.tier0_groups_dict[tier0_uuid]
|
tier0_info = self.tier0_groups_dict[tier0_uuid]
|
||||||
return tier0_info['edge_cluster_uuid']
|
return tier0_info['edge_cluster_uuid']
|
||||||
|
|
||||||
def _validate_external_net_create(self, net_data, az):
|
def _allow_ens_networks(self):
|
||||||
if not validators.is_attr_set(net_data.get(pnet.PHYSICAL_NETWORK)):
|
return cfg.CONF.nsx_v3.ens_support
|
||||||
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 _create_network_at_the_backend(self, context, net_data, az,
|
def _create_network_at_the_backend(self, context, net_data, az,
|
||||||
transparent_vlan):
|
transparent_vlan):
|
||||||
provider_data = self._validate_provider_create(context, net_data, az,
|
provider_data = self._validate_provider_create(
|
||||||
transparent_vlan)
|
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()
|
neutron_net_id = net_data.get('id') or uuidutils.generate_uuid()
|
||||||
net_data['id'] = neutron_net_id
|
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
|
if (provider_data['is_provider_net'] and
|
||||||
provider_data['net_type'] == utils.NsxV3NetworkTypes.NSX_NETWORK):
|
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(
|
return (is_overlay or self.nsxlib.feature_supported(
|
||||||
nsxlib_consts.FEATURE_VLAN_ROUTER_INTERFACE)), net_type
|
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):
|
def _is_ddi_supported_on_network(self, context, network_id):
|
||||||
result, _ = self._is_ddi_supported_on_net_with_type(
|
result, _ = self._is_ddi_supported_on_net_with_type(
|
||||||
context, network_id)
|
context, network_id)
|
||||||
@ -1199,19 +1005,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
self.nsxlib.transport_zone.TRANSPORT_TYPE_OVERLAY)
|
self.nsxlib.transport_zone.TRANSPORT_TYPE_OVERLAY)
|
||||||
return False
|
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,
|
def get_subnets(self, context, filters=None, fields=None, sorts=None,
|
||||||
limit=None, marker=None, page_reverse=False):
|
limit=None, marker=None, page_reverse=False):
|
||||||
filters = filters or {}
|
filters = filters or {}
|
||||||
@ -1227,38 +1020,17 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
return super(NsxV3Plugin, self).get_subnets(
|
return super(NsxV3Plugin, self).get_subnets(
|
||||||
context, filters, fields, sorts, limit, marker, page_reverse)
|
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):
|
def _get_mdproxy_port_name(self, net_name, net_id):
|
||||||
return utils.get_name_and_uuid('%s-%s' % ('mdproxy',
|
return utils.get_name_and_uuid('%s-%s' % ('mdproxy',
|
||||||
net_name or 'network'),
|
net_name or 'network'),
|
||||||
net_id)
|
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):
|
def create_network(self, context, network):
|
||||||
net_data = network['network']
|
net_data = network['network']
|
||||||
external = net_data.get(extnet_apidef.EXTERNAL)
|
external = net_data.get(extnet_apidef.EXTERNAL)
|
||||||
@ -1283,7 +1055,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
|
|
||||||
if is_external_net:
|
if is_external_net:
|
||||||
is_provider_net, net_type, physical_net, vlan_id = (
|
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
|
nsx_net_id = None
|
||||||
is_backend_network = False
|
is_backend_network = False
|
||||||
else:
|
else:
|
||||||
@ -1389,12 +1163,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
|
|
||||||
return created_net
|
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):
|
def _ens_psec_supported(self):
|
||||||
return self.nsxlib.feature_supported(
|
return self.nsxlib.feature_supported(
|
||||||
nsxlib_consts.FEATURE_ENS_WITH_SEC)
|
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:
|
if cfg.CONF.nsx_v3.disable_port_security_for_ens:
|
||||||
# Override the port-security to False
|
# Override the port-security to False
|
||||||
if net_data[psec.PORTSECURITY]:
|
if net_data[psec.PORTSECURITY]:
|
||||||
LOG.warning("Disabling port security for network %s",
|
LOG.warning("Disabling port security for bew network")
|
||||||
net_data['id'])
|
|
||||||
# Set the port security to False
|
# Set the port security to False
|
||||||
net_data[psec.PORTSECURITY] = 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)
|
is_ens_net = self._is_ens_tz_net(context, id)
|
||||||
|
|
||||||
# Validate the updated parameters
|
# 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
|
# add some plugin specific validations
|
||||||
if is_ens_net:
|
if is_ens_net:
|
||||||
self._assert_on_ens_with_qos(net_data)
|
self._assert_on_ens_with_qos(net_data)
|
||||||
@ -2205,38 +1972,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
|
|
||||||
return address_bindings
|
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):
|
def _get_qos_profile_id(self, context, policy_id):
|
||||||
switch_profile_id = nsx_db.get_switch_profile_by_qos_policy(
|
switch_profile_id = nsx_db.get_switch_profile_by_qos_policy(
|
||||||
context.session, policy_id)
|
context.session, policy_id)
|
||||||
@ -2820,10 +2555,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
context, port['port'], neutron_db)
|
context, port['port'], neutron_db)
|
||||||
return 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):
|
def _validate_ens_create_port(self, context, port_data):
|
||||||
qos_selected = validators.is_attr_set(port_data.get(
|
qos_selected = validators.is_attr_set(port_data.get(
|
||||||
qos_consts.QOS_POLICY_ID))
|
qos_consts.QOS_POLICY_ID))
|
||||||
@ -2862,19 +2593,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
neutron_db = self.base_create_port(context, port)
|
neutron_db = self.base_create_port(context, port)
|
||||||
port["port"].update(neutron_db)
|
port["port"].update(neutron_db)
|
||||||
|
|
||||||
if direct_vnic_type:
|
self.fix_direct_vnic_port_sec(direct_vnic_type, port_data)
|
||||||
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
|
|
||||||
|
|
||||||
(is_psec_on, has_ip, sgids, psgids) = (
|
(is_psec_on, has_ip, sgids, psgids) = (
|
||||||
self._create_port_preprocess_security(context, port,
|
self._create_port_preprocess_security(context, port,
|
||||||
port_data, neutron_db,
|
port_data, neutron_db,
|
||||||
|
@ -15,13 +15,35 @@
|
|||||||
|
|
||||||
import mock
|
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.db import test_db_base_plugin_v2
|
||||||
from neutron.tests.unit.extensions import test_securitygroup
|
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
|
from vmware_nsxlib.v3 import nsx_constants
|
||||||
|
|
||||||
|
|
||||||
PLUGIN_NAME = 'vmware_nsx.plugin.NsxPolicyPlugin'
|
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(
|
class NsxPPluginTestCaseMixin(
|
||||||
@ -56,14 +78,38 @@ class NsxPPluginTestCaseMixin(
|
|||||||
return_value=-1).start()
|
return_value=-1).start()
|
||||||
|
|
||||||
def setup_conf_overrides(self):
|
def setup_conf_overrides(self):
|
||||||
#TODO(asarfaty): will be needed in the future
|
cfg.CONF.set_override('default_overlay_tz', NSX_OVERLAY_TZ_NAME,
|
||||||
#cfg.CONF.set_override('default_overlay_tz', NSX_TZ_NAME, 'nsx_p')
|
'nsx_p')
|
||||||
#cfg.CONF.set_override('native_dhcp_metadata', False, 'nsx_p')
|
cfg.CONF.set_override('default_vlan_tz', NSX_VLAN_TZ_NAME, 'nsx_p')
|
||||||
#cfg.CONF.set_override('dhcp_profile',
|
|
||||||
# NSX_DHCP_PROFILE_ID, 'nsx_p')
|
def _create_network(self, fmt, name, admin_state_up,
|
||||||
#cfg.CONF.set_override('metadata_proxy',
|
arg_list=None, providernet_args=None,
|
||||||
# NSX_METADATA_PROXY_ID, 'nsx_p')
|
set_context=False, tenant_id=None,
|
||||||
pass
|
**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,
|
class NsxPTestNetworks(test_db_base_plugin_v2.TestNetworksV2,
|
||||||
@ -78,6 +124,220 @@ class NsxPTestNetworks(test_db_base_plugin_v2.TestNetworksV2,
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super(NsxPTestNetworks, self).tearDown()
|
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,
|
class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
|
||||||
NsxPPluginTestCaseMixin):
|
NsxPPluginTestCaseMixin):
|
||||||
@ -147,18 +407,125 @@ class NsxPTestSecurityGroup(NsxPPluginTestCaseMixin,
|
|||||||
for k, v, in keys:
|
for k, v, in keys:
|
||||||
self.assertEqual(rule['security_group_rule'][k], v)
|
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
|
# 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):
|
def test_update_port_with_security_group(self):
|
||||||
self.skipTest('Temporarily not supported')
|
self.skipTest('Temporarily not supported')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user