Merge "NSX|V: enable allow_address_pairs upon request"
This commit is contained in:
commit
9fbfaf22a7
@ -808,6 +808,10 @@ nsxv_opts = [
|
||||
default=False,
|
||||
help=_("Use subnet's exclusive router as a platform for "
|
||||
"LBaaS")),
|
||||
cfg.BoolOpt('allow_multiple_ip_addresses',
|
||||
default=False,
|
||||
help=_("Allow associating multiple IPs to VMs "
|
||||
"without spoofguard limitations")),
|
||||
]
|
||||
|
||||
# define the configuration of each NSX-V availability zone.
|
||||
|
@ -1355,8 +1355,15 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
net_data['id'])
|
||||
net_data[psec.PORTSECURITY] = False
|
||||
# Create SpoofGuard policy for network anti-spoofing
|
||||
# allow_multiple_addresses will be overridden in case the user
|
||||
# requires allowing multiple or cidr-based allowed address pairs
|
||||
# defined per port but doesn't want to disable spoofguard globally
|
||||
sg_policy_id = None
|
||||
if cfg.CONF.nsxv.spoofguard_enabled and backend_network:
|
||||
allow_multiple_addresses = (not net_data[psec.PORTSECURITY]
|
||||
and cfg.CONF.nsxv.
|
||||
allow_multiple_ip_addresses)
|
||||
if (cfg.CONF.nsxv.spoofguard_enabled and backend_network and not
|
||||
allow_multiple_addresses):
|
||||
# This variable is set as the method below may result in a
|
||||
# exception and we may need to rollback
|
||||
predefined = False
|
||||
@ -1742,6 +1749,75 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
"installed in order for security-groups to "
|
||||
"function properly.", net_id)
|
||||
|
||||
def allow_multiple_addresses_configure_spoofguard(self, context, id,
|
||||
net_attrs, net_morefs):
|
||||
# User requires multiple addresses to be assigned to compute port
|
||||
# and therefore, the spoofguard policy is being removed for this net.
|
||||
orig_net = self.get_network(context, id)
|
||||
if not net_attrs[psec.PORTSECURITY]:
|
||||
sg_policy = nsxv_db.get_spoofguard_policy_id(context.session,
|
||||
orig_net['id'])
|
||||
if sg_policy:
|
||||
try:
|
||||
self.nsx_v.vcns.delete_spoofguard_policy(sg_policy)
|
||||
except Exception as e:
|
||||
LOG.error('Unable to delete spoofguard policy '
|
||||
'%(sg_policy)s. Error: %(e)s',
|
||||
{'sg_policy': sg_policy, 'e': e})
|
||||
else:
|
||||
LOG.warning("Could not locate spoofguard policy for "
|
||||
"network %s", id)
|
||||
# User requires port-security-enabled set to True and thus requires
|
||||
# spoofguard installed for this network
|
||||
else:
|
||||
# Verifying that all ports are legal, i.e. not CIDR/subnet
|
||||
filters = {'network_id': [id]}
|
||||
ports = self.get_ports(context, filters=filters)
|
||||
valid_ports = []
|
||||
if ports:
|
||||
for port in ports:
|
||||
if self._is_compute_port(port):
|
||||
for ap in port[addr_apidef.ADDRESS_PAIRS]:
|
||||
if len(ap['ip_address'].split('/')) > 1:
|
||||
msg = _('Port %s contains CIDR/subnet, '
|
||||
'which is not supported at the '
|
||||
'backend ') % port['id']
|
||||
raise n_exc.BadRequest(
|
||||
resource='ports',
|
||||
msg=msg)
|
||||
else:
|
||||
valid_ports.append(port)
|
||||
try:
|
||||
sg_policy_id, predefined = (
|
||||
self._prepare_spoofguard_policy(
|
||||
orig_net.get(pnet.NETWORK_TYPE), orig_net,
|
||||
net_morefs))
|
||||
if sg_policy_id:
|
||||
nsxv_db.map_spoofguard_policy_for_network(
|
||||
context.session,
|
||||
orig_net['id'], sg_policy_id)
|
||||
except Exception as e:
|
||||
msg = _('Unable to create spoofguard policy, error: %('
|
||||
'error)s, '
|
||||
'net_morefs=%(net_morefs)s, network_id= %('
|
||||
'network_id)s') % {'error': e, 'net_morefs':
|
||||
net_morefs, 'network_id': orig_net}
|
||||
raise n_exc.BadRequest(resource='spoofguard policy', msg=msg)
|
||||
|
||||
try:
|
||||
for port in valid_ports:
|
||||
vnic_idx = port.get(ext_vnic_idx.VNIC_INDEX)
|
||||
device_id = port['device_id']
|
||||
vnic_id = self._get_port_vnic_id(vnic_idx,
|
||||
device_id)
|
||||
self._update_vnic_assigned_addresses(context.session, port,
|
||||
vnic_id)
|
||||
except Exception as e:
|
||||
msg = _('Unable to add port to spoofguard policy error '
|
||||
'%s') % e
|
||||
raise n_exc.BadRequest(resource='spoofguard policy',
|
||||
msg=msg)
|
||||
|
||||
def update_network(self, context, id, network):
|
||||
net_attrs = network['network']
|
||||
orig_net = self.get_network(context, id)
|
||||
@ -1767,6 +1843,15 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
if psec_update:
|
||||
self._update_network_validate_port_sec(context, id, net_attrs)
|
||||
|
||||
# Change spoofguard accordingly - either remove if
|
||||
# port-security-enabled was set to false or add (with relevant ports)
|
||||
# if set to true.
|
||||
if (cfg.CONF.nsxv.spoofguard_enabled and
|
||||
cfg.CONF.nsxv.allow_multiple_ip_addresses and psec_update):
|
||||
self.allow_multiple_addresses_configure_spoofguard(context, id,
|
||||
net_attrs,
|
||||
net_morefs)
|
||||
|
||||
# Check if the physical network of a vlan provider network was updated
|
||||
updated_morefs = False
|
||||
if (net_attrs.get(pnet.PHYSICAL_NETWORK) and
|
||||
@ -1875,17 +1960,24 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
|
||||
return net_res
|
||||
|
||||
def _validate_address_pairs(self, attrs, db_port):
|
||||
def _validate_address_pairs(self, context, attrs, db_port):
|
||||
network_port_security = self._get_network_security_binding(
|
||||
context, db_port['network_id'])
|
||||
if (not cfg.CONF.nsxv.allow_multiple_ip_addresses and
|
||||
not network_port_security):
|
||||
for ap in attrs[addr_apidef.ADDRESS_PAIRS]:
|
||||
# Check that the IP address is a subnet
|
||||
if len(ap['ip_address'].split('/')) > 1:
|
||||
msg = _('NSXv does not support CIDR as address pairs')
|
||||
raise n_exc.BadRequest(resource='address_pairs',
|
||||
msg=msg)
|
||||
# Check that the MAC address is the same as the port
|
||||
for ap in attrs[addr_apidef.ADDRESS_PAIRS]:
|
||||
# Check that the IP address is a subnet
|
||||
if len(ap['ip_address'].split('/')) > 1:
|
||||
msg = _('NSXv does not support CIDR as address pairs')
|
||||
raise n_exc.BadRequest(resource='address_pairs', msg=msg)
|
||||
# Check that the MAC address is the same as the port
|
||||
if ('mac_address' in ap and
|
||||
ap['mac_address'] != db_port['mac_address']):
|
||||
msg = _('Address pairs should have same MAC as the port')
|
||||
raise n_exc.BadRequest(resource='address_pairs', msg=msg)
|
||||
ap['mac_address'] != db_port['mac_address']):
|
||||
msg = _('Address pairs should have same MAC as the '
|
||||
'port')
|
||||
raise n_exc.BadRequest(resource='address_pairs', msg=msg)
|
||||
|
||||
def _is_mac_in_use(self, context, network_id, mac_address):
|
||||
# Override this method as the backed doesn't support using the same
|
||||
@ -2004,7 +2096,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
if self._check_update_has_allowed_address_pairs(port):
|
||||
if not port_security:
|
||||
raise addr_exc.AddressPairAndPortSecurityRequired()
|
||||
self._validate_address_pairs(attrs, neutron_db)
|
||||
self._validate_address_pairs(context, attrs, neutron_db)
|
||||
else:
|
||||
# remove ATTR_NOT_SPECIFIED
|
||||
attrs[addr_apidef.ADDRESS_PAIRS] = []
|
||||
@ -2216,7 +2308,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
self._validate_extra_dhcp_options(dhcp_opts)
|
||||
self._validate_port_qos(port_data)
|
||||
if addr_apidef.ADDRESS_PAIRS in attrs:
|
||||
self._validate_address_pairs(attrs, original_port)
|
||||
self._validate_address_pairs(context, attrs, original_port)
|
||||
self._validate_max_ips_per_port(
|
||||
port_data.get('fixed_ips', []),
|
||||
port_data.get('device_owner', original_port['device_owner']))
|
||||
|
@ -615,6 +615,33 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase):
|
||||
# Assert neutron name is not truncated
|
||||
self.assertEqual(net['network']['name'], name)
|
||||
|
||||
def test_create_update_network_allow_multiple_addresses_spoofguard(self):
|
||||
# allow_multiple_addresses flag is True, first step is to check that
|
||||
# when port-security-allowed is false - spoofguard policy is not
|
||||
# created.
|
||||
# next step is to update port-security-allowed to true - spoofguard
|
||||
# policy is now created for this network.
|
||||
q_context = context.Context('', 'tenant_1')
|
||||
providernet_args = {psec.PORTSECURITY: False}
|
||||
cfg.CONF.set_default('allow_multiple_ip_addresses', True, 'nsxv')
|
||||
res = self._create_network(fmt='json', name='net-1',
|
||||
admin_state_up=True,
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(psec.PORTSECURITY,))
|
||||
network1 = self.deserialize(self.fmt, res)
|
||||
net1_id = network1['network']['id']
|
||||
# not creating spoofguard policy
|
||||
self.assertIsNone(nsxv_db.get_spoofguard_policy_id(q_context.session,
|
||||
net1_id))
|
||||
args = {'network': {psec.PORTSECURITY: True}}
|
||||
req = self.new_update_request('networks', args,
|
||||
network1['network']['id'], fmt='json')
|
||||
res = self.deserialize('json', req.get_response(self.api))
|
||||
net1_id = res['network']['id']
|
||||
# creating spoofguard policy
|
||||
self.assertIsNotNone(nsxv_db.get_spoofguard_policy_id(
|
||||
q_context.session, net1_id))
|
||||
|
||||
def test_update_network_with_admin_false(self):
|
||||
data = {'network': {'admin_state_up': False}}
|
||||
with self.network() as net:
|
||||
@ -1769,6 +1796,93 @@ class TestPortsV2(NsxVPluginV2TestCase,
|
||||
self.assertEqual("PortSecurityAndIPRequiredForSecurityGroups",
|
||||
res['NeutronError']['type'])
|
||||
|
||||
def test_port_add_to_spoofguard_allow_multiple_addresses(self):
|
||||
# allow_multiple_addresses flag is True, first step is to check that
|
||||
# when port-security-allowed is false - spoofguard policy is not
|
||||
# created.
|
||||
# next step is to update port-security-allowed to true - spoofguard
|
||||
# policy is now created for this network.
|
||||
providernet_args = {psec.PORTSECURITY: False}
|
||||
cfg.CONF.set_default('allow_multiple_ip_addresses', True, 'nsxv')
|
||||
res = self._create_network(fmt='json', name='net-1',
|
||||
admin_state_up=True,
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(psec.PORTSECURITY,))
|
||||
network1 = self.deserialize(self.fmt, res)
|
||||
net1_id = network1['network']['id']
|
||||
with self.subnet(network=network1, cidr='10.0.0.0/24'):
|
||||
# create a compute port with port security
|
||||
address_pairs = [{'ip_address': '192.168.1.1'}]
|
||||
device_id = _uuid()
|
||||
vnic_index = 3
|
||||
compute_port_create = self._create_port(
|
||||
'json', net1_id,
|
||||
arg_list=(
|
||||
'port_security_enabled',
|
||||
'device_id',
|
||||
'device_owner',
|
||||
'allowed_address_pairs',),
|
||||
port_security_enabled=True,
|
||||
device_id=device_id,
|
||||
device_owner='compute:None',
|
||||
allowed_address_pairs=address_pairs)
|
||||
port = self.deserialize('json', compute_port_create)
|
||||
port = self._update_port_index(
|
||||
port['port']['id'], device_id, vnic_index)
|
||||
# Verify the port is added to the spoofguard policy
|
||||
with mock.patch.object(
|
||||
self.plugin, '_update_vnic_assigned_addresses') as \
|
||||
update_approved_port:
|
||||
args = {'network': {psec.PORTSECURITY: True}}
|
||||
req = self.new_update_request('networks', args, net1_id,
|
||||
fmt='json')
|
||||
req.get_response(self.api)
|
||||
# The expected vnic-id format by NsxV
|
||||
update_approved_port.assert_called_once_with(
|
||||
mock.ANY, mock.ANY, '%s.%03d' % (device_id, vnic_index))
|
||||
|
||||
def test_port_add_to_spoofguard_allow_multiple_addresses_fail(self):
|
||||
# allow_multiple_addresses flag is True, first step is to check that
|
||||
# when port-security-allowed is false - spoofguard policy is not
|
||||
# created.
|
||||
# next step is to update port-security-allowed to true but the port
|
||||
# has CIDR defined as a address pair - action is aborted.
|
||||
# policy is now created for this network.
|
||||
providernet_args = {psec.PORTSECURITY: False}
|
||||
cfg.CONF.set_default('allow_multiple_ip_addresses', True, 'nsxv')
|
||||
res = self._create_network(fmt='json', name='net-1',
|
||||
admin_state_up=True,
|
||||
providernet_args=providernet_args,
|
||||
arg_list=(psec.PORTSECURITY,))
|
||||
network1 = self.deserialize(self.fmt, res)
|
||||
net1_id = network1['network']['id']
|
||||
with self.subnet(network=network1, cidr='10.0.0.0/24'):
|
||||
# create a compute port with port security
|
||||
address_pairs = [{'ip_address': '192.168.1.0/24'}]
|
||||
device_id = _uuid()
|
||||
vnic_index = 3
|
||||
compute_port_create = self._create_port(
|
||||
'json', net1_id,
|
||||
arg_list=(
|
||||
'port_security_enabled',
|
||||
'device_id',
|
||||
'device_owner',
|
||||
'allowed_address_pairs',),
|
||||
port_security_enabled=True,
|
||||
device_id=device_id,
|
||||
device_owner='compute:None',
|
||||
allowed_address_pairs=address_pairs)
|
||||
port = self.deserialize('json', compute_port_create)
|
||||
port = self._update_port_index(
|
||||
port['port']['id'], device_id, vnic_index)
|
||||
# Action is failed due to CIDR defined in the port.
|
||||
args = {'network': {psec.PORTSECURITY: True}}
|
||||
plugin = directory.get_plugin()
|
||||
self.assertRaises(n_exc.BadRequest,
|
||||
plugin.update_network,
|
||||
context.get_admin_context(),
|
||||
net1_id, args)
|
||||
|
||||
|
||||
class TestSubnetsV2(NsxVPluginV2TestCase,
|
||||
test_plugin.TestSubnetsV2):
|
||||
|
Loading…
Reference in New Issue
Block a user