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",
|
default="169.254.169.254/31",
|
||||||
help=_("The metadata route used for native metadata proxy "
|
help=_("The metadata route used for native metadata proxy "
|
||||||
"service.")),
|
"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',
|
cfg.StrOpt('dns_domain',
|
||||||
default='openstacklocal',
|
default='openstacklocal',
|
||||||
help=_("Domain to use for building the hostnames.")),
|
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 "
|
help=_("This is the scope of the tag that will be used for "
|
||||||
"finding the objects uuids on the NSX during plugin "
|
"finding the objects uuids on the NSX during plugin "
|
||||||
"init.")),
|
"init.")),
|
||||||
|
cfg.IntOpt('dhcp_lease_time',
|
||||||
|
default=86400,
|
||||||
|
help=_("DHCP default lease time.")),
|
||||||
]
|
]
|
||||||
|
|
||||||
nsx_v3_opts = nsx_v3_and_p + [
|
nsx_v3_opts = nsx_v3_and_p + [
|
||||||
@ -459,9 +458,6 @@ nsx_v3_opts = nsx_v3_and_p + [
|
|||||||
default=True,
|
default=True,
|
||||||
help=_("If true, DHCP and metadata proxy services will be "
|
help=_("If true, DHCP and metadata proxy services will be "
|
||||||
"provided by NSX backend.")),
|
"provided by NSX backend.")),
|
||||||
cfg.IntOpt('dhcp_lease_time',
|
|
||||||
default=86400,
|
|
||||||
help=_("DHCP default lease time.")),
|
|
||||||
cfg.ListOpt('switching_profiles',
|
cfg.ListOpt('switching_profiles',
|
||||||
default=[],
|
default=[],
|
||||||
help=_("Optional parameter defining a list switching profiles "
|
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 "
|
help=_("When True, port security will be set to False for "
|
||||||
"newly created ENS networks and ports, overriding "
|
"newly created ENS networks and ports, overriding "
|
||||||
"user settings")),
|
"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',
|
cfg.ListOpt('housekeeping_jobs',
|
||||||
default=['orphaned_dhcp_server', 'orphaned_logical_switch',
|
default=['orphaned_dhcp_server', 'orphaned_logical_switch',
|
||||||
'orphaned_logical_router', 'mismatch_logical_port',
|
'orphaned_logical_router', 'mismatch_logical_port',
|
||||||
|
@ -85,10 +85,6 @@ class NsxV3AvailabilityZone(common_az.ConfiguredAvailabilityZone):
|
|||||||
if nameservers:
|
if nameservers:
|
||||||
self.nameservers = 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):
|
def init_defaults(self):
|
||||||
# Should be implemented by children
|
# Should be implemented by children
|
||||||
pass
|
pass
|
||||||
|
@ -27,6 +27,7 @@ from six import moves
|
|||||||
from neutron.db import agentschedulers_db
|
from neutron.db import agentschedulers_db
|
||||||
from neutron.db import allowedaddresspairs_db as addr_pair_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.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 dns_db
|
||||||
from neutron.db import external_net_db
|
from neutron.db import external_net_db
|
||||||
from neutron.db import extradhcpopt_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_attrs_db
|
||||||
from neutron.db import l3_db
|
from neutron.db import l3_db
|
||||||
from neutron.db import l3_gwmode_db
|
from neutron.db import l3_gwmode_db
|
||||||
|
from neutron.db import models_v2
|
||||||
from neutron.db import portbindings_db
|
from neutron.db import portbindings_db
|
||||||
from neutron.db import portsecurity_db
|
from neutron.db import portsecurity_db
|
||||||
from neutron.db import securitygroups_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 allowedaddresspairs as addr_apidef
|
||||||
from neutron_lib.api.definitions import availability_zone as az_def
|
from neutron_lib.api.definitions import availability_zone as az_def
|
||||||
from neutron_lib.api.definitions import external_net as extnet_apidef
|
from neutron_lib.api.definitions import 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 port_security as psec
|
||||||
from neutron_lib.api.definitions import portbindings as pbin
|
from neutron_lib.api.definitions import portbindings as pbin
|
||||||
from neutron_lib.api.definitions import provider_net as pnet
|
from neutron_lib.api.definitions import provider_net as pnet
|
||||||
@ -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 exceptions as nsx_lib_exc
|
||||||
from vmware_nsxlib.v3 import nsx_constants as nsxlib_consts
|
from vmware_nsxlib.v3 import nsx_constants as nsxlib_consts
|
||||||
|
from vmware_nsxlib.v3 import utils as nsxlib_utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -115,20 +119,32 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
super(NsxPluginV3Base, self).__init__()
|
super(NsxPluginV3Base, self).__init__()
|
||||||
self._network_vlans = plugin_utils.parse_network_vlan_ranges(
|
self._network_vlans = plugin_utils.parse_network_vlan_ranges(
|
||||||
self._get_conf_attr('network_vlan_ranges'))
|
self._get_conf_attr('network_vlan_ranges'))
|
||||||
|
self._native_dhcp_enabled = False
|
||||||
|
|
||||||
def _init_native_dhcp(self):
|
def _init_native_dhcp(self):
|
||||||
if not self.nsxlib:
|
if not self.nsxlib:
|
||||||
|
self._native_dhcp_enabled = False
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
self._native_dhcp_enabled = True
|
||||||
for az in self.get_azs_list():
|
for az in self.get_azs_list():
|
||||||
self.nsxlib.native_dhcp_profile.get(
|
if not az._native_dhcp_profile_uuid:
|
||||||
az._native_dhcp_profile_uuid)
|
LOG.error("Unable to retrieve DHCP Profile %s for "
|
||||||
except nsx_lib_exc.ManagerError:
|
"availability zone %s, "
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
LOG.error("Unable to retrieve DHCP Profile %s, "
|
|
||||||
"native DHCP service is not supported",
|
"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):
|
def _extend_fault_map(self):
|
||||||
"""Extends the Neutron Fault Map.
|
"""Extends the Neutron Fault Map.
|
||||||
@ -150,19 +166,6 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
webob.exc.HTTPBadRequest,
|
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):
|
def _get_conf_attr(self, attr):
|
||||||
plugin_cfg = getattr(cfg.CONF, self.cfg_group)
|
plugin_cfg = getattr(cfg.CONF, self.cfg_group)
|
||||||
return getattr(plugin_cfg, attr)
|
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_external_net_with_compute(port_data)
|
||||||
|
|
||||||
self._assert_on_port_admin_state(port_data, device_owner)
|
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):
|
def _assert_on_vpn_port_change(self, port_data):
|
||||||
if port_data['device_owner'] == ipsec_utils.VPN_PORT_OWNER:
|
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)
|
port_data.get('fixed_ips', []), device_owner)
|
||||||
self._assert_on_vpn_port_change(original_port)
|
self._assert_on_vpn_port_change(original_port)
|
||||||
self._assert_on_lb_port_fixed_ip_change(port_data, orig_dev_owner)
|
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):
|
def _get_dhcp_port_name(self, net_name, net_id):
|
||||||
return utils.get_name_and_uuid('%s-%s' % ('dhcp',
|
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.
|
# Disable native DHCP service on the backend for this network.
|
||||||
# First delete the DHCP port in this network. Then delete the
|
# First delete the DHCP port in this network. Then delete the
|
||||||
# corresponding LogicalDhcpServer for this network.
|
# corresponding LogicalDhcpServer for this network.
|
||||||
|
self._ensure_native_dhcp()
|
||||||
dhcp_service = nsx_db.get_nsx_service_binding(
|
dhcp_service = nsx_db.get_nsx_service_binding(
|
||||||
context.session, network_id, nsxlib_consts.SERVICE_DHCP)
|
context.session, network_id, nsxlib_consts.SERVICE_DHCP)
|
||||||
if not dhcp_service:
|
if not dhcp_service:
|
||||||
@ -1379,6 +1385,226 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
LOG.error("Unable to delete DHCP server mapping for "
|
LOG.error("Unable to delete DHCP server mapping for "
|
||||||
"network %s", network_id)
|
"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):
|
def _cleanup_port(self, context, port_id, nsx_port_id=None):
|
||||||
# Clean up neutron port and nsx manager port if provided
|
# Clean up neutron port and nsx manager port if provided
|
||||||
# Does not handle cleanup of policy port
|
# Does not handle cleanup of policy port
|
||||||
@ -1481,6 +1707,25 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
# Validate against the configured AZs
|
# Validate against the configured AZs
|
||||||
return self.validate_obj_azs(availability_zones)
|
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):
|
def _create_subnet(self, context, subnet):
|
||||||
self._validate_host_routes_input(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)):
|
if (native_metadata and subnet['subnet'].get('enable_dhcp', False)):
|
||||||
self._validate_external_subnet(context,
|
self._validate_external_subnet(context,
|
||||||
subnet['subnet']['network_id'])
|
subnet['subnet']['network_id'])
|
||||||
|
self._ensure_native_dhcp()
|
||||||
lock = 'nsxv3_network_' + subnet['subnet']['network_id']
|
lock = 'nsxv3_network_' + subnet['subnet']['network_id']
|
||||||
ddi_support, ddi_type = self._is_ddi_supported_on_net_with_type(
|
ddi_support, ddi_type = self._is_ddi_supported_on_net_with_type(
|
||||||
context, subnet['subnet']['network_id'])
|
context, subnet['subnet']['network_id'])
|
||||||
@ -1515,9 +1761,8 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
context, created_subnet['id'])
|
context, created_subnet['id'])
|
||||||
self._extension_manager.process_create_subnet(context,
|
self._extension_manager.process_create_subnet(context,
|
||||||
subnet['subnet'], created_subnet)
|
subnet['subnet'], created_subnet)
|
||||||
dhcp_relay = self.get_network_az_by_net_id(
|
dhcp_relay = self._get_net_dhcp_relay(
|
||||||
context,
|
context, subnet['subnet']['network_id'])
|
||||||
subnet['subnet']['network_id']).dhcp_relay_service
|
|
||||||
if not dhcp_relay:
|
if not dhcp_relay:
|
||||||
if self.nsxlib:
|
if self.nsxlib:
|
||||||
try:
|
try:
|
||||||
@ -1531,6 +1776,8 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
else:
|
else:
|
||||||
msg = (_("Native DHCP is not supported since "
|
msg = (_("Native DHCP is not supported since "
|
||||||
"passthough API is disabled"))
|
"passthough API is disabled"))
|
||||||
|
self._enable_native_dhcp(context, network,
|
||||||
|
created_subnet)
|
||||||
msg = None
|
msg = None
|
||||||
else:
|
else:
|
||||||
msg = (_("Can not create more than one DHCP-enabled "
|
msg = (_("Can not create more than one DHCP-enabled "
|
||||||
@ -1698,6 +1945,266 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
|||||||
return
|
return
|
||||||
super(NsxPluginV3Base, self).delete_subnet(context, subnet_id)
|
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):
|
def _is_vlan_router_interface_supported(self):
|
||||||
"""Should be implemented by each plugin"""
|
"""Should be implemented by each plugin"""
|
||||||
|
|
||||||
|
@ -129,8 +129,6 @@ class NsxPAvailabilityZone(v3_az.NsxV3AvailabilityZone):
|
|||||||
auto_config=True, is_mandatory=True,
|
auto_config=True, is_mandatory=True,
|
||||||
search_scope=search_scope)
|
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 passthrough api is supported, also initialize those NSX objects
|
||||||
if nsxlib:
|
if nsxlib:
|
||||||
self._translate_dhcp_profile(nsxlib, search_scope=search_scope)
|
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.quota import resource_registry
|
||||||
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
|
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
|
||||||
from neutron_lib.api.definitions import external_net
|
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 l3 as l3_apidef
|
||||||
from neutron_lib.api.definitions import port_security as psec
|
from neutron_lib.api.definitions import port_security as psec
|
||||||
from neutron_lib.api.definitions import vlantransparent as vlan_apidef
|
from neutron_lib.api.definitions import vlantransparent as vlan_apidef
|
||||||
@ -483,12 +484,15 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
return created_net
|
return created_net
|
||||||
|
|
||||||
def delete_network(self, context, network_id):
|
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_nsx_net = self._network_is_nsx_net(context, network_id)
|
||||||
is_external_net = self._network_is_external(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)
|
# First call DB operation for delete network as it will perform
|
||||||
super(NsxPolicyPlugin, self).delete_network(
|
# checks on active ports
|
||||||
context, network_id)
|
self._retry_delete_network(context, network_id)
|
||||||
|
|
||||||
# MD Proxy is currently supported by the passthrough api only.
|
# MD Proxy is currently supported by the passthrough api only.
|
||||||
# Use it to delete mdproxy ports
|
# Use it to delete mdproxy ports
|
||||||
@ -568,18 +572,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
super(NsxPolicyPlugin, self).delete_subnet(context, subnet_id)
|
super(NsxPolicyPlugin, self).delete_subnet(context, subnet_id)
|
||||||
|
|
||||||
def update_subnet(self, context, subnet_id, subnet):
|
def update_subnet(self, context, subnet_id, subnet):
|
||||||
updated_subnet = None
|
return self._update_subnet(context, subnet_id, subnet)
|
||||||
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
|
|
||||||
|
|
||||||
def _build_port_address_bindings(self, context, port_data):
|
def _build_port_address_bindings(self, context, port_data):
|
||||||
psec_on, has_ip = self._determine_port_security_and_has_ip(context,
|
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(
|
self._process_portbindings_create_and_update(
|
||||||
context, port['port'], port_data,
|
context, port['port'], port_data,
|
||||||
vif_type=self._vif_type_by_vnic_type(direct_vnic_type))
|
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_security_group(context, port_data, sgids)
|
||||||
self._process_port_create_provider_security_group(
|
self._process_port_create_provider_security_group(
|
||||||
context, port_data, psgids)
|
context, port_data, psgids)
|
||||||
@ -790,6 +785,18 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
self._extend_nsx_port_dict_binding(context, port_data)
|
self._extend_nsx_port_dict_binding(context, port_data)
|
||||||
self._remove_provider_security_groups_from_list(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}
|
kwargs = {'context': context, 'port': neutron_db}
|
||||||
registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)
|
registry.notify(resources.PORT, events.AFTER_CREATE, self, **kwargs)
|
||||||
return port_data
|
return port_data
|
||||||
@ -805,8 +812,13 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
# a l3 router. If so, we should prevent deletion here
|
# a l3 router. If so, we should prevent deletion here
|
||||||
if l3_port_check:
|
if l3_port_check:
|
||||||
self.prevent_l3_port_deletion(context, port_id)
|
self.prevent_l3_port_deletion(context, port_id)
|
||||||
self.disassociate_floatingips(context, port_id)
|
port = self.get_port(context, port_id)
|
||||||
super(NsxPolicyPlugin, self).delete_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):
|
if not self._network_is_external(context, net_id):
|
||||||
try:
|
try:
|
||||||
@ -824,6 +836,14 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
"due to %(e)s", {'id': port_id, 'e': ex})
|
"due to %(e)s", {'id': port_id, 'e': ex})
|
||||||
# Do not fail the neutron action
|
# 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,
|
def _update_port_on_backend(self, context, lport_id,
|
||||||
original_port, updated_port,
|
original_port, updated_port,
|
||||||
is_psec_on, qos_policy_id):
|
is_psec_on, qos_policy_id):
|
||||||
@ -871,6 +891,9 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
validate_port_sec=validate_port_sec,
|
validate_port_sec=validate_port_sec,
|
||||||
direct_vnic_type=direct_vnic_type)
|
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(
|
sec_grp_updated = self.update_security_group_on_port(
|
||||||
context, port_id, port, original_port, updated_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,
|
context, port_id, original_port, updated_port,
|
||||||
port_security, sec_grp_updated)
|
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
|
# Make sure the port revision is updated
|
||||||
if 'revision_number' in updated_port:
|
if 'revision_number' in updated_port:
|
||||||
port_model = self._get_port(context, port_id)
|
port_model = self._get_port(context, port_id)
|
||||||
@ -2004,3 +2031,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
|
|||||||
'tz': tz_uuid,
|
'tz': tz_uuid,
|
||||||
'net': sub['network_id']})
|
'net': sub['network_id']})
|
||||||
raise n_exc.InvalidInput(error_message=msg)
|
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:
|
if edge_cluster:
|
||||||
self.edge_cluster = 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):
|
def init_defaults(self):
|
||||||
# use the default configuration
|
# use the default configuration
|
||||||
self.metadata_proxy = cfg.CONF.nsx_v3.metadata_proxy
|
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 dhcp_rpc
|
||||||
from neutron.api.rpc.handlers import metadata_rpc
|
from neutron.api.rpc.handlers import metadata_rpc
|
||||||
from neutron.db import agents_db
|
from neutron.db import agents_db
|
||||||
from neutron.db import db_base_plugin_v2
|
|
||||||
from neutron.db import l3_db
|
from neutron.db import l3_db
|
||||||
from neutron.db.models import l3 as l3_db_models
|
from neutron.db.models import l3 as l3_db_models
|
||||||
from neutron.db.models import securitygroup as securitygroup_model # noqa
|
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 excutils
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
from sqlalchemy import exc as sql_exc
|
|
||||||
|
|
||||||
from vmware_nsx._i18n import _
|
from vmware_nsx._i18n import _
|
||||||
from vmware_nsx.api_replay import utils as api_replay_utils
|
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):
|
def _setup_dhcp(self):
|
||||||
"""Initialize components to support DHCP."""
|
"""Initialize components to support DHCP."""
|
||||||
#TODO(asarfaty): move to common code and use in policy plugin too
|
|
||||||
self.network_scheduler = importutils.import_object(
|
self.network_scheduler = importutils.import_object(
|
||||||
cfg.CONF.network_scheduler_driver
|
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
|
# Update the default port security to False if not set
|
||||||
net_data[psec.PORTSECURITY] = False
|
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):
|
def delete_network(self, context, network_id):
|
||||||
if cfg.CONF.nsx_v3.native_dhcp_metadata:
|
if cfg.CONF.nsx_v3.native_dhcp_metadata:
|
||||||
lock = 'nsxv3_network_' + network_id
|
self._delete_network_disable_dhcp(context, 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)
|
|
||||||
|
|
||||||
nsx_net_id = self._get_network_nsx_id(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)
|
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})
|
"the NSX: %(e)s", {'id': network['id'], 'e': e})
|
||||||
|
|
||||||
def create_subnet(self, context, subnet):
|
def create_subnet(self, context, subnet):
|
||||||
self._validate_host_routes_input(subnet)
|
return self._create_subnet(context, 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
|
|
||||||
|
|
||||||
def delete_subnet(self, context, subnet_id):
|
def delete_subnet(self, context, subnet_id):
|
||||||
# Call common V3 code to delete the subnet
|
# Call common V3 code to delete the subnet
|
||||||
super(NsxV3Plugin, self).delete_subnet(context, subnet_id)
|
super(NsxV3Plugin, self).delete_subnet(context, subnet_id)
|
||||||
|
|
||||||
def update_subnet(self, context, subnet_id, subnet):
|
def update_subnet(self, context, subnet_id, subnet):
|
||||||
updated_subnet = None
|
updated_subnet = self._update_subnet(context,
|
||||||
orig_subnet = self.get_subnet(context, subnet_id)
|
subnet_id,
|
||||||
self._validate_host_routes_input(
|
subnet)
|
||||||
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)
|
|
||||||
|
|
||||||
if (cfg.CONF.nsx_v3.metadata_on_demand and
|
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,
|
# If enable_dhcp is changed on a subnet attached to a router,
|
||||||
# update internal metadata network accordingly.
|
# update internal metadata network accordingly.
|
||||||
if 'enable_dhcp' in subnet['subnet']:
|
if 'enable_dhcp' in subnet['subnet']:
|
||||||
@ -1716,323 +1495,6 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
|||||||
LOG.warning(err_msg)
|
LOG.warning(err_msg)
|
||||||
raise n_exc.InvalidInput(error_message=err_msg)
|
raise n_exc.InvalidInput(error_message=err_msg)
|
||||||
|
|
||||||
def _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,
|
def _update_lport_with_security_groups(self, context, lport_id,
|
||||||
original, updated):
|
original, updated):
|
||||||
# translate the neutron sg ids to nsx ids, and call nsxlib
|
# 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
|
# validate the new port parameters
|
||||||
self._validate_create_port(context, port_data)
|
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)
|
self._assert_on_dhcp_relay_without_router(context, port_data)
|
||||||
is_ens_tz_port = self._is_ens_tz_port(context, port_data)
|
is_ens_tz_port = self._is_ens_tz_port(context, port_data)
|
||||||
if is_ens_tz_port:
|
if is_ens_tz_port:
|
||||||
@ -2097,7 +1556,8 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
|||||||
context, port['port'], port_data,
|
context, port['port'], port_data,
|
||||||
vif_type=self._vif_type_by_vnic_type(direct_vnic_type))
|
vif_type=self._vif_type_by_vnic_type(direct_vnic_type))
|
||||||
self._process_port_create_extra_dhcp_opts(
|
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
|
# handle adding security groups to port
|
||||||
self._process_port_create_security_group(
|
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,
|
self._assert_on_dhcp_relay_without_router(context, port_data,
|
||||||
original_port)
|
original_port)
|
||||||
is_ens_tz_port = self._is_ens_tz_port(context, 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(
|
direct_vnic_type = self._validate_port_vnic_type(
|
||||||
context, port_data, original_port['network_id'])
|
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):
|
def _get_neutron_net_ids_by_nsx_id(self, context, lswitch_id):
|
||||||
return nsx_db.get_net_ids(context.session, 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,
|
def _verify_dhcp_binding(self, subnet, port_data, update_data,
|
||||||
assert_data):
|
assert_data):
|
||||||
# TODO(asarfaty)- Enabled this once port dhcp binding is supported
|
|
||||||
return
|
|
||||||
# Verify if DHCP binding is updated.
|
# Verify if DHCP binding is updated.
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
'vmware_nsxlib.v3.resources.LogicalDhcpServer.update_binding'
|
'vmware_nsxlib.v3.resources.LogicalDhcpServer.update_binding'
|
||||||
@ -308,8 +306,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
False)
|
False)
|
||||||
|
|
||||||
def test_dhcp_service_with_update_dhcp_subnet(self):
|
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
|
# Test if DHCP service is enabled on a network when a DHCP-disabled
|
||||||
# subnet is updated to DHCP-enabled.
|
# subnet is updated to DHCP-enabled.
|
||||||
with self.network() as network:
|
with self.network() as network:
|
||||||
@ -325,8 +321,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
True)
|
True)
|
||||||
|
|
||||||
def test_dhcp_service_with_update_multiple_dhcp_subnets(self):
|
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
|
# Test if a DHCP-disabled subnet cannot be updated to DHCP-enabled
|
||||||
# if a DHCP-enabled subnet already exists in the same network.
|
# if a DHCP-enabled subnet already exists in the same network.
|
||||||
with self.network() as network:
|
with self.network() as network:
|
||||||
@ -344,8 +338,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
data)
|
data)
|
||||||
|
|
||||||
def test_dhcp_service_with_update_dhcp_port(self):
|
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
|
# Test if DHCP server IP is updated when the corresponding DHCP port
|
||||||
# IP is changed.
|
# IP is changed.
|
||||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
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)
|
dhcp_service['nsx_service_id'], server_ip=new_ip)
|
||||||
|
|
||||||
def test_dhcp_binding_with_create_port(self):
|
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.
|
# Test if DHCP binding is added when a compute port is created.
|
||||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
||||||
'create_binding',
|
'create_binding',
|
||||||
@ -403,8 +393,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
subnet['subnet']['gateway_ip'])
|
subnet['subnet']['gateway_ip'])
|
||||||
|
|
||||||
def test_dhcp_binding_with_create_port_with_opts(self):
|
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
|
# Test if DHCP binding is added when a compute port is created
|
||||||
# with extra options.
|
# with extra options.
|
||||||
opt_name = 'interface-mtu'
|
opt_name = 'interface-mtu'
|
||||||
@ -448,8 +436,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
subnet['subnet']['gateway_ip'])
|
subnet['subnet']['gateway_ip'])
|
||||||
|
|
||||||
def test_dhcp_binding_with_create_port_with_opts121(self):
|
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
|
# Test if DHCP binding is added when a compute port is created
|
||||||
# with extra option121.
|
# with extra option121.
|
||||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
||||||
@ -491,8 +477,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
subnet['subnet']['gateway_ip'])
|
subnet['subnet']['gateway_ip'])
|
||||||
|
|
||||||
def test_dhcp_binding_with_create_port_with_bad_opts(self):
|
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:
|
with self.subnet(enable_dhcp=True) as subnet:
|
||||||
device_owner = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'None'
|
device_owner = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'None'
|
||||||
device_id = uuidutils.generate_uuid()
|
device_id = uuidutils.generate_uuid()
|
||||||
@ -523,8 +507,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
self.plugin.create_port, ctx, data)
|
self.plugin.create_port, ctx, data)
|
||||||
|
|
||||||
def test_dhcp_binding_with_disable_enable_dhcp(self):
|
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
|
# Test if DHCP binding is preserved after DHCP is disabled and
|
||||||
# re-enabled on a subnet.
|
# re-enabled on a subnet.
|
||||||
with self.subnet(enable_dhcp=True) as subnet:
|
with self.subnet(enable_dhcp=True) as subnet:
|
||||||
@ -559,8 +541,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
dhcp_bindings[0]['nsx_service_id'])
|
dhcp_bindings[0]['nsx_service_id'])
|
||||||
|
|
||||||
def test_dhcp_binding_with_delete_port(self):
|
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
|
# Test if DHCP binding is removed when the associated compute port
|
||||||
# is deleted.
|
# is deleted.
|
||||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
||||||
@ -580,8 +560,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
dhcp_binding['nsx_binding_id'])
|
dhcp_binding['nsx_binding_id'])
|
||||||
|
|
||||||
def test_dhcp_binding_with_update_port_delete_ip(self):
|
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
|
# Test if DHCP binding is deleted when the IP of the associated
|
||||||
# compute port is deleted.
|
# compute port is deleted.
|
||||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
||||||
@ -604,8 +582,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
dhcp_binding['nsx_binding_id'])
|
dhcp_binding['nsx_binding_id'])
|
||||||
|
|
||||||
def test_dhcp_binding_with_update_port_ip(self):
|
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
|
# Test if DHCP binding is updated when the IP of the associated
|
||||||
# compute port is changed.
|
# compute port is changed.
|
||||||
with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True) as subnet:
|
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)
|
assert_data)
|
||||||
|
|
||||||
def test_dhcp_binding_with_update_port_mac(self):
|
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
|
# Test if DHCP binding is updated when the Mac of the associated
|
||||||
# compute port is changed.
|
# compute port is changed.
|
||||||
with self.subnet(enable_dhcp=True) as subnet:
|
with self.subnet(enable_dhcp=True) as subnet:
|
||||||
@ -683,8 +657,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
assert_data)
|
assert_data)
|
||||||
|
|
||||||
def test_update_port_with_update_dhcp_opt(self):
|
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.
|
# Test updating extra-dhcp-opts via port update.
|
||||||
with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True) as subnet:
|
with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True) as subnet:
|
||||||
mac_address = '11:22:33:44:55:66'
|
mac_address = '11:22:33:44:55:66'
|
||||||
@ -717,8 +689,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
assert_data)
|
assert_data)
|
||||||
|
|
||||||
def test_update_port_with_adding_dhcp_opt(self):
|
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.
|
# Test adding extra-dhcp-opts via port update.
|
||||||
with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True) as subnet:
|
with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True) as subnet:
|
||||||
mac_address = '11:22:33:44:55:66'
|
mac_address = '11:22:33:44:55:66'
|
||||||
@ -752,8 +722,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
assert_data)
|
assert_data)
|
||||||
|
|
||||||
def test_update_port_with_deleting_dhcp_opt(self):
|
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.
|
# Test adding extra-dhcp-opts via port update.
|
||||||
with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True) as subnet:
|
with self.subnet(cidr='10.0.0.0/24', enable_dhcp=True) as subnet:
|
||||||
mac_address = '11:22:33:44:55:66'
|
mac_address = '11:22:33:44:55:66'
|
||||||
@ -864,8 +832,6 @@ class NsxNativeDhcpTestCase(test_plugin.NsxPPluginTestCaseMixin):
|
|||||||
network['network']['tenant_id'], False)
|
network['network']['tenant_id'], False)
|
||||||
|
|
||||||
def test_dhcp_binding_with_create_az_port(self):
|
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.
|
# Test if DHCP binding is added when a compute port is created.
|
||||||
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
with mock.patch.object(nsx_resources.LogicalDhcpServer,
|
||||||
'create_binding',
|
'create_binding',
|
||||||
|
@ -132,6 +132,11 @@ class NsxPPluginTestCaseMixin(
|
|||||||
"get_id_by_name_or_id",
|
"get_id_by_name_or_id",
|
||||||
return_value=NSX_DHCP_PROFILE_ID).start()
|
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(
|
mock.patch(
|
||||||
"vmware_nsxlib.v3.core_resources.NsxLibMetadataProxy."
|
"vmware_nsxlib.v3.core_resources.NsxLibMetadataProxy."
|
||||||
"get_id_by_name_or_id",
|
"get_id_by_name_or_id",
|
||||||
@ -145,10 +150,17 @@ class NsxPPluginTestCaseMixin(
|
|||||||
"vmware_nsxlib.v3.resources.LogicalDhcpServer.create",
|
"vmware_nsxlib.v3.resources.LogicalDhcpServer.create",
|
||||||
side_effect=_return_id_key).start()
|
side_effect=_return_id_key).start()
|
||||||
|
|
||||||
|
mock.patch(
|
||||||
|
"vmware_nsxlib.v3.resources.LogicalDhcpServer.update",
|
||||||
|
side_effect=_return_id_key).start()
|
||||||
|
|
||||||
mock.patch(
|
mock.patch(
|
||||||
"vmware_nsxlib.v3.resources.LogicalDhcpServer.create_binding",
|
"vmware_nsxlib.v3.resources.LogicalDhcpServer.create_binding",
|
||||||
side_effect=_return_id_key).start()
|
side_effect=_return_id_key).start()
|
||||||
|
|
||||||
|
mock.patch("vmware_nsxlib.v3.resources.LogicalDhcpServer."
|
||||||
|
"update_binding").start()
|
||||||
|
|
||||||
mock.patch("vmware_nsxlib.v3.NsxLib."
|
mock.patch("vmware_nsxlib.v3.NsxLib."
|
||||||
"get_id_by_resource_and_tag").start()
|
"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):
|
def test_update_port_add_additional_ip(self):
|
||||||
self.skipTest('Multiple fixed ips on a port are not supported')
|
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
|
@with_disable_dhcp
|
||||||
def test_duplicate_mac_generation(self):
|
def test_duplicate_mac_generation(self):
|
||||||
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
return super(NsxPTestPorts, self).test_duplicate_mac_generation()
|
return super(NsxPTestPorts, self).test_duplicate_mac_generation()
|
||||||
|
|
||||||
@with_disable_dhcp
|
@with_disable_dhcp
|
||||||
@ -1028,6 +1050,9 @@ class NsxPTestSubnets(test_db_base_plugin_v2.TestSubnetsV2,
|
|||||||
super(NsxPTestSubnets, self).\
|
super(NsxPTestSubnets, self).\
|
||||||
test_delete_subnet_with_other_subnet_on_network_still_in_use()
|
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):
|
def test_create_subnet_dhcpv6_stateless_with_ip_already_allocated(self):
|
||||||
self.skipTest('No DHCP v6 Support yet')
|
self.skipTest('No DHCP v6 Support yet')
|
||||||
|
|
||||||
|
@ -287,15 +287,20 @@ class NsxV3PluginTestCaseMixin(test_plugin.NeutronDbPluginV2TestCase,
|
|||||||
self.plugin.init_availability_zones()
|
self.plugin.init_availability_zones()
|
||||||
self.plugin._translate_configured_names_to_uuids()
|
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):
|
def _enable_dhcp_relay(self):
|
||||||
# Add the relay service to the config and availability zones
|
# 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,
|
cfg.CONF.set_override('dhcp_relay_service', NSX_DHCP_RELAY_SRV,
|
||||||
'nsx_v3')
|
'nsx_v3')
|
||||||
mock_nsx_version = mock.patch.object(
|
mock_nsx_version = mock.patch.object(
|
||||||
self.plugin.nsxlib, 'feature_supported', return_value=True)
|
self.plugin.nsxlib, 'feature_supported', return_value=True)
|
||||||
mock_nsx_version.start()
|
mock_nsx_version.start()
|
||||||
self._initialize_azs()
|
self._initialize_azs()
|
||||||
|
self._enable_native_dhcp_md()
|
||||||
|
|
||||||
|
|
||||||
class TestNetworksV2(test_plugin.TestNetworksV2, NsxV3PluginTestCaseMixin):
|
class TestNetworksV2(test_plugin.TestNetworksV2, NsxV3PluginTestCaseMixin):
|
||||||
@ -322,7 +327,7 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxV3PluginTestCaseMixin):
|
|||||||
self.assertListEqual(az_hints, zone)
|
self.assertListEqual(az_hints, zone)
|
||||||
|
|
||||||
def test_network_failure_rollback(self):
|
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()
|
self.plugin = directory.get_plugin()
|
||||||
with mock.patch.object(self.plugin.nsxlib.logical_port, 'create',
|
with mock.patch.object(self.plugin.nsxlib.logical_port, 'create',
|
||||||
side_effect=api_exc.NsxApiException):
|
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')
|
self.skipTest('Multiple fixed ips on a port are not supported')
|
||||||
|
|
||||||
def test_subnet_native_dhcp_subnet_enabled(self):
|
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 self.network() as network:
|
||||||
with mock.patch.object(self.plugin,
|
with mock.patch.object(self.plugin,
|
||||||
'_enable_native_dhcp') as enable_dhcp,\
|
'_enable_native_dhcp') as enable_dhcp,\
|
||||||
@ -863,7 +868,7 @@ class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
|||||||
self.assertTrue(enable_dhcp.called)
|
self.assertTrue(enable_dhcp.called)
|
||||||
|
|
||||||
def test_subnet_native_dhcp_subnet_disabled(self):
|
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 self.network() as network:
|
||||||
with mock.patch.object(self.plugin,
|
with mock.patch.object(self.plugin,
|
||||||
'_enable_native_dhcp') as enable_dhcp,\
|
'_enable_native_dhcp') as enable_dhcp,\
|
||||||
@ -882,7 +887,7 @@ class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
|||||||
self.assertFalse(enable_dhcp.called)
|
self.assertFalse(enable_dhcp.called)
|
||||||
|
|
||||||
def test_subnet_native_dhcp_flat_subnet_disabled(self):
|
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'}
|
providernet_args = {pnet.NETWORK_TYPE: 'flat'}
|
||||||
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||||
'get_transport_type', return_value='VLAN'):
|
'get_transport_type', return_value='VLAN'):
|
||||||
@ -902,7 +907,7 @@ class TestSubnetsV2(test_plugin.TestSubnetsV2, NsxV3PluginTestCaseMixin):
|
|||||||
context.get_admin_context(), data)
|
context.get_admin_context(), data)
|
||||||
|
|
||||||
def test_subnet_native_dhcp_flat_subnet_enabled(self):
|
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'}
|
providernet_args = {pnet.NETWORK_TYPE: 'flat'}
|
||||||
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
|
||||||
'get_transport_type', return_value='VLAN'):
|
'get_transport_type', return_value='VLAN'):
|
||||||
@ -994,7 +999,7 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
|
|||||||
data['port']['fixed_ips'])
|
data['port']['fixed_ips'])
|
||||||
|
|
||||||
def test_delete_dhcp_port(self):
|
def test_delete_dhcp_port(self):
|
||||||
cfg.CONF.set_override('native_dhcp_metadata', True, 'nsx_v3')
|
self._enable_native_dhcp_md()
|
||||||
with self.subnet():
|
with self.subnet():
|
||||||
pl = directory.get_plugin()
|
pl = directory.get_plugin()
|
||||||
ctx = context.Context(user_id=None, tenant_id=self._tenant_id,
|
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'])
|
self.assertEqual(port2['port']['id'], ports_data['ports'][0]['id'])
|
||||||
|
|
||||||
def test_port_failure_rollback_dhcp_exception(self):
|
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()
|
self.plugin = directory.get_plugin()
|
||||||
with mock.patch.object(self.plugin, '_add_dhcp_binding',
|
with mock.patch.object(self.plugin, '_add_dhcp_binding',
|
||||||
side_effect=nsxlib_exc.ManagerError):
|
side_effect=nsxlib_exc.ManagerError):
|
||||||
@ -1495,7 +1500,7 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
|
|||||||
self.assertListEqual([], networks)
|
self.assertListEqual([], networks)
|
||||||
|
|
||||||
def test_port_DB_failure_rollback_dhcp_exception(self):
|
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()
|
self.plugin = directory.get_plugin()
|
||||||
with mock.patch('vmware_nsx.db.db.add_neutron_nsx_dhcp_binding',
|
with mock.patch('vmware_nsx.db.db.add_neutron_nsx_dhcp_binding',
|
||||||
side_effect=db_exc.DBError),\
|
side_effect=db_exc.DBError),\
|
||||||
@ -1671,7 +1676,7 @@ class TestPortsV2(test_plugin.TestPortsV2, NsxV3PluginTestCaseMixin,
|
|||||||
self.assertTrue(res['port'][psec.PORTSECURITY])
|
self.assertTrue(res['port'][psec.PORTSECURITY])
|
||||||
|
|
||||||
def test_update_dhcp_port_device_owner(self):
|
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():
|
with self.subnet():
|
||||||
pl = directory.get_plugin()
|
pl = directory.get_plugin()
|
||||||
ctx = context.Context(user_id=None, tenant_id=self._tenant_id,
|
ctx = context.Context(user_id=None, tenant_id=self._tenant_id,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user