Merge "NSX-V add nsx-policies extension"
This commit is contained in:
commit
f9c790b423
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()
|
||||
@ -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):
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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']))
|
||||
|
@ -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}
|
||||
|
Loading…
x
Reference in New Issue
Block a user