Merge "NSX-V add nsx-policies extension"

This commit is contained in:
Jenkins 2016-11-28 09:08:00 +00:00 committed by Gerrit Code Review
commit f9c790b423
6 changed files with 229 additions and 34 deletions

View File

@ -0,0 +1,107 @@
# Copyright 2016 VMware. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# 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 abc
from neutron.api import extensions
from neutron.api.v2 import resource_helper
from neutron_lib import exceptions as nexception
from vmware_nsx._i18n import _
POLICY_RESOURCE_NAME = "nsx_policy"
# Use dash for alias and collection name
EXT_ALIAS = POLICY_RESOURCE_NAME.replace('_', '-')
NSX_POLICIES = "nsx_policies"
# The nsx-policies table is read only
RESOURCE_ATTRIBUTE_MAP = {
NSX_POLICIES: {
'id': {
'allow_post': False, 'allow_put': False, 'is_visible': True},
'name': {
'allow_post': False, 'allow_put': False, 'is_visible': True},
'description': {
'allow_post': False, 'allow_put': False, 'is_visible': True},
}
}
class Nsxpolicy(extensions.ExtensionDescriptor):
"""API extension for NSX policies."""
@classmethod
def get_name(cls):
return "NSX Policy"
@classmethod
def get_alias(cls):
return EXT_ALIAS
@classmethod
def get_description(cls):
return "NSX security policies."
@classmethod
def get_updated(cls):
return "2016-11-20T00:00:00-00:00"
@classmethod
def get_resources(cls):
"""Returns Ext Resources."""
plural_mappings = resource_helper.build_plural_mappings(
{}, RESOURCE_ATTRIBUTE_MAP)
member_actions = {}
return resource_helper.build_resource_info(plural_mappings,
RESOURCE_ATTRIBUTE_MAP,
None,
action_map=member_actions,
register_quota=True,
translate_name=True)
def get_extended_resources(self, version):
if version == "2.0":
return RESOURCE_ATTRIBUTE_MAP
else:
return {}
class NsxPolicyReadOnly(nexception.NotAuthorized):
message = _("NSX policies are read-only.")
class NsxPolicyPluginBase(object):
@abc.abstractmethod
def create_nsx_policy(self, context, nsx_policy):
raise NsxPolicyReadOnly()
@abc.abstractmethod
def update_nsx_policy(self, context, id, nsx_policy):
raise NsxPolicyReadOnly()
@abc.abstractmethod
def get_nsx_policy(self, context, id, fields=None):
pass
@abc.abstractmethod
def delete_nsx_policy(self, context, id):
raise NsxPolicyReadOnly()
@abc.abstractmethod
def get_nsx_policies(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
pass

View File

@ -20,6 +20,7 @@ import uuid
import netaddr
from neutron_lib.api import validators
from neutron_lib import constants
from neutron_lib.db import constants as db_const
from neutron_lib import exceptions as n_exc
from neutron_lib.plugins import directory
from oslo_config import cfg
@ -98,6 +99,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 nsxpolicy
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
@ -136,7 +138,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
securitygroups_db.SecurityGroupDbMixin,
extended_secgroup.ExtendedSecurityGroupPropertiesMixin,
vnic_index_db.VnicIndexDbMixin,
dns_db.DNSDbMixin):
dns_db.DNSDbMixin, nsxpolicy.NsxPolicyPluginBase):
supported_extension_aliases = ["agent",
"allowed-address-pairs",
@ -227,6 +229,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
self._use_nsx_policies = True
# enable the extension
self.supported_extension_aliases.append("security-group-policy")
self.supported_extension_aliases.append("nsx-policy")
self.sg_container_id = self._create_security_group_container()
self.default_section = self._create_cluster_default_fw_section()
@ -3119,9 +3122,11 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'group %s') % id)
raise n_exc.InvalidInput(error_message=msg)
# validate that the new policy exists
if new_policy and not self.nsx_v.vcns.validate_inventory(
new_policy):
# validate that the new policy exists (and not hidden) by using the
# plugin getter that raises an exception if it fails.
try:
new_policy = self.get_nsx_policy(context, new_policy)
except n_exc.ObjectNotFound:
msg = _('Policy %s was not found on the NSX') % new_policy
raise n_exc.InvalidInput(error_message=msg)
@ -3133,9 +3138,11 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# Use the NSX policy description as the description of this
# security group if the description was not set by the user
# and the security group is new or policy was updated
# if the nsx policy has not description - use its name
if new_policy and not security_group.get('description'):
security_group['description'] = (
self.nsx_sg_utils.get_nsx_policy_description(new_policy))
new_policy.get('description') or
new_policy.get('name'))[:db_const.DESCRIPTION_FIELD_SIZE]
else:
# must not have a policy:
if security_group.get(sg_policy.POLICY):
@ -3580,6 +3587,34 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
def get_default_az(self):
return self._availability_zones_data.get_default_availability_zone()
def _nsx_policy_is_hidden(self, policy):
for attrib in policy.get('extendedAttributes', []):
if (attrib['name'].lower() == 'ishidden' and
attrib['value'].lower() == 'true'):
return True
return False
def _nsx_policy_to_dict(self, policy):
return {'id': policy['objectId'],
'name': policy.get('name'),
'description': policy.get('description')}
def get_nsx_policy(self, context, id, fields=None):
policy = self.nsx_v.vcns.get_security_policy(id, return_xml=False)
if self._nsx_policy_is_hidden(policy):
raise n_exc.ObjectNotFound(id=id)
return self._nsx_policy_to_dict(policy)
def get_nsx_policies(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
policies = self.nsx_v.vcns.get_security_policies()
results = []
for policy in policies.get('policies', []):
if not self._nsx_policy_is_hidden(policy):
results.append(self._nsx_policy_to_dict(policy))
return results
# Register the callback
def _validate_network_has_subnet(resource, event, trigger, **kwargs):

View File

@ -15,7 +15,6 @@
import xml.etree.ElementTree as et
from neutron_lib.db import constants as db_const
from oslo_log import log as logging
from vmware_nsx.common import utils
@ -203,16 +202,3 @@ class NsxSecurityGroupUtils(object):
return self.nsxv_manager.vcns.update_security_policy(
policy_id, et.tostring(policy))
def get_nsx_policy_description(self, policy_id):
if not policy_id:
return
# Get the policy configuration
policy = self.nsxv_manager.vcns.get_security_policy(policy_id)
policy = utils.normalize_xml(policy)
# If no description - use the name instead
description = policy.find('description').text
if not description:
description = policy.find('name').text
# use only the allowed length
return description[:db_const.DESCRIPTION_FIELD_SIZE]

View File

@ -1001,10 +1001,17 @@ class Vcns(object):
'ipaddresses', ip_addr)
return self.do_request(HTTP_DELETE, uri)
def get_security_policy(self, policy_id):
# get the policy configuration as an xml string
def get_security_policy(self, policy_id, return_xml=True):
# get the policy configuration as an xml string / dictionary
uri = '%s/%s' % (SECURITY_POLICY_PREFIX, policy_id)
h, policy = self.do_request(HTTP_GET, uri, format='xml', decode=False)
if return_xml:
format = 'xml'
decode = False
else:
format = 'json'
decode = True
h, policy = self.do_request(HTTP_GET, uri, format=format,
decode=decode)
return policy
def update_security_policy(self, policy_id, request):
@ -1013,3 +1020,9 @@ class Vcns(object):
return self.do_request(HTTP_PUT, uri, request,
format='xml',
decode=False, encode=True)
def get_security_policies(self):
# get the policies configuration dictionary
uri = '%s/all' % (SECURITY_POLICY_PREFIX)
h, policies = self.do_request(HTTP_GET, uri, decode=True)
return policies

View File

@ -18,9 +18,12 @@ import webob.exc
from neutron.api.v2 import attributes as attr
from neutron import context
from neutron.tests.unit.api import test_extensions
from neutron.tests.unit.extensions import test_securitygroup
from neutron_lib import constants
from neutron_lib import exceptions as n_exc
from vmware_nsx.extensions import nsxpolicy
from vmware_nsx.extensions import securitygrouplogging as ext_logging
from vmware_nsx.extensions import securitygrouppolicy as ext_policy
from vmware_nsx.tests.unit.nsx_v import test_plugin
@ -89,10 +92,9 @@ class SecGroupPolicyExtensionTestCase(
self.assertEqual(400, res.status_int)
def test_secgroup_create_with_illegal_policy(self):
with mock.patch.object(fake_vcns.FakeVcns,
'validate_inventory',
return_value=False):
policy_id = 'bad-policy'
policy_id = 'bad-policy'
with mock.patch(PLUGIN_NAME + '.get_nsx_policy',
side_effect=n_exc.ObjectNotFound(id=policy_id)):
res = self._create_secgroup_with_policy(policy_id)
self.assertEqual(400, res.status_int)
@ -211,3 +213,41 @@ class SecGroupPolicyExtensionTestCaseWithRules(
self.assertEqual(
sg['security_group']['id'],
rule_data['security_group_rule']['security_group_id'])
class NsxPolExtensionManager(object):
def get_resources(self):
return nsxpolicy.Nsxpolicy.get_resources()
def get_actions(self):
return []
def get_request_extensions(self):
return []
class TestNsxPolicies(test_plugin.NsxVPluginV2TestCase):
def setUp(self, plugin=None):
super(TestNsxPolicies, self).setUp()
ext_mgr = NsxPolExtensionManager()
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
def test_get_policy(self):
id = 'policy-1'
req = self.new_show_request('nsx-policies', id)
res = self.deserialize(
self.fmt, req.get_response(self.ext_api)
)
policy = res['nsx_policy']
self.assertEqual(id, policy['id'])
def test_list_policies(self):
req = self.new_list_request('nsx-policies')
res = self.deserialize(
self.fmt, req.get_response(self.ext_api)
)
self.assertIn('nsx_policies', res)
# the fake_vcns api returns 3 policies
self.assertEqual(3, len(res['nsx_policies']))

View File

@ -1366,14 +1366,28 @@ class FakeVcns(object):
msg, 120054, 'core-services')
return self.return_helper(header, response)
def get_security_policy(self, policy_id):
response_text = (
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<securityPolicy><objectId>%s</objectId>"
"<name>pol1</name>"
"<description>dummy</description>"
"</securityPolicy>") % policy_id
return response_text
def get_security_policy(self, policy_id, return_xml=True):
name = 'pol1'
description = 'dummy'
if return_xml:
response_text = (
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<securityPolicy><objectId>%(id)s</objectId>"
"<name>%(name)s</name>"
"<description>%(desc)s</description>"
"</securityPolicy>") % {'id': policy_id, 'name': name,
'desc': description}
return response_text
else:
return {'objectId': policy_id,
'name': name,
'description': description}
def update_security_policy(self, policy_id, request):
pass
def get_security_policies(self):
policies = []
for id in ['policy-1', 'policy-2', 'policy-3']:
policies.append(self.get_security_policy(id, return_xml=False))
return {'policies': policies}