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:
Anna Khmelnitsky 2018-12-31 14:45:33 -08:00 committed by Adit Sarfaty
parent 89d7b863f3
commit cd85073e7e
10 changed files with 644 additions and 650 deletions

View File

@ -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',

View File

@ -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

View File

@ -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"""

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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',

View File

@ -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')

View File

@ -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,