NSX|v3: DHCP Relay support
Support DHCP relay by configuring the relay service per network availability zone, or globally. When a router interface port is created, the relay service will be added to it. DHCP traffic on the subnet will go through the DHCP server configured in the dhcp relay service on the NSX, if it is connected to the router. Also add admin utility to update exsiting router ports when the dhcp relay configuration changes. A future patch will take care of firewall rules allowint the dhcp traffic. Change-Id: I626b3377e71c269600a47b3bd805eed9d58bad82
This commit is contained in:
parent
dd49c633ce
commit
5dac3f4a4c
@ -193,6 +193,7 @@ function neutron_plugin_configure_service {
|
||||
_nsxv3_ini_set native_metadata_route $NATIVE_METADATA_ROUTE
|
||||
_nsxv3_ini_set dhcp_profile $DHCP_PROFILE_UUID
|
||||
_nsxv3_ini_set metadata_proxy $METADATA_PROXY_UUID
|
||||
_nsxv3_ini_set dhcp_relay_service $DHCP_RELAY_SERVICE
|
||||
iniset $NEUTRON_CONF DEFAULT dhcp_agent_notification False
|
||||
fi
|
||||
if [[ "$NSX_USE_CLIENT_CERT_AUTH" == "True" ]]; then
|
||||
|
@ -13,3 +13,4 @@ NSX_MANAGER=<FILL_IN>
|
||||
NSX_CONTROLLERS=<FILL_IN>
|
||||
DHCP_PROFILE_UUID=<FILL_IN>
|
||||
METADATA_PROXY_UUID=<FILL_IN>
|
||||
DHCP_RELAY_SERVICE=<FILL_IN>
|
||||
|
@ -109,6 +109,7 @@ DEFAULT_EDGE_CLUSTER_UUID=<edge-cluster-uuid>
|
||||
|
||||
# Enabled native DHCP support from NSX backend
|
||||
DHCP_PROFILE_UUID=<dhcp-profile-uuid>
|
||||
DHCP_RELAY_SERVICE=<dhcp-relay-service>
|
||||
METADATA_PROXY_UUID=<metadata-proxy-uuid>
|
||||
METADATA_PROXY_SHARED_SECRET=<metadata-proxy-secret>
|
||||
METADATA_PROXY_USE_HTTPS=False
|
||||
|
@ -278,6 +278,10 @@ Routers
|
||||
|
||||
nsxadmin -r routers -o nsx-update-rules
|
||||
|
||||
- Update DHCP relay service on NSX router ports according to the current configuration::
|
||||
|
||||
nsxadmin -r routers -o nsx-update-dhcp-relay
|
||||
|
||||
Orphaned Routers
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
11
releasenotes/notes/nsxv3-dhcp-relay-32cf1ae281e1.yaml
Normal file
11
releasenotes/notes/nsxv3-dhcp-relay-32cf1ae281e1.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
prelude: >
|
||||
The NSX-v3 plugin supports DHCP relay service per network
|
||||
availability zones.
|
||||
features:
|
||||
- The NSX-v3 plugin supports DHCP relay service per network
|
||||
availability zones. When a router interface port is created,
|
||||
the relay service will be added to it.
|
||||
DHCP traffic on the subnet will go through the DHCP server
|
||||
configured in the dhcp relay service on the NSX, if it is
|
||||
connected to the router.
|
@ -399,6 +399,10 @@ nsx_v3_opts = [
|
||||
"that will be used to enable native metadata service. "
|
||||
"It needs to be created in NSX before starting Neutron "
|
||||
"with the NSX plugin.")),
|
||||
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.BoolOpt('log_security_groups_blocked_traffic',
|
||||
default=False,
|
||||
help=_("(Optional) Indicates whether distributed-firewall "
|
||||
@ -793,6 +797,10 @@ nsxv3_az_opts = [
|
||||
cfg.ListOpt('switching_profiles',
|
||||
help=_("(Optional) list switching profiles uuids that will be "
|
||||
"attached to all neutron created nsx ports.")),
|
||||
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.")),
|
||||
]
|
||||
|
||||
# Register the configuration options
|
||||
|
@ -20,6 +20,7 @@ from vmware_nsx.common import availability_zones as common_az
|
||||
from vmware_nsx.common import config
|
||||
from vmware_nsx.common import exceptions as nsx_exc
|
||||
from vmware_nsxlib.v3 import core_resources
|
||||
from vmware_nsxlib.v3 import nsx_constants as nsxlib_consts
|
||||
|
||||
DEFAULT_NAME = common_az.DEFAULT_NAME
|
||||
|
||||
@ -78,6 +79,10 @@ class NsxV3AvailabilityZone(common_az.ConfiguredAvailabilityZone):
|
||||
if self.switching_profiles is None:
|
||||
self.switching_profiles = cfg.CONF.nsx_v3.switching_profiles
|
||||
|
||||
self.dhcp_relay_service = az_info.get('dhcp_relay_service')
|
||||
if self.dhcp_relay_service is None:
|
||||
self.dhcp_relay_service = cfg.CONF.nsx_v3.dhcp_relay_service
|
||||
|
||||
def init_default_az(self):
|
||||
# use the default configuration
|
||||
self.metadata_proxy = cfg.CONF.nsx_v3.metadata_proxy
|
||||
@ -88,6 +93,7 @@ class NsxV3AvailabilityZone(common_az.ConfiguredAvailabilityZone):
|
||||
self.default_overlay_tz = cfg.CONF.nsx_v3.default_overlay_tz
|
||||
self.default_vlan_tz = cfg.CONF.nsx_v3.default_vlan_tz
|
||||
self.switching_profiles = cfg.CONF.nsx_v3.switching_profiles
|
||||
self.dhcp_relay_service = cfg.CONF.nsx_v3.dhcp_relay_service
|
||||
|
||||
def translate_configured_names_to_uuids(self, nsxlib):
|
||||
# Mandatory configurations (in AZ or inherited from global values)
|
||||
@ -171,6 +177,23 @@ class NsxV3AvailabilityZone(common_az.ConfiguredAvailabilityZone):
|
||||
nsx_profile.get('id')))
|
||||
self.switching_profiles_objs = profiles
|
||||
|
||||
if (self.dhcp_relay_service and
|
||||
nsxlib.feature_supported(nsxlib_consts.FEATURE_DHCP_RELAY)):
|
||||
relay_id = None
|
||||
if cfg.CONF.nsx_v3.init_objects_by_tags:
|
||||
# Find the TZ by its tag
|
||||
relay_id = nsxlib.get_id_by_resource_and_tag(
|
||||
nsxlib.relay_service.resource_type,
|
||||
cfg.CONF.nsx_v3.search_objects_scope,
|
||||
self.dhcp_relay_service)
|
||||
if not relay_id:
|
||||
# Find the service by its name or id
|
||||
relay_id = nsxlib.relay_service.get_id_by_name_or_id(
|
||||
self.dhcp_relay_service)
|
||||
self.dhcp_relay_service = relay_id
|
||||
else:
|
||||
self.dhcp_relay_service = None
|
||||
|
||||
|
||||
class NsxV3AvailabilityZones(common_az.ConfiguredAvailabilityZones):
|
||||
|
||||
|
@ -3260,13 +3260,15 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
|
||||
port, resource_type='os-neutron-rport-id',
|
||||
project_name=context.tenant_name)
|
||||
tags.append({'scope': 'os-subnet-id', 'tag': subnet['id']})
|
||||
net_az = self.get_network_az_by_net_id(context, network_id)
|
||||
self._routerlib.create_logical_router_intf_port_by_ls_id(
|
||||
logical_router_id=nsx_router_id,
|
||||
display_name=display_name,
|
||||
tags=tags,
|
||||
ls_id=nsx_net_id,
|
||||
logical_switch_port_id=nsx_port_id,
|
||||
address_groups=address_groups)
|
||||
address_groups=address_groups,
|
||||
relay_service_uuid=net_az.dhcp_relay_service)
|
||||
|
||||
if router_db.gw_port and not router_db.enable_snat:
|
||||
# TODO(berlin): Announce the subnet on tier0 if enable_snat
|
||||
|
@ -347,6 +347,7 @@ class EdgeFwaasV3Driver(fwaas_base.FwaasDriverBase):
|
||||
nsx_router_id, section_id = self._get_backend_router_and_fw_section(
|
||||
context, router_id)
|
||||
|
||||
#TODO(asarfaty) add dhcp relay allow rules here
|
||||
# Add default drop all rule at the end
|
||||
old_default_rule = self.nsx_firewall.get_default_rule(
|
||||
section_id)
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
import sys
|
||||
|
||||
from vmware_nsx.common import config # noqa
|
||||
from vmware_nsx.common import utils as nsx_utils
|
||||
from vmware_nsx.db import db as nsx_db
|
||||
from vmware_nsx.shell.admin.plugins.common import constants
|
||||
@ -22,11 +23,13 @@ from vmware_nsx.shell.admin.plugins.common import utils as admin_utils
|
||||
from vmware_nsx.shell.admin.plugins.nsxv3.resources import utils
|
||||
from vmware_nsx.shell import resources as shell
|
||||
from vmware_nsxlib.v3 import exceptions as nsx_exc
|
||||
from vmware_nsxlib.v3 import nsx_constants
|
||||
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import l3_db
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib import context as neutron_context
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -173,6 +176,42 @@ def delete_backend_router(resource, event, trigger, **kwargs):
|
||||
LOG.error("Failed to delete backend router %s.", nsx_id)
|
||||
|
||||
|
||||
@admin_utils.output_header
|
||||
def update_dhcp_relay(resource, event, trigger, **kwargs):
|
||||
"""Update all routers dhcp relay service by the current configuration"""
|
||||
nsxlib = utils.get_connected_nsxlib()
|
||||
if not nsxlib.feature_supported(nsx_constants.FEATURE_DHCP_RELAY):
|
||||
version = nsxlib.get_version()
|
||||
LOG.error("DHCP relay is not supported by NSX version %s", version)
|
||||
return
|
||||
|
||||
# initialize the availability zones and nsxlib
|
||||
config.register_nsxv3_azs(cfg.CONF, cfg.CONF.nsx_v3.availability_zones)
|
||||
|
||||
# get all neutron router interfaces ports
|
||||
admin_cxt = neutron_context.get_admin_context()
|
||||
with utils.NsxV3PluginWrapper() as plugin:
|
||||
filters = {'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]}
|
||||
ports = plugin.get_ports(admin_cxt, filters=filters)
|
||||
for port in ports:
|
||||
# get the backend router port by the tag
|
||||
nsx_port_id = nsxlib.get_id_by_resource_and_tag(
|
||||
'LogicalRouterDownLinkPort',
|
||||
'os-neutron-rport-id', port['id'])
|
||||
if not nsx_port_id:
|
||||
LOG.warning("Couldn't find nsx router port for interface %s",
|
||||
port['id'])
|
||||
continue
|
||||
# get the network of this port
|
||||
network_id = port['network_id']
|
||||
# check the relay service on the az of the network
|
||||
az = plugin.get_network_az_by_net_id(admin_cxt, network_id)
|
||||
nsxlib.logical_router_port.update(
|
||||
nsx_port_id, relay_service_uuid=az.dhcp_relay_service)
|
||||
#TODO(asarfaty) also update the firewall rules of the routers
|
||||
LOG.info("Done.")
|
||||
|
||||
|
||||
registry.subscribe(list_missing_routers,
|
||||
constants.ROUTERS,
|
||||
shell.Operations.LIST_MISMATCHES.value)
|
||||
@ -188,3 +227,7 @@ registry.subscribe(list_orphaned_routers,
|
||||
registry.subscribe(delete_backend_router,
|
||||
constants.ORPHANED_ROUTERS,
|
||||
shell.Operations.NSX_CLEAN.value)
|
||||
|
||||
registry.subscribe(update_dhcp_relay,
|
||||
constants.ROUTERS,
|
||||
shell.Operations.NSX_UPDATE_DHCP_RELAY.value)
|
||||
|
@ -49,6 +49,7 @@ class Operations(enum.Enum):
|
||||
NSX_UPDATE_ALL = 'nsx-update-all'
|
||||
NSX_UPDATE_SECRET = 'nsx-update-secret'
|
||||
NSX_UPDATE_RULES = 'nsx-update-rules'
|
||||
NSX_UPDATE_DHCP_RELAY = 'nsx-update-dhcp-relay'
|
||||
NSX_UPDATE_IP = 'nsx-update-ip'
|
||||
NSX_RECREATE = 'nsx-recreate'
|
||||
NSX_REORDER = 'nsx-reorder'
|
||||
@ -95,7 +96,8 @@ nsxv3_resources = {
|
||||
Operations.NSX_MIGRATE_EXCLUDE_PORTS.value]),
|
||||
constants.ROUTERS: Resource(constants.ROUTERS,
|
||||
[Operations.LIST_MISMATCHES.value,
|
||||
Operations.NSX_UPDATE_RULES.value]),
|
||||
Operations.NSX_UPDATE_RULES.value,
|
||||
Operations.NSX_UPDATE_DHCP_RELAY.value]),
|
||||
constants.DHCP_BINDING: Resource(constants.DHCP_BINDING,
|
||||
[Operations.LIST.value,
|
||||
Operations.NSX_UPDATE.value]),
|
||||
|
@ -41,6 +41,7 @@ class Nsxv3AvailabilityZonesTestCase(base.BaseTestCase):
|
||||
cfg.CONF.set_override("dns_domain", "xxx.com", group="nsx_v3")
|
||||
cfg.CONF.set_override("nameservers", ["10.1.1.1"], group="nsx_v3")
|
||||
cfg.CONF.set_override("switching_profiles", ["uuid1"], group="nsx_v3")
|
||||
cfg.CONF.set_override("dhcp_relay_service", "service1", group="nsx_v3")
|
||||
|
||||
def _config_az(self,
|
||||
metadata_proxy="metadata_proxy1",
|
||||
@ -50,7 +51,8 @@ class Nsxv3AvailabilityZonesTestCase(base.BaseTestCase):
|
||||
nameservers=["20.1.1.1"],
|
||||
default_overlay_tz='otz',
|
||||
default_vlan_tz='vtz',
|
||||
switching_profiles=["uuid2"]):
|
||||
switching_profiles=["uuid2"],
|
||||
dhcp_relay_service="service2"):
|
||||
if metadata_proxy is not None:
|
||||
cfg.CONF.set_override("metadata_proxy", metadata_proxy,
|
||||
group=self.group_name)
|
||||
@ -76,6 +78,9 @@ class Nsxv3AvailabilityZonesTestCase(base.BaseTestCase):
|
||||
if switching_profiles is not None:
|
||||
cfg.CONF.set_override("switching_profiles", switching_profiles,
|
||||
group=self.group_name)
|
||||
if dhcp_relay_service is not None:
|
||||
cfg.CONF.set_override("dhcp_relay_service", dhcp_relay_service,
|
||||
group=self.group_name)
|
||||
|
||||
def test_simple_availability_zone(self):
|
||||
self._config_az()
|
||||
@ -89,6 +94,7 @@ class Nsxv3AvailabilityZonesTestCase(base.BaseTestCase):
|
||||
self.assertEqual("otz", az.default_overlay_tz)
|
||||
self.assertEqual("vtz", az.default_vlan_tz)
|
||||
self.assertEqual(["uuid2"], az.switching_profiles)
|
||||
self.assertEqual("service2", az.dhcp_relay_service)
|
||||
|
||||
def test_missing_group_section(self):
|
||||
self.assertRaises(
|
||||
|
@ -64,6 +64,7 @@ NSX_TZ_NAME = 'default transport zone'
|
||||
NSX_DHCP_PROFILE_ID = 'default dhcp profile'
|
||||
NSX_METADATA_PROXY_ID = 'default metadata proxy'
|
||||
NSX_SWITCH_PROFILE = 'dummy switch profile'
|
||||
NSX_DHCP_RELAY_SRV = 'dhcp relay srv'
|
||||
|
||||
|
||||
def _mock_create_firewall_rules(*args):
|
||||
@ -134,6 +135,11 @@ def _mock_nsx_backend_calls():
|
||||
"get_id_by_name_or_id",
|
||||
return_value=NSX_DHCP_PROFILE_ID).start()
|
||||
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.core_resources.NsxLibDhcpRelayService."
|
||||
"get_id_by_name_or_id",
|
||||
return_value=NSX_DHCP_RELAY_SRV).start()
|
||||
|
||||
mock.patch(
|
||||
"vmware_nsxlib.v3.core_resources.NsxLibMetadataProxy."
|
||||
"get_id_by_name_or_id",
|
||||
@ -1315,6 +1321,29 @@ class TestL3NatTestCase(L3NatTest,
|
||||
{'router': {'admin_state_up': False}},
|
||||
expected_code=exc.HTTPBadRequest.code)
|
||||
|
||||
def test_router_dhcp_relay(self):
|
||||
# Add the relay service to the config and availability zones
|
||||
cfg.CONF.set_override('dhcp_relay_service', NSX_DHCP_RELAY_SRV,
|
||||
'nsx_v3')
|
||||
mock_nsx_version = mock.patch.object(
|
||||
self.plugin.nsxlib, 'feature_supported', return_value=True)
|
||||
mock_nsx_version.start()
|
||||
self.plugin.init_availability_zones()
|
||||
for az in self.plugin.get_azs_list():
|
||||
az.translate_configured_names_to_uuids(self.plugin.nsxlib)
|
||||
|
||||
with self.network() as network:
|
||||
with self.subnet(network=network) as s1,\
|
||||
self.router() as r1,\
|
||||
mock.patch.object(self.plugin.nsxlib.logical_router_port,
|
||||
'update') as mock_update_port:
|
||||
self._router_interface_action('add', r1['router']['id'],
|
||||
s1['subnet']['id'], None)
|
||||
mock_update_port.assert_called_once_with(
|
||||
mock.ANY,
|
||||
relay_service_uuid=NSX_DHCP_RELAY_SRV,
|
||||
subnets=mock.ANY)
|
||||
|
||||
|
||||
class ExtGwModeTestCase(test_ext_gw_mode.ExtGwModeIntTestCase,
|
||||
L3NatTest):
|
||||
|
Loading…
Reference in New Issue
Block a user