NSX|P: Handle subnet update and port dhcp bindings
Handle passthrough dhcp for updated subnet & ports binding Change-Id: I98986210f42e75f01815bbd2c863bc169a6cc7b4
This commit is contained in:
parent
89d7b863f3
commit
cd85073e7e
@ -370,10 +370,6 @@ nsx_v3_and_p = [
|
||||
default="169.254.169.254/31",
|
||||
help=_("The metadata route used for native metadata proxy "
|
||||
"service.")),
|
||||
cfg.StrOpt('dhcp_relay_service',
|
||||
help=_("(Optional) This is the name or UUID of the NSX dhcp "
|
||||
"relay service that will be used to enable DHCP relay "
|
||||
"on router ports.")),
|
||||
cfg.StrOpt('dns_domain',
|
||||
default='openstacklocal',
|
||||
help=_("Domain to use for building the hostnames.")),
|
||||
@ -402,6 +398,9 @@ nsx_v3_and_p = [
|
||||
help=_("This is the scope of the tag that will be used for "
|
||||
"finding the objects uuids on the NSX during plugin "
|
||||
"init.")),
|
||||
cfg.IntOpt('dhcp_lease_time',
|
||||
default=86400,
|
||||
help=_("DHCP default lease time.")),
|
||||
]
|
||||
|
||||
nsx_v3_opts = nsx_v3_and_p + [
|
||||
@ -459,9 +458,6 @@ nsx_v3_opts = nsx_v3_and_p + [
|
||||
default=True,
|
||||
help=_("If true, DHCP and metadata proxy services will be "
|
||||
"provided by NSX backend.")),
|
||||
cfg.IntOpt('dhcp_lease_time',
|
||||
default=86400,
|
||||
help=_("DHCP default lease time.")),
|
||||
cfg.ListOpt('switching_profiles',
|
||||
default=[],
|
||||
help=_("Optional parameter defining a list switching profiles "
|
||||
@ -476,6 +472,10 @@ nsx_v3_opts = nsx_v3_and_p + [
|
||||
help=_("When True, port security will be set to False for "
|
||||
"newly created ENS networks and ports, overriding "
|
||||
"user settings")),
|
||||
cfg.StrOpt('dhcp_relay_service',
|
||||
help=_("(Optional) This is the name or UUID of the NSX dhcp "
|
||||
"relay service that will be used to enable DHCP relay "
|
||||
"on router ports.")),
|
||||
cfg.ListOpt('housekeeping_jobs',
|
||||
default=['orphaned_dhcp_server', 'orphaned_logical_switch',
|
||||
'orphaned_logical_router', 'mismatch_logical_port',
|
||||
|
@ -85,10 +85,6 @@ class NsxV3AvailabilityZone(common_az.ConfiguredAvailabilityZone):
|
||||
if nameservers:
|
||||
self.nameservers = nameservers
|
||||
|
||||
dhcp_relay_service = az_info.get('dhcp_relay_service')
|
||||
if dhcp_relay_service:
|
||||
self.dhcp_relay_service = dhcp_relay_service
|
||||
|
||||
def init_defaults(self):
|
||||
# Should be implemented by children
|
||||
pass
|
||||
|
@ -27,6 +27,7 @@ from six import moves
|
||||
from neutron.db import agentschedulers_db
|
||||
from neutron.db import allowedaddresspairs_db as addr_pair_db
|
||||
from neutron.db.availability_zone import router as router_az_db
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import dns_db
|
||||
from neutron.db import external_net_db
|
||||
from neutron.db import extradhcpopt_db
|
||||
@ -34,6 +35,7 @@ from neutron.db import extraroute_db
|
||||
from neutron.db import l3_attrs_db
|
||||
from neutron.db import l3_db
|
||||
from neutron.db import l3_gwmode_db
|
||||
from neutron.db import models_v2
|
||||
from neutron.db import portbindings_db
|
||||
from neutron.db import portsecurity_db
|
||||
from neutron.db import securitygroups_db
|
||||
@ -42,6 +44,7 @@ from neutron.extensions import securitygroup as ext_sg
|
||||
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
|
||||
from neutron_lib.api.definitions import availability_zone as az_def
|
||||
from neutron_lib.api.definitions import external_net as extnet_apidef
|
||||
from neutron_lib.api.definitions import extra_dhcp_opt as ext_edo
|
||||
from neutron_lib.api.definitions import port_security as psec
|
||||
from neutron_lib.api.definitions import portbindings as pbin
|
||||
from neutron_lib.api.definitions import provider_net as pnet
|
||||
@ -79,6 +82,7 @@ from vmware_nsx.services.vpnaas.nsxv3 import ipsec_utils
|
||||
|
||||
from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
|
||||
from vmware_nsxlib.v3 import nsx_constants as nsxlib_consts
|
||||
from vmware_nsxlib.v3 import utils as nsxlib_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -115,20 +119,32 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
super(NsxPluginV3Base, self).__init__()
|
||||
self._network_vlans = plugin_utils.parse_network_vlan_ranges(
|
||||
self._get_conf_attr('network_vlan_ranges'))
|
||||
self._native_dhcp_enabled = False
|
||||
|
||||
def _init_native_dhcp(self):
|
||||
if not self.nsxlib:
|
||||
self._native_dhcp_enabled = False
|
||||
return
|
||||
|
||||
try:
|
||||
for az in self.get_azs_list():
|
||||
self.nsxlib.native_dhcp_profile.get(
|
||||
az._native_dhcp_profile_uuid)
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to retrieve DHCP Profile %s, "
|
||||
self._native_dhcp_enabled = True
|
||||
for az in self.get_azs_list():
|
||||
if not az._native_dhcp_profile_uuid:
|
||||
LOG.error("Unable to retrieve DHCP Profile %s for "
|
||||
"availability zone %s, "
|
||||
"native DHCP service is not supported",
|
||||
az._native_dhcp_profile_uuid)
|
||||
az.name, az.dhcp_profile)
|
||||
self._native_dhcp_enabled = False
|
||||
|
||||
def _init_native_metadata(self):
|
||||
if not self.nsxlib:
|
||||
return
|
||||
|
||||
for az in self.get_azs_list():
|
||||
if not az._native_md_proxy_uuid:
|
||||
LOG.error("Unable to retrieve Metadata Proxy %s for "
|
||||
"availability zone %s, "
|
||||
"native metadata service is not supported",
|
||||
az.name, az.metadata_proxy)
|
||||
|
||||
def _extend_fault_map(self):
|
||||
"""Extends the Neutron Fault Map.
|
||||
@ -150,19 +166,6 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
webob.exc.HTTPBadRequest,
|
||||
})
|
||||
|
||||
def _init_native_metadata(self):
|
||||
if not self.nsxlib:
|
||||
return
|
||||
|
||||
try:
|
||||
for az in self.get_azs_list():
|
||||
self.nsxlib.native_md_proxy.get(az._native_md_proxy_uuid)
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to retrieve Metadata Proxy %s, "
|
||||
"native metadata service is not supported",
|
||||
az._native_md_proxy_uuid)
|
||||
|
||||
def _get_conf_attr(self, attr):
|
||||
plugin_cfg = getattr(cfg.CONF, self.cfg_group)
|
||||
return getattr(plugin_cfg, attr)
|
||||
@ -458,6 +461,7 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
self._assert_on_external_net_with_compute(port_data)
|
||||
|
||||
self._assert_on_port_admin_state(port_data, device_owner)
|
||||
self._validate_extra_dhcp_options(port_data.get(ext_edo.EXTRADHCPOPTS))
|
||||
|
||||
def _assert_on_vpn_port_change(self, port_data):
|
||||
if port_data['device_owner'] == ipsec_utils.VPN_PORT_OWNER:
|
||||
@ -554,6 +558,7 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
port_data.get('fixed_ips', []), device_owner)
|
||||
self._assert_on_vpn_port_change(original_port)
|
||||
self._assert_on_lb_port_fixed_ip_change(port_data, orig_dev_owner)
|
||||
self._validate_extra_dhcp_options(port_data.get(ext_edo.EXTRADHCPOPTS))
|
||||
|
||||
def _get_dhcp_port_name(self, net_name, net_id):
|
||||
return utils.get_name_and_uuid('%s-%s' % ('dhcp',
|
||||
@ -1334,6 +1339,7 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
# Disable native DHCP service on the backend for this network.
|
||||
# First delete the DHCP port in this network. Then delete the
|
||||
# corresponding LogicalDhcpServer for this network.
|
||||
self._ensure_native_dhcp()
|
||||
dhcp_service = nsx_db.get_nsx_service_binding(
|
||||
context.session, network_id, nsxlib_consts.SERVICE_DHCP)
|
||||
if not dhcp_service:
|
||||
@ -1379,6 +1385,226 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
LOG.error("Unable to delete DHCP server mapping for "
|
||||
"network %s", network_id)
|
||||
|
||||
def _filter_ipv4_dhcp_fixed_ips(self, context, fixed_ips):
|
||||
ips = []
|
||||
for fixed_ip in fixed_ips:
|
||||
if netaddr.IPNetwork(fixed_ip['ip_address']).version != 4:
|
||||
continue
|
||||
with db_api.CONTEXT_READER.using(context):
|
||||
subnet = self.get_subnet(context, fixed_ip['subnet_id'])
|
||||
if subnet['enable_dhcp']:
|
||||
ips.append(fixed_ip)
|
||||
return ips
|
||||
|
||||
def _add_dhcp_binding(self, context, port):
|
||||
if not utils.is_port_dhcp_configurable(port):
|
||||
return
|
||||
dhcp_service = nsx_db.get_nsx_service_binding(
|
||||
context.session, port['network_id'], nsxlib_consts.SERVICE_DHCP)
|
||||
if not dhcp_service:
|
||||
return
|
||||
for fixed_ip in self._filter_ipv4_dhcp_fixed_ips(
|
||||
context, port['fixed_ips']):
|
||||
binding = self._add_dhcp_binding_on_server(
|
||||
context, dhcp_service['nsx_service_id'], fixed_ip['subnet_id'],
|
||||
fixed_ip['ip_address'], port)
|
||||
try:
|
||||
nsx_db.add_neutron_nsx_dhcp_binding(
|
||||
context.session, port['id'], fixed_ip['subnet_id'],
|
||||
fixed_ip['ip_address'], dhcp_service['nsx_service_id'],
|
||||
binding['id'])
|
||||
except (db_exc.DBError, sql_exc.TimeoutError):
|
||||
LOG.error("Failed to add mapping of DHCP binding "
|
||||
"%(binding)s for port %(port)s, deleting "
|
||||
"DHCP binding on server",
|
||||
{'binding': binding['id'], 'port': port['id']})
|
||||
fake_db_binding = {
|
||||
'port_id': port['id'],
|
||||
'nsx_service_id': dhcp_service['nsx_service_id'],
|
||||
'nsx_binding_id': binding['id']}
|
||||
self._delete_dhcp_binding_on_server(context, fake_db_binding)
|
||||
|
||||
def _add_dhcp_binding_on_server(self, context, dhcp_service_id, subnet_id,
|
||||
ip, port):
|
||||
try:
|
||||
hostname = 'host-%s' % ip.replace('.', '-')
|
||||
subnet = self.get_subnet(context, subnet_id)
|
||||
gateway_ip = subnet.get('gateway_ip')
|
||||
options = self._get_dhcp_options(
|
||||
context, ip, port.get(ext_edo.EXTRADHCPOPTS),
|
||||
port['network_id'], subnet)
|
||||
binding = self.nsxlib.dhcp_server.create_binding(
|
||||
dhcp_service_id, port['mac_address'], ip, hostname,
|
||||
self._get_conf_attr('dhcp_lease_time'), options, gateway_ip)
|
||||
LOG.debug("Created static binding (mac: %(mac)s, ip: %(ip)s, "
|
||||
"gateway: %(gateway)s, options: %(options)s) for port "
|
||||
"%(port)s on logical DHCP server %(server)s",
|
||||
{'mac': port['mac_address'], 'ip': ip,
|
||||
'gateway': gateway_ip, 'options': options,
|
||||
'port': port['id'],
|
||||
'server': dhcp_service_id})
|
||||
return binding
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to create static binding (mac: %(mac)s, "
|
||||
"ip: %(ip)s, gateway: %(gateway)s, options: "
|
||||
"%(options)s) for port %(port)s on logical DHCP "
|
||||
"server %(server)s",
|
||||
{'mac': port['mac_address'], 'ip': ip,
|
||||
'gateway': gateway_ip, 'options': options,
|
||||
'port': port['id'],
|
||||
'server': dhcp_service_id})
|
||||
|
||||
def _delete_dhcp_binding(self, context, port):
|
||||
# Do not check device_owner here because Nova may have already
|
||||
# deleted that before Neutron's port deletion.
|
||||
bindings = nsx_db.get_nsx_dhcp_bindings(context.session, port['id'])
|
||||
for binding in bindings:
|
||||
self._delete_dhcp_binding_on_server(context, binding)
|
||||
try:
|
||||
nsx_db.delete_neutron_nsx_dhcp_binding(
|
||||
context.session, binding['port_id'],
|
||||
binding['nsx_binding_id'])
|
||||
except db_exc.DBError:
|
||||
LOG.error("Unable to delete mapping of DHCP binding "
|
||||
"%(binding)s for port %(port)s",
|
||||
{'binding': binding['nsx_binding_id'],
|
||||
'port': binding['port_id']})
|
||||
|
||||
def _delete_dhcp_binding_on_server(self, context, binding):
|
||||
try:
|
||||
self.nsxlib.dhcp_server.delete_binding(
|
||||
binding['nsx_service_id'], binding['nsx_binding_id'])
|
||||
LOG.debug("Deleted static binding for port %(port)s) on "
|
||||
"logical DHCP server %(server)s",
|
||||
{'port': binding['port_id'],
|
||||
'server': binding['nsx_service_id']})
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to delete static binding for port "
|
||||
"%(port)s) on logical DHCP server %(server)s",
|
||||
{'port': binding['port_id'],
|
||||
'server': binding['nsx_service_id']})
|
||||
|
||||
def _find_dhcp_binding(self, subnet_id, ip_address, bindings):
|
||||
for binding in bindings:
|
||||
if (subnet_id == binding['subnet_id'] and
|
||||
ip_address == binding['ip_address']):
|
||||
return binding
|
||||
|
||||
def _update_dhcp_binding(self, context, old_port, new_port):
|
||||
# First check if any IPv4 address in fixed_ips is changed.
|
||||
# Then update DHCP server setting or DHCP static binding
|
||||
# depending on the port type.
|
||||
# Note that Neutron allows a port with multiple IPs in the
|
||||
# same subnet. But backend DHCP server may not support that.
|
||||
if (utils.is_port_dhcp_configurable(old_port) !=
|
||||
utils.is_port_dhcp_configurable(new_port)):
|
||||
# Note that the device_owner could be changed,
|
||||
# but still needs DHCP binding.
|
||||
if utils.is_port_dhcp_configurable(old_port):
|
||||
self._delete_dhcp_binding(context, old_port)
|
||||
else:
|
||||
self._add_dhcp_binding(context, new_port)
|
||||
return
|
||||
|
||||
# Collect IPv4 DHCP addresses from original and updated fixed_ips
|
||||
# in the form of [(subnet_id, ip_address)].
|
||||
old_fixed_ips = set([(fixed_ip['subnet_id'], fixed_ip['ip_address'])
|
||||
for fixed_ip in self._filter_ipv4_dhcp_fixed_ips(
|
||||
context, old_port['fixed_ips'])])
|
||||
new_fixed_ips = set([(fixed_ip['subnet_id'], fixed_ip['ip_address'])
|
||||
for fixed_ip in self._filter_ipv4_dhcp_fixed_ips(
|
||||
context, new_port['fixed_ips'])])
|
||||
# Find out the subnet/IP differences before and after the update.
|
||||
ips_to_add = list(new_fixed_ips - old_fixed_ips)
|
||||
ips_to_delete = list(old_fixed_ips - new_fixed_ips)
|
||||
ip_change = (ips_to_add or ips_to_delete)
|
||||
|
||||
if (old_port["device_owner"] == constants.DEVICE_OWNER_DHCP and
|
||||
ip_change):
|
||||
# Update backend DHCP server address if the IP address of a DHCP
|
||||
# port is changed.
|
||||
if len(new_fixed_ips) != 1:
|
||||
msg = _("Can only configure one IP address on a DHCP server")
|
||||
LOG.error(msg)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
# Locate the backend DHCP server for this DHCP port.
|
||||
dhcp_service = nsx_db.get_nsx_service_binding(
|
||||
context.session, old_port['network_id'],
|
||||
nsxlib_consts.SERVICE_DHCP)
|
||||
if dhcp_service:
|
||||
new_ip = ips_to_add[0][1]
|
||||
try:
|
||||
self.nsxlib.dhcp_server.update(
|
||||
dhcp_service['nsx_service_id'],
|
||||
server_ip=new_ip)
|
||||
LOG.debug("Updated IP %(ip)s for logical DHCP server "
|
||||
"%(server)s",
|
||||
{'ip': new_ip,
|
||||
'server': dhcp_service['nsx_service_id']})
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to update IP %(ip)s for logical "
|
||||
"DHCP server %(server)s",
|
||||
{'ip': new_ip,
|
||||
'server': dhcp_service['nsx_service_id']})
|
||||
elif utils.is_port_dhcp_configurable(old_port):
|
||||
# Update static DHCP bindings for a compute port.
|
||||
bindings = nsx_db.get_nsx_dhcp_bindings(context.session,
|
||||
old_port['id'])
|
||||
dhcp_opts = new_port.get(ext_edo.EXTRADHCPOPTS)
|
||||
dhcp_opts_changed = (old_port[ext_edo.EXTRADHCPOPTS] !=
|
||||
new_port[ext_edo.EXTRADHCPOPTS])
|
||||
if ip_change:
|
||||
# If IP address is changed, update associated DHCP bindings,
|
||||
# metadata route, and default hostname.
|
||||
# Mac address (if changed) will be updated at the same time.
|
||||
if ([subnet_id for (subnet_id, ip) in ips_to_add] ==
|
||||
[subnet_id for (subnet_id, ip) in ips_to_delete]):
|
||||
# No change on subnet_id, just update corresponding IPs.
|
||||
for i, (subnet_id, ip) in enumerate(ips_to_delete):
|
||||
binding = self._find_dhcp_binding(subnet_id, ip,
|
||||
bindings)
|
||||
if binding:
|
||||
subnet = self.get_subnet(context,
|
||||
binding['subnet_id'])
|
||||
self._update_dhcp_binding_on_server(
|
||||
context, binding, new_port['mac_address'],
|
||||
ips_to_add[i][1], old_port['network_id'],
|
||||
dhcp_opts=dhcp_opts, subnet=subnet)
|
||||
# Update DB IP
|
||||
nsx_db.update_nsx_dhcp_bindings(context.session,
|
||||
old_port['id'],
|
||||
ip,
|
||||
ips_to_add[i][1])
|
||||
else:
|
||||
for (subnet_id, ip) in ips_to_delete:
|
||||
binding = self._find_dhcp_binding(subnet_id, ip,
|
||||
bindings)
|
||||
if binding:
|
||||
self._delete_dhcp_binding_on_server(context,
|
||||
binding)
|
||||
if ips_to_add:
|
||||
dhcp_service = nsx_db.get_nsx_service_binding(
|
||||
context.session, new_port['network_id'],
|
||||
nsxlib_consts.SERVICE_DHCP)
|
||||
if dhcp_service:
|
||||
for (subnet_id, ip) in ips_to_add:
|
||||
self._add_dhcp_binding_on_server(
|
||||
context, dhcp_service['nsx_service_id'],
|
||||
subnet_id, ip, new_port)
|
||||
elif (old_port['mac_address'] != new_port['mac_address'] or
|
||||
dhcp_opts_changed):
|
||||
# If only Mac address/dhcp opts is changed,
|
||||
# update it in all associated DHCP bindings.
|
||||
for binding in bindings:
|
||||
subnet = self.get_subnet(context, binding['subnet_id'])
|
||||
self._update_dhcp_binding_on_server(
|
||||
context, binding, new_port['mac_address'],
|
||||
binding['ip_address'], old_port['network_id'],
|
||||
dhcp_opts=dhcp_opts, subnet=subnet)
|
||||
|
||||
def _cleanup_port(self, context, port_id, nsx_port_id=None):
|
||||
# Clean up neutron port and nsx manager port if provided
|
||||
# Does not handle cleanup of policy port
|
||||
@ -1481,6 +1707,25 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
# Validate against the configured AZs
|
||||
return self.validate_obj_azs(availability_zones)
|
||||
|
||||
def _ensure_nsxlib(self, feature):
|
||||
if not self.nsxlib:
|
||||
msg = (_("%s is not supported since passthough API is disabled") %
|
||||
feature)
|
||||
LOG.error(msg)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
|
||||
def _ensure_native_dhcp(self):
|
||||
self._ensure_nsxlib("Native DHCP")
|
||||
if not self._native_dhcp_enabled:
|
||||
msg = (_("Native DHCP is not supported since dhcp_profile is not"
|
||||
" provided in plugin configuration"))
|
||||
LOG.error(msg)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
|
||||
def _get_net_dhcp_relay(self, context, net_id):
|
||||
"""Should be implemented by each plugin"""
|
||||
pass
|
||||
|
||||
def _create_subnet(self, context, subnet):
|
||||
self._validate_host_routes_input(subnet)
|
||||
|
||||
@ -1489,6 +1734,7 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
if (native_metadata and subnet['subnet'].get('enable_dhcp', False)):
|
||||
self._validate_external_subnet(context,
|
||||
subnet['subnet']['network_id'])
|
||||
self._ensure_native_dhcp()
|
||||
lock = 'nsxv3_network_' + subnet['subnet']['network_id']
|
||||
ddi_support, ddi_type = self._is_ddi_supported_on_net_with_type(
|
||||
context, subnet['subnet']['network_id'])
|
||||
@ -1515,9 +1761,8 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
context, created_subnet['id'])
|
||||
self._extension_manager.process_create_subnet(context,
|
||||
subnet['subnet'], created_subnet)
|
||||
dhcp_relay = self.get_network_az_by_net_id(
|
||||
context,
|
||||
subnet['subnet']['network_id']).dhcp_relay_service
|
||||
dhcp_relay = self._get_net_dhcp_relay(
|
||||
context, subnet['subnet']['network_id'])
|
||||
if not dhcp_relay:
|
||||
if self.nsxlib:
|
||||
try:
|
||||
@ -1531,6 +1776,8 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
else:
|
||||
msg = (_("Native DHCP is not supported since "
|
||||
"passthough API is disabled"))
|
||||
self._enable_native_dhcp(context, network,
|
||||
created_subnet)
|
||||
msg = None
|
||||
else:
|
||||
msg = (_("Can not create more than one DHCP-enabled "
|
||||
@ -1698,6 +1945,266 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
return
|
||||
super(NsxPluginV3Base, self).delete_subnet(context, subnet_id)
|
||||
|
||||
def _update_subnet(self, context, subnet_id, subnet):
|
||||
updated_subnet = None
|
||||
orig_subnet = self.get_subnet(context, subnet_id)
|
||||
self._validate_host_routes_input(
|
||||
subnet,
|
||||
orig_enable_dhcp=orig_subnet['enable_dhcp'],
|
||||
orig_host_routes=orig_subnet['host_routes'])
|
||||
if self._has_native_dhcp_metadata():
|
||||
enable_dhcp = subnet['subnet'].get('enable_dhcp')
|
||||
if (enable_dhcp is not None and
|
||||
enable_dhcp != orig_subnet['enable_dhcp']):
|
||||
self._ensure_native_dhcp()
|
||||
lock = 'nsxv3_network_' + orig_subnet['network_id']
|
||||
with locking.LockManager.get_lock(lock):
|
||||
network = self._get_network(
|
||||
context, orig_subnet['network_id'])
|
||||
if enable_dhcp:
|
||||
(ddi_support,
|
||||
ddi_type) = self._is_ddi_supported_on_net_with_type(
|
||||
context, orig_subnet['network_id'])
|
||||
if ddi_support:
|
||||
if self._has_no_dhcp_enabled_subnet(
|
||||
context, network):
|
||||
updated_subnet = super(
|
||||
NsxPluginV3Base, self).update_subnet(
|
||||
context, subnet_id, subnet)
|
||||
self._extension_manager.process_update_subnet(
|
||||
context, subnet['subnet'], updated_subnet)
|
||||
self._enable_native_dhcp(context, network,
|
||||
updated_subnet)
|
||||
msg = None
|
||||
else:
|
||||
msg = (_("Multiple DHCP-enabled subnets is "
|
||||
"not allowed in network %s") %
|
||||
orig_subnet['network_id'])
|
||||
else:
|
||||
msg = (_("Native DHCP is not supported for "
|
||||
"%(type)s network %(id)s") %
|
||||
{'id': orig_subnet['network_id'],
|
||||
'type': ddi_type})
|
||||
if msg:
|
||||
LOG.error(msg)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
elif self._has_single_dhcp_enabled_subnet(context,
|
||||
network):
|
||||
self._disable_native_dhcp(context, network['id'])
|
||||
updated_subnet = super(
|
||||
NsxPluginV3Base, self).update_subnet(
|
||||
context, subnet_id, subnet)
|
||||
self._extension_manager.process_update_subnet(
|
||||
context, subnet['subnet'], updated_subnet)
|
||||
|
||||
if not updated_subnet:
|
||||
updated_subnet = super(NsxPluginV3Base, self).update_subnet(
|
||||
context, subnet_id, subnet)
|
||||
self._extension_manager.process_update_subnet(
|
||||
context, subnet['subnet'], updated_subnet)
|
||||
|
||||
# Check if needs to update logical DHCP server for native DHCP.
|
||||
if (self._has_native_dhcp_metadata() and
|
||||
updated_subnet['enable_dhcp']):
|
||||
self._ensure_native_dhcp()
|
||||
kwargs = {}
|
||||
for key in ('dns_nameservers', 'gateway_ip', 'host_routes'):
|
||||
if key in subnet['subnet']:
|
||||
value = subnet['subnet'][key]
|
||||
if value != orig_subnet[key]:
|
||||
kwargs[key] = value
|
||||
if key != 'dns_nameservers':
|
||||
kwargs['options'] = None
|
||||
if 'options' in kwargs:
|
||||
sr, gw_ip = self.nsxlib.native_dhcp.build_static_routes(
|
||||
updated_subnet.get('gateway_ip'),
|
||||
updated_subnet.get('cidr'),
|
||||
updated_subnet.get('host_routes', []))
|
||||
kwargs['options'] = {'option121': {'static_routes': sr}}
|
||||
kwargs.pop('host_routes', None)
|
||||
if (gw_ip is not None and 'gateway_ip' not in kwargs and
|
||||
gw_ip != updated_subnet['gateway_ip']):
|
||||
kwargs['gateway_ip'] = gw_ip
|
||||
if kwargs:
|
||||
dhcp_service = nsx_db.get_nsx_service_binding(
|
||||
context.session, orig_subnet['network_id'],
|
||||
nsxlib_consts.SERVICE_DHCP)
|
||||
if dhcp_service:
|
||||
try:
|
||||
self.nsxlib.dhcp_server.update(
|
||||
dhcp_service['nsx_service_id'], **kwargs)
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(
|
||||
"Unable to update logical DHCP server "
|
||||
"%(server)s for network %(network)s",
|
||||
{'server': dhcp_service['nsx_service_id'],
|
||||
'network': orig_subnet['network_id']})
|
||||
if 'options' in kwargs:
|
||||
# Need to update the static binding of every VM in
|
||||
# this logical DHCP server.
|
||||
bindings = nsx_db.get_nsx_dhcp_bindings_by_service(
|
||||
context.session, dhcp_service['nsx_service_id'])
|
||||
for binding in bindings:
|
||||
port = self._get_port(context, binding['port_id'])
|
||||
dhcp_opts = port.get(ext_edo.EXTRADHCPOPTS)
|
||||
self._update_dhcp_binding_on_server(
|
||||
context, binding, port['mac_address'],
|
||||
binding['ip_address'],
|
||||
port['network_id'],
|
||||
gateway_ip=kwargs.get('gateway_ip', False),
|
||||
dhcp_opts=dhcp_opts,
|
||||
options=kwargs.get('options'),
|
||||
subnet=updated_subnet)
|
||||
|
||||
return updated_subnet
|
||||
|
||||
def _has_active_port(self, context, network_id):
|
||||
ports_in_use = context.session.query(models_v2.Port).filter_by(
|
||||
network_id=network_id).all()
|
||||
return not all([p.device_owner in
|
||||
db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS
|
||||
for p in ports_in_use]) if ports_in_use else False
|
||||
|
||||
def _delete_network_disable_dhcp(self, context, network_id):
|
||||
# Disable native DHCP and delete DHCP ports before network deletion
|
||||
lock = 'nsxv3_network_' + network_id
|
||||
with locking.LockManager.get_lock(lock):
|
||||
# Disable native DHCP if there is no other existing port
|
||||
# besides DHCP port.
|
||||
if not self._has_active_port(context, network_id):
|
||||
self._disable_native_dhcp(context, network_id)
|
||||
|
||||
def _retry_delete_network(self, context, network_id):
|
||||
"""This method attempts to retry the delete on a network if there are
|
||||
AUTO_DELETE_PORT_OWNERS left. This is to avoid a race condition
|
||||
between delete_network and the dhcp creating a port on the network.
|
||||
"""
|
||||
first_try = True
|
||||
while True:
|
||||
try:
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
self._process_l3_delete(context, network_id)
|
||||
return super(NsxPluginV3Base, self).delete_network(
|
||||
context, network_id)
|
||||
except n_exc.NetworkInUse:
|
||||
# There is a race condition in delete_network() that we need
|
||||
# to work around here. delete_network() issues a query to
|
||||
# automatically delete DHCP ports and then checks to see if any
|
||||
# ports exist on the network. If a network is created and
|
||||
# deleted quickly, such as when running tempest, the DHCP agent
|
||||
# may be creating its port for the network around the same time
|
||||
# that the network is deleted. This can result in the DHCP
|
||||
# port getting created in between these two queries in
|
||||
# delete_network(). To work around that, we'll call
|
||||
# delete_network() a second time if we get a NetworkInUse
|
||||
# exception but the only port(s) that exist are ones that
|
||||
# delete_network() is supposed to automatically delete.
|
||||
if not first_try:
|
||||
# We tried once to work around the known race condition,
|
||||
# but we still got the exception, so something else is
|
||||
# wrong that we can't recover from.
|
||||
raise
|
||||
first_try = False
|
||||
if self._has_active_port(context, network_id):
|
||||
# There is a port on the network that is not going to be
|
||||
# automatically deleted (such as a tenant created port), so
|
||||
# we have nothing else to do but raise the exception.
|
||||
raise
|
||||
|
||||
def _get_dhcp_options(self, context, ip, extra_dhcp_opts, net_id,
|
||||
subnet):
|
||||
# Always add option121.
|
||||
net_az = self.get_network_az_by_net_id(context, net_id)
|
||||
options = {'option121': {'static_routes': [
|
||||
{'network': '%s' % net_az.native_metadata_route,
|
||||
'next_hop': '0.0.0.0'},
|
||||
{'network': '%s' % net_az.native_metadata_route,
|
||||
'next_hop': ip}]}}
|
||||
if subnet:
|
||||
sr, gateway_ip = self.nsxlib.native_dhcp.build_static_routes(
|
||||
subnet.get('gateway_ip'), subnet.get('cidr'),
|
||||
subnet.get('host_routes', []))
|
||||
options['option121']['static_routes'].extend(sr)
|
||||
# Adding extra options only if configured on port
|
||||
if extra_dhcp_opts:
|
||||
other_opts = []
|
||||
for opt in extra_dhcp_opts:
|
||||
opt_name = opt['opt_name']
|
||||
if opt['opt_value'] is not None:
|
||||
# None value means - delete this option. Since we rebuild
|
||||
# the options from scratch, it can be ignored.
|
||||
opt_val = opt['opt_value']
|
||||
if opt_name == 'classless-static-route':
|
||||
# Add to the option121 static routes
|
||||
net, ip = opt_val.split(',')
|
||||
options['option121']['static_routes'].append({
|
||||
'network': net, 'next_hop': ip})
|
||||
else:
|
||||
other_opts.append({
|
||||
'code': nsxlib_utils.get_dhcp_opt_code(opt_name),
|
||||
'values': [opt_val]})
|
||||
if other_opts:
|
||||
options['others'] = other_opts
|
||||
return options
|
||||
|
||||
def _update_dhcp_binding_on_server(self, context, binding, mac, ip,
|
||||
net_id, gateway_ip=False,
|
||||
dhcp_opts=None, options=None,
|
||||
subnet=None):
|
||||
try:
|
||||
data = {'mac_address': mac, 'ip_address': ip}
|
||||
if ip != binding['ip_address']:
|
||||
data['host_name'] = 'host-%s' % ip.replace('.', '-')
|
||||
data['options'] = self._get_dhcp_options(
|
||||
context, ip, dhcp_opts, net_id,
|
||||
subnet)
|
||||
elif (dhcp_opts is not None or
|
||||
options is not None):
|
||||
data['options'] = self._get_dhcp_options(
|
||||
context, ip, dhcp_opts, net_id,
|
||||
subnet)
|
||||
if gateway_ip is not False:
|
||||
# Note that None is valid for gateway_ip, means deleting it.
|
||||
data['gateway_ip'] = gateway_ip
|
||||
|
||||
self.nsxlib.dhcp_server.update_binding(
|
||||
binding['nsx_service_id'], binding['nsx_binding_id'], **data)
|
||||
LOG.debug("Updated static binding (mac: %(mac)s, ip: %(ip)s, "
|
||||
"gateway: %(gateway)s) for port %(port)s on "
|
||||
"logical DHCP server %(server)s",
|
||||
{'mac': mac, 'ip': ip, 'gateway': gateway_ip,
|
||||
'port': binding['port_id'],
|
||||
'server': binding['nsx_service_id']})
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to update static binding (mac: %(mac)s, "
|
||||
"ip: %(ip)s, gateway: %(gateway)s) for port "
|
||||
"%(port)s on logical DHCP server %(server)s",
|
||||
{'mac': mac, 'ip': ip, 'gateway': gateway_ip,
|
||||
'port': binding['port_id'],
|
||||
'server': binding['nsx_service_id']})
|
||||
|
||||
def _validate_extra_dhcp_options(self, opts):
|
||||
if not opts or not self._has_native_dhcp_metadata():
|
||||
return
|
||||
for opt in opts:
|
||||
opt_name = opt['opt_name']
|
||||
opt_val = opt['opt_value']
|
||||
if opt_name == 'classless-static-route':
|
||||
# separate validation for option121
|
||||
if opt_val is not None:
|
||||
try:
|
||||
net, ip = opt_val.split(',')
|
||||
except Exception:
|
||||
msg = (_("Bad value %(val)s for DHCP option "
|
||||
"%(name)s") % {'name': opt_name,
|
||||
'val': opt_val})
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
elif not nsxlib_utils.get_dhcp_opt_code(opt_name):
|
||||
msg = (_("DHCP option %s is not supported") % opt_name)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
|
||||
def _is_vlan_router_interface_supported(self):
|
||||
"""Should be implemented by each plugin"""
|
||||
|
||||
|
@ -129,8 +129,6 @@ class NsxPAvailabilityZone(v3_az.NsxV3AvailabilityZone):
|
||||
auto_config=True, is_mandatory=True,
|
||||
search_scope=search_scope)
|
||||
|
||||
self.dhcp_relay_service = cfg.CONF.nsx_p.dhcp_relay_service
|
||||
|
||||
# If passthrough api is supported, also initialize those NSX objects
|
||||
if nsxlib:
|
||||
self._translate_dhcp_profile(nsxlib, search_scope=search_scope)
|
||||
|
@ -30,6 +30,7 @@ from neutron.extensions import securitygroup as ext_sg
|
||||
from neutron.quota import resource_registry
|
||||
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
|
||||
from neutron_lib.api.definitions import external_net
|
||||
from neutron_lib.api.definitions import extra_dhcp_opt as ext_edo
|
||||
from neutron_lib.api.definitions import l3 as l3_apidef
|
||||
from neutron_lib.api.definitions import port_security as psec
|
||||
from neutron_lib.api.definitions import vlantransparent as vlan_apidef
|
||||
@ -483,12 +484,15 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
||||
return created_net
|
||||
|
||||
def delete_network(self, context, network_id):
|
||||
if cfg.CONF.nsx_p.allow_passthrough:
|
||||
self._delete_network_disable_dhcp(context, network_id)
|
||||
|
||||
is_nsx_net = self._network_is_nsx_net(context, network_id)
|
||||
is_external_net = self._network_is_external(context, network_id)
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
self._process_l3_delete(context, network_id)
|
||||
super(NsxPolicyPlugin, self).delete_network(
|
||||
context, network_id)
|
||||
|
||||
# First call DB operation for delete network as it will perform
|
||||
# checks on active ports
|
||||
self._retry_delete_network(context, network_id)
|
||||
|
||||
# MD Proxy is currently supported by the passthrough api only.
|
||||
# Use it to delete mdproxy ports
|
||||
@ -568,18 +572,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
||||
super(NsxPolicyPlugin, self).delete_subnet(context, subnet_id)
|
||||
|
||||
def update_subnet(self, context, subnet_id, subnet):
|
||||
updated_subnet = None
|
||||
orig = self._get_subnet(context, subnet_id)
|
||||
self._validate_host_routes_input(subnet,
|
||||
orig_enable_dhcp=orig['enable_dhcp'],
|
||||
orig_host_routes=orig['routes'])
|
||||
# TODO(asarfaty): Handle dhcp updates on the policy manager
|
||||
updated_subnet = super(NsxPolicyPlugin, self).update_subnet(
|
||||
context, subnet_id, subnet)
|
||||
self._extension_manager.process_update_subnet(
|
||||
context, subnet['subnet'], updated_subnet)
|
||||
|
||||
return updated_subnet
|
||||
return self._update_subnet(context, subnet_id, subnet)
|
||||
|
||||
def _build_port_address_bindings(self, context, port_data):
|
||||
psec_on, has_ip = self._determine_port_security_and_has_ip(context,
|
||||
@ -743,7 +736,9 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
||||
self._process_portbindings_create_and_update(
|
||||
context, port['port'], port_data,
|
||||
vif_type=self._vif_type_by_vnic_type(direct_vnic_type))
|
||||
|
||||
self._process_port_create_extra_dhcp_opts(
|
||||
context, port_data,
|
||||
port_data.get(ext_edo.EXTRADHCPOPTS))
|
||||
self._process_port_create_security_group(context, port_data, sgids)
|
||||
self._process_port_create_provider_security_group(
|
||||
context, port_data, psgids)
|
||||
@ -790,6 +785,18 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
||||
self._extend_nsx_port_dict_binding(context, port_data)
|
||||
self._remove_provider_security_groups_from_list(port_data)
|
||||
|
||||
# Add Mac/IP binding to native DHCP server and neutron DB.
|
||||
if cfg.CONF.nsx_p.allow_passthrough:
|
||||
try:
|
||||
self._add_dhcp_binding(context, port_data)
|
||||
except nsx_lib_exc.ManagerError:
|
||||
# Rollback create port
|
||||
self.delete_port(context, port_data['id'],
|
||||
force_delete_dhcp=True)
|
||||
msg = _('Unable to create port. Please contact admin')
|
||||
LOG.exception(msg)
|
||||
raise nsx_exc.NsxPluginException(err_msg=msg)
|
||||
|
||||
kwargs = {'context': context, 'port': neutron_db}
|
||||
registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)
|
||||
return port_data
|
||||
@ -805,8 +812,13 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
||||
# a l3 router. If so, we should prevent deletion here
|
||||
if l3_port_check:
|
||||
self.prevent_l3_port_deletion(context, port_id)
|
||||
self.disassociate_floatingips(context, port_id)
|
||||
super(NsxPolicyPlugin, self).delete_port(context, port_id)
|
||||
port = self.get_port(context, port_id)
|
||||
# Prevent DHCP port deletion if native support is enabled
|
||||
if (cfg.CONF.nsx_p.allow_passthrough and
|
||||
not force_delete_dhcp and
|
||||
port['device_owner'] in [const.DEVICE_OWNER_DHCP]):
|
||||
msg = (_('Can not delete DHCP port %s') % port_id)
|
||||
raise n_exc.BadRequest(resource='port', msg=msg)
|
||||
|
||||
if not self._network_is_external(context, net_id):
|
||||
try:
|
||||
@ -824,6 +836,14 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
||||
"due to %(e)s", {'id': port_id, 'e': ex})
|
||||
# Do not fail the neutron action
|
||||
|
||||
self.disassociate_floatingips(context, port_id)
|
||||
|
||||
# Remove Mac/IP binding from native DHCP server and neutron DB.
|
||||
if cfg.CONF.nsx_p.allow_passthrough:
|
||||
self._delete_dhcp_binding(context, port)
|
||||
|
||||
super(NsxPolicyPlugin, self).delete_port(context, port_id)
|
||||
|
||||
def _update_port_on_backend(self, context, lport_id,
|
||||
original_port, updated_port,
|
||||
is_psec_on, qos_policy_id):
|
||||
@ -871,6 +891,9 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
||||
validate_port_sec=validate_port_sec,
|
||||
direct_vnic_type=direct_vnic_type)
|
||||
|
||||
self._update_extra_dhcp_opts_on_port(context, port_id, port,
|
||||
updated_port)
|
||||
|
||||
sec_grp_updated = self.update_security_group_on_port(
|
||||
context, port_id, port, original_port, updated_port)
|
||||
|
||||
@ -919,6 +942,10 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
||||
context, port_id, original_port, updated_port,
|
||||
port_security, sec_grp_updated)
|
||||
|
||||
# Update DHCP bindings.
|
||||
if cfg.CONF.nsx_p.allow_passthrough:
|
||||
self._update_dhcp_binding(context, original_port, updated_port)
|
||||
|
||||
# Make sure the port revision is updated
|
||||
if 'revision_number' in updated_port:
|
||||
port_model = self._get_port(context, port_id)
|
||||
@ -2004,3 +2031,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
||||
'tz': tz_uuid,
|
||||
'net': sub['network_id']})
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
|
||||
def _get_net_dhcp_relay(self, context, net_id):
|
||||
# No dhcp relay support yet
|
||||
return None
|
||||
|
@ -45,6 +45,10 @@ class NsxV3AvailabilityZone(v3_az.NsxV3AvailabilityZone):
|
||||
if edge_cluster:
|
||||
self.edge_cluster = edge_cluster
|
||||
|
||||
dhcp_relay_service = az_info.get('dhcp_relay_service')
|
||||
if dhcp_relay_service:
|
||||
self.dhcp_relay_service = dhcp_relay_service
|
||||
|
||||
def init_defaults(self):
|
||||
# use the default configuration
|
||||
self.metadata_proxy = cfg.CONF.nsx_v3.metadata_proxy
|
||||
|
@ -36,7 +36,6 @@ from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
|
||||
from neutron.api.rpc.handlers import dhcp_rpc
|
||||
from neutron.api.rpc.handlers import metadata_rpc
|
||||
from neutron.db import agents_db
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import l3_db
|
||||
from neutron.db.models import l3 as l3_db_models
|
||||
from neutron.db.models import securitygroup as securitygroup_model # noqa
|
||||
@ -61,7 +60,6 @@ from oslo_log import log
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import importutils
|
||||
from oslo_utils import uuidutils
|
||||
from sqlalchemy import exc as sql_exc
|
||||
|
||||
from vmware_nsx._i18n import _
|
||||
from vmware_nsx.api_replay import utils as api_replay_utils
|
||||
@ -772,7 +770,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
||||
|
||||
def _setup_dhcp(self):
|
||||
"""Initialize components to support DHCP."""
|
||||
#TODO(asarfaty): move to common code and use in policy plugin too
|
||||
self.network_scheduler = importutils.import_object(
|
||||
cfg.CONF.network_scheduler_driver
|
||||
)
|
||||
@ -1041,58 +1038,9 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
||||
# Update the default port security to False if not set
|
||||
net_data[psec.PORTSECURITY] = False
|
||||
|
||||
def _has_active_port(self, context, network_id):
|
||||
ports_in_use = context.session.query(models_v2.Port).filter_by(
|
||||
network_id=network_id).all()
|
||||
return not all([p.device_owner in
|
||||
db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS
|
||||
for p in ports_in_use]) if ports_in_use else False
|
||||
|
||||
def _retry_delete_network(self, context, network_id):
|
||||
"""This method attempts to retry the delete on a network if there are
|
||||
AUTO_DELETE_PORT_OWNERS left. This is to avoid a race condition
|
||||
between delete_network and the dhcp creating a port on the network.
|
||||
"""
|
||||
first_try = True
|
||||
while True:
|
||||
try:
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
self._process_l3_delete(context, network_id)
|
||||
return super(NsxV3Plugin, self).delete_network(
|
||||
context, network_id)
|
||||
except n_exc.NetworkInUse:
|
||||
# There is a race condition in delete_network() that we need
|
||||
# to work around here. delete_network() issues a query to
|
||||
# automatically delete DHCP ports and then checks to see if any
|
||||
# ports exist on the network. If a network is created and
|
||||
# deleted quickly, such as when running tempest, the DHCP agent
|
||||
# may be creating its port for the network around the same time
|
||||
# that the network is deleted. This can result in the DHCP
|
||||
# port getting created in between these two queries in
|
||||
# delete_network(). To work around that, we'll call
|
||||
# delete_network() a second time if we get a NetworkInUse
|
||||
# exception but the only port(s) that exist are ones that
|
||||
# delete_network() is supposed to automatically delete.
|
||||
if not first_try:
|
||||
# We tried once to work around the known race condition,
|
||||
# but we still got the exception, so something else is
|
||||
# wrong that we can't recover from.
|
||||
raise
|
||||
first_try = False
|
||||
if self._has_active_port(context, network_id):
|
||||
# There is a port on the network that is not going to be
|
||||
# automatically deleted (such as a tenant created port), so
|
||||
# we have nothing else to do but raise the exception.
|
||||
raise
|
||||
|
||||
def delete_network(self, context, network_id):
|
||||
if cfg.CONF.nsx_v3.native_dhcp_metadata:
|
||||
lock = 'nsxv3_network_' + network_id
|
||||
with locking.LockManager.get_lock(lock):
|
||||
# Disable native DHCP if there is no other existing port
|
||||
# besides DHCP port.
|
||||
if not self._has_active_port(context, network_id):
|
||||
self._disable_native_dhcp(context, network_id)
|
||||
self._delete_network_disable_dhcp(context, network_id)
|
||||
|
||||
nsx_net_id = self._get_network_nsx_id(context, network_id)
|
||||
is_nsx_net = self._network_is_nsx_net(context, network_id)
|
||||
@ -1281,187 +1229,18 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
||||
"the NSX: %(e)s", {'id': network['id'], 'e': e})
|
||||
|
||||
def create_subnet(self, context, subnet):
|
||||
self._validate_host_routes_input(subnet)
|
||||
# TODO(berlin): public external subnet announcement
|
||||
if (cfg.CONF.nsx_v3.native_dhcp_metadata and
|
||||
subnet['subnet'].get('enable_dhcp', False)):
|
||||
self._validate_external_subnet(context,
|
||||
subnet['subnet']['network_id'])
|
||||
lock = 'nsxv3_network_' + subnet['subnet']['network_id']
|
||||
ddi_support, ddi_type = self._is_ddi_supported_on_net_with_type(
|
||||
context, subnet['subnet']['network_id'])
|
||||
with locking.LockManager.get_lock(lock):
|
||||
# Check if it is on an overlay network and is the first
|
||||
# DHCP-enabled subnet to create.
|
||||
if ddi_support:
|
||||
network = self._get_network(
|
||||
context, subnet['subnet']['network_id'])
|
||||
if self._has_no_dhcp_enabled_subnet(context, network):
|
||||
created_subnet = super(
|
||||
NsxV3Plugin, self).create_subnet(context, subnet)
|
||||
try:
|
||||
# This can be called only after the super create
|
||||
# since we need the subnet pool to be translated
|
||||
# to allocation pools
|
||||
self._validate_address_space(
|
||||
context, created_subnet)
|
||||
except n_exc.InvalidInput:
|
||||
# revert the subnet creation
|
||||
with excutils.save_and_reraise_exception():
|
||||
super(NsxV3Plugin, self).delete_subnet(
|
||||
context, created_subnet['id'])
|
||||
self._extension_manager.process_create_subnet(context,
|
||||
subnet['subnet'], created_subnet)
|
||||
dhcp_relay = self.get_network_az_by_net_id(
|
||||
context,
|
||||
subnet['subnet']['network_id']).dhcp_relay_service
|
||||
if not dhcp_relay:
|
||||
self._enable_native_dhcp(context, network,
|
||||
created_subnet)
|
||||
msg = None
|
||||
else:
|
||||
msg = (_("Can not create more than one DHCP-enabled "
|
||||
"subnet in network %s") %
|
||||
subnet['subnet']['network_id'])
|
||||
else:
|
||||
msg = _("Native DHCP is not supported for %(type)s "
|
||||
"network %(id)s") % {
|
||||
'id': subnet['subnet']['network_id'],
|
||||
'type': ddi_type}
|
||||
if msg:
|
||||
LOG.error(msg)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
else:
|
||||
created_subnet = super(NsxV3Plugin, self).create_subnet(
|
||||
context, subnet)
|
||||
try:
|
||||
# This can be called only after the super create
|
||||
# since we need the subnet pool to be translated
|
||||
# to allocation pools
|
||||
self._validate_address_space(context, created_subnet)
|
||||
except n_exc.InvalidInput:
|
||||
# revert the subnet creation
|
||||
with excutils.save_and_reraise_exception():
|
||||
super(NsxV3Plugin, self).delete_subnet(
|
||||
context, created_subnet['id'])
|
||||
return created_subnet
|
||||
return self._create_subnet(context, subnet)
|
||||
|
||||
def delete_subnet(self, context, subnet_id):
|
||||
# Call common V3 code to delete the subnet
|
||||
super(NsxV3Plugin, self).delete_subnet(context, subnet_id)
|
||||
|
||||
def update_subnet(self, context, subnet_id, subnet):
|
||||
updated_subnet = None
|
||||
orig_subnet = self.get_subnet(context, subnet_id)
|
||||
self._validate_host_routes_input(
|
||||
subnet,
|
||||
orig_enable_dhcp=orig_subnet['enable_dhcp'],
|
||||
orig_host_routes=orig_subnet['host_routes'])
|
||||
if cfg.CONF.nsx_v3.native_dhcp_metadata:
|
||||
enable_dhcp = subnet['subnet'].get('enable_dhcp')
|
||||
if (enable_dhcp is not None and
|
||||
enable_dhcp != orig_subnet['enable_dhcp']):
|
||||
lock = 'nsxv3_network_' + orig_subnet['network_id']
|
||||
with locking.LockManager.get_lock(lock):
|
||||
network = self._get_network(
|
||||
context, orig_subnet['network_id'])
|
||||
if enable_dhcp:
|
||||
(ddi_support,
|
||||
ddi_type) = self._is_ddi_supported_on_net_with_type(
|
||||
context, orig_subnet['network_id'])
|
||||
if ddi_support:
|
||||
if self._has_no_dhcp_enabled_subnet(
|
||||
context, network):
|
||||
updated_subnet = super(
|
||||
NsxV3Plugin, self).update_subnet(
|
||||
context, subnet_id, subnet)
|
||||
self._extension_manager.process_update_subnet(
|
||||
context, subnet['subnet'], updated_subnet)
|
||||
self._enable_native_dhcp(context, network,
|
||||
updated_subnet)
|
||||
msg = None
|
||||
else:
|
||||
msg = (_("Multiple DHCP-enabled subnets is "
|
||||
"not allowed in network %s") %
|
||||
orig_subnet['network_id'])
|
||||
else:
|
||||
msg = (_("Native DHCP is not supported for "
|
||||
"%(type)s network %(id)s") %
|
||||
{'id': orig_subnet['network_id'],
|
||||
'type': ddi_type})
|
||||
if msg:
|
||||
LOG.error(msg)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
elif self._has_single_dhcp_enabled_subnet(context,
|
||||
network):
|
||||
self._disable_native_dhcp(context, network['id'])
|
||||
updated_subnet = super(
|
||||
NsxV3Plugin, self).update_subnet(
|
||||
context, subnet_id, subnet)
|
||||
self._extension_manager.process_update_subnet(
|
||||
context, subnet['subnet'], updated_subnet)
|
||||
|
||||
if not updated_subnet:
|
||||
updated_subnet = super(NsxV3Plugin, self).update_subnet(
|
||||
context, subnet_id, subnet)
|
||||
self._extension_manager.process_update_subnet(
|
||||
context, subnet['subnet'], updated_subnet)
|
||||
|
||||
# Check if needs to update logical DHCP server for native DHCP.
|
||||
if (cfg.CONF.nsx_v3.native_dhcp_metadata and
|
||||
updated_subnet['enable_dhcp']):
|
||||
kwargs = {}
|
||||
for key in ('dns_nameservers', 'gateway_ip', 'host_routes'):
|
||||
if key in subnet['subnet']:
|
||||
value = subnet['subnet'][key]
|
||||
if value != orig_subnet[key]:
|
||||
kwargs[key] = value
|
||||
if key != 'dns_nameservers':
|
||||
kwargs['options'] = None
|
||||
if 'options' in kwargs:
|
||||
sr, gw_ip = self.nsxlib.native_dhcp.build_static_routes(
|
||||
updated_subnet.get('gateway_ip'),
|
||||
updated_subnet.get('cidr'),
|
||||
updated_subnet.get('host_routes', []))
|
||||
kwargs['options'] = {'option121': {'static_routes': sr}}
|
||||
kwargs.pop('host_routes', None)
|
||||
if (gw_ip is not None and 'gateway_ip' not in kwargs and
|
||||
gw_ip != updated_subnet['gateway_ip']):
|
||||
kwargs['gateway_ip'] = gw_ip
|
||||
if kwargs:
|
||||
dhcp_service = nsx_db.get_nsx_service_binding(
|
||||
context.session, orig_subnet['network_id'],
|
||||
nsxlib_consts.SERVICE_DHCP)
|
||||
if dhcp_service:
|
||||
try:
|
||||
self.nsxlib.dhcp_server.update(
|
||||
dhcp_service['nsx_service_id'], **kwargs)
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(
|
||||
"Unable to update logical DHCP server "
|
||||
"%(server)s for network %(network)s",
|
||||
{'server': dhcp_service['nsx_service_id'],
|
||||
'network': orig_subnet['network_id']})
|
||||
if 'options' in kwargs:
|
||||
# Need to update the static binding of every VM in
|
||||
# this logical DHCP server.
|
||||
bindings = nsx_db.get_nsx_dhcp_bindings_by_service(
|
||||
context.session, dhcp_service['nsx_service_id'])
|
||||
for binding in bindings:
|
||||
port = self._get_port(context, binding['port_id'])
|
||||
dhcp_opts = port.get(ext_edo.EXTRADHCPOPTS)
|
||||
self._update_dhcp_binding_on_server(
|
||||
context, binding, port['mac_address'],
|
||||
binding['ip_address'],
|
||||
port['network_id'],
|
||||
gateway_ip=kwargs.get('gateway_ip', False),
|
||||
dhcp_opts=dhcp_opts,
|
||||
options=kwargs.get('options'),
|
||||
subnet=updated_subnet)
|
||||
|
||||
updated_subnet = self._update_subnet(context,
|
||||
subnet_id,
|
||||
subnet)
|
||||
if (cfg.CONF.nsx_v3.metadata_on_demand and
|
||||
not cfg.CONF.nsx_v3.native_dhcp_metadata):
|
||||
not self._has_native_dhcp_metadata()):
|
||||
# If enable_dhcp is changed on a subnet attached to a router,
|
||||
# update internal metadata network accordingly.
|
||||
if 'enable_dhcp' in subnet['subnet']:
|
||||
@ -1716,323 +1495,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
||||
LOG.warning(err_msg)
|
||||
raise n_exc.InvalidInput(error_message=err_msg)
|
||||
|
||||
def _filter_ipv4_dhcp_fixed_ips(self, context, fixed_ips):
|
||||
ips = []
|
||||
for fixed_ip in fixed_ips:
|
||||
if netaddr.IPNetwork(fixed_ip['ip_address']).version != 4:
|
||||
continue
|
||||
with db_api.CONTEXT_READER.using(context):
|
||||
subnet = self.get_subnet(context, fixed_ip['subnet_id'])
|
||||
if subnet['enable_dhcp']:
|
||||
ips.append(fixed_ip)
|
||||
return ips
|
||||
|
||||
def _add_dhcp_binding(self, context, port):
|
||||
#TODO(asarfaty): move to common code and use in policy plugin too
|
||||
if not utils.is_port_dhcp_configurable(port):
|
||||
return
|
||||
dhcp_service = nsx_db.get_nsx_service_binding(
|
||||
context.session, port['network_id'], nsxlib_consts.SERVICE_DHCP)
|
||||
if not dhcp_service:
|
||||
return
|
||||
for fixed_ip in self._filter_ipv4_dhcp_fixed_ips(
|
||||
context, port['fixed_ips']):
|
||||
binding = self._add_dhcp_binding_on_server(
|
||||
context, dhcp_service['nsx_service_id'], fixed_ip['subnet_id'],
|
||||
fixed_ip['ip_address'], port)
|
||||
try:
|
||||
nsx_db.add_neutron_nsx_dhcp_binding(
|
||||
context.session, port['id'], fixed_ip['subnet_id'],
|
||||
fixed_ip['ip_address'], dhcp_service['nsx_service_id'],
|
||||
binding['id'])
|
||||
except (db_exc.DBError, sql_exc.TimeoutError):
|
||||
LOG.error("Failed to add mapping of DHCP binding "
|
||||
"%(binding)s for port %(port)s, deleting "
|
||||
"DHCP binding on server",
|
||||
{'binding': binding['id'], 'port': port['id']})
|
||||
fake_db_binding = {
|
||||
'port_id': port['id'],
|
||||
'nsx_service_id': dhcp_service['nsx_service_id'],
|
||||
'nsx_binding_id': binding['id']}
|
||||
self._delete_dhcp_binding_on_server(context, fake_db_binding)
|
||||
|
||||
def _validate_extra_dhcp_options(self, opts):
|
||||
if not opts or not cfg.CONF.nsx_v3.native_dhcp_metadata:
|
||||
return
|
||||
for opt in opts:
|
||||
opt_name = opt['opt_name']
|
||||
opt_val = opt['opt_value']
|
||||
if opt_name == 'classless-static-route':
|
||||
# separate validation for option121
|
||||
if opt_val is not None:
|
||||
try:
|
||||
net, ip = opt_val.split(',')
|
||||
except Exception:
|
||||
msg = (_("Bad value %(val)s for DHCP option "
|
||||
"%(name)s") % {'name': opt_name,
|
||||
'val': opt_val})
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
elif not self.nsxlib.dhcp_server.get_dhcp_opt_code(opt_name):
|
||||
msg = (_("DHCP option %s is not supported") % opt_name)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
|
||||
def _get_dhcp_options(self, context, ip, extra_dhcp_opts, net_id,
|
||||
subnet):
|
||||
#TODO(asarfaty): move to common code and use in policy plugin too
|
||||
# Always add option121.
|
||||
net_az = self.get_network_az_by_net_id(context, net_id)
|
||||
options = {'option121': {'static_routes': [
|
||||
{'network': '%s' % net_az.native_metadata_route,
|
||||
'next_hop': '0.0.0.0'},
|
||||
{'network': '%s' % net_az.native_metadata_route,
|
||||
'next_hop': ip}]}}
|
||||
if subnet:
|
||||
sr, gateway_ip = self.nsxlib.native_dhcp.build_static_routes(
|
||||
subnet.get('gateway_ip'), subnet.get('cidr'),
|
||||
subnet.get('host_routes', []))
|
||||
options['option121']['static_routes'].extend(sr)
|
||||
# Adding extra options only if configured on port
|
||||
if extra_dhcp_opts:
|
||||
other_opts = []
|
||||
for opt in extra_dhcp_opts:
|
||||
opt_name = opt['opt_name']
|
||||
if opt['opt_value'] is not None:
|
||||
# None value means - delete this option. Since we rebuild
|
||||
# the options from scratch, it can be ignored.
|
||||
opt_val = opt['opt_value']
|
||||
if opt_name == 'classless-static-route':
|
||||
# Add to the option121 static routes
|
||||
net, ip = opt_val.split(',')
|
||||
options['option121']['static_routes'].append({
|
||||
'network': net, 'next_hop': ip})
|
||||
else:
|
||||
other_opts.append({
|
||||
'code': self.nsxlib.dhcp_server.get_dhcp_opt_code(
|
||||
opt_name),
|
||||
'values': [opt_val]})
|
||||
if other_opts:
|
||||
options['others'] = other_opts
|
||||
return options
|
||||
|
||||
def _add_dhcp_binding_on_server(self, context, dhcp_service_id, subnet_id,
|
||||
ip, port):
|
||||
#TODO(asarfaty): move to common code and use in policy plugin too
|
||||
try:
|
||||
hostname = 'host-%s' % ip.replace('.', '-')
|
||||
subnet = self.get_subnet(context, subnet_id)
|
||||
gateway_ip = subnet.get('gateway_ip')
|
||||
options = self._get_dhcp_options(
|
||||
context, ip, port.get(ext_edo.EXTRADHCPOPTS),
|
||||
port['network_id'], subnet)
|
||||
binding = self.nsxlib.dhcp_server.create_binding(
|
||||
dhcp_service_id, port['mac_address'], ip, hostname,
|
||||
cfg.CONF.nsx_v3.dhcp_lease_time, options, gateway_ip)
|
||||
LOG.debug("Created static binding (mac: %(mac)s, ip: %(ip)s, "
|
||||
"gateway: %(gateway)s, options: %(options)s) for port "
|
||||
"%(port)s on logical DHCP server %(server)s",
|
||||
{'mac': port['mac_address'], 'ip': ip,
|
||||
'gateway': gateway_ip, 'options': options,
|
||||
'port': port['id'],
|
||||
'server': dhcp_service_id})
|
||||
return binding
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to create static binding (mac: %(mac)s, "
|
||||
"ip: %(ip)s, gateway: %(gateway)s, options: "
|
||||
"%(options)s) for port %(port)s on logical DHCP "
|
||||
"server %(server)s",
|
||||
{'mac': port['mac_address'], 'ip': ip,
|
||||
'gateway': gateway_ip, 'options': options,
|
||||
'port': port['id'],
|
||||
'server': dhcp_service_id})
|
||||
|
||||
def _delete_dhcp_binding(self, context, port):
|
||||
# Do not check device_owner here because Nova may have already
|
||||
# deleted that before Neutron's port deletion.
|
||||
bindings = nsx_db.get_nsx_dhcp_bindings(context.session, port['id'])
|
||||
for binding in bindings:
|
||||
self._delete_dhcp_binding_on_server(context, binding)
|
||||
try:
|
||||
nsx_db.delete_neutron_nsx_dhcp_binding(
|
||||
context.session, binding['port_id'],
|
||||
binding['nsx_binding_id'])
|
||||
except db_exc.DBError:
|
||||
LOG.error("Unable to delete mapping of DHCP binding "
|
||||
"%(binding)s for port %(port)s",
|
||||
{'binding': binding['nsx_binding_id'],
|
||||
'port': binding['port_id']})
|
||||
|
||||
def _delete_dhcp_binding_on_server(self, context, binding):
|
||||
try:
|
||||
self.nsxlib.dhcp_server.delete_binding(
|
||||
binding['nsx_service_id'], binding['nsx_binding_id'])
|
||||
LOG.debug("Deleted static binding for port %(port)s) on "
|
||||
"logical DHCP server %(server)s",
|
||||
{'port': binding['port_id'],
|
||||
'server': binding['nsx_service_id']})
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to delete static binding for port "
|
||||
"%(port)s) on logical DHCP server %(server)s",
|
||||
{'port': binding['port_id'],
|
||||
'server': binding['nsx_service_id']})
|
||||
|
||||
def _find_dhcp_binding(self, subnet_id, ip_address, bindings):
|
||||
for binding in bindings:
|
||||
if (subnet_id == binding['subnet_id'] and
|
||||
ip_address == binding['ip_address']):
|
||||
return binding
|
||||
|
||||
def _update_dhcp_binding(self, context, old_port, new_port):
|
||||
# First check if any IPv4 address in fixed_ips is changed.
|
||||
# Then update DHCP server setting or DHCP static binding
|
||||
# depending on the port type.
|
||||
# Note that Neutron allows a port with multiple IPs in the
|
||||
# same subnet. But backend DHCP server may not support that.
|
||||
|
||||
if (utils.is_port_dhcp_configurable(old_port) !=
|
||||
utils.is_port_dhcp_configurable(new_port)):
|
||||
# Note that the device_owner could be changed,
|
||||
# but still needs DHCP binding.
|
||||
if utils.is_port_dhcp_configurable(old_port):
|
||||
self._delete_dhcp_binding(context, old_port)
|
||||
else:
|
||||
self._add_dhcp_binding(context, new_port)
|
||||
return
|
||||
|
||||
# Collect IPv4 DHCP addresses from original and updated fixed_ips
|
||||
# in the form of [(subnet_id, ip_address)].
|
||||
old_fixed_ips = set([(fixed_ip['subnet_id'], fixed_ip['ip_address'])
|
||||
for fixed_ip in self._filter_ipv4_dhcp_fixed_ips(
|
||||
context, old_port['fixed_ips'])])
|
||||
new_fixed_ips = set([(fixed_ip['subnet_id'], fixed_ip['ip_address'])
|
||||
for fixed_ip in self._filter_ipv4_dhcp_fixed_ips(
|
||||
context, new_port['fixed_ips'])])
|
||||
# Find out the subnet/IP differences before and after the update.
|
||||
ips_to_add = list(new_fixed_ips - old_fixed_ips)
|
||||
ips_to_delete = list(old_fixed_ips - new_fixed_ips)
|
||||
ip_change = (ips_to_add or ips_to_delete)
|
||||
|
||||
if old_port["device_owner"] == const.DEVICE_OWNER_DHCP and ip_change:
|
||||
# Update backend DHCP server address if the IP address of a DHCP
|
||||
# port is changed.
|
||||
if len(new_fixed_ips) != 1:
|
||||
msg = _("Can only configure one IP address on a DHCP server")
|
||||
LOG.error(msg)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
# Locate the backend DHCP server for this DHCP port.
|
||||
dhcp_service = nsx_db.get_nsx_service_binding(
|
||||
context.session, old_port['network_id'],
|
||||
nsxlib_consts.SERVICE_DHCP)
|
||||
if dhcp_service:
|
||||
new_ip = ips_to_add[0][1]
|
||||
try:
|
||||
self.nsxlib.dhcp_server.update(
|
||||
dhcp_service['nsx_service_id'],
|
||||
server_ip=new_ip)
|
||||
LOG.debug("Updated IP %(ip)s for logical DHCP server "
|
||||
"%(server)s",
|
||||
{'ip': new_ip,
|
||||
'server': dhcp_service['nsx_service_id']})
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to update IP %(ip)s for logical "
|
||||
"DHCP server %(server)s",
|
||||
{'ip': new_ip,
|
||||
'server': dhcp_service['nsx_service_id']})
|
||||
elif utils.is_port_dhcp_configurable(old_port):
|
||||
# Update static DHCP bindings for a compute port.
|
||||
bindings = nsx_db.get_nsx_dhcp_bindings(context.session,
|
||||
old_port['id'])
|
||||
dhcp_opts = new_port.get(ext_edo.EXTRADHCPOPTS)
|
||||
dhcp_opts_changed = (old_port[ext_edo.EXTRADHCPOPTS] !=
|
||||
new_port[ext_edo.EXTRADHCPOPTS])
|
||||
if ip_change:
|
||||
# If IP address is changed, update associated DHCP bindings,
|
||||
# metadata route, and default hostname.
|
||||
# Mac address (if changed) will be updated at the same time.
|
||||
if ([subnet_id for (subnet_id, ip) in ips_to_add] ==
|
||||
[subnet_id for (subnet_id, ip) in ips_to_delete]):
|
||||
# No change on subnet_id, just update corresponding IPs.
|
||||
for i, (subnet_id, ip) in enumerate(ips_to_delete):
|
||||
binding = self._find_dhcp_binding(subnet_id, ip,
|
||||
bindings)
|
||||
if binding:
|
||||
subnet = self.get_subnet(context,
|
||||
binding['subnet_id'])
|
||||
self._update_dhcp_binding_on_server(
|
||||
context, binding, new_port['mac_address'],
|
||||
ips_to_add[i][1], old_port['network_id'],
|
||||
dhcp_opts=dhcp_opts, subnet=subnet)
|
||||
# Update DB IP
|
||||
nsx_db.update_nsx_dhcp_bindings(context.session,
|
||||
old_port['id'],
|
||||
ip,
|
||||
ips_to_add[i][1])
|
||||
else:
|
||||
for (subnet_id, ip) in ips_to_delete:
|
||||
binding = self._find_dhcp_binding(subnet_id, ip,
|
||||
bindings)
|
||||
if binding:
|
||||
self._delete_dhcp_binding_on_server(context,
|
||||
binding)
|
||||
if ips_to_add:
|
||||
dhcp_service = nsx_db.get_nsx_service_binding(
|
||||
context.session, new_port['network_id'],
|
||||
nsxlib_consts.SERVICE_DHCP)
|
||||
if dhcp_service:
|
||||
for (subnet_id, ip) in ips_to_add:
|
||||
self._add_dhcp_binding_on_server(
|
||||
context, dhcp_service['nsx_service_id'],
|
||||
subnet_id, ip, new_port)
|
||||
elif (old_port['mac_address'] != new_port['mac_address'] or
|
||||
dhcp_opts_changed):
|
||||
# If only Mac address/dhcp opts is changed,
|
||||
# update it in all associated DHCP bindings.
|
||||
for binding in bindings:
|
||||
subnet = self.get_subnet(context, binding['subnet_id'])
|
||||
self._update_dhcp_binding_on_server(
|
||||
context, binding, new_port['mac_address'],
|
||||
binding['ip_address'], old_port['network_id'],
|
||||
dhcp_opts=dhcp_opts, subnet=subnet)
|
||||
|
||||
def _update_dhcp_binding_on_server(self, context, binding, mac, ip,
|
||||
net_id, gateway_ip=False,
|
||||
dhcp_opts=None, options=None,
|
||||
subnet=None):
|
||||
try:
|
||||
data = {'mac_address': mac, 'ip_address': ip}
|
||||
if ip != binding['ip_address']:
|
||||
data['host_name'] = 'host-%s' % ip.replace('.', '-')
|
||||
data['options'] = self._get_dhcp_options(
|
||||
context, ip, dhcp_opts, net_id,
|
||||
subnet)
|
||||
elif (dhcp_opts is not None or
|
||||
options is not None):
|
||||
data['options'] = self._get_dhcp_options(
|
||||
context, ip, dhcp_opts, net_id,
|
||||
subnet)
|
||||
if gateway_ip is not False:
|
||||
# Note that None is valid for gateway_ip, means deleting it.
|
||||
data['gateway_ip'] = gateway_ip
|
||||
|
||||
self.nsxlib.dhcp_server.update_binding(
|
||||
binding['nsx_service_id'], binding['nsx_binding_id'], **data)
|
||||
LOG.debug("Updated static binding (mac: %(mac)s, ip: %(ip)s, "
|
||||
"gateway: %(gateway)s) for port %(port)s on "
|
||||
"logical DHCP server %(server)s",
|
||||
{'mac': mac, 'ip': ip, 'gateway': gateway_ip,
|
||||
'port': binding['port_id'],
|
||||
'server': binding['nsx_service_id']})
|
||||
except nsx_lib_exc.ManagerError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Unable to update static binding (mac: %(mac)s, "
|
||||
"ip: %(ip)s, gateway: %(gateway)s) for port "
|
||||
"%(port)s on logical DHCP server %(server)s",
|
||||
{'mac': mac, 'ip': ip, 'gateway': gateway_ip,
|
||||
'port': binding['port_id'],
|
||||
'server': binding['nsx_service_id']})
|
||||
|
||||
def _update_lport_with_security_groups(self, context, lport_id,
|
||||
original, updated):
|
||||
# translate the neutron sg ids to nsx ids, and call nsxlib
|
||||
@ -2062,9 +1524,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
||||
|
||||
# validate the new port parameters
|
||||
self._validate_create_port(context, port_data)
|
||||
# Add the plugin specific validations
|
||||
dhcp_opts = port_data.get(ext_edo.EXTRADHCPOPTS)
|
||||
self._validate_extra_dhcp_options(dhcp_opts)
|
||||
self._assert_on_dhcp_relay_without_router(context, port_data)
|
||||
is_ens_tz_port = self._is_ens_tz_port(context, port_data)
|
||||
if is_ens_tz_port:
|
||||
@ -2097,7 +1556,8 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
||||
context, port['port'], port_data,
|
||||
vif_type=self._vif_type_by_vnic_type(direct_vnic_type))
|
||||
self._process_port_create_extra_dhcp_opts(
|
||||
context, port_data, dhcp_opts)
|
||||
context, port_data,
|
||||
port_data.get(ext_edo.EXTRADHCPOPTS))
|
||||
|
||||
# handle adding security groups to port
|
||||
self._process_port_create_security_group(
|
||||
@ -2461,8 +1921,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
||||
self._assert_on_dhcp_relay_without_router(context, port_data,
|
||||
original_port)
|
||||
is_ens_tz_port = self._is_ens_tz_port(context, original_port)
|
||||
dhcp_opts = port_data.get(ext_edo.EXTRADHCPOPTS)
|
||||
self._validate_extra_dhcp_options(dhcp_opts)
|
||||
|
||||
direct_vnic_type = self._validate_port_vnic_type(
|
||||
context, port_data, original_port['network_id'])
|
||||
@ -3954,3 +3412,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
||||
|
||||
def _get_neutron_net_ids_by_nsx_id(self, context, lswitch_id):
|
||||
return nsx_db.get_net_ids(context.session, lswitch_id)
|
||||
|
||||
def _get_net_dhcp_relay(self, context, net_id):
|
||||
return self.get_network_az_by_net_id(
|
||||
context, net_id).dhcp_relay_service
|
||||
|
@ -114,8 +114,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
|
||||
def _verify_dhcp_binding(self, subnet, port_data, update_data,
|
||||
assert_data):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Verify if DHCP binding is updated.
|
||||
with mock.patch(
|
||||
'vmware_nsxlib.v3.resources.LogicalDhcpServer.update_binding'
|
||||
@ -308,8 +306,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
False)
|
||||
|
||||
def test_dhcp_service_with_update_dhcp_subnet(self):
|
||||
# TODO(asarfaty) : Enable this test after update subnet is supported
|
||||
return
|
||||
# Test if DHCP service is enabled on a network when a DHCP-disabled
|
||||
# subnet is updated to DHCP-enabled.
|
||||
with self.network() as network:
|
||||
@ -325,8 +321,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
True)
|
||||
|
||||
def test_dhcp_service_with_update_multiple_dhcp_subnets(self):
|
||||
# TODO(asarfaty) : Enable this test after update subnet is supported
|
||||
return
|
||||
# Test if a DHCP-disabled subnet cannot be updated to DHCP-enabled
|
||||
# if a DHCP-enabled subnet already exists in the same network.
|
||||
with self.network() as network:
|
||||
@ -344,8 +338,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
data)
|
||||
|
||||
def test_dhcp_service_with_update_dhcp_port(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test if DHCP server IP is updated when the corresponding DHCP port
|
||||
# IP is changed.
|
||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
||||
@ -367,8 +359,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
dhcp_service['nsx_service_id'], server_ip=new_ip)
|
||||
|
||||
def test_dhcp_binding_with_create_port(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test if DHCP binding is added when a compute port is created.
|
||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
||||
'create_binding',
|
||||
@ -403,8 +393,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
subnet['subnet']['gateway_ip'])
|
||||
|
||||
def test_dhcp_binding_with_create_port_with_opts(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test if DHCP binding is added when a compute port is created
|
||||
# with extra options.
|
||||
opt_name = 'interface-mtu'
|
||||
@ -448,8 +436,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
subnet['subnet']['gateway_ip'])
|
||||
|
||||
def test_dhcp_binding_with_create_port_with_opts121(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test if DHCP binding is added when a compute port is created
|
||||
# with extra option121.
|
||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
||||
@ -491,8 +477,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
subnet['subnet']['gateway_ip'])
|
||||
|
||||
def test_dhcp_binding_with_create_port_with_bad_opts(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
with self.subnet(enable_dhcp=True) as subnet:
|
||||
device_owner = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'None'
|
||||
device_id = uuidutils.generate_uuid()
|
||||
@ -523,8 +507,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
self.plugin.create_port, ctx, data)
|
||||
|
||||
def test_dhcp_binding_with_disable_enable_dhcp(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test if DHCP binding is preserved after DHCP is disabled and
|
||||
# re-enabled on a subnet.
|
||||
with self.subnet(enable_dhcp=True) as subnet:
|
||||
@ -559,8 +541,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
dhcp_bindings[0]['nsx_service_id'])
|
||||
|
||||
def test_dhcp_binding_with_delete_port(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test if DHCP binding is removed when the associated compute port
|
||||
# is deleted.
|
||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
||||
@ -580,8 +560,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
dhcp_binding['nsx_binding_id'])
|
||||
|
||||
def test_dhcp_binding_with_update_port_delete_ip(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test if DHCP binding is deleted when the IP of the associated
|
||||
# compute port is deleted.
|
||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
||||
@ -604,8 +582,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
dhcp_binding['nsx_binding_id'])
|
||||
|
||||
def test_dhcp_binding_with_update_port_ip(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test if DHCP binding is updated when the IP of the associated
|
||||
# compute port is changed.
|
||||
with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True) as subnet:
|
||||
@ -631,8 +607,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
assert_data)
|
||||
|
||||
def test_dhcp_binding_with_update_port_mac(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test if DHCP binding is updated when the Mac of the associated
|
||||
# compute port is changed.
|
||||
with self.subnet(enable_dhcp=True) as subnet:
|
||||
@ -683,8 +657,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
assert_data)
|
||||
|
||||
def test_update_port_with_update_dhcp_opt(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test updating extra-dhcp-opts via port update.
|
||||
with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True) as subnet:
|
||||
mac_address = '11:22:33:44:55:66'
|
||||
@ -717,8 +689,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
assert_data)
|
||||
|
||||
def test_update_port_with_adding_dhcp_opt(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test adding extra-dhcp-opts via port update.
|
||||
with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True) as subnet:
|
||||
mac_address = '11:22:33:44:55:66'
|
||||
@ -752,8 +722,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
assert_data)
|
||||
|
||||
def test_update_port_with_deleting_dhcp_opt(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test adding extra-dhcp-opts via port update.
|
||||
with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True) as subnet:
|
||||
mac_address = '11:22:33:44:55:66'
|
||||
@ -864,8 +832,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
||||
network['network']['tenant_id'], False)
|
||||
|
||||
def test_dhcp_binding_with_create_az_port(self):
|
||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
||||
return
|
||||
# Test if DHCP binding is added when a compute port is created.
|
||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
||||
'create_binding',
|
||||
|
@ -132,6 +132,11 @@ class NsxPPluginTestCaseMixin(
|
||||
"get_id_by_name_or_id",
|
||||
return_value=NSX_DHCP_PROFILE_ID).start()
|
||||
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.resources.LogicalDhcpServer."
|
||||
"get_id_by_name_or_id",
|
||||
return_value=_return_same).start()
|
||||
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.core_resources.NsxLibMetadataProxy."
|
||||
"get_id_by_name_or_id",
|
||||
@ -145,10 +150,17 @@ class NsxPPluginTestCaseMixin(
|
||||
"vmware_nsxlib.v3.resources.LogicalDhcpServer.create",
|
||||
side_effect=_return_id_key).start()
|
||||
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.resources.LogicalDhcpServer.update",
|
||||
side_effect=_return_id_key).start()
|
||||
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.resources.LogicalDhcpServer.create_binding",
|
||||
side_effect=_return_id_key).start()
|
||||
|
||||
mock.patch("vmware_nsxlib.v3.resources.LogicalDhcpServer."
|
||||
"update_binding").start()
|
||||
|
||||
mock.patch("vmware_nsxlib.v3.NsxLib."
|
||||
"get_id_by_resource_and_tag").start()
|
||||
|
||||
@ -602,8 +614,18 @@ class NsxPTestPorts(test_db_base_plugin_v2.TestPortsV2,
|
||||
def test_update_port_add_additional_ip(self):
|
||||
self.skipTest('Multiple fixed ips on a port are not supported')
|
||||
|
||||
def test_delete_network_port_exists_owned_by_network_race(self):
|
||||
self.skipTest('Skip need to address in future')
|
||||
|
||||
def test_delete_network_port_exists_owned_by_network_port_not_found(self):
|
||||
self.skipTest('Skip need to address in future')
|
||||
|
||||
def test_delete_network_port_exists_owned_by_network(self):
|
||||
self.skipTest('Skip need to address in future')
|
||||
|
||||
@with_disable_dhcp
|
||||
def test_duplicate_mac_generation(self):
|
||||
self.skipTest('No DHCP v6 Support yet')
|
||||
return super(NsxPTestPorts, self).test_duplicate_mac_generation()
|
||||
|
||||
@with_disable_dhcp
|
||||
@ -1028,6 +1050,9 @@ class NsxPTestSubnets(test_db_base_plugin_v2.TestSubnetsV2,
|
||||
super(NsxPTestSubnets, self).\
|
||||
test_delete_subnet_with_other_subnet_on_network_still_in_use()
|
||||
|
||||
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')
|
||||
|
||||
|
@ -287,15 +287,20 @@ class NsxV3PluginTestCaseMixin(test_plugin.NeutronDbPluginV2TestCase,
|
||||
self.plugin.init_availability_zones()
|
||||
self.plugin._translate_configured_names_to_uuids()
|
||||
|
||||
def _enable_native_dhcp_md(self):
|
||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
||||
cfg.CONF.set_override('dhcp_agent_notification', False)
|
||||
self.plugin._init_dhcp_metadata()
|
||||
|
||||
def _enable_dhcp_relay(self):
|
||||
# Add the relay service to the config and availability zones
|
||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
||||
cfg.CONF.set_override('dhcp_relay_service', NSX_DHCP_RELAY_SRV,
|
||||
'nsx_v3')
|
||||
mock_nsx_version = mock.patch.object(
|
||||
self.plugin.nsxlib, 'feature_supported', return_value=True)
|
||||
mock_nsx_version.start()
|
||||
self._initialize_azs()
|
||||
self._enable_native_dhcp_md()
|
||||
|
||||
|
||||
class TestNetworksV2(test_plugin.TestNetworksV2, NsxV3PluginTestCaseMixin):
|
||||
@ -322,7 +327,7 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxV3PluginTestCaseMixin):
|
||||
self.assertListEqual(az_hints, zone)
|
||||
|
||||
def test_network_failure_rollback(self):
|
||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
||||
self._enable_native_dhcp_md()
|
||||
self.plugin = directory.get_plugin()
|
||||
with mock.patch.object(self.plugin.nsxlib.logical_port, 'create',
|
||||
side_effect=api_exc.NsxApiException):
|
||||
@ -854,7 +859,7 @@ class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
||||
self.skipTest('Multiple fixed ips on a port are not supported')
|
||||
|
||||
def test_subnet_native_dhcp_subnet_enabled(self):
|
||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
||||
self._enable_native_dhcp_md()
|
||||
with self.network() as network:
|
||||
with mock.patch.object(self.plugin,
|
||||
'_enable_native_dhcp') as enable_dhcp,\
|
||||
@ -863,7 +868,7 @@ class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
||||
self.assertTrue(enable_dhcp.called)
|
||||
|
||||
def test_subnet_native_dhcp_subnet_disabled(self):
|
||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
||||
self._enable_native_dhcp_md()
|
||||
with self.network() as network:
|
||||
with mock.patch.object(self.plugin,
|
||||
'_enable_native_dhcp') as enable_dhcp,\
|
||||
@ -882,7 +887,7 @@ class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
||||
self.assertFalse(enable_dhcp.called)
|
||||
|
||||
def test_subnet_native_dhcp_flat_subnet_disabled(self):
|
||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
||||
self._enable_native_dhcp_md()
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'flat'}
|
||||
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||
'get_transport_type', return_value='VLAN'):
|
||||
@ -902,7 +907,7 @@ class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
||||
context.get_admin_context(), data)
|
||||
|
||||
def test_subnet_native_dhcp_flat_subnet_enabled(self):
|
||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
||||
self._enable_native_dhcp_md()
|
||||
providernet_args = {pnet.NETWORK_TYPE: 'flat'}
|
||||
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||
'get_transport_type', return_value='VLAN'):
|
||||
@ -994,7 +999,7 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
|
||||
data['port']['fixed_ips'])
|
||||
|
||||
def test_delete_dhcp_port(self):
|
||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
||||
self._enable_native_dhcp_md()
|
||||
with self.subnet():
|
||||
pl = directory.get_plugin()
|
||||
ctx = context.Context(user_id=None, tenant_id=self._tenant_id,
|
||||
@ -1485,7 +1490,7 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
|
||||
self.assertEqual(port2['port']['id'], ports_data['ports'][0]['id'])
|
||||
|
||||
def test_port_failure_rollback_dhcp_exception(self):
|
||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
||||
self._enable_native_dhcp_md()
|
||||
self.plugin = directory.get_plugin()
|
||||
with mock.patch.object(self.plugin, '_add_dhcp_binding',
|
||||
side_effect=nsxlib_exc.ManagerError):
|
||||
@ -1495,7 +1500,7 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
|
||||
self.assertListEqual([], networks)
|
||||
|
||||
def test_port_DB_failure_rollback_dhcp_exception(self):
|
||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
||||
self._enable_native_dhcp_md()
|
||||
self.plugin = directory.get_plugin()
|
||||
with mock.patch('vmware_nsx.db.db.add_neutron_nsx_dhcp_binding',
|
||||
side_effect=db_exc.DBError),\
|
||||
@ -1671,7 +1676,7 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
|
||||
self.assertTrue(res['port'][psec.PORTSECURITY])
|
||||
|
||||
def test_update_dhcp_port_device_owner(self):
|
||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
||||
self._enable_native_dhcp_md()
|
||||
with self.subnet():
|
||||
pl = directory.get_plugin()
|
||||
ctx = context.Context(user_id=None, tenant_id=self._tenant_id,
|
||||
|
Loading…
x
Reference in New Issue
Block a user