NSX|P: Support policy DHCP v6
Change-Id: Ibe4936b6f0b64e67cb3c7838d0f1a16304eb1180
This commit is contained in:
parent
35b9312a79
commit
5b84dd5e4d
@ -231,6 +231,7 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
'ip_version': subnet.ip_version,
|
'ip_version': subnet.ip_version,
|
||||||
'network_id': subnet.network_id,
|
'network_id': subnet.network_id,
|
||||||
'gateway_ip': subnet.gateway_ip,
|
'gateway_ip': subnet.gateway_ip,
|
||||||
|
'enable_dhcp': subnet.enable_dhcp,
|
||||||
'ipv6_address_mode': subnet.ipv6_address_mode}
|
'ipv6_address_mode': subnet.ipv6_address_mode}
|
||||||
for subnet in db_subnets]
|
for subnet in db_subnets]
|
||||||
return subnets
|
return subnets
|
||||||
|
@ -1489,15 +1489,7 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
LOG.warning(err_msg)
|
LOG.warning(err_msg)
|
||||||
raise n_exc.InvalidInput(error_message=err_msg)
|
raise n_exc.InvalidInput(error_message=err_msg)
|
||||||
|
|
||||||
def _build_dhcp_server_config(self, context, network, subnet, port, az):
|
def _get_network_dns_domain(self, az, network):
|
||||||
|
|
||||||
name = self.nsxlib.native_dhcp.build_server_name(
|
|
||||||
network['name'], network['id'])
|
|
||||||
|
|
||||||
net_tags = self.nsxlib.build_v3_tags_payload(
|
|
||||||
network, resource_type='os-neutron-net-id',
|
|
||||||
project_name=context.tenant_name)
|
|
||||||
|
|
||||||
dns_domain = None
|
dns_domain = None
|
||||||
if network.get('dns_domain'):
|
if network.get('dns_domain'):
|
||||||
net_dns = network['dns_domain']
|
net_dns = network['dns_domain']
|
||||||
@ -1507,6 +1499,18 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
dns_domain = net_dns.dns_domain
|
dns_domain = net_dns.dns_domain
|
||||||
if not dns_domain or not validators.is_attr_set(dns_domain):
|
if not dns_domain or not validators.is_attr_set(dns_domain):
|
||||||
dns_domain = az.dns_domain
|
dns_domain = az.dns_domain
|
||||||
|
return dns_domain
|
||||||
|
|
||||||
|
def _build_dhcp_server_config(self, context, network, subnet, port, az):
|
||||||
|
|
||||||
|
name = self.nsxlib.native_dhcp.build_server_name(
|
||||||
|
network['name'], network['id'])
|
||||||
|
|
||||||
|
net_tags = self.nsxlib.build_v3_tags_payload(
|
||||||
|
network, resource_type='os-neutron-net-id',
|
||||||
|
project_name=context.tenant_name)
|
||||||
|
|
||||||
|
dns_domain = self._get_network_dns_domain(az, network)
|
||||||
|
|
||||||
dns_nameservers = subnet['dns_nameservers']
|
dns_nameservers = subnet['dns_nameservers']
|
||||||
if not dns_nameservers or not validators.is_attr_set(dns_nameservers):
|
if not dns_nameservers or not validators.is_attr_set(dns_nameservers):
|
||||||
@ -2032,15 +2036,15 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
native_metadata = self._has_native_dhcp_metadata()
|
native_metadata = self._has_native_dhcp_metadata()
|
||||||
default_enable_dhcp = (orig_subnet.get('enable_dhcp', False)
|
default_enable_dhcp = (orig_subnet.get('enable_dhcp', False)
|
||||||
if orig_subnet else False)
|
if orig_subnet else False)
|
||||||
# DHCPv6 is not yet supported, but slaac is
|
# DHCPv6 is not yet supported, but slaac is.
|
||||||
# When configuring slaac, neutron requires the user
|
# When configuring slaac, neutron requires the user
|
||||||
# to enable dhcp, however plugin code does not consider
|
# to enable dhcp, however plugin code does not consider
|
||||||
# slaac as dhcp.
|
# slaac as dhcp.
|
||||||
return (native_metadata and
|
return (native_metadata and
|
||||||
subnet.get('enable_dhcp', default_enable_dhcp) and
|
subnet.get('enable_dhcp', default_enable_dhcp) and
|
||||||
subnet.get('ipv6_address_mode') != 'slaac')
|
subnet.get('ipv6_address_mode') != constants.IPV6_SLAAC)
|
||||||
|
|
||||||
def _validate_subnet_ip_version(self, subnet):
|
def _validate_mp_subnet_ip_version(self, subnet):
|
||||||
# This validation only needs to be called at create,
|
# This validation only needs to be called at create,
|
||||||
# since ip version and ipv6 mode attributes are read only
|
# since ip version and ipv6 mode attributes are read only
|
||||||
if subnet.get('ip_version') == 4:
|
if subnet.get('ip_version') == 4:
|
||||||
@ -2048,10 +2052,9 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
return
|
return
|
||||||
|
|
||||||
enable_dhcp = subnet.get('enable_dhcp', False)
|
enable_dhcp = subnet.get('enable_dhcp', False)
|
||||||
is_slaac = (subnet.get('ipv6_address_mode') == 'slaac')
|
is_slaac = (subnet.get('ipv6_address_mode') == constants.IPV6_SLAAC)
|
||||||
if enable_dhcp and not is_slaac:
|
if enable_dhcp and not is_slaac:
|
||||||
# No DHCPv6 support yet
|
# No DHCPv6 support with the MP DHCP
|
||||||
# TODO(asarfaty): add ipv6 support for policy plugin
|
|
||||||
msg = _("DHCPv6 is not supported")
|
msg = _("DHCPv6 is not supported")
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
@ -2085,7 +2088,7 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
def _create_subnet_with_mp_dhcp(self, context, subnet):
|
def _create_subnet_with_mp_dhcp(self, context, subnet):
|
||||||
self._validate_number_of_subnet_static_routes(subnet)
|
self._validate_number_of_subnet_static_routes(subnet)
|
||||||
self._validate_host_routes_input(subnet)
|
self._validate_host_routes_input(subnet)
|
||||||
self._validate_subnet_ip_version(subnet['subnet'])
|
self._validate_mp_subnet_ip_version(subnet['subnet'])
|
||||||
|
|
||||||
net_id = subnet['subnet']['network_id']
|
net_id = subnet['subnet']['network_id']
|
||||||
network = self._get_network(context, net_id)
|
network = self._get_network(context, net_id)
|
||||||
@ -2505,6 +2508,7 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
def _build_static_routes(self, gateway_ip, cidr, host_routes):
|
def _build_static_routes(self, gateway_ip, cidr, host_routes):
|
||||||
# The following code is based on _generate_opts_per_subnet() in
|
# The following code is based on _generate_opts_per_subnet() in
|
||||||
# neutron/agent/linux/dhcp.py. It prepares DHCP options for a subnet.
|
# neutron/agent/linux/dhcp.py. It prepares DHCP options for a subnet.
|
||||||
|
# This code is for IPv4 only (IPv6 dhcp does not support options)
|
||||||
|
|
||||||
# Add route for directly connected network.
|
# Add route for directly connected network.
|
||||||
static_routes = [{'network': cidr, 'next_hop': '0.0.0.0'}]
|
static_routes = [{'network': cidr, 'next_hop': '0.0.0.0'}]
|
||||||
@ -2649,13 +2653,11 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
def _has_no_dhcp_enabled_subnet(self, context, network):
|
def _has_no_dhcp_enabled_subnet(self, context, network):
|
||||||
# Check if there is no DHCP-enabled subnet in the network.
|
# Check if there is no DHCP-enabled subnet in the network.
|
||||||
for subnet in network.subnets:
|
for subnet in network.subnets:
|
||||||
if subnet.enable_dhcp and subnet.ipv6_address_mode != 'slaac':
|
if (subnet.enable_dhcp and
|
||||||
|
subnet.ipv6_address_mode != constants.IPV6_SLAAC):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _has_dhcp_enabled_subnet(self, context, network):
|
|
||||||
return not self._has_no_dhcp_enabled_subnet(context, network)
|
|
||||||
|
|
||||||
def _has_single_dhcp_enabled_subnet(self, context, network):
|
def _has_single_dhcp_enabled_subnet(self, context, network):
|
||||||
# Check if there is only one DHCP-enabled subnet in the network.
|
# Check if there is only one DHCP-enabled subnet in the network.
|
||||||
count = 0
|
count = 0
|
||||||
|
@ -128,6 +128,8 @@ SEG_SECURITY_PROFILE_ID = (
|
|||||||
policy_defs.SegmentSecurityProfileDef.DEFAULT_PROFILE)
|
policy_defs.SegmentSecurityProfileDef.DEFAULT_PROFILE)
|
||||||
SLAAC_NDRA_PROFILE_ID = 'neutron-slaac-profile'
|
SLAAC_NDRA_PROFILE_ID = 'neutron-slaac-profile'
|
||||||
NO_SLAAC_NDRA_PROFILE_ID = 'neutron-no-slaac-profile'
|
NO_SLAAC_NDRA_PROFILE_ID = 'neutron-no-slaac-profile'
|
||||||
|
STATELESS_DHCP_NDRA_PROFILE_ID = 'neutron-stateless-dhcp-profile'
|
||||||
|
STATEFUL_DHCP_NDRA_PROFILE_ID = 'neutron-stateful-dhcp-profile'
|
||||||
|
|
||||||
IPV6_RA_SERVICE = 'neutron-ipv6-ra'
|
IPV6_RA_SERVICE = 'neutron-ipv6-ra'
|
||||||
IPV6_ROUTER_ADV_RULE_NAME = 'all-ipv6'
|
IPV6_ROUTER_ADV_RULE_NAME = 'all-ipv6'
|
||||||
@ -364,9 +366,11 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
default_az = self.get_default_az()
|
default_az = self.get_default_az()
|
||||||
if default_az.use_policy_dhcp:
|
if default_az.use_policy_dhcp:
|
||||||
self.use_policy_dhcp = True
|
self.use_policy_dhcp = True
|
||||||
|
LOG.info("The policy plugin will use policy based DHCP v4/6")
|
||||||
else:
|
else:
|
||||||
self._init_native_dhcp()
|
self._init_native_dhcp()
|
||||||
self.use_policy_dhcp = False
|
self.use_policy_dhcp = False
|
||||||
|
LOG.info("The policy plugin will use MP based DHCP v4")
|
||||||
|
|
||||||
self._init_native_metadata()
|
self._init_native_metadata()
|
||||||
|
|
||||||
@ -466,35 +470,29 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
SEG_SECURITY_PROFILE_ID)
|
SEG_SECURITY_PROFILE_ID)
|
||||||
raise nsx_exc.NsxPluginException(err_msg=msg)
|
raise nsx_exc.NsxPluginException(err_msg=msg)
|
||||||
|
|
||||||
# Ipv6 SLAAC NDRA profile (find it or create)
|
# Find or create all neutron NDRA profiles
|
||||||
try:
|
ndra_profiles = {
|
||||||
self.nsxpolicy.ipv6_ndra_profile.get(SLAAC_NDRA_PROFILE_ID)
|
SLAAC_NDRA_PROFILE_ID: policy_constants.IPV6_RA_MODE_SLAAC_RA,
|
||||||
except nsx_lib_exc.ResourceNotFound:
|
STATELESS_DHCP_NDRA_PROFILE_ID:
|
||||||
try:
|
policy_constants.IPV6_RA_MODE_SLAAC_DHCP,
|
||||||
self.nsxpolicy.ipv6_ndra_profile.create_or_overwrite(
|
STATEFUL_DHCP_NDRA_PROFILE_ID: policy_constants.IPV6_RA_MODE_DHCP,
|
||||||
SLAAC_NDRA_PROFILE_ID,
|
NO_SLAAC_NDRA_PROFILE_ID: policy_constants.IPV6_RA_MODE_DISABLED
|
||||||
profile_id=SLAAC_NDRA_PROFILE_ID,
|
}
|
||||||
ra_mode=policy_constants.IPV6_RA_MODE_SLAAC_RA,
|
|
||||||
tags=self.nsxpolicy.build_v3_api_version_tag())
|
|
||||||
except nsx_lib_exc.StaleRevision as e:
|
|
||||||
# This means that another controller is also creating this
|
|
||||||
LOG.info("Failed to configure ipv6_ndra_profile for SLAAC: %s",
|
|
||||||
e)
|
|
||||||
|
|
||||||
# Verify NO SLAAC NDRA profile (find it or create)
|
for profile in ndra_profiles:
|
||||||
try:
|
try:
|
||||||
self.nsxpolicy.ipv6_ndra_profile.get(NO_SLAAC_NDRA_PROFILE_ID)
|
self.nsxpolicy.ipv6_ndra_profile.get(profile)
|
||||||
except nsx_lib_exc.ResourceNotFound:
|
except nsx_lib_exc.ResourceNotFound:
|
||||||
try:
|
try:
|
||||||
self.nsxpolicy.ipv6_ndra_profile.create_or_overwrite(
|
self.nsxpolicy.ipv6_ndra_profile.create_or_overwrite(
|
||||||
NO_SLAAC_NDRA_PROFILE_ID,
|
profile,
|
||||||
profile_id=NO_SLAAC_NDRA_PROFILE_ID,
|
profile_id=profile,
|
||||||
ra_mode=policy_constants.IPV6_RA_MODE_DISABLED,
|
ra_mode=ndra_profiles[profile],
|
||||||
tags=self.nsxpolicy.build_v3_api_version_tag())
|
tags=self.nsxpolicy.build_v3_api_version_tag())
|
||||||
except nsx_lib_exc.StaleRevision as e:
|
except nsx_lib_exc.StaleRevision as e:
|
||||||
# This means that another controller is also creating this
|
# This means that another controller is also creating this
|
||||||
LOG.info("Failed to configure ipv6_ndra_profile for NO SLAAC: "
|
LOG.info("Failed to configure ipv6_ndra_profile %s: %s",
|
||||||
"%s", e)
|
profile, e)
|
||||||
|
|
||||||
self.client_ssl_profile = None
|
self.client_ssl_profile = None
|
||||||
|
|
||||||
@ -935,6 +933,23 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
|
|
||||||
return updated_net
|
return updated_net
|
||||||
|
|
||||||
|
def _get_subnets_nd_profile(self, subnets, additional_profile=None):
|
||||||
|
profiles = []
|
||||||
|
if additional_profile:
|
||||||
|
profiles.append(additional_profile)
|
||||||
|
for sub in subnets:
|
||||||
|
profiles.append(self._get_subnet_ndra_profile(sub))
|
||||||
|
# If there is 1 stateful/stateless DHCP subnet (cannot have both)
|
||||||
|
# use this profile
|
||||||
|
if STATEFUL_DHCP_NDRA_PROFILE_ID in profiles:
|
||||||
|
return STATEFUL_DHCP_NDRA_PROFILE_ID
|
||||||
|
elif STATELESS_DHCP_NDRA_PROFILE_ID in profiles:
|
||||||
|
return STATELESS_DHCP_NDRA_PROFILE_ID
|
||||||
|
elif SLAAC_NDRA_PROFILE_ID in profiles:
|
||||||
|
# if there is slaac subnet and no DHCP subnet use SLAAC
|
||||||
|
return SLAAC_NDRA_PROFILE_ID
|
||||||
|
return NO_SLAAC_NDRA_PROFILE_ID
|
||||||
|
|
||||||
def _update_slaac_on_router(self, context, router_id,
|
def _update_slaac_on_router(self, context, router_id,
|
||||||
subnet, router_subnets, delete=False):
|
subnet, router_subnets, delete=False):
|
||||||
# TODO(annak): redesign when policy supports downlink-level
|
# TODO(annak): redesign when policy supports downlink-level
|
||||||
@ -946,33 +961,44 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
# We prefer to make another backend call for attaching the
|
# We prefer to make another backend call for attaching the
|
||||||
# profile even if it is already attached, than rely on DB
|
# profile even if it is already attached, than rely on DB
|
||||||
# to have an accurate picture of existing subnets.
|
# to have an accurate picture of existing subnets.
|
||||||
profile_id = None
|
|
||||||
|
|
||||||
slaac_subnet = (subnet.get('ipv6_address_mode') == 'slaac')
|
# This method assumes that all the v6 subnets have the same
|
||||||
|
# ipv6_address_mode.
|
||||||
|
# Otherwise, earlier validation would already fail.
|
||||||
|
|
||||||
if slaac_subnet and not delete:
|
if subnet.get('ip_version') == 4:
|
||||||
# slaac subnet connected - verify slaac is set on router
|
# This subnet will not affect the ND profile
|
||||||
profile_id = SLAAC_NDRA_PROFILE_ID
|
return
|
||||||
|
|
||||||
if delete:
|
# Fetch other overlay interface networks
|
||||||
router_subnets = self._load_router_subnet_cidrs_from_db(
|
# (VLAN advertising is attached on interface level)
|
||||||
context.elevated(), router_id)
|
ipv6_overlay_subnets = [s for s in router_subnets
|
||||||
# check if there is another slaac overlay subnet that needs
|
|
||||||
# advertising (vlan advertising is attached on interface level)
|
|
||||||
slaac_subnets = [s for s in router_subnets
|
|
||||||
if s['id'] != subnet['id'] and
|
if s['id'] != subnet['id'] and
|
||||||
s.get('ipv6_address_mode') == 'slaac' and
|
s.get('ip_version') == 6 and
|
||||||
|
s.get('enable_dhcp') and
|
||||||
self._is_overlay_network(context,
|
self._is_overlay_network(context,
|
||||||
s['network_id'])]
|
s['network_id'])]
|
||||||
|
if delete:
|
||||||
if not slaac_subnets and slaac_subnet:
|
# 'subnet' was already removed from the router_subnets list before
|
||||||
# this was the last slaac subnet connected -
|
# calling this method
|
||||||
|
if ipv6_overlay_subnets:
|
||||||
|
# If there is another ipv6 overlay - select the profile by its
|
||||||
|
# address mode
|
||||||
|
profile_id = self._get_subnets_nd_profile(ipv6_overlay_subnets)
|
||||||
|
else:
|
||||||
|
# this was the last ipv6 subnet connected -
|
||||||
# need to disable slaac on router
|
# need to disable slaac on router
|
||||||
profile_id = NO_SLAAC_NDRA_PROFILE_ID
|
profile_id = NO_SLAAC_NDRA_PROFILE_ID
|
||||||
|
else:
|
||||||
|
profile_id = self._get_subnet_ndra_profile(subnet)
|
||||||
|
# Check the other subnets too
|
||||||
|
if (ipv6_overlay_subnets and
|
||||||
|
profile_id in [NO_SLAAC_NDRA_PROFILE_ID,
|
||||||
|
SLAAC_NDRA_PROFILE_ID]):
|
||||||
|
profile_id = self._get_subnets_nd_profile(
|
||||||
|
ipv6_overlay_subnets, additional_profile=profile_id)
|
||||||
|
|
||||||
if profile_id:
|
self.nsxpolicy.tier1.update(router_id, ipv6_ndra_profile_id=profile_id)
|
||||||
self.nsxpolicy.tier1.update(router_id,
|
|
||||||
ipv6_ndra_profile_id=profile_id)
|
|
||||||
|
|
||||||
def _validate_net_dhcp_edge_cluster(self, context, network, az):
|
def _validate_net_dhcp_edge_cluster(self, context, network, az):
|
||||||
"""Validate that the dhcp server edge cluster match the one of
|
"""Validate that the dhcp server edge cluster match the one of
|
||||||
@ -1000,15 +1026,25 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
raise n_exc.InvalidInput(error_message=msg)
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
def _create_subnet_dhcp_port(self, context, az, network, subnet):
|
def _create_subnet_dhcp_port(self, context, az, network, subnet):
|
||||||
|
port = self._get_net_dhcp_port(context, network['id'])
|
||||||
|
if port:
|
||||||
|
# If the port already exist (with another subnet) - update it with
|
||||||
|
# the additional ip
|
||||||
|
port['fixed_ips'].append({'subnet_id': subnet['id']})
|
||||||
|
super(NsxPolicyPlugin, self).update_port(
|
||||||
|
context, port['id'],
|
||||||
|
{'port': {'fixed_ips': port['fixed_ips']}})
|
||||||
|
return
|
||||||
|
|
||||||
port_data = {
|
port_data = {
|
||||||
"name": "",
|
"name": "",
|
||||||
"admin_state_up": True,
|
"admin_state_up": True,
|
||||||
"device_id": network['id'],
|
"device_id": network['id'],
|
||||||
"device_owner": const.DEVICE_OWNER_DHCP,
|
"device_owner": const.DEVICE_OWNER_DHCP,
|
||||||
"network_id": network['id'],
|
"network_id": network['id'],
|
||||||
"tenant_id": network["tenant_id"],
|
"tenant_id": network['tenant_id'],
|
||||||
"mac_address": const.ATTR_NOT_SPECIFIED,
|
"mac_address": const.ATTR_NOT_SPECIFIED,
|
||||||
"fixed_ips": [{"subnet_id": subnet['id']}],
|
"fixed_ips": [{'subnet_id': subnet['id']}],
|
||||||
psec.PORTSECURITY: False
|
psec.PORTSECURITY: False
|
||||||
}
|
}
|
||||||
# Create the DHCP port (on neutron only) and update its port security
|
# Create the DHCP port (on neutron only) and update its port security
|
||||||
@ -1020,13 +1056,23 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
self._process_portbindings_create_and_update(
|
self._process_portbindings_create_and_update(
|
||||||
context, port_data, neutron_port)
|
context, port_data, neutron_port)
|
||||||
|
|
||||||
def _delete_subnet_dhcp_port(self, context, net_id):
|
def _delete_subnet_dhcp_port(self, context, net_id, subnet_id=None):
|
||||||
dhcp_port = self._get_sunbet_dhcp_port(context, net_id)
|
dhcp_port = self._get_net_dhcp_port(context, net_id)
|
||||||
if dhcp_port:
|
if dhcp_port:
|
||||||
|
if subnet_id:
|
||||||
|
# deleting just this subnets dhcp
|
||||||
|
if len(dhcp_port['fixed_ips']) > 1:
|
||||||
|
new_fixed_ips = [ip for ip in dhcp_port['fixed_ips']
|
||||||
|
if ip['subnet_id'] != subnet_id]
|
||||||
|
super(NsxPolicyPlugin, self).update_port(
|
||||||
|
context, dhcp_port['id'],
|
||||||
|
{'port': {'fixed_ips': new_fixed_ips}})
|
||||||
|
return
|
||||||
|
# Delete the port itself
|
||||||
self.delete_port(context, dhcp_port['id'],
|
self.delete_port(context, dhcp_port['id'],
|
||||||
force_delete_dhcp=True)
|
force_delete_dhcp=True)
|
||||||
|
|
||||||
def _get_sunbet_dhcp_port(self, context, net_id):
|
def _get_net_dhcp_port(self, context, net_id):
|
||||||
filters = {
|
filters = {
|
||||||
'network_id': [net_id],
|
'network_id': [net_id],
|
||||||
'device_owner': [const.DEVICE_OWNER_DHCP]
|
'device_owner': [const.DEVICE_OWNER_DHCP]
|
||||||
@ -1035,7 +1081,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
return dhcp_ports[0] if dhcp_ports else None
|
return dhcp_ports[0] if dhcp_ports else None
|
||||||
|
|
||||||
def _get_sunbet_dhcp_server_ip(self, context, net_id, dhcp_subnet_id):
|
def _get_sunbet_dhcp_server_ip(self, context, net_id, dhcp_subnet_id):
|
||||||
dhcp_port = self._get_sunbet_dhcp_port(context, net_id)
|
dhcp_port = self._get_net_dhcp_port(context, net_id)
|
||||||
if dhcp_port:
|
if dhcp_port:
|
||||||
dhcp_server_ips = [fip['ip_address']
|
dhcp_server_ips = [fip['ip_address']
|
||||||
for fip in dhcp_port['fixed_ips']
|
for fip in dhcp_port['fixed_ips']
|
||||||
@ -1044,33 +1090,39 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
return dhcp_server_ips[0]
|
return dhcp_server_ips[0]
|
||||||
|
|
||||||
def _is_dhcp_network(self, context, net_id):
|
def _is_dhcp_network(self, context, net_id):
|
||||||
dhcp_port = self._get_sunbet_dhcp_port(context, net_id)
|
dhcp_port = self._get_net_dhcp_port(context, net_id)
|
||||||
return True if dhcp_port else False
|
return True if dhcp_port else False
|
||||||
|
|
||||||
def _get_segment_subnets(self, context, net_id, net_az=None,
|
def _get_segment_subnets(self, context, net_id, net_az=None,
|
||||||
interface_subnets=None, **kwargs):
|
interface_subnets=None,
|
||||||
"""Get list of segmentSubnet objects to put on the segment
|
deleted_dhcp_subnets=None):
|
||||||
|
"""Get an updated list of segmentSubnet objects to put on the segment
|
||||||
Including router interface subnets (for overlay networks) &
|
Including router interface subnets (for overlay networks) &
|
||||||
DHCP subnet (if using policy DHCP)
|
DHCP subnets (if using policy v4/v6 DHCP)
|
||||||
"""
|
"""
|
||||||
dhcp_subnet = None
|
dhcp_subnets = []
|
||||||
if 'dhcp_subnet' in kwargs:
|
|
||||||
dhcp_subnet = kwargs['dhcp_subnet']
|
|
||||||
else:
|
|
||||||
# Get it from the network
|
|
||||||
if self.use_policy_dhcp:
|
if self.use_policy_dhcp:
|
||||||
# TODO(asarfaty): Add ipv6 support
|
# Find networks DHCP enabled subnets
|
||||||
|
with db_api.CONTEXT_READER.using(context):
|
||||||
network = self._get_network(context, net_id)
|
network = self._get_network(context, net_id)
|
||||||
for subnet in network.subnets:
|
for subnet in network.subnets:
|
||||||
if subnet.enable_dhcp and subnet.ip_version == 4:
|
if(subnet.enable_dhcp and
|
||||||
dhcp_subnet = self.get_subnet(context, subnet.id)
|
(subnet.ip_version == 4 or
|
||||||
|
subnet.ipv6_address_mode != const.IPV6_SLAAC)):
|
||||||
|
if (deleted_dhcp_subnets and
|
||||||
|
subnet.id in deleted_dhcp_subnets):
|
||||||
|
# Skip this one as it is being deleted
|
||||||
|
continue
|
||||||
|
dhcp_subnets.append(self.get_subnet(context, subnet.id))
|
||||||
|
if len(dhcp_subnets) == 2:
|
||||||
|
# A network an have at most 2 DHCP subnets
|
||||||
break
|
break
|
||||||
|
|
||||||
router_subnets = []
|
router_subnets = []
|
||||||
if interface_subnets:
|
if interface_subnets:
|
||||||
router_subnets = interface_subnets
|
router_subnets = interface_subnets
|
||||||
else:
|
else:
|
||||||
# Get it from the network, only if overlay
|
# Get networks overlay router interfaces
|
||||||
if self._is_overlay_network(context, net_id):
|
if self._is_overlay_network(context, net_id):
|
||||||
router_ids = self._get_network_router_ids(
|
router_ids = self._get_network_router_ids(
|
||||||
context.elevated(), net_id)
|
context.elevated(), net_id)
|
||||||
@ -1081,31 +1133,39 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
|
|
||||||
seg_subnets = []
|
seg_subnets = []
|
||||||
|
|
||||||
dhcp_subnet_id = None
|
dhcp_subnet_ids = []
|
||||||
if dhcp_subnet:
|
for dhcp_subnet in dhcp_subnets:
|
||||||
dhcp_subnet_id = dhcp_subnet['id']
|
dhcp_subnet_id = dhcp_subnet['id']
|
||||||
|
dhcp_subnet_ids.append(dhcp_subnet_id)
|
||||||
gw_addr = self._get_gateway_addr_from_subnet(dhcp_subnet)
|
gw_addr = self._get_gateway_addr_from_subnet(dhcp_subnet)
|
||||||
cidr_prefix = int(dhcp_subnet['cidr'].split('/')[1])
|
cidr_prefix = int(dhcp_subnet['cidr'].split('/')[1])
|
||||||
dhcp_server_ip = self._get_sunbet_dhcp_server_ip(
|
dhcp_server_ip = self._get_sunbet_dhcp_server_ip(
|
||||||
context, net_id, dhcp_subnet_id)
|
context, net_id, dhcp_subnet_id)
|
||||||
dns_nameservers = dhcp_subnet['dns_nameservers']
|
dns_nameservers = dhcp_subnet['dns_nameservers']
|
||||||
|
if not net_az:
|
||||||
|
net_az = self.get_network_az_by_net_id(context, net_id)
|
||||||
if (not dns_nameservers or
|
if (not dns_nameservers or
|
||||||
not validators.is_attr_set(dns_nameservers)):
|
not validators.is_attr_set(dns_nameservers)):
|
||||||
# Use pre-configured dns server
|
# Use pre-configured dns server
|
||||||
if not net_az:
|
|
||||||
net_az = self.get_network_az_by_net_id(context, net_id)
|
|
||||||
dns_nameservers = net_az.nameservers
|
dns_nameservers = net_az.nameservers
|
||||||
dhcp_config = policy_defs.SegmentDhcpConfig(
|
is_ipv6 = True if dhcp_subnet.get('ip_version') == 6 else False
|
||||||
server_address="%s/%s" % (dhcp_server_ip, cidr_prefix),
|
server_ip = "%s/%s" % (dhcp_server_ip, cidr_prefix)
|
||||||
dns_servers=dns_nameservers,
|
kwargs = {'server_address': server_ip,
|
||||||
is_ipv6=False) # TODO(asarfaty): add ipv6 support
|
'dns_servers': dns_nameservers}
|
||||||
|
if is_ipv6:
|
||||||
|
network = self._get_network(context, net_id)
|
||||||
|
kwargs['domain_names'] = [
|
||||||
|
self._get_network_dns_domain(net_az, network)]
|
||||||
|
dhcp_config = policy_defs.SegmentDhcpConfigV6(**kwargs)
|
||||||
|
else:
|
||||||
|
dhcp_config = policy_defs.SegmentDhcpConfigV4(**kwargs)
|
||||||
|
|
||||||
seg_subnet = policy_defs.Subnet(gateway_address=gw_addr,
|
seg_subnet = policy_defs.Subnet(gateway_address=gw_addr,
|
||||||
dhcp_config=dhcp_config)
|
dhcp_config=dhcp_config)
|
||||||
seg_subnets.append(seg_subnet)
|
seg_subnets.append(seg_subnet)
|
||||||
|
|
||||||
for rtr_subnet in router_subnets:
|
for rtr_subnet in router_subnets:
|
||||||
if rtr_subnet['id'] == dhcp_subnet_id:
|
if rtr_subnet['id'] in dhcp_subnet_ids:
|
||||||
# Do not add the same subnet twice
|
# Do not add the same subnet twice
|
||||||
continue
|
continue
|
||||||
if rtr_subnet['network_id'] == net_id:
|
if rtr_subnet['network_id'] == net_id:
|
||||||
@ -1124,35 +1184,58 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
net_id = network['id']
|
net_id = network['id']
|
||||||
segment_id = self._get_network_nsx_segment_id(context, net_id)
|
segment_id = self._get_network_nsx_segment_id(context, net_id)
|
||||||
|
|
||||||
seg_subnets = self._get_segment_subnets(
|
seg_subnets = self._get_segment_subnets(context, net_id, net_az=az)
|
||||||
context, net_id, net_az=az, dhcp_subnet=subnet)
|
|
||||||
# Update dhcp server config on the segment
|
# Update dhcp server config on the segment
|
||||||
self.nsxpolicy.segment.update(
|
self.nsxpolicy.segment.update(
|
||||||
segment_id=segment_id,
|
segment_id=segment_id,
|
||||||
dhcp_server_config_id=az._policy_dhcp_server_config,
|
dhcp_server_config_id=az._policy_dhcp_server_config,
|
||||||
subnets=seg_subnets)
|
subnets=seg_subnets)
|
||||||
|
|
||||||
def _disable_network_dhcp(self, context, network):
|
def _get_net_dhcp_subnets(self, context, net_id):
|
||||||
net_id = network['id']
|
net_dhcp_subnets = []
|
||||||
|
net_obj = self._get_network(context, net_id)
|
||||||
|
for subnet in net_obj.subnets:
|
||||||
|
if(subnet.enable_dhcp and
|
||||||
|
(subnet.ip_version == 4 or
|
||||||
|
subnet.ipv6_address_mode != const.IPV6_SLAAC)):
|
||||||
|
# This is a DHCP subnet
|
||||||
|
net_dhcp_subnets.append(subnet.id)
|
||||||
|
return net_dhcp_subnets
|
||||||
|
|
||||||
# Remove dhcp server config from the segment
|
def _disable_network_dhcp(self, context, network, subnet_id=None):
|
||||||
segment_id = self._get_network_nsx_segment_id(
|
net_id = network['id']
|
||||||
context, net_id)
|
net_dhcp_subnets = self._get_net_dhcp_subnets(context, net_id)
|
||||||
|
segment_id = self._get_network_nsx_segment_id(context, net_id)
|
||||||
|
|
||||||
|
if subnet_id and len(net_dhcp_subnets) > 1:
|
||||||
|
# remove dhcp only from this subnet
|
||||||
seg_subnets = self._get_segment_subnets(
|
seg_subnets = self._get_segment_subnets(
|
||||||
context, net_id, dhcp_subnet=None)
|
context, net_id, deleted_dhcp_subnets=[subnet_id])
|
||||||
self.nsxpolicy.segment.update(
|
self.nsxpolicy.segment.update(
|
||||||
segment_id,
|
segment_id,
|
||||||
|
subnets=seg_subnets)
|
||||||
|
self._delete_subnet_dhcp_port(context, net_id, subnet_id=subnet_id)
|
||||||
|
else:
|
||||||
|
# Remove dhcp server config completly from the segment
|
||||||
|
seg_subnets = self._get_segment_subnets(
|
||||||
|
context, net_id, deleted_dhcp_subnets=net_dhcp_subnets)
|
||||||
|
self.nsxpolicy.segment.update(
|
||||||
|
segment_id=segment_id,
|
||||||
subnets=seg_subnets,
|
subnets=seg_subnets,
|
||||||
dhcp_server_config_id=None)
|
dhcp_server_config_id=None)
|
||||||
|
|
||||||
# Delete the neutron DHCP port (and its bindings)
|
# Delete the neutron DHCP port (and its bindings)
|
||||||
self._delete_subnet_dhcp_port(context, net_id)
|
self._delete_subnet_dhcp_port(context, net_id)
|
||||||
|
|
||||||
def _update_subnet_dhcp(self, context, network, subnet, az):
|
def _update_nsx_net_dhcp(self, context, network, az, subnet=None):
|
||||||
|
"""Update the DHCP config on a network
|
||||||
|
Update the segment DHCP config, as well as the dhcp bindings on the
|
||||||
|
ports.
|
||||||
|
If just a specific subnet was modified, update only its ports.
|
||||||
|
"""
|
||||||
net_id = network['id']
|
net_id = network['id']
|
||||||
segment_id = self._get_network_nsx_segment_id(context, net_id)
|
segment_id = self._get_network_nsx_segment_id(context, net_id)
|
||||||
seg_subnets = self._get_segment_subnets(
|
seg_subnets = self._get_segment_subnets(context, net_id, net_az=az)
|
||||||
context, net_id, net_az=az, dhcp_subnet=subnet)
|
|
||||||
|
|
||||||
filters = {'network_id': [net_id]}
|
filters = {'network_id': [net_id]}
|
||||||
ports = self.get_ports(context, filters=filters)
|
ports = self.get_ports(context, filters=filters)
|
||||||
@ -1213,18 +1296,55 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
|
def _init_ipv6_gateway(self, subnet):
|
||||||
|
# Override neutron decision to verify that also for ipv6 the first
|
||||||
|
# ip in the cidr is not used, as the NSX does not support xxxx::0 as a
|
||||||
|
# segment subnet gateway.
|
||||||
|
if (subnet.get('gateway_ip') is const.ATTR_NOT_SPECIFIED and
|
||||||
|
subnet.get('ip_version') == const.IP_VERSION_6 and
|
||||||
|
subnet.get('cidr') and subnet['cidr'] != const.ATTR_NOT_SPECIFIED):
|
||||||
|
net = netaddr.IPNetwork(subnet['cidr'])
|
||||||
|
subnet['gateway_ip'] = str(net.network + 1)
|
||||||
|
|
||||||
|
def _validate_subnet_host_routes(self, subnet, orig_subnet=None):
|
||||||
|
self._validate_number_of_subnet_static_routes(subnet)
|
||||||
|
if orig_subnet:
|
||||||
|
self._validate_host_routes_input(
|
||||||
|
subnet,
|
||||||
|
orig_enable_dhcp=orig_subnet['enable_dhcp'],
|
||||||
|
orig_host_routes=orig_subnet['host_routes'])
|
||||||
|
else:
|
||||||
|
self._validate_host_routes_input(subnet)
|
||||||
|
|
||||||
|
# IPv6 subnets cannot support host routes
|
||||||
|
if (subnet['subnet'].get('ip_version') == 6 or
|
||||||
|
(orig_subnet and orig_subnet.get('ip_version') == 6)):
|
||||||
|
if (validators.is_attr_set(subnet['subnet'].get('host_routes')) and
|
||||||
|
subnet['subnet']['host_routes']):
|
||||||
|
err_msg = _("Host routes can only be supported with IPv4 "
|
||||||
|
"subnets")
|
||||||
|
raise n_exc.InvalidInput(error_message=err_msg)
|
||||||
|
|
||||||
|
def _has_dhcp_enabled_subnet(self, context, network, ip_version=4):
|
||||||
|
for subnet in network.subnets:
|
||||||
|
if subnet.enable_dhcp and subnet.ip_version == ip_version:
|
||||||
|
if ip_version == 4:
|
||||||
|
return True
|
||||||
|
elif subnet.ipv6_address_mode != const.IPV6_SLAAC:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@nsx_plugin_common.api_replay_mode_wrapper
|
@nsx_plugin_common.api_replay_mode_wrapper
|
||||||
def create_subnet(self, context, subnet):
|
def create_subnet(self, context, subnet):
|
||||||
if not self.use_policy_dhcp:
|
if not self.use_policy_dhcp:
|
||||||
# Subnet with MP DHCP
|
# Subnet with MP DHCP
|
||||||
return self._create_subnet_with_mp_dhcp(context, subnet)
|
return self._create_subnet_with_mp_dhcp(context, subnet)
|
||||||
|
|
||||||
self._validate_number_of_subnet_static_routes(subnet)
|
self._validate_subnet_host_routes(subnet)
|
||||||
self._validate_host_routes_input(subnet)
|
|
||||||
self._validate_subnet_ip_version(subnet['subnet'])
|
|
||||||
net_id = subnet['subnet']['network_id']
|
net_id = subnet['subnet']['network_id']
|
||||||
network = self._get_network(context, net_id)
|
network = self._get_network(context, net_id)
|
||||||
self._validate_single_ipv6_subnet(context, network, subnet['subnet'])
|
self._validate_single_ipv6_subnet(context, network, subnet['subnet'])
|
||||||
|
self._init_ipv6_gateway(subnet['subnet'])
|
||||||
net_az = self.get_network_az_by_net_id(context, net_id)
|
net_az = self.get_network_az_by_net_id(context, net_id)
|
||||||
|
|
||||||
# Allow manipulation of only 1 subnet of the same network at once
|
# Allow manipulation of only 1 subnet of the same network at once
|
||||||
@ -1237,10 +1357,11 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
self._validate_external_subnet(context, net_id)
|
self._validate_external_subnet(context, net_id)
|
||||||
self._validate_net_dhcp_edge_cluster(context, network, net_az)
|
self._validate_net_dhcp_edge_cluster(context, network, net_az)
|
||||||
self._validate_net_type_with_dhcp(context, network)
|
self._validate_net_type_with_dhcp(context, network)
|
||||||
|
ip_version = subnet['subnet'].get('ip_version', 4)
|
||||||
if self._has_dhcp_enabled_subnet(context, network):
|
if self._has_dhcp_enabled_subnet(context, network, ip_version):
|
||||||
msg = (_("Can not create more than one DHCP-enabled "
|
msg = (_("Can not create more than one DHCP-enabled "
|
||||||
"subnet in network %s") % net_id)
|
"subnet for IPv%(ver)s in network %(net)s") %
|
||||||
|
{'ver': ip_version, 'net': net_id})
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
self._validate_segment_subnets_num(
|
self._validate_segment_subnets_num(
|
||||||
@ -1273,7 +1394,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
# revert the subnet creation
|
# revert the subnet creation
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
# Try to delete the DHCP port, and the neutron subnet
|
# Try to delete the DHCP port, and the neutron subnet
|
||||||
self._delete_subnet_dhcp_port(context, net_id)
|
self._delete_subnet_dhcp_port(
|
||||||
|
context, net_id, subnet_id=created_subnet['id'])
|
||||||
super(NsxPolicyPlugin, self).delete_subnet(
|
super(NsxPolicyPlugin, self).delete_subnet(
|
||||||
context, created_subnet['id'])
|
context, created_subnet['id'])
|
||||||
|
|
||||||
@ -1292,11 +1414,11 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
if self._subnet_with_native_dhcp(subnet):
|
if self._subnet_with_native_dhcp(subnet):
|
||||||
lock = 'nsxp_network_' + subnet['network_id']
|
lock = 'nsxp_network_' + subnet['network_id']
|
||||||
with locking.LockManager.get_lock(lock):
|
with locking.LockManager.get_lock(lock):
|
||||||
# Check if it is the last DHCP-enabled subnet to delete.
|
# Remove this subnet DHCP config
|
||||||
network = self._get_network(context, subnet['network_id'])
|
network = self._get_network(context, subnet['network_id'])
|
||||||
if self._has_single_dhcp_enabled_subnet(context, network):
|
|
||||||
try:
|
try:
|
||||||
self._disable_network_dhcp(context, network)
|
self._disable_network_dhcp(context, network,
|
||||||
|
subnet_id=subnet_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error("Failed to disable DHCP for "
|
LOG.error("Failed to disable DHCP for "
|
||||||
"network %(id)s. Exception: %(e)s",
|
"network %(id)s. Exception: %(e)s",
|
||||||
@ -1312,11 +1434,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
subnet_data = subnet['subnet']
|
subnet_data = subnet['subnet']
|
||||||
updated_subnet = None
|
updated_subnet = None
|
||||||
orig_subnet = self.get_subnet(context, subnet_id)
|
orig_subnet = self.get_subnet(context, subnet_id)
|
||||||
self._validate_number_of_subnet_static_routes(subnet)
|
self._validate_subnet_host_routes(subnet, orig_subnet=orig_subnet)
|
||||||
self._validate_host_routes_input(
|
|
||||||
subnet,
|
|
||||||
orig_enable_dhcp=orig_subnet['enable_dhcp'],
|
|
||||||
orig_host_routes=orig_subnet['host_routes'])
|
|
||||||
|
|
||||||
net_id = orig_subnet['network_id']
|
net_id = orig_subnet['network_id']
|
||||||
network = self._get_network(context, net_id)
|
network = self._get_network(context, net_id)
|
||||||
@ -1333,10 +1451,12 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
with locking.LockManager.get_lock(lock):
|
with locking.LockManager.get_lock(lock):
|
||||||
if enable_dhcp:
|
if enable_dhcp:
|
||||||
self._validate_net_type_with_dhcp(context, network)
|
self._validate_net_type_with_dhcp(context, network)
|
||||||
|
ip_version = orig_subnet.get('ip_version', 4)
|
||||||
if self._has_dhcp_enabled_subnet(context, network):
|
if self._has_dhcp_enabled_subnet(context, network,
|
||||||
|
ip_version):
|
||||||
msg = (_("Can not create more than one DHCP-enabled "
|
msg = (_("Can not create more than one DHCP-enabled "
|
||||||
"subnet in network %s") % net_id)
|
"subnet for IPv%(ver)s in network %(net)s") %
|
||||||
|
{'net': net_id, 'ver': ip_version})
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
@ -1353,7 +1473,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
self._enable_subnet_dhcp(context, network,
|
self._enable_subnet_dhcp(context, network,
|
||||||
updated_subnet, net_az)
|
updated_subnet, net_az)
|
||||||
else:
|
else:
|
||||||
self._disable_network_dhcp(context, network)
|
self._disable_network_dhcp(context, network,
|
||||||
|
subnet_id=subnet_id)
|
||||||
except (nsx_lib_exc.ManagerError, nsx_exc.NsxPluginException):
|
except (nsx_lib_exc.ManagerError, nsx_exc.NsxPluginException):
|
||||||
# revert the subnet update
|
# revert the subnet update
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
@ -1373,8 +1494,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
('dns_nameservers' in subnet_data or
|
('dns_nameservers' in subnet_data or
|
||||||
'gateway_ip' in subnet_data or
|
'gateway_ip' in subnet_data or
|
||||||
'host_routes' in subnet_data)):
|
'host_routes' in subnet_data)):
|
||||||
self._update_subnet_dhcp(context, network,
|
self._update_nsx_net_dhcp(context, network, net_az, updated_subnet)
|
||||||
updated_subnet, net_az)
|
|
||||||
|
|
||||||
return updated_subnet
|
return updated_subnet
|
||||||
|
|
||||||
@ -1615,6 +1735,18 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _filter_ipv6_dhcp_fixed_ips(self, context, fixed_ips):
|
||||||
|
ips = []
|
||||||
|
for fixed_ip in fixed_ips:
|
||||||
|
if netaddr.IPNetwork(fixed_ip['ip_address']).version != 6:
|
||||||
|
continue
|
||||||
|
with db_api.CONTEXT_READER.using(context):
|
||||||
|
subnet = self.get_subnet(context, fixed_ip['subnet_id'])
|
||||||
|
if (subnet['enable_dhcp'] and
|
||||||
|
subnet.get('ipv6_address_mode') != 'slaac'):
|
||||||
|
ips.append(fixed_ip)
|
||||||
|
return ips
|
||||||
|
|
||||||
def _add_or_overwrite_port_policy_dhcp_binding(
|
def _add_or_overwrite_port_policy_dhcp_binding(
|
||||||
self, context, port, segment_id, dhcp_subnet=None):
|
self, context, port, segment_id, dhcp_subnet=None):
|
||||||
if not utils.is_port_dhcp_configurable(port):
|
if not utils.is_port_dhcp_configurable(port):
|
||||||
@ -1647,7 +1779,23 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
mac_address=port['mac_address'],
|
mac_address=port['mac_address'],
|
||||||
options=options)
|
options=options)
|
||||||
|
|
||||||
# TODO(asarfaty): add ipv6 bindings (without options)
|
for fixed_ip in self._filter_ipv6_dhcp_fixed_ips(
|
||||||
|
context, port['fixed_ips']):
|
||||||
|
# There will be only one ipv6 ip here
|
||||||
|
binding_id = port['id'] + '-ipv6'
|
||||||
|
name = 'IPv6 binding for port %s' % port['id']
|
||||||
|
ip = fixed_ip['ip_address']
|
||||||
|
if dhcp_subnet:
|
||||||
|
if fixed_ip['subnet_id'] != dhcp_subnet['id']:
|
||||||
|
continue
|
||||||
|
subnet = dhcp_subnet
|
||||||
|
else:
|
||||||
|
subnet = self.get_subnet(context, fixed_ip['subnet_id'])
|
||||||
|
self.nsxpolicy.segment_dhcp_static_bindings.create_or_overwrite_v6(
|
||||||
|
name, segment_id, binding_id=binding_id,
|
||||||
|
ip_addresses=[ip],
|
||||||
|
lease_time=cfg.CONF.nsx_p.dhcp_lease_time,
|
||||||
|
mac_address=port['mac_address'])
|
||||||
|
|
||||||
def _add_port_policy_dhcp_binding(self, context, port):
|
def _add_port_policy_dhcp_binding(self, context, port):
|
||||||
net_id = port['network_id']
|
net_id = port['network_id']
|
||||||
@ -1690,7 +1838,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def _update_port_policy_dhcp_binding(self, context, old_port, new_port):
|
def _update_port_policy_dhcp_binding(self, context, old_port, new_port):
|
||||||
# First check if any IPv4 address in fixed_ips is changed.
|
# First check if any address in fixed_ips changed.
|
||||||
# Then update DHCP server setting or DHCP static binding
|
# Then update DHCP server setting or DHCP static binding
|
||||||
# depending on the port type.
|
# depending on the port type.
|
||||||
# Note that Neutron allows a port with multiple IPs in the
|
# Note that Neutron allows a port with multiple IPs in the
|
||||||
@ -1707,44 +1855,87 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
|
|
||||||
# Collect IPv4 DHCP addresses from original and updated fixed_ips
|
# Collect IPv4 DHCP addresses from original and updated fixed_ips
|
||||||
# in the form of [(subnet_id, ip_address)].
|
# in the form of [(subnet_id, ip_address)].
|
||||||
old_fixed_ips = set([(fixed_ip['subnet_id'], fixed_ip['ip_address'])
|
old_fixed_v4 = set([(fixed_ip['subnet_id'], fixed_ip['ip_address'])
|
||||||
for fixed_ip in self._filter_ipv4_dhcp_fixed_ips(
|
for fixed_ip in self._filter_ipv4_dhcp_fixed_ips(
|
||||||
context, old_port['fixed_ips'])])
|
context, old_port['fixed_ips'])])
|
||||||
new_fixed_ips = set([(fixed_ip['subnet_id'], fixed_ip['ip_address'])
|
new_fixed_v4 = set([(fixed_ip['subnet_id'], fixed_ip['ip_address'])
|
||||||
for fixed_ip in self._filter_ipv4_dhcp_fixed_ips(
|
for fixed_ip in self._filter_ipv4_dhcp_fixed_ips(
|
||||||
context, new_port['fixed_ips'])])
|
context, new_port['fixed_ips'])])
|
||||||
|
old_fixed_v6 = set([(fixed_ip['subnet_id'], fixed_ip['ip_address'])
|
||||||
|
for fixed_ip in self._filter_ipv6_dhcp_fixed_ips(
|
||||||
|
context, old_port['fixed_ips'])])
|
||||||
|
new_fixed_v6 = set([(fixed_ip['subnet_id'], fixed_ip['ip_address'])
|
||||||
|
for fixed_ip in self._filter_ipv6_dhcp_fixed_ips(
|
||||||
|
context, new_port['fixed_ips'])])
|
||||||
# Find out the subnet/IP differences before and after the update.
|
# Find out the subnet/IP differences before and after the update.
|
||||||
ips_to_add = list(new_fixed_ips - old_fixed_ips)
|
v4_to_add = list(new_fixed_v4 - old_fixed_v4)
|
||||||
ips_to_delete = list(old_fixed_ips - new_fixed_ips)
|
v4_to_delete = list(old_fixed_v4 - new_fixed_v4)
|
||||||
ip_change = (ips_to_add or ips_to_delete)
|
v6_to_add = list(new_fixed_v6 - old_fixed_v6)
|
||||||
|
v6_to_delete = list(old_fixed_v6 - new_fixed_v6)
|
||||||
|
ip_change = (v4_to_add or v4_to_delete or v6_to_add or v6_to_delete)
|
||||||
|
|
||||||
if (old_port["device_owner"] == const.DEVICE_OWNER_DHCP and
|
if (old_port["device_owner"] == const.DEVICE_OWNER_DHCP and
|
||||||
ip_change):
|
ip_change):
|
||||||
# Update backend DHCP server address if the IP address of a DHCP
|
# Update backend DHCP server address if the IP address of a DHCP
|
||||||
# port is changed.
|
# port is changed.
|
||||||
if len(new_fixed_ips) != 1:
|
if len(new_fixed_v4) > 1 or len(new_fixed_v6) > 1:
|
||||||
msg = _("Can only configure one IP address on a DHCP server")
|
msg = _("Can only configure one IP address on a DHCP server")
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
fixed_ip = list(new_fixed_ips)[0]
|
|
||||||
subnet_id = fixed_ip[0]
|
|
||||||
net_id = old_port['network_id']
|
net_id = old_port['network_id']
|
||||||
network = self.get_network(context, net_id)
|
network = self.get_network(context, net_id)
|
||||||
subnet = self.get_subnet(context, subnet_id)
|
|
||||||
net_az = self.get_network_az_by_net_id(context, net_id)
|
net_az = self.get_network_az_by_net_id(context, net_id)
|
||||||
self._update_subnet_dhcp(context, network, subnet, net_az)
|
self._update_nsx_net_dhcp(context, network, net_az)
|
||||||
|
|
||||||
elif utils.is_port_dhcp_configurable(new_port):
|
elif utils.is_port_dhcp_configurable(new_port):
|
||||||
dhcp_opts_changed = (old_port[ext_edo.EXTRADHCPOPTS] !=
|
dhcp_opts_changed = (old_port[ext_edo.EXTRADHCPOPTS] !=
|
||||||
new_port[ext_edo.EXTRADHCPOPTS])
|
new_port[ext_edo.EXTRADHCPOPTS])
|
||||||
if (ip_change or dhcp_opts_changed or
|
if (ip_change or dhcp_opts_changed or
|
||||||
old_port['mac_address'] != new_port['mac_address']):
|
old_port['mac_address'] != new_port['mac_address']):
|
||||||
if new_fixed_ips:
|
if new_fixed_v4 or new_fixed_v6:
|
||||||
# Recreate the bindings of this port
|
# Recreate the bindings of this port
|
||||||
self._add_port_policy_dhcp_binding(context, new_port)
|
self._add_port_policy_dhcp_binding(context, new_port)
|
||||||
else:
|
else:
|
||||||
self._delete_port_policy_dhcp_binding(context, old_port)
|
self._delete_port_policy_dhcp_binding(context, old_port)
|
||||||
|
|
||||||
|
def _assert_on_ipv6_port_with_dhcpopts(self, context, port_data,
|
||||||
|
orig_port=None):
|
||||||
|
"""IPv6 port only port cannot support EXTRADHCPOPTS"""
|
||||||
|
|
||||||
|
# Get the updated EXTRADHCPOPTS
|
||||||
|
extradhcpopts = None
|
||||||
|
if ext_edo.EXTRADHCPOPTS in port_data:
|
||||||
|
extradhcpopts = port_data[ext_edo.EXTRADHCPOPTS]
|
||||||
|
elif orig_port:
|
||||||
|
extradhcpopts = orig_port.get(ext_edo.EXTRADHCPOPTS)
|
||||||
|
|
||||||
|
if not extradhcpopts:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get the updated list of fixed ips
|
||||||
|
fixed_ips = []
|
||||||
|
if (port_data.get('fixed_ips') and
|
||||||
|
validators.is_attr_set(port_data['fixed_ips'])):
|
||||||
|
fixed_ips = port_data['fixed_ips']
|
||||||
|
elif (orig_port and orig_port.get('fixed_ips') and
|
||||||
|
validators.is_attr_set(orig_port['fixed_ips'])):
|
||||||
|
fixed_ips = orig_port['fixed_ips']
|
||||||
|
|
||||||
|
# Check if any of the ips belongs to an ipv6 subnet with DHCP
|
||||||
|
# And no ipv4 subnets
|
||||||
|
for fixed_ip in fixed_ips:
|
||||||
|
if netaddr.IPNetwork(fixed_ip['ip_address']).version == 4:
|
||||||
|
# If there are ipv4 addresses - it is allowed
|
||||||
|
return
|
||||||
|
with db_api.CONTEXT_READER.using(context):
|
||||||
|
subnet = self.get_subnet(context, fixed_ip['subnet_id'])
|
||||||
|
if (subnet['enable_dhcp'] and
|
||||||
|
subnet['ipv6_address_mode'] != const.IPV6_SLAAC):
|
||||||
|
err_msg = (_("%s are not supported for IPv6 ports with "
|
||||||
|
"DHCP v6") % ext_edo.EXTRADHCPOPTS)
|
||||||
|
LOG.error(err_msg)
|
||||||
|
raise n_exc.InvalidInput(error_message=err_msg)
|
||||||
|
|
||||||
def create_port(self, context, port, l2gw_port_check=False):
|
def create_port(self, context, port, l2gw_port_check=False):
|
||||||
port_data = port['port']
|
port_data = port['port']
|
||||||
# validate the new port parameters
|
# validate the new port parameters
|
||||||
@ -1765,6 +1956,15 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
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)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Validate ipv6 only after fixed_ips are allocated
|
||||||
|
self._assert_on_ipv6_port_with_dhcpopts(context, port["port"])
|
||||||
|
except Exception:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
# rollback
|
||||||
|
super(NsxPolicyPlugin, self).delete_port(
|
||||||
|
context, neutron_db['id'])
|
||||||
|
|
||||||
self.fix_direct_vnic_port_sec(direct_vnic_type, port_data)
|
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,
|
||||||
@ -1949,6 +2149,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
|
|
||||||
direct_vnic_type = self._validate_port_vnic_type(
|
direct_vnic_type = self._validate_port_vnic_type(
|
||||||
context, port_data, original_port['network_id'])
|
context, port_data, original_port['network_id'])
|
||||||
|
self._assert_on_ipv6_port_with_dhcpopts(
|
||||||
|
context, port_data, orig_port=original_port)
|
||||||
|
|
||||||
updated_port = super(NsxPolicyPlugin, self).update_port(
|
updated_port = super(NsxPolicyPlugin, self).update_port(
|
||||||
context, port_id, port)
|
context, port_id, port)
|
||||||
@ -2615,6 +2817,50 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
|
def _get_subnet_ndra_profile(self, subnet):
|
||||||
|
ndra_profile_id = NO_SLAAC_NDRA_PROFILE_ID
|
||||||
|
if subnet.get('ip_version') == 6 and subnet.get('enable_dhcp', False):
|
||||||
|
# Subnet with DHCP v6 of some kind
|
||||||
|
addr_mode = subnet.get('ipv6_address_mode')
|
||||||
|
if addr_mode == const.IPV6_SLAAC:
|
||||||
|
ndra_profile_id = SLAAC_NDRA_PROFILE_ID
|
||||||
|
elif addr_mode == const.DHCPV6_STATELESS:
|
||||||
|
ndra_profile_id = STATELESS_DHCP_NDRA_PROFILE_ID
|
||||||
|
else:
|
||||||
|
# Stateful DHCP v6 is the default
|
||||||
|
ndra_profile_id = STATEFUL_DHCP_NDRA_PROFILE_ID
|
||||||
|
return ndra_profile_id
|
||||||
|
|
||||||
|
def _validate_interfaces_address_mode(self, context, router_id,
|
||||||
|
router_subnets, subnet):
|
||||||
|
"""Validate that all the overlay ipv6 interfaces of the router have
|
||||||
|
the same ipv6_address_mode, when a new subnet is added
|
||||||
|
"""
|
||||||
|
if subnet['enable_dhcp']:
|
||||||
|
subnet_address_mode = subnet.get('ipv6_address_mode',
|
||||||
|
const.DHCPV6_STATEFUL)
|
||||||
|
else:
|
||||||
|
# Slaac and non-dhcp can coexist
|
||||||
|
subnet_address_mode = const.IPV6_SLAAC
|
||||||
|
|
||||||
|
ipv6_overlay_subnets = [s for s in router_subnets
|
||||||
|
if s['id'] != subnet['id'] and
|
||||||
|
s.get('ip_version') == 6 and
|
||||||
|
s.get('enable_dhcp') and
|
||||||
|
self._is_overlay_network(context,
|
||||||
|
s['network_id'])]
|
||||||
|
for rtr_subnet in ipv6_overlay_subnets:
|
||||||
|
address_mode = rtr_subnet.get('ipv6_address_mode',
|
||||||
|
const.DHCPV6_STATEFUL)
|
||||||
|
if address_mode != subnet_address_mode:
|
||||||
|
msg = (_("Interface network %(net_id)s with address mode "
|
||||||
|
"%(am)s conflicts with other interfaces of router "
|
||||||
|
"%(rtr_id)s") % {'net_id': subnet['network_id'],
|
||||||
|
'am': subnet_address_mode,
|
||||||
|
'rtr_id': router_id})
|
||||||
|
LOG.error(msg)
|
||||||
|
raise n_exc.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
@nsx_plugin_common.api_replay_mode_wrapper
|
@nsx_plugin_common.api_replay_mode_wrapper
|
||||||
def add_router_interface(self, context, router_id, interface_info):
|
def add_router_interface(self, context, router_id, interface_info):
|
||||||
# NOTE: In dual stack case, neutron would create a separate interface
|
# NOTE: In dual stack case, neutron would create a separate interface
|
||||||
@ -2656,6 +2902,12 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
self._validate_router_segment_subnets(context, network_id,
|
self._validate_router_segment_subnets(context, network_id,
|
||||||
overlay_net, subnet)
|
overlay_net, subnet)
|
||||||
|
|
||||||
|
if subnet and subnet.get('ip_version') == 6 and overlay_net:
|
||||||
|
orig_rtr_subnets = self._load_router_subnet_cidrs_from_db(
|
||||||
|
context.elevated(), router_id)
|
||||||
|
self._validate_interfaces_address_mode(
|
||||||
|
context, router_id, orig_rtr_subnets, subnet)
|
||||||
|
|
||||||
# Update the interface of the neutron router
|
# Update the interface of the neutron router
|
||||||
info = super(NsxPolicyPlugin, self).add_router_interface(
|
info = super(NsxPolicyPlugin, self).add_router_interface(
|
||||||
context, router_id, interface_info)
|
context, router_id, interface_info)
|
||||||
@ -2678,7 +2930,6 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
# overlay interface
|
# overlay interface
|
||||||
pol_subnets = self._get_segment_subnets(
|
pol_subnets = self._get_segment_subnets(
|
||||||
context, network_id, interface_subnets=rtr_subnets)
|
context, network_id, interface_subnets=rtr_subnets)
|
||||||
|
|
||||||
self.nsxpolicy.segment.update(segment_id,
|
self.nsxpolicy.segment.update(segment_id,
|
||||||
tier1_id=router_id,
|
tier1_id=router_id,
|
||||||
subnets=pol_subnets)
|
subnets=pol_subnets)
|
||||||
@ -2696,9 +2947,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
ip_addresses=[rtr_subnet['gateway_ip']],
|
ip_addresses=[rtr_subnet['gateway_ip']],
|
||||||
prefix_len=prefix_len))
|
prefix_len=prefix_len))
|
||||||
|
|
||||||
slaac_subnet = (subnet.get('ipv6_address_mode') == 'slaac')
|
ndra_profile_id = self._get_subnet_ndra_profile(subnet)
|
||||||
ndra_profile_id = (SLAAC_NDRA_PROFILE_ID if slaac_subnet
|
|
||||||
else NO_SLAAC_NDRA_PROFILE_ID)
|
|
||||||
self.nsxpolicy.tier1.add_segment_interface(
|
self.nsxpolicy.tier1.add_segment_interface(
|
||||||
router_id, segment_id,
|
router_id, segment_id,
|
||||||
segment_id, pol_subnets,
|
segment_id, pol_subnets,
|
||||||
@ -3045,13 +3294,17 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
NSX_P_DEFAULT_GROUP)
|
NSX_P_DEFAULT_GROUP)
|
||||||
except nsx_lib_exc.ResourceNotFound:
|
except nsx_lib_exc.ResourceNotFound:
|
||||||
LOG.info("Going to create default group & "
|
LOG.info("Going to create default group & "
|
||||||
"communication map under the default domain")
|
"communication map")
|
||||||
|
exists = False
|
||||||
else:
|
else:
|
||||||
LOG.debug("Verified default group already exist")
|
LOG.info("Going to update default group & "
|
||||||
return
|
"communication map")
|
||||||
|
exists = True
|
||||||
|
|
||||||
# Create the default group membership criteria to match all neutron
|
# Create the group only if not exists - no need to update it
|
||||||
# ports by scope (and no tag)
|
if not exists:
|
||||||
|
# Create the default group membership criteria to match all
|
||||||
|
# neutron ports by scope (and no tag)
|
||||||
scope_and_tag = "%s|" % (NSX_P_PORT_RESOURCE_TYPE)
|
scope_and_tag = "%s|" % (NSX_P_PORT_RESOURCE_TYPE)
|
||||||
conditions = [self.nsxpolicy.group.build_condition(
|
conditions = [self.nsxpolicy.group.build_condition(
|
||||||
cond_val=scope_and_tag,
|
cond_val=scope_and_tag,
|
||||||
@ -3066,13 +3319,13 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
group_id=NSX_P_DEFAULT_GROUP,
|
group_id=NSX_P_DEFAULT_GROUP,
|
||||||
description=NSX_P_DEFAULT_GROUP_DESC,
|
description=NSX_P_DEFAULT_GROUP_DESC,
|
||||||
conditions=conditions)
|
conditions=conditions)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = (_("Failed to create NSX default group: %(e)s") % {
|
msg = (_("Failed to create NSX default group: %(e)s") % {
|
||||||
'e': e})
|
'e': e})
|
||||||
raise nsx_exc.NsxPluginException(err_msg=msg)
|
raise nsx_exc.NsxPluginException(err_msg=msg)
|
||||||
|
|
||||||
# create default section and rules
|
# create default section and rules
|
||||||
|
# (even if already exists - may need to update rules)
|
||||||
logged = cfg.CONF.nsx_p.log_security_groups_blocked_traffic
|
logged = cfg.CONF.nsx_p.log_security_groups_blocked_traffic
|
||||||
scope = [self.nsxpolicy.group.get_path(
|
scope = [self.nsxpolicy.group.get_path(
|
||||||
NSX_P_GLOBAL_DOMAIN_ID, NSX_P_DEFAULT_GROUP)]
|
NSX_P_GLOBAL_DOMAIN_ID, NSX_P_DEFAULT_GROUP)]
|
||||||
@ -3114,6 +3367,28 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
direction=nsxlib_consts.IN_OUT,
|
direction=nsxlib_consts.IN_OUT,
|
||||||
logged=logged)
|
logged=logged)
|
||||||
rule_id += 1
|
rule_id += 1
|
||||||
|
dhcpv6_server_rule = self.nsxpolicy.comm_map.build_entry(
|
||||||
|
'DHCPv6 Request', NSX_P_GLOBAL_DOMAIN_ID,
|
||||||
|
NSX_P_DEFAULT_SECTION,
|
||||||
|
rule_id, sequence_number=rule_id,
|
||||||
|
service_ids=['DHCPv6_Server'],
|
||||||
|
action=policy_constants.ACTION_ALLOW,
|
||||||
|
ip_protocol=nsxlib_consts.IPV6,
|
||||||
|
scope=scope,
|
||||||
|
direction=nsxlib_consts.OUT,
|
||||||
|
logged=logged)
|
||||||
|
rule_id += 1
|
||||||
|
dhcpv6_client_rule = self.nsxpolicy.comm_map.build_entry(
|
||||||
|
'DHCPv6 Reply', NSX_P_GLOBAL_DOMAIN_ID,
|
||||||
|
NSX_P_DEFAULT_SECTION,
|
||||||
|
rule_id, sequence_number=rule_id,
|
||||||
|
service_ids=['DHCPv6_Client'],
|
||||||
|
action=policy_constants.ACTION_ALLOW,
|
||||||
|
ip_protocol=nsxlib_consts.IPV6,
|
||||||
|
scope=scope,
|
||||||
|
direction=nsxlib_consts.IN,
|
||||||
|
logged=logged)
|
||||||
|
rule_id += 1
|
||||||
block_rule = self.nsxpolicy.comm_map.build_entry(
|
block_rule = self.nsxpolicy.comm_map.build_entry(
|
||||||
'Block All', NSX_P_GLOBAL_DOMAIN_ID,
|
'Block All', NSX_P_GLOBAL_DOMAIN_ID,
|
||||||
NSX_P_DEFAULT_SECTION,
|
NSX_P_DEFAULT_SECTION,
|
||||||
@ -3122,7 +3397,8 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
scope=scope,
|
scope=scope,
|
||||||
direction=nsxlib_consts.IN_OUT,
|
direction=nsxlib_consts.IN_OUT,
|
||||||
logged=logged)
|
logged=logged)
|
||||||
rules = [dhcp_client_rule, dhcp_server_rule, nd_rule, block_rule]
|
rules = [dhcp_client_rule, dhcp_server_rule, dhcpv6_client_rule,
|
||||||
|
dhcpv6_server_rule, nd_rule, block_rule]
|
||||||
try:
|
try:
|
||||||
# This will not fail if the map already exists
|
# This will not fail if the map already exists
|
||||||
self.nsxpolicy.comm_map.create_with_entries(
|
self.nsxpolicy.comm_map.create_with_entries(
|
||||||
|
@ -284,38 +284,26 @@ class NsxV3TestSubnets(NsxV3SubnetMixin,
|
|||||||
def test_list_subnets_with_parameter(self):
|
def test_list_subnets_with_parameter(self):
|
||||||
super(NsxV3TestSubnets, self).test_list_subnets_with_parameter()
|
super(NsxV3TestSubnets, self).test_list_subnets_with_parameter()
|
||||||
|
|
||||||
def test_create_subnet_ipv6_gw_is_nw_start_addr(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_create_subnet_ipv6_gw_is_nw_start_addr_canonicalize(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_create_subnet_ipv6_gw_is_nw_end_addr(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_create_subnet_ipv6_first_ip_owned_by_router(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_create_subnet_ipv6_first_ip_owned_by_non_router(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_create_subnet_with_v6_pd_allocation_pool(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_create_subnet_with_v6_allocation_pool(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_create_subnet_ipv6_pd_gw_values(self):
|
def test_create_subnet_ipv6_pd_gw_values(self):
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
self.skipTest('Test not suited to the plugin DHCP code')
|
||||||
|
|
||||||
def test_create_subnet_ipv6_slaac_with_dhcp_port_on_network(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_create_subnet_ipv6_slaac_with_port_not_found(self):
|
def test_create_subnet_ipv6_slaac_with_port_not_found(self):
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
self.skipTest('Test not suited to the plugin DHCP code')
|
||||||
|
|
||||||
def test_bulk_create_subnet_ipv6_auto_addr_with_port_on_network(self):
|
def test_bulk_create_subnet_ipv6_auto_addr_with_port_on_network(self):
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
self.skipTest('No Multiple v6 subnets support yet')
|
||||||
|
|
||||||
|
def test_create_subnet_dhcpv6_stateless_with_ip_already_allocated(self):
|
||||||
|
self.skipTest('Test not suited to the plugin DHCP code')
|
||||||
|
|
||||||
|
def test_create_subnet_ipv6_slaac_with_dhcp_port_on_network(self):
|
||||||
|
self.skipTest('Test not suited to the plugin DHCP code')
|
||||||
|
|
||||||
|
def test_create_subnet_dhcpv6_stateless_with_port_on_network(self):
|
||||||
|
self.skipTest('Test not suited to the plugin DHCP code')
|
||||||
|
|
||||||
|
def test_delete_subnet_port_exists_owned_by_network(self):
|
||||||
|
self.skipTest('Test not suited to the plugin DHCP code')
|
||||||
|
|
||||||
def test_create_subnets_bulk_native_ipv6(self):
|
def test_create_subnets_bulk_native_ipv6(self):
|
||||||
self.skipTest('Multiple IPv6 subnets on one network is not supported')
|
self.skipTest('Multiple IPv6 subnets on one network is not supported')
|
||||||
@ -380,24 +368,6 @@ class NsxV3TestSubnets(NsxV3SubnetMixin,
|
|||||||
super(NsxV3TestSubnets,
|
super(NsxV3TestSubnets,
|
||||||
self).test_update_subnet_inconsistent_ipv6_hostroute_np_v4()
|
self).test_update_subnet_inconsistent_ipv6_hostroute_np_v4()
|
||||||
|
|
||||||
def test_update_subnet_ipv6_ra_mode_fails(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_create_subnet_dhcpv6_stateless_with_port_on_network(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_delete_subnet_port_exists_owned_by_network(self):
|
|
||||||
self.skipTest('No support for multiple ips')
|
|
||||||
|
|
||||||
def test_create_subnet_dhcpv6_stateless_with_ip_already_allocated(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_create_subnet_ipv6_slaac_with_db_reference_error(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_create_subnet_ipv6_slaac_with_ip_already_allocated(self):
|
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
|
||||||
|
|
||||||
def test_subnet_update_ipv4_and_ipv6_pd_v6stateless_subnets(self):
|
def test_subnet_update_ipv4_and_ipv6_pd_v6stateless_subnets(self):
|
||||||
self.skipTest('Multiple fixed ips on a port are not supported')
|
self.skipTest('Multiple fixed ips on a port are not supported')
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ from neutron.tests.unit.extensions import test_l3 as test_l3_plugin
|
|||||||
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 external_net as extnet_apidef
|
||||||
|
from neutron_lib.api.definitions import extra_dhcp_opt as edo_ext
|
||||||
from neutron_lib.api.definitions import extraroute as xroute_apidef
|
from neutron_lib.api.definitions import extraroute as xroute_apidef
|
||||||
from neutron_lib.api.definitions import l3_ext_gw_mode as l3_egm_apidef
|
from neutron_lib.api.definitions import l3_ext_gw_mode as l3_egm_apidef
|
||||||
from neutron_lib.api.definitions import port_security as psec
|
from neutron_lib.api.definitions import port_security as psec
|
||||||
@ -574,10 +575,6 @@ class NsxPTestPorts(common_v3.NsxV3TestPorts,
|
|||||||
def setUp(self, **kwargs):
|
def setUp(self, **kwargs):
|
||||||
super(NsxPTestPorts, self).setUp(**kwargs)
|
super(NsxPTestPorts, self).setUp(**kwargs)
|
||||||
|
|
||||||
@common_v3.with_disable_dhcp
|
|
||||||
def test_requested_subnet_id_v4_and_v6(self):
|
|
||||||
return super(NsxPTestPorts, self).test_requested_subnet_id_v4_and_v6()
|
|
||||||
|
|
||||||
@common_v3.with_disable_dhcp
|
@common_v3.with_disable_dhcp
|
||||||
def test_requested_ips_only(self):
|
def test_requested_ips_only(self):
|
||||||
return super(NsxPTestPorts, self).test_requested_ips_only()
|
return super(NsxPTestPorts, self).test_requested_ips_only()
|
||||||
@ -1004,12 +1001,46 @@ class NsxPTestPorts(common_v3.NsxV3TestPorts,
|
|||||||
**kwargs)
|
**kwargs)
|
||||||
self.assertEqual(res.status_int, exc.HTTPBadRequest.code)
|
self.assertEqual(res.status_int, exc.HTTPBadRequest.code)
|
||||||
|
|
||||||
|
def test_create_ipv6_port(self):
|
||||||
|
with self.network(name='net') as network:
|
||||||
|
self._make_v6_subnet(network, constants.DHCPV6_STATEFUL)
|
||||||
|
res = self._create_port(self.fmt, net_id=network['network']['id'])
|
||||||
|
port = self.deserialize(self.fmt, res)
|
||||||
|
self.assertIn('id', port['port'])
|
||||||
|
|
||||||
|
def test_create_ipv6_port_with_extra_dhcp(self):
|
||||||
|
with self.network(name='net') as network:
|
||||||
|
self._make_v6_subnet(network, constants.DHCPV6_STATEFUL)
|
||||||
|
opt_list = [{'opt_name': 'bootfile-name',
|
||||||
|
'opt_value': 'pxelinux.0'},
|
||||||
|
{'opt_name': 'tftp-server-address',
|
||||||
|
'opt_value': '123.123.123.123'}]
|
||||||
|
params = {edo_ext.EXTRADHCPOPTS: opt_list,
|
||||||
|
'arg_list': (edo_ext.EXTRADHCPOPTS,)}
|
||||||
|
self._create_port(self.fmt, network['network']['id'],
|
||||||
|
exc.HTTPBadRequest.code, **params)
|
||||||
|
|
||||||
|
def test_update_ipv6_port_with_extra_dhcp(self):
|
||||||
|
with self.network(name='net') as network:
|
||||||
|
self._make_v6_subnet(network, constants.DHCPV6_STATEFUL)
|
||||||
|
res = self._create_port(self.fmt, net_id=network['network']['id'])
|
||||||
|
port = self.deserialize(self.fmt, res)
|
||||||
|
self.assertIn('id', port['port'])
|
||||||
|
|
||||||
|
opt_list = [{'opt_name': 'bootfile-name',
|
||||||
|
'opt_value': 'pxelinux.0'},
|
||||||
|
{'opt_name': 'tftp-server-address',
|
||||||
|
'opt_value': '123.123.123.123'}]
|
||||||
|
data = {'port': {edo_ext.EXTRADHCPOPTS: opt_list}}
|
||||||
|
req = self.new_update_request('ports', data, port['port']['id'])
|
||||||
|
res = self.deserialize(self.fmt, req.get_response(self.api))
|
||||||
|
self.assertIn('NeutronError', res)
|
||||||
|
|
||||||
|
|
||||||
class NsxPTestSubnets(common_v3.NsxV3TestSubnets,
|
class NsxPTestSubnets(common_v3.NsxV3TestSubnets,
|
||||||
NsxPPluginTestCaseMixin):
|
NsxPPluginTestCaseMixin):
|
||||||
def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None):
|
def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None):
|
||||||
super(NsxPTestSubnets, self).setUp(plugin=plugin, ext_mgr=ext_mgr)
|
super(NsxPTestSubnets, self).setUp(plugin=plugin, ext_mgr=ext_mgr)
|
||||||
self.force_slaac = False
|
|
||||||
|
|
||||||
def _create_subnet_bulk(self, fmt, number, net_id, name,
|
def _create_subnet_bulk(self, fmt, number, net_id, name,
|
||||||
ip_version=4, **kwargs):
|
ip_version=4, **kwargs):
|
||||||
@ -1033,17 +1064,6 @@ class NsxPTestSubnets(common_v3.NsxV3TestSubnets,
|
|||||||
kwargs.update({'override': overrides})
|
kwargs.update({'override': overrides})
|
||||||
return self._create_bulk(fmt, number, 'subnet', base_data, **kwargs)
|
return self._create_bulk(fmt, number, 'subnet', base_data, **kwargs)
|
||||||
|
|
||||||
def _test_create_subnet(self, network=None, expected=None, **kwargs):
|
|
||||||
# Until DHCPv6 is supported, switch all test to slaac-only
|
|
||||||
#TODO(asarfaty): remove when DHCPv6 is supported
|
|
||||||
if (self.force_slaac and
|
|
||||||
'ipv6_ra_mode' in kwargs and 'ipv6_address_mode' in kwargs):
|
|
||||||
kwargs['ipv6_ra_mode'] = constants.IPV6_SLAAC
|
|
||||||
kwargs['ipv6_address_mode'] = constants.IPV6_SLAAC
|
|
||||||
|
|
||||||
return super(NsxPTestSubnets,
|
|
||||||
self)._test_create_subnet(network, expected, **kwargs)
|
|
||||||
|
|
||||||
def test_create_external_subnet_with_conflicting_t0_address(self):
|
def test_create_external_subnet_with_conflicting_t0_address(self):
|
||||||
with self._create_l3_ext_network() as network:
|
with self._create_l3_ext_network() as network:
|
||||||
data = {'subnet': {'network_id': network['network']['id'],
|
data = {'subnet': {'network_id': network['network']['id'],
|
||||||
@ -1084,6 +1104,119 @@ class NsxPTestSubnets(common_v3.NsxV3TestSubnets,
|
|||||||
super(NsxPTestSubnets,
|
super(NsxPTestSubnets,
|
||||||
self).test_create_subnet_ipv6_slaac_with_port_on_network()
|
self).test_create_subnet_ipv6_slaac_with_port_on_network()
|
||||||
|
|
||||||
|
def test_create_subnet_ipv6_gw_values(self):
|
||||||
|
self.skipTest("IPv6 gateway IP is assigned by the plugin")
|
||||||
|
|
||||||
|
def test_create_ipv6_subnet_with_host_routes(self):
|
||||||
|
# IPv6 host routes are not allowed
|
||||||
|
with self.network() as network:
|
||||||
|
data = {'subnet': {'network_id': network['network']['id'],
|
||||||
|
'cidr': '100::/64',
|
||||||
|
'ip_version': 6,
|
||||||
|
'tenant_id': network['network']['tenant_id'],
|
||||||
|
'host_routes': [{'destination': '200::/64',
|
||||||
|
'nexthop': '100::16'}]}}
|
||||||
|
subnet_req = self.new_create_request('subnets', data)
|
||||||
|
res = subnet_req.get_response(self.api)
|
||||||
|
self.assertEqual(exc.HTTPClientError.code, res.status_int)
|
||||||
|
|
||||||
|
def test_update_ipv6_subnet_with_host_routes(self):
|
||||||
|
# IPv6 host routes are not allowed
|
||||||
|
with self.network() as network:
|
||||||
|
data = {'subnet': {'network_id': network['network']['id'],
|
||||||
|
'cidr': '100::/64',
|
||||||
|
'ip_version': 6,
|
||||||
|
'tenant_id': network['network']['tenant_id']}}
|
||||||
|
subnet_req = self.new_create_request('subnets', data)
|
||||||
|
subnet = self.deserialize(self.fmt,
|
||||||
|
subnet_req.get_response(self.api))
|
||||||
|
sub_id = subnet['subnet']['id']
|
||||||
|
# update host routes should fail
|
||||||
|
data = {'subnet': {'host_routes': [{'destination': '200::/64',
|
||||||
|
'nexthop': '100::16'}]}}
|
||||||
|
update_req = self.new_update_request('subnets', data, sub_id)
|
||||||
|
res = update_req.get_response(self.api)
|
||||||
|
self.assertEqual(exc.HTTPClientError.code, res.status_int)
|
||||||
|
|
||||||
|
def _verify_dhcp_service(self, network_id, tenant_id, enabled):
|
||||||
|
# Verify if DHCP service is enabled on a network.
|
||||||
|
port_res = self._list_ports('json', 200, network_id,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
device_owner=constants.DEVICE_OWNER_DHCP)
|
||||||
|
port_list = self.deserialize('json', port_res)
|
||||||
|
self.assertEqual(len(port_list['ports']) == 1, enabled)
|
||||||
|
|
||||||
|
def test_create_dhcpv6_subnet(self):
|
||||||
|
with mock.patch("vmware_nsxlib.v3.policy.core_resources."
|
||||||
|
"NsxPolicySegmentApi.update") as seg_update,\
|
||||||
|
self.subnet(ip_version=constants.IP_VERSION_6, cidr='fe80::/64',
|
||||||
|
enable_dhcp=True) as subnet:
|
||||||
|
self.assertEqual(True, subnet['subnet']['enable_dhcp'])
|
||||||
|
# verify that the dhcp port was created
|
||||||
|
self._verify_dhcp_service(subnet['subnet']['network_id'],
|
||||||
|
subnet['subnet']['tenant_id'], True)
|
||||||
|
# verify backend calls
|
||||||
|
seg_update.assert_called_once_with(
|
||||||
|
dhcp_server_config_id=NSX_DHCP_PROFILE_ID,
|
||||||
|
segment_id=subnet['subnet']['network_id'],
|
||||||
|
subnets=[mock.ANY])
|
||||||
|
|
||||||
|
def test_subnet_enable_dhcpv6(self):
|
||||||
|
with self.subnet(ip_version=constants.IP_VERSION_6, cidr='fe80::/64',
|
||||||
|
enable_dhcp=False) as subnet:
|
||||||
|
data = {'subnet': {'enable_dhcp': True}}
|
||||||
|
with mock.patch("vmware_nsxlib.v3.policy.core_resources."
|
||||||
|
"NsxPolicySegmentApi.update") as seg_update:
|
||||||
|
req = self.new_update_request('subnets', data,
|
||||||
|
subnet['subnet']['id'])
|
||||||
|
res = self.deserialize('json', req.get_response(self.api))
|
||||||
|
self.assertEqual(True, res['subnet']['enable_dhcp'])
|
||||||
|
# verify that the dhcp port was created
|
||||||
|
self._verify_dhcp_service(subnet['subnet']['network_id'],
|
||||||
|
subnet['subnet']['tenant_id'], True)
|
||||||
|
# verify backend calls
|
||||||
|
seg_update.assert_called_once_with(
|
||||||
|
dhcp_server_config_id=NSX_DHCP_PROFILE_ID,
|
||||||
|
segment_id=subnet['subnet']['network_id'],
|
||||||
|
subnets=[mock.ANY])
|
||||||
|
|
||||||
|
def test_subnet_disable_dhcpv6(self):
|
||||||
|
with self.subnet(ip_version=constants.IP_VERSION_6, cidr='fe80::/64',
|
||||||
|
enable_dhcp=True) as subnet:
|
||||||
|
data = {'subnet': {'enable_dhcp': False}}
|
||||||
|
with mock.patch("vmware_nsxlib.v3.policy.core_resources."
|
||||||
|
"NsxPolicySegmentApi.update") as seg_update:
|
||||||
|
req = self.new_update_request('subnets', data,
|
||||||
|
subnet['subnet']['id'])
|
||||||
|
res = self.deserialize('json', req.get_response(self.api))
|
||||||
|
self.assertEqual(False, res['subnet']['enable_dhcp'])
|
||||||
|
# verify that the dhcp port was deleted
|
||||||
|
self._verify_dhcp_service(subnet['subnet']['network_id'],
|
||||||
|
subnet['subnet']['tenant_id'], False)
|
||||||
|
# verify backend calls
|
||||||
|
seg_update.assert_called_once_with(
|
||||||
|
dhcp_server_config_id=None,
|
||||||
|
segment_id=subnet['subnet']['network_id'],
|
||||||
|
subnets=[])
|
||||||
|
|
||||||
|
def test_delete_ipv6_dhcp_subnet(self):
|
||||||
|
with self.subnet(ip_version=constants.IP_VERSION_6, cidr='fe80::/64',
|
||||||
|
enable_dhcp=True) as subnet:
|
||||||
|
with mock.patch("vmware_nsxlib.v3.policy.core_resources."
|
||||||
|
"NsxPolicySegmentApi.update") as seg_update:
|
||||||
|
req = self.new_delete_request(
|
||||||
|
'subnets', subnet['subnet']['id'])
|
||||||
|
res = req.get_response(self.api)
|
||||||
|
self.assertEqual(exc.HTTPNoContent.code, res.status_int)
|
||||||
|
# verify that the dhcp port was deleted
|
||||||
|
self._verify_dhcp_service(subnet['subnet']['network_id'],
|
||||||
|
subnet['subnet']['tenant_id'], False)
|
||||||
|
# verify backend calls
|
||||||
|
seg_update.assert_called_once_with(
|
||||||
|
dhcp_server_config_id=None,
|
||||||
|
segment_id=subnet['subnet']['network_id'],
|
||||||
|
subnets=[])
|
||||||
|
|
||||||
|
|
||||||
class NsxPTestSecurityGroup(common_v3.FixExternalNetBaseTest,
|
class NsxPTestSecurityGroup(common_v3.FixExternalNetBaseTest,
|
||||||
NsxPPluginTestCaseMixin,
|
NsxPPluginTestCaseMixin,
|
||||||
@ -1471,9 +1604,6 @@ class NsxPTestL3NatTestCase(NsxPTestL3NatTest,
|
|||||||
def test_router_add_gateway_multiple_subnets_ipv6(self):
|
def test_router_add_gateway_multiple_subnets_ipv6(self):
|
||||||
self.skipTest('not supported')
|
self.skipTest('not supported')
|
||||||
|
|
||||||
def test_router_add_interface_ipv6_subnet(self):
|
|
||||||
self.skipTest('DHCPv6 not supported')
|
|
||||||
|
|
||||||
def test_slaac_profile_single_subnet(self):
|
def test_slaac_profile_single_subnet(self):
|
||||||
with mock.patch("vmware_nsxlib.v3.policy.core_resources."
|
with mock.patch("vmware_nsxlib.v3.policy.core_resources."
|
||||||
"NsxPolicyTier1Api.update") as t1_update:
|
"NsxPolicyTier1Api.update") as t1_update:
|
||||||
@ -2268,6 +2398,8 @@ class NsxPTestL3NatTestCase(NsxPTestL3NatTest,
|
|||||||
False, pol_const.NAT_FIREWALL_MATCH_EXTERNAL)
|
False, pol_const.NAT_FIREWALL_MATCH_EXTERNAL)
|
||||||
|
|
||||||
def test_router_interface_with_dhcp_subnet(self):
|
def test_router_interface_with_dhcp_subnet(self):
|
||||||
|
# Policy DHCP does not allow 1 dhcp subnet and another router
|
||||||
|
# interface subnet on the same overlay network
|
||||||
with self.router() as r,\
|
with self.router() as r,\
|
||||||
self.network() as net,\
|
self.network() as net,\
|
||||||
self.subnet(cidr='20.0.0.0/24', network=net),\
|
self.subnet(cidr='20.0.0.0/24', network=net),\
|
||||||
@ -2277,3 +2409,252 @@ class NsxPTestL3NatTestCase(NsxPTestL3NatTest,
|
|||||||
'add', r['router']['id'],
|
'add', r['router']['id'],
|
||||||
if_subnet['subnet']['id'], None,
|
if_subnet['subnet']['id'], None,
|
||||||
expected_code=exc.HTTPBadRequest.code)
|
expected_code=exc.HTTPBadRequest.code)
|
||||||
|
|
||||||
|
def test_router_interface_ndprofile_ipv4(self):
|
||||||
|
with self.router() as r,\
|
||||||
|
self.network() as net,\
|
||||||
|
self.subnet(cidr='20.0.0.0/24', network=net) as if_subnet,\
|
||||||
|
mock.patch("vmware_nsxlib.v3.policy.core_resources."
|
||||||
|
"NsxPolicyTier1Api.update") as t1_update:
|
||||||
|
# Adding ipv4 interface
|
||||||
|
self._router_interface_action(
|
||||||
|
'add', r['router']['id'],
|
||||||
|
if_subnet['subnet']['id'], None)
|
||||||
|
t1_update.assert_not_called()
|
||||||
|
|
||||||
|
# Removing ipv4 interface
|
||||||
|
self._router_interface_action(
|
||||||
|
'remove', r['router']['id'],
|
||||||
|
if_subnet['subnet']['id'], None)
|
||||||
|
t1_update.assert_not_called()
|
||||||
|
|
||||||
|
def _test_router_interface_ndprofile(self, profile_with,
|
||||||
|
enable_dhcp=True, mode='slaac'):
|
||||||
|
with self.router() as r,\
|
||||||
|
self.network() as net,\
|
||||||
|
self.subnet(cidr='2001::/64', network=net,
|
||||||
|
ip_version=6, enable_dhcp=enable_dhcp,
|
||||||
|
ipv6_address_mode=mode,
|
||||||
|
ipv6_ra_mode=mode) as if_subnet,\
|
||||||
|
mock.patch("vmware_nsxlib.v3.policy.core_resources."
|
||||||
|
"NsxPolicyTier1Api.update") as t1_update:
|
||||||
|
# Adding subnet interface
|
||||||
|
self._router_interface_action(
|
||||||
|
'add', r['router']['id'],
|
||||||
|
if_subnet['subnet']['id'], None)
|
||||||
|
t1_update.assert_called_with(
|
||||||
|
r['router']['id'],
|
||||||
|
ipv6_ndra_profile_id=profile_with)
|
||||||
|
t1_update.reset_mock()
|
||||||
|
|
||||||
|
# Removing subnet interface
|
||||||
|
self._router_interface_action(
|
||||||
|
'remove', r['router']['id'],
|
||||||
|
if_subnet['subnet']['id'], None)
|
||||||
|
t1_update.assert_called_with(
|
||||||
|
r['router']['id'],
|
||||||
|
ipv6_ndra_profile_id=nsx_plugin.NO_SLAAC_NDRA_PROFILE_ID)
|
||||||
|
|
||||||
|
def test_router_interface_ndprofile_no_dhcp(self):
|
||||||
|
self._test_router_interface_ndprofile(
|
||||||
|
nsx_plugin.NO_SLAAC_NDRA_PROFILE_ID,
|
||||||
|
enable_dhcp=False, mode=None)
|
||||||
|
|
||||||
|
def test_router_interface_ndprofile_slaac(self):
|
||||||
|
self._test_router_interface_ndprofile(
|
||||||
|
nsx_plugin.SLAAC_NDRA_PROFILE_ID,
|
||||||
|
enable_dhcp=True, mode=constants.IPV6_SLAAC)
|
||||||
|
|
||||||
|
def test_router_interface_ndprofile_stateful(self):
|
||||||
|
self._test_router_interface_ndprofile(
|
||||||
|
nsx_plugin.STATEFUL_DHCP_NDRA_PROFILE_ID,
|
||||||
|
enable_dhcp=True, mode=constants.DHCPV6_STATEFUL)
|
||||||
|
|
||||||
|
def test_router_interface_ndprofile_stateless(self):
|
||||||
|
self._test_router_interface_ndprofile(
|
||||||
|
nsx_plugin.STATELESS_DHCP_NDRA_PROFILE_ID,
|
||||||
|
enable_dhcp=True, mode=constants.DHCPV6_STATELESS)
|
||||||
|
|
||||||
|
def _test_router_interfaces_ndprofile(self, sub1_enable_dhcp, sub1_mode,
|
||||||
|
sub2_enable_dhcp, sub2_mode,
|
||||||
|
sub1_profile, mixed_profile=None,
|
||||||
|
successful=True,
|
||||||
|
sub1_ipversion=6, sub2_ipversion=6):
|
||||||
|
cidr1 = '2001::/64' if sub1_ipversion == 6 else '201.0.0.0/24'
|
||||||
|
cidr2 = '2002::/64' if sub2_ipversion == 6 else '202.0.0.0/24'
|
||||||
|
with self.router() as r,\
|
||||||
|
self.network() as net1, self.network() as net2,\
|
||||||
|
self.subnet(cidr=cidr1, network=net1,
|
||||||
|
ip_version=sub1_ipversion,
|
||||||
|
enable_dhcp=sub1_enable_dhcp,
|
||||||
|
ipv6_address_mode=sub1_mode,
|
||||||
|
ipv6_ra_mode=sub1_mode) as sub1,\
|
||||||
|
self.subnet(cidr=cidr2, network=net2,
|
||||||
|
ip_version=sub2_ipversion,
|
||||||
|
enable_dhcp=sub2_enable_dhcp,
|
||||||
|
ipv6_address_mode=sub2_mode,
|
||||||
|
ipv6_ra_mode=sub2_mode) as sub2,\
|
||||||
|
mock.patch("vmware_nsxlib.v3.policy.core_resources."
|
||||||
|
"NsxPolicyTier1Api.update") as t1_update:
|
||||||
|
|
||||||
|
# Adding first interface
|
||||||
|
self._router_interface_action(
|
||||||
|
'add', r['router']['id'],
|
||||||
|
sub1['subnet']['id'], None)
|
||||||
|
if sub1_ipversion == 6:
|
||||||
|
t1_update.assert_called_with(
|
||||||
|
r['router']['id'],
|
||||||
|
ipv6_ndra_profile_id=sub1_profile)
|
||||||
|
t1_update.reset_mock()
|
||||||
|
else:
|
||||||
|
t1_update.assert_not_called()
|
||||||
|
|
||||||
|
# Adding the 2nd interface
|
||||||
|
expected_code = (exc.HTTPBadRequest.code if not successful
|
||||||
|
else exc.HTTPOk.code)
|
||||||
|
self._router_interface_action(
|
||||||
|
'add', r['router']['id'],
|
||||||
|
sub2['subnet']['id'], None,
|
||||||
|
expected_code=expected_code)
|
||||||
|
if not successful:
|
||||||
|
return
|
||||||
|
if sub2_ipversion == 6:
|
||||||
|
t1_update.assert_called_with(
|
||||||
|
r['router']['id'],
|
||||||
|
ipv6_ndra_profile_id=mixed_profile)
|
||||||
|
t1_update.reset_mock()
|
||||||
|
else:
|
||||||
|
t1_update.assert_not_called()
|
||||||
|
|
||||||
|
# Removing the 2nd interface
|
||||||
|
self._router_interface_action(
|
||||||
|
'remove', r['router']['id'],
|
||||||
|
sub2['subnet']['id'], None)
|
||||||
|
if sub2_ipversion == 6:
|
||||||
|
t1_update.assert_called_with(
|
||||||
|
r['router']['id'],
|
||||||
|
ipv6_ndra_profile_id=sub1_profile)
|
||||||
|
else:
|
||||||
|
t1_update.assert_not_called()
|
||||||
|
|
||||||
|
def test_router_interfaces_ndprofile_slaac_slaac(self):
|
||||||
|
self._test_router_interfaces_ndprofile(
|
||||||
|
True, constants.IPV6_SLAAC,
|
||||||
|
True, constants.IPV6_SLAAC,
|
||||||
|
nsx_plugin.SLAAC_NDRA_PROFILE_ID,
|
||||||
|
nsx_plugin.SLAAC_NDRA_PROFILE_ID)
|
||||||
|
|
||||||
|
def test_router_interfaces_ndprofile_slaac_stateful(self):
|
||||||
|
self._test_router_interfaces_ndprofile(
|
||||||
|
True, constants.IPV6_SLAAC,
|
||||||
|
True, constants.DHCPV6_STATEFUL,
|
||||||
|
nsx_plugin.SLAAC_NDRA_PROFILE_ID,
|
||||||
|
None, successful=False)
|
||||||
|
|
||||||
|
def test_router_interfaces_ndprofile_slaac_stateless(self):
|
||||||
|
self._test_router_interfaces_ndprofile(
|
||||||
|
True, constants.IPV6_SLAAC,
|
||||||
|
True, constants.DHCPV6_STATELESS,
|
||||||
|
nsx_plugin.SLAAC_NDRA_PROFILE_ID,
|
||||||
|
None, successful=False)
|
||||||
|
|
||||||
|
def test_router_interfaces_ndprofile_disabled_stateful(self):
|
||||||
|
self._test_router_interfaces_ndprofile(
|
||||||
|
False, None,
|
||||||
|
True, constants.DHCPV6_STATEFUL,
|
||||||
|
nsx_plugin.NO_SLAAC_NDRA_PROFILE_ID,
|
||||||
|
nsx_plugin.STATEFUL_DHCP_NDRA_PROFILE_ID)
|
||||||
|
|
||||||
|
def test_router_interfaces_ndprofile_disabled_stateless(self):
|
||||||
|
self._test_router_interfaces_ndprofile(
|
||||||
|
False, None,
|
||||||
|
True, constants.DHCPV6_STATELESS,
|
||||||
|
nsx_plugin.NO_SLAAC_NDRA_PROFILE_ID,
|
||||||
|
nsx_plugin.STATELESS_DHCP_NDRA_PROFILE_ID)
|
||||||
|
|
||||||
|
def test_router_interfaces_ndprofile_stateful_stateless(self):
|
||||||
|
self._test_router_interfaces_ndprofile(
|
||||||
|
True, constants.DHCPV6_STATEFUL,
|
||||||
|
True, constants.DHCPV6_STATELESS,
|
||||||
|
nsx_plugin.STATEFUL_DHCP_NDRA_PROFILE_ID,
|
||||||
|
None, successful=False)
|
||||||
|
|
||||||
|
def test_router_interfaces_ndprofile_v4_stateless(self):
|
||||||
|
self._test_router_interfaces_ndprofile(
|
||||||
|
True, None,
|
||||||
|
True, constants.DHCPV6_STATELESS,
|
||||||
|
nsx_plugin.NO_SLAAC_NDRA_PROFILE_ID,
|
||||||
|
nsx_plugin.STATELESS_DHCP_NDRA_PROFILE_ID,
|
||||||
|
sub1_ipversion=4)
|
||||||
|
|
||||||
|
def test_router_interfaces_ndprofile_stateless_v4(self):
|
||||||
|
self._test_router_interfaces_ndprofile(
|
||||||
|
True, constants.DHCPV6_STATELESS,
|
||||||
|
True, None,
|
||||||
|
nsx_plugin.STATELESS_DHCP_NDRA_PROFILE_ID,
|
||||||
|
nsx_plugin.STATELESS_DHCP_NDRA_PROFILE_ID,
|
||||||
|
sub2_ipversion=4)
|
||||||
|
|
||||||
|
def _test_router_vlan_interface_ndprofile(self, profile_with,
|
||||||
|
enable_dhcp=True, mode='slaac'):
|
||||||
|
providernet_args = {pnet.NETWORK_TYPE: 'vlan',
|
||||||
|
pnet.SEGMENTATION_ID: 11}
|
||||||
|
|
||||||
|
with mock.patch('vmware_nsxlib.v3.policy.core_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.router() as r,\
|
||||||
|
self.subnet(cidr='2001::/64', network=net,
|
||||||
|
ip_version=6, enable_dhcp=enable_dhcp,
|
||||||
|
ipv6_address_mode=mode,
|
||||||
|
ipv6_ra_mode=mode) as if_subnet,\
|
||||||
|
self._create_l3_ext_network() as ext_net,\
|
||||||
|
self.subnet(network=ext_net, cidr='10.0.0.0/16',
|
||||||
|
enable_dhcp=False) as ext_sub,\
|
||||||
|
mock.patch("vmware_nsxlib.v3.policy.core_resources."
|
||||||
|
"NsxPolicyTier1Api.add_segment_interface") as t1_add,\
|
||||||
|
mock.patch("vmware_nsxlib.v3.policy.core_resources."
|
||||||
|
"NsxPolicyTier1Api.remove_segment_interface") as t1_del:
|
||||||
|
|
||||||
|
# Add router GW
|
||||||
|
self._add_external_gateway_to_router(
|
||||||
|
r['router']['id'],
|
||||||
|
ext_sub['subnet']['network_id'])
|
||||||
|
|
||||||
|
# Adding subnet interface
|
||||||
|
self._router_interface_action(
|
||||||
|
'add', r['router']['id'],
|
||||||
|
if_subnet['subnet']['id'], None)
|
||||||
|
t1_add.assert_called_once_with(
|
||||||
|
r['router']['id'], mock.ANY, mock.ANY, [mock.ANY],
|
||||||
|
profile_with)
|
||||||
|
|
||||||
|
# Removing subnet interface
|
||||||
|
self._router_interface_action(
|
||||||
|
'remove', r['router']['id'],
|
||||||
|
if_subnet['subnet']['id'], None)
|
||||||
|
t1_del.assert_called_once_with(r['router']['id'], mock.ANY)
|
||||||
|
|
||||||
|
def test_router_vlan_interface_ndprofile_no_dhcp(self):
|
||||||
|
self._test_router_vlan_interface_ndprofile(
|
||||||
|
nsx_plugin.NO_SLAAC_NDRA_PROFILE_ID,
|
||||||
|
enable_dhcp=False, mode=None)
|
||||||
|
|
||||||
|
def test_router_vlan_interface_ndprofile_slaac(self):
|
||||||
|
self._test_router_vlan_interface_ndprofile(
|
||||||
|
nsx_plugin.SLAAC_NDRA_PROFILE_ID,
|
||||||
|
enable_dhcp=True, mode=constants.IPV6_SLAAC)
|
||||||
|
|
||||||
|
def test_router_vlan_interface_ndprofile_stateful(self):
|
||||||
|
self._test_router_vlan_interface_ndprofile(
|
||||||
|
nsx_plugin.STATEFUL_DHCP_NDRA_PROFILE_ID,
|
||||||
|
enable_dhcp=True, mode=constants.DHCPV6_STATEFUL)
|
||||||
|
|
||||||
|
def test_router_vlan_interface_ndprofile_stateless(self):
|
||||||
|
self._test_router_vlan_interface_ndprofile(
|
||||||
|
nsx_plugin.STATELESS_DHCP_NDRA_PROFILE_ID,
|
||||||
|
enable_dhcp=True, mode=constants.DHCPV6_STATELESS)
|
||||||
|
@ -104,8 +104,8 @@ class NsxPolicyDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
'host_routes': host_routes,
|
'host_routes': host_routes,
|
||||||
'shared': shared}}
|
'shared': shared}}
|
||||||
|
|
||||||
def _bind_name(self, port):
|
def _bind_name(self, port, ip_version=4):
|
||||||
return 'IPv4 binding for port %s' % port['port']['id']
|
return 'IPv%s binding for port %s' % (ip_version, port['port']['id'])
|
||||||
|
|
||||||
def _verify_dhcp_service(self, network_id, tenant_id, enabled):
|
def _verify_dhcp_service(self, network_id, tenant_id, enabled):
|
||||||
# Verify if DHCP service is enabled on a network.
|
# Verify if DHCP service is enabled on a network.
|
||||||
@ -174,6 +174,39 @@ class NsxPolicyDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
binding_id=port['port']['id'] + '-ipv4',
|
binding_id=port['port']['id'] + '-ipv4',
|
||||||
**binding_data)
|
**binding_data)
|
||||||
|
|
||||||
|
def _verify_dhcp_binding_v6(self, subnet, port_data, update_data,
|
||||||
|
assert_data):
|
||||||
|
# Verify if DHCP-v6 binding is updated.
|
||||||
|
with mock.patch('vmware_nsxlib.v3.policy.core_resources.'
|
||||||
|
'SegmentDhcpStaticBindingConfigApi.'
|
||||||
|
'create_or_overwrite_v6') as update_dhcp_binding:
|
||||||
|
device_owner = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'None'
|
||||||
|
device_id = uuidutils.generate_uuid()
|
||||||
|
with self.port(subnet=subnet, device_owner=device_owner,
|
||||||
|
device_id=device_id, **port_data) as port:
|
||||||
|
binding_name = self._bind_name(port, 6)
|
||||||
|
ip_address = port['port']['fixed_ips'][0]['ip_address']
|
||||||
|
binding_data = {'mac_address': port['port']['mac_address'],
|
||||||
|
'ip_addresses': [ip_address],
|
||||||
|
'lease_time': 86400}
|
||||||
|
# Verify the initial bindings call.
|
||||||
|
update_dhcp_binding.assert_called_once_with(
|
||||||
|
binding_name, subnet['subnet']['network_id'],
|
||||||
|
binding_id=port['port']['id'] + '-ipv6',
|
||||||
|
**binding_data)
|
||||||
|
update_dhcp_binding.reset_mock()
|
||||||
|
# Update the port with provided data.
|
||||||
|
self.plugin.update_port(
|
||||||
|
context.get_admin_context(), port['port']['id'],
|
||||||
|
update_data)
|
||||||
|
# Extend basic binding data with to-be-asserted data.
|
||||||
|
binding_data.update(assert_data)
|
||||||
|
# Verify the update call.
|
||||||
|
update_dhcp_binding.assert_called_once_with(
|
||||||
|
binding_name, subnet['subnet']['network_id'],
|
||||||
|
binding_id=port['port']['id'] + '-ipv6',
|
||||||
|
**binding_data)
|
||||||
|
|
||||||
def test_dhcp_service_with_create_network(self):
|
def test_dhcp_service_with_create_network(self):
|
||||||
# Test if DHCP service is disabled on a network when it is created.
|
# Test if DHCP service is disabled on a network when it is created.
|
||||||
with self.network() as network:
|
with self.network() as network:
|
||||||
@ -569,6 +602,20 @@ class NsxPolicyDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
self._verify_dhcp_binding(subnet, port_data, update_data,
|
self._verify_dhcp_binding(subnet, port_data, update_data,
|
||||||
assert_data)
|
assert_data)
|
||||||
|
|
||||||
|
def test_dhcp_binding_v6_with_update_port_ip(self):
|
||||||
|
# Test if DHCP binding is updated when the IP of the associated
|
||||||
|
# compute port is changed.
|
||||||
|
with self.subnet(ip_version=6, cidr='101::/64',
|
||||||
|
enable_dhcp=True) as subnet:
|
||||||
|
port_data = {'fixed_ips': [{'subnet_id': subnet['subnet']['id'],
|
||||||
|
'ip_address': '101::3'}]}
|
||||||
|
new_ip = '101::4'
|
||||||
|
update_data = {'port': {'fixed_ips': [
|
||||||
|
{'subnet_id': subnet['subnet']['id'], 'ip_address': new_ip}]}}
|
||||||
|
assert_data = {'ip_addresses': [new_ip]}
|
||||||
|
self._verify_dhcp_binding_v6(subnet, port_data, update_data,
|
||||||
|
assert_data)
|
||||||
|
|
||||||
def test_dhcp_binding_with_update_port_mac(self):
|
def test_dhcp_binding_with_update_port_mac(self):
|
||||||
# Test if DHCP binding is updated when the Mac of the associated
|
# Test if DHCP binding is updated when the Mac of the associated
|
||||||
# compute port is changed.
|
# compute port is changed.
|
||||||
@ -874,6 +921,18 @@ class NsxPolicyDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
context.get_admin_context(), neutron_subnet['id'],
|
context.get_admin_context(), neutron_subnet['id'],
|
||||||
{'subnet': {'enable_dhcp': True}})
|
{'subnet': {'enable_dhcp': True}})
|
||||||
|
|
||||||
|
def test_create_subnet_with_dhcp_v6_port(self):
|
||||||
|
with self.subnet(enable_dhcp=True, ip_version=6,
|
||||||
|
cidr="2002::/64") as subnet:
|
||||||
|
# find the dhcp port and verify it has port security disabled
|
||||||
|
ports = self.plugin.get_ports(
|
||||||
|
context.get_admin_context())
|
||||||
|
self.assertEqual(1, len(ports))
|
||||||
|
self.assertEqual('network:dhcp', ports[0]['device_owner'])
|
||||||
|
self.assertEqual(subnet['subnet']['network_id'],
|
||||||
|
ports[0]['network_id'])
|
||||||
|
self.assertEqual(False, ports[0]['port_security_enabled'])
|
||||||
|
|
||||||
|
|
||||||
class NsxPolicyMetadataTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
class NsxPolicyMetadataTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||||
"""Test native metadata config when using MP MDProxy"""
|
"""Test native metadata config when using MP MDProxy"""
|
||||||
|
@ -1007,6 +1007,36 @@ class TestSubnetsV2(common_v3.NsxV3TestSubnets, NsxV3PluginTestCaseMixin):
|
|||||||
self.plugin.update_subnet,
|
self.plugin.update_subnet,
|
||||||
context.get_admin_context(), subnet['id'], data)
|
context.get_admin_context(), subnet['id'], data)
|
||||||
|
|
||||||
|
def test_create_subnet_ipv6_gw_is_nw_start_addr(self):
|
||||||
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
|
|
||||||
|
def test_create_subnet_ipv6_gw_is_nw_start_addr_canonicalize(self):
|
||||||
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
|
|
||||||
|
def test_create_subnet_ipv6_gw_is_nw_end_addr(self):
|
||||||
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
|
|
||||||
|
def test_create_subnet_ipv6_first_ip_owned_by_router(self):
|
||||||
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
|
|
||||||
|
def test_create_subnet_ipv6_first_ip_owned_by_non_router(self):
|
||||||
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
|
|
||||||
|
def test_create_subnet_with_v6_pd_allocation_pool(self):
|
||||||
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
|
|
||||||
|
def test_create_subnet_with_v6_allocation_pool(self):
|
||||||
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
|
|
||||||
|
def test_update_subnet_ipv6_ra_mode_fails(self):
|
||||||
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
|
|
||||||
|
def test_create_subnet_ipv6_slaac_with_ip_already_allocated(self):
|
||||||
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
|
|
||||||
|
def test_create_subnet_ipv6_slaac_with_db_reference_error(self):
|
||||||
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
|
|
||||||
|
|
||||||
class TestPortsV2(common_v3.NsxV3SubnetMixin,
|
class TestPortsV2(common_v3.NsxV3SubnetMixin,
|
||||||
common_v3.NsxV3TestPorts, NsxV3PluginTestCaseMixin,
|
common_v3.NsxV3TestPorts, NsxV3PluginTestCaseMixin,
|
||||||
|
Loading…
Reference in New Issue
Block a user