From d237335a5a23b09abb10bd9a11b64ae07d51b974 Mon Sep 17 00:00:00 2001 From: Daryl Walleck Date: Thu, 14 Nov 2013 00:44:50 -0600 Subject: [PATCH] Additions for flavor access requests * Added functions to flavors client for flavor access requests * Added flavor access models * Added metatests for flavor access models Change-Id: Id54afed0b39050b8cec6cedae57d86a04393e4fa --- cloudcafe/compute/flavors_api/client.py | 29 +++++ .../flavors_api/models/flavor_access.py | 116 ++++++++++++++++++ .../flavors/models/test_flavor_access.py | 66 ++++++++++ 3 files changed, 211 insertions(+) create mode 100644 cloudcafe/compute/flavors_api/models/flavor_access.py create mode 100644 metatests/cloudcafe/compute/flavors/models/test_flavor_access.py diff --git a/cloudcafe/compute/flavors_api/client.py b/cloudcafe/compute/flavors_api/client.py index 4dd2c35d..4bd2520f 100644 --- a/cloudcafe/compute/flavors_api/client.py +++ b/cloudcafe/compute/flavors_api/client.py @@ -21,6 +21,8 @@ from cloudcafe.compute.flavors_api.models.flavor import \ Flavor, CreateFlavor, Flavors, FlavorMins from cloudcafe.compute.flavors_api.models.flavor_extra_specs import \ FlavorExtraSpecs +from cloudcafe.compute.flavors_api.models.flavor_access import ( + AddTenantFlavorAccess, FlavorAccessList, RemoveTenantFlavorAccess) class FlavorsClient(AutoMarshallingRestClient): @@ -151,3 +153,30 @@ class FlavorsClient(AutoMarshallingRestClient): response_entity_type=FlavorExtraSpecs, requestslib_kwargs=requestslib_kwargs) return response + + def list_flavor_access(self, flavor_id, requestslib_kwargs=None): + url = '{base_url}/flavors/{flavor_id}/os-flavor-access'.format( + base_url=self.url, flavor_id=flavor_id) + response = self.request( + 'GET', url, response_entity_type=FlavorAccessList, + requestslib_kwargs=requestslib_kwargs) + return response + + def add_tenant_access(self, tenant, flavor_id, requestslib_kwargs=None): + request = AddTenantFlavorAccess(tenant=tenant) + url = '{base_url}/flavors/{flavor_id}/action'.format( + base_url=self.url, flavor_id=flavor_id) + response = self.request( + 'POST', url, response_entity_type=FlavorAccessList, + request_entity=request, requestslib_kwargs=requestslib_kwargs) + return response + + def remove_tenant_access(self, tenant, flavor_id, + requestslib_kwargs=None): + request = RemoveTenantFlavorAccess(tenant=tenant) + url = '{base_url}/flavors/{flavor_id}/action'.format( + base_url=self.url, flavor_id=flavor_id) + response = self.request( + 'DELETE', url, response_entity_type=FlavorAccessList, + request_entity=request, requestslib_kwargs=requestslib_kwargs) + return response diff --git a/cloudcafe/compute/flavors_api/models/flavor_access.py b/cloudcafe/compute/flavors_api/models/flavor_access.py new file mode 100644 index 00000000..1bf49e8c --- /dev/null +++ b/cloudcafe/compute/flavors_api/models/flavor_access.py @@ -0,0 +1,116 @@ +""" +Copyright 2013 Rackspace + +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 json +import xml.etree.ElementTree as ET + +from cafe.engine.models.base import AutoMarshallingModel +from cafe.engine.models.base import AutoMarshallingListModel +from cloudcafe.compute.common.constants import Constants +from cloudcafe.compute.common.equality_tools import EqualityTools + + +class FlavorAccess(AutoMarshallingModel): + + def __init__(self, flavor_id=None, tenant_id=None): + super(FlavorAccess, self).__init__() + self.flavor_id = flavor_id + self.tenant_id = tenant_id + + @classmethod + def _json_to_obj(cls, json_dict): + access = FlavorAccess(flavor_id=json_dict.get('flavor_id'), + tenant_id=json_dict.get('tenant_id')) + return access + + @classmethod + def _xml_to_obj(cls, element): + access_dict = element.attrib + access = FlavorAccess(flavor_id=access_dict.get('flavor_id'), + tenant_id=access_dict.get('tenant_id')) + return access + + def __eq__(self, other): + return EqualityTools.are_objects_equal(self, other) + + def __ne__(self, other): + return not self == other + + +class FlavorAccessList(AutoMarshallingListModel): + + @classmethod + def _json_to_obj(cls, serialized_str): + json_dict = json.loads(serialized_str) + return cls._list_to_obj(json_dict.get('flavor_access')) + + @classmethod + def _list_to_obj(cls, access_dict_list): + access_list = FlavorAccessList() + for flavor_dict in access_dict_list: + access = FlavorAccess._json_to_obj(flavor_dict) + access_list.append(access) + return access_list + + @classmethod + def _xml_to_obj(cls, serialized_str): + element = ET.fromstring(serialized_str) + return cls._xml_list_to_obj(element.findall('access')) + + @classmethod + def _xml_list_to_obj(cls, xml_list): + flavors = FlavorAccessList() + for ele in xml_list: + flavors.append(FlavorAccess._xml_to_obj(ele)) + return flavors + + +class AddTenantFlavorAccess(AutoMarshallingModel): + + def __init__(self, tenant=None): + super(AddTenantFlavorAccess, self).__init__() + self.tenant = tenant + + def _obj_to_json(self): + ret = {'addTenantAccess': {'tenant': self.tenant}} + return json.dumps(ret) + + def _obj_to_xml(self): + xml = Constants.XML_HEADER + element = ET.Element('addTenantAccess') + element.set('xmlns', Constants.XML_API_NAMESPACE) + element.set('tenant', self.tenant) + xml += ET.tostring(element) + return xml + + +class RemoveTenantFlavorAccess(AutoMarshallingModel): + + def __init__(self, tenant=None): + super(RemoveTenantFlavorAccess, self).__init__() + self.tenant = tenant + + def _obj_to_json(self): + ret = {'removeTenantAccess': {'tenant': self.tenant}} + return json.dumps(ret) + + def _obj_to_xml(self): + xml = Constants.XML_HEADER + element = ET.Element('removeTenantAccess') + element.set('xmlns', Constants.XML_API_NAMESPACE) + element.set('tenant', self.tenant) + xml += ET.tostring(element) + return xml diff --git a/metatests/cloudcafe/compute/flavors/models/test_flavor_access.py b/metatests/cloudcafe/compute/flavors/models/test_flavor_access.py new file mode 100644 index 00000000..b644d122 --- /dev/null +++ b/metatests/cloudcafe/compute/flavors/models/test_flavor_access.py @@ -0,0 +1,66 @@ +""" +Copyright 2013 Rackspace + +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 unittest2 as unittest + +from cloudcafe.compute.flavors_api.models.flavor_access \ + import FlavorAccess, FlavorAccessList + + +class FlavorAccessModelTest(object): + + def test_flavor_access_entries(self): + first_entry = FlavorAccess(flavor_id='42', tenant_id='user1') + second_entry = FlavorAccess(flavor_id='84', tenant_id='user2') + + self.assertIn(first_entry, self.access_list) + self.assertIn(second_entry, self.access_list) + + +class FlavorAccessXMLTest(unittest.TestCase, FlavorAccessModelTest): + + @classmethod + def setUpClass(cls): + cls.access_xml = \ + """ + + + + + """ + cls.access_list = FlavorAccessList.deserialize(cls.access_xml, 'xml') + + +class FlavorAccessJSONTest(unittest.TestCase, FlavorAccessModelTest): + + @classmethod + def setUpClass(cls): + cls.access_json = \ + """ + { + "flavor_access": [ + { + "flavor_id": "42", + "tenant_id": "user1" + }, + { + "flavor_id": "84", + "tenant_id": "user2" + } + ] + } + """ + cls.access_list = FlavorAccessList.deserialize(cls.access_json, 'json')