Merge "Move native dhcp logic to common plugin"

This commit is contained in:
Zuul 2018-12-25 15:25:45 +00:00 committed by Gerrit Code Review
commit ee46de95c3
3 changed files with 219 additions and 154 deletions

View File

@ -16,7 +16,11 @@
import netaddr import netaddr
from oslo_config import cfg from oslo_config import cfg
from oslo_db import exception as db_exc
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils
from sqlalchemy import exc as sql_exc
from six import moves from six import moves
from neutron.db import l3_db from neutron.db import l3_db
@ -64,9 +68,12 @@ class NsxPluginV3Base(plugin.NsxPluginBase,
def __init__(self): def __init__(self):
super(NsxPluginV3Base, self).__init__() super(NsxPluginV3Base, self).__init__()
plugin_cfg = getattr(cfg.CONF, self.cfg_group)
self._network_vlans = plugin_utils.parse_network_vlan_ranges( self._network_vlans = plugin_utils.parse_network_vlan_ranges(
plugin_cfg.network_vlan_ranges) self._get_conf_attr('network_vlan_ranges'))
def _get_conf_attr(self, attr):
plugin_cfg = getattr(cfg.CONF, self.cfg_group)
return getattr(plugin_cfg, attr)
def _get_interface_network(self, context, interface_info): def _get_interface_network(self, context, interface_info):
is_port, is_sub = self._validate_interface_info(interface_info) is_port, is_sub = self._validate_interface_info(interface_info)
@ -529,16 +536,32 @@ class NsxPluginV3Base(plugin.NsxPluginBase,
"""Should be implemented by each plugin""" """Should be implemented by each plugin"""
pass pass
def _has_native_dhcp_metadata(self):
"""Should be implemented by each plugin"""
pass
def _get_nsx_net_tz_id(self, nsx_net): def _get_nsx_net_tz_id(self, nsx_net):
"""Should be implemented by each plugin""" """Should be implemented by each plugin"""
pass pass
def _get_network_nsx_id(self, context, neutron_id):
"""Should be implemented by each plugin"""
pass
def _validate_ens_net_portsecurity(self, net_data): def _validate_ens_net_portsecurity(self, net_data):
"""Validate/Update the port security of the new network for ENS TZ """Validate/Update the port security of the new network for ENS TZ
Should be implemented by the plugin if necessary Should be implemented by the plugin if necessary
""" """
pass pass
def _is_ens_tz_net(self, context, net_id):
"""Should be implemented by each plugin"""
pass
def _is_ens_tz_port(self, context, port_data):
"""Should be implemented by each plugin"""
pass
def _generate_segment_id(self, context, physical_network, net_data): def _generate_segment_id(self, context, physical_network, net_data):
bindings = nsx_db.get_network_bindings_by_phy_uuid( bindings = nsx_db.get_network_bindings_by_phy_uuid(
context.session, physical_network) context.session, physical_network)
@ -970,3 +993,184 @@ class NsxPluginV3Base(plugin.NsxPluginBase,
err_msg = _("admin_state_up=False routers are not supported") err_msg = _("admin_state_up=False routers are not supported")
LOG.warning(err_msg) LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg) raise n_exc.InvalidInput(error_message=err_msg)
def _build_dhcp_server_config(self, context, network, subnet, port, az):
name = self.nsxlib.native_dhcp.build_server_name(
network['name'], network['id'])
net_tags = self.nsxlib.build_v3_tags_payload(
network, resource_type='os-neutron-net-id',
project_name=context.tenant_name)
dns_domain = network.get('dns_domain')
if not dns_domain or not validators.is_attr_set(dns_domain):
dns_domain = az.dns_domain
dns_nameservers = subnet['dns_nameservers']
if not dns_nameservers or not validators.is_attr_set(dns_nameservers):
dns_nameservers = az.nameservers
return self.nsxlib.native_dhcp.build_server(
name,
ip_address=port['fixed_ips'][0]['ip_address'],
cidr=subnet['cidr'],
gateway_ip=subnet['gateway_ip'],
host_routes=subnet['host_routes'],
dns_domain=dns_domain,
dns_nameservers=dns_nameservers,
dhcp_profile_id=az._native_dhcp_profile_uuid,
tags=net_tags)
def _enable_native_dhcp(self, context, network, subnet):
# Enable native DHCP service on the backend for this network.
# First create a Neutron DHCP port and use its assigned IP
# address as the DHCP server address in an API call to create a
# LogicalDhcpServer on the backend. Then create the corresponding
# logical port for the Neutron port with DHCP attachment as the
# LogicalDhcpServer UUID.
# TODO(annak):
# This function temporarily serves both nsx_v3 and nsx_p plugins.
# In future, when platform supports native dhcp in policy for infra
# segments, this function should move back to nsx_v3 plugin
# Delete obsolete settings if exist. This could happen when a
# previous failed transaction was rolled back. But the backend
# entries are still there.
self._disable_native_dhcp(context, network['id'])
# Get existing ports on subnet.
existing_ports = super(NsxPluginV3Base, self).get_ports(
context, filters={'network_id': [network['id']],
'fixed_ips': {'subnet_id': [subnet['id']]}})
az = self.get_network_az_by_net_id(context, network['id'])
port_data = {
"name": "",
"admin_state_up": True,
"device_id": az._native_dhcp_profile_uuid,
"device_owner": constants.DEVICE_OWNER_DHCP,
"network_id": network['id'],
"tenant_id": network["tenant_id"],
"mac_address": constants.ATTR_NOT_SPECIFIED,
"fixed_ips": [{"subnet_id": subnet['id']}],
psec.PORTSECURITY: False
}
# Create the DHCP port (on neutron only) and update its port security
port = {'port': port_data}
neutron_port = super(NsxPluginV3Base, self).create_port(context, port)
is_ens_tz_port = self._is_ens_tz_port(context, port_data)
self._create_port_preprocess_security(context, port, port_data,
neutron_port, is_ens_tz_port)
server_data = self._build_dhcp_server_config(
context, network, subnet, neutron_port, az)
nsx_net_id = self._get_network_nsx_id(context, network['id'])
port_tags = self.nsxlib.build_v3_tags_payload(
neutron_port, resource_type='os-neutron-dport-id',
project_name=context.tenant_name)
dhcp_server = None
dhcp_port_profiles = []
if (not self._is_ens_tz_net(context, network['id']) and
not self._has_native_dhcp_metadata()):
dhcp_port_profiles.append(self._dhcp_profile)
try:
dhcp_server = self.nsxlib.dhcp_server.create(**server_data)
LOG.debug("Created logical DHCP server %(server)s for network "
"%(network)s",
{'server': dhcp_server['id'], 'network': network['id']})
name = self._build_port_name(context, port_data)
nsx_port = self.nsxlib.logical_port.create(
nsx_net_id, dhcp_server['id'], tags=port_tags, name=name,
attachment_type=nsxlib_consts.ATTACHMENT_DHCP,
switch_profile_ids=dhcp_port_profiles)
LOG.debug("Created DHCP logical port %(port)s for "
"network %(network)s",
{'port': nsx_port['id'], 'network': network['id']})
except nsx_lib_exc.ManagerError:
with excutils.save_and_reraise_exception():
LOG.error("Unable to create logical DHCP server for "
"network %s", network['id'])
if dhcp_server:
self.nsxlib.dhcp_server.delete(dhcp_server['id'])
super(NsxPluginV3Base, self).delete_port(
context, neutron_port['id'])
try:
# Add neutron_port_id -> nsx_port_id mapping to the DB.
nsx_db.add_neutron_nsx_port_mapping(
context.session, neutron_port['id'], nsx_net_id,
nsx_port['id'])
# Add neutron_net_id -> dhcp_service_id mapping to the DB.
nsx_db.add_neutron_nsx_service_binding(
context.session, network['id'], neutron_port['id'],
nsxlib_consts.SERVICE_DHCP, dhcp_server['id'])
except (db_exc.DBError, sql_exc.TimeoutError):
with excutils.save_and_reraise_exception():
LOG.error("Failed to create mapping for DHCP port %s,"
"deleting port and logical DHCP server",
neutron_port['id'])
self.nsxlib.dhcp_server.delete(dhcp_server['id'])
self._cleanup_port(context, neutron_port['id'], nsx_port['id'])
# Configure existing ports to work with the new DHCP server
try:
for port_data in existing_ports:
self._add_dhcp_binding(context, port_data)
except Exception:
LOG.error('Unable to create DHCP bindings for existing ports '
'on subnet %s', subnet['id'])
def _disable_native_dhcp(self, context, network_id):
# Disable native DHCP service on the backend for this network.
# First delete the DHCP port in this network. Then delete the
# corresponding LogicalDhcpServer for this network.
dhcp_service = nsx_db.get_nsx_service_binding(
context.session, network_id, nsxlib_consts.SERVICE_DHCP)
if not dhcp_service:
return
if dhcp_service['port_id']:
try:
self.delete_port(context, dhcp_service['port_id'],
force_delete_dhcp=True)
except nsx_lib_exc.ResourceNotFound:
# This could happen when the port has been manually deleted.
LOG.error("Failed to delete DHCP port %(port)s for "
"network %(network)s",
{'port': dhcp_service['port_id'],
'network': network_id})
else:
LOG.error("DHCP port is not configured for network %s",
network_id)
try:
self.nsxlib.dhcp_server.delete(dhcp_service['nsx_service_id'])
LOG.debug("Deleted logical DHCP server %(server)s for network "
"%(network)s",
{'server': dhcp_service['nsx_service_id'],
'network': network_id})
except nsx_lib_exc.ManagerError:
with excutils.save_and_reraise_exception():
LOG.error("Unable to delete logical DHCP server %(server)s "
"for network %(network)s",
{'server': dhcp_service['nsx_service_id'],
'network': network_id})
try:
# Delete neutron_id -> dhcp_service_id mapping from the DB.
nsx_db.delete_neutron_nsx_service_binding(
context.session, network_id, nsxlib_consts.SERVICE_DHCP)
# Delete all DHCP bindings under this DHCP server from the DB.
nsx_db.delete_neutron_nsx_dhcp_bindings_by_service_id(
context.session, dhcp_service['nsx_service_id'])
except db_exc.DBError:
with excutils.save_and_reraise_exception():
LOG.error("Unable to delete DHCP server mapping for "
"network %s", network_id)
def _cleanup_port(self, context, port_id, nsx_port_id=None):
# Clean up neutron port and nsx manager port if provided
# Does not handle cleanup of policy port
super(NsxPluginV3Base, self).delete_port(context, port_id)
if nsx_port_id:
self.nsxlib.logical_port.delete(nsx_port_id)

View File

@ -1833,4 +1833,14 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
type = self.nsxpolicy.transport_zone.get_transport_type( type = self.nsxpolicy.transport_zone.get_transport_type(
tz) tz)
return type == nsxlib_consts.TRANSPORT_TYPE_OVERLAY return type == nsxlib_consts.TRANSPORT_TYPE_OVERLAY
def _has_native_dhcp_metadata(self):
return True
def _is_ens_tz_net(self, context, net_id):
#TODO(annak): handle ENS case
return False
def _is_ens_tz_port(self, context, port_data):
#TODO(annak): handle ENS case
return False return False

View File

@ -1451,153 +1451,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return False return False
return True if count == 1 else False return True if count == 1 else False
def _enable_native_dhcp(self, context, network, subnet):
# Enable native DHCP service on the backend for this network.
# First create a Neutron DHCP port and use its assigned IP
# address as the DHCP server address in an API call to create a
# LogicalDhcpServer on the backend. Then create the corresponding
# logical port for the Neutron port with DHCP attachment as the
# LogicalDhcpServer UUID.
# Delete obsolete settings if exist. This could happen when a
# previous failed transaction was rolled back. But the backend
# entries are still there.
self._disable_native_dhcp(context, network['id'])
# Get existing ports on subnet.
existing_ports = super(NsxV3Plugin, self).get_ports(
context, filters={'network_id': [network['id']],
'fixed_ips': {'subnet_id': [subnet['id']]}})
az = self.get_network_az_by_net_id(context, network['id'])
port_data = {
"name": "",
"admin_state_up": True,
"device_id": az._native_dhcp_profile_uuid,
"device_owner": const.DEVICE_OWNER_DHCP,
"network_id": network['id'],
"tenant_id": network["tenant_id"],
"mac_address": const.ATTR_NOT_SPECIFIED,
"fixed_ips": [{"subnet_id": subnet['id']}],
psec.PORTSECURITY: False
}
# Create the DHCP port (on neutron only) and update its port security
port = {'port': port_data}
neutron_port = super(NsxV3Plugin, self).create_port(context, port)
is_ens_tz_port = self._is_ens_tz_port(context, port_data)
self._create_port_preprocess_security(context, port, port_data,
neutron_port, is_ens_tz_port)
net_tags = self.nsxlib.build_v3_tags_payload(
network, resource_type='os-neutron-net-id',
project_name=context.tenant_name)
server_data = self.nsxlib.native_dhcp.build_server_config(
network, subnet, neutron_port, net_tags,
default_dns_nameservers=az.nameservers,
default_dns_domain=az.dns_domain)
server_data['dhcp_profile_id'] = az._native_dhcp_profile_uuid
nsx_net_id = self._get_network_nsx_id(context, network['id'])
port_tags = self.nsxlib.build_v3_tags_payload(
neutron_port, resource_type='os-neutron-dport-id',
project_name=context.tenant_name)
dhcp_server = None
dhcp_port_profiles = []
if (not self._is_ens_tz_net(context, network['id']) and
not cfg.CONF.nsx_v3.native_dhcp_metadata):
dhcp_port_profiles.append(self._dhcp_profile)
try:
dhcp_server = self.nsxlib.dhcp_server.create(**server_data)
LOG.debug("Created logical DHCP server %(server)s for network "
"%(network)s",
{'server': dhcp_server['id'], 'network': network['id']})
name = self._build_port_name(context, port_data)
nsx_port = self.nsxlib.logical_port.create(
nsx_net_id, dhcp_server['id'], tags=port_tags, name=name,
attachment_type=nsxlib_consts.ATTACHMENT_DHCP,
switch_profile_ids=dhcp_port_profiles)
LOG.debug("Created DHCP logical port %(port)s for "
"network %(network)s",
{'port': nsx_port['id'], 'network': network['id']})
except nsx_lib_exc.ManagerError:
with excutils.save_and_reraise_exception():
LOG.error("Unable to create logical DHCP server for "
"network %s", network['id'])
if dhcp_server:
self.nsxlib.dhcp_server.delete(dhcp_server['id'])
super(NsxV3Plugin, self).delete_port(
context, neutron_port['id'])
try:
# Add neutron_port_id -> nsx_port_id mapping to the DB.
nsx_db.add_neutron_nsx_port_mapping(
context.session, neutron_port['id'], nsx_net_id,
nsx_port['id'])
# Add neutron_net_id -> dhcp_service_id mapping to the DB.
nsx_db.add_neutron_nsx_service_binding(
context.session, network['id'], neutron_port['id'],
nsxlib_consts.SERVICE_DHCP, dhcp_server['id'])
except (db_exc.DBError, sql_exc.TimeoutError):
with excutils.save_and_reraise_exception():
LOG.error("Failed to create mapping for DHCP port %s,"
"deleting port and logical DHCP server",
neutron_port['id'])
self.nsxlib.dhcp_server.delete(dhcp_server['id'])
self._cleanup_port(context, neutron_port['id'], nsx_port['id'])
# Configure existing ports to work with the new DHCP server
try:
for port_data in existing_ports:
self._add_dhcp_binding(context, port_data)
except Exception:
LOG.error('Unable to create DHCP bindings for existing ports '
'on subnet %s', subnet['id'])
def _disable_native_dhcp(self, context, network_id):
# Disable native DHCP service on the backend for this network.
# First delete the DHCP port in this network. Then delete the
# corresponding LogicalDhcpServer for this network.
dhcp_service = nsx_db.get_nsx_service_binding(
context.session, network_id, nsxlib_consts.SERVICE_DHCP)
if not dhcp_service:
return
if dhcp_service['port_id']:
try:
self.delete_port(context, dhcp_service['port_id'],
force_delete_dhcp=True)
except nsx_lib_exc.ResourceNotFound:
# This could happen when the port has been manually deleted.
LOG.error("Failed to delete DHCP port %(port)s for "
"network %(network)s",
{'port': dhcp_service['port_id'],
'network': network_id})
else:
LOG.error("DHCP port is not configured for network %s",
network_id)
try:
self.nsxlib.dhcp_server.delete(dhcp_service['nsx_service_id'])
LOG.debug("Deleted logical DHCP server %(server)s for network "
"%(network)s",
{'server': dhcp_service['nsx_service_id'],
'network': network_id})
except nsx_lib_exc.ManagerError:
with excutils.save_and_reraise_exception():
LOG.error("Unable to delete logical DHCP server %(server)s "
"for network %(network)s",
{'server': dhcp_service['nsx_service_id'],
'network': network_id})
try:
# Delete neutron_id -> dhcp_service_id mapping from the DB.
nsx_db.delete_neutron_nsx_service_binding(
context.session, network_id, nsxlib_consts.SERVICE_DHCP)
# Delete all DHCP bindings under this DHCP server from the DB.
nsx_db.delete_neutron_nsx_dhcp_bindings_by_service_id(
context.session, dhcp_service['nsx_service_id'])
except db_exc.DBError:
with excutils.save_and_reraise_exception():
LOG.error("Unable to delete DHCP server mapping for "
"network %s", network_id)
def _validate_address_space(self, context, subnet): def _validate_address_space(self, context, subnet):
# Only working for IPv4 at the moment # Only working for IPv4 at the moment
if (subnet['ip_version'] != 4): if (subnet['ip_version'] != 4):
@ -2175,6 +2028,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# Check the host-switch-mode of the TZ connected to the ports network # Check the host-switch-mode of the TZ connected to the ports network
return self._is_ens_tz_net(context, port_data['network_id']) return self._is_ens_tz_net(context, port_data['network_id'])
def _has_native_dhcp_metadata(self):
return cfg.CONF.nsx_v3.native_dhcp_metadata
def _assert_on_dhcp_relay_without_router(self, context, port_data, def _assert_on_dhcp_relay_without_router(self, context, port_data,
original_port=None): original_port=None):
# Prevent creating/updating port with device owner prefix 'compute' # Prevent creating/updating port with device owner prefix 'compute'
@ -2221,11 +2077,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
LOG.warning(err_msg) LOG.warning(err_msg)
raise n_exc.InvalidInput(error_message=err_msg) raise n_exc.InvalidInput(error_message=err_msg)
def _cleanup_port(self, context, port_id, lport_id):
super(NsxV3Plugin, self).delete_port(context, port_id)
if lport_id:
self.nsxlib.logical_port.delete(lport_id)
def _assert_on_port_admin_state(self, port_data, device_owner): def _assert_on_port_admin_state(self, port_data, device_owner):
"""Do not allow changing the admin state of some ports""" """Do not allow changing the admin state of some ports"""
if (device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF or if (device_owner == l3_db.DEVICE_OWNER_ROUTER_INTF or