Neutron Public IP Addresses extension

- Updating parent behavior get method to handle multiple params
- Adding IP Addresses (Shared IPs) Neutron extension models and metatests
- Adding IP Addresses (Shared IPs) Neutron extension constants, client and behaviors
- Adding IP Addresses (Shared IPs) Neutron extension composite

Change-Id: I62baf54f344147c1c4006efbf8aa1e2db0639488
This commit is contained in:
Leonardo Maycotte 2015-06-09 11:25:48 -05:00
parent 5279061acf
commit 4eb0ba9f49
13 changed files with 966 additions and 11 deletions

View File

@ -398,10 +398,9 @@ class NetworkingBaseBehaviors(BaseBehavior):
raise ResourceUpdateException(err_msg)
return result
def _get_resource(self, resource, resource_id,
resource_get_attempts=None, raise_exception=False,
poll_interval=None, timeout=None,
use_over_limit_retry=False):
def _get_resource(self, resource, resource_id, resource_get_attempts=None,
raise_exception=False, poll_interval=None, timeout=None,
use_over_limit_retry=False, fn_kwargs=None):
"""
@summary: Shows and verifies a specified resource
@param resource: type of resource for ex. network, subnet, port, etc.
@ -409,6 +408,8 @@ class NetworkingBaseBehaviors(BaseBehavior):
@type resource: resource instance with singular and plural forms
@param resource_id: The UUID for the resource
@type resource_id: str
@param fn_kwargs: function client call params besides the ID
@type fn_kwargs: dict
@param resource_get_attempts: number of API retries
@type resource_get_attempts: int
@param raise_exception: flag to raise an exception if the get
@ -445,20 +446,20 @@ class NetworkingBaseBehaviors(BaseBehavior):
resource_type=resource_type,
resource_id=resource_id))
fn_kwargs = fn_kwargs or {}
# Adding the resource id to the function kwargs
resource_id_name = '{0}_id'.format(resource_type)
fn_kwargs[resource_id_name] = resource_id
# Method uses resource type in singular form (slicing the ending s)
get_fn_name = 'get_{0}'.format(resource_type)
resp = getattr(self.client, get_fn_name)(resource_id)
resp = getattr(self.client, get_fn_name)(**fn_kwargs)
if use_over_limit_retry:
entity_too_large_status_code = (getattr(self.response_codes,
'REQUEST_ENTITY_TOO_LARGE'))
if resp.status_code == entity_too_large_status_code:
fn_kwargs = {}
# Adding the resource id to the function kwargs
resource_id_name = '{0}_id'.format(resource_type)
fn_kwargs[resource_id_name] = resource_id
resp = self.__over_limit_retry(
resource_type=resource_type, timeout=timeout,
poll_interval=poll_interval,

View File

@ -0,0 +1,15 @@
"""
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.
"""

View File

@ -0,0 +1,270 @@
"""
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.networking.networks.common.behaviors \
import NetworkingBaseBehaviors
from cloudcafe.networking.networks.extensions.ip_addresses_api.constants \
import IPAddressesResource, IPAddressesResponseCodes
class IPAddressesBehaviors(NetworkingBaseBehaviors):
def __init__(self, ip_addresses_client, ip_addresses_config):
super(IPAddressesBehaviors, self).__init__()
self.config = ip_addresses_config
self.client = ip_addresses_client
self.response_codes = IPAddressesResponseCodes
self.ip_address_resource = IPAddressesResource(
IPAddressesResource.IP_ADDRESS)
def create_ip_address(self, network_id=None, version=None, device_ids=None,
port_ids=None, resource_build_attempts=None,
raise_exception=True, poll_interval=None):
"""
@summary: Creates an IP address on a specified network
A list of device_ids may be optionally specified to create the IP
address and added to their respective ports. A list of port_ids may
be optionally specified to create the IP address and added to the
specified ports. At least one of device_ids or port_ids must be
specified.
@param network_id: network UUID to get the IP address from
@type network_id: str
@param version: IP address version 4 or 6
@type version: int
@param device_ids (optional): server UUIDs to add the IP address to
their respective ports on the given network
@type device_ids: list
@param port_ids(optional): port UUIDs to add the IP address on the
given network
@type port_ids: list
@param resource_build_attempts: number of API retries
@type resource_build_attempts: int
@param raise_exception: flag to raise an exception if the
Network was not created or to return None
@type raise_exception: bool
@param poll_interval: sleep time interval between API retries
@type poll_interval: int
@return: NetworkingResponse object with api response and failure list
@rtype: common.behaviors.NetworkingResponse
"""
attrs_kwargs = dict(network_id=network_id, version=version,
device_ids=device_ids, port_ids=port_ids)
result = self._create_resource(
resource=self.ip_address_resource,
resource_build_attempts=resource_build_attempts,
raise_exception=raise_exception, poll_interval=poll_interval,
has_name=False, attrs_kwargs=attrs_kwargs)
return result
def update_ip_address(self, ip_address_id, port_ids=None,
resource_update_attempts=None, raise_exception=False,
poll_interval=None):
"""
@summary: Update an IP address, ex. to change ports.
This will eliminate any previous associations to ports.
@param ip_address_id: The UUID for the ip_address
@type ip_address_id: str
@param port_ids: port UUIDs to associate to the IP address
@param resource_update_attempts: number of API retries
@type resource_update_attempts: int
@param raise_exception: flag to raise an exception if the
Network was not updated or to return None
@type raise_exception: bool
@param poll_interval: sleep time interval between API retries
@type poll_interval: int
@return: NetworkingResponse object with api response and failure list
@rtype: common.behaviors.NetworkingResponse
"""
attrs_kwargs = dict(port_ids=port_ids)
result = self._update_resource(
resource=self.ip_address_resource, resource_id=ip_address_id,
resource_update_attempts=resource_update_attempts,
raise_exception=raise_exception, poll_interval=poll_interval,
attrs_kwargs=attrs_kwargs)
return result
def get_ip_address(self, ip_address_id, resource_get_attempts=None,
raise_exception=False, poll_interval=None):
"""
@summary: Shows a specific IP address
@param ip_address_id: The UUID for the ip_address
@type ip_address_id: str
@param resource_get_attempts: number of API retries
@type resource_get_attempts: int
@param raise_exception: flag to raise an exception if the get
Network was not as expected or to return None
@type raise_exception: bool
@param poll_interval: sleep time interval between API retries
@type poll_interval: int
@return: NetworkingResponse object with api response and failure list
@rtype: common.behaviors.NetworkingResponse
"""
result = self._get_resource(
resource=self.ip_address_resource, resource_id=ip_address_id,
resource_get_attempts=resource_get_attempts,
raise_exception=raise_exception, poll_interval=poll_interval)
return result
def list_ip_addresses(self, ip_address_id=None, network_id=None,
address=None, subnet_id=None, port_ids=None,
tenant_id=None, version=None, type_=None,
port_id=None, device_id=None, service=None,
limit=None, marker=None, page_reverse=None,
resource_list_attempts=None, raise_exception=False,
poll_interval=None):
"""
@summary: Lists IP addresses, filtered by params if given
@param ip_address_id: shared IP UUID
@type ip_address_id: str
@param network_id: network UUID where the IP address belongs to
@type network_id: str
@param address: IP address
@type address: str
@param subnet_id: subnet UUID where the IP address belongs to
@type subnet_id: str
@param port_ids: IP addresses port UUIDs
@type port_ids: list
@param tenant_id: tenant ID of the shared IP user
@type tenant_id: str
@param version: IP address version 4 or 6
@type version: int
@param type_: IP address type, for ex. fixed
@type type_: str
@param port_id: IP address by their port ID
@type port_id: str (/ip_addresses/{id}/ports child resource attr)
@param device_id: IP address by their port device ID
@type device_id: str (/ip_addresses/{id}/ports child resource attr)
@param service: IP address by their port service, for ex. compute
@type service: str (/ip_addresses/{id}/ports child resource attr)
@param limit: page size
@type limit: int
@param marker: Id of the last item of the previous page
@type marker: string
@param page_reverse: direction of the page
@type page_reverse: bool
@param resource_list_attempts: number of API retries
@type resource_list_attempts: int
@param raise_exception: flag to raise an exception if the list
Network was not as expected or to return None
@type raise_exception: bool
@param poll_interval: sleep time interval between API retries
@type poll_interval: int
@return: NetworkingResponse object with api response and failure list
@rtype: common.behaviors.NetworkingResponse
"""
params_kwargs = dict(
ip_address_id=ip_address_id, network_id=network_id,
address=address, subnet_id=subnet_id, port_ids=port_ids,
tenant_id=tenant_id, version=version, type_=type_, port_id=port_id,
device_id=device_id, service=service, limit=limit, marker=marker,
page_reverse=page_reverse)
result = self._list_resources(
resource=self.ip_address_resource,
resource_list_attempts=resource_list_attempts,
raise_exception=raise_exception, poll_interval=poll_interval,
params_kwargs=params_kwargs)
return result
def delete_ip_address(self, ip_address_id, resource_delete_attempts=None,
raise_exception=False, poll_interval=None):
"""
@summary: Deletes a specified IP address
@param ip_address_id: The UUID for the ip_address to delete
@type ip_address_id: str
@param resource_delete_attempts: number of API retries
@type resource_delete_attempts: int
@param raise_exception: flag to raise an exception if the deleted
Network was not as expected or to return None
@type raise_exception: bool
@param poll_interval: sleep time interval between API retries
@type poll_interval: int
@return: NetworkingResponse object with api response and failure list
@rtype: common.behaviors.NetworkingResponse
"""
result = self._delete_resource(
resource=self.ip_address_resource,
resource_id=ip_address_id,
resource_delete_attempts=resource_delete_attempts,
raise_exception=raise_exception, poll_interval=poll_interval)
return result
def delete_ip_addresses(self, ip_address_list=None, tenant_id=None,
skip_delete=None):
"""
@summary: deletes multiple IP Addresses
@param ip_address_list: list of IP Address UUIDs
@type ip_address_list: list(str)
@param tenant_id: IP Address tenant ID to filter by
@type tenant_id: string (ignored if ip_address_list given)
@param skip_delete: list of IP Address UUIDs that should skip deletion
@type skip_delete: list
@return: failed deletes list with IP Address IDs and failures
@rtype: list(dict)
"""
result = self._delete_resources(
resource=self.ip_address_resource,
resource_list=ip_address_list,
tenant_id=tenant_id, skip_delete=skip_delete)
return result
def clean_ip_address(self, ip_address_id, timeout=None,
poll_interval=None):
"""
@summary: deletes an IP Address within a time out
@param ip_address_id: The UUID for the ip_address to delete
@type ip_address_id: str
@param timeout: seconds to wait for the IP Address to be deleted
@type timeout: int
@param poll_interval: sleep time interval between API delete/get calls
@type poll_interval: int
@return: None if delete was successful or the undeleted ip_address_id
@rtype: None or string
"""
result = self._clean_resource(
resource=self.ip_address_resource,
resource_id=ip_address_id,
timeout=timeout, poll_interval=poll_interval)
return result
def clean_ip_addresses(self, ip_address_list, timeout=None,
poll_interval=None):
"""
@summary: deletes each IP Address from a list calling clean_ip_address
@param ip_address_list: list of IP Address UUIDs
@type ip_address_list: list(str)
@param timeout: seconds to wait for the IP Address to be deleted
@type timeout: int
@param poll_interval: sleep time interval between API delete/get calls
@type poll_interval: int
@return: list of undeleted IP Address UUIDs
@rtype: list(str)
"""
result = self._clean_resources(
resource=self.ip_address_resource,
resource_list=ip_address_list,
timeout=timeout, poll_interval=poll_interval)
return result

View File

@ -0,0 +1,193 @@
"""
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.ip_addresses_api.models.request\
import IPAddressRequest
from cloudcafe.networking.networks.extensions.ip_addresses_api.models.response\
import IPAddress, IPAddresses
class IPAddressesClient(AutoMarshallingHTTPClient):
def __init__(self, url, auth_token, serialize_format=None,
deserialize_format=None, tenant_id=None):
"""
@summary: Rackspace Neutron API IP Addresses 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(IPAddressesClient, self).__init__(serialize_format,
deserialize_format)
self.auth_token = auth_token
self.default_headers['X-Auth-Token'] = auth_token
ct = '{content_type}/{content_subtype}'.format(
content_type='application',
content_subtype=self.serialize_format)
accept = '{content_type}/{content_subtype}'.format(
content_type='application',
content_subtype=self.deserialize_format)
self.default_headers['Content-Type'] = ct
self.default_headers['Accept'] = accept
if tenant_id:
self.default_headers['X-Auth-Project-Id'] = tenant_id
self.url = url
self.ip_addresses_url = '{url}/ip_addresses'.format(url=self.url)
def create_ip_address(self, network_id=None, version=None, device_ids=None,
port_ids=None, requestslib_kwargs=None):
"""
@summary: Creates an IP address on a specified network
A list of device_ids may be optionally specified to create the IP
address and added to their respective ports. A list of port_ids may
be optionally specified to create the IP address and added to the
specified ports. At least one of device_ids or port_ids must be
specified.
@param network_id: network UUID to get the IP address from
@type network_id: str
@param version: IP address version 4 or 6
@type version: int
@param device_ids (optional): server UUIDs to add the IP address to
their respective ports on the given network
@type device_ids: list
@param port_ids(optional): port UUIDs to add the IP address on the
given network
@type port_ids: list
@return: IP address create response
@rtype: Requests.response
"""
url = self.ip_addresses_url
request = IPAddressRequest(network_id=network_id, version=version,
device_ids=device_ids, port_ids=port_ids)
resp = self.request('POST', url,
response_entity_type=IPAddress,
request_entity=request,
requestslib_kwargs=requestslib_kwargs)
return resp
def update_ip_address(self, ip_address_id, port_ids=None,
requestslib_kwargs=None):
"""
@summary: Update an IP address, ex. to change ports.
This will eliminate any previous associations to ports.
@param ip_address_id: The UUID for the ip_address
@type ip_address_id: str
@param port_ids: port UUIDs to associate to the IP address
@return: IP address update response
@rtype: Requests.response
"""
url = '{base_url}/{ip_address_id}'.format(
base_url=self.ip_addresses_url, ip_address_id=ip_address_id)
request = IPAddressRequest(port_ids=port_ids)
resp = self.request('PUT', url,
response_entity_type=IPAddress,
request_entity=request,
requestslib_kwargs=requestslib_kwargs)
return resp
def get_ip_address(self, ip_address_id, requestslib_kwargs=None):
"""
@summary: Shows a specific IP address
@param ip_address_id: The UUID for the ip_address
@type ip_address_id: str
@return: IP address get response
@rtype: Requests.response
"""
url = '{base_url}/{ip_address_id}'.format(
base_url=self.ip_addresses_url, ip_address_id=ip_address_id)
resp = self.request('GET', url,
response_entity_type=IPAddress,
requestslib_kwargs=requestslib_kwargs)
return resp
def list_ip_addresses(self, ip_address_id=None, network_id=None,
address=None, subnet_id=None, port_ids=None,
tenant_id=None, version=None,
type_=None, port_id=None, device_id=None,
service=None, limit=None, marker=None,
page_reverse=None, requestslib_kwargs=None):
"""
@summary: Lists IP addresses, filtered by params if given
@param ip_address_id: shared IP UUID
@type ip_address_id: str
@param network_id: network UUID where the IP address belongs to
@type network_id: str
@param address: IP address
@type address: str
@param subnet_id: subnet UUID where the IP address belongs to
@type subnet_id: str
@param port_ids: IP addresses port UUIDs
@type port_ids: list
@param tenant_id: tenant ID of the shared IP user
@type tenant_id: str
@param version: IP address version 4 or 6
@type version: int
@param type_: IP address type, for ex. fixed
@type type_: str
@param port_id: IP address by their port ID
@type port_id: str (/ip_addresses/{id}/ports child resource attr)
@param device_id: IP address by their port device ID
@type device_id: str (/ip_addresses/{id}/ports child resource attr)
@param service: IP address by their port service, for ex. compute
@type service: str (/ip_addresses/{id}/ports child resource attr)
@param limit: page size
@type limit: int
@param marker: Id of the last item of the previous page
@type marker: string
@param page_reverse: direction of the page
@type page_reverse: bool
@return: IP address list response
@rtype: Requests.response
"""
params = {'id': ip_address_id, 'network_id': network_id,
'address': address, 'subnet_id': subnet_id,
'port_ids[]': port_ids, 'tenant_id': tenant_id,
'version': version, 'type': type_, 'port_id': port_id,
'device_id': device_id, 'service': service,
'limit': limit, 'marker': marker,
'page_reverse': page_reverse}
url = self.ip_addresses_url
resp = self.request('GET', url, params=params,
response_entity_type=IPAddresses,
requestslib_kwargs=requestslib_kwargs)
return resp
def delete_ip_address(self, ip_address_id, requestslib_kwargs=None):
"""
@summary: Deletes a specified IP address
@param ip_address_id: The UUID for the ip_address to delete
@type ip_address_id: str
@return: IP address delete response
@rtype: Requests.response
"""
url = '{base_url}/{ip_address_id}'.format(
base_url=self.ip_addresses_url, ip_address_id=ip_address_id)
resp = self.request('DELETE', url,
requestslib_kwargs=requestslib_kwargs)
return resp

View File

@ -0,0 +1,38 @@
"""
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.networking.networks.composites import _NetworkingAuthComposite
from cloudcafe.networking.networks.extensions.ip_addresses_api.behaviors \
import IPAddressesBehaviors
from cloudcafe.networking.networks.extensions.ip_addresses_api.client \
import IPAddressesClient
from cloudcafe.networking.networks.extensions.ip_addresses_api.config \
import IPAddressesConfig
class IPAddressesComposite(object):
networking_auth_composite = _NetworkingAuthComposite
def __init__(self):
auth_composite = self.networking_auth_composite()
self.url = auth_composite.networking_url
self.user = auth_composite._auth_user_config
self.config = IPAddressesConfig()
self.client = IPAddressesClient(**auth_composite.client_args)
self.behaviors = IPAddressesBehaviors(
ip_addresses_client=self.client,
ip_addresses_config=self.config)

View File

@ -0,0 +1,23 @@
"""
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.networking.networks.common.config import NetworkingBaseConfig
class IPAddressesConfig(NetworkingBaseConfig):
"""IP Addresses configuration parameters"""
SECTION_NAME = 'ip_addresses'

View File

@ -0,0 +1,44 @@
"""
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.networking.networks.common.constants \
import NeutronResource, NeutronResponseCodes, NeutronErrorTypes
class IPAddressesResource(NeutronResource):
"""IP addresses resource types"""
# Resources to be used by the behavior
IP_ADDRESS = 'ip_address'
IP_ADDRESSES = 'ip_addresses'
PLURALS = NeutronResource.PLURALS.update(
{IP_ADDRESS: IP_ADDRESSES})
class IPAddressesResponseCodes(NeutronResponseCodes):
"""HTTP IP Address API Response codes"""
LIST_IP_ADDRESSES = 200
GET_IP_ADDRESS = 200
CREATE_IP_ADDRESS = 201
UPDATE_IP_ADDRESS = 200
DELETE_IP_ADDRESS = 204
class IPAddressesErrorTypes(NeutronErrorTypes):
"""IP Address Error Types"""
IP_ADDRESS_NOT_FOUND = 'IPAddressNotFound'

View File

@ -0,0 +1,15 @@
"""
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.
"""

View File

@ -0,0 +1,59 @@
"""
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 AutoMarshallingModel
class IPAddressRequest(AutoMarshallingModel):
"""
@summary: IP Address model request object for the Shared IPs Rackspace
Networking v2.0 API extension for creating (POST) and updating (PUT)
IP addresses.
@param network_id: network UUID to get the IP address from
@type network_id: str
@param version: IP address version 4 or 6
@type version: int
@param device_ids (optional): server UUIDs to add the IP address to their
respective ports on the given network
@type device_ids: list
@param port_ids(optional): port UUIDs to add the IP address on the given
network
@type port_ids: list
"""
def __init__(self, network_id=None, version=None, device_ids=None,
port_ids=None):
super(IPAddressRequest, self).__init__()
self.network_id = network_id
self.version = version
self.device_ids = device_ids
self.port_ids = port_ids
def _obj_to_json(self):
body = {
'network_id': self.network_id,
'version': self.version,
'device_ids': self.device_ids,
'port_ids': self.port_ids
}
# Removing optional params not given
body = self._remove_empty_values(body)
main_body = {'ip_address': body}
return json.dumps(main_body)

View File

@ -0,0 +1,110 @@
"""
Copyright 2014 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 IPAddress(AutoMarshallingModel):
"""
@summary: IP Address model response object for the Shared IPs Rackspace
Networking v2.0 API extension.
@param id_: shared IP UUID
@type id_: str
@param network_id: network UUID where the IP address belongs to
@type network_id: str
@param address: IP address
@type address: str
@param port_ids: list of port UUIDs where the shared IP will be associated
@type port_ids: list(str)
@param subnet_id: subnet UUID where the IP address belongs to
@type subnet_id: str
@param tenant_id: tenant ID of the shared IP user
@type tenant_id: str
@param version: IP address version 4 or 6
@type version: str
@param type_: IP address type, for ex. fixed
@type type_: str
"""
IP_ADDRESS = 'ip_address'
def __init__(self, id_=None, network_id=None, address=None, port_ids=None,
subnet_id=None, tenant_id=None, version=None, type_=None,
**kwargs):
# kwargs is to be used for checking unexpected attrs
super(IPAddress, self).__init__()
self.id = id_
self.network_id = network_id
self.address = address
self.port_ids = port_ids
self.subnet_id = subnet_id
self.tenant_id = tenant_id
self.version = version
self.type = type_
self.kwargs = kwargs
@classmethod
def _json_to_obj(cls, serialized_str):
"""
@summary: Return IP address object from a JSON serialized string
"""
ret = None
json_dict = json.loads(serialized_str)
# Replacing attribute response names if they are Python reserved words
# with a trailing underscore, for ex. id for id_
json_dict = cls._replace_dict_key(
json_dict, 'id', 'id_', recursion=True)
json_dict = cls._replace_dict_key(
json_dict, 'type', 'type_', recursion=True)
if cls.IP_ADDRESS in json_dict:
ip_address_dict = json_dict.get(cls.IP_ADDRESS)
ret = IPAddress(**ip_address_dict)
return ret
class IPAddresses(AutoMarshallingListModel):
IP_ADDRESSES = 'ip_addresses'
@classmethod
def _json_to_obj(cls, serialized_str):
"""
@summary: Return a list of IP address objects from a JSON
serialized string
"""
ret = cls()
json_dict = json.loads(serialized_str)
# Replacing attribute response names if they are Python reserved words
# with a trailing underscore, for ex. id for id_
json_dict = cls._replace_dict_key(
json_dict, 'id', 'id_', recursion=True)
json_dict = cls._replace_dict_key(
json_dict, 'type', 'type_', recursion=True)
if cls.IP_ADDRESSES in json_dict:
ip_addresses = json_dict.get(cls.IP_ADDRESSES)
for ip_address in ip_addresses:
result = IPAddress(**ip_address)
ret.append(result)
return ret

View File

@ -0,0 +1,15 @@
"""
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.
"""

View File

@ -0,0 +1,15 @@
"""
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.
"""

View File

@ -0,0 +1,157 @@
"""
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
from cloudcafe.networking.networks.extensions.ip_addresses_api.models.request\
import IPAddressRequest
from cloudcafe.networking.networks.extensions.ip_addresses_api.models.response\
import IPAddress, IPAddresses
ERROR_MSG_REQ = ('JSON unexpected IP Address request serialization\n'
'Actual Serialization:\n{request}\n'
'Expected Serialization:\n{expected}\n')
ERROR_MSG_RESP = ('JSON to Obj response different than expected\n'
'Actual Response:\n{response}\n'
'Expected Response:\n{expected}\n')
class CreateIPAddressTest(unittest.TestCase):
"""
@summary: Test for the IP Address POST model object request
"""
@classmethod
def setUpClass(cls):
create_attrs = dict(
network_id='testnet_id', version='4',
device_ids=['dev1, dev2, dev3'],
port_ids=['port1', 'port2', 'port3'])
cls.ip_address_model = IPAddressRequest(**create_attrs)
cls.expected_json_output = (
'{"ip_address": {"network_id": "testnet_id", "port_ids": '
'["port1", "port2", "port3"], "version": "4", "device_ids": '
'["dev1, dev2, dev3"]}}')
def test_json_request(self):
request_body = self.ip_address_model._obj_to_json()
msg = ERROR_MSG_REQ.format(request=request_body,
expected=self.expected_json_output)
self.assertEqual(request_body, self.expected_json_output, msg)
class GetIPAddressTest(unittest.TestCase):
"""
@sumary: Test for the IP Address GET model object response
"""
@classmethod
def setUpClass(cls):
# Setting the expected response object
get_attrs = dict(
id_='4cacd68e-d7aa-4ff2-96f4-5c6f57dba737',
network_id='fda61e0b-a410-49e8-ad3a-64c595618c7e',
address='192.168.10.1',
port_ids=['6200d533-a42b-4c04-82a1-cc14dbdbf2de'],
subnet_id='f11687e8-ef0d-4207-8e22-c60e737e473b',
tenant_id='2345678',
version='4',
type_='fixed')
cls.expected_response = IPAddress(**get_attrs)
# Data simulating the JSON API response
cls.api_json_resp = ("""
{
"ip_address": {
"id": "4cacd68e-d7aa-4ff2-96f4-5c6f57dba737",
"network_id": "fda61e0b-a410-49e8-ad3a-64c595618c7e",
"address": "192.168.10.1",
"port_ids": ["6200d533-a42b-4c04-82a1-cc14dbdbf2de"],
"subnet_id": "f11687e8-ef0d-4207-8e22-c60e737e473b",
"tenant_id": "2345678",
"version": "4",
"type": "fixed"}
}
""")
def test_json_response(self):
response_obj = IPAddress()._json_to_obj(self.api_json_resp)
msg = ERROR_MSG_RESP.format(response=response_obj,
expected=self.expected_response)
self.assertEqual(response_obj, self.expected_response, msg)
class ListIPAddressesTest(unittest.TestCase):
"""
@sumary: Test for the IP Addresses (List) GET model object response
"""
@classmethod
def setUpClass(cls):
# Setting the expected response object
get_attrs1 = dict(
id_='address',
network_id='fda61e0b-a410-49e8-ad3a-64c595618c7e',
address='192.168.10.1',
port_ids=['6200d533-a42b-4c04-82a1-cc14dbdbf2de'],
subnet_id='f11687e8-ef0d-4207-8e22-c60e737e473b',
tenant_id='2345678',
version='4',
type_='fixed')
get_attrs2 = dict(
id_='address2',
network_id='a_network_id',
address='192.168.10.7',
port_ids=['port_id1', 'port_id2', 'port_id3'],
subnet_id='a_subnet_id',
tenant_id='a_tenant_id',
version='6',
type_='floating')
ip_address1 = IPAddress(**get_attrs1)
ip_address2 = IPAddress(**get_attrs2)
cls.expected_response = [ip_address1, ip_address2]
# Data simulating the JSON API response
cls.api_json_resp = ("""
{
"ip_addresses": [{
"id": "address",
"network_id": "fda61e0b-a410-49e8-ad3a-64c595618c7e",
"address": "192.168.10.1",
"port_ids": ["6200d533-a42b-4c04-82a1-cc14dbdbf2de"],
"subnet_id": "f11687e8-ef0d-4207-8e22-c60e737e473b",
"tenant_id": "2345678",
"version": "4",
"type": "fixed"},
{
"id": "address2",
"network_id": "a_network_id",
"address": "192.168.10.7",
"port_ids": ["port_id1", "port_id2", "port_id3"],
"subnet_id": "a_subnet_id",
"tenant_id": "a_tenant_id",
"version": "6",
"type": "floating"}]
}
""")
def test_json_response(self):
response_obj = IPAddresses()._json_to_obj(self.api_json_resp)
msg = ERROR_MSG_RESP.format(response=response_obj,
expected=self.expected_response)
self.assertEqual(response_obj, self.expected_response, msg)
if __name__ == "__main__":
unittest.main()