NSX-V add nsx-policies extension
This extension will list/show nsx security policies, that can be used in the security groups for the Admin policy feature In addition, we are using this new api for policy validation in the security group create/update Change-Id: I66f75ae24c814c0d644f1fc4c6b9c52b24ddc77c
This commit is contained in:
parent
22b6a3f0a0
commit
c9d44f5031
107
vmware_nsx/extensions/nsxpolicy.py
Normal file
107
vmware_nsx/extensions/nsxpolicy.py
Normal 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
|
@ -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()
|
||||
@ -3115,9 +3118,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)
|
||||
|
||||
@ -3129,9 +3134,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):
|
||||
@ -3576,6 +3583,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):
|
||||
|
@ -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]
|
||||
|
@ -989,10 +989,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):
|
||||
@ -1001,3 +1008,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
|
||||
|
@ -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']))
|
||||
|
@ -1342,14 +1342,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}
|
||||
|
Loading…
Reference in New Issue
Block a user