Merge "Infrastructure updates for Shared IPs testing"

This commit is contained in:
Jenkins
2015-11-04 19:01:33 +00:00
committed by Gerrit Code Review
10 changed files with 561 additions and 24 deletions

View File

@@ -16,20 +16,22 @@ limitations under the License.
import time
from cloudcafe.compute.common.exceptions import ItemNotFound, TimeoutException
from cloudcafe.compute.common.exceptions import TimeoutException
from cloudcafe.compute.composites import ComputeComposite
from cloudcafe.networking.networks.common.behaviors \
import NetworkingBaseBehaviors
from cloudcafe.networking.networks.common.config import NetworkingBaseConfig
from cloudcafe.networking.networks.common.constants \
import ComputeResponseCodes, NeutronResponseCodes
import ComputeResponseCodes, ComputeStatus, NeutronResponseCodes
from cloudcafe.networking.networks.common.exceptions \
import NetworkGETException, SubnetGETException, UnsupportedTypeException, \
UnavailableComputeInteractionException
UnavailableComputeInteractionException, UnableToGetNetworkingServer
from cloudcafe.networking.networks.common.models.response.network \
import Network
from cloudcafe.networking.networks.common.models.response.port \
import Port
from cloudcafe.networking.networks.extensions.ip_addresses_api.constants \
import IPAddressesServerZone
class NetworkingBehaviors(NetworkingBaseBehaviors):
@@ -159,9 +161,9 @@ class NetworkingBehaviors(NetworkingBaseBehaviors):
subnet_id=subnet_id)
err_msg = 'Subnet Get failure'
resp_check = self.check_response(resp=resp,
status_code=NeutronResponseCodes.GET_SUBNET, label=subnet_id,
message=err_msg, network_id=network_id)
resp_check = self.check_response(
resp=resp, status_code=NeutronResponseCodes.GET_SUBNET,
label=subnet_id, message=err_msg, network_id=network_id)
if not resp_check:
subnet = resp.entity
@@ -209,8 +211,9 @@ class NetworkingBehaviors(NetworkingBaseBehaviors):
log_msg = ('Checking {resource} entity type initial {init_status} '
'status is updated to {updated_status} status within a '
'timeout of {timeout}').format(resource=resource_type,
init_status=initial_status, updated_status=new_status,
timeout=timeout)
init_status=initial_status,
updated_status=new_status,
timeout=timeout)
self._log.info(log_msg)
while time.time() < endtime:
@@ -324,6 +327,58 @@ class NetworkingBehaviors(NetworkingBaseBehaviors):
# The compute behavior verifies the response, no need to check
return resp
def get_networking_server(self, server=None, server_id=None):
"""
@summary: takes a server entity or ID and returns its latest version
"""
if server is None and server_id is None:
raise UnableToGetNetworkingServer('Must provide server entity '
'or server_id')
net_server_id = getattr(server, 'id', None) or server_id
resp = self.compute.servers.client.get_server(net_server_id)
if resp.status_code != ComputeResponseCodes.SERVER_GET:
msg = 'Unable to get networks server {0}'.format(net_server_id)
raise UnableToGetNetworkingServer(msg)
if hasattr(server, 'admin_pass'):
resp.entity.admin_pass = server.admin_pass
return resp.entity
def wait_for_servers_to_be_active(self, server_id_list,
interval_time=None, timeout=None,
raise_exception=False):
"""
@summary: Waits for multiple servers to be ACTIVE
@param server_id_list: The uuids of the servers to wait for ACTIVE
@type server_id_list: List
@param interval_time: Seconds to wait between polling
@type interval_time: Integer
@param timeout: The amount of time in seconds to wait before aborting
@type timeout: Integer
"""
interval_time = (interval_time or
self.compute.servers.config.server_status_interval)
timeout = timeout or self.compute.servers.config.server_build_timeout
end_time = time.time() + timeout
while time.time() < end_time:
for server_id in server_id_list:
resp = self.compute.servers.client.get_server(server_id)
if (resp.status_code == ComputeResponseCodes.SERVER_GET and
resp.entity.status == ComputeStatus.ACTIVE
and server_id in server_id_list):
server_id_list.remove(server_id)
if not server_id_list:
break
time.sleep(interval_time)
else:
msg = ('wait_for_servers_to_be_active {0} seconds timeout waiting'
'for servers: {1}').format(timeout, server_id_list)
self._log.info(msg)
if raise_exception:
raise TimeoutException(msg)
def wait_for_servers_to_be_deleted(self, server_id_list,
interval_time=None, timeout=None,
raise_exception=False):
@@ -337,29 +392,106 @@ class NetworkingBehaviors(NetworkingBaseBehaviors):
@type timeout: Integer
"""
self._wait_for_compute_delete(
resource_id_list=server_id_list, resource='servers',
delete_method=self.compute.servers.client.delete_server,
get_method=self.compute.servers.client.get_server,
interval_time=interval_time, timeout=timeout,
raise_exception=raise_exception)
def _wait_for_compute_delete(self, resource_id_list, resource,
delete_method, get_method, interval_time=None,
timeout=None, raise_exception=False):
"""
@summary: Waits for compute resource deletes
@param resource_id_list: uuids of the compute resources to be deleted
@type resource_id_list: List
@param resource: compute resource like servers, ip associations, etc.
@type resource: String
@param delete_method: method used to delete the resource
@type delete_method: behavior or client method
@param get_method: method used to get the resource
@type get_method: behavior or client method
@param interval_time: Seconds to wait between polling
@type interval_time: Integer
@param timeout: The amount of time in seconds to wait before aborting
@type timeout: Integer
@param raise_exception: flag to raise an exception if deletes fail
@type raise_exception: bool
"""
interval_time = (interval_time or
self.compute.servers.config.server_status_interval)
timeout = timeout or self.compute.servers.config.server_build_timeout
end_time = time.time() + timeout
for server_id in server_id_list:
self.compute.servers.client.delete_server(server_id)
for resource_id in resource_id_list:
delete_method(resource_id)
while time.time() < end_time:
for server_id in server_id_list:
resp = self.compute.servers.client.get_server(server_id)
for resource_id in resource_id_list:
resp = get_method(resource_id)
if (resp.status_code == ComputeResponseCodes.NOT_FOUND
and server_id in server_id_list):
server_id_list.remove(server_id)
if not server_id_list:
and resource_id in resource_id_list):
resource_id_list.remove(resource_id)
if not resource_id_list:
break
time.sleep(interval_time)
else:
msg = ('wait_for_servers_to_be_deleted {0} seconds timeout waiting'
'for the expected get_server HTTP {1} status code for '
'servers: {2}').format(timeout,
ComputeResponseCodes.NOT_FOUND,
server_id_list)
msg = ('Wait for compute {0} resource delete {0} seconds timeout '
'for the expected resource get HTTP {1} status code for '
'resources: {2}').format(resource, timeout,
ComputeResponseCodes.NOT_FOUND,
resource_id_list)
self._log.info(msg)
if raise_exception:
raise TimeoutException(msg)
def create_servers_in_same_cell(self, n_servers, name='same_cell_server',
network_ids=None, port_ids=None):
"""
@summary: Creates n servers in same cell using the first server ID with
scheduler hints
@param n_servers: number of servers to create
@type n_servers: int
@param name: servers name (n will be appended at the end)
@type name: str
@param network_ids: server network ids, for ex. public, private, etc.
@type network_ids: list
@param port_ids: server network port ids from an isolated network
@type port_ids: list
@return: server entity list
@rtype: list
"""
msg = 'Creating {0} servers in same cell...'.format(n_servers)
self._log.info(msg)
created_servers = []
server_id_list = []
for n_server in range(n_servers):
server_name = ''.join([name, str(n_server)])
if n_server < 1:
resp = self.create_networking_server(
name=server_name, network_ids=network_ids,
port_ids=port_ids, active_server=False)
server = resp.entity
# Setting up the scheduler hints for creating other servers
pz = IPAddressesServerZone.PUBLIC_IP_ZONE_NEAR
scheduler_hints = {pz: server.id}
else:
resp = self.create_networking_server(
name=server_name, network_ids=network_ids,
port_ids=port_ids, scheduler_hints=scheduler_hints,
active_server=False)
server = resp.entity
created_servers.append(server)
server_id_list.append(server.id)
# Waiting for the servers to get into Active state
self.wait_for_servers_to_be_active(server_id_list=server_id_list)
# Getting and returning the active server entity objects
servers_list = [self.get_networking_server(svr)
for svr in created_servers]
return servers_list

View File

@@ -13,8 +13,6 @@ 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
@@ -40,6 +38,14 @@ class NetworkingBaseConfig(ConfigSectionInterface):
"""
return self.get_boolean("use_compute_api", False)
@property
def device_owner(self):
"""
Port device owner attribute expected value when assigned to a
server instance
"""
return self.get("device_owner", "compute:None")
@property
def keep_servers(self):
"""Flag for not deleting servers on tearDown"""
@@ -50,6 +56,11 @@ class NetworkingBaseConfig(ConfigSectionInterface):
"""Flag for not deleting servers w failures on tearDown"""
return self.get_boolean("keep_servers_on_failure", False)
@property
def keep_ip_associations(self):
"""Flag for not deleting ip_associations on tearDown"""
return self.get_boolean("keep_ip_associations", False)
@property
def api_poll_interval(self):
"""Time interval for api calls on while loops retries"""

View File

@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
"""
from cloudcafe.compute.common.types import NovaServerStatusTypes
class NeutronResource(object):
"""Neutron resource types"""
@@ -35,6 +37,20 @@ class NeutronResource(object):
return self.PLURALS.get(self.singular, self.singular)
class NetworkTypes(object):
"""Network types"""
PUBLIC = 'public'
SERVICE = 'service'
ISOLATED = 'isolated'
class PortTypes(object):
"""Port types"""
PUBLIC = 'pnet'
SERVICE = 'snet'
ISOLATED = 'inet'
class NeutronResponseCodes(object):
"""HTTP Neutron API Response codes"""
@@ -85,9 +101,16 @@ class NeutronErrorTypes(object):
PORT_NOT_FOUND = 'PortNotFound'
SECURITY_GROUPS_NOT_IMPLEMENTED = 'SecurityGroupsNotImplemented'
SUBNET_NOT_FOUND = 'SubnetNotFound'
TENANT_NETWORK_SECURITY_GROUP_RULES_NOT_ENABLED = (
'TenantNetworkSecurityGroupRulesNotEnabled')
class ComputeResponseCodes(object):
"""HTTP Compute API Response codes"""
NOT_FOUND = 404
SERVER_GET = 200
class ComputeStatus(NovaServerStatusTypes):
"""Compute server instance status"""

View File

@@ -31,6 +31,10 @@ class UnavailableComputeInteractionException(BaseNetworkingException):
'compute_endpoint_name at the compute_endpoint section')
class UnableToGetNetworkingServer(BaseNetworkingException):
MSG = 'Unable to GET nova server used for networking'
class NetworkIDMissingException(BaseNetworkingException):
MSG = 'Network ID is required'

View File

@@ -222,6 +222,14 @@ class IPAddressesBehaviors(NetworkingBaseBehaviors):
@return: failed deletes list with IP Address IDs and failures
@rtype: list(dict)
"""
# If IP address list not given, deleting all shared IPs
if not ip_address_list:
resp = self.list_ip_addresses(type_='shared', raise_exception=True)
shared_ips = resp.response.entity
ip_address_list = self.get_id_list_from_entity_list(
entity_list=shared_ips)
result = self._delete_resources(
resource=self.ip_address_resource,
resource_list=ip_address_list,

View File

@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
"""
import json
from cloudcafe.networking.networks.common.config import NetworkingBaseConfig
@@ -21,3 +23,17 @@ class IPAddressesConfig(NetworkingBaseConfig):
"""IP Addresses configuration parameters"""
SECTION_NAME = 'ip_addresses'
@property
def scheduler_hints(self):
"""
JSON string containing the os:scheduler_hints for server boot, for ex.
For targeting the same cell of the server ID given,
{"public_ip_zone:near": [serverID]}
For targeting another cell of the server ID given,
{"public_ip_zone:far": [serverID]}
For targeting another host in the same cell of the server ID given,
{"different_host": [serverID]}
"""
result = self.get("scheduler_hints", "{}")
return json.loads(result)

View File

@@ -33,7 +33,9 @@ class IPAddressesResponseCodes(NeutronResponseCodes):
LIST_IP_ADDRESSES = 200
GET_IP_ADDRESS = 200
CREATE_IP_ADDRESS = 201
# Using HTTP 200 instead of 201 till NCP-1577 is fixed
CREATE_IP_ADDRESS = 200
UPDATE_IP_ADDRESS = 200
DELETE_IP_ADDRESS = 204
@@ -42,3 +44,14 @@ class IPAddressesErrorTypes(NeutronErrorTypes):
"""IP Address Error Types"""
IP_ADDRESS_NOT_FOUND = 'IPAddressNotFound'
class IPAddressesServerZone(object):
"""
Scheduler hint keys for targeting the same or different cell/host for
server builds
"""
PUBLIC_IP_ZONE_NEAR = 'public_ip_zone:near'
PUBLIC_IP_ZONE_FAR = 'public_ip_zone:far'
DIFFERENT_HOST = 'different_host'

View File

@@ -0,0 +1,313 @@
"""
Copyright 2015 Rackspace
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from cafe.engine.models.base import BaseModel
from cloudcafe.networking.networks.common.behaviors \
import NetworkingBaseBehaviors
from cloudcafe.networking.networks.common.constants \
import NetworkTypes, PortTypes
from cloudcafe.networking.networks.composites import NetworkingComposite
class ServerPersona(BaseModel, NetworkingBaseBehaviors):
"""
@summary: Server data object for quick access to networking data and also
tracking expected counts for ports and fixed IPs
"""
def __init__(self, server=None, pnet=True, snet=True, inet=False,
network=None, subnetv4=None, portv4=None, subnetv6=None,
portv6=None, inet_port_count=0, snet_port_count=1,
pnet_port_count=1, inet_fix_ipv4_count=0,
inet_fix_ipv6_count=0, snet_fix_ipv4_count=1,
snet_fix_ipv6_count=0, pnet_fix_ipv4_count=1,
pnet_fix_ipv6_count=1):
super(ServerPersona, self).__init__()
"""
@param server: server entity
@type server: compute.servers_api.models.servers.Server
@param pnet: if the server has public network flag
@type pnet: bool
@param snet: if the server has service (private) network flag
@type snet: bool
@param inet: if the server has isolated network flag
@type inet: bool
@param network: (optional) isolated network entity if applies
@type network: networking.networks.common.models.response
.network.Network
@param subnetv4: (optional) isolated network IPv4 subnet if applies
@type subnetv4: networking.networks.common.models.response
.subnet.Subnet
@param portv4: (optional) isolated network IPv4 port if applies
@type portv4: networking.networks.common.models.response.port.Port
@param subnetv6: (optional) isolated network IPv6 subnet if applies
@type subnetv6: networking.networks.common.models.response
.subnet.Subnet
@param portv6: (optional) isolated network IPv6 port if applies
@type portv6: networking.networks.common.models.response.port.Port
@param inet_port_count: expected isolated network port count
@type inet_port_count: int
@param snet_port_count: expected service (private) network port count
@type snet_port_count: int
@param pnet_port_count: expected public network port count
@type pnet_port_count: int
@param inet_fix_ipv4_count: expected isolated network fixed IPv4s count
@type inet_fix_ipv4_count: int
@param inet_fix_ipv6_count: expected isolated network fixed IPv6s count
@type inet_fix_ipv6_count: int
@param snet_fix_ipv4_count: expected service network fixed IPv4s count
@type snet_fix_ipv4_count: int
@param snet_fix_ipv6_count: expected service network fixed IPv6s count
@type snet_fix_ipv6_count: int
@param pnet_fix_ipv4_count: expected public network fixed IPv4s count
@type pnet_fix_ipv4_count: int
@param pnet_fix_ipv6_count: expected public network fixed IPv6s count
@type pnet_fix_ipv6_count: int
"""
# Server entity object
self.server = server
# Server expected networks (bool value)
self.pnet = pnet
self.snet = snet
self.inet = inet
# Server isolated network, subnet and port (entity objects, if any)
self.network = network
self.subnetv4 = subnetv4
self.portv4 = portv4
self.subnetv6 = subnetv6
self.portv6 = portv6
# Expected server port count by network
self.inet_port_count = inet_port_count
self.snet_port_count = snet_port_count
self.pnet_port_count = pnet_port_count
# Expected server Fixed IP address count by network and version
self.inet_fix_ipv4_count = inet_fix_ipv4_count
self.inet_fix_ipv6_count = inet_fix_ipv6_count
self.snet_fix_ipv4_count = snet_fix_ipv4_count
self.snet_fix_ipv6_count = snet_fix_ipv6_count
self.pnet_fix_ipv4_count = pnet_fix_ipv4_count
self.pnet_fix_ipv6_count = pnet_fix_ipv6_count
# Networking composite
self.net = NetworkingComposite()
# base config from networking/networks/common/config.py
self.config = self.net.config
# sub-composites
self.networks = self.net.networks
self.subnets = self.net.subnets
self.ports = self.net.ports
self.behaviors = self.net.behaviors
# Other reusable values (service_network_id aka Private Network)
self.public_network_id = self.networks.config.public_network_id
self.service_network_id = self.networks.config.service_network_id
self.isolated_network_id = getattr(self.network, 'id', None)
# Error list for behavior method calls
self.errors = []
self.list_port_failure_msg = ('Unable to get server {0} ports for '
'network {1}. Failures: {2}.')
self.fixed_ips_failure_msg = ('Unable to get server {0} fixed IPs '
'with IPv{1} version for network {2}')
@property
def pnet_ports(self):
"""
@summary: server public network ports
@return: public network ports
@rtype: list of port entity objects
"""
return self._port_response(network_type=NetworkTypes.PUBLIC)
@property
def snet_ports(self):
"""
@summary: server service (private) network ports
@return: private network ports
@rtype: list of port entity objects
"""
return self._port_response(network_type=NetworkTypes.SERVICE)
@property
def inet_ports(self):
"""
@summary: server isolated network ports
@return: isolated network ports
@rtype: list of port entity objects
"""
return self._port_response(network_type=NetworkTypes.ISOLATED)
@property
def inet_fix_ipv4(self):
"""
@summary: updates server attribute with latest isolated fixed IPs
@return: isolated network fixed IPv4 addresses
@rtype: list of strings
"""
return self._get_fixed_ips(ip_version=4, port_type=PortTypes.ISOLATED,
network_type=NetworkTypes.ISOLATED)
@property
def inet_fix_ipv6(self):
"""
@summary: updates server attribute with latest isolated fixed IPs
@return: isolated network fixed IPv6 addresses
@rtype: list of strings
"""
return self._get_fixed_ips(ip_version=6, port_type=PortTypes.ISOLATED,
network_type=NetworkTypes.ISOLATED)
@property
def snet_fix_ipv4(self):
"""
@summary: updates server attribute with latest private fixed IPs
@return: service (private) network fixed IPv4 addresses
@rtype: list of strings
"""
return self._get_fixed_ips(ip_version=4, port_type=PortTypes.SERVICE,
network_type=NetworkTypes.SERVICE)
@property
def snet_fix_ipv6(self):
"""
@summary: updates server attribute with latest private fixed IPs
@return: service (private) network fixed IPv6 addresses
@rtype: list of strings
"""
return self._get_fixed_ips(ip_version=6, port_type=PortTypes.SERVICE,
network_type=NetworkTypes.SERVICE)
@property
def pnet_fix_ipv4(self):
"""
@summary: updates server attribute with latest public fixed IPs
@return: public network fixed IPv4 addresses
@rtype: list of strings
"""
return self._get_fixed_ips(ip_version=4, port_type=PortTypes.PUBLIC,
network_type=NetworkTypes.PUBLIC)
@property
def pnet_fix_ipv6(self):
"""
@summary: updates server attribute with latest public fixed IPs
@return: public network fixed IPv6 addresses
@rtype: list of strings
"""
return self._get_fixed_ips(ip_version=6, port_type=PortTypes.PUBLIC,
network_type=NetworkTypes.PUBLIC)
@property
def pnet_port_ids(self):
"""
@summary: gets the public network port ids
"""
return self._get_port_ids(port_type=PortTypes.PUBLIC)
@property
def snet_port_ids(self):
"""
@summary: gets the service (private) network port ids
"""
return self._get_port_ids(port_type=PortTypes.SERVICE)
@property
def inet_port_ids(self):
"""
@summary: gets the public network port ids
"""
return self._get_port_ids(port_type=PortTypes.ISOLATED)
def update_server_persona(self, clear_errors=True):
"""
@summary: updates the self.server entity doing a GET server call
"""
self.server = self.behaviors.get_networking_server(server=self.server)
self.isolated_network_id = getattr(self.network, 'id', None)
if clear_errors:
self.errors = []
def _port_response(self, network_type):
"""
@summary: returns server network ports based on network type
@param network_type: public, service or isolated network type
@param network_type: str
"""
network_id_label = '{0}_network_id'.format(network_type.lower())
network_id = getattr(self, network_id_label, None)
if not network_id:
return []
ports = self.ports.behaviors.list_ports(device_id=self.server.id,
network_id=network_id)
if ports.failures:
msg = self.list_port_failure_msg.format(self.server.id, network_id,
ports.failures)
self.errors.append(msg)
self._log.error(msg)
return []
return ports.response.entity
def _get_fixed_ips(self, ip_version, port_type, network_type):
"""
@summary: gets fixed IPs from server ports
@param ip_version: 4 or 6 depending on the port IP version
@type ip_version: int
@param port_type: pnet, snet or inet port type
@type port_type: str
@param network_type: public, service or isolated network type
@param network_type: str
@return: fixed IP addresses
@rtype: list of strings
"""
port_attr = '{0}_ports'.format(port_type)
network_id_label = '{0}_network_id'.format(network_type.lower())
network_id = getattr(self, network_id_label, None)
ports = getattr(self, port_attr, [])
result = []
for port in ports:
addresses = self.ports.behaviors.get_addresses_from_fixed_ips(
fixed_ips=port.fixed_ips, ip_version=ip_version)
result.extend(addresses)
if not result:
msg = self.fixed_ips_failure_msg.format(
self.server.id, ip_version, network_id)
self.errors.append(msg)
self._log.error(msg)
return result
def _get_port_ids(self, port_type):
"""
@summary: gets the port ids
@param port_type: pnet, snet or inet port type
@type port_type: str
@return: port IDs
@rtype: list(str)
"""
port_attr = '{0}_ports'.format(port_type.lower())
ports = getattr(self, port_attr, [])
port_ids = [port.id for port in ports]
return port_ids

View File

@@ -50,6 +50,23 @@ class PortsBehaviors(NetworkingBaseBehaviors):
results['subnet_ids'].append(fixed_ip['subnet_id'])
return results
def get_addresses_from_fixed_ips(self, fixed_ips, ip_version=4):
"""
@summary: gets the IP addresses from the port fixed IPs attribute
@param fixed_ips: list of fixed_ips
@type fixed_ips: list(dict)
@param version: IP version of addresses to get
@type version: int
@return: IP addresses from fixed IPs
@rtype: list(str)
"""
result = []
for fixed_ip in fixed_ips:
ip = netaddr.IPAddress(fixed_ip['ip_address'])
if ip.version == ip_version:
result.append(str(ip))
return result
def format_fixed_ips(self, fixed_ips):
"""
@summary: formats fixed ips for assertions removing zeros on

View File

@@ -45,7 +45,7 @@ class PortsConfig(NetworkingBaseConfig):
@property
def fixed_ips_per_port(self):
"""Ports fixed IPs quota"""
return int(self.get("fixed_ips_per_port", 5))
return int(self.get("fixed_ips_per_port", 6))
@property
def api_poll_interval(self):