diff --git a/vmware_nsx/extensions/nsxpolicy.py b/vmware_nsx/extensions/nsxpolicy.py
new file mode 100644
index 0000000000..d0fed28af7
--- /dev/null
+++ b/vmware_nsx/extensions/nsxpolicy.py
@@ -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
diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py
index 2e6a6e093a..3765cd8bb1 100644
--- a/vmware_nsx/plugins/nsx_v/plugin.py
+++ b/vmware_nsx/plugins/nsx_v/plugin.py
@@ -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):
diff --git a/vmware_nsx/plugins/nsx_v/vshield/securitygroup_utils.py b/vmware_nsx/plugins/nsx_v/vshield/securitygroup_utils.py
index 63fd6a1955..894251fbd0 100644
--- a/vmware_nsx/plugins/nsx_v/vshield/securitygroup_utils.py
+++ b/vmware_nsx/plugins/nsx_v/vshield/securitygroup_utils.py
@@ -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]
diff --git a/vmware_nsx/plugins/nsx_v/vshield/vcns.py b/vmware_nsx/plugins/nsx_v/vshield/vcns.py
index 05196b8646..2bc2d8d4d2 100644
--- a/vmware_nsx/plugins/nsx_v/vshield/vcns.py
+++ b/vmware_nsx/plugins/nsx_v/vshield/vcns.py
@@ -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
diff --git a/vmware_nsx/tests/unit/extensions/test_security_group_policy.py b/vmware_nsx/tests/unit/extensions/test_security_group_policy.py
index 3f4f0c407e..8cb483bbb4 100644
--- a/vmware_nsx/tests/unit/extensions/test_security_group_policy.py
+++ b/vmware_nsx/tests/unit/extensions/test_security_group_policy.py
@@ -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']))
diff --git a/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py b/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py
index fddfa14c43..daf459e00f 100644
--- a/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py
+++ b/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py
@@ -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 = (
- ""
- "%s"
- "pol1"
- "dummy"
- "") % policy_id
- return response_text
+ def get_security_policy(self, policy_id, return_xml=True):
+ name = 'pol1'
+ description = 'dummy'
+ if return_xml:
+ response_text = (
+ ""
+ "%(id)s"
+ "%(name)s"
+ "%(desc)s"
+ "") % {'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}