Networking Behavior Compute Methods

- Minor updates on get_networking_server behavior method
- Adding compute wrapper behavior methods: list_server,
list_server_ids, delete_servers, create_keypair, get_keypair,
list_keypairs, and delete_keypairs
- Adding create_multiple_servers behavior method
- Removing create_servers_in_same_cell behavior method.
Functionality now available in create_multiple_servers
- Adding compute response code constants
- Adding server, keypair and missing data exceptions

Change-Id: I17e0503da7055c0193ed148f6ac39c0fb61b60e0
This commit is contained in:
Leonardo Maycotte 2016-06-27 15:59:23 -05:00
parent c2b148b4c0
commit 01252995ca
3 changed files with 353 additions and 52 deletions

View File

@ -16,6 +16,7 @@ limitations under the License.
import time
from cloudcafe.common.tools.datagen import rand_name
from cloudcafe.compute.common.exceptions import TimeoutException
from cloudcafe.compute.composites import ComputeComposite
from cloudcafe.networking.networks.common.behaviors \
@ -25,7 +26,9 @@ from cloudcafe.networking.networks.common.constants \
import ComputeResponseCodes, ComputeStatus, NeutronResponseCodes
from cloudcafe.networking.networks.common.exceptions \
import NetworkGETException, SubnetGETException, UnsupportedTypeException, \
UnavailableComputeInteractionException, UnableToGetNetworkingServer
UnavailableComputeInteractionException, UnableToCreateKeypair, \
UnableToDeleteKeypair, UnableToDeleteServer, UnableToGetKeypairs, \
UnableToGetNetworkingServer, MissingDataException
from cloudcafe.networking.networks.common.models.response.network \
import Network
from cloudcafe.networking.networks.common.models.response.port \
@ -56,6 +59,9 @@ class NetworkingBehaviors(NetworkingBaseBehaviors):
else:
self.compute = None
self.error_msg = ('{message}: {status} {reason} {content}. '
'Expected status code {expected_status}')
def get_port_fixed_ips(self, port):
"""Get the port fixed ips"""
if hasattr(port, 'fixed_ips') and port.fixed_ips:
@ -327,24 +333,203 @@ 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):
def get_networking_server(self, server=None, server_id=None,
raise_exception=False):
"""
@summary: takes a server entity or ID and returns its latest version
"""
if self.compute is None:
raise UnavailableComputeInteractionException
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)
net_server_id = getattr(server, 'id', None) or server_id
msg = 'Getting server {0}'.format(net_server_id)
self._log.info(msg)
resp = self.compute.servers.client.get_server(net_server_id)
resp_check = self.check_response(
resp=resp, status_code=ComputeResponseCodes.GET_SERVER,
label=net_server_id, message='Server GET failure')
if resp_check and raise_exception:
raise UnableToGetNetworkingServer(resp_check)
if hasattr(server, 'admin_pass'):
resp.entity.admin_pass = server.admin_pass
return resp.entity
def list_servers(self, name=None, raise_exception=False):
"""
@summary: list servers wrapper for networks
@param name: name or name_starts_with* to filter by
@type name: str
@param raise_exception: flag to raise an exception if get fails
@type raise_exception: bool
@return: server entities list
@rtype: list
"""
if self.compute is None:
raise UnavailableComputeInteractionException
self._log.info('Getting servers')
resp = self.compute.servers.client.list_servers()
resp_check = self.check_response(
resp=resp, status_code=ComputeResponseCodes.GET_SERVER,
label='Servers', message='LIST failure')
if resp_check and raise_exception:
raise UnableToGetNetworkingServer(resp_check)
server_list = resp.entity
if name and server_list:
server_list = self.filter_entity_list_by_name(
entity_list=server_list, name=name)
return server_list
def list_server_ids(self, name=None, raise_exception=False):
"""
@summary: gets a server id list from a server entities list
@param name: name or name_starts_with* to filter by
@type name: str
@param raise_exception: flag to raise an exception if get fails
@type raise_exception: bool
@return: server id list
@rtype: list(str)
"""
server_list = self.list_servers(name=name,
raise_exception=raise_exception)
server_ids = self.get_id_list_from_entity_list(entity_list=server_list)
return server_ids
def delete_servers(self, name=None, raise_exception=False):
"""
@summary: deletes servers (all or filtered by name) without waiting
@param name: name or name_starts_with* to filter by
@type name: str
@param raise_exception: flag to raise an exception if delete fails
@type raise_exception: bool
"""
if self.compute is None:
raise UnavailableComputeInteractionException
server_ids = self.list_server_ids(name=name)
log_msg = 'Deleting servers: {0}'.format(server_ids)
self._log.info(log_msg)
for server_id in server_ids:
resp = self.compute.servers.client.delete_server(
server_id=server_id)
resp_check = self.check_response(
resp=resp, status_code=ComputeResponseCodes.DELETE_SERVER,
label=server_id, message='Server DELETE failure')
if resp_check and raise_exception:
raise UnableToDeleteServer(resp_check)
def create_keypair(self, name='networks_keypair', raise_exception=False):
"""
@summary: create keypair wrapper for networks
@param name: starts with keypair name
@type name: str
@param raise_exception: flag to raise an exception if create fails
@type raise_exception: bool
@return: keypair
@rtype: keypair entity object
"""
if self.compute is None:
raise UnavailableComputeInteractionException
# Using rand_name to avoid HTTP 409 Conflict due to duplicate names
name = rand_name(name)
log_msg = 'Creating test server keypair: {0}'.format(name)
self._log.info(log_msg)
resp = self.compute.keypairs.client.create_keypair(name)
resp_check = self.check_response(
resp=resp, status_code=ComputeResponseCodes.CREATE_KEYPAIR,
label=name, message='Keypair POST failure')
if resp_check and raise_exception:
raise UnableToCreateKeypair(resp_check)
return resp.entity
def get_keypair(self, name, raise_exception=False):
"""
@summary: get keypair wrapper for networks
@param name: keypair name
@type name: str
@param raise_exception: flag to raise an exception if get fails
@type raise_exception: bool
@return: keypair
@rtype: keypair entity object
"""
if self.compute is None:
raise UnavailableComputeInteractionException
log_msg = 'Getting keypair: {0}'.format(name)
self._log.info(log_msg)
resp = self.compute.keypairs.client.get_keypair(name)
resp_check = self.check_response(
resp=resp, status_code=ComputeResponseCodes.GET_KEYPAIR,
label=name, message='Keypair GET failure')
if resp_check and raise_exception:
raise UnableToGetKeypairs(resp_check)
return resp.entity
def list_keypairs(self, name=None, raise_exception=False):
"""
@summary: gets the keypairs (all or filtered by name)
@param name: name or name_starts_with* to filter by
@type name: str
@param raise_exception: flag to raise an exception if get fails
@type raise_exception: bool
@return: keypair list
@rtype: list
"""
if self.compute is None:
raise UnavailableComputeInteractionException
self._log.info('Getting keypairs')
resp = self.compute.keypairs.client.list_keypairs()
resp_check = self.check_response(
resp=resp, status_code=ComputeResponseCodes.LIST_KEYPAIRS,
label='Keypairs', message='LIST failure')
if resp_check and raise_exception:
raise UnableToGetKeypairs(resp_check)
keypair_list = resp.entity
if name and keypair_list:
keypair_list = self.filter_entity_list_by_name(
entity_list=keypair_list, name=name)
return keypair_list
def delete_keypairs(self, name=None, raise_exception=False):
"""
@summary: deletes the keypairs (all or filtered by name)
@param name: name or name_starts_with* to filter by
@type name: str
@param raise_exception: flag to raise an exception if delete fails
@type raise_exception: bool
"""
if self.compute is None:
raise UnavailableComputeInteractionException
keypair_list = self.list_keypairs(name=name)
log_msg = 'Deleting keypairs: {0}'.format(keypair_list)
self._log.info(log_msg)
if keypair_list:
for key in keypair_list:
resp = self.compute.keypairs.client.delete_keypair(key.name)
resp_check = self.check_response(
resp=resp, status_code=ComputeResponseCodes.DELETE_KEYPAIR,
label=key.name, message='Keypair DELETE failure')
if resp_check and raise_exception:
raise UnableToDeleteKeypair(resp_check)
def wait_for_servers_to_be_active(self, server_id_list,
interval_time=None, timeout=None,
raise_exception=False):
@ -365,7 +550,7 @@ class NetworkingBehaviors(NetworkingBaseBehaviors):
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
if (resp.status_code == ComputeResponseCodes.GET_SERVER and
resp.entity.status == ComputeStatus.ACTIVE and
server_id in server_id_list):
server_id_list.remove(server_id)
@ -447,52 +632,144 @@ class NetworkingBehaviors(NetworkingBaseBehaviors):
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,
ip_zone_hint=None):
def create_multiple_servers(self, create_by='names', names=None,
images=None, flavors=None,
keypair_name=None, networks=None, ports=None,
pnet=True, snet=True, use_ip_zone_hint=False,
ip_zone_hint=None, ip_zone_hint_id=None,
raise_exception=False):
"""
@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
@summary: Create multiple test servers with the same keypairs and
networks given a list of names, image ids or flavor ids.
@param create_by: type of list to create servers from
(names, images or flavors)
@type create_by: str
@param names: list of names to create servers from
@type names: list(str)
@param images: image ids to create servers from
@type images: list(str)
@param flavors: list of flavor ids to create servers from
@type flavors: list(str)
@param keypair_name: (optional) keypair to create servers with
@type keypair_name: str
@param networks: (optional) isolated network ids to create servers with
@type networks: list(str)
@param ports: (optional) isolated ports ids to create servers with
@type ports: list(str)
@param pnet: flag to create server with public network
@type pnet: bool
@param snet: flag to create server with service (private) network
@type snet: bool
@param use_ip_zone_hint: flag to use scheduler hints with ip zone hint
WARNING if using with create by flavors, make sure the flavors
are within the same cell type.
@type use_ip_zone_hint: bool
@param ip_zone_hint: scheduler ip zone hint, for ex.
public_ip_zone:near, public_ip_zone:far, different_host, etc.
@type ip_zone_hint: str
@param ip_zone_hint_id: id to use for the ip_zone_hint, for ex.
server id or a public ip zone id
@type ip_zone_hint_id: str
@param raise_exception: flag to raise an exception if the wait for
active servers times out
@type raise_exception: bool
@return: server entity objects
@rtype: dict(server name: server entity object)
"""
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
NAMES_CREATE_BY_TYPE = 'names'
IMAGES_CREATE_BY_TYPE = 'images'
FLAVORS_CREATE_BY_TYPE = 'flavors'
CREATE_TYPES = [NAMES_CREATE_BY_TYPE, IMAGES_CREATE_BY_TYPE,
FLAVORS_CREATE_BY_TYPE]
# Setting up the scheduler hints for creating other servers
pz = ip_zone_hint or 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)
# Verify the create_by is withing the expected create types
if create_by not in CREATE_TYPES:
msg = 'Unsupported create_by: {0}. Expecting: {1}'.format(
create_by, CREATE_TYPES)
raise UnsupportedTypeException(msg)
# Waiting for the servers to get into Active state
self.wait_for_servers_to_be_active(server_id_list=server_id_list)
# Verifying the expected name or image id or flavor id list is given
create_by_list = None
if create_by == NAMES_CREATE_BY_TYPE and names:
create_by_list = names
elif create_by == IMAGES_CREATE_BY_TYPE and images:
create_by_list = images
elif create_by == FLAVORS_CREATE_BY_TYPE and flavors:
create_by_list = flavors
else:
msg = 'Missing {0} list'.format(create_by)
raise MissingDataException(msg)
# Getting and returning the active server entity objects
servers_list = [self.get_networking_server(svr)
for svr in created_servers]
if type(create_by_list) != list:
msg = 'Expecting a {0} list instead of: {1}'.format(create_by,
create_by_list)
raise UnsupportedTypeException(msg)
return servers_list
server_count = len(create_by_list)
create_by_msg = 'Creating {0} servers from {1}: {2}'.format(
server_count, create_by, create_by_list)
self._log.info(create_by_msg)
self._log.info('Defining the network IDs to be used')
network_ids = []
if pnet:
network_ids.append(self.networks_config.public_network_id)
if snet:
network_ids.append(self.networks_config.service_network_id)
if networks:
network_ids.extend(networks)
# Removing duplicates if any (in case networks had pnet or snet too)
network_ids = list(set(network_ids))
net_ids_msg = 'Creating servers with networks: {0}'.format(network_ids)
self._log.info(net_ids_msg)
# Setting the ip_zone_hint if the use_ip_zone_hint set to True
scheduler_hints = None
if use_ip_zone_hint:
ip_zone_hint = ip_zone_hint or (
IPAddressesServerZone.PUBLIC_IP_ZONE_NEAR)
# Setting the scheduler hints if the ip_zone_hint_id given
if ip_zone_hint_id:
scheduler_hints = {ip_zone_hint: ip_zone_hint_id}
# Response dict where the key will be the server name and the value the
# server entity object
servers = dict()
server_ids = []
for n, param in enumerate(create_by_list):
server_name = '{0}_{1}_svr_{2}'.format(create_by, param, n+1)
server_kwargs = dict(name=server_name, key_name=keypair_name,
network_ids=network_ids, port_ids=ports,
active_server=False)
if create_by == IMAGES_CREATE_BY_TYPE:
server_kwargs.update(image_ref=param)
if create_by == FLAVORS_CREATE_BY_TYPE:
server_kwargs.update(flavor_ref=param)
if use_ip_zone_hint and scheduler_hints:
server_kwargs.update(scheduler_hints=scheduler_hints)
resp = self.create_networking_server(**server_kwargs)
server = resp.entity
server_ids.append(server.id)
servers[server_name] = server
# Setting the scheduler hints with the first server ID if the
# ip_zone_hint_id was not given and the use_ip_zone_hint was set
if use_ip_zone_hint and not scheduler_hints and n < 1:
scheduler_hints = {ip_zone_hint: server.id}
# Waiting for the servers to be active
self.wait_for_servers_to_be_active(server_id_list=server_ids,
raise_exception=raise_exception)
# Updating the servers dict with the latest server entities
# Hopefully in active status with all expected data
for server_name, server in servers.items():
updated_server = self.get_networking_server(server=server)
servers[server_name] = updated_server
return servers

View File

@ -110,8 +110,12 @@ class ComputeResponseCodes(object):
"""HTTP Compute API Response codes"""
NOT_FOUND = 404
SERVER_GET = 200
GET_SERVER = 200
DELETE_SERVER = 204
CREATE_KEYPAIR = 200
GET_KEYPAIR = 200
LIST_KEYPAIRS = 200
DELETE_KEYPAIR = 204
class ComputeStatus(NovaServerStatusTypes):

View File

@ -35,6 +35,22 @@ class UnableToGetNetworkingServer(BaseNetworkingException):
MSG = 'Unable to GET nova server used for networking'
class UnableToDeleteServer(BaseNetworkingException):
MSG = 'Unable to DELETE nova server used for networking'
class UnableToCreateKeypair(BaseNetworkingException):
MSG = 'Unable to create keypair used for networking'
class UnableToGetKeypairs(BaseNetworkingException):
MSG = 'Unable to GET keypairs used for networking'
class UnableToDeleteKeypair(BaseNetworkingException):
MSG = 'Unable to DELETE keypairs used for networking'
class NetworkIDMissingException(BaseNetworkingException):
MSG = 'Network ID is required'
@ -81,3 +97,7 @@ class UnsupportedTypeException(BaseNetworkingException):
class UnhandledMethodCaseException(BaseNetworkingException):
MSG = 'Unhandled Case'
class MissingDataException(BaseNetworkingException):
MSG = 'Missing expected data'