Merge "NSX|V: add in support for DHCP options"
This commit is contained in:
commit
350cb07e10
@ -52,6 +52,7 @@ from neutron.db.availability_zone import router as router_az_db
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import dns_db
|
||||
from neutron.db import external_net_db
|
||||
from neutron.db import extradhcpopt_db
|
||||
from neutron.db import extraroute_db
|
||||
from neutron.db import l3_attrs_db
|
||||
from neutron.db import l3_db
|
||||
@ -67,6 +68,7 @@ from neutron.db import vlantransparent_db
|
||||
from neutron.extensions import allowedaddresspairs as addr_pair
|
||||
from neutron.extensions import availability_zone as az_ext
|
||||
from neutron.extensions import external_net as ext_net_extn
|
||||
from neutron.extensions import extra_dhcp_opt as ext_edo
|
||||
from neutron.extensions import flavors
|
||||
from neutron.extensions import l3
|
||||
from neutron.extensions import multiprovidernet as mpnet
|
||||
@ -141,6 +143,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
rt_rtr.RouterType_mixin,
|
||||
external_net_db.External_net_db_mixin,
|
||||
extraroute_db.ExtraRoute_db_mixin,
|
||||
extradhcpopt_db.ExtraDhcpOptMixin,
|
||||
router_az_db.RouterAvailabilityZoneMixin,
|
||||
l3_gwmode_db.L3_NAT_db_mixin,
|
||||
portbindings_db.PortBindingMixin,
|
||||
@ -164,6 +167,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
"provider",
|
||||
"quotas",
|
||||
"external-net",
|
||||
"extra_dhcp_opt",
|
||||
"extraroute",
|
||||
"router",
|
||||
"security-group",
|
||||
@ -1638,8 +1642,37 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
port_id=port_data['id'],
|
||||
vnic_type=vnic_type)
|
||||
|
||||
def _validate_extra_dhcp_options(self, opts):
|
||||
if not opts:
|
||||
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 opt_name not in vcns_const.SUPPORTED_DHCP_OPTIONS:
|
||||
try:
|
||||
option = int(opt_name)
|
||||
except ValueError:
|
||||
option = 255
|
||||
if option >= 255:
|
||||
msg = (_("DHCP option %s is not supported") % opt_name)
|
||||
LOG.error(msg)
|
||||
raise n_exc.InvalidInput(error_message=msg)
|
||||
|
||||
def create_port(self, context, port):
|
||||
port_data = port['port']
|
||||
dhcp_opts = port_data.get(ext_edo.EXTRADHCPOPTS)
|
||||
self._validate_extra_dhcp_options(dhcp_opts)
|
||||
|
||||
with db_api.context_manager.writer.using(context):
|
||||
# First we allocate port in neutron database
|
||||
neutron_db = super(NsxVPluginV2, self).create_port(context, port)
|
||||
@ -1700,6 +1733,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
self._process_vnic_type(context, port_data, attrs,
|
||||
has_security_groups,
|
||||
port_security)
|
||||
self._process_port_create_extra_dhcp_opts(
|
||||
context, port_data, dhcp_opts)
|
||||
|
||||
# Invoking the manager callback under transaction fails so here
|
||||
# we do it outside. If this fails we will blow away the port
|
||||
@ -1718,7 +1753,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
try:
|
||||
# Configure NSX - this should not be done in the DB transaction
|
||||
# Configure the DHCP Edge service
|
||||
self._create_dhcp_static_binding(context, neutron_db)
|
||||
self._create_dhcp_static_binding(context, port_data)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.exception('Failed to create port')
|
||||
@ -1833,6 +1868,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
device_id):
|
||||
attrs = port[attr.PORT]
|
||||
port_data = port['port']
|
||||
dhcp_opts = port_data.get(ext_edo.EXTRADHCPOPTS)
|
||||
self._validate_extra_dhcp_options(dhcp_opts)
|
||||
if addr_pair.ADDRESS_PAIRS in attrs:
|
||||
self._validate_address_pairs(attrs, original_port)
|
||||
orig_has_port_security = (cfg.CONF.nsxv.spoofguard_enabled and
|
||||
@ -1946,11 +1983,13 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
self._process_vnic_type(context, ret_port, attrs,
|
||||
has_security_groups,
|
||||
has_port_security)
|
||||
self._update_extra_dhcp_opts_on_port(context, id, port,
|
||||
ret_port)
|
||||
|
||||
if comp_owner_update:
|
||||
# Create dhcp bindings, the port is now owned by an instance
|
||||
self._create_dhcp_static_binding(context, ret_port)
|
||||
elif port_ip_change:
|
||||
elif port_ip_change or dhcp_opts:
|
||||
owner = original_port['device_owner']
|
||||
# If port IP has changed we should update according to device
|
||||
# owner
|
||||
|
@ -72,6 +72,14 @@ ALLOWED_EDGE_SIZES = (nsxv_constants.COMPACT,
|
||||
ALLOWED_EDGE_TYPES = (nsxv_constants.SERVICE_EDGE,
|
||||
nsxv_constants.VDR_EDGE)
|
||||
|
||||
SUPPORTED_DHCP_OPTIONS = {
|
||||
'interface-mtu': 'option26',
|
||||
'tftp-server-name': 'option66',
|
||||
'bootfile-name': 'option67',
|
||||
'classless-static-route': 'option121',
|
||||
'tftp-server-address': 'option150',
|
||||
}
|
||||
|
||||
|
||||
# router status by number
|
||||
class RouterStatus(object):
|
||||
|
@ -32,6 +32,7 @@ from oslo_utils import timeutils
|
||||
from oslo_utils import uuidutils
|
||||
from six import moves
|
||||
|
||||
from neutron.extensions import extra_dhcp_opt as ext_edo
|
||||
from neutron.extensions import l3
|
||||
from neutron.plugins.common import constants as plugin_const
|
||||
|
||||
@ -926,6 +927,35 @@ class EdgeManager(object):
|
||||
with locking.LockManager.get_lock(str(edge_binding['edge_id'])):
|
||||
self.update_dhcp_service_config(context, edge_binding['edge_id'])
|
||||
|
||||
def _add_dhcp_option(self, static_config, opt):
|
||||
if 'dhcpOptions' not in static_config:
|
||||
static_config['dhcpOptions'] = {}
|
||||
opt_name = opt['opt_name']
|
||||
opt_val = opt['opt_value']
|
||||
if opt_name in vcns_const.SUPPORTED_DHCP_OPTIONS:
|
||||
key = vcns_const.SUPPORTED_DHCP_OPTIONS[opt_name]
|
||||
if opt_name == 'classless-static-route':
|
||||
if 'option121' not in static_config['dhcpOptions']:
|
||||
static_config['dhcpOptions']['option121'] = {
|
||||
'staticRoutes': []}
|
||||
opt121 = static_config['dhcpOptions']['option121']
|
||||
net, ip = opt_val.split(',')
|
||||
opt121['staticRoutes'].append({'destinationSubnet': net,
|
||||
'router': ip})
|
||||
elif opt_name == 'tftp-server-address':
|
||||
if 'option150' not in static_config['dhcpOptions']:
|
||||
static_config['dhcpOptions']['option150'] = {
|
||||
'tftpServers': []}
|
||||
opt150 = static_config['dhcpOptions']['option150']
|
||||
opt150['tftpServers'].append(opt_val)
|
||||
else:
|
||||
static_config['dhcpOptions'][key] = opt_val
|
||||
else:
|
||||
if 'other' not in static_config['dhcpOptions']:
|
||||
static_config['dhcpOptions']['others'] = []
|
||||
static_config['dhcpOptions']['others'].append(
|
||||
{'code': opt_name, 'value': opt_val})
|
||||
|
||||
def create_static_binding(self, context, port):
|
||||
"""Create the DHCP Edge static binding configuration
|
||||
|
||||
@ -993,6 +1023,11 @@ class EdgeManager(object):
|
||||
host_route['destination'],
|
||||
host_route['nexthop'])
|
||||
|
||||
dhcp_opts = port.get(ext_edo.EXTRADHCPOPTS)
|
||||
if dhcp_opts is not None:
|
||||
for opt in dhcp_opts:
|
||||
self._add_dhcp_option(static_config, opt)
|
||||
|
||||
static_bindings.append(static_config)
|
||||
return static_bindings
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import contextlib
|
||||
import copy
|
||||
from eventlet import greenthread
|
||||
import mock
|
||||
import netaddr
|
||||
@ -23,6 +24,7 @@ from neutron.api.v2 import attributes
|
||||
from neutron.extensions import allowedaddresspairs as addr_pair
|
||||
from neutron.extensions import dvr as dist_router
|
||||
from neutron.extensions import external_net
|
||||
from neutron.extensions import extra_dhcp_opt as edo_ext
|
||||
from neutron.extensions import l3
|
||||
from neutron.extensions import l3_ext_gw_mode
|
||||
from neutron.extensions import l3_flavors
|
||||
@ -35,6 +37,7 @@ from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
||||
import neutron.tests.unit.db.test_allowedaddresspairs_db as test_addr_pair
|
||||
import neutron.tests.unit.db.test_db_base_plugin_v2 as test_plugin
|
||||
from neutron.tests.unit.extensions import base as extension
|
||||
from neutron.tests.unit.extensions import test_extra_dhcp_opt as test_dhcpopts
|
||||
import neutron.tests.unit.extensions.test_l3 as test_l3_plugin
|
||||
import neutron.tests.unit.extensions.test_l3_ext_gw_mode as test_ext_gw_mode
|
||||
import neutron.tests.unit.extensions.test_portsecurity as test_psec
|
||||
@ -5305,3 +5308,183 @@ class TestRouterFlavorTestCase(extension.ExtensionTestCase,
|
||||
self._test_router_create_with_flavor_error(
|
||||
metainfo, n_exc.BadRequest,
|
||||
create_az=['az2'])
|
||||
|
||||
|
||||
class DHCPOptsTestCase(test_dhcpopts.TestExtraDhcpOpt,
|
||||
NsxVPluginV2TestCase):
|
||||
|
||||
def setUp(self, plugin=None):
|
||||
super(test_dhcpopts.ExtraDhcpOptDBTestCase, self).setUp(
|
||||
plugin=PLUGIN_NAME)
|
||||
|
||||
def test_create_port_with_extradhcpopts(self):
|
||||
opt_list = [{'opt_name': 'bootfile-name',
|
||||
'opt_value': 'pxelinux.0'},
|
||||
{'opt_name': 'tftp-server-address',
|
||||
'opt_value': '123.123.123.123'}]
|
||||
|
||||
params = {edo_ext.EXTRADHCPOPTS: opt_list,
|
||||
'arg_list': (edo_ext.EXTRADHCPOPTS,)}
|
||||
|
||||
with self.port(**params) as port:
|
||||
self._check_opts(opt_list,
|
||||
port['port'][edo_ext.EXTRADHCPOPTS])
|
||||
|
||||
def test_create_port_with_extradhcpopts_ipv6_opt_version(self):
|
||||
self.skipTest('No DHCP v6 Support yet')
|
||||
|
||||
def test_create_port_with_extradhcpopts_ipv4_opt_version(self):
|
||||
opt_list = [{'opt_name': 'bootfile-name',
|
||||
'opt_value': 'pxelinux.0',
|
||||
'ip_version': 4},
|
||||
{'opt_name': 'tftp-server-address',
|
||||
'opt_value': '123.123.123.123',
|
||||
'ip_version': 4}]
|
||||
|
||||
params = {edo_ext.EXTRADHCPOPTS: opt_list,
|
||||
'arg_list': (edo_ext.EXTRADHCPOPTS,)}
|
||||
|
||||
with self.port(**params) as port:
|
||||
self._check_opts(opt_list,
|
||||
port['port'][edo_ext.EXTRADHCPOPTS])
|
||||
|
||||
def test_update_port_with_extradhcpopts_with_same(self):
|
||||
opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'},
|
||||
{'opt_name': 'tftp-server-address',
|
||||
'opt_value': '123.123.123.123'}]
|
||||
upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': 'changeme.0'}]
|
||||
expected_opts = opt_list[:]
|
||||
for i in expected_opts:
|
||||
if i['opt_name'] == upd_opts[0]['opt_name']:
|
||||
i['opt_value'] = upd_opts[0]['opt_value']
|
||||
break
|
||||
self._test_update_port_with_extradhcpopts(opt_list, upd_opts,
|
||||
expected_opts)
|
||||
|
||||
def test_update_port_with_additional_extradhcpopt(self):
|
||||
opt_list = [{'opt_name': 'tftp-server-address',
|
||||
'opt_value': '123.123.123.123'}]
|
||||
upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': 'changeme.0'}]
|
||||
expected_opts = copy.deepcopy(opt_list)
|
||||
expected_opts.append(upd_opts[0])
|
||||
self._test_update_port_with_extradhcpopts(opt_list, upd_opts,
|
||||
expected_opts)
|
||||
|
||||
def test_update_port_with_extradhcpopts(self):
|
||||
opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'},
|
||||
{'opt_name': 'tftp-server-address',
|
||||
'opt_value': '123.123.123.123'}]
|
||||
upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': 'changeme.0'}]
|
||||
expected_opts = copy.deepcopy(opt_list)
|
||||
for i in expected_opts:
|
||||
if i['opt_name'] == upd_opts[0]['opt_name']:
|
||||
i['opt_value'] = upd_opts[0]['opt_value']
|
||||
break
|
||||
self._test_update_port_with_extradhcpopts(opt_list, upd_opts,
|
||||
expected_opts)
|
||||
|
||||
def test_update_port_with_extradhcpopt_delete(self):
|
||||
opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'},
|
||||
{'opt_name': 'tftp-server-address',
|
||||
'opt_value': '123.123.123.123'}]
|
||||
upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': None}]
|
||||
expected_opts = []
|
||||
|
||||
expected_opts = [opt for opt in opt_list
|
||||
if opt['opt_name'] != 'bootfile-name']
|
||||
self._test_update_port_with_extradhcpopts(opt_list, upd_opts,
|
||||
expected_opts)
|
||||
|
||||
def test_update_port_adding_extradhcpopts(self):
|
||||
opt_list = []
|
||||
upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'},
|
||||
{'opt_name': 'tftp-server-address',
|
||||
'opt_value': '123.123.123.123'}]
|
||||
expected_opts = copy.deepcopy(upd_opts)
|
||||
self._test_update_port_with_extradhcpopts(opt_list, upd_opts,
|
||||
expected_opts)
|
||||
|
||||
def test_update_port_with_blank_name_extradhcpopt(self):
|
||||
opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'},
|
||||
{'opt_name': 'tftp-server-address',
|
||||
'opt_value': '123.123.123.123'}]
|
||||
upd_opts = [{'opt_name': ' ', 'opt_value': 'pxelinux.0'}]
|
||||
|
||||
params = {edo_ext.EXTRADHCPOPTS: opt_list,
|
||||
'arg_list': (edo_ext.EXTRADHCPOPTS,)}
|
||||
|
||||
with self.port(**params) as port:
|
||||
update_port = {'port': {edo_ext.EXTRADHCPOPTS: upd_opts}}
|
||||
|
||||
req = self.new_update_request('ports', update_port,
|
||||
port['port']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int)
|
||||
|
||||
def test_create_port_with_empty_router_extradhcpopts(self):
|
||||
self.skipTest('No DHCP support option for router')
|
||||
|
||||
def test_update_port_with_blank_router_extradhcpopt(self):
|
||||
self.skipTest('No DHCP support option for router')
|
||||
|
||||
def test_update_port_with_extradhcpopts_ipv6_change_value(self):
|
||||
self.skipTest('No DHCP v6 Support yet')
|
||||
|
||||
def test_update_port_with_extradhcpopts_add_another_ver_opt(self):
|
||||
self.skipTest('No DHCP v6 Support yet')
|
||||
|
||||
def test_update_port_with_blank_string_extradhcpopt(self):
|
||||
opt_list = [{'opt_name': 'bootfile-name', 'opt_value': 'pxelinux.0'},
|
||||
{'opt_name': 'tftp-server-address',
|
||||
'opt_value': '123.123.123.123'}]
|
||||
upd_opts = [{'opt_name': 'bootfile-name', 'opt_value': ' '}]
|
||||
|
||||
params = {edo_ext.EXTRADHCPOPTS: opt_list,
|
||||
'arg_list': (edo_ext.EXTRADHCPOPTS,)}
|
||||
|
||||
with self.port(**params) as port:
|
||||
update_port = {'port': {edo_ext.EXTRADHCPOPTS: upd_opts}}
|
||||
|
||||
req = self.new_update_request('ports', update_port,
|
||||
port['port']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int)
|
||||
|
||||
def test_create_port_with_none_extradhcpopts(self):
|
||||
opt_list = [{'opt_name': 'bootfile-name',
|
||||
'opt_value': None},
|
||||
{'opt_name': 'tftp-server-address',
|
||||
'opt_value': '123.123.123.123'}]
|
||||
expected = [{'opt_name': 'tftp-server-address',
|
||||
'opt_value': '123.123.123.123'}]
|
||||
|
||||
params = {edo_ext.EXTRADHCPOPTS: opt_list,
|
||||
'arg_list': (edo_ext.EXTRADHCPOPTS,)}
|
||||
|
||||
with self.port(**params) as port:
|
||||
self._check_opts(expected,
|
||||
port['port'][edo_ext.EXTRADHCPOPTS])
|
||||
|
||||
def test_create_port_with_extradhcpopts_codes(self):
|
||||
opt_list = [{'opt_name': '85',
|
||||
'opt_value': 'cafecafe'}]
|
||||
|
||||
params = {edo_ext.EXTRADHCPOPTS: opt_list,
|
||||
'arg_list': (edo_ext.EXTRADHCPOPTS,)}
|
||||
|
||||
with self.port(**params) as port:
|
||||
self._check_opts(opt_list,
|
||||
port['port'][edo_ext.EXTRADHCPOPTS])
|
||||
|
||||
def test_update_port_with_extradhcpopts_codes(self):
|
||||
opt_list = [{'opt_name': '85',
|
||||
'opt_value': 'cafecafe'}]
|
||||
upd_opts = [{'opt_name': '85',
|
||||
'opt_value': '01010101'}]
|
||||
expected_opts = copy.deepcopy(opt_list)
|
||||
for i in expected_opts:
|
||||
if i['opt_name'] == upd_opts[0]['opt_name']:
|
||||
i['opt_value'] = upd_opts[0]['opt_value']
|
||||
break
|
||||
self._test_update_port_with_extradhcpopts(opt_list, upd_opts,
|
||||
expected_opts)
|
||||
|
Loading…
Reference in New Issue
Block a user