Model, Client, and Config for querying deployed Neutron Extensions

* Implemented Response Model (JSON only)
* Implemented Client - Supports single API call
* Implemented Config - Specific extensions required for test execution
* Added section and options to reference.json.config template
* Metatests for validating model logic

Change-Id: I90b87d6092bbbcc928901fac71c9690ef26535a4
This commit is contained in:
Christopher Hunt 2015-09-14 10:37:23 -05:00
parent 0062307d8e
commit 2bce7ad403
5 changed files with 224 additions and 0 deletions

View File

@ -0,0 +1,70 @@
"""
Copyright 2015 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.
"""
from cafe.engine.http.client import AutoMarshallingHTTPClient
from cloudcafe.networking.networks.extensions.model import NeutronExtensions
content_type_fmt = '{content_type}/{content_subtype}'
class ExtensionsClient(AutoMarshallingHTTPClient):
"""
This client will retrieve a list of Neutron extensions currently
deployed in the target environment.
"""
def __init__(self, url, auth_token, serialize_format=None,
deserialize_format=None, tenant_id=None):
"""
@summary: Rackspace Neutron API extension client
@param url: Base URL for the networks service
@type url: string
@param auth_token: Auth token to be used for all requests
@type auth_token: string
@param serialize_format: Format for serializing requests
@type serialize_format: string
@param deserialize_format: Format for de-serializing responses
@type deserialize_format: string
@param tenant_id: optional tenant id to be included in the
header if given
@type tenant_id: string
"""
super(ExtensionsClient, self).__init__(serialize_format,
deserialize_format)
self.auth_token = auth_token
self.default_headers['X-Auth-Token'] = auth_token
self.default_headers['Content-Type'] = content_type_fmt.format(
content_type='application',
content_subtype=self.serialize_format)
self.default_headers['Accept'] = content_type_fmt.format(
content_type='application',
content_subtype=self.deserialize_format)
if tenant_id:
self.default_headers['X-Auth-Project-Id'] = tenant_id
self.url = url
self.extensions_url = '{url}/extensions'.format(url=self.url)
def get_deployed_extensions(self, requestslib_kwargs=None):
"""
Get the list of extensions deployed in the target environment.
:param requestslib_kwargs:
:return:
"""
return self.request('GET', self.extensions_url,
response_entity_type=NeutronExtensions,
requestslib_kwargs=requestslib_kwargs)

View File

@ -0,0 +1,30 @@
"""
Copyright 2015 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.
"""
from cloudcafe.common.models.configuration import ConfigSectionInterface
class NeutronExtensionConfig(ConfigSectionInterface):
SECTION_NAME = 'neutron_extensions'
@property
def required(self):
"""
List (comma-separated string) of required, deployed neutron extensions.
"""
extensions = self.get("required", '')
return [ext.strip() for ext in extensions.split(',')
if ext.strip() != '']

View File

@ -0,0 +1,50 @@
"""
Copyright 2015 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
from cafe.engine.models.base \
import AutoMarshallingListModel, AutoMarshallingModel
class NeutronExtension(AutoMarshallingModel):
def __init__(self, updated, name, links, namespace, alias, description):
super(NeutronExtension, self).__init__()
self.updated = updated
self.name = name
self.links = links
self.namespace = namespace
self.alias = alias
self.description = description
@classmethod
def _json_to_obj(cls, serialized_str):
# NOTE: The individual extension summaries do not have a ROOT TAG.
json_dict = json.loads(serialized_str)
return cls(**json_dict)
class NeutronExtensions(AutoMarshallingListModel):
ROOT_TAG = 'extensions'
LIST_MODEL = NeutronExtension
@classmethod
def _json_to_obj(cls, serialized_str):
json_dict = json.loads(serialized_str)
extensions = cls()
for extension in json_dict.get(cls.ROOT_TAG, {}):
extensions.append(cls.LIST_MODEL(**extension))
return extensions

View File

@ -152,3 +152,6 @@ keep_resources_on_failure=<Flag for not deleting resources w failures on tearDow
resource_create_timeout=<Seconds to wait for creating a resource>
resource_delete_timeout=<Seconds to wait for deleting a resource>
starts_with_name=<Port start name label for test runs>
[neutron_extensions]
required=<extensions required for test execution>

View File

@ -0,0 +1,71 @@
"""
Copyright 2015 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 unittest
import cloudcafe.networking.networks.extensions.model as ext_models
class ValidateExtensionEntries(unittest.TestCase):
"""
This test validates the model generated from a Neutron Extensions query.
These tests only verify the JSON-to-Obj translation, since there is no
request (POST/PUT/DELETE) aspect to the API.
"""
MODEL = ext_models.NeutronExtensions
LIST_ELEMENT_MODEL = ext_models.NeutronExtension
RESPONSE_FORMAT = 'json'
NUM_OF_EXTENSIONS = 10
@classmethod
def setUpClass(cls):
cls.expected_response_obj = []
cls.json_parts = []
json_format = """
{{
"updated": "{updated}",
"name": "{name}",
"links": {links},
"namespace": "{namespace}",
"alias": "{alias}",
"description": "{description}"
}} """
data_name = 'test_extension_{0}'
namespace = 'namespace_link_{0}'
for index in xrange(cls.NUM_OF_EXTENSIONS):
data = {'updated': "Timestamp goes here",
'name': data_name.format(index),
'links': [],
'namespace': namespace.format(index),
'alias': data_name.format(index),
'description': 'A brief description of the extension'}
cls.expected_response_obj.append(cls.LIST_ELEMENT_MODEL(**data))
cls.json_parts.append(json_format.format(**data))
list_body = ','.join(cls.json_parts)
cls.json_response = '{{ "{root_tag}": [{body}\n ]\n}}'.format(
root_tag=cls.MODEL.ROOT_TAG, body=list_body)
def test_deserialize_extension_list_response_into_obj(self):
""" Deserialize a JSON response into an NeutronExtensions obj """
response_obj = self.MODEL.deserialize(
self.json_response, self.RESPONSE_FORMAT)
self.assertEqual(response_obj, self.expected_response_obj)