Group Policy API-1: EP, EPG, L2 Policy, L3 Policy

(Patch series identifier: GP-API-1)
This is the first in a series of patches which implement Group Policy. It
includes the Resource Model and API for Endpoint, Endpoint Group, L2
Policy, and L3 Policy resources.
In the context of the larger Group Policy Model, the Endpoint Group resource
references Contracts, which will be introduced in a subsequent patch.
The DB and Plugin layers to back these resources will also be introduced in
subsequent patches.

DocImpact
Gerrit Spec: https://review.openstack.org/#/c/89469

Author:    Sumit Naiksatam <sumitnaiksatam@gmail.com>
Co-Authored-By:    Bob Kukura <kukura@noironetworks.com>
Co-Authored-By:    Stephen Wong <s3wong@midokura.com>
Co-Authored-By:    Mohammad Banikazemi <mb@us.ibm.com>
Co-Authored-By:    Mandeep Dhami <dhami@noironetworks.com>
Co-Authored-By:    Ivar Lazzaro <ivarlazzaro@gmail.com>

Change-Id: I4f4cc7e2a899b39947b784ec390c3df599cf01ae
This commit is contained in:
Ivar Lazzaro 2014-09-23 16:17:00 -07:00
parent af3cbdfc7f
commit 8ab2b5243b
10 changed files with 868 additions and 5 deletions

138
etc/policy.json Normal file
View File

@ -0,0 +1,138 @@
{
"context_is_admin": "role:admin",
"admin_or_owner": "rule:context_is_admin or tenant_id:%(tenant_id)s",
"admin_or_network_owner": "rule:context_is_admin or tenant_id:%(network:tenant_id)s",
"admin_only": "rule:context_is_admin",
"regular_user": "",
"shared": "field:networks:shared=True",
"shared_firewalls": "field:firewalls:shared=True",
"external": "field:networks:router:external=True",
"default": "rule:admin_or_owner",
"create_subnet": "rule:admin_or_network_owner",
"get_subnet": "rule:admin_or_owner or rule:shared",
"update_subnet": "rule:admin_or_network_owner",
"delete_subnet": "rule:admin_or_network_owner",
"create_network": "",
"get_network": "rule:admin_or_owner or rule:shared or rule:external",
"get_network:router:external": "rule:regular_user",
"get_network:segments": "rule:admin_only",
"get_network:provider:network_type": "rule:admin_only",
"get_network:provider:physical_network": "rule:admin_only",
"get_network:provider:segmentation_id": "rule:admin_only",
"get_network:queue_id": "rule:admin_only",
"create_network:shared": "rule:admin_only",
"create_network:router:external": "rule:admin_only",
"create_network:segments": "rule:admin_only",
"create_network:provider:network_type": "rule:admin_only",
"create_network:provider:physical_network": "rule:admin_only",
"create_network:provider:segmentation_id": "rule:admin_only",
"update_network": "rule:admin_or_owner",
"update_network:segments": "rule:admin_only",
"update_network:shared": "rule:admin_only",
"update_network:provider:network_type": "rule:admin_only",
"update_network:provider:physical_network": "rule:admin_only",
"update_network:provider:segmentation_id": "rule:admin_only",
"update_network:router:external": "rule:admin_only",
"delete_network": "rule:admin_or_owner",
"create_port": "",
"create_port:mac_address": "rule:admin_or_network_owner",
"create_port:fixed_ips": "rule:admin_or_network_owner",
"create_port:port_security_enabled": "rule:admin_or_network_owner",
"create_port:binding:host_id": "rule:admin_only",
"create_port:binding:profile": "rule:admin_only",
"create_port:mac_learning_enabled": "rule:admin_or_network_owner",
"get_port": "rule:admin_or_owner",
"get_port:queue_id": "rule:admin_only",
"get_port:binding:vif_type": "rule:admin_only",
"get_port:binding:vif_details": "rule:admin_only",
"get_port:binding:host_id": "rule:admin_only",
"get_port:binding:profile": "rule:admin_only",
"update_port": "rule:admin_or_owner",
"update_port:fixed_ips": "rule:admin_or_network_owner",
"update_port:port_security_enabled": "rule:admin_or_network_owner",
"update_port:binding:host_id": "rule:admin_only",
"update_port:binding:profile": "rule:admin_only",
"update_port:mac_learning_enabled": "rule:admin_or_network_owner",
"delete_port": "rule:admin_or_owner",
"get_router:ha": "rule:admin_only",
"create_router": "rule:regular_user",
"create_router:external_gateway_info:enable_snat": "rule:admin_only",
"create_router:distributed": "rule:admin_only",
"create_router:ha": "rule:admin_only",
"get_router": "rule:admin_or_owner",
"get_router:distributed": "rule:admin_only",
"update_router:external_gateway_info:enable_snat": "rule:admin_only",
"update_router:distributed": "rule:admin_only",
"update_router:ha": "rule:admin_only",
"delete_router": "rule:admin_or_owner",
"add_router_interface": "rule:admin_or_owner",
"remove_router_interface": "rule:admin_or_owner",
"create_firewall": "",
"get_firewall": "rule:admin_or_owner",
"create_firewall:shared": "rule:admin_only",
"get_firewall:shared": "rule:admin_only",
"update_firewall": "rule:admin_or_owner",
"update_firewall:shared": "rule:admin_only",
"delete_firewall": "rule:admin_or_owner",
"create_firewall_policy": "",
"get_firewall_policy": "rule:admin_or_owner or rule:shared_firewalls",
"create_firewall_policy:shared": "rule:admin_or_owner",
"update_firewall_policy": "rule:admin_or_owner",
"delete_firewall_policy": "rule:admin_or_owner",
"create_firewall_rule": "",
"get_firewall_rule": "rule:admin_or_owner or rule:shared_firewalls",
"update_firewall_rule": "rule:admin_or_owner",
"delete_firewall_rule": "rule:admin_or_owner",
"create_qos_queue": "rule:admin_only",
"get_qos_queue": "rule:admin_only",
"update_agent": "rule:admin_only",
"delete_agent": "rule:admin_only",
"get_agent": "rule:admin_only",
"create_dhcp-network": "rule:admin_only",
"delete_dhcp-network": "rule:admin_only",
"get_dhcp-networks": "rule:admin_only",
"create_l3-router": "rule:admin_only",
"delete_l3-router": "rule:admin_only",
"get_l3-routers": "rule:admin_only",
"get_dhcp-agents": "rule:admin_only",
"get_l3-agents": "rule:admin_only",
"get_loadbalancer-agent": "rule:admin_only",
"get_loadbalancer-pools": "rule:admin_only",
"create_floatingip": "rule:regular_user",
"update_floatingip": "rule:admin_or_owner",
"delete_floatingip": "rule:admin_or_owner",
"get_floatingip": "rule:admin_or_owner",
"create_network_profile": "rule:admin_only",
"update_network_profile": "rule:admin_only",
"delete_network_profile": "rule:admin_only",
"get_network_profiles": "",
"get_network_profile": "",
"update_policy_profiles": "rule:admin_only",
"get_policy_profiles": "",
"get_policy_profile": "",
"create_metering_label": "rule:admin_only",
"delete_metering_label": "rule:admin_only",
"get_metering_label": "rule:admin_only",
"create_metering_label_rule": "rule:admin_only",
"delete_metering_label_rule": "rule:admin_only",
"get_metering_label_rule": "rule:admin_only",
"get_service_provider": "rule:regular_user",
"get_lsn": "rule:admin_only",
"create_lsn": "rule:admin_only"
}

0
gbp/neutron/__init__.py Normal file
View File

View File

View File

@ -0,0 +1,285 @@
# 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
import six
from neutron.api import extensions
from neutron.api.v2 import attributes as attr
from neutron.api.v2 import resource_helper
from neutron.plugins.common import constants
from neutron.services import service_base
import gbp.neutron.extensions
# The code below is a monkey patch of key Neutron's modules. This is needed for
# the GBP service to be loaded correctly. GBP extensions' path is added
# to Neutron's so that it's found at extension scanning time.
extensions.append_api_extensions_path(gbp.neutron.extensions.__path__)
constants.GROUP_POLICY = "GROUP_POLICY"
constants.COMMON_PREFIXES["GROUP_POLICY"] = "/grouppolicy"
ENDPOINTS = 'endpoints'
ENDPOINT_GROUPS = 'endpoint_groups'
L2_POLICIES = 'l2_policies'
L3_POLICIES = 'l3_policies'
RESOURCE_ATTRIBUTE_MAP = {
ENDPOINTS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None}, 'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None}, 'default': '',
'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'endpoint_group_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'required': True, 'is_visible': True},
},
ENDPOINT_GROUPS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None}, 'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'endpoints': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
'l2_policy_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'default': None, 'is_visible': True},
'provided_contracts': {'allow_post': True, 'allow_put': True,
'validate': {'type:dict_or_none': None},
'convert_to': attr.convert_none_to_empty_dict,
'default': None, 'is_visible': True},
'consumed_contracts': {'allow_post': True, 'allow_put': True,
'validate': {'type:dict_or_none': None},
'convert_to': attr.convert_none_to_empty_dict,
'default': None, 'is_visible': True},
},
L2_POLICIES: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None}, 'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'endpoint_groups': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
'l3_policy_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'default': None, 'is_visible': True,
'required': True},
# TODO(Sumit): uncomment when supported in data path
# 'allow_broadcast': {'allow_post': True, 'allow_put': True,
# 'default': True, 'is_visible': True,
# 'convert_to': attr.convert_to_boolean,
# 'required': False},
},
L3_POLICIES: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None}, 'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'ip_version': {'allow_post': True, 'allow_put': False,
'convert_to': attr.convert_to_int,
'validate': {'type:values': [4, 6]},
'default': 4, 'is_visible': True},
'ip_pool': {'allow_post': True, 'allow_put': False,
'validate': {'type:subnet': None},
'default': '10.0.0.0/8', 'is_visible': True},
'subnet_prefix_length': {'allow_post': True, 'allow_put': True,
'convert_to': attr.convert_to_int,
# for ipv4 legal values are 2 to 30
# for ipv6 legal values are 2 to 127
'default': 24, 'is_visible': True},
'l2_policies': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
},
}
class Group_policy(extensions.ExtensionDescriptor):
@classmethod
def get_name(cls):
return "Group Policy Abstraction"
@classmethod
def get_alias(cls):
return "group-policy"
@classmethod
def get_description(cls):
return "Extension for Group Policy Abstraction"
@classmethod
def get_namespace(cls):
return "http://wiki.openstack.org/neutron/gp/v2.0/"
@classmethod
def get_updated(cls):
return "2014-03-03T12:00:00-00:00"
@classmethod
def get_resources(cls):
special_mappings = {'l2_policies': 'l2_policy',
'l3_policies': 'l3_policy'}
plural_mappings = resource_helper.build_plural_mappings(
special_mappings, RESOURCE_ATTRIBUTE_MAP)
attr.PLURALS.update(plural_mappings)
return resource_helper.build_resource_info(plural_mappings,
RESOURCE_ATTRIBUTE_MAP,
constants.GROUP_POLICY)
@classmethod
def get_plugin_interface(cls):
return GroupPolicyPluginBase
def update_attributes_map(self, attributes):
super(Group_policy, self).update_attributes_map(
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
def get_extended_resources(self, version):
if version == "2.0":
return RESOURCE_ATTRIBUTE_MAP
else:
return {}
@six.add_metaclass(abc.ABCMeta)
class GroupPolicyPluginBase(service_base.ServicePluginBase):
def get_plugin_name(self):
return constants.GROUP_POLICY
def get_plugin_type(self):
return constants.GROUP_POLICY
def get_plugin_description(self):
return 'Group Policy plugin'
@abc.abstractmethod
def get_endpoints(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_endpoint(self, context, endpoint_id, fields=None):
pass
@abc.abstractmethod
def create_endpoint(self, context, endpoint):
pass
@abc.abstractmethod
def update_endpoint(self, context, endpoint_id, endpoint):
pass
@abc.abstractmethod
def delete_endpoint(self, context, endpoint_id):
pass
@abc.abstractmethod
def get_endpoint_groups(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_endpoint_group(self, context, endpoint_group_id, fields=None):
pass
@abc.abstractmethod
def create_endpoint_group(self, context, endpoint_group):
pass
@abc.abstractmethod
def update_endpoint_group(self, context, endpoint_group_id,
endpoint_group):
pass
@abc.abstractmethod
def delete_endpoint_group(self, context, endpoint_group_id):
pass
@abc.abstractmethod
def get_l2_policies(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_l2_policy(self, context, l2_policy_id, fields=None):
pass
@abc.abstractmethod
def create_l2_policy(self, context, l2_policy):
pass
@abc.abstractmethod
def update_l2_policy(self, context, l2_policy_id, l2_policy):
pass
@abc.abstractmethod
def delete_l2_policy(self, context, l2_policy_id):
pass
@abc.abstractmethod
def get_l3_policies(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_l3_policy(self, context, l3_policy_id, fields=None):
pass
@abc.abstractmethod
def create_l3_policy(self, context, l3_policy):
pass
@abc.abstractmethod
def update_l3_policy(self, context, l3_policy_id, l3_policy):
pass
@abc.abstractmethod
def delete_l3_policy(self, context, l3_policy_id):
pass

View File

View File

View File

@ -0,0 +1,437 @@
# 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 copy
import mock
from neutron.openstack.common import uuidutils
from neutron.plugins.common import constants
from neutron.tests.unit import test_api_v2
from neutron.tests.unit import test_api_v2_extension
from webob import exc
from gbp.neutron.extensions import group_policy as gp
_uuid = uuidutils.generate_uuid
_get_path = test_api_v2._get_path
GP_PLUGIN_BASE_NAME = (
gp.GroupPolicyPluginBase.__module__ + '.' +
gp.GroupPolicyPluginBase.__name__)
GROUPPOLICY_URI = 'grouppolicy'
ENDPOINTS_URI = GROUPPOLICY_URI + '/' + 'endpoints'
ENDPOINT_GROUPS_URI = GROUPPOLICY_URI + '/' + 'endpoint_groups'
L2_POLICIES_URI = GROUPPOLICY_URI + '/' + 'l2_policies'
L3_POLICIES_URI = GROUPPOLICY_URI + '/' + 'l3_policies'
class GroupPolicyExtensionTestCase(test_api_v2_extension.ExtensionTestCase):
fmt = 'json'
def setUp(self):
super(GroupPolicyExtensionTestCase, self).setUp()
plural_mappings = {'l2_policy': 'l2_policies',
'l3_policy': 'l3_policies'}
self._setUpExtension(
GP_PLUGIN_BASE_NAME, constants.GROUP_POLICY,
gp.RESOURCE_ATTRIBUTE_MAP, gp.Group_policy, GROUPPOLICY_URI,
plural_mappings=plural_mappings)
self.instance = self.plugin.return_value
def _test_create_endpoint(self, data, expected_value, default_data=None):
if not default_data:
default_data = data
self.instance.create_endpoint.return_value = expected_value
res = self.api.post(_get_path(ENDPOINTS_URI, fmt=self.fmt),
self.serialize(data),
content_type='application/%s' % self.fmt)
self.instance.create_endpoint.assert_called_once_with(
mock.ANY, endpoint=default_data)
self.assertEqual(exc.HTTPCreated.code, res.status_int)
res = self.deserialize(res)
self.assertIn('endpoint', res)
self.assertEqual(expected_value, res['endpoint'])
def _get_create_endpoint_default_attrs(self):
return {'name': '', 'description': ''}
def _get_create_endpoint_attrs(self):
return {'name': 'ep1', 'endpoint_group_id': _uuid(),
'tenant_id': _uuid(), 'description': 'test endpoint'}
def _get_update_endpoint_attrs(self):
return {'name': 'new_name'}
def test_create_endpoint_with_defaults(self):
endpoint_id = _uuid()
data = {'endpoint': {'endpoint_group_id': _uuid(),
'tenant_id': _uuid()}}
default_attrs = self._get_create_endpoint_default_attrs()
default_data = copy.copy(data)
default_data['endpoint'].update(default_attrs)
expected_value = dict(default_data['endpoint'])
expected_value['id'] = endpoint_id
self._test_create_endpoint(data, expected_value, default_data)
def test_create_endpoint(self):
endpoint_id = _uuid()
data = {'endpoint': self._get_create_endpoint_attrs()}
expected_value = dict(data['endpoint'])
expected_value['id'] = endpoint_id
self._test_create_endpoint(data, expected_value)
def test_list_endpoints(self):
endpoint_id = _uuid()
expected_value = [{'tenant_id': _uuid(), 'id': endpoint_id}]
self.instance.get_endpoints.return_value = expected_value
res = self.api.get(_get_path(ENDPOINTS_URI, fmt=self.fmt))
self.instance.get_endpoints.assert_called_once_with(
mock.ANY, fields=mock.ANY, filters=mock.ANY)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('endpoints', res)
self.assertEqual(expected_value, res['endpoints'])
def test_get_endpoint(self):
endpoint_id = _uuid()
expected_value = {'tenant_id': _uuid(), 'id': endpoint_id}
self.instance.get_endpoint.return_value = expected_value
res = self.api.get(_get_path(ENDPOINTS_URI, id=endpoint_id,
fmt=self.fmt))
self.instance.get_endpoint.assert_called_once_with(
mock.ANY, endpoint_id, fields=mock.ANY)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('endpoint', res)
self.assertEqual(expected_value, res['endpoint'])
def test_update_endpoint(self):
endpoint_id = _uuid()
update_data = {'endpoint': self._get_update_endpoint_attrs()}
expected_value = {'tenant_id': _uuid(), 'id': endpoint_id}
self.instance.update_endpoint.return_value = expected_value
res = self.api.put(_get_path(ENDPOINTS_URI, id=endpoint_id,
fmt=self.fmt),
self.serialize(update_data))
self.instance.update_endpoint.assert_called_once_with(
mock.ANY, endpoint_id, endpoint=update_data)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('endpoint', res)
self.assertEqual(expected_value, res['endpoint'])
def test_delete_endpoint(self):
self._test_entity_delete('endpoint')
def _test_create_endpoint_group(self, data, expected_value,
default_data=None):
if not default_data:
default_data = data
self.instance.create_endpoint_group.return_value = expected_value
res = self.api.post(_get_path(ENDPOINT_GROUPS_URI, fmt=self.fmt),
self.serialize(data),
content_type='application/%s' % self.fmt)
self.instance.create_endpoint_group.assert_called_once_with(
mock.ANY, endpoint_group=default_data)
self.assertEqual(exc.HTTPCreated.code, res.status_int)
res = self.deserialize(res)
self.assertIn('endpoint_group', res)
self.assertEqual(expected_value, res['endpoint_group'])
def _get_create_endpoint_group_default_attrs(self):
return {'name': '', 'description': '', 'l2_policy_id': None,
'provided_contracts': {}, 'consumed_contracts': {}}
def _get_create_endpoint_group_attrs(self):
return {'name': 'epg1', 'tenant_id': _uuid(),
'description': 'test endpoint group', 'l2_policy_id': _uuid(),
'provided_contracts': {_uuid(): None},
'consumed_contracts': {_uuid(): None}}
def _get_update_endpoint_group_attrs(self):
return {'name': 'new_name'}
def test_create_endpoint_group_with_defaults(self):
endpoint_group_id = _uuid()
data = {'endpoint_group': {'tenant_id': _uuid()}}
default_attrs = self._get_create_endpoint_group_default_attrs()
default_data = copy.copy(data)
default_data['endpoint_group'].update(default_attrs)
expected_value = copy.deepcopy(default_data['endpoint_group'])
expected_value['id'] = endpoint_group_id
self._test_create_endpoint_group(data, expected_value, default_data)
def test_create_endpoint_group(self):
endpoint_group_id = _uuid()
data = {'endpoint_group': self._get_create_endpoint_group_attrs()}
expected_value = copy.deepcopy(data['endpoint_group'])
expected_value['id'] = endpoint_group_id
self._test_create_endpoint_group(data, expected_value)
def test_list_endpoint_groups(self):
endpoint_group_id = _uuid()
expected_value = [{'tenant_id': _uuid(), 'id': endpoint_group_id}]
self.instance.get_endpoint_groups.return_value = expected_value
res = self.api.get(_get_path(ENDPOINT_GROUPS_URI, fmt=self.fmt))
self.instance.get_endpoint_groups.assert_called_once_with(
mock.ANY, fields=mock.ANY, filters=mock.ANY)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('endpoint_groups', res)
self.assertEqual(expected_value, res['endpoint_groups'])
def test_get_endpoint_group(self):
endpoint_group_id = _uuid()
expected_value = {'tenant_id': _uuid(), 'id': endpoint_group_id}
self.instance.get_endpoint_group.return_value = expected_value
res = self.api.get(_get_path(ENDPOINT_GROUPS_URI, id=endpoint_group_id,
fmt=self.fmt))
self.instance.get_endpoint_group.assert_called_once_with(
mock.ANY, endpoint_group_id, fields=mock.ANY)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('endpoint_group', res)
self.assertEqual(expected_value, res['endpoint_group'])
def test_update_endpoint_group(self):
endpoint_group_id = _uuid()
update_data = {'endpoint_group':
self._get_update_endpoint_group_attrs()}
expected_value = {'tenant_id': _uuid(), 'id': endpoint_group_id}
self.instance.update_endpoint_group.return_value = expected_value
res = self.api.put(_get_path(ENDPOINT_GROUPS_URI,
id=endpoint_group_id, fmt=self.fmt),
self.serialize(update_data))
self.instance.update_endpoint_group.assert_called_once_with(
mock.ANY, endpoint_group_id, endpoint_group=update_data)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('endpoint_group', res)
self.assertEqual(expected_value, res['endpoint_group'])
def test_delete_endpoint_group(self):
self._test_entity_delete('endpoint_group')
def _test_create_l2_policy(self, data, expected_value, default_data=None):
if not default_data:
default_data = data
self.instance.create_l2_policy.return_value = expected_value
res = self.api.post(_get_path(L2_POLICIES_URI, fmt=self.fmt),
self.serialize(data),
content_type='application/%s' % self.fmt)
self.instance.create_l2_policy.assert_called_once_with(
mock.ANY, l2_policy=default_data)
self.assertEqual(exc.HTTPCreated.code, res.status_int)
res = self.deserialize(res)
self.assertIn('l2_policy', res)
self.assertEqual(expected_value, res['l2_policy'])
def _get_create_l2_policy_default_attrs(self):
return {'name': '', 'description': ''}
def _get_create_l2_policy_attrs(self):
return {'name': 'l2p1', 'tenant_id': _uuid(),
'description': 'test L2 policy', 'l3_policy_id': _uuid()}
def _get_update_l2_policy_attrs(self):
return {'name': 'new_name'}
def test_create_l2_policy_with_defaults(self):
l2_policy_id = _uuid()
data = {'l2_policy': {'tenant_id': _uuid(), 'l3_policy_id': _uuid()}}
default_attrs = self._get_create_l2_policy_default_attrs()
default_data = copy.copy(data)
default_data['l2_policy'].update(default_attrs)
expected_value = dict(default_data['l2_policy'])
expected_value['id'] = l2_policy_id
self._test_create_l2_policy(data, expected_value, default_data)
def test_create_l2_policy(self):
l2_policy_id = _uuid()
data = {'l2_policy': self._get_create_l2_policy_attrs()}
expected_value = dict(data['l2_policy'])
expected_value['id'] = l2_policy_id
self._test_create_l2_policy(data, expected_value)
def test_list_l2_policies(self):
l2_policy_id = _uuid()
expected_value = [{'tenant_id': _uuid(), 'id': l2_policy_id}]
self.instance.get_l2_policies.return_value = expected_value
res = self.api.get(_get_path(L2_POLICIES_URI, fmt=self.fmt))
self.instance.get_l2_policies.assert_called_once_with(
mock.ANY, fields=mock.ANY, filters=mock.ANY)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('l2_policies', res)
self.assertEqual(expected_value, res['l2_policies'])
def test_get_l2_policy(self):
l2_policy_id = _uuid()
expected_value = {'tenant_id': _uuid(), 'id': l2_policy_id}
self.instance.get_l2_policy.return_value = expected_value
res = self.api.get(_get_path(L2_POLICIES_URI, id=l2_policy_id,
fmt=self.fmt))
self.instance.get_l2_policy.assert_called_once_with(
mock.ANY, l2_policy_id, fields=mock.ANY)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('l2_policy', res)
self.assertEqual(expected_value, res['l2_policy'])
def test_update_l2_policy(self):
l2_policy_id = _uuid()
update_data = {'l2_policy': self._get_update_l2_policy_attrs()}
expected_value = {'tenant_id': _uuid(), 'id': l2_policy_id}
self.instance.update_l2_policy.return_value = expected_value
res = self.api.put(_get_path(L2_POLICIES_URI, id=l2_policy_id,
fmt=self.fmt),
self.serialize(update_data))
self.instance.update_l2_policy.assert_called_once_with(
mock.ANY, l2_policy_id, l2_policy=update_data)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('l2_policy', res)
self.assertEqual(expected_value, res['l2_policy'])
def test_delete_l2_policy(self):
self._test_entity_delete('l2_policy')
def _test_create_l3_policy(self, data, expected_value, default_data=None):
if not default_data:
default_data = data
self.instance.create_l3_policy.return_value = expected_value
res = self.api.post(_get_path(L3_POLICIES_URI, fmt=self.fmt),
self.serialize(data),
content_type='application/%s' % self.fmt)
self.instance.create_l3_policy.assert_called_once_with(
mock.ANY, l3_policy=default_data)
self.assertEqual(exc.HTTPCreated.code, res.status_int)
res = self.deserialize(res)
self.assertIn('l3_policy', res)
self.assertEqual(res['l3_policy'], expected_value)
def _get_create_l3_policy_default_attrs(self):
return {'name': '', 'description': '', 'ip_version': 4,
'ip_pool': '10.0.0.0/8', 'subnet_prefix_length': 24}
def _get_create_l3_policy_attrs(self):
return {'name': 'l3p1', 'tenant_id': _uuid(),
'description': 'test L3 policy', 'ip_version': 6,
'ip_pool': 'fd01:2345:6789::/48',
'subnet_prefix_length': 64}
def _get_update_l3_policy_attrs(self):
return {'name': 'new_name'}
def test_create_l3_policy_with_defaults(self):
l3_policy_id = _uuid()
data = {'l3_policy': {'tenant_id': _uuid()}}
default_attrs = self._get_create_l3_policy_default_attrs()
default_data = copy.copy(data)
default_data['l3_policy'].update(default_attrs)
expected_value = dict(default_data['l3_policy'])
expected_value['id'] = l3_policy_id
self._test_create_l3_policy(data, expected_value, default_data)
def test_create_l3_policy(self):
l3_policy_id = _uuid()
data = {'l3_policy': self._get_create_l3_policy_attrs()}
expected_value = dict(data['l3_policy'])
expected_value.update({'id': l3_policy_id})
self._test_create_l3_policy(data, expected_value)
def test_list_l3_policies(self):
l3_policy_id = _uuid()
expected_value = [{'tenant_id': _uuid(), 'id': l3_policy_id}]
self.instance.get_l3_policies.return_value = expected_value
res = self.api.get(_get_path(L3_POLICIES_URI, fmt=self.fmt))
self.instance.get_l3_policies.assert_called_once_with(
mock.ANY, fields=mock.ANY, filters=mock.ANY)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('l3_policies', res)
self.assertEqual(expected_value, res['l3_policies'])
def test_get_l3_policy(self):
l3_policy_id = _uuid()
expected_value = {'tenant_id': _uuid(), 'id': l3_policy_id}
self.instance.get_l3_policy.return_value = expected_value
res = self.api.get(_get_path(L3_POLICIES_URI, id=l3_policy_id,
fmt=self.fmt))
self.instance.get_l3_policy.assert_called_once_with(
mock.ANY, l3_policy_id, fields=mock.ANY)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('l3_policy', res)
self.assertEqual(expected_value, res['l3_policy'])
def test_update_l3_policy(self):
l3_policy_id = _uuid()
update_data = {'l3_policy': self._get_update_l3_policy_attrs()}
expected_value = {'tenant_id': _uuid(), 'id': l3_policy_id}
self.instance.update_l3_policy.return_value = expected_value
res = self.api.put(_get_path(L3_POLICIES_URI, id=l3_policy_id,
fmt=self.fmt),
self.serialize(update_data))
self.instance.update_l3_policy.assert_called_once_with(
mock.ANY, l3_policy_id, l3_policy=update_data)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn('l3_policy', res)
self.assertEqual(expected_value, res['l3_policy'])
def test_delete_l3_policy(self):
self._test_entity_delete('l3_policy')

View File

@ -1,6 +1,3 @@
# Copyright 2010-2011 OpenStack Foundation
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
#
# 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

View File

@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# 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

View File

@ -4,7 +4,15 @@
hacking>=0.9.2,<0.10
cliff>=1.6.0
coverage>=3.6
discover
fixtures>=0.3.14
mock>=1.0
python-subunit>=0.0.18
ordereddict
requests-mock>=0.4.0 # Apache-2.0
sphinx>=1.1.2,!=1.2.0,<1.3
oslosphinx>=2.2.0.0a2
testrepository>=0.0.18