94621238d7
Removed '--is_public' from args_dict inside create_flavor since it is not a valid argument for openstack create flavor, it is just used to add either '--public' or '--private' to the comand. Story: 2007472 Task: 39164 Patch Set 2: added Signed off by added Story added Task Change-Id: I16cc0d4000ab9313c65a7fa76c66678b4af65956 Signed-off-by: George Postolache <george.postolache@intel.com>
1310 lines
44 KiB
Python
Executable File
1310 lines
44 KiB
Python
Executable File
#
|
|
# Copyright (c) 2019 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
|
|
|
|
from utils import cli, exceptions
|
|
from utils import table_parser
|
|
from utils.tis_log import LOG
|
|
from consts.proj_vars import ProjVar
|
|
from consts.auth import Tenant
|
|
from consts.stx import FlavorSpec, GuestImages
|
|
from keywords import common
|
|
from testfixtures.fixture_resources import ResourceCleanup
|
|
|
|
|
|
def create_flavor(name=None, flavor_id=None, vcpus=1, ram=1024, root_disk=None,
|
|
ephemeral=None, swap=None,
|
|
is_public=None, rxtx_factor=None, project=None,
|
|
project_domain=None, description=None, guest_os=None,
|
|
fail_ok=False, auth_info=Tenant.get('admin'), con_ssh=None,
|
|
storage_backing=None,
|
|
rtn_id=True, cleanup=None, add_default_specs=True,
|
|
properties=None):
|
|
"""
|
|
Create a flavor with given criteria.
|
|
|
|
Args:
|
|
name (str): substring of flavor name. Whole name will be
|
|
<name>-<auto_count>. e,g., 'myflavor-1'. If None, name
|
|
will be set to 'flavor'.
|
|
flavor_id (str): auto generated by default unless specified.
|
|
vcpus (int):
|
|
ram (int):
|
|
root_disk (int):
|
|
ephemeral (int):
|
|
swap (int|None):
|
|
is_public (bool):
|
|
rxtx_factor (str):
|
|
project
|
|
project_domain
|
|
description
|
|
guest_os (str|None): guest name such as 'tis-centos-guest' or None -
|
|
default tis guest assumed
|
|
fail_ok (bool): whether it's okay to fail to create a flavor. Default
|
|
to False.
|
|
auth_info (dict): This is set to Admin by default. Can be set to
|
|
other tenant for negative test.
|
|
con_ssh (SSHClient):
|
|
storage_backing (str): storage backing in extra flavor. Auto set
|
|
storage backing based on system config if None.
|
|
Valid values: 'local_image', 'remote'
|
|
rtn_id (bool): return id or name
|
|
cleanup (str|None): cleanup scope. function, class, module, or session
|
|
add_default_specs (False): Whether to automatically add extra specs
|
|
that are needed to launch vm
|
|
properties (str|list|dict)
|
|
|
|
Returns (tuple): (rtn_code (int), flavor_id/err_msg (str))
|
|
(0, <flavor_id/name>): flavor created successfully
|
|
(1, <stderr>): create flavor cli rejected
|
|
|
|
"""
|
|
|
|
table_ = table_parser.table(
|
|
cli.openstack('flavor list', ssh_client=con_ssh, auth_info=auth_info)[
|
|
1])
|
|
existing_names = table_parser.get_column(table_, 'Name')
|
|
|
|
if name is None:
|
|
name = 'flavor'
|
|
flavor_name = common.get_unique_name(name_str=name,
|
|
existing_names=existing_names,
|
|
resource_type='flavor')
|
|
|
|
if root_disk is None:
|
|
if not guest_os:
|
|
guest_os = GuestImages.DEFAULT['guest']
|
|
root_disk = GuestImages.IMAGE_FILES[guest_os][1]
|
|
|
|
args_dict = {
|
|
'--ephemeral': ephemeral,
|
|
'--swap': swap,
|
|
'--rxtx-factor': rxtx_factor,
|
|
'--disk': root_disk,
|
|
'--ram': ram,
|
|
'--vcpus': vcpus,
|
|
'--id': flavor_id,
|
|
'--project': project,
|
|
'--project-domain': project_domain,
|
|
'--description': description,
|
|
'--public': True if is_public else None,
|
|
'--private': True if is_public is False else None,
|
|
'--property': properties,
|
|
}
|
|
args = '{} {}'.format(common.parse_args(args_dict, repeat_arg=True),
|
|
flavor_name)
|
|
|
|
LOG.info("Creating flavor {}...".format(flavor_name))
|
|
LOG.info("openstack flavor create option: {}".format(args))
|
|
exit_code, output = cli.openstack('flavor create', args, ssh_client=con_ssh,
|
|
fail_ok=fail_ok, auth_info=auth_info)
|
|
if exit_code > 1:
|
|
return 1, output
|
|
|
|
table_ = table_parser.table(output)
|
|
flavor_id = table_parser.get_value_two_col_table(table_, 'id')
|
|
LOG.info("Flavor {} created successfully.".format(flavor_name))
|
|
|
|
if cleanup:
|
|
ResourceCleanup.add('flavor', flavor_id, scope=cleanup)
|
|
|
|
if add_default_specs:
|
|
extra_specs = {FlavorSpec.MEM_PAGE_SIZE: '2048'}
|
|
# extra_specs = {FlavorSpec.MEM_PAGE_SIZE: 'small'}
|
|
default_flavor_backing = ProjVar.get_var('DEFAULT_INSTANCE_BACKING')
|
|
sys_inst_backing = ProjVar.get_var('INSTANCE_BACKING')
|
|
if not default_flavor_backing:
|
|
from keywords import host_helper
|
|
sys_inst_backing = host_helper.get_hosts_per_storage_backing(
|
|
up_only=False, auth_info=auth_info,
|
|
con_ssh=con_ssh, refresh=True)
|
|
configured_backings = [backing for backing in sys_inst_backing if
|
|
sys_inst_backing.get(backing)]
|
|
LOG.debug(
|
|
"configured backing:{} sys inst backing: {}, required storage "
|
|
"backing: {}".
|
|
format(configured_backings, sys_inst_backing, storage_backing))
|
|
|
|
if storage_backing and storage_backing not in configured_backings:
|
|
raise ValueError(
|
|
'Required local_storage {} is not configured on any nova '
|
|
'hypervisor'.
|
|
format(storage_backing))
|
|
|
|
if len(configured_backings) > 1:
|
|
extra_specs[
|
|
FlavorSpec.STORAGE_BACKING] = storage_backing if \
|
|
storage_backing else \
|
|
ProjVar.get_var('DEFAULT_INSTANCE_BACKING')
|
|
|
|
if extra_specs:
|
|
LOG.info("Setting flavor specs: {}".format(extra_specs))
|
|
set_flavor(flavor_id, con_ssh=con_ssh, auth_info=auth_info,
|
|
**extra_specs)
|
|
|
|
flavor = flavor_id if rtn_id else flavor_name
|
|
return 0, flavor, storage_backing
|
|
|
|
|
|
def set_aggregate(aggregate, properties=None, no_property=None, zone=None,
|
|
name=None, fail_ok=False, con_ssh=None,
|
|
auth_info=Tenant.get('admin')):
|
|
"""
|
|
Set aggregate with given params
|
|
Args:
|
|
aggregate (str): aggregate to set
|
|
properties (dict|None):
|
|
no_property (bool|None):
|
|
zone (str|None):
|
|
name (str|None):
|
|
fail_ok (bool):
|
|
con_ssh:
|
|
auth_info:
|
|
|
|
Returns (tuple):
|
|
(0, "Aggregate <aggregate> set successfully with param: <params>)
|
|
(1, <std_err>) returns only if fail_ok=True
|
|
|
|
"""
|
|
args_dict = {
|
|
'--zone': zone,
|
|
'--name': name,
|
|
'--property': properties,
|
|
'--no-property': no_property,
|
|
}
|
|
|
|
args = '{} {}'.format(common.parse_args(args_dict, repeat_arg=True),
|
|
aggregate)
|
|
code, output = cli.openstack('aggregate set', args, ssh_client=con_ssh,
|
|
fail_ok=fail_ok, auth_info=auth_info)
|
|
if code > 0:
|
|
return 1, output
|
|
|
|
msg = "Aggregate {} set successfully with param: {}".format(aggregate, args)
|
|
LOG.info(msg)
|
|
return 0, msg
|
|
|
|
|
|
def unset_aggregate(aggregate, properties, fail_ok=False, con_ssh=None,
|
|
auth_info=Tenant.get('admin')):
|
|
"""
|
|
Unset given properties for aggregate
|
|
Args:
|
|
aggregate (str): aggregate to unset
|
|
properties (list|tuple|str|None):
|
|
fail_ok (bool):
|
|
con_ssh:
|
|
auth_info:
|
|
|
|
Returns (tuple):
|
|
(0, "Aggregate <aggregate> set successfully with param: <params>)
|
|
(1, <std_err>) returns only if fail_ok=True
|
|
|
|
"""
|
|
if isinstance(properties, str):
|
|
properties = (properties,)
|
|
|
|
args = ' '.join(['--property {}'.format(key) for key in properties])
|
|
args = '{} {}'.format(args, aggregate)
|
|
code, output = cli.openstack('aggregate unset', args, ssh_client=con_ssh,
|
|
fail_ok=fail_ok, auth_info=auth_info)
|
|
if code > 0:
|
|
return 1, output
|
|
|
|
msg = "Aggregate {} properties unset successfully: {}".format(aggregate,
|
|
properties)
|
|
LOG.info(msg)
|
|
return 0, msg
|
|
|
|
|
|
def get_aggregate_values(aggregate, fields, con_ssh=None,
|
|
auth_info=Tenant.get('admin'), fail_ok=False):
|
|
"""
|
|
Get values of a nova aggregate for given fields
|
|
Args:
|
|
aggregate (str):
|
|
fields (str|list|tuple):
|
|
con_ssh:
|
|
auth_info (dict):
|
|
fail_ok (bool)
|
|
|
|
Returns (list):
|
|
|
|
"""
|
|
code, out = cli.openstack('aggregate show', aggregate, ssh_client=con_ssh,
|
|
auth_info=auth_info, fail_ok=fail_ok)
|
|
if code > 0:
|
|
return []
|
|
|
|
table_ = table_parser.table(out)
|
|
return table_parser.get_multi_values_two_col_table(
|
|
table_, fields, evaluate=True, dict_fields=('properties',))
|
|
|
|
|
|
def delete_flavors(flavors, check_first=True, fail_ok=False, con_ssh=None,
|
|
auth_info=Tenant.get('admin')):
|
|
"""
|
|
Delete given flavor(s)
|
|
Args:
|
|
flavors (list|str): id(s) of flavor(s) to delete
|
|
check_first (bool)
|
|
fail_ok (bool): whether to raise exception if any flavor fails to delete
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
|
|
Returns (tuple):
|
|
(-1, 'None of the flavor(s) exists. Do nothing.')
|
|
(0, 'Flavor is successfully deleted')
|
|
(1, <std_out>)
|
|
(2, "Flavor <flavor_id> still exists on system after deleted.")
|
|
|
|
"""
|
|
if isinstance(flavors, str):
|
|
flavors = [flavors]
|
|
|
|
if check_first:
|
|
existing_favors = get_flavors(con_ssh=con_ssh, auth_info=auth_info)
|
|
flavors = list(set(flavors) & set(existing_favors))
|
|
if not flavors:
|
|
msg = "None of the given flavors exist. Do nothing."
|
|
LOG.info(msg)
|
|
return -1, msg
|
|
|
|
LOG.info("Flavor(s) to delete: {}".format(flavors))
|
|
code, output = cli.openstack('flavor delete', ' '.join(flavors),
|
|
ssh_client=con_ssh, fail_ok=fail_ok,
|
|
auth_info=auth_info)
|
|
if code > 0:
|
|
return 1, output
|
|
|
|
existing_favors = get_flavors(con_ssh=con_ssh, auth_info=auth_info)
|
|
flavors_still_exist = list(set(flavors) & set(existing_favors))
|
|
if flavors_still_exist:
|
|
err_msg = "Flavor(s) still exist after deletion: {}".format(
|
|
flavors_still_exist)
|
|
LOG.warning(err_msg)
|
|
if fail_ok:
|
|
return 2, err_msg
|
|
else:
|
|
raise exceptions.FlavorError(err_msg)
|
|
|
|
success_msg = "Flavor(s) deleted successfully."
|
|
LOG.info(success_msg)
|
|
return 0, success_msg
|
|
|
|
|
|
def get_flavors(name=None, memory=None, disk=None, ephemeral=None, swap=None,
|
|
vcpu=None, rxtx=None, is_public=None,
|
|
flv_id=None, long=False, con_ssh=None, auth_info=None,
|
|
strict=True, field='id'):
|
|
"""
|
|
Get a flavor id with given criteria. If no criteria given, a random
|
|
flavor will be returned.
|
|
|
|
Args:
|
|
name (str): name of a flavor
|
|
memory (int): memory size in MB
|
|
disk (int): size of the disk in GB
|
|
ephemeral (int): size of ephemeral disk in GB
|
|
swap (int): size of swap disk in GB
|
|
vcpu (int): number of vcpus
|
|
rxtx (str):
|
|
is_public (bool):
|
|
flv_id (str)
|
|
long (bool)
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
strict (bool): whether or not to perform strict search on provided
|
|
values
|
|
field (str|list|tuple)
|
|
|
|
Returns (list):
|
|
|
|
"""
|
|
|
|
args = '--long' if long else ''
|
|
table_ = table_parser.table(
|
|
cli.openstack('flavor list', args, ssh_client=con_ssh,
|
|
auth_info=auth_info)[1])
|
|
|
|
req_dict = {'Name': name,
|
|
'RAM': memory,
|
|
'Disk': disk,
|
|
'Ephemeral': ephemeral,
|
|
'Swap': '' if str(swap) == '0' else swap,
|
|
'VCPUs': vcpu,
|
|
'RXTX Factor': rxtx,
|
|
'Is Public': is_public,
|
|
'ID': flv_id,
|
|
}
|
|
final_dict = {k: str(v) for k, v in req_dict.items() if v is not None}
|
|
return table_parser.get_multi_values(table_, field, strict=strict,
|
|
**final_dict)
|
|
|
|
|
|
def get_basic_flavor(auth_info=None, con_ssh=None, guest_os='', rtn_id=True):
|
|
"""
|
|
Get a basic flavor with the default arg values and without adding extra
|
|
specs.
|
|
Args:
|
|
auth_info (dict):
|
|
con_ssh (SSHClient):
|
|
guest_os
|
|
rtn_id (bool): return flavor id or name
|
|
|
|
Returns (str): id of the basic flavor
|
|
|
|
"""
|
|
if not guest_os:
|
|
guest_os = GuestImages.DEFAULT['guest']
|
|
size = GuestImages.IMAGE_FILES[guest_os][1]
|
|
|
|
default_flavor_name = 'flavor-default-size{}'.format(size)
|
|
rtn_val = 'id' if rtn_id else 'name'
|
|
flavors = get_flavors(name=default_flavor_name, con_ssh=con_ssh,
|
|
auth_info=auth_info, strict=False,
|
|
field=rtn_val)
|
|
flavor = flavors[0] if flavors else \
|
|
create_flavor(name=default_flavor_name, root_disk=size, con_ssh=con_ssh,
|
|
cleanup='session', rtn_id=rtn_id)[1]
|
|
|
|
return flavor
|
|
|
|
|
|
def set_flavor(flavor, project=None, project_domain=None, description=None,
|
|
no_property=None, con_ssh=None,
|
|
auth_info=Tenant.get('admin'), fail_ok=False, **properties):
|
|
"""
|
|
Set flavor with given parameters
|
|
Args:
|
|
flavor (str): id of a flavor
|
|
project (str)
|
|
project_domain (str)
|
|
description (str)
|
|
no_property (bool)
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
fail_ok (bool):
|
|
**properties: extra specs to set. e.g., **{"hw:mem_page_size": "2048"}
|
|
|
|
Returns (tuple): (rtn_code (int), message (str))
|
|
(0, 'Flavor extra specs set successfully.'): required extra spec(s)
|
|
added successfully
|
|
(1, <stderr>): add extra spec cli rejected
|
|
|
|
"""
|
|
args_dict = {
|
|
'--description': description,
|
|
'--project': project,
|
|
'--project-domain': project_domain,
|
|
'--no-property': no_property and not properties,
|
|
'--property': properties
|
|
}
|
|
args = common.parse_args(args_dict, repeat_arg=True)
|
|
|
|
if not args.strip():
|
|
raise ValueError("Nothing is provided to set")
|
|
|
|
LOG.info("Setting flavor {} with args: {}".format(flavor, args))
|
|
args = '{} {}'.format(args, flavor)
|
|
exit_code, output = cli.openstack('flavor set', args, ssh_client=con_ssh,
|
|
fail_ok=fail_ok, auth_info=auth_info)
|
|
if exit_code == 1:
|
|
return 1, output
|
|
|
|
msg = "Flavor {} set successfully".format(flavor)
|
|
LOG.info(msg)
|
|
return 0, flavor
|
|
|
|
|
|
def unset_flavor(flavor, properties=None, project=None, project_domain=None,
|
|
check_first=True, fail_ok=False,
|
|
auth_info=Tenant.get('admin'), con_ssh=None):
|
|
"""
|
|
Unset specific extra spec(s) from given flavor.
|
|
|
|
Args:
|
|
flavor (str): id of the flavor
|
|
properties (str|list|tuple): extra spec(s) to be removed. At least
|
|
one should be provided.
|
|
project_domain
|
|
project
|
|
check_first (bool): Whether to check if extra spec exists in flavor
|
|
before attempt to unset
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
fail_ok (bool):
|
|
con_ssh
|
|
|
|
Returns (tuple): (rtn_code (int), message (str))
|
|
(-1, 'Extra spec(s) <specs> not exist in flavor. Do nothing.')
|
|
(0, 'Flavor extra specs unset successfully.'): required extra spec(s)
|
|
removed successfully
|
|
(1, <stderr>): unset extra spec cli rejected
|
|
(2, '<spec_name> is still in the extra specs list'): post action
|
|
check failed
|
|
|
|
"""
|
|
if isinstance(properties, str):
|
|
properties = [properties]
|
|
|
|
if properties and check_first:
|
|
existing_specs = get_flavor_values(flavor, fields='properties',
|
|
con_ssh=con_ssh,
|
|
auth_info=auth_info)[0]
|
|
properties = list(set(properties) & set(existing_specs.keys()))
|
|
|
|
args_dict = {
|
|
'--property': properties,
|
|
'--project': project,
|
|
'--project_domain': project_domain,
|
|
}
|
|
args = common.parse_args(args_dict, repeat_arg=True)
|
|
if not args:
|
|
msg = "Nothing to unset for flavor {}. Do nothing.".format(flavor)
|
|
LOG.info(msg)
|
|
return -1, msg
|
|
|
|
LOG.info("Unsetting flavor {} with args: {}".format(flavor, args))
|
|
exit_code, output = cli.openstack('flavor unset', args, ssh_client=con_ssh,
|
|
fail_ok=fail_ok, auth_info=auth_info)
|
|
if exit_code > 0:
|
|
return 1, output
|
|
|
|
success_msg = "Flavor {} unset successfully".format(flavor)
|
|
LOG.info(success_msg)
|
|
return 0, success_msg
|
|
|
|
|
|
def get_flavor_properties(flavor, con_ssh=None, auth_info=Tenant.get('admin')):
|
|
"""
|
|
Get extra specs of a flavor as dictionary
|
|
Args:
|
|
flavor (str): id of a flavor
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
|
|
Returns (dict): e.g., {"aggregate_instance_extra_specs:storage":
|
|
"local_image", "hw:mem_page_size": "2048"}
|
|
|
|
"""
|
|
return get_flavor_values(flavor, fields='properties', con_ssh=con_ssh,
|
|
auth_info=auth_info)[0]
|
|
|
|
|
|
def create_server_group(name=None, policy='affinity', rule=None, fail_ok=False,
|
|
auth_info=None, con_ssh=None,
|
|
rtn_exist=False, field='id'):
|
|
"""
|
|
Create a server group with given criteria
|
|
|
|
Args:
|
|
name (str): name of the server group
|
|
policy (str): affinity or anti_infinity
|
|
rule (str|None): max_server_per_host can be specified when
|
|
policy=anti-affinity
|
|
fail_ok (bool):
|
|
auth_info (dict):
|
|
con_ssh (SSHClient):
|
|
rtn_exist (bool): Whether to return existing server group that
|
|
matches the given name
|
|
field (str): id or name
|
|
|
|
Returns (tuple): (rtn_code (int), err_msg_or_srv_grp_id (str))
|
|
- (0, <server_group_id>) # server group created successfully
|
|
- (1, <stderr>) # create server group cli rejected
|
|
|
|
"""
|
|
# process server group metadata
|
|
if name and rtn_exist:
|
|
existing_grp = get_server_groups(name=name, strict=False,
|
|
con_ssh=con_ssh, auth_info=auth_info,
|
|
field=field)
|
|
if existing_grp:
|
|
LOG.debug(
|
|
"Returning existing server group {}".format(existing_grp[0]))
|
|
return -1, existing_grp[0]
|
|
|
|
# process server group name and policy
|
|
if not name:
|
|
name = 'grp_{}'.format(policy.replace('-', '_'))
|
|
name = common.get_unique_name(name_str=name)
|
|
args = '{}{} {}'.format('--rule {} '.format(rule) if rule else '', name,
|
|
policy.replace('_', '-'))
|
|
|
|
LOG.info("Creating server group with args: {}...".format(args))
|
|
exit_code, output = cli.nova('server-group-create', args,
|
|
ssh_client=con_ssh, fail_ok=fail_ok,
|
|
auth_info=auth_info)
|
|
if exit_code > 0:
|
|
return 1, output
|
|
|
|
table_ = table_parser.table(output)
|
|
srv_grp_id = table_parser.get_values(table_, field)[0]
|
|
LOG.info("Server group {} created successfully.".format(name))
|
|
return 0, srv_grp_id
|
|
|
|
|
|
def get_server_groups(field='ID', all_projects=True, long=False, strict=True,
|
|
regex=False,
|
|
auth_info=Tenant.get('admin'), con_ssh=None, **kwargs):
|
|
"""
|
|
Get server groups ids based on the given criteria
|
|
|
|
Args:
|
|
auth_info (dict):
|
|
con_ssh (SSHClient):
|
|
strict (bool): whether to do strict search for given name
|
|
regex (bool): whether or not to use regex when for given name
|
|
all_projects(bool): whether to list for all projects
|
|
long
|
|
field (str|list|tuple):
|
|
**kwargs: filters
|
|
|
|
Returns (list): list of server groups
|
|
|
|
"""
|
|
args_dict = {
|
|
'--all-projects': all_projects,
|
|
'--long': long
|
|
}
|
|
args = common.parse_args(args_dict)
|
|
table_ = table_parser.table(
|
|
cli.openstack('server group list', args, ssh_client=con_ssh,
|
|
auth_info=auth_info)[1])
|
|
|
|
def _parse_list(value_str):
|
|
return [val.strip() for val in value_str.split(',')]
|
|
|
|
parsers = {_parse_list: ('Policies', 'Members')}
|
|
|
|
return table_parser.get_multi_values(table_, field, strict=strict,
|
|
regex=regex, parsers=parsers, **kwargs)
|
|
|
|
|
|
def get_server_groups_info(headers=('Policies', 'Members'), auth_info=None,
|
|
con_ssh=None,
|
|
strict=False, **kwargs):
|
|
"""
|
|
Get a server group(s) info as a list
|
|
|
|
Args:
|
|
headers (str|list|tuple): header string for info. such as 'Member',
|
|
'Metadata', 'Policies'
|
|
auth_info (dict):
|
|
con_ssh (SSHClient):
|
|
strict
|
|
kwargs
|
|
|
|
Returns (dict): server group(s) info in dict. server group id as key,
|
|
and values of specified headers as value.
|
|
Examples: {<server_group1>: [['affinity'], [<vm_id1>, <vm_id2>, ...]],
|
|
<server_group2>: ['anti-affinity', []]}
|
|
|
|
"""
|
|
if isinstance(headers, str):
|
|
headers = [headers]
|
|
headers = ['ID'] + list(headers)
|
|
|
|
values = get_server_groups(field=headers, all_projects=True, long=True,
|
|
con_ssh=con_ssh, auth_info=auth_info,
|
|
strict=strict, **kwargs)
|
|
group_ids = values.pop(0)
|
|
values = list(zip(*values))
|
|
srv_groups_info = {group_ids[i]: values[i] for i in range(len(group_ids))}
|
|
return srv_groups_info
|
|
|
|
|
|
def get_server_group_info(group_id=None, group_name=None,
|
|
headers=('Policies', 'Members'), strict=False,
|
|
auth_info=None, con_ssh=None):
|
|
"""
|
|
Get server group info for specified server group
|
|
Args:
|
|
group_id:
|
|
group_name:
|
|
headers (str|list|tuple):
|
|
auth_info:
|
|
strict
|
|
con_ssh:
|
|
|
|
Returns (list):
|
|
|
|
"""
|
|
filters = {'ID': group_id}
|
|
if group_name:
|
|
filters['Name'] = group_name
|
|
|
|
group_info = get_server_groups_info(headers=headers, auth_info=auth_info,
|
|
strict=strict,
|
|
con_ssh=con_ssh, **filters)
|
|
assert len(group_info) == 1, "More than 1 server group filtered"
|
|
|
|
values = list(group_info.values())[0]
|
|
|
|
return values
|
|
|
|
|
|
def server_group_exists(srv_grp_id, auth_info=Tenant.get('admin'),
|
|
con_ssh=None):
|
|
"""
|
|
Return True if given server group exists else False
|
|
|
|
Args:
|
|
srv_grp_id (str):
|
|
auth_info (dict):
|
|
con_ssh (SSHClient):
|
|
|
|
Returns (bool): True or False
|
|
|
|
"""
|
|
existing_server_groups = get_server_groups(all_projects=True,
|
|
auth_info=auth_info,
|
|
con_ssh=con_ssh)
|
|
return srv_grp_id in existing_server_groups
|
|
|
|
|
|
def delete_server_groups(srv_grp_ids=None, check_first=True, fail_ok=False,
|
|
auth_info=Tenant.get('admin'),
|
|
con_ssh=None):
|
|
"""
|
|
Delete server group(s)
|
|
|
|
Args:
|
|
srv_grp_ids (list|str): id(s) for server group(s) to delete.
|
|
check_first (bool): whether to check existence of given server groups
|
|
before attempt to delete. Default: True.
|
|
fail_ok (bool):
|
|
auth_info (dict|None):
|
|
con_ssh (SSHClient):
|
|
|
|
Returns (tuple): (rtn_code(int), msg(str)) # rtn_code 1,2 only returns
|
|
when fail_ok=True
|
|
(-1, 'No server group(s) to delete.') # "Empty vm list/string
|
|
provided and no vm exist on system.
|
|
(-1, 'None of the given server group(s) exists on system.')
|
|
(0, "Server group(s) deleted successfully.")
|
|
(1, <stderr>) # Deletion rejected for all of the server groups.
|
|
Return CLI stderr.
|
|
(2, "Some deleted server group(s) still exist on system::
|
|
<srv_grp_ids>")
|
|
"""
|
|
existing_sgs = None
|
|
if not srv_grp_ids:
|
|
existing_sgs = srv_grp_ids = get_server_groups(con_ssh=con_ssh,
|
|
auth_info=auth_info)
|
|
elif isinstance(srv_grp_ids, str):
|
|
srv_grp_ids = [srv_grp_ids]
|
|
|
|
srv_grp_ids = [sg for sg in srv_grp_ids if sg]
|
|
if not srv_grp_ids:
|
|
LOG.info("No server group(s) to delete. Do Nothing")
|
|
return -1, 'No server group(s) to delete.'
|
|
|
|
if check_first:
|
|
if existing_sgs is None:
|
|
existing_sgs = get_server_groups(con_ssh=con_ssh,
|
|
auth_info=auth_info)
|
|
|
|
srv_grp_ids = list(set(srv_grp_ids) & set(existing_sgs))
|
|
if not srv_grp_ids:
|
|
msg = "None of the given server group(s) exists on system. Do " \
|
|
"nothing"
|
|
LOG.info(msg)
|
|
return -1, msg
|
|
|
|
LOG.info("Deleting server group(s): {}".format(srv_grp_ids))
|
|
code, output = cli.openstack('server group delete', ' '.join(srv_grp_ids),
|
|
ssh_client=con_ssh, fail_ok=True,
|
|
auth_info=auth_info, timeout=60)
|
|
if code == 1:
|
|
return 1, output
|
|
|
|
existing_sgs = get_server_groups(con_ssh=con_ssh, auth_info=auth_info)
|
|
grps_undeleted = list(set(srv_grp_ids) & set(existing_sgs))
|
|
if grps_undeleted:
|
|
msg = "Some server group(s) still exist on system after deletion: " \
|
|
"{}".format(grps_undeleted)
|
|
LOG.warning(msg)
|
|
if fail_ok:
|
|
return 2, msg
|
|
raise exceptions.NovaError(msg)
|
|
|
|
msg = "Server group(s) deleted successfully."
|
|
LOG.info(msg)
|
|
return 0, "Server group(s) deleted successfully."
|
|
|
|
|
|
def get_keypairs(name=None, field='Name', con_ssh=None, auth_info=None):
|
|
"""
|
|
|
|
Args:
|
|
name (str): Name of the key pair to filter for a given user
|
|
field (str|list|tuple)
|
|
con_ssh (SSHClient):
|
|
auth_info (dict): Tenant to be used to execute the cli if none
|
|
Primary tenant will be used
|
|
|
|
Returns (list):return keypair names
|
|
|
|
"""
|
|
table_ = table_parser.table(
|
|
cli.openstack('keypair list', ssh_client=con_ssh, auth_info=auth_info)[
|
|
1])
|
|
return table_parser.get_multi_values(table_, field, Name=name)
|
|
|
|
|
|
def get_flavor_values(flavor, fields, strict=True, con_ssh=None,
|
|
auth_info=Tenant.get('admin')):
|
|
"""
|
|
Get flavor values for given fields via openstack flavor show
|
|
Args:
|
|
flavor (str):
|
|
fields (str|list|tuple):
|
|
strict (bool): strict search for field name or not
|
|
con_ssh:
|
|
auth_info:
|
|
|
|
Returns (list):
|
|
|
|
"""
|
|
table_ = table_parser.table(
|
|
cli.openstack('flavor show', flavor, ssh_client=con_ssh,
|
|
auth_info=auth_info)[1])
|
|
return table_parser.get_multi_values_two_col_table(
|
|
table_, fields, merge_lines=True, evaluate=True,
|
|
strict=strict, dict_fields=('properties',))
|
|
|
|
|
|
def copy_flavor(origin_flavor, new_name=None, con_ssh=None):
|
|
"""
|
|
Extract the info from an existing flavor and create a new flavor that is
|
|
has identical info
|
|
|
|
Args:
|
|
origin_flavor (str): id of an existing flavor to extract the info from
|
|
new_name:
|
|
con_ssh:
|
|
|
|
Returns (str): flavor_id
|
|
|
|
"""
|
|
table_ = table_parser.table(
|
|
cli.openstack('flavor show', origin_flavor, ssh_client=con_ssh,
|
|
auth_info=Tenant.get('admin'))[1])
|
|
|
|
extra_specs = table_parser.get_value_two_col_table(table_, 'properties')
|
|
extra_specs = table_parser.convert_value_to_dict(value=extra_specs)
|
|
ephemeral = table_parser.get_value_two_col_table(table_, 'ephemeral',
|
|
strict=False)
|
|
disk = table_parser.get_value_two_col_table(table_, 'disk')
|
|
is_public = table_parser.get_value_two_col_table(table_, 'is_public',
|
|
strict=False)
|
|
ram = table_parser.get_value_two_col_table(table_, 'ram')
|
|
rxtx_factor = table_parser.get_value_two_col_table(table_, 'rxtx_factor')
|
|
swap = table_parser.get_value_two_col_table(table_, 'swap')
|
|
vcpus = table_parser.get_value_two_col_table(table_, 'vcpus')
|
|
old_name = table_parser.get_value_two_col_table(table_, 'name')
|
|
|
|
if not new_name:
|
|
new_name = "{}-{}".format(old_name, new_name)
|
|
swap = swap if swap else 0
|
|
new_flavor_id = \
|
|
create_flavor(name=new_name, vcpus=vcpus, ram=ram, swap=swap,
|
|
root_disk=disk, ephemeral=ephemeral,
|
|
is_public=is_public, rxtx_factor=rxtx_factor,
|
|
con_ssh=con_ssh)[1]
|
|
set_flavor(new_flavor_id, con_ssh=con_ssh, **extra_specs)
|
|
|
|
return new_flavor_id
|
|
|
|
|
|
# TODO: nova providernet-show no longer exists for pci pfs/vfs info. Update
|
|
# required.
|
|
def get_provider_net_info(providernet_id, field='pci_pfs_configured',
|
|
strict=True, auth_info=Tenant.get('admin'),
|
|
con_ssh=None, rtn_int=True):
|
|
"""
|
|
Get provider net info from "nova providernet-show"
|
|
|
|
Args:
|
|
providernet_id (str): id of a providernet
|
|
field (str): Field name such as pci_vfs_configured, pci_pfs_used, etc
|
|
strict (bool): whether to perform a strict search on field name
|
|
auth_info (dict):
|
|
con_ssh (SSHClient):
|
|
rtn_int (bool): whether to return integer or string
|
|
|
|
Returns (int|str): value of specified field. Convert to integer by
|
|
default unless rnt_int=False.
|
|
|
|
"""
|
|
if not providernet_id:
|
|
raise ValueError("Providernet id is not provided.")
|
|
|
|
table_ = table_parser.table(
|
|
cli.nova('providernet-show', providernet_id, ssh_client=con_ssh,
|
|
auth_info=auth_info)[1])
|
|
info_str = table_parser.get_value_two_col_table(table_, field,
|
|
strict=strict)
|
|
return int(info_str) if rtn_int else info_str
|
|
|
|
|
|
def get_pci_interface_stats_for_providernet(
|
|
providernet_id,
|
|
fields=('pci_pfs_configured', 'pci_pfs_used', 'pci_vfs_configured',
|
|
'pci_vfs_used'),
|
|
auth_info=Tenant.get('admin'), con_ssh=None):
|
|
"""
|
|
get pci interface usage
|
|
Args:
|
|
providernet_id (str): id of a providernet
|
|
fields: fields such as ('pci_vfs_configured', 'pci_pfs_used')
|
|
auth_info (dict):
|
|
con_ssh (SSHClient):
|
|
|
|
Returns (tuple): tuple of integers
|
|
|
|
"""
|
|
if not providernet_id:
|
|
raise ValueError("Providernet id is not provided.")
|
|
|
|
table_ = table_parser.table(
|
|
cli.nova('providernet-show', providernet_id, ssh_client=con_ssh,
|
|
auth_info=auth_info)[1])
|
|
rtn_vals = []
|
|
for field in fields:
|
|
pci_stat = int(
|
|
table_parser.get_value_two_col_table(table_, field, strict=True))
|
|
rtn_vals.append(pci_stat)
|
|
return tuple(rtn_vals)
|
|
|
|
|
|
def create_aggregate(field='name', name=None, avail_zone=None, properties=None,
|
|
check_first=True, fail_ok=False,
|
|
con_ssh=None, auth_info=Tenant.get('admin')):
|
|
"""
|
|
Add a aggregate with given name and availability zone.
|
|
|
|
Args:
|
|
field (str): name or id
|
|
name (str): name for aggregate to create
|
|
avail_zone (str|None):
|
|
properties (dict|None)
|
|
check_first (bool)
|
|
fail_ok (bool):
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
|
|
Returns (tuple):
|
|
(0, <rtn_val>) -- aggregate successfully created
|
|
(1, <stderr>) -- cli rejected
|
|
(2, "Created aggregate is not as specified") -- name and/or
|
|
availability zone mismatch
|
|
|
|
"""
|
|
if not name:
|
|
existing_names = get_aggregates(field='name')
|
|
name = common.get_unique_name(name_str='stxauto',
|
|
existing_names=existing_names)
|
|
|
|
args_dict = {
|
|
'--zone': avail_zone,
|
|
'--property': properties,
|
|
}
|
|
args = '{} {}'.format(common.parse_args(args_dict, repeat_arg=True), name)
|
|
|
|
if check_first:
|
|
aggregates_ = get_aggregates(field=field, name=name,
|
|
avail_zone=avail_zone)
|
|
if aggregates_:
|
|
LOG.warning("Aggregate {} already exists. Do nothing.".format(name))
|
|
return -1, aggregates_[0]
|
|
|
|
LOG.info("Adding aggregate {}".format(name))
|
|
res, out = cli.openstack('aggregate create', args, ssh_client=con_ssh,
|
|
fail_ok=fail_ok, auth_info=auth_info)
|
|
if res == 1:
|
|
return res, out
|
|
|
|
out_tab = table_parser.table(out)
|
|
|
|
succ_msg = "Aggregate {} is successfully created".format(name)
|
|
LOG.info(succ_msg)
|
|
return 0, table_parser.get_value_two_col_table(out_tab, field)
|
|
|
|
|
|
def get_aggregates(field='name', name=None, avail_zone=None, con_ssh=None,
|
|
auth_info=Tenant.get('admin')):
|
|
"""
|
|
Get a list of aggregates
|
|
|
|
Args:
|
|
field (str|list|tuple): id or name
|
|
name (str|list): filter out the aggregates with given name if specified
|
|
avail_zone (str): filter out the aggregates with given availability
|
|
zone if specified
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
|
|
Returns (list):
|
|
|
|
"""
|
|
kwargs = {}
|
|
if avail_zone:
|
|
kwargs['Availability Zone'] = avail_zone
|
|
if name:
|
|
kwargs['Name'] = name
|
|
|
|
aggregates_tab = table_parser.table(
|
|
cli.openstack('aggregate list', ssh_client=con_ssh,
|
|
auth_info=auth_info)[1])
|
|
return table_parser.get_multi_values(aggregates_tab, field, **kwargs)
|
|
|
|
|
|
def delete_aggregates(names, check_first=True, remove_hosts=True, fail_ok=False,
|
|
con_ssh=None,
|
|
auth_info=Tenant.get('admin')):
|
|
"""
|
|
Add a aggregate with given name and availability zone.
|
|
|
|
Args:
|
|
names (str|list): name for aggregate to delete
|
|
check_first (bool)
|
|
remove_hosts (bool)
|
|
fail_ok (bool):
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
|
|
Returns (tuple):
|
|
(0, "Aggregate <name> is successfully deleted") -- aggregate
|
|
successfully deletec
|
|
(1, <stderr>) -- cli rejected
|
|
(2, "Aggregate <name> still exists in aggregate-list after deletion")
|
|
-- failed although cli accepted
|
|
|
|
"""
|
|
if check_first:
|
|
names = get_aggregates(name=names, con_ssh=con_ssh, auth_info=auth_info)
|
|
if not names:
|
|
msg = 'Aggregate {} does not exists. Do nothing.'.format(names)
|
|
LOG.warning(msg)
|
|
return -1, msg
|
|
elif isinstance(names, str):
|
|
names = [names]
|
|
|
|
if remove_hosts:
|
|
for name in names:
|
|
remove_hosts_from_aggregate(aggregate=name, check_first=True)
|
|
|
|
LOG.info("Deleting aggregate {}".format(names))
|
|
res, out = cli.openstack('aggregate delete', ' '.join(names),
|
|
ssh_client=con_ssh, fail_ok=fail_ok,
|
|
auth_info=auth_info)
|
|
if res == 1:
|
|
return res, out
|
|
|
|
post_aggregates = get_aggregates(name=names, con_ssh=con_ssh,
|
|
auth_info=auth_info)
|
|
if post_aggregates:
|
|
err_msg = "Aggregate {} still exists in openstack aggregate list " \
|
|
"after deletion.".format(post_aggregates)
|
|
LOG.warning(err_msg)
|
|
if fail_ok:
|
|
return 2, err_msg
|
|
else:
|
|
raise exceptions.NovaError(err_msg)
|
|
|
|
succ_msg = "Aggregate(s) successfully deleted: {}".format(names)
|
|
LOG.info(succ_msg)
|
|
return 0, succ_msg
|
|
|
|
|
|
def get_compute_services(field, con_ssh=None, auth_info=Tenant.get('admin'),
|
|
**kwargs):
|
|
"""
|
|
Get values from compute services list
|
|
|
|
System: Regular, Small footprint
|
|
|
|
Args:
|
|
field (str)
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
kwargs: Valid keys: Id, Binary, Host, Zone, Status, State, Updated At
|
|
|
|
Returns (list): a list of hypervisors in given zone
|
|
"""
|
|
table_ = table_parser.table(
|
|
cli.openstack('compute service list', ssh_client=con_ssh,
|
|
auth_info=auth_info)[1])
|
|
return table_parser.get_values(table_, field, **kwargs)
|
|
|
|
|
|
def remove_hosts_from_aggregate(aggregate, hosts=None, check_first=True,
|
|
fail_ok=False, con_ssh=None,
|
|
auth_info=Tenant.get('admin')):
|
|
"""
|
|
Remove hosts from specified aggregate
|
|
|
|
Args:
|
|
aggregate (str): name of the aggregate to remove hosts. stxauto
|
|
aggregate can be added via add_stxauto_zone
|
|
session fixture
|
|
hosts (list|str): host(s) to remove from aggregate
|
|
check_first (bool):
|
|
fail_ok (bool):
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
|
|
Returns (tuple):
|
|
(0, "Hosts successfully removed from aggregate")
|
|
(1, <stderr>) cli rejected on at least one host
|
|
(2, "Host(s) still exist in aggregate <aggr> after
|
|
aggregate-remove-host: <unremoved_hosts>)
|
|
|
|
"""
|
|
__remove_or_add_hosts_in_aggregate(remove=True, aggregate=aggregate,
|
|
hosts=hosts, check_first=check_first,
|
|
fail_ok=fail_ok, con_ssh=con_ssh,
|
|
auth_info=auth_info)
|
|
|
|
|
|
def add_hosts_to_aggregate(aggregate, hosts, check_first=True, fail_ok=False,
|
|
con_ssh=None,
|
|
auth_info=Tenant.get('admin')):
|
|
"""
|
|
Add host(s) to specified aggregate
|
|
|
|
Args:
|
|
aggregate (str): name of the aggregate to add hosts. stxauto
|
|
aggregate can be added via add_stxauto_zone
|
|
session fixture
|
|
hosts (list|str): host(s) to add to aggregate
|
|
check_first (bool):
|
|
fail_ok (bool):
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
|
|
Returns (tuple):
|
|
(0, "Hosts successfully added from aggregate")
|
|
(1, <stderr>) cli rejected on at least one host
|
|
(2, "aggregate-add-host accepted, but some host(s) are not added in
|
|
aggregate")
|
|
|
|
"""
|
|
__remove_or_add_hosts_in_aggregate(remove=False, aggregate=aggregate,
|
|
hosts=hosts, check_first=check_first,
|
|
fail_ok=fail_ok, con_ssh=con_ssh,
|
|
auth_info=auth_info)
|
|
|
|
|
|
def __remove_or_add_hosts_in_aggregate(aggregate, hosts=None, remove=False,
|
|
check_first=True, fail_ok=False,
|
|
con_ssh=None,
|
|
auth_info=Tenant.get('admin')):
|
|
"""
|
|
Remove/Add hosts from/to given aggregate
|
|
|
|
Args:
|
|
aggregate (str): name of the aggregate to add/remove hosts. stxauto
|
|
aggregate can be added via
|
|
add_stxauto_zone session fixture
|
|
hosts (list|str):
|
|
remove (bool): True if remove hosts from given aggregate, otherwise
|
|
add hosts to aggregate
|
|
check_first (bool):
|
|
fail_ok (bool):
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
|
|
Returns (tuple):
|
|
(0, "Hosts successfully removed from aggregate")
|
|
(1, <stderr>) cli rejected on at least one host
|
|
(2, "Host(s) still exist in aggregate <aggr> after
|
|
aggregate-remove-host: <unremoved_hosts>)
|
|
|
|
"""
|
|
hosts_in_aggregate = get_hosts_in_aggregate(aggregate, con_ssh=con_ssh)
|
|
|
|
if hosts is None:
|
|
if remove:
|
|
hosts = hosts_in_aggregate
|
|
else:
|
|
from keywords import host_helper
|
|
hosts = host_helper.get_hypervisors()
|
|
|
|
if isinstance(hosts, str):
|
|
hosts = [hosts]
|
|
|
|
msg_str = 'Remov' if remove else 'Add'
|
|
LOG.info("{}ing hosts {} in aggregate {}".format(msg_str, hosts, aggregate))
|
|
if check_first:
|
|
if remove:
|
|
hosts_to_rm_or_add = list(set(hosts) & set(hosts_in_aggregate))
|
|
else:
|
|
hosts_to_rm_or_add = list(set(hosts) - set(hosts_in_aggregate))
|
|
else:
|
|
hosts_to_rm_or_add = list(hosts)
|
|
|
|
if not hosts_to_rm_or_add:
|
|
warn_str = 'No' if remove else 'All'
|
|
msg = "{} given host(s) in aggregate {}. Do nothing. Given hosts: " \
|
|
"{}; hosts in aggregate: {}". \
|
|
format(warn_str, aggregate, hosts, hosts_in_aggregate)
|
|
LOG.warning(msg)
|
|
return -1, msg
|
|
|
|
failed_res = {}
|
|
cmd = 'aggregate remove host' if remove else 'aggregate add host'
|
|
for host in hosts_to_rm_or_add:
|
|
args = '{} {}'.format(aggregate, host)
|
|
code, output = cli.openstack(cmd, args, ssh_client=con_ssh,
|
|
fail_ok=True, auth_info=auth_info)
|
|
if code > 0:
|
|
failed_res[host] = output
|
|
|
|
if failed_res:
|
|
err_msg = "'{}' is rejected for following host(s) in aggregate " \
|
|
"{}: {}".format(cmd, aggregate, failed_res)
|
|
if fail_ok:
|
|
LOG.warning(err_msg)
|
|
return 1, err_msg
|
|
else:
|
|
raise exceptions.NovaError(err_msg)
|
|
|
|
post_hosts_in_aggregate = get_hosts_in_aggregate(aggregate, con_ssh=con_ssh)
|
|
if remove:
|
|
failed_hosts = list(set(hosts) & set(post_hosts_in_aggregate))
|
|
else:
|
|
failed_hosts = list(set(hosts) - set(post_hosts_in_aggregate))
|
|
|
|
if failed_hosts:
|
|
err_msg = "{} accepted, but some host(s) are not {}ed in aggregate " \
|
|
"{}: {}".format(cmd, msg_str, aggregate, failed_hosts)
|
|
if fail_ok:
|
|
LOG.warning(err_msg)
|
|
return 2, err_msg
|
|
else:
|
|
raise exceptions.NovaError(err_msg)
|
|
|
|
succ_msg = "Hosts successfully {}ed in aggregate {}: {}".format(
|
|
msg_str.lower(), aggregate, hosts)
|
|
LOG.info(succ_msg)
|
|
return 0, succ_msg
|
|
|
|
|
|
def get_migration_list_table(con_ssh=None, auth_info=Tenant.get('admin')):
|
|
"""
|
|
nova migration-list to collect migration history of each vm
|
|
Args:
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
|
|
"""
|
|
LOG.info("Listing migration history...")
|
|
return table_parser.table(
|
|
cli.nova('migration-list', ssh_client=con_ssh, auth_info=auth_info)[1])
|
|
|
|
|
|
def create_keypair(name, public_key=None, private_key=None, fail_ok=False,
|
|
con_ssh=None,
|
|
auth_info=Tenant.get('admin')):
|
|
"""
|
|
Create a new keypair
|
|
Args:
|
|
name (str): keypair name to create
|
|
public_key (str|None): existing public key file path to use
|
|
private_key (str|None): file path to save private key
|
|
fail_ok (bool)
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
|
|
Returns (tuple):
|
|
|
|
"""
|
|
args_dict = {'--public-key': public_key, '--private-key': private_key}
|
|
args = '{} "{}"'.format(common.parse_args(args_dict), name)
|
|
LOG.info("Creating keypair with args: {}".format(args))
|
|
|
|
code, out = cli.openstack('keypair create', args, ssh_client=con_ssh,
|
|
fail_ok=fail_ok, auth_info=auth_info)
|
|
if code > 0:
|
|
return 1, out
|
|
|
|
LOG.info("Keypair {} created successfully".format(name))
|
|
return 0, name
|
|
|
|
|
|
def delete_keypairs(keypairs, check_first=True, fail_ok=False, con_ssh=None,
|
|
auth_info=None):
|
|
"""
|
|
Delete keypair(s)
|
|
Args:
|
|
keypairs (list/str): keypair(s) to delete
|
|
check_first (bool)
|
|
fail_ok (bool)
|
|
con_ssh (SSHClient):
|
|
auth_info (dict):
|
|
|
|
Returns (tuple):
|
|
|
|
"""
|
|
if isinstance(keypairs, str):
|
|
keypairs = (keypairs,)
|
|
|
|
if check_first:
|
|
existing_keypairs = get_keypairs(con_ssh=con_ssh, auth_info=auth_info)
|
|
keypairs = list(set(keypairs) & set(existing_keypairs))
|
|
if not keypairs:
|
|
msg = 'Give keypair(s) not exist. Do nothing.'
|
|
LOG.info(msg)
|
|
return -1, msg
|
|
|
|
LOG.info('Deleting keypairs: {}'.format(keypairs))
|
|
code, out = cli.openstack('keypair delete', ' '.join(keypairs),
|
|
ssh_client=con_ssh, fail_ok=fail_ok,
|
|
auth_info=auth_info)
|
|
if code > 0:
|
|
return code, out
|
|
|
|
post_keypairs = get_keypairs(con_ssh=con_ssh, auth_info=auth_info)
|
|
undeleted_kp_names = list(set(keypairs) & set(post_keypairs))
|
|
if undeleted_kp_names:
|
|
raise exceptions.NovaError(
|
|
"keypair(s) still exist after deletion: {}".format(
|
|
undeleted_kp_names))
|
|
|
|
msg = 'keypairs deleted successfully: {}'.format(keypairs)
|
|
LOG.info(msg)
|
|
return 0, msg
|
|
|
|
|
|
def get_hosts_in_aggregate(aggregate, con_ssh=None,
|
|
auth_info=Tenant.get('admin'), fail_ok=False):
|
|
"""
|
|
Get list of hosts in given nova aggregate
|
|
Args:
|
|
aggregate (str):
|
|
con_ssh:
|
|
auth_info:
|
|
fail_ok (bool)
|
|
|
|
Returns (list):
|
|
|
|
"""
|
|
if 'image' in aggregate:
|
|
aggregate = 'local_storage_image_hosts'
|
|
elif 'remote' in aggregate:
|
|
aggregate = 'remote_storage_hosts'
|
|
|
|
hosts = get_aggregate_values(aggregate, 'hosts', con_ssh=con_ssh,
|
|
auth_info=auth_info, fail_ok=fail_ok)
|
|
if hosts:
|
|
hosts = hosts[0]
|
|
LOG.info("Hosts in {} aggregate: {}".format(aggregate, hosts))
|
|
return hosts
|