NSXv - Support provider security-groups

This patch implements the provider security-groups extension for NsxV
Neutron plugin.
For more details, please refer to the feature
change: I57b130437327b0bbe5cc0068695f226b76b4e2ba.

Change-Id: I0efa29893eff7d76ee69496210cda33f79742cfd
This commit is contained in:
Roey Chen 2016-07-17 01:57:46 -07:00 committed by garyk
parent d1103e24f2
commit 4d7b6a305c
6 changed files with 124 additions and 42 deletions

View File

@ -74,6 +74,11 @@ def is_nsx_version_1_1_0(nsx_version):
version.LooseVersion(NSXV3_VERSION_1_1_0))
def is_nsxv_version_6_2(nsx_version):
return (version.LooseVersion(nsx_version) >=
version.LooseVersion('6.2'))
def get_tags(**kwargs):
tags = ([dict(tag=value, scope=key)
for key, value in six.iteritems(kwargs)])

View File

@ -91,6 +91,7 @@ from vmware_nsx.extensions import (
vnicindex as ext_vnic_idx)
from vmware_nsx.extensions import dhcp_mtu as ext_dhcp_mtu
from vmware_nsx.extensions import dns_search_domain as ext_dns_search_domain
from vmware_nsx.extensions import providersecuritygroup as provider_sg
from vmware_nsx.extensions import routersize
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix
from vmware_nsx.extensions import securitygrouplogging as sg_logging
@ -238,6 +239,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
fc_utils.SERVICE_INSERTION_RESOURCE,
events.AFTER_CREATE)
if c_utils.is_nsxv_version_6_2(self.nsx_v.vcns.get_version()):
self.supported_extension_aliases.append("provider-security-group")
def init_complete(self, resource, event, trigger, **kwargs):
self.init_is_complete = True
@ -377,8 +381,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
self.nsx_v.vcns.update_section_by_id(
section_id, 'ip', section_req_body)
else:
h, c = self.nsx_v.vcns.create_section(
'ip', section_req_body)
h, c = self.nsx_v.vcns.create_section('ip', section_req_body)
section_id = self.nsx_sg_utils.parse_and_get_section_id(c)
return section_id
@ -1245,6 +1248,11 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# Update fields obtained from neutron db (eg: MAC address)
port["port"].update(neutron_db)
has_ip = self._ip_on_port(neutron_db)
provider_sg_specified = (validators.is_attr_set(
port_data.get(provider_sg.PROVIDER_SECURITYGROUPS))
and port_data[provider_sg.PROVIDER_SECURITYGROUPS] != [])
has_security_groups = (
self._check_update_has_security_groups(port))
# allowed address pair checks
attrs = port[attr.PORT]
@ -1259,12 +1267,18 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# security group extension checks
if has_ip:
self._ensure_default_security_group_on_port(context, port)
elif validators.is_attr_set(port_data.get(ext_sg.SECURITYGROUPS)):
elif (has_security_groups or provider_sg_specified):
raise psec.PortSecurityAndIPRequiredForSecurityGroups()
port_data[ext_sg.SECURITYGROUPS] = (
self._get_security_groups_on_port(context, port))
self._process_port_create_security_group(
context, port_data, port_data[ext_sg.SECURITYGROUPS])
else:
port_data[provider_sg.PROVIDER_SECURITYGROUPS] = []
sgids = self._get_security_groups_on_port(context, port)
ssgids = self._get_provider_security_groups_on_port(context, port)
self._process_port_create_security_group(context, port_data, sgids)
self._process_port_create_provider_security_group(context,
port_data,
ssgids)
self._process_portbindings_create_and_update(context,
port['port'],
port_data)
@ -1430,7 +1444,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
context, id, device_id, vnic_idx)
vnic_id = self._get_port_vnic_id(vnic_idx, device_id)
self._add_security_groups_port_mapping(
context.session, vnic_id, original_port['security_groups'])
context.session, vnic_id,
original_port[ext_sg.SECURITYGROUPS] +
original_port[provider_sg.PROVIDER_SECURITYGROUPS])
if has_port_security:
LOG.debug("Assigning vnic port fixed-ips: port %s, "
"vnic %s, with fixed-ips %s", id, vnic_id,
@ -1446,6 +1462,10 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
self._add_member_to_security_group(self._si_handler.sg_id,
vnic_id)
provider_sgs_specified = validators.is_attr_set(
port_data.get(provider_sg.PROVIDER_SECURITYGROUPS))
delete_provider_sg = provider_sgs_specified and (
port_data[provider_sg.PROVIDER_SECURITYGROUPS] != [])
delete_security_groups = self._check_update_deletes_security_groups(
port)
has_security_groups = self._check_update_has_security_groups(port)
@ -1464,22 +1484,22 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# checks that if update adds/modify security groups,
# then port has ip
if not has_ip:
if has_security_groups:
raise psec.PortSecurityAndIPRequiredForSecurityGroups()
security_groups = (
super(NsxVPluginV2,
self)._get_port_security_group_bindings(
context, {'port_id': [id]})
)
if security_groups and not delete_security_groups:
raise psec.PortSecurityAndIPRequiredForSecurityGroups()
if (has_security_groups or provider_sgs_specified):
raise psec.PortSecurityAndIPRequiredForSecurityGroups()
if ((not delete_security_groups
and original_port[ext_sg.SECURITYGROUPS]) or
(not delete_provider_sg and
original_port[provider_sg.PROVIDER_SECURITYGROUPS])):
raise psec.PortSecurityAndIPRequiredForSecurityGroups()
if delete_security_groups or has_security_groups:
# delete the port binding and read it with the new rules.
self._delete_port_security_group_bindings(context, id)
new_sgids = self._get_security_groups_on_port(context, port)
self._process_port_create_security_group(context, ret_port,
new_sgids)
self.update_security_group_on_port(context, id, port,
original_port, ret_port)
# NOTE(roeyc): Should call this method only after
# update_security_group_on_port was called.
self._process_port_update_provider_security_group(context, port,
original_port,
ret_port)
LOG.debug("Updating port: %s", port)
self._process_portbindings_create_and_update(context,
@ -1537,7 +1557,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
vnic_idx = original_port.get(ext_vnic_idx.VNIC_INDEX)
if validators.is_attr_set(vnic_idx) and is_compute_port:
vnic_id = self._get_port_vnic_id(vnic_idx, device_id)
curr_sgids = original_port.get(ext_sg.SECURITYGROUPS)
curr_sgids = (
original_port[provider_sg.PROVIDER_SECURITYGROUPS] +
original_port[ext_sg.SECURITYGROUPS])
if ret_port['device_id'] != device_id:
# Update change device_id - remove port-vnic association and
# delete security-groups memberships for the vnic
@ -1623,6 +1645,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# Update security-groups,
# calculate differences and update vnic membership
# accordingly.
new_sgids = (
ret_port[provider_sg.PROVIDER_SECURITYGROUPS] +
ret_port[ext_sg.SECURITYGROUPS])
self._update_security_groups_port_mapping(
context.session, id, vnic_id, curr_sgids, new_sgids)
@ -2797,19 +2822,22 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
nsx_sg_id):
logging = (cfg.CONF.nsxv.log_security_groups_allowed_traffic or
securitygroup[sg_logging.LOGGING])
action = 'deny' if securitygroup[provider_sg.PROVIDER] else 'allow'
section_name = self.nsx_sg_utils.get_nsx_section_name(securitygroup)
nsx_rules = []
# Translate Neutron rules to NSXv fw rules and construct the fw section
for rule in securitygroup['security_group_rules']:
nsx_rule = self._create_nsx_rule(
context, rule, nsx_sg_id, logged=logging)
context, rule, nsx_sg_id, logged=logging, action=action)
nsx_rules.append(nsx_rule)
section = self.nsx_sg_utils.get_section_with_rules(
section_name, nsx_rules)
# Execute REST API for creating the section
h, c = self.nsx_v.vcns.create_section(
'ip', self.nsx_sg_utils.to_xml_string(section),
insert_top=securitygroup[provider_sg.PROVIDER],
insert_before=self.default_section)
rule_pairs = self.nsx_sg_utils.get_rule_id_pair_from_section(c)
# Add database associations for fw section and rules
nsxv_db.add_neutron_nsx_section_mapping(
@ -2844,22 +2872,29 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
with excutils.save_and_reraise_exception():
self._delete_nsx_security_group(nsx_sg_id)
# Add this Security Group to the Security Groups container
self._add_member_to_security_group(self.sg_container_id, nsx_sg_id)
if not securitygroup[provider_sg.PROVIDER]:
# Add Security Group to the Security Groups container inorder to
# apply the default block rule. provider security-groups should not
# have a default blocking rule.
self._add_member_to_security_group(self.sg_container_id, nsx_sg_id)
def create_security_group(self, context, security_group, default_sg=False):
"""Create a security group."""
sg_data = security_group['security_group']
sg_id = sg_data["id"] = str(uuid.uuid4())
new_security_group = super(NsxVPluginV2, self).create_security_group(
context, security_group, default_sg)
self._process_security_group_properties_create(
context, new_security_group, sg_data)
with context.session.begin(subtransactions=True):
if sg_data.get(provider_sg.PROVIDER):
new_sg = self.create_provider_security_group(
context, security_group)
else:
new_sg = super(NsxVPluginV2, self).create_security_group(
context, security_group, default_sg)
self._process_security_group_properties_create(
context, new_sg, sg_data, default_sg)
try:
self._process_security_group_create_backend_resources(
context, new_security_group)
context, new_sg)
except Exception:
# Couldn't create backend resources, rolling back neutron db
# changes.
@ -2870,7 +2905,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
context = context.elevated()
super(NsxVPluginV2, self).delete_security_group(context, sg_id)
LOG.exception(_LE('Failed to create security group'))
return new_security_group
return new_sg
def update_security_group(self, context, id, security_group):
s = security_group['security_group']
@ -2925,7 +2960,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
with excutils.save_and_reraise_exception():
LOG.exception(_LE("Failed to delete security group"))
def _create_nsx_rule(self, context, rule, nsx_sg_id=None, logged=False):
def _create_nsx_rule(self, context, rule,
nsx_sg_id=None, logged=False, action='allow'):
src = None
dest = None
port = None
@ -2987,6 +3023,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
destination=dest,
services=services,
flags=flags,
action=action,
logged=logged)
return nsx_rule
@ -3010,6 +3047,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# Querying DB for associated dfw section id
section_uri = self._get_section_uri(context.session, sg_id)
logging = self._is_security_group_logged(context, sg_id)
provider = self._is_provider_security_group(context, sg_id)
log_all_rules = cfg.CONF.nsxv.log_security_groups_allowed_traffic
# Translating Neutron rules to Nsx DFW rules
@ -3019,8 +3057,11 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
rule[secgroup_rule_local_ip_prefix.LOCAL_IP_PREFIX] = None
rule['id'] = uuidutils.generate_uuid()
ruleids.add(rule['id'])
nsx_rules.append(self._create_nsx_rule(
context, rule, logged=log_all_rules or logging))
nsx_rules.append(
self._create_nsx_rule(context, rule,
logged=log_all_rules or logging,
action='deny' if provider else 'allow')
)
_h, _c = self.nsx_v.vcns.get_section(section_uri)
section = self.nsx_sg_utils.parse_section(_c)

View File

@ -554,7 +554,8 @@ class Vcns(object):
return self.do_request(HTTP_POST, uri, request, format='xml',
decode=False, encode=False)
def create_section(self, type, request, insert_before=None):
def create_section(self, type, request,
insert_top=False, insert_before=None):
"""Creates a layer 3 or layer 2 section in nsx rule table.
The method will return the uri to newly created section.
@ -564,10 +565,12 @@ class Vcns(object):
else:
sec_type = 'layer2sections'
uri = '%s/%s?autoSaveDraft=false' % (FIREWALL_PREFIX, sec_type)
if insert_top:
uri += '&operation=insert_top'
# We want to place security-group sections before the default cluster
# section, and we want to place the default cluster section before the
# global default section.
if insert_before:
elif insert_before:
uri += '&operation=insert_before&anchorId=%s' % insert_before
else:
uri += '&operation=insert_before&anchorId=1003'

View File

@ -12,16 +12,18 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
import webob.exc
from neutron.api.v2 import attributes as attr
from neutron import context
from neutron.db import db_base_plugin_v2
from neutron.db import securitygroups_db
from neutron.tests.unit.extensions import test_securitygroup
import webob.exc
from vmware_nsx.db import extended_security_group
from vmware_nsx.extensions import providersecuritygroup as provider_sg
from vmware_nsx.tests.unit.nsx_v import test_plugin as test_nsxv_plugin
from vmware_nsx.tests.unit.nsx_v3 import test_plugin as test_nsxv3_plugin
@ -246,4 +248,34 @@ class TestNSXv3ProviderSecurityGrp(test_nsxv3_plugin.NsxV3PluginTestCaseMixin,
ProviderSecurityGroupExtTestCase):
pass
# TODO(roeyc): add nsxv test case mixin when ready
class TestNSXvProviderSecurityGroup(test_nsxv_plugin.NsxVPluginV2TestCase,
ProviderSecurityGroupExtTestCase):
def test_create_provider_security_group(self):
_create_section_tmp = self.fc2.create_section
def _create_section(*args, **kwargs):
return _create_section_tmp(*args, **kwargs)
with mock.patch.object(self.fc2, 'create_section',
side_effect=_create_section) as create_sec_mock:
super(TestNSXvProviderSecurityGroup,
self).test_create_provider_security_group()
create_sec_mock.assert_called_with('ip', mock.ANY,
insert_top=True,
insert_before=mock.ANY)
def test_create_provider_security_group_rule(self):
provider_secgroup = self._create_provider_security_group()
sg_id = provider_secgroup['security_group']['id']
_create_nsx_rule_tmp = self.plugin._create_nsx_rule
def m_create_nsx_rule(*args, **kwargs):
return _create_nsx_rule_tmp(*args, **kwargs)
with mock.patch.object(self.plugin, '_create_nsx_rule',
side_effect=m_create_nsx_rule) as create_rule_m:
with self.security_group_rule(security_group_id=sg_id):
create_rule_m.assert_called_with(mock.ANY, mock.ANY,
logged=mock.ANY,
action='deny')

View File

@ -118,7 +118,7 @@ class TestNsxVExtendedSGRule(test_nsxv_plugin.NsxVSecurityGroupsTestCase,
plugin.nsx_sg_utils.get_rule_config.assert_called_with(
source=mock.ANY, destination=dest, services=mock.ANY,
name=mock.ANY, applied_to_ids=mock.ANY, flags=mock.ANY,
logged=mock.ANY)
logged=mock.ANY, action=mock.ANY)
class TestNSXv3ExtendedSGRule(test_nsxv3_plugin.NsxV3PluginTestCaseMixin,

View File

@ -903,7 +903,8 @@ class FakeVcns(object):
def create_redirect_section(self, request):
return self.create_section('layer3redirect', request)
def create_section(self, type, request, insert_before=None):
def create_section(self, type, request,
insert_top=False, insert_before=None):
section = ET.fromstring(request)
section_name = section.attrib.get('name')
if section_name in self._sections['names']: