Adding vanilla flavor clients for the Neutron API

Renamed nets_subnets_ports_api for networks
Adding configs and composites
Code Review updates
More Code Review updates

Change-Id: If7bd73863ead708e5e690bf8df18a7577ea1f84d
This commit is contained in:
Leonardo Maycotte 2014-08-15 09:05:11 -05:00
parent 388dadbf36
commit cfa112f7d5
32 changed files with 2298 additions and 481 deletions

View File

@ -1,481 +0,0 @@
"""
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.
"""
from cloudcafe.common.client import BaseClient, setup_rest_operation
class NetsSubnetsPortsClient(BaseClient):
"""Implements the Neutron ReST client for the following API resources:
networks
subnets
ports
"""
def __init__(self, url, auth_token, serialize_format=None,
deserialize_format=None):
"""
@param url: Base URL for the Neutron 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
"""
super(NetsSubnetsPortsClient, self).__init__(url, auth_token,
serialize_format,
deserialize_format)
self._models_classes = {'networks': (None, None),
'subnets': (None, None),
'ports': (None, None), }
self._resource_plural_map = {}
@setup_rest_operation
def list_networks(self, name=None, admin_state_up=None, status=None,
shared=None, tenant_id=None, limit=None, marker=None,
page_reverse=None, requestslib_kwargs=None):
"""
@summary: Lists all networks. Additionally, can filter results by
params. Maps to /networks
@param name: Network name to filter by
@type name: String
@param admin_state_up: Network administrative state up value to filter
by
@type admin_state_up: Boolean
@param status: Network status to filter by
@type status: String
@param shared: Network shared attribute value to filter by
@type shared: Boolean
@param tenant_id: Network owner tenant_id to filter by
@type tenant_id: String
@param marker: Network id to be used as a marker for the next list
@type marker: String
@param limit: The maximum number of results to return
@type limit: Int
@param page_reverse: Page direction setting
@type page_reverse: Boolean
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._list(name=name, admin_state_up=admin_state_up,
status=status, shared=shared, tenant_id=tenant_id,
marker=marker, limit=limit,
page_reverse=page_reverse,
requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def create_network(self, name, admin_state_up=None, shared=None,
tenant_id=None, requestslib_kwargs=None):
"""
@summary: Creates an instance of a network given the
provided parameters
@param name: human readable name of the network. Might not be unique.
No default value provided by the API
@type name: String
@param admin_state_up: Network administrative state up value. If down,
the network does not forward packets. API default value is true
@type admin_state_up: Boolean
@param shared: Specifies whether the network can be shared by other
tenants or not. API default value is false
@type shared: Boolean
@param tenant_id: owner of network. Only admin users can specify a
tenant id other than its own. No default value provided by the API
@type tenant_id: String
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._create(name=name, admin_state_up=admin_state_up,
shared=shared, tenant_id=tenant_id,
requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def update_network(self, network_id, name=None, admin_state_up=None,
shared=None, requestslib_kwargs=None):
"""
@summary: updates an instance of a network given the
provided parameters
@param network_id: id of the network to update
@type network_id: String
@param name: human readable name of the network. Might not be unique.
@type name: String
@param admin_state_up: Network administrative state up value. If down,
the network does not forward packets.
@type admin_state_up: Boolean
@param shared: Specifies whether the network can be shared by other
tenants or not.
@type shared: Boolean
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._update(network_id, name=name,
admin_state_up=admin_state_up, shared=shared,
requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def show_network(self, network_id, requestslib_kwargs=None):
"""
@summary: get the attributes of a network instance
@param network_id: id of the network whose attributes will be gotten
@type network_id: String
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._show(network_id, requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def delete_network(self, network_id, requestslib_kwargs=None):
"""
@summary: delete a network
@param network_id: id of the network to delete
@type network_id: String
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._delete(network_id, requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def list_subnets(self, network_id=None, name=None, ip_version=None,
cidr=None, gateway_ip=None, enable_dhcp=None,
tenant_id=None, limit=None, marker=None,
page_reverse=None, requestslib_kwargs=None):
"""
@summary: Lists all subnets. Additionally, can filter results by
params. Maps to /subnets
@param network_id: Network id to filter by
@type network_id: String
@param name: Subnet id to filter by
@type name: String
@param ip_version: IP version to filter by
@type ip_version: Int
@param cidr: cidr to filter by
@type cidr: String
@param gateway_ip: Gateway ip address to filter by
@type gateway_ip: String
@param enable_dhcp: Enable dhcp setting to filter by
@type enable_dhcp: Boolen
@param tenant_id: ID of tenant to filter by
@type tenant_id: String
@param marker: Subnet id to be used as a marker for the next list
@type marker: String
@param limit: The maximum number of results to return
@type limit: Int
@param page_reverse: Page direction setting
@type page_reverse: Boolean
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._list(network_id=network_id, name=name,
ip_version=ip_version, cidr=cidr,
gateway_ip=gateway_ip, enable_dhcp=enable_dhcp,
tenant_id=tenant_id, marker=marker, limit=limit,
page_reverse=page_reverse,
requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def create_subnet(self, network_id, cidr, name=None, ip_version=None,
gateway_ip=None, dns_nameservers=None,
allocation_pools=None, host_routes=None,
enable_dhcp=None, tenant_id=None,
requestslib_kwargs=None):
"""
@summary: Creates an instance of a subnet given the
provided parameters
@param network_id: the id of the network the subnet will be
associated with
@type network_id: String
@param cidr: the ip range for the subnet
@type cidr: String
@param name: human readable name for the subnet. Might not be unique.
No default value provided by the API
@type name: String
@param ip_version: the IP version for the subnet. API default value is
4
@type ip_version: Int
@param gateway_ip: default gateway used by devices in the subnet
API default value is the first address in the cidr
@type gateway_ip: String
@param dns_nameservers: DNS nanme servers used by hosts in the subnet
API default value is an empty list
@type dns_nameservers: List of String
@param allocation_pools: sub-ranges of cidr available for dynamic
allocation to ports. API default value is every address in cidr,
excluding gateway ip if configured
@type allocation_pools: List of Dictionaries
@param host_routes: routes that should be used by devices with IP's
from the subnet (not including local subnet route). API default value
is empty list
@type host_routes: List of Dictionaries
@param enable_dhcp: specifies whether DHCP is enabled for the subnet or
not. API default value is true
@type enable_dhcp: Boolean
@param tenant_id: owner of subnet. Only admin users can specify a
tenant id other than its own. No default value provided by the API
@type tenant_id: String
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._create(network_id=network_id, cidr=cidr, name=name,
ip_version=ip_version, gateway_ip=gateway_ip,
dns_nameservers=dns_nameservers,
allocation_pools=allocation_pools,
host_routes=host_routes, enable_dhcp=enable_dhcp,
tenant_id=tenant_id,
requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def update_subnet(self, subnet_id, name=None, gateway_ip=None,
dns_nameservers=None, host_routes=None,
enable_dhcp=None, requestslib_kwargs=None):
"""
@summary: updates an instance of a subnet given the
provided parameters
@param subnet_id: the id of the subnet to updtes
@type subnet_id: String
@param name: human readable name for the subnet. Might not be unique.
@type name: String
@param gateway_ip: default gateway used by devices in the subnet
@type gateway_ip: String
@param dns_nameservers: DNS nanme servers used by hosts in the subnet
@type dns_nameservers: List of String
@param host_routes: routes that should be used by devices with IP's
from the subnet (not including local subnet route).
@type host_routes: List of Dictionaries
@param enable_dhcp: specifies whether DHCP is enabled for the subnet or
not.
@type enable_dhcp: Boolean
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._update(subnet_id, name=name, gateway_ip=gateway_ip,
dns_nameservers=dns_nameservers,
host_routes=host_routes, enable_dhcp=enable_dhcp,
requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def show_subnet(self, subnet_id, requestslib_kwargs=None):
"""
@summary: get the attributes of a subnet instance
@param subnet_id: id of the subnet whose attributes will be gotten
@type subnet_id: String
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._show(subnet_id, requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def delete_subnet(self, subnet_id, requestslib_kwargs=None):
"""
@summary: delete a subnet
@param subnet_id: id of the subnet to delete
@type subnet_id: String
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._delete(subnet_id, requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def list_ports(self, network_id=None, name=None, admin_state_up=None,
status=None, mac_address=None, device_id=None,
device_owner=None, tenant_id=None, limit=None, marker=None,
page_reverse=None, requestslib_kwargs=None):
"""
@summary: Lists all ports. Additionally, can filter results by
params. Maps to /ports
@param network_id: Network id to filter by
@type network_id: String
@param name: Port id to filter by
@type name: String
@param admin_state_up: Network administrative state up value to filter
by
@type admin_state_up: Boolean
@param status: Network status to filter by
@type status: String
@param mac_address: mac_address to filter by
@type mac_address: String
@param device_id: Device id to filter by
@type device_id: String
@param device_owner: Device owner to filter by
@type device_owner: String
@param tenant_id: ID of tenant to filter by
@type tenant_id: String
@param marker: Subnet id to be used as a marker for the next list
@type marker: String
@param limit: The maximum number of results to return
@type limit: Int
@param page_reverse: Page direction setting
@type page_reverse: Boolean
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._list(network_id=network_id, name=name,
admin_state_up=admin_state_up, status=status,
mac_address=mac_address, device_id=device_id,
device_owner=device_owner, tenant_id=tenant_id,
marker=marker, limit=limit,
page_reverse=page_reverse,
requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def create_port(self, network_id, name=None, admin_state_up=None,
mac_address=None, fixed_ips=None, device_id=None,
device_owner=None, tenant_id=None, security_groups=None,
requestslib_kwargs=None):
"""
@summary: Creates an instance of a port given the
provided parameters
@param network_id: network that the port is associated with. No default
value provided by the API
@type neywork_id: String
@param name: hurman readable name for the port. Might not be unique. No
default value provided by the API
@type name: String
@param admin_state_up: administrative state of the port. If false, port
does not forward packets. API default value is true
@type admin_state_up: Boolean
@param mac_address: mac addres to be used on this port. API default
value is generated
@type mac_address: String
@param fixed_ips: specifies ip adddreses for the port, associating it
with the subnets where the ip addresses are picked from. API default
value is an ip address selected from the allocation pools associated
with the network
@type fixed_ips: List of Dictionaries
@param device_id: identifies the device (e.g. virtual server) using the
port. No default value provided by the API
@type device_id: String
@param device_owner: identifies the entity (e.g. dhcp agent) using the
port. No default value provided by the API
@type device_owner: String
@param tenant_id: owner of port. Only admin users can specify tenants
other than their own. No default value provided by the API
@type tenant_id: String
@param security_groups: specifies the ID's of any security groups
associated with the port. No default value provided by the API
@type security_groups: List of Dictionaries
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._create(network_id=network_id, name=name,
admin_state_up=admin_state_up,
mac_address=mac_address, fixed_ips=fixed_ips,
device_id=device_id, device_owner=device_owner,
tenant_id=tenant_id,
security_groups=security_groups,
requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def update_port(self, port_id, name=None, admin_state_up=None,
fixed_ips=None, device_id=None, device_owner=None,
security_groups=None, requestslib_kwargs=None):
"""
@summary: updates an instance of a port given the
provided parameters
@param port_id: id of the port to be updated
@type neywork_id: String
@param name: hurman readable name for the port. Might not be unique.
@type name: String
@param admin_state_up: administrative state of the port. If false, port
does not forward packets.
@type admin_state_up: Boolean
@param fixed_ips: specifies ip adddreses for the port, associating it
with the subnets where the ip addresses are picked from.
@type fixed_ips: List of Dictionaries
@param device_id: identifies the device (e.g. virtual server) using the
port.
@type device_id: String
@param device_owner: identifies the entity (e.g. dhcp agent) using the
port.
@type device_owner: String
@param security_groups: specifies the ID's of any security groups
associated with the port
@type security_groups: List of Dictionaries
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._update(port_id, name=name, admin_state_up=admin_state_up,
fixed_ips=fixed_ips, device_id=device_id,
device_owner=device_owner,
security_groups=security_groups,
requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def show_port(self, port_id, requestslib_kwargs=None):
"""
@summary: get the attributes of a port instance
@param port_id: id of the port whose attributes will be gotten
@type port_id: String
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._show(port_id, requestslib_kwargs=requestslib_kwargs)
@setup_rest_operation
def delete_port(self, port_id, requestslib_kwargs=None):
"""
@summary: delete a port
@param port_id: id of the port to be deleted
@type port_id: String
@param requestslib_kwargs: keyword arguments to be passed to the
requests library
@type requestslib_kwargs: Dictionary
@return: response from the API
@rtype: Requests.response
"""
return self._delete(port_id, requestslib_kwargs=requestslib_kwargs)

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,59 @@
"""
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
import xml.etree.ElementTree as ET
from cafe.engine.models.base import AutoMarshallingModel
class NetworkRequest(AutoMarshallingModel):
"""
@summary: Network model object for the OpenStack Neutron v2.0 API
requests for creating (POST) and updating (PUT) networks calls
@param string name: human readable name for the network,
may not be unique. (CRUD: CRU)
@param bool admin_state_up: true or false, the admin state
of the network. If down, the network does not forward packets.
Default value is True (CRUD: CRU)
@param bool shared: specifies if the network can be accessed by any
tenant. Default value is False. (CRUD: CRU)
@param string tenant_id: owner of the network. (CRUD: CR)
"""
def __init__(self, name=None, admin_state_up=None, shared=None,
tenant_id=None, **kwargs):
# kwargs is to be used for extensions
super(NetworkRequest, self).__init__()
self.name = name
self.admin_state_up = admin_state_up
self.shared = shared
self.tenant_id = tenant_id
def _obj_to_json(self):
body = {
'shared': self.shared,
'tenant_id': self.tenant_id,
'name': self.name,
'admin_state_up': self.admin_state_up
}
# Removing optional params not given
body = self._remove_empty_values(body)
main_body = {'network': body}
return json.dumps(main_body)

View File

@ -0,0 +1,77 @@
"""
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
import xml.etree.ElementTree as ET
from cafe.engine.models.base import AutoMarshallingModel
class PortRequest(AutoMarshallingModel):
"""
@summary: Port model object for the OpenStack Neutron v2.0 API
requests for creating (POST) and updating (PUT) ports calls
@param string network_id: network port is associated with (CRUD: CR)
@param string name: human readable name for the port,
may not be unique. (CRUD: CRU)
@param bool admin_state_up: true or false (default true), the admin state
of the port. If down, the port does not forward packets (CRUD: CRU)
@param string mac_address: mac address to use on the port (CRUD: CR)
@param list(dict) fixed_ips: ip addresses for the port associating the
port with the subnets where the IPs come from (CRUD: CRU)
@param string device_id: id of device using this port (CRUD: CRUD)
@param string device_owner: entity using this port (ex. dhcp agent,
CRUD: CRUD)
@param string tenant_id: owner of the port (CRUD: CR)
@param list(dict) security_groups: ids of any security groups associated
with the port (CRUD: CRUD)
"""
def __init__(self, network_id=None, name=None, admin_state_up=None,
mac_address=None, fixed_ips=None, device_id=None,
device_owner=None, tenant_id=None, security_groups=None,
**kwargs):
# kwargs is to be used for extensions
super(PortRequest, self).__init__()
self.network_id = network_id
self.name = name
self.admin_state_up = admin_state_up
self.mac_address = mac_address
self.fixed_ips = fixed_ips
self.device_id = device_id
self.device_owner = device_owner
self.tenant_id = tenant_id
self.security_groups = security_groups
def _obj_to_json(self):
body = {
'network_id': self.network_id,
'name': self.name,
'admin_state_up': self.admin_state_up,
'mac_address': self.mac_address,
'fixed_ips': self.fixed_ips,
'device_id': self.device_id,
'device_owner': self.device_owner,
'tenant_id': self.tenant_id,
'security_groups': self.security_groups
}
# The client should instantiate the model with only desired parameters
body = self._remove_empty_values(body)
main_body = {'port': body}
return json.dumps(main_body)

View File

@ -0,0 +1,81 @@
"""
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
import xml.etree.ElementTree as ET
from cafe.engine.models.base import AutoMarshallingModel
class SubnetRequest(AutoMarshallingModel):
"""
@summary: Subnet model object for the OpenStack Neutron v2.0 API
requests for creating (POST) and updating (PUT) subnets
@param string name: human readable name for the subnet,
may not be unique. (CRUD: CRU)
@param string tenant_id: owner of the network. (CRUD: CR)
@param string network_id: network subnet is associated with (CRUD: CR)
@param int ip_version: IP version 4 or 6 (CRUD: CR)
@param string cidr: represents IP range for the subnet and should be in
the form <network_address>/<prefix> (CRUD: CR)
@param string gateway_ip: default gateway used by devices in the subnet
(CRUD: CRUD)
@param list(str) dns_nameservers: DNS name servers used by subnet hosts
(CRUD: CRU)
@param list(dict) allocation_pools: sub range of cidr available for dynamic
allocation to ports (CRUD: CR)
@param list(dict) host_routes: routes that should be used by devices with
IPs from this subnet (does not includes the local route, CRUD: CRU)
@param bool enable_dhcp: whether DHCP is enabled (CRUD:CRU)
"""
def __init__(self, name=None, tenant_id=None, network_id=None,
ip_version=None, cidr=None, gateway_ip=None,
dns_nameservers=None, allocation_pools=None,
host_routes=None, enable_dhcp=None, **kwargs):
# kwargs is to be used for extensions
super(SubnetRequest, self).__init__()
self.name = name
self.tenant_id = tenant_id
self.network_id = network_id
self.ip_version = ip_version
self.cidr = cidr
self.gateway_ip = gateway_ip
self.allocation_pools = allocation_pools
self.dns_nameservers = dns_nameservers
self.host_routes = host_routes
self.enable_dhcp = enable_dhcp
def _obj_to_json(self):
body = {
'name': self.name,
'tenant_id': self.tenant_id,
'ip_version': self.ip_version,
'network_id': self.network_id,
'cidr': self.cidr,
'dns_nameservers': self.dns_nameservers,
'gateway_ip': self.gateway_ip,
'allocation_pools': self.allocation_pools,
'host_routes': self.host_routes,
'enable_dhcp': self.enable_dhcp
}
# The client should instantiate the model with only desired parameters
body = self._remove_empty_values(body)
main_body = {'subnet': body}
return json.dumps(main_body)

View File

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

View File

@ -0,0 +1,104 @@
"""
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 copy
import json
import xml.etree.ElementTree as ET
from cafe.engine.models.base import AutoMarshallingListModel, \
AutoMarshallingModel
class Network(AutoMarshallingModel):
"""
@summary: Network model object for the OpenStack Neutron v2.0 API
responses for networks show and list (GET) calls
@param string id: UUID for the network (CRUD: R)
@param string name: human readable name for the network,
may not be unique. (CRUD: CRU)
@param bool admin_state_up: true or false, the admin state
of the network. If down, the network does not forward packets.
Default value is True (CRUD: CRU)
@param string status: Indicates if the network is currently
operational. Possible values: ACTIVE, DOWN, BUILD, ERROR. (CRUD: R)
@param list subnets: associated network subnets UUID list. (CRUD: R)
@param bool shared: specifies if the network can be accessed by any
tenant. Default value is False. (CRUD: CRU)
@param string tenant_id: owner of the network. (CRUD: CR)
"""
NETWORK = 'network'
def __init__(self, id_=None, name=None, admin_state_up=None,
status=None, subnets=None, shared=None, tenant_id=None,
**kwargs):
# kwargs is to be used for extensions
super(Network, self).__init__()
self.id = id_
self.name = name
self.admin_state_up = admin_state_up
self.status = status
self.subnets = subnets
self.shared = shared
self.tenant_id = tenant_id
@classmethod
def _json_to_obj(cls, serialized_str):
"""Return network object from a JSON serialized string"""
ret = None
json_response = json.loads(serialized_str)
# Creating a deep copy just in case later we want the original resp
json_dict = copy.deepcopy(json_response)
# Replacing attribute response names if they are Python reserved words
# with a trailing underscore, for ex. id for id_ or if they have a
# special character within the name replacing it for an underscore too
json_dict = cls._replace_dict_key(
json_dict, 'id', 'id_', recursion=True)
if cls.NETWORK in json_dict:
network_dict = json_dict.get(cls.NETWORK)
ret = Network(**network_dict)
return ret
class Networks(AutoMarshallingListModel):
NETWORKS = 'networks'
@classmethod
def _json_to_obj(cls, serialized_str):
"""Return a list of network objects from a JSON serialized string"""
ret = cls()
json_response = json.loads(serialized_str)
# Creating a deep copy just in case later we want the original resp
json_dict = copy.deepcopy(json_response)
# Replacing attribute response names if they are Python reserved words
# with a trailing underscore, for ex. id for id_ or if they have a
# special character within the name replacing it for an underscore too
json_dict = cls._replace_dict_key(
json_dict, 'id', 'id_', recursion=True)
if cls.NETWORKS in json_dict:
networks = json_dict.get(cls.NETWORKS)
for network in networks:
ret.append(Network(**network))
return ret

View File

@ -0,0 +1,115 @@
"""
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 copy
import json
import xml.etree.ElementTree as ET
from cafe.engine.models.base import AutoMarshallingListModel
from cafe.engine.models.base import AutoMarshallingModel
class Port(AutoMarshallingModel):
"""
@summary: Port model object for the OpenStack Neutron v2.0 API
responses for ports show and list (GET) calls
@param string id: UUID for the port (CRUD: R)
@param string network_id: network port is associated with (CRUD: CR)
@param string name: human readable name for the port,
may not be unique. (CRUD: CRU)
@param bool admin_state_up: true or false (default true), the admin state
of the port. If down, the port does not forward packets (CRUD: CRU)
@param string status: Indicates if the port is currently
operational. Possible values: ACTIVE, DOWN, BUILD, ERROR (CRUD: R)
@param string mac_address: mac address to use on the port (CRUD: CR)
@param list(dict) fixed_ips: ip addresses for the port associating the
port with the subnets where the IPs come from (CRUD: CRU)
@param string device_id: id of device using this port (CRUD: CRUD)
@param string device_owner: entity using this port (ex. dhcp agent,
CRUD: CRUD)
@param string tenant_id: owner of the port (CRUD: CR)
@param list(dict) security_groups: ids of any security groups associated
with the port (CRUD: CRUD)
"""
PORT = 'port'
def __init__(self, id_=None, network_id=None, name=None,
admin_state_up=None, status=None, mac_address=None,
fixed_ips=None, device_id=None, device_owner=None,
tenant_id=None, security_groups=None, **kwargs):
# kwargs is to be used for extensions
super(Port, self).__init__()
self.id = id_
self.network_id = network_id
self.name = name
self.admin_state_up = admin_state_up
self.status = status
self.mac_address = mac_address
self.fixed_ips = fixed_ips
self.device_id = device_id
self.device_owner = device_owner
self.tenant_id = tenant_id
self.security_groups = security_groups
@classmethod
def _json_to_obj(cls, serialized_str):
"""Return port object from a JSON serialized string"""
ret = None
json_response = json.loads(serialized_str)
# Creating a deep copy just in case later we want the original resp
json_dict = copy.deepcopy(json_response)
# Replacing attribute response names if they are Python reserved words
# with a trailing underscore, for ex. id for id_ or if they have a
# special character within the name replacing it for an underscore too
json_dict = cls._replace_dict_key(
json_dict, 'id', 'id_', recursion=True)
if cls.PORT in json_dict:
subnet_dict = json_dict.get(cls.PORT)
ret = Port(**subnet_dict)
return ret
class Ports(AutoMarshallingListModel):
PORTS = 'ports'
@classmethod
def _json_to_obj(cls, serialized_str):
"""Return a list of port objects from a JSON serialized string"""
ret = cls()
json_response = json.loads(serialized_str)
# Creating a deep copy just in case later we want the original resp
json_dict = copy.deepcopy(json_response)
# Replacing attribute response names if they are Python reserved words
# with a trailing underscore, for ex. id for id_ or if they have a
# special character within the name replacing it for an underscore too
json_dict = cls._replace_dict_key(
json_dict, 'id', 'id_', recursion=True)
if cls.PORTS in json_dict:
ports = json_dict.get(cls.PORTS)
for port in ports:
ret.append(Port(**port))
return ret

View File

@ -0,0 +1,115 @@
"""
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 copy
import json
import xml.etree.ElementTree as ET
from cafe.engine.models.base import AutoMarshallingListModel
from cafe.engine.models.base import AutoMarshallingModel
class Subnet(AutoMarshallingModel):
"""
@summary: Subnet model object for the OpenStack Neutron v2.0 API
responses for subnets show and list (GET) calls
@param string id: UUID for the subnet (CRUD: R)
@param string name: human readable name for the subnet,
may not be unique. (CRUD: CRU)
@param string tenant_id: owner of the network. (CRUD: CR)
@param string network_id: network subnet is associated with (CRUD: CR)
@param int ip_version: IP version 4 or 6 (CRUD: CR)
@param string cidr: represents IP range for the subnet and should be in
the form <network_address>/<prefix> (CRUD: CR)
@param string gateway_ip: default gateway used by devices in the subnet
(CRUD: CRUD)
@param list(str) dns_nameservers: DNS name servers used by subnet hosts
(CRUD: CRU)
@param list(dict) allocation_pools: sub range of cidr available for dynamic
allocation to ports (CRUD: CR)
@param list(dict) host_routes: routes that should be used by devices with
IPs from this subnet (does not includes the local route, CRUD: CRU)
@param bool enable_dhcp: whether DHCP is enabled (CRUD:CRU)
"""
SUBNET = 'subnet'
def __init__(self, id_=None, name=None, tenant_id=None, network_id=None,
ip_version=None, cidr=None, gateway_ip=None,
dns_nameservers=None, allocation_pools=None,
host_routes=None, enable_dhcp=None, **kwargs):
# kwargs is to be used for extensions
super(Subnet, self).__init__()
self.id = id_
self.name = name
self.tenant_id = tenant_id
self.network_id = network_id
self.ip_version = ip_version
self.cidr = cidr
self.gateway_ip = gateway_ip
self.dns_nameservers = dns_nameservers
self.allocation_pools = allocation_pools
self.host_routes = host_routes
self.enable_dhcp = enable_dhcp
@classmethod
def _json_to_obj(cls, serialized_str):
"""Return subnet object from a JSON serialized string"""
ret = None
json_response = json.loads(serialized_str)
# Creating a deep copy just in case later we want the original resp
json_dict = copy.deepcopy(json_response)
# Replacing attribute response names if they are Python reserved words
# with a trailing underscore, for ex. id for id_ or if they have a
# special character within the name replacing it for an underscore too
json_dict = cls._replace_dict_key(
json_dict, 'id', 'id_', recursion=True)
if cls.SUBNET in json_dict:
subnet_dict = json_dict.get(cls.SUBNET)
ret = Subnet(**subnet_dict)
return ret
class Subnets(AutoMarshallingListModel):
SUBNETS = 'subnets'
@classmethod
def _json_to_obj(cls, serialized_str):
"""Return a list of subnet objects from a JSON serialized string"""
ret = cls()
json_response = json.loads(serialized_str)
# Creating a deep copy just in case later we want the original resp
json_dict = copy.deepcopy(json_response)
# Replacing attribute response names if they are Python reserved words
# with a trailing underscore, for ex. id for id_ or if they have a
# special character within the name replacing it for an underscore too
json_dict = cls._replace_dict_key(
json_dict, 'id', 'id_', recursion=True)
if cls.SUBNETS in json_dict:
subnets = json_dict.get(cls.SUBNETS)
for subnet in subnets:
ret.append(Subnet(**subnet))
return ret

View File

@ -0,0 +1,146 @@
"""
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.
"""
from cloudcafe.auth.provider import MemoizedAuthServiceComposite
from cloudcafe.networking.networks.config import MarshallingConfig,\
NetworksConfig, NetworksEndpointConfig, NetworksAdminEndpointConfig,\
NetworksAdminAuthConfig, NetworksSecondUserConfig, \
NetworksAdminUserConfig, UserAuthConfig, UserConfig
from cloudcafe.networking.networks.networks_api.client import NetworksClient
from cloudcafe.networking.networks.networks_api.config import NetworksAPIConfig
from cloudcafe.networking.networks.networks_api.behaviors \
import NetworksAPIBehaviors
from cloudcafe.networking.networks.ports_api.client import PortsClient
from cloudcafe.networking.networks.ports_api.config import PortsAPIConfig
from cloudcafe.networking.networks.ports_api.behaviors import PortsAPIBehaviors
from cloudcafe.networking.networks.subnets_api.client import SubnetsClient
from cloudcafe.networking.networks.subnets_api.config import SubnetsAPIConfig
from cloudcafe.networking.networks.subnets_api.behaviors \
import SubnetsAPIBehaviors
class _NetworksAuthComposite(MemoizedAuthServiceComposite):
_networks_config = NetworksConfig
_networks_endpoint_config = NetworksEndpointConfig
_auth_endpoint_config = UserAuthConfig
_auth_user_config = UserConfig
def __init__(self):
self.networks_endpoint_config = self._networks_endpoint_config()
self.marshalling_config = MarshallingConfig()
self._auth_endpoint_config = self._auth_endpoint_config()
self._auth_user_config = self._auth_user_config()
super(_NetworksAuthComposite, self).__init__(
service_name=self.networks_endpoint_config.networks_endpoint_name,
region=self.networks_endpoint_config.region,
endpoint_config=self._auth_endpoint_config,
user_config=self._auth_user_config)
self.networks_url = self.public_url
# Overriding the publicURL if networks_endpoint_url given
if self.networks_endpoint_config.networks_endpoint_url:
self.networks_url = \
self.networks_endpoint_config.networks_endpoint_url
# Ending backslash is not expected, removing if present
if self.networks_url[-1] == '/':
self.networks_url = self.networks_url[:-1]
self.client_args = {
'url': self.networks_url,
'auth_token': self.token_id,
'serialize_format': self.marshalling_config.serializer,
'deserialize_format': self.marshalling_config.deserializer}
if self.networks_endpoint_config.header_tenant_id:
self.client_args.update(
tenant_id=self.networks_endpoint_config.header_tenant_id)
class _NetworksAdminAuthComposite(_NetworksAuthComposite):
_networks_endpoint_config = NetworksAdminEndpointConfig
_auth_endpoint_config = NetworksAdminAuthConfig
_auth_user_config = NetworksAdminUserConfig
class NetworksComposite(object):
networks_auth_composite = _NetworksAuthComposite
def __init__(self):
auth_composite = self.networks_auth_composite()
self.url = auth_composite.networks_url
self.user = auth_composite._auth_user_config
self.config = NetworksConfig()
self.networks = NetworksAPIComposite(auth_composite)
self.subnets = SubnetsAPIComposite(auth_composite)
self.ports = PortsAPIComposite(auth_composite)
self.networks.behaviors = self.networks.behavior_class(
networks_client=self.networks.client,
networks_config=self.networks.config,
subnets_client=self.subnets.client,
subnets_config=self.subnets.config,
ports_client=self.ports.client,
ports_config=self.ports.config)
self.subnets.behaviors = self.subnets.behavior_class(
subnets_client=self.subnets.client,
subnets_config=self.subnets.config,
networks_client=self.networks.client,
networks_config=self.networks.config,
ports_client=self.ports.client,
ports_config=self.ports.config)
self.ports.behaviors = self.ports.behavior_class(
ports_client=self.ports.client,
ports_config=self.ports.config,
networks_client=self.networks.client,
networks_config=self.networks.config,
subnets_client=self.subnets.client,
subnets_config=self.subnets.config)
class NetworksAdminComposite(NetworksComposite):
_auth_composite = _NetworksAdminAuthComposite
class NetworksAPIComposite(NetworksComposite):
behavior_class = NetworksAPIBehaviors
def __init__(self, auth_composite):
self.config = NetworksAPIConfig()
self.client = NetworksClient(**auth_composite.client_args)
self.behaviors = None
class SubnetsAPIComposite(NetworksComposite):
behavior_class = SubnetsAPIBehaviors
def __init__(self, auth_composite):
self.config = SubnetsAPIConfig()
self.client = SubnetsClient(**auth_composite.client_args)
self.behaviors = None
class PortsAPIComposite(NetworksComposite):
behavior_class = PortsAPIBehaviors
def __init__(self, auth_composite):
self.config = PortsAPIConfig()
self.client = PortsClient(**auth_composite.client_args)
self.behaviors = None

View File

@ -0,0 +1,90 @@
"""
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.
"""
from cloudcafe.auth.config import UserAuthConfig, UserConfig
from cloudcafe.common.models.configuration import ConfigSectionInterface
class MarshallingConfig(ConfigSectionInterface):
SECTION_NAME = 'marshalling'
@property
def serializer(self):
return self.get("serialize_format")
@property
def deserializer(self):
return self.get("deserialize_format")
class NetworksConfig(ConfigSectionInterface):
SECTION_NAME = 'networks'
@property
def public_network_id(self):
"""The uuid of the public network"""
return self.get("public_network_id",
"00000000-0000-0000-0000-000000000000")
@property
def service_network_id(self):
"""The uuid of the service network (aka private)"""
return self.get("service_network_id",
"11111111-1111-1111-1111-111111111111")
class NetworksEndpointConfig(ConfigSectionInterface):
SECTION_NAME = 'networks_endpoint'
@property
def region(self):
return self.get("region")
@property
def networks_endpoint_name(self):
return self.get("networks_endpoint_name")
@property
def networks_endpoint_url(self):
"""Optional override of the Networks url"""
return self.get("networks_endpoint_url")
@property
def header_tenant_id(self):
"""Optional tenant ID to set in client request headers"""
return self.get("header_tenant_id")
class NetworksAdminEndpointConfig(NetworksEndpointConfig):
"""RackerAdmin API endpoint and name"""
SECTION_NAME = 'networks_admin_endpoint'
class NetworksAdminAuthConfig(UserAuthConfig):
"""Networks Admin endpoint and auth strategy, for ex. keystone"""
SECTION_NAME = 'networks_admin_auth_config'
class NetworksSecondUserConfig(UserConfig):
SECTION_NAME = 'networks_secondary_user'
class NetworksAdminUserConfig(UserConfig):
SECTION_NAME = 'networks_admin_user'

View File

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

View File

@ -0,0 +1,30 @@
"""
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.
"""
from cafe.engine.behaviors import BaseBehavior
class NetworksAPIBehaviors(BaseBehavior):
def __init__(self, networks_client, networks_config, subnets_client,
subnets_config, ports_client, ports_config):
super(NetworksAPIBehaviors, self).__init__()
self.config = networks_config
self.client = networks_client
self.subnets_client = subnets_client
self.subnets_config = subnets_config
self.ports_client = ports_client
self.ports_config = ports_config

View File

@ -0,0 +1,137 @@
"""
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.
"""
from cafe.engine.http.client import AutoMarshallingHTTPClient
from cloudcafe.networking.networks.common.models.request.network \
import NetworkRequest
from cloudcafe.networking.networks.common.models.response.network \
import Network, Networks
class NetworksClient(AutoMarshallingHTTPClient):
def __init__(self, url, auth_token, serialize_format=None,
deserialize_format=None, tenant_id=None):
"""
@param string url: Base URL for the networks service
@param string auth_token: Auth token to be used for all requests
@param string serialize_format: Format for serializing requests
@param string deserialize_format: Format for de-serializing responses
@param string tenant_id: optional tenant id to be included in the
header if given
"""
super(NetworksClient, 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
def create_network(self, name=None, admin_state_up=None, shared=None,
tenant_id=None, requestslib_kwargs=None):
"""
@summary: Creates a Network
@param string name: human readable name for the network,
may not be unique. (CRUD: CRU)
@param bool admin_state_up: true or false, the admin state
of the network. If down, the network does not forward packets.
Default value is True (CRUD: CRU)
@param bool shared: specifies if the network can be accessed by any
tenant. Default value is False. (CRUD: CRU)
@param string tenant_id: owner of the network. (CRUD: CR)
"""
url = '{base_url}/networks'.format(base_url=self.url)
request = NetworkRequest(name=name, admin_state_up=admin_state_up,
shared=shared, tenant_id=tenant_id)
resp = self.request('POST', url,
response_entity_type=Network,
request_entity=request,
requestslib_kwargs=requestslib_kwargs)
return resp
def update_network(self, network_id, name=None, admin_state_up=None,
shared=None, tenant_id=None, requestslib_kwargs=None):
"""
@summary: Updates a specified Network
@param string network_id: The UUID for the network
@param string name: human readable name for the network,
may not be unique. (CRUD: CRU)
@param bool admin_state_up: true or false, the admin state
of the network. If down, the network does not forward packets.
Default value is True (CRUD: CRU)
@param bool shared: specifies if the network can be accessed by any
tenant. Default value is False. (CRUD: CRU)
@param string tenant_id: owner of the network. (CRUD: CR)
"""
url = '{base_url}/networks/{network_id}'.format(
base_url=self.url, network_id=network_id)
request = NetworkRequest(name=name, admin_state_up=admin_state_up,
shared=shared, tenant_id=tenant_id)
resp = self.request('PUT', url,
response_entity_type=Network,
request_entity=request,
requestslib_kwargs=requestslib_kwargs)
return resp
def get_network(self, network_id, requestslib_kwargs=None):
"""
@summary: Shows information for a specified network
@param string network_id: The UUID for the network
"""
url = '{base_url}/networks/{network_id}'.format(
base_url=self.url, network_id=network_id)
resp = self.request('GET', url,
response_entity_type=Network,
requestslib_kwargs=requestslib_kwargs)
return resp
def list_networks(self, requestslib_kwargs=None):
"""
@summary: Lists networks
"""
# TODO: add field query params to filter the response
url = '{base_url}/networks'.format(base_url=self.url)
resp = self.request('GET', url,
response_entity_type=Networks,
requestslib_kwargs=requestslib_kwargs)
return resp
def delete_network(self, network_id, requestslib_kwargs=None):
"""
@summary: Deletes a specified network and its associated resources
@param string network_id: The UUID for the network
"""
url = '{base_url}/networks/{network_id}'.format(
base_url=self.url, network_id=network_id)
resp = self.request('DELETE', url,
requestslib_kwargs=requestslib_kwargs)
return resp

View File

@ -0,0 +1,53 @@
"""
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.
"""
from cloudcafe.common.models.configuration import ConfigSectionInterface
class NetworksAPIConfig(ConfigSectionInterface):
"""Network is the resource"""
SECTION_NAME = 'networks_api'
@property
def resource_build_attempts(self):
"""Number of times to try to create a resource"""
return int(self.get("resource_build_attempts", 1))
@property
def keep_resources(self):
"""Flag for not deleting resources on tearDown"""
return self.get_boolean("keep_resources", False)
@property
def keep_resources_on_failure(self):
"""Flag for not deleting resources w failures on tearDown"""
return self.get_boolean("keep_resources_on_failure", False)
@property
def resource_create_timeout(self):
"""Seconds to wait for creating a resource"""
return int(self.get("resource_create_timeout", 15))
@property
def resource_delete_timeout(self):
"""Seconds to wait for deleting a resource"""
return int(self.get("resource_delete_timeout", 15))
@property
def resource_change_status_timeout(self):
"""Seconds to wait for a status change in the resource"""
return int(self.get("resource_change_status_timeout", 15))

View File

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

View File

@ -0,0 +1,30 @@
"""
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.
"""
from cafe.engine.behaviors import BaseBehavior
class PortsAPIBehaviors(BaseBehavior):
def __init__(self, ports_client, ports_config, networks_client,
networks_config, subnets_client, subnets_config):
super(PortsAPIBehaviors, self).__init__()
self.config = ports_config
self.client = ports_client
self.networks_client = networks_client
self.networks_config = networks_config
self.subnets_client = subnets_client
self.subnets_config = subnets_config

View File

@ -0,0 +1,155 @@
"""
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.
"""
from cafe.engine.http.client import AutoMarshallingHTTPClient
from cloudcafe.networking.networks.common.models.request.port \
import PortRequest
from cloudcafe.networking.networks.common.models.response.port \
import Port, Ports
class PortsClient(AutoMarshallingHTTPClient):
def __init__(self, url, auth_token, serialize_format=None,
deserialize_format=None, tenant_id=None):
"""
@param string url: Base URL for the ports service
@param string auth_token: Auth token to be used for all requests
@param string serialize_format: Format for serializing requests
@param string deserialize_format: Format for de-serializing responses
@param string tenant_id: optional tenant id to be included in the
header if given
"""
super(PortsClient, 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
def create_port(self, network_id, name=None, admin_state_up=None,
mac_address=None, fixed_ips=None, device_id=None,
device_owner=None, tenant_id=None, security_groups=None,
requestslib_kwargs=None):
"""
@summary: Creates a Port
@param string network_id: network port is associated with (CRUD: CR)
@param string name: human readable name for the port,
may not be unique. (CRUD: CRU)
@param bool admin_state_up: true or false (default true),
the admin state of the port. If down, the port does not forward
packets (CRUD: CRU)
@param string mac_address: mac address to use on the port (CRUD: CR)
@param list(dict) fixed_ips: ip addresses for the port associating the
port with the subnets where the IPs come from (CRUD: CRU)
@param string device_id: id of device using this port (CRUD: CRUD)
@param string device_owner: entity using this port (ex. dhcp agent,
CRUD: CRUD)
@param string tenant_id: owner of the port (CRUD: CR)
@param list(dict) security_groups: ids of any security groups
associated with the port (CRUD: CRUD)
"""
url = '{base_url}/ports'.format(base_url=self.url)
request = PortRequest(
network_id=network_id, name=name, admin_state_up=admin_state_up,
mac_address=mac_address, fixed_ips=fixed_ips, device_id=device_id,
device_owner=device_owner, tenant_id=tenant_id,
security_groups=security_groups)
resp = self.request('POST', url,
response_entity_type=Port,
request_entity=request,
requestslib_kwargs=requestslib_kwargs)
return resp
def update_port(self, port_id, name=None, admin_state_up=None,
fixed_ips=None, device_id=None, device_owner=None,
security_groups=None, requestslib_kwargs=None):
"""
@summary: Updates a specified Port
@param string port_id: The UUID for the port
@param string name: human readable name for the port,
may not be unique. (CRUD: CRU)
@param bool admin_state_up: true or false (default true),
the admin state of the port. If down, the port does not forward
packets (CRUD: CRU)
@param list(dict) fixed_ips: ip addresses for the port associating the
port with the subnets where the IPs come from (CRUD: CRU)
@param string device_id: id of device using this port (CRUD: CRUD)
@param string device_owner: entity using this port (ex. dhcp agent,
CRUD: CRUD)
@param list(dict) security_groups: ids of any security groups
associated with the port (CRUD: CRUD)
"""
url = '{base_url}/ports/{port_id}'.format(
base_url=self.url, port_id=port_id)
request = PortRequest(name=name, admin_state_up=admin_state_up,
fixed_ips=fixed_ips, device_id=device_id,
device_owner=device_owner, security_groups=security_groups)
resp = self.request('PUT', url,
response_entity_type=Port,
request_entity=request,
requestslib_kwargs=requestslib_kwargs)
return resp
def get_port(self, port_id, requestslib_kwargs=None):
"""
@summary: Shows information for a specified port
@param string port_id: The UUID for the port
"""
url = '{base_url}/ports/{port_id}'.format(
base_url=self.url, port_id=port_id)
resp = self.request('GET', url,
response_entity_type=Port,
requestslib_kwargs=requestslib_kwargs)
return resp
def list_ports(self, requestslib_kwargs=None):
"""
@summary: Lists ports
"""
# TODO: add field query params to filter the response
url = '{base_url}/ports'.format(base_url=self.url)
resp = self.request('GET', url,
response_entity_type=Ports,
requestslib_kwargs=requestslib_kwargs)
return resp
def delete_port(self, port_id, requestslib_kwargs=None):
"""
@summary: Deletes a specified port
@param string port_id: The UUID for the port
"""
url = '{base_url}/ports/{port_id}'.format(
base_url=self.url, port_id=port_id)
resp = self.request('DELETE', url,
requestslib_kwargs=requestslib_kwargs)
return resp

View File

@ -0,0 +1,53 @@
"""
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.
"""
from cloudcafe.common.models.configuration import ConfigSectionInterface
class PortsAPIConfig(ConfigSectionInterface):
"""Port is the resource"""
SECTION_NAME = 'ports_api'
@property
def resource_build_attempts(self):
"""Number of times to try to create a resource"""
return int(self.get("resource_build_attempts", 1))
@property
def keep_resources(self):
"""Flag for not deleting resources on tearDown"""
return self.get_boolean("keep_resources", False)
@property
def keep_resources_on_failure(self):
"""Flag for not deleting resources w failures on tearDown"""
return self.get_boolean("keep_resources_on_failure", False)
@property
def resource_create_timeout(self):
"""Seconds to wait for creating a resource"""
return int(self.get("resource_create_timeout", 15))
@property
def resource_delete_timeout(self):
"""Seconds to wait for deleting a resource"""
return int(self.get("resource_delete_timeout", 15))
@property
def resource_change_status_timeout(self):
"""Seconds to wait for a status change in the resource"""
return int(self.get("resource_change_status_timeout", 15))

View File

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

View File

@ -0,0 +1,30 @@
"""
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.
"""
from cafe.engine.behaviors import BaseBehavior
class SubnetsAPIBehaviors(BaseBehavior):
def __init__(self, subnets_client, subnets_config, networks_client,
networks_config, ports_client, ports_config):
super(SubnetsAPIBehaviors, self).__init__()
self.config = subnets_config
self.client = subnets_client
self.networks_client = networks_client
self.networks_config = networks_config
self.ports_client = ports_client
self.ports_config = ports_config

View File

@ -0,0 +1,157 @@
"""
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.
"""
from cafe.engine.http.client import AutoMarshallingHTTPClient
from cloudcafe.networking.networks.common.models.request.subnet \
import SubnetRequest
from cloudcafe.networking.networks.common.models.response.subnet \
import Subnet, Subnets
class SubnetsClient(AutoMarshallingHTTPClient):
def __init__(self, url, auth_token, serialize_format=None,
deserialize_format=None, tenant_id=None):
"""
@param string url: Base URL for the subnets service
@param string auth_token: Auth token to be used for all requests
@param string serialize_format: Format for serializing requests
@param string deserialize_format: Format for de-serializing responses
@param string tenant_id: optional tenant id to be included in the
header if given
"""
super(SubnetsClient, 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
def create_subnet(self, network_id, ip_version, cidr, name=None,
tenant_id=None, gateway_ip=None, dns_nameservers=None,
allocation_pools=None, host_routes=None,
enable_dhcp=None, requestslib_kwargs=None):
"""
@summary: Creates a Subnet
@param string name: human readable name for the subnet,
may not be unique. (CRUD: CRU)
@param string tenant_id: owner of the network. (CRUD: CR)
@param string network_id: network subnet is associated with (CRUD: CR)
@param int ip_version: IP version 4 or 6 (CRUD: CR)
@param string cidr: represents IP range for the subnet and should be in
the form <network_address>/<prefix> (CRUD: CR)
@param string gateway_ip: default gateway used by devices in the subnet
(CRUD: CRUD)
@param list(str) dns_nameservers: DNS name servers used by subnet hosts
(CRUD: CRU)
@param list(dict) allocation_pools: sub range of cidr available for
dynamic allocation to ports (CRUD: CR)
@param list(dict) host_routes: routes that should be used by devices
with IPs from this subnet (does not includes the local route,
CRUD: CRU)
@param bool enable_dhcp: whether DHCP is enabled (CRUD:CRU)
"""
url = '{base_url}/subnets'.format(base_url=self.url)
request = SubnetRequest(network_id=network_id, ip_version=ip_version,
cidr=cidr, name=name, tenant_id=tenant_id,
gateway_ip=gateway_ip,
dns_nameservers=dns_nameservers,
allocation_pools=allocation_pools,
host_routes=host_routes,
enable_dhcp=enable_dhcp)
resp = self.request('POST', url,
response_entity_type=Subnet,
request_entity=request,
requestslib_kwargs=requestslib_kwargs)
return resp
def update_subnet(self, subnet_id, name=None, gateway_ip=None,
dns_nameservers=None, host_routes=None,
enable_dhcp=None, requestslib_kwargs=None):
"""
@summary: Updates a specified Subnet
@param string subnet_id: The UUID for the subnet
@param string name: human readable name for the subnet,
may not be unique. (CRUD: CRU)
@param string gateway_ip: default gateway used by devices in the subnet
(CRUD: CRUD)
@param list(str) dns_nameservers: DNS name servers used by subnet hosts
(CRUD: CRU)
@param list(dict) host_routes: routes that should be used by devices
with IPs from this subnet (does not includes the local route,
CRUD: CRU)
@param bool enable_dhcp: whether DHCP is enabled (CRUD:CRU)
"""
url = '{base_url}/subnets/{subnet_id}'.format(
base_url=self.url, subnet_id=subnet_id)
request = SubnetRequest(name=name, gateway_ip=gateway_ip,
dns_nameservers=dns_nameservers,
host_routes=host_routes,
enable_dhcp=enable_dhcp)
resp = self.request('PUT', url,
response_entity_type=Subnet,
request_entity=request,
requestslib_kwargs=requestslib_kwargs)
return resp
def get_subnet(self, subnet_id, requestslib_kwargs=None):
"""
@summary: Shows information for a specified subnet
@param string subnet_id: The UUID for the subnet
"""
url = '{base_url}/subnets/{subnet_id}'.format(
base_url=self.url, subnet_id=subnet_id)
resp = self.request('GET', url,
response_entity_type=Subnet,
requestslib_kwargs=requestslib_kwargs)
return resp
def list_subnets(self, requestslib_kwargs=None):
"""
@summary: Lists subnets
"""
# TODO: add field query params to filter the response
url = '{base_url}/subnets'.format(base_url=self.url)
resp = self.request('GET', url,
response_entity_type=Subnets,
requestslib_kwargs=requestslib_kwargs)
return resp
def delete_subnet(self, subnet_id, requestslib_kwargs=None):
"""
@summary: Deletes a specified subnet
@param string subnet_id: The UUID for the subnet
"""
url = '{base_url}/subnets/{subnet_id}'.format(
base_url=self.url, subnet_id=subnet_id)
resp = self.request('DELETE', url,
requestslib_kwargs=requestslib_kwargs)
return resp

View File

@ -0,0 +1,53 @@
"""
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.
"""
from cloudcafe.common.models.configuration import ConfigSectionInterface
class SubnetsAPIConfig(ConfigSectionInterface):
"""Subnet is the resource"""
SECTION_NAME = 'subnets_api'
@property
def resource_build_attempts(self):
"""Number of times to try to create a resource"""
return int(self.get("resource_build_attempts", 1))
@property
def keep_resources(self):
"""Flag for not deleting resources on tearDown"""
return self.get_boolean("keep_resources", False)
@property
def keep_resources_on_failure(self):
"""Flag for not deleting resources w failures on tearDown"""
return self.get_boolean("keep_resources_on_failure", False)
@property
def resource_create_timeout(self):
"""Seconds to wait for creating a resource"""
return int(self.get("resource_create_timeout", 15))
@property
def resource_delete_timeout(self):
"""Seconds to wait for deleting a resource"""
return int(self.get("resource_delete_timeout", 15))
@property
def resource_change_status_timeout(self):
"""Seconds to wait for a status change in the resource"""
return int(self.get("resource_change_status_timeout", 15))

View File

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

View File

@ -0,0 +1,187 @@
"""
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 unittest
from cloudcafe.networking.networks.common.models.request.network \
import NetworkRequest
from cloudcafe.networking.networks.common.models.response.network \
import Network, Networks
NETWORK_TAG = Network.NETWORK
NETWORKS_TAG = Networks.NETWORKS
class CreateNetworkTest(unittest.TestCase):
"""Test for the Network Create (POST) Model object requests"""
@classmethod
def setUpClass(cls):
create_attrs = dict(
name='test_name_value', admin_state_up='test_admin_state_up_value',
shared='test_shared_value', tenant_id='test_tenant_id_value')
cls.network_model = NetworkRequest(**create_attrs)
def test_json_request(self):
"""JSON test with all possible create attrs"""
expected_json_output = (
'{{"{tag}": {{"shared": "test_shared_value", '
'"tenant_id": "test_tenant_id_value", "name": "test_name_value", '
'"admin_state_up": "test_admin_state_up_value"}}}}').format(
tag=NETWORK_TAG)
request_body = self.network_model._obj_to_json()
msg = ('Unexpected JSON Network request serialization. Expected {0} '
'instead of {1}'.format(expected_json_output, request_body))
self.assertEqual(request_body, expected_json_output, msg)
class UpdateNetworkTest(unittest.TestCase):
"""Test for the Network Update (PUT) Model object requests"""
@classmethod
def setUpClass(cls):
update_attrs = dict(
name='test_name_value', admin_state_up='test_admin_state_up_value',
shared='test_shared_value')
cls.network_model = NetworkRequest(**update_attrs)
def test_json_request(self):
"""JSON test with all possible update attrs"""
expected_json_output = (
'{{"{tag}": {{"shared": "test_shared_value", '
'"name": "test_name_value", '
'"admin_state_up": "test_admin_state_up_value"}}}}').format(
tag=NETWORK_TAG)
request_body = self.network_model._obj_to_json()
msg = ('Unexpected JSON Network request serialization. Expected {0} '
'instead of {1}'.format(expected_json_output, request_body))
self.assertEqual(request_body, expected_json_output, msg)
class ShowNetworkTest(unittest.TestCase):
"""Test for the Network Show (GET) Model object response"""
@classmethod
def setUpClass(cls):
"""Creating network_model with currently supported attributes"""
show_attrs = dict(
status='ACTIVE', subnets=['54d6f61d-db07-451c-9ab3-b9609b6b6f0b',
'79d6f61d-d007-51cd-9a33-b9609b6b6f0c'],
name='net1', admin_state_up=True,
tenant_id='9bacb3c5d39d41a79512987f338cf177', shared=False,
id_='4e8e5957-649f-477b-9e5b-f1f75b21c03c', router_external=True)
cls.expected_response = Network(**show_attrs)
def test_json_response(self):
# Response data with extension attributes, if supported later on they
# will need to be added to the setUp object model in this test class
api_json_resp = (
"""{{
"{tag}": {{
"status": "ACTIVE",
"subnets": [
"54d6f61d-db07-451c-9ab3-b9609b6b6f0b",
"79d6f61d-d007-51cd-9a33-b9609b6b6f0c"
],
"name": "net1",
"admin_state_up": true,
"tenant_id": "9bacb3c5d39d41a79512987f338cf177",
"segments": [
{{
"provider:segmentation_id": 2,
"provider:physical_network":
"8bab8453-1bc9-45af-8c70-f83aa9b50453",
"provider:network_type": "vlan"
}},
{{
"provider:segmentation_id": null,
"provider:physical_network":
"8bab8453-1bc9-45af-8c70-f83aa9b50453",
"provider:network_type": "stt"
}}
],
"shared": false,
"port_security_enabled": true,
"id": "4e8e5957-649f-477b-9e5b-f1f75b21c03c",
"router:external": true
}}
}}""").format(tag=NETWORK_TAG)
response_obj = Network()._json_to_obj(api_json_resp)
self.assertEqual(response_obj, self.expected_response,
'JSON to Obj response different than expected')
class ShowMultipleNetworksTest(unittest.TestCase):
"""Test for the Networks List (GET) Model object response"""
@classmethod
def setUpClass(cls):
"""Creating network_model with currently supported attributes"""
show_attrs_1 = dict(
status='ACTIVE', subnets=['54d6f61d-db07-451c-9ab3-b9609b6b6f0b'],
name='private-network', admin_state_up=True,
tenant_id='4fd44f30292945e481c7b8a0c8908869', shared=True,
id_='d32019d3-bc6e-4319-9c1d-6722fc136a22', router_external=True)
show_attrs_2 = dict(
status='ACTIVE', subnets=['08eae331-0402-425a-923c-34f7cfe39c1b'],
name='private', admin_state_up=True,
tenant_id='26a7980765d0414dbc1fc1f88cdb7e6e', shared=True,
id_='db193ab3-96e3-4cb3-8fc5-05f4296d0324', router_external=True)
net1 = Network(**show_attrs_1)
net2 = Network(**show_attrs_2)
cls.expected_response = [net1, net2]
def test_json_response(self):
# Response data with extension attributes, if supported later on they
# will need to be added to the setUp object model in this test class
api_json_resp = (
"""{{
"{tag}": [
{{
"status": "ACTIVE",
"subnets": [
"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
],
"name": "private-network",
"provider:physical_network": null,
"admin_state_up": true,
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
"provider:network_type": "local",
"router:external": true,
"shared": true,
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
"provider:segmentation_id": null
}},
{{
"status": "ACTIVE",
"subnets": [
"08eae331-0402-425a-923c-34f7cfe39c1b"
],
"name": "private",
"provider:physical_network": null,
"admin_state_up": true,
"tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
"provider:network_type": "local",
"router:external": true,
"shared": true,
"id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
"provider:segmentation_id": null
}}
]
}}""").format(tag=NETWORKS_TAG)
response_obj = Networks()._json_to_obj(api_json_resp)
self.assertEqual(response_obj, self.expected_response,
'JSON to Obj response different than expected')
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,268 @@
"""
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 unittest
from cloudcafe.networking.networks.common.models.request.port \
import PortRequest
from cloudcafe.networking.networks.common.models.response.port \
import Port, Ports
PORT_TAG = Port.PORT
PORTS_TAG = Ports.PORTS
class CreatePortTest(unittest.TestCase):
"""Test for the Port Create (POST) Model object requests"""
@classmethod
def setUpClass(cls):
create_attrs = dict(
network_id='a87cc70a-3e15-4acf-8205-9b711a3531b7',
name='private-port', admin_state_up=True)
cls.subnet_model = PortRequest(**create_attrs)
# With all possible create attributes
create_attrs_all = dict(
network_id='test_net_id', name='port_name', admin_state_up=False,
mac_address='fa:16:3e:c9:cb:f0', fixed_ips=[{'subnet_id':
'subnet_id_value', 'ip_address': 'ip_address_value'},
{'subnet_id2': 'subnet_id_value2', 'ip_address2':
'ip_address_value2'}], device_id='test_device_id',
device_owner='test_device_owner', tenant_id='test_tenant_id',
security_groups=[[{'key1': 'value1', 'key2': 'value2'}, {'key1b':
'value1b', 'key2b':'value2b'}]])
cls.subnet_model_all = PortRequest(**create_attrs_all)
def test_json_request(self):
"""JSON test with create attrs"""
expected_json_output = (
'{{"{tag}": {{"network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7"'
', "name": "private-port", "admin_state_up": true}}}}').format(
tag=PORT_TAG)
request_body = self.subnet_model._obj_to_json()
msg = ('Unexpected JSON Port request serialization. Expected {0} '
'instead of {1}'.format(expected_json_output, request_body))
self.assertEqual(request_body, expected_json_output, msg)
def test_json_request_all_attrs(self):
"""JSON test with all create attrs"""
expected_json_output = (
'{{"{tag}": {{"name": "port_name", "admin_state_up": false, '
'"network_id": "test_net_id", "tenant_id": "test_tenant_id", '
'"device_owner": "test_device_owner", "mac_address": '
'"fa:16:3e:c9:cb:f0", "fixed_ips": [{{"subnet_id": '
'"subnet_id_value", "ip_address": "ip_address_value"}}, '
'{{"subnet_id2": "subnet_id_value2", "ip_address2": '
'"ip_address_value2"}}], "security_groups": [[{{"key2": "value2", '
'"key1": "value1"}}, {{"key2b": "value2b", "key1b": "value1b"}}]],'
' "device_id": "test_device_id"}}}}').format(tag=PORT_TAG)
request_body = self.subnet_model_all._obj_to_json()
msg = ('Unexpected JSON Subnet request serialization. Expected {0} '
'instead of {1}'.format(expected_json_output, request_body))
self.assertEqual(request_body, expected_json_output, msg)
class UpdatePortTest(unittest.TestCase):
"""Test for the Port Update (PUT) Model object requests"""
@classmethod
def setUpClass(cls):
update_attrs = dict(
name='port_update_name', admin_state_up=True,
fixed_ips=[{'subnet_id': 'subnet_updated_id_value', 'ip_address':
'ip_address_updated_value'}, {'subnet_id2':
'subnet_id_updated_value2', 'ip_address2':
'ip_address_updated_value2'}],
device_id='updated_device_id', device_owner='updated_device_owner',
security_groups=[[{'key1': 'updated_value1', 'key2':
'updated_value2'}, {'key1b': 'updated_value1b', 'key2b':
'updated_value2b'}]])
cls.subnet_model = PortRequest(**update_attrs)
def test_json_request(self):
"""JSON test with all possible update attrs"""
expected_json_output = (
'{{"{tag}": {{"name": "port_update_name", "admin_state_up": true, '
'"device_owner": "updated_device_owner", "fixed_ips": '
'[{{"subnet_id": "subnet_updated_id_value", "ip_address": '
'"ip_address_updated_value"}}, {{"subnet_id2": '
'"subnet_id_updated_value2", "ip_address2": '
'"ip_address_updated_value2"}}], "security_groups": '
'[[{{"key2": "updated_value2", "key1": "updated_value1"}}, '
'{{"key2b": "updated_value2b", "key1b": "updated_value1b"}}]], '
'"device_id": "updated_device_id"}}}}').format(tag=PORT_TAG)
request_body = self.subnet_model._obj_to_json()
msg = ('Unexpected JSON Subnet request serialization. Expected {0} '
'instead of {1}'.format(expected_json_output, request_body))
self.assertEqual(request_body, expected_json_output, msg)
class ShowPortTest(unittest.TestCase):
"""Test for the Port Show (GET) Model object response"""
@classmethod
def setUpClass(cls):
"""Creating port_model with with extension included attributes"""
show_attrs = dict(
status="ACTIVE", binding_host_id="devstack", name="response_name",
allowed_address_pairs=[], admin_state_up=True,
network_id="a87cc70a-3e15-4acf-8205-9b711a3531b7",
tenant_id="7e02058126cc4950b75f9970368ba177",
extra_dhcp_opts=[], binding_vif_details={"port_filter": True,
"ovs_hybrid_plug": True}, binding_vif_type="ovs",
device_owner="network:router_interface",
mac_address="fa:16:3e:23:fd:d7", binding_profile={},
binding_vnic_type="normal", fixed_ips=[{"subnet_id":
"a0304c3a-4f08-4c43-88af-d796509c97d2", "ip_address": "10.0.0.1"}],
id_="46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", security_groups=[],
device_id="5e3898d7-11be-483e-9732-b2f5eccd2b2e")
cls.expected_response = Port(**show_attrs)
def test_json_response(self):
api_json_resp = (
"""{{
"{tag}": {{
"status": "ACTIVE",
"binding:host_id": "devstack",
"name": "response_name",
"allowed_address_pairs": [],
"admin_state_up": true,
"network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
"tenant_id": "7e02058126cc4950b75f9970368ba177",
"extra_dhcp_opts": [],
"binding:vif_details": {{
"port_filter": true,
"ovs_hybrid_plug": true
}},
"binding:vif_type": "ovs",
"device_owner": "network:router_interface",
"mac_address": "fa:16:3e:23:fd:d7",
"binding:profile": {{}},
"binding:vnic_type": "normal",
"fixed_ips": [
{{
"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
"ip_address": "10.0.0.1"
}}
],
"id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2",
"security_groups": [],
"device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e"
}}
}}""").format(tag=PORT_TAG)
response_obj = Port()._json_to_obj(api_json_resp)
self.assertEqual(response_obj, self.expected_response,
'JSON to Obj response different than expected')
class ShowMultiplePortTest(unittest.TestCase):
"""Test for the Port List (GET) Model object response"""
@classmethod
def setUpClass(cls):
"""Creating port_model with currently supported attributes"""
show_attrs_1 = dict(
status='ACTIVE', name='', admin_state_up=True,
network_id='70c1db1f-b701-45bd-96e0-a313ee3430b3', tenant_id='',
mac_address='fa:16:3e:58:42:ed',
device_owner='network:router_gateway',
fixed_ips=[{u'subnet_id': u'008ba151-0b8c-4a67-98b5-0d2b87666062',
u'ip_address': u'172.24.4.2'}],
id_='d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b', security_groups=[],
device_id='9ae135f4-b6e0-4dad-9e91-3c223e385824')
show_attrs_2 = dict(
status='ACTIVE', name='', admin_state_up=True,
network_id='f27aa545-cbdd-4907-b0c6-c9e8b039dcc2',
tenant_id='d397de8a63f341818f198abb0966f6f3',
mac_address='fa:16:3e:bb:3c:e4',
device_owner='network:router_interface', fixed_ips=[{u'subnet_id':
u'288bf4a1-51ba-43b6-9d0a-520e9005db17', u'ip_address':
u'10.0.0.1'}], id_='f71a6703-d6de-4be1-a91a-a570ede1d159',
security_groups=[],
device_id='9ae135f4-b6e0-4dad-9e91-3c223e385824',)
sub1 = Port(**show_attrs_1)
sub2 = Port(**show_attrs_2)
cls.expected_response = [sub1, sub2]
def test_json_response(self):
# Response data with extension attributes, if supported later on they
# will need to be added to the setUp object model in this test class
api_json_resp = (
"""{{
"{tag}": [
{{
"status": "ACTIVE",
"binding:host_id": "devstack",
"name": "",
"allowed_address_pairs": [],
"admin_state_up": true,
"network_id": "70c1db1f-b701-45bd-96e0-a313ee3430b3",
"tenant_id": "",
"extra_dhcp_opts": [],
"binding:vif_details": {{
"port_filter": true,
"ovs_hybrid_plug": true
}},
"binding:vif_type": "ovs",
"device_owner": "network:router_gateway",
"mac_address": "fa:16:3e:58:42:ed",
"binding:profile": {{}},
"binding:vnic_type": "normal",
"fixed_ips": [
{{
"subnet_id": "008ba151-0b8c-4a67-98b5-0d2b87666062",
"ip_address": "172.24.4.2"
}}
],
"id": "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
"security_groups": [],
"device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824"
}},
{{
"status": "ACTIVE",
"binding:host_id": "devstack",
"name": "",
"allowed_address_pairs": [],
"admin_state_up": true,
"network_id": "f27aa545-cbdd-4907-b0c6-c9e8b039dcc2",
"tenant_id": "d397de8a63f341818f198abb0966f6f3",
"extra_dhcp_opts": [],
"binding:vif_details": {{
"port_filter": true,
"ovs_hybrid_plug": true
}},
"binding:vif_type": "ovs",
"device_owner": "network:router_interface",
"mac_address": "fa:16:3e:bb:3c:e4",
"binding:profile": {{}},
"binding:vnic_type": "normal",
"fixed_ips": [
{{
"subnet_id": "288bf4a1-51ba-43b6-9d0a-520e9005db17",
"ip_address": "10.0.0.1"
}}
],
"id": "f71a6703-d6de-4be1-a91a-a570ede1d159",
"security_groups": [],
"device_id": "9ae135f4-b6e0-4dad-9e91-3c223e385824"
}}
]
}}""").format(tag=PORTS_TAG)
response_obj = Ports()._json_to_obj(api_json_resp)
self.assertEqual(response_obj, self.expected_response,
'JSON to Obj response different than expected')
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,223 @@
"""
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 unittest
from cloudcafe.networking.networks.common.models.request.subnet \
import SubnetRequest
from cloudcafe.networking.networks.common.models.response.subnet \
import Subnet, Subnets
SUBNET_TAG = Subnet.SUBNET
SUBNETS_TAG = Subnets.SUBNETS
class CreateSubnetTest(unittest.TestCase):
"""Test for the Subnet Create (POST) Model object requests"""
@classmethod
def setUpClass(cls):
create_attrs = dict(
network_id='d32019d3-bc6e-4319-9c1d-6722fc136a22',
ip_version=4, cidr='192.168.199.0/24')
cls.subnet_model = SubnetRequest(**create_attrs)
# With all possible create attributes
create_attrs_all = dict(
name='test_subnet_name', tenant_id='test_tenant_id',
network_id='test_network_id', ip_version=6, cidr='test_cidr',
gateway_ip='test_gateway_ip', dns_nameservers=['test_dnsname1',
'test_dnsname2', 'test_dnsname3'], allocation_pools=[{'start':
'start_ip', 'end': 'end_ip'}, {'start2': 'start_ip2', 'end2':
'end_ip2'}, {'start3': 'start_ip3', 'end3': 'end_ip3'}],
host_routes=[{'route1_key': 'route1_value', 'route1_key2':
'route1_value2'}, {'route2_key': 'route2_value', 'route2_key2':
'route2_value2'}], enable_dhcp=True)
cls.subnet_model_all = SubnetRequest(**create_attrs_all)
def test_json_request(self):
"""JSON test with create attrs"""
expected_json_output = (
'{{"{tag}": {{"network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"'
', "ip_version": 4, "cidr": "192.168.199.0/24"}}}}').format(
tag=SUBNET_TAG)
request_body = self.subnet_model._obj_to_json()
msg = ('Unexpected JSON Subnet request serialization. Expected {0} '
'instead of {1}'.format(expected_json_output, request_body))
self.assertEqual(request_body, expected_json_output, msg)
def test_json_request_all_attrs(self):
"""JSON test with all create attrs"""
expected_json_output = (
'{{"{tag}": {{"name": "test_subnet_name", "enable_dhcp": true, '
'"network_id": "test_network_id", "tenant_id": "test_tenant_id", '
'"dns_nameservers": ["test_dnsname1", "test_dnsname2", '
'"test_dnsname3"], "allocation_pools": [{{"start": "start_ip", '
'"end": "end_ip"}}, {{"start2": "start_ip2", "end2": "end_ip2"}}, '
'{{"start3": "start_ip3", "end3": "end_ip3"}}], "gateway_ip": '
'"test_gateway_ip", "ip_version": 6, "host_routes": '
'[{{"route1_key2": "route1_value2", "route1_key": "route1_value"}}'
', {{"route2_key2": "route2_value2", "route2_key": "route2_value"'
'}}], "cidr": "test_cidr"}}}}').format(tag=SUBNET_TAG)
request_body = self.subnet_model_all._obj_to_json()
msg = ('Unexpected JSON Subnet request serialization. Expected {0} '
'instead of {1}'.format(expected_json_output, request_body))
self.assertEqual(request_body, expected_json_output, msg)
class UpdateSubnetTest(unittest.TestCase):
"""Test for the Subnet Update (PUT) Model object requests"""
@classmethod
def setUpClass(cls):
update_attrs = dict(
name='test_subnet_name', gateway_ip='test_gateway_ip',
dns_nameservers=['test_dnsname1', 'test_dnsname2',
'test_dnsname3'], host_routes=[{'route1_key': 'route1_value',
'route1_key2': 'route1_value2'}, {'route2_key': 'route2_value',
'route2_key2': 'route2_value2'}], enable_dhcp=True)
cls.subnet_model = SubnetRequest(**update_attrs)
def test_json_request(self):
"""JSON test with all possible update attrs"""
expected_json_output = (
'{{"{tag}": {{"gateway_ip": "test_gateway_ip", "host_routes": '
'[{{"route1_key2": "route1_value2", "route1_key": "route1_value"}}'
', {{"route2_key2": "route2_value2", "route2_key": "route2_value"'
'}}], "name": "test_subnet_name", "enable_dhcp": true, '
'"dns_nameservers": ["test_dnsname1", "test_dnsname2", '
'"test_dnsname3"]}}}}').format(tag=SUBNET_TAG)
request_body = self.subnet_model._obj_to_json()
msg = ('Unexpected JSON Subnet request serialization. Expected {0} '
'instead of {1}'.format(expected_json_output, request_body))
self.assertEqual(request_body, expected_json_output, msg)
class ShowSubnetTest(unittest.TestCase):
"""Test for the Subnet Show (GET) Model object response"""
@classmethod
def setUpClass(cls):
"""Creating subnet_model with currently supported attributes"""
show_attrs = dict(
name='my_subnet', enable_dhcp=True,
network_id='d32019d3-bc6e-4319-9c1d-6722fc136a22',
tenant_id='4fd44f30292945e481c7b8a0c8908869', dns_nameservers=[],
allocation_pools=[{u'start': u'192.0.0.2', u'end':
u'192.255.255.254'}], gateway_ip='192.0.0.1', ip_version=4,
host_routes=[], cidr='192.0.0.0/8',
id_='54d6f61d-db07-451c-9ab3-b9609b6b6f0b')
cls.expected_response = Subnet(**show_attrs)
def test_json_response(self):
api_json_resp = (
"""{{
"{tag}": {{
"name": "my_subnet",
"enable_dhcp": true,
"network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
"dns_nameservers": [],
"allocation_pools": [
{{
"start": "192.0.0.2",
"end": "192.255.255.254"
}}
],
"host_routes": [],
"ip_version": 4,
"gateway_ip": "192.0.0.1",
"cidr": "192.0.0.0/8",
"id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
}}
}}""").format(tag=SUBNET_TAG)
response_obj = Subnet()._json_to_obj(api_json_resp)
self.assertEqual(response_obj, self.expected_response,
'JSON to Obj response different than expected')
class ShowMultipleSubnetTest(unittest.TestCase):
"""Test for the Subnet List (GET) Model object response"""
@classmethod
def setUpClass(cls):
"""Creating subnet_model with currently supported attributes"""
show_attrs_1 = dict(
name='private-subnet', enable_dhcp=True,
network_id='db193ab3-96e3-4cb3-8fc5-05f4296d0324',
tenant_id='26a7980765d0414dbc1fc1f88cdb7e6e', dns_nameservers=[],
allocation_pools=[{u'start': u'10.0.0.2', u'end': u'10.0.0.254'}],
gateway_ip='10.0.0.1', ip_version=4, host_routes=[],
cidr='10.0.0.0/24', id_='08eae331-0402-425a-923c-34f7cfe39c1b')
show_attrs_2 = dict(
name='my_subnet', enable_dhcp=True,
network_id='d32019d3-bc6e-4319-9c1d-6722fc136a22',
tenant_id='4fd44f30292945e481c7b8a0c8908869', dns_nameservers=[],
allocation_pools=[{u'start': u'192.0.0.2', u'end':
u'192.255.255.254'}], gateway_ip='192.0.0.1', ip_version=4,
host_routes=[], cidr='192.0.0.0/8',
id_='54d6f61d-db07-451c-9ab3-b9609b6b6f0b')
sub1 = Subnet(**show_attrs_1)
sub2 = Subnet(**show_attrs_2)
cls.expected_response = [sub1, sub2]
def test_json_response(self):
# Response data with extension attributes, if supported later on they
# will need to be added to the setUp object model in this test class
api_json_resp = (
"""{{
"{tag}": [
{{
"name": "private-subnet",
"enable_dhcp": true,
"network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
"tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
"dns_nameservers": [],
"allocation_pools": [
{{
"start": "10.0.0.2",
"end": "10.0.0.254"
}}
],
"host_routes": [],
"ip_version": 4,
"gateway_ip": "10.0.0.1",
"cidr": "10.0.0.0/24",
"id": "08eae331-0402-425a-923c-34f7cfe39c1b"
}},
{{
"name": "my_subnet",
"enable_dhcp": true,
"network_id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
"dns_nameservers": [],
"allocation_pools": [
{{
"start": "192.0.0.2",
"end": "192.255.255.254"
}}
],
"host_routes": [],
"ip_version": 4,
"gateway_ip": "192.0.0.1",
"cidr": "192.0.0.0/8",
"id": "54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
}}
]
}}""").format(tag=SUBNETS_TAG)
response_obj = Subnets()._json_to_obj(api_json_resp)
self.assertEqual(response_obj, self.expected_response,
'JSON to Obj response different than expected')
if __name__ == "__main__":
unittest.main()