# Copyright 2014 Mirantis Inc. # All Rights Reserved. # # 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 ast import re import time from oslo_utils import strutils from tempest.lib.cli import base from tempest.lib.cli import output_parser from tempest.lib.common.utils import data_utils from tempest.lib import exceptions as tempest_lib_exc from manilaclient.common import constants from manilaclient import config from manilaclient.tests.functional import exceptions from manilaclient.tests.functional import utils CONF = config.CONF MESSAGE = 'message' SHARE = 'share' SHARE_TYPE = 'share_type' SHARE_NETWORK = 'share_network' SHARE_NETWORK_SUBNET = 'share_network_subnet' SHARE_SERVER = 'share_server' SNAPSHOT = 'snapshot' SHARE_REPLICA = 'share_replica' TRANSFER = 'transfer' def not_found_wrapper(f): def wrapped_func(self, *args, **kwargs): try: return f(self, *args, **kwargs) except tempest_lib_exc.CommandFailed as e: for regexp in (r'No (\w+) with a name or ID', r'not(.*){0,5}found'): if re.search(regexp, str(e.stderr)): # Raise appropriate 'NotFound' error raise tempest_lib_exc.NotFound() raise return wrapped_func def forbidden_wrapper(f): def wrapped_func(self, *args, **kwargs): try: return f(self, *args, **kwargs) except tempest_lib_exc.CommandFailed as e: if re.search('HTTP 403', str(e.stderr)): # Raise appropriate 'Forbidden' error. raise tempest_lib_exc.Forbidden() raise return wrapped_func class ManilaCLIClient(base.CLIClient): def __init__(self, *args, **kwargs): super(ManilaCLIClient, self).__init__(*args, **kwargs) if CONF.enable_protocols: self.share_protocol = CONF.enable_protocols[0] else: msg = "Configuration option 'enable_protocols' is not defined." raise exceptions.InvalidConfiguration(reason=msg) self.build_interval = CONF.build_interval self.build_timeout = CONF.build_timeout def manila(self, action, flags='', params='', fail_ok=False, endpoint_type='publicURL', merge_stderr=False, microversion=None): """Executes manila command for the given action. :param action: the cli command to run using manila :type action: string :param flags: any optional cli flags to use. For specifying microversion, please, use 'microversion' param :type flags: string :param params: any optional positional args to use :type params: string :param fail_ok: if True an exception is not raised when the cli return code is non-zero :type fail_ok: boolean :param endpoint_type: the type of endpoint for the service :type endpoint_type: string :param merge_stderr: if True the stderr buffer is merged into stdout :type merge_stderr: boolean :param microversion: API microversion to be used for request :type microversion: str """ flags += ' --endpoint-type %s' % endpoint_type if not microversion: # NOTE(vponomaryov): use max API version from config microversion = CONF.max_api_microversion # NOTE(vponomaryov): it is possible that param 'flags' already # can contain '--os-share-api-version' key. If it is so and we # reached this part then value of 'microversion' param will be # used and existing one in 'flags' param will be ignored. flags += ' --os-share-api-version %s' % microversion return self.cmd_with_auth( 'manila', action, flags, params, fail_ok, merge_stderr) def wait_for_resource_deletion(self, res_type, res_id, interval=3, timeout=180, microversion=None, **kwargs): """Resource deletion waiter. :param res_type: text -- type of resource :param res_id: text -- ID of resource to use for deletion check :param interval: int -- interval between requests in seconds :param timeout: int -- total time in seconds to wait for deletion :param args: dict -- additional keyword arguments for deletion func """ if res_type == SHARE_TYPE: func = self.is_share_type_deleted elif res_type == SHARE_NETWORK: func = self.is_share_network_deleted elif res_type == SHARE_NETWORK_SUBNET: func = self.is_share_network_subnet_deleted elif res_type == SHARE_SERVER: func = self.is_share_server_deleted elif res_type == SHARE: func = self.is_share_deleted elif res_type == SNAPSHOT: func = self.is_snapshot_deleted elif res_type == MESSAGE: func = self.is_message_deleted elif res_type == SHARE_REPLICA: func = self.is_share_replica_deleted elif res_type == TRANSFER: func = self.is_share_transfer_deleted else: raise exceptions.InvalidResource(message=res_type) end_loop_time = time.time() + timeout deleted = func(res_id, microversion=microversion, **kwargs) while not (deleted or time.time() > end_loop_time): time.sleep(interval) deleted = func(res_id, microversion=microversion, **kwargs) if not deleted: raise exceptions.ResourceReleaseFailed( res_type=res_type, res_id=res_id) def list_availability_zones(self, columns=None, microversion=None): """List availability zones. :param columns: comma separated string of columns. Example, "--columns id,name" :param microversion: API microversion that should be used. """ cmd = 'availability-zone-list' if columns is not None: cmd += ' --columns ' + columns azs_raw = self.manila(cmd, microversion=microversion) azs = output_parser.listing(azs_raw) return azs # Share types def create_share_type(self, name=None, driver_handles_share_servers=True, snapshot_support=None, create_share_from_snapshot=None, revert_to_snapshot=None, mount_snapshot=None, is_public=True, microversion=None, extra_specs=None, description=None): """Creates share type. :param name: text -- name of share type to use, if not set then autogenerated will be used :param description: text -- description of share type to use. Default is None. :param driver_handles_share_servers: bool/str -- boolean or its string alias. Default is True. :param snapshot_support: bool/str -- boolean or its string alias. Default is None. :param is_public: bool/str -- boolean or its string alias. Default is True. :param extra_specs: -- dictionary of extra specs Default is None. :param create_share_from_snapshot: -- boolean or its string alias. Default is None. :param revert_to_snapshot: -- boolean or its string alias. Default is None. :param mount_snapshot: -- boolean or its string alias. Default is None. """ if name is None: name = data_utils.rand_name('manilaclient_functional_test') dhss = driver_handles_share_servers if not isinstance(dhss, str): dhss = str(dhss) if not isinstance(is_public, str): is_public = str(is_public) cmd = ('type-create %(name)s %(dhss)s --is-public %(is_public)s ') % { 'name': name, 'dhss': dhss, 'is_public': is_public} if description is not None: cmd += " --description " + description if snapshot_support is not None: if not isinstance(snapshot_support, str): snapshot_support = str(snapshot_support) cmd += " --snapshot-support " + snapshot_support if create_share_from_snapshot is not None: if not isinstance(create_share_from_snapshot, str): create_share_from_snapshot = str(create_share_from_snapshot) cmd += (" --create-share-from-snapshot-support " + create_share_from_snapshot) if revert_to_snapshot is not None: if not isinstance(revert_to_snapshot, str): revert_to_snapshot = str(revert_to_snapshot) cmd += (" --revert-to-snapshot-support " + revert_to_snapshot) if mount_snapshot is not None: if not isinstance(mount_snapshot, str): mount_snapshot = str(mount_snapshot) cmd += (" --mount-snapshot-support " + mount_snapshot) if extra_specs is not None: extra_spec_str = '' for k, v in extra_specs.items(): if not isinstance(v, str): extra_specs[k] = str(v) extra_spec_str += "{}='{}' ".format(k, v) cmd += " --extra_specs " + extra_spec_str share_type_raw = self.manila(cmd, microversion=microversion) share_type = utils.details(share_type_raw) return share_type def update_share_type(self, share_type_id, name=None, is_public=None, microversion=None, description=None): """Update share type. :param share_type_id: text -- id of share type. :param name: text -- new name of share type, if not set then it will not be updated. :param description: text -- new description of share type. if not set then it will not be updated. :param is_public: bool/str -- boolean or its string alias. new visibility of the share type.If set to True, share type will be available to all tenants in the cloud. """ cmd = ('type-update %(share_type_id)s ') % { 'share_type_id': share_type_id} if is_public is not None: if not isinstance(is_public, str): is_public = str(is_public) cmd += " --is_public " + is_public if description: cmd += " --description " + description elif description == "": cmd += ' --description "" ' if name: cmd += " --name " + name share_type_raw = self.manila(cmd, microversion=microversion) share_type = utils.details(share_type_raw) return share_type @not_found_wrapper def delete_share_type(self, share_type, microversion=None): """Deletes share type by its Name or ID.""" return self.manila( 'type-delete %s' % share_type, microversion=microversion) def list_share_types(self, list_all=True, columns=None, search_opts=None, microversion=None): """List share types. :param list_all: bool -- whether to list all share types or only public :param search_opts: dict search_opts for filter search. :param columns: comma separated string of columns. Example, "--columns id,name" """ cmd = 'type-list' if list_all: cmd += ' --all' if search_opts is not None: extra_specs = search_opts.get('extra_specs') if extra_specs: cmd += ' --extra_specs' for spec_key in extra_specs.keys(): cmd += ' ' + spec_key + '=' + extra_specs[spec_key] if columns is not None: cmd += ' --columns ' + columns share_types_raw = self.manila(cmd, microversion=microversion) share_types = output_parser.listing(share_types_raw) return share_types def get_share_type(self, share_type, microversion=None): """Get share type. :param share_type: str -- Name or ID of share type, or None to retrieve default share type """ share_types = self.list_share_types(True, microversion=microversion) for stype in share_types: if share_type is None and stype["is_default"] == 'YES': return stype elif share_type in (stype['ID'], stype['Name']): return stype raise tempest_lib_exc.NotFound() def is_share_type_deleted(self, share_type, microversion=None): """Says whether share type is deleted or not. :param share_type: text -- Name or ID of share type """ # NOTE(vponomaryov): we use 'list' operation because there is no # 'get/show' operation for share-types available for CLI share_types = self.list_share_types( list_all=True, microversion=microversion) for list_element in share_types: if share_type in (list_element['ID'], list_element['Name']): return False return True def wait_for_share_type_deletion(self, share_type, microversion=None): """Wait for share type deletion by its Name or ID. :param share_type: text -- Name or ID of share type """ self.wait_for_resource_deletion( SHARE_TYPE, res_id=share_type, interval=2, timeout=6, microversion=microversion) def get_project_id(self, name_or_id): identity_api_version = '3' flags = ( "--os-username %(username)s " "--os-project-name %(project_name)s " "--os-password %(password)s " "--os-identity-api-version %(identity_api_version)s " ) % { "username": CONF.admin_username, "project_name": CONF.admin_tenant_name, "password": CONF.admin_password, "identity_api_version": identity_api_version, } if identity_api_version == "3": if CONF.admin_project_domain_name: flags += ( "--os-project-domain-name %s " % CONF.admin_project_domain_name) elif CONF.admin_project_domain_id: flags += ( "--os-project-domain-id %s " % CONF.admin_project_domain_id) if CONF.admin_user_domain_name: flags += ( "--os-user-domain-name %s " % CONF.admin_user_domain_name) elif CONF.admin_user_domain_id: flags += ( "--os-user-domain-id %s " % CONF.admin_user_domain_id) project_id = self.openstack( 'project show -f value -c id %s' % name_or_id, flags=flags) return project_id.strip() @not_found_wrapper def add_share_type_access(self, share_type_name_or_id, project_id, microversion=None): data = dict(st=share_type_name_or_id, project=project_id) self.manila('type-access-add %(st)s %(project)s' % data, microversion=microversion) @not_found_wrapper def remove_share_type_access(self, share_type_name_or_id, project_id, microversion=None): data = dict(st=share_type_name_or_id, project=project_id) self.manila('type-access-remove %(st)s %(project)s' % data, microversion=microversion) @not_found_wrapper def list_share_type_access(self, share_type_id, microversion=None): projects_raw = self.manila( 'type-access-list %s' % share_type_id, microversion=microversion) projects = output_parser.listing(projects_raw) project_ids = [pr['Project_ID'] for pr in projects] return project_ids @not_found_wrapper def set_share_type_extra_specs(self, share_type_name_or_id, extra_specs, microversion=None): """Set key-value pair for share type.""" if not (isinstance(extra_specs, dict) and extra_specs): raise exceptions.InvalidData( message='Provided invalid extra specs - %s' % extra_specs) cmd = 'type-key %s set ' % share_type_name_or_id for key, value in extra_specs.items(): cmd += '%(key)s=%(value)s ' % {'key': key, 'value': value} return self.manila(cmd, microversion=microversion) @not_found_wrapper def unset_share_type_extra_specs(self, share_type_name_or_id, extra_specs_keys, microversion=None): """Unset key-value pair for share type.""" if not (isinstance(extra_specs_keys, (list, tuple, set)) and extra_specs_keys): raise exceptions.InvalidData( message='Provided invalid extra specs - %s' % extra_specs_keys) cmd = 'type-key %s unset ' % share_type_name_or_id for key in extra_specs_keys: cmd += '%s ' % key return self.manila(cmd, microversion=microversion) def list_all_share_type_extra_specs(self, microversion=None): """List extra specs for all share types.""" extra_specs_raw = self.manila( 'extra-specs-list', microversion=microversion) extra_specs = utils.listing(extra_specs_raw) return extra_specs def list_share_type_extra_specs(self, share_type_name_or_id, microversion=None): """List extra specs for specific share type by its Name or ID.""" all_share_types = self.list_all_share_type_extra_specs( microversion=microversion) for share_type in all_share_types: if share_type_name_or_id in (share_type['ID'], share_type['Name']): return share_type['all_extra_specs'] raise exceptions.ShareTypeNotFound(share_type=share_type_name_or_id) # Share networks def create_share_network(self, name=None, description=None, nova_net_id=None, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None, microversion=None): """Creates share network. :param name: text -- desired name of new share network :param description: text -- desired description of new share network :param nova_net_id: text -- ID of Nova network :param neutron_net_id: text -- ID of Neutron network :param neutron_subnet_id: text -- ID of Neutron subnet NOTE: 'nova_net_id' and 'neutron_net_id'/'neutron_subnet_id' are mutually exclusive. """ params = self._combine_share_network_data( name=name, description=description, nova_net_id=nova_net_id, neutron_net_id=neutron_net_id, neutron_subnet_id=neutron_subnet_id, availability_zone=availability_zone ) share_network_raw = self.manila( 'share-network-create %s' % params, microversion=microversion) share_network = output_parser.details(share_network_raw) return share_network def _combine_share_network_data(self, name=None, description=None, nova_net_id=None, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None): """Combines params for share network operations 'create' and 'update'. :returns: text -- set of CLI parameters """ data = dict() if name is not None: data['--name'] = name if description is not None: data['--description'] = description if nova_net_id is not None: data['--nova_net_id'] = nova_net_id if neutron_net_id is not None: data['--neutron_net_id'] = neutron_net_id if neutron_subnet_id is not None: data['--neutron_subnet_id'] = neutron_subnet_id if availability_zone is not None: data['--availability_zone'] = availability_zone cmd = '' for key, value in data.items(): cmd += "%(k)s=%(v)s " % {'k': key, 'v': value} return cmd @not_found_wrapper def get_share_network(self, share_network, microversion=None): """Returns share network by its Name or ID.""" share_network_raw = self.manila( 'share-network-show %s' % share_network, microversion=microversion) share_network = output_parser.details(share_network_raw) return share_network @not_found_wrapper def update_share_network(self, share_network, name=None, description=None, nova_net_id=None, neutron_net_id=None, neutron_subnet_id=None, microversion=None): """Updates share-network by its name or ID. :param name: text -- new name for share network :param description: text -- new description for share network :param nova_net_id: text -- ID of some Nova network :param neutron_net_id: text -- ID of some Neutron network :param neutron_subnet_id: text -- ID of some Neutron subnet NOTE: 'nova_net_id' and 'neutron_net_id'/'neutron_subnet_id' are mutually exclusive. """ sn_params = self._combine_share_network_data( name=name, description=description, nova_net_id=nova_net_id, neutron_net_id=neutron_net_id, neutron_subnet_id=neutron_subnet_id) share_network_raw = self.manila( 'share-network-update %(sn)s %(params)s' % dict( sn=share_network, params=sn_params), microversion=microversion) share_network = output_parser.details(share_network_raw) return share_network @not_found_wrapper def delete_share_network(self, share_network, microversion=None): """Deletes share network by its Name or ID.""" return self.manila('share-network-delete %s' % share_network, microversion=microversion) @staticmethod def _stranslate_to_cli_optional_param(param): if len(param) < 1 or not isinstance(param, str): raise exceptions.InvalidData( 'Provided wrong parameter for translation.') while not param[0:2] == '--': param = '-' + param return param.replace('_', '-') def list_share_networks(self, all_tenants=False, filters=None, columns=None, microversion=None): """List share networks. :param all_tenants: bool -- whether to list share-networks that belong only to current project or for all tenants. :param filters: dict -- filters for listing of share networks. Example, input: {'project_id': 'foo'} {'-project_id': 'foo'} {'--project_id': 'foo'} {'project-id': 'foo'} will be transformed to filter parameter "--project-id=foo" :param columns: comma separated string of columns. Example, "--columns id" """ cmd = 'share-network-list ' if columns is not None: cmd += ' --columns ' + columns if all_tenants: cmd += ' --all-tenants ' if filters and isinstance(filters, dict): for k, v in filters.items(): cmd += '%(k)s=%(v)s ' % { 'k': self._stranslate_to_cli_optional_param(k), 'v': v} share_networks_raw = self.manila(cmd, microversion=microversion) share_networks = utils.listing(share_networks_raw) return share_networks def share_network_reset_state(self, id=None, state=None, microversion=None): cmd = 'share-network-reset-state %s ' % id if state: cmd += '--state %s' % state share_network_raw = self.manila(cmd, microversion=microversion) share_network = utils.listing(share_network_raw) return share_network def share_network_security_service_add( self, share_network_id, security_service_id, microversion=None): cmd = ('share-network-security-service-add %(network_id)s ' '%(service_id)s' % {'network_id': share_network_id, 'service_id': security_service_id}) self.manila(cmd, microversion=microversion) def share_network_security_service_update( self, share_network_id, current_security_service_id, new_security_service_id, microversion=None): cmd = ( 'share-network-security-service-update %(network_id)s ' '%(current_service_id)s %(new_security_service_id)s' % { 'network_id': share_network_id, 'current_service_id': current_security_service_id, 'new_security_service_id': new_security_service_id}) self.manila(cmd, microversion=microversion) def share_network_security_service_add_check( self, share_network_id, security_service_id, reset=False, microversion=None): cmd = ( 'share-network-security-service-add-check %(network_id)s ' '%(security_service_id)s' % { 'network_id': share_network_id, 'security_service_id': security_service_id}) if reset: cmd += '--reset %s' % reset return output_parser.details( self.manila(cmd, microversion=microversion)) def share_network_security_service_update_check( self, share_network_id, current_security_service_id, new_security_service_id, reset=False, microversion=None): cmd = ( 'share-network-security-service-update-check %(network_id)s ' '%(current_security_service_id)s %(new_security_service_id)s ' % { 'network_id': share_network_id, 'current_security_service_id': current_security_service_id, 'new_security_service_id': new_security_service_id}) if reset: cmd += '--reset %s' % reset return output_parser.details( self.manila(cmd, microversion=microversion)) def share_network_security_service_list( self, share_network_id, microversion=None): cmd = ('share-network-security-service-list %s' % share_network_id) share_networks_raw = self.manila(cmd, microversion=microversion) network_services = utils.listing(share_networks_raw) return network_services def is_share_network_deleted(self, share_network, microversion=None): """Says whether share network is deleted or not. :param share_network: text -- Name or ID of share network """ share_types = self.list_share_networks(True, microversion=microversion) for list_element in share_types: if share_network in (list_element['id'], list_element['name']): return False return True def wait_for_share_network_deletion(self, share_network, microversion=None): """Wait for share network deletion by its Name or ID. :param share_network: text -- Name or ID of share network """ self.wait_for_resource_deletion( SHARE_NETWORK, res_id=share_network, interval=2, timeout=6, microversion=microversion) def share_network_subnet_create_check( self, share_network_id, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None, reset=False, microversion=None): params = self._combine_share_network_subnet_data( neutron_net_id=neutron_net_id, neutron_subnet_id=neutron_subnet_id, availability_zone=availability_zone) cmd = ( 'share-network-subnet-create-check %(network_id)s ' '%(params)s' % { 'network_id': share_network_id, 'params': params}) if reset: cmd += '--reset %s' % reset return output_parser.details( self.manila(cmd, microversion=microversion)) # Share Network Subnets def _combine_share_network_subnet_data(self, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None): """Combines params for share network subnet 'create' operation. :returns: text -- set of CLI parameters """ data = dict() if neutron_net_id is not None: data['--neutron_net_id'] = neutron_net_id if neutron_subnet_id is not None: data['--neutron_subnet_id'] = neutron_subnet_id if availability_zone is not None: data['--availability_zone'] = availability_zone cmd = '' for key, value in data.items(): cmd += "%(k)s=%(v)s " % dict(k=key, v=value) return cmd def add_share_network_subnet(self, share_network, neutron_net_id=None, neutron_subnet_id=None, availability_zone=None, microversion=None): """Create new share network subnet for the given share network.""" params = self._combine_share_network_subnet_data( neutron_net_id=neutron_net_id, neutron_subnet_id=neutron_subnet_id, availability_zone=availability_zone) share_network_subnet_raw = self.manila( 'share-network-subnet-create %(sn)s %(params)s' % {'sn': share_network, 'params': params}, microversion=microversion) share_network_subnet = output_parser.details(share_network_subnet_raw) return share_network_subnet def get_share_network_subnet(self, share_network, share_network_subnet, microversion=None): """Returns share network subnet by share network ID and subnet ID.""" share_network_subnet_raw = self.manila( 'share-network-subnet-show %(share_net)s %(share_subnet)s' % { 'share_net': share_network, 'share_subnet': share_network_subnet, }) share_network_subnet = output_parser.details(share_network_subnet_raw) return share_network_subnet def get_share_network_subnets(self, share_network, microversion=None): share_network = self.get_share_network(share_network, microversion=microversion) raw_subnets = share_network.get('share_network_subnets') subnets = ast.literal_eval(raw_subnets) # NOTE(lseki): convert literal None to string 'None' for subnet in subnets: for k, v in subnet.items(): subnet[k] = str(v) if v is None else v return subnets @not_found_wrapper def delete_share_network_subnet(self, share_network, share_network_subnet, microversion=None): """Delete a share_network.""" self.manila( 'share-network-subnet-delete %(share_net)s %(share_subnet)s' % { 'share_net': share_network, 'share_subnet': share_network_subnet, }, microversion=microversion) def is_share_network_subnet_deleted(self, share_network_subnet, share_network, microversion=None): # NOTE(lseki): the parameter share_network_subnet comes before # share_network, because the wrapper method wait_for_resource_deletion # expects the resource id in first place (subnet ID in this case). """Says whether share network subnet is deleted or not. :param share_network_subnet: text -- Name or ID of share network subnet :param share_network: text -- Name or ID of share network the subnet belongs to """ subnets = self.get_share_network_subnets(share_network) return not any(subnet['id'] == share_network_subnet for subnet in subnets) def wait_for_share_network_subnet_deletion(self, share_network_subnet, share_network, microversion=None): # NOTE(lseki): the parameter share_network_subnet comes before # share_network, because the wrapper method wait_for_resource_deletion # expects the resource id in first place (subnet ID in this case). """Wait for share network subnet deletion by its Name or ID. :param share_network_subnet: text -- Name or ID of share network subnet :param share_network: text -- Name or ID of share network the subnet belongs to """ args = {'share_network': share_network} self.wait_for_resource_deletion( SHARE_NETWORK_SUBNET, res_id=share_network_subnet, interval=2, timeout=6, microversion=microversion, **args) # Shares def create_share(self, share_protocol, size, share_network=None, share_type=None, name=None, description=None, public=False, snapshot=None, metadata=None, wait=False, microversion=None): """Creates a share. :param share_protocol: str -- share protocol of a share. :param size: int/str -- desired size of a share. :param share_network: str -- Name or ID of share network to use. :param share_type: str -- Name or ID of share type to use. :param name: str -- desired name of new share. :param description: str -- desired description of new share. :param public: bool -- should a share be public or not. Default is False. :param snapshot: str -- Name or ID of a snapshot to use as source. :param metadata: dict -- key-value data to provide with share creation. :param wait: bool - the client must wait for "available" state :param microversion: str -- API microversion that should be used. """ cmd = 'create %(share_protocol)s %(size)s ' % { 'share_protocol': share_protocol, 'size': size} if share_network is not None: cmd += '--share-network %s ' % share_network if share_type is not None: cmd += '--share-type %s ' % share_type if name is None: name = data_utils.rand_name('autotest_share_name') cmd += '--name %s ' % name if description is None: description = data_utils.rand_name('autotest_share_description') cmd += '--description %s ' % description if public: cmd += '--public ' if snapshot is not None: cmd += '--snapshot %s ' % snapshot if metadata: metadata_cli = '' for k, v in metadata.items(): metadata_cli += '%(k)s=%(v)s ' % {'k': k, 'v': v} if metadata_cli: cmd += '--metadata %s ' % metadata_cli if wait: cmd += '--wait ' share_raw = self.manila(cmd, microversion=microversion) share = output_parser.details(share_raw) return share @not_found_wrapper def get_share(self, share, microversion=None): """Returns a share by its Name or ID.""" share_raw = self.manila('show %s' % share, microversion=microversion) share = output_parser.details(share_raw) return share @not_found_wrapper def update_share(self, share, name=None, description=None, is_public=False, microversion=None): """Updates a share. :param share: str -- name or ID of a share that should be updated. :param name: str -- desired name of new share. :param description: str -- desired description of new share. :param is_public: bool -- should a share be public or not. Default is False. """ cmd = 'update %s ' % share if name: cmd += '--name %s ' % name if description: cmd += '--description %s ' % description is_public = strutils.bool_from_string(is_public, strict=True) cmd += '--is-public %s ' % is_public return self.manila(cmd, microversion=microversion) @not_found_wrapper @forbidden_wrapper def delete_share(self, shares, share_group_id=None, wait=False, microversion=None): """Deletes share[s] by Names or IDs. :param shares: either str or list of str that can be either Name or ID of a share(s) that should be deleted. :param share_group_id: a common share group ID for the shares being deleted :param wait: bool -- whether to wait for the shares to be deleted """ if not isinstance(shares, list): shares = [shares] cmd = 'delete ' for share in shares: cmd += '%s ' % share if share_group_id: cmd += '--share-group-id %s ' % share_group_id if wait: cmd += '--wait ' return self.manila(cmd, microversion=microversion) @not_found_wrapper @forbidden_wrapper def soft_delete_share(self, shares, microversion=None): """Soft Delete share[s] by Names or IDs. :param shares: either str or list of str that can be either Name or ID of a share(s) that should be soft deleted. """ if not isinstance(shares, list): shares = [shares] cmd = 'soft-delete ' for share in shares: cmd += '%s ' % share return self.manila(cmd, microversion=microversion) @not_found_wrapper @forbidden_wrapper def restore_share(self, shares, microversion=None): """Restore share[s] by Names or IDs. :param shares: either str or list of str that can be either Name or ID of a share(s) that should be soft deleted. """ if not isinstance(shares, list): shares = [shares] cmd = 'restore ' for share in shares: cmd += '%s ' % share return self.manila(cmd, microversion=microversion) def create_share_transfer(self, share_id, name=None, microversion=None): """Create a share transfer. :param share_id: ID of share. ":param name: name of transfer. """ cmd = 'share-transfer-create %s ' % share_id if name: cmd += '--name %s' % name transfer_raw = self.manila(cmd, microversion=microversion) transfer = output_parser.details(transfer_raw) return transfer def delete_share_transfer(self, transfer, microversion=None): """Delete a share transfer. :param transfer: ID or name of share transfer. """ cmd = 'share-transfer-delete %s ' % transfer self.manila(cmd, microversion=microversion) def get_share_transfer(self, transfer, microversion=None): """Get a share transfer. :param transfer: ID or name of share transfer. """ cmd = 'share-transfer-show %s ' % transfer transfer_raw = self.manila(cmd, microversion=microversion) transfer = output_parser.details(transfer_raw) return transfer def list_share_transfer(self, microversion=None): """Get a share transfer.""" cmd = 'share-transfer-list ' transfer_raw = self.manila(cmd, microversion=microversion) transfers = utils.listing(transfer_raw) return transfers def accept_share_transfer(self, transfer, auth_key, microversion=None): """Accept a share transfer. :param transfer: ID or name of share transfer. """ cmd = 'share-transfer-accept %s %s' % (transfer, auth_key) self.manila(cmd, microversion=microversion) def list_shares(self, all_tenants=False, is_soft_deleted=False, filters=None, columns=None, is_public=False, microversion=None): """List shares. :param all_tenants: bool -- whether to list shares that belong only to current project or for all tenants. :param is_soft_deleted: bool -- whether to list shares that has been soft deleted to recycle bin. :param filters: dict -- filters for listing of shares. Example, input: {'project_id': 'foo'} {-'project_id': 'foo'} {--'project_id': 'foo'} {'project-id': 'foo'} will be transformed to filter parameter "--project-id=foo" :param columns: comma separated string of columns. Example, "--columns Name,Size" :param is_public: bool -- should list public shares or not. Default is False. :param microversion: str -- the request api version. """ cmd = 'list ' if all_tenants: cmd += '--all-tenants ' if is_public: cmd += '--public ' if is_soft_deleted: cmd += '--soft-deleted ' if filters and isinstance(filters, dict): for k, v in filters.items(): cmd += '%(k)s=%(v)s ' % { 'k': self._stranslate_to_cli_optional_param(k), 'v': v} if columns is not None: cmd += '--columns ' + columns shares_raw = self.manila(cmd, microversion=microversion) shares = utils.listing(shares_raw) return shares def list_share_instances(self, share_id=None, filters=None, microversion=None): """List share instances. :param share_id: ID of a share to filter by. :param filters: dict -- filters for listing of shares. Example, input: {'project_id': 'foo'} {-'project_id': 'foo'} {--'project_id': 'foo'} {'project-id': 'foo'} will be transformed to filter parameter "--export-location=foo" :param microversion: API microversion to be used for request. """ cmd = 'share-instance-list ' if share_id: cmd += '--share-id %s' % share_id if filters and isinstance(filters, dict): for k, v in filters.items(): cmd += '%(k)s=%(v)s ' % { 'k': self._stranslate_to_cli_optional_param(k), 'v': v} share_instances_raw = self.manila(cmd, microversion=microversion) share_instances = utils.listing(share_instances_raw) return share_instances def is_share_deleted(self, share, microversion=None): """Says whether share is deleted or not. :param share: str -- Name or ID of share """ try: self.get_share(share, microversion=microversion) return False except tempest_lib_exc.NotFound: return True def wait_for_share_deletion(self, share, microversion=None): """Wait for share deletion by its Name or ID. :param share: str -- Name or ID of share """ self.wait_for_resource_deletion( SHARE, res_id=share, interval=5, timeout=300, microversion=microversion) def is_share_transfer_deleted(self, transfer, microversion=None): """Says whether transfer is deleted or not. :param transfer: str -- Name or ID of transfer """ try: self.get_transfer(transfer, microversion=microversion) return False except tempest_lib_exc.NotFound: return True def wait_for_transfer_deletion(self, transfer, microversion=None): """Wait for transfer deletion by its Name or ID. :param transfer: str -- Name or ID of transfer. """ self.wait_for_resource_deletion( SHARE, res_id=transfer, interval=5, timeout=300, microversion=microversion) def wait_for_share_soft_deletion(self, share_id, microversion=None): body = self.get_share(share_id, microversion=microversion) is_soft_deleted = body['is_soft_deleted'] start = int(time.time()) while is_soft_deleted == "False": time.sleep(self.build_interval) body = self.get_share(share_id, microversion=microversion) is_soft_deleted = body['is_soft_deleted'] if is_soft_deleted == "True": return if int(time.time()) - start >= self.build_timeout: message = ("Share %(share_id)s failed to be soft deleted " "within the required time %(build_timeout)s." % {"share_id": share_id, "build_timeout": self.build_timeout}) raise tempest_lib_exc.TimeoutException(message) def wait_for_share_restore(self, share_id, microversion=None): body = self.get_share(share_id, microversion=microversion) is_soft_deleted = body['is_soft_deleted'] start = int(time.time()) while is_soft_deleted == "True": time.sleep(self.build_interval) body = self.get_share(share_id, microversion=microversion) is_soft_deleted = body['is_soft_deleted'] if is_soft_deleted == "False": return if int(time.time()) - start >= self.build_timeout: message = ("Share %(share_id)s failed to be restored " "within the required time %(build_timeout)s." % {"share_id": share_id, "build_timeout": self.build_timeout}) raise tempest_lib_exc.TimeoutException(message) def wait_for_resource_status(self, resource_id, status, microversion=None, resource_type="share"): """Waits for a share to reach a given status.""" get_func = getattr(self, 'get_' + resource_type) body = get_func(resource_id, microversion=microversion) share_status = body['status'] start = int(time.time()) while share_status != status: time.sleep(self.build_interval) body = get_func(resource_id, microversion=microversion) share_status = body['status'] if share_status == status: return elif 'error' in share_status.lower(): raise exceptions.ShareBuildErrorException(share=resource_id) if int(time.time()) - start >= self.build_timeout: message = ("Resource %(resource_id)s failed to reach " "%(status)s status within the required time " "(%(build_timeout)s)." % {"resource_id": resource_id, "status": status, "build_timeout": self.build_timeout}) raise tempest_lib_exc.TimeoutException(message) def wait_for_migration_task_state(self, share_id, dest_host, task_state_to_wait, microversion=None): """Waits for a share to migrate to a certain host.""" statuses = ((task_state_to_wait,) if not isinstance(task_state_to_wait, (tuple, list, set)) else task_state_to_wait) share = self.get_share(share_id, microversion=microversion) start = int(time.time()) while share['task_state'] not in statuses: time.sleep(self.build_interval) share = self.get_share(share_id, microversion=microversion) if share['task_state'] in statuses: break elif share['task_state'] == constants.TASK_STATE_MIGRATION_ERROR: raise exceptions.ShareMigrationException( share_id=share['id'], src=share['host'], dest=dest_host) elif int(time.time()) - start >= self.build_timeout: message = ('Share %(share_id)s failed to reach a status in' '%(status)s when migrating from host %(src)s to ' 'host %(dest)s within the required time ' '%(timeout)s.' % { 'src': share['host'], 'dest': dest_host, 'share_id': share['id'], 'timeout': self.build_timeout, 'status': str(statuses), }) raise tempest_lib_exc.TimeoutException(message) return share @not_found_wrapper def _set_share_metadata(self, share, data, update_all=False, microversion=None): """Sets a share metadata. :param share: str -- Name or ID of a share. :param data: dict -- key-value pairs to set as metadata. :param update_all: bool -- if set True then all keys except provided will be deleted. """ if not (isinstance(data, dict) and data): msg = ('Provided invalid data for setting of share metadata - ' '%s' % data) raise exceptions.InvalidData(message=msg) if update_all: cmd = 'metadata-update-all %s ' % share else: cmd = 'metadata %s set ' % share for k, v in data.items(): cmd += '%(k)s=%(v)s ' % {'k': k, 'v': v} return self.manila(cmd, microversion=microversion) def update_all_share_metadata(self, share, data, microversion=None): metadata_raw = self._set_share_metadata( share, data, True, microversion=microversion) metadata = output_parser.details(metadata_raw) return metadata def set_share_metadata(self, share, data, microversion=None): return self._set_share_metadata( share, data, False, microversion=microversion) @not_found_wrapper def unset_share_metadata(self, share, keys, microversion=None): """Unsets some share metadata by keys. :param share: str -- Name or ID of a share :param keys: str/list -- key or list of keys to unset. """ if not (isinstance(keys, list) and keys): msg = ('Provided invalid data for unsetting of share metadata - ' '%s' % keys) raise exceptions.InvalidData(message=msg) cmd = 'metadata %s unset ' % share for key in keys: cmd += '%s ' % key return self.manila(cmd, microversion=microversion) @not_found_wrapper def get_share_metadata(self, share, microversion=None): """Returns list of all share metadata. :param share: str -- Name or ID of a share. """ metadata_raw = self.manila( 'metadata-show %s' % share, microversion=microversion) metadata = output_parser.details(metadata_raw) return metadata def create_snapshot(self, share, name=None, description=None, force=False, microversion=None): """Creates a snapshot.""" cmd = 'snapshot-create %(share)s ' % {'share': share} if name is None: name = data_utils.rand_name('autotest_snapshot_name') cmd += '--name %s ' % name if description is None: description = data_utils.rand_name('autotest_snapshot_description') cmd += '--description %s ' % description if force: cmd += '--force %s' % force snapshot_raw = self.manila(cmd, microversion=microversion) snapshot = output_parser.details(snapshot_raw) return snapshot @not_found_wrapper def get_snapshot(self, snapshot, microversion=None): """Retrieves a snapshot by its Name or ID.""" snapshot_raw = self.manila('snapshot-show %s' % snapshot, microversion=microversion) snapshot = output_parser.details(snapshot_raw) return snapshot @not_found_wrapper def list_snapshot_export_locations(self, snapshot, columns=None, microversion=None): """List snapshot export locations. :param snapshot: str -- Name or ID of a snapshot. :param columns: str -- comma separated string of columns. Example, "--columns uuid,path". :param microversion: API microversion to be used for request. """ cmd = "snapshot-export-location-list %s" % snapshot if columns is not None: cmd += " --columns " + columns export_locations_raw = self.manila(cmd, microversion=microversion) export_locations = utils.listing(export_locations_raw) return export_locations @not_found_wrapper @forbidden_wrapper def list_snapshot_instance_export_locations(self, snapshot_instance, columns=None, microversion=None): """List snapshot instance export locations. :param snapshot_instance: str -- Name or ID of a snapshot instance. :param columns: str -- comma separated string of columns. Example, "--columns uuid,path". :param microversion: API microversion to be used for request. """ cmd = "snapshot-instance-export-location-list %s" % snapshot_instance if columns is not None: cmd += " --columns " + columns export_locations_raw = self.manila(cmd, microversion=microversion) export_locations = utils.listing(export_locations_raw) return export_locations @not_found_wrapper @forbidden_wrapper def delete_snapshot(self, snapshot, microversion=None): """Deletes snapshot by Names or IDs.""" return self.manila( "snapshot-delete %s" % snapshot, microversion=microversion) def list_snapshot_instances(self, snapshot_id=None, columns=None, detailed=None, microversion=None): """List snapshot instances.""" cmd = 'snapshot-instance-list ' if snapshot_id: cmd += '--snapshot %s' % snapshot_id if columns is not None: cmd += ' --columns ' + columns if detailed: cmd += ' --detailed True ' snapshot_instances_raw = self.manila(cmd, microversion=microversion) snapshot_instances = utils.listing(snapshot_instances_raw) return snapshot_instances def get_snapshot_instance(self, id=None, microversion=None): """Get snapshot instance.""" cmd = 'snapshot-instance-show %s ' % id snapshot_instance_raw = self.manila(cmd, microversion=microversion) snapshot_instance = output_parser.details(snapshot_instance_raw) return snapshot_instance def reset_snapshot_instance(self, id=None, state=None, microversion=None): """Reset snapshot instance status.""" cmd = 'snapshot-instance-reset-state %s ' % id if state: cmd += '--state %s' % state snapshot_instance_raw = self.manila(cmd, microversion=microversion) snapshot_instance = utils.listing(snapshot_instance_raw) return snapshot_instance def is_snapshot_deleted(self, snapshot, microversion=None): """Indicates whether snapshot is deleted or not. :param snapshot: str -- Name or ID of snapshot """ try: self.get_snapshot(snapshot, microversion=microversion) return False except tempest_lib_exc.NotFound: return True def wait_for_snapshot_deletion(self, snapshot, microversion=None): """Wait for snapshot deletion by its Name or ID. :param snapshot: str -- Name or ID of snapshot """ self.wait_for_resource_deletion( SNAPSHOT, res_id=snapshot, interval=5, timeout=300, microversion=microversion) def wait_for_snapshot_status(self, snapshot, status, microversion=None): """Waits for a snapshot to reach a given status.""" body = self.get_snapshot(snapshot, microversion=microversion) snapshot_name = body['name'] snapshot_status = body['status'] start = int(time.time()) while snapshot_status != status: time.sleep(self.build_interval) body = self.get_snapshot(snapshot, microversion=microversion) snapshot_status = body['status'] if snapshot_status == status: return elif 'error' in snapshot_status.lower(): raise exceptions.SnapshotBuildErrorException(snapshot=snapshot) if int(time.time()) - start >= self.build_timeout: message = ( "Snapshot %(snapshot_name)s failed to reach %(status)s " "status within the required time (%(timeout)s s)." % { "snapshot_name": snapshot_name, "status": status, "timeout": self.build_timeout}) raise tempest_lib_exc.TimeoutException(message) @not_found_wrapper def list_access(self, entity_id, columns=None, microversion=None, is_snapshot=False, metadata=None): """Returns list of access rules for a share. :param entity_id: str -- Name or ID of a share or snapshot. :param columns: comma separated string of columns. Example, "--columns access_type,access_to" :param is_snapshot: Boolean value to determine if should list access of a share or snapshot. """ if is_snapshot: cmd = 'snapshot-access-list %s ' % entity_id else: cmd = 'access-list %s ' % entity_id if columns is not None: cmd += ' --columns ' + columns if metadata: metadata_cli = '' for k, v in metadata.items(): metadata_cli += '%(k)s=%(v)s ' % {'k': k, 'v': v} if metadata_cli: cmd += ' --metadata %s ' % metadata_cli access_list_raw = self.manila(cmd, microversion=microversion) return output_parser.listing(access_list_raw) @not_found_wrapper def get_access(self, share_id, access_id, microversion=None, is_snapshot=False): for access in self.list_access(share_id, microversion=microversion, is_snapshot=is_snapshot): if access['id'] == access_id: return access raise tempest_lib_exc.NotFound() @not_found_wrapper def access_show(self, access_id, microversion=None): raw_access = self.manila("access-show %s" % access_id, microversion=microversion) return output_parser.details(raw_access) @not_found_wrapper def access_set_metadata(self, access_id, metadata, microversion=None): if not (isinstance(metadata, dict) and metadata): msg = ('Provided invalid metadata for setting of access rule' ' metadata - %s' % metadata) raise exceptions.InvalidData(message=msg) cmd = "access-metadata %s set " % access_id for k, v in metadata.items(): cmd += '%(k)s=%(v)s ' % {'k': k, 'v': v} return self.manila(cmd, microversion=microversion) @not_found_wrapper def access_unset_metadata(self, access_id, keys, microversion=None): if not (isinstance(keys, (list, tuple, set)) and keys): raise exceptions.InvalidData( message='Provided invalid keys - %s' % keys) cmd = 'access-metadata %s unset ' % access_id for key in keys: cmd += '%s ' % key return self.manila(cmd, microversion=microversion) @not_found_wrapper def snapshot_access_allow(self, snapshot_id, access_type, access_to, microversion=None): raw_access = self.manila( 'snapshot-access-allow %(id)s %(type)s %(access_to)s' % { 'id': snapshot_id, 'type': access_type, 'access_to': access_to, }, microversion=microversion) return output_parser.details(raw_access) @not_found_wrapper def snapshot_access_deny(self, snapshot_id, access_id, microversion=None): return self.manila( 'snapshot-access-deny %(share_id)s %(access_id)s' % { 'share_id': snapshot_id, 'access_id': access_id, }, microversion=microversion) @not_found_wrapper def access_allow(self, share_id, access_type, access_to, access_level, metadata=None, microversion=None): cmd = ('access-allow --access-level %(level)s %(id)s %(type)s ' '%(access_to)s' % { 'level': access_level, 'id': share_id, 'type': access_type, 'access_to': access_to}) if metadata: metadata_cli = '' for k, v in metadata.items(): metadata_cli += '%(k)s=%(v)s ' % {'k': k, 'v': v} if metadata_cli: cmd += ' --metadata %s ' % metadata_cli raw_access = self.manila(cmd, microversion=microversion) return output_parser.details(raw_access) @not_found_wrapper def access_deny(self, share_id, access_id, microversion=None): return self.manila( 'access-deny %(share_id)s %(access_id)s' % { 'share_id': share_id, 'access_id': access_id, }, microversion=microversion) def wait_for_access_rule_status(self, share_id, access_id, state='active', microversion=None, is_snapshot=False): access = self.get_access( share_id, access_id, microversion=microversion, is_snapshot=is_snapshot) start = int(time.time()) while access['state'] != state: time.sleep(self.build_interval) access = self.get_access( share_id, access_id, microversion=microversion, is_snapshot=is_snapshot) if access['state'] == state: return elif access['state'] == 'error': raise exceptions.AccessRuleCreateErrorException( access=access_id) if int(time.time()) - start >= self.build_timeout: message = ( "Access rule %(access)s failed to reach %(state)s state " "within the required time (%(build_timeout)s s)." % { "access": access_id, "state": state, "build_timeout": self.build_timeout}) raise tempest_lib_exc.TimeoutException(message) def wait_for_access_rule_deletion(self, share_id, access_id, microversion=None, is_snapshot=False): try: access = self.get_access( share_id, access_id, microversion=microversion, is_snapshot=is_snapshot) except tempest_lib_exc.NotFound: return start = int(time.time()) while True: time.sleep(self.build_interval) try: access = self.get_access( share_id, access_id, microversion=microversion, is_snapshot=is_snapshot) except tempest_lib_exc.NotFound: return if access['state'] == 'error': raise exceptions.AccessRuleDeleteErrorException( access=access_id) if int(time.time()) - start >= self.build_timeout: message = ( "Access rule %(access)s failed to reach deleted state " "within the required time (%(timeout)s s)." % {"access": access_id, "timeout": self.build_timeout}) raise tempest_lib_exc.TimeoutException(message) def reset_task_state(self, share_id, state, version=None): state = '--task_state %s' % state if state else '' return self.manila('reset-task-state %(state)s %(share)s' % { 'state': state, 'share': share_id, }, microversion=version) def migration_start(self, share_id, dest_host, writable, nondisruptive, preserve_metadata, preserve_snapshots, force_host_assisted_migration, new_share_network=None, new_share_type=None): cmd = ('migration-start %(share)s %(host)s ' '--writable %(writable)s --nondisruptive %(nondisruptive)s ' '--preserve-metadata %(preserve_metadata)s ' '--preserve-snapshots %(preserve_snapshots)s') % { 'share': share_id, 'host': dest_host, 'writable': writable, 'nondisruptive': nondisruptive, 'preserve_metadata': preserve_metadata, 'preserve_snapshots': preserve_snapshots, } if force_host_assisted_migration: cmd += (' --force-host-assisted-migration %s' % force_host_assisted_migration) if new_share_network: cmd += ' --new-share-network %s' % new_share_network if new_share_type: cmd += ' --new-share-type %s' % new_share_type return self.manila(cmd) def migration_complete(self, share_id): return self.manila('migration-complete %s' % share_id) def migration_cancel(self, share_id): return self.manila('migration-cancel %s' % share_id) def migration_get_progress(self, share_id): result = self.manila('migration-get-progress %s' % share_id) return output_parser.details(result) def pool_list(self, detail=False): cmd = 'pool-list' if detail: cmd += ' --column name,host,backend,pool,capabilities' response = self.manila(cmd) return output_parser.listing(response) def create_security_service(self, type='ldap', name=None, description=None, dns_ip=None, ou=None, server=None, domain=None, user=None, password=None, default_ad_site=None, microversion=None): """Creates security service. :param type: security service type (ldap, kerberos or active_directory) :param name: desired name of new security service. :param description: desired description of new security service. :param dns_ip: DNS IP address inside tenant's network. :param ou: security service organizational unit :param server: security service IP address or hostname. :param domain: security service domain. :param user: user of the new security service. :param password: password used by user. :param default_ad_site: default AD site """ cmd = 'security-service-create %s ' % type cmd += self. _combine_security_service_data( name=name, description=description, dns_ip=dns_ip, ou=ou, server=server, domain=domain, user=user, password=password, default_ad_site=default_ad_site) ss_raw = self.manila(cmd, microversion=microversion) security_service = output_parser.details(ss_raw) return security_service @not_found_wrapper def update_security_service(self, security_service, name=None, description=None, dns_ip=None, ou=None, server=None, domain=None, user=None, password=None, default_ad_site=None, microversion=None): cmd = 'security-service-update %s ' % security_service cmd += self. _combine_security_service_data( name=name, description=description, dns_ip=dns_ip, ou=ou, server=server, domain=domain, user=user, password=password, default_ad_site=default_ad_site) return output_parser.details( self.manila(cmd, microversion=microversion)) def _combine_security_service_data(self, name=None, description=None, dns_ip=None, ou=None, server=None, domain=None, user=None, password=None, default_ad_site=None): data = '' if name is not None: data += '--name %s ' % name if description is not None: data += '--description %s ' % description if dns_ip is not None: data += '--dns-ip %s ' % dns_ip if ou is not None: data += '--ou %s ' % ou if server is not None: data += '--server %s ' % server if domain is not None: data += '--domain %s ' % domain if user is not None: data += '--user %s ' % user if password is not None: data += '--password %s ' % password if default_ad_site is not None: data += '--default-ad-site %s ' % default_ad_site return data @not_found_wrapper def list_share_export_locations(self, share, columns=None, microversion=None): """List share export locations. :param share: str -- Name or ID of a share. :param columns: str -- comma separated string of columns. Example, "--columns uuid,path". :param microversion: API microversion to be used for request. """ cmd = "share-export-location-list %s" % share if columns is not None: cmd += " --columns " + columns export_locations_raw = self.manila(cmd, microversion=microversion) export_locations = utils.listing(export_locations_raw) return export_locations @not_found_wrapper def get_snapshot_export_location(self, snapshot, export_location_uuid, microversion=None): """Returns an export location by snapshot and its UUID. :param snapshot: str -- Name or ID of a snapshot. :param export_location_uuid: str -- UUID of an export location. :param microversion: API microversion to be used for request. """ snapshot_raw = self.manila( 'snapshot-export-location-show %(snapshot)s %(el_uuid)s' % { 'snapshot': snapshot, 'el_uuid': export_location_uuid, }, microversion=microversion) snapshot = output_parser.details(snapshot_raw) return snapshot @not_found_wrapper def get_snapshot_instance_export_location( self, snapshot, export_location_uuid, microversion=None): """Returns an export location by snapshot instance and its UUID. :param snapshot: str -- Name or ID of a snapshot instance. :param export_location_uuid: str -- UUID of an export location. :param microversion: API microversion to be used for request. """ snapshot_raw = self.manila( 'snapshot-instance-export-location-show %(snapshot)s %(el_uuid)s' % {'snapshot': snapshot, 'el_uuid': export_location_uuid}, microversion=microversion) snapshot = output_parser.details(snapshot_raw) return snapshot @not_found_wrapper def get_share_export_location(self, share, export_location_uuid, microversion=None): """Returns an export location by share and its UUID. :param share: str -- Name or ID of a share. :param export_location_uuid: str -- UUID of an export location. :param microversion: API microversion to be used for request. """ share_raw = self.manila( 'share-export-location-show %(share)s %(el_uuid)s' % { 'share': share, 'el_uuid': export_location_uuid, }, microversion=microversion) share = output_parser.details(share_raw) return share @not_found_wrapper @forbidden_wrapper def list_share_instance_export_locations(self, share_instance, columns=None, microversion=None): """List share instance export locations. :param share_instance: str -- Name or ID of a share instance. :param columns: str -- comma separated string of columns. Example, "--columns uuid,path". :param microversion: API microversion to be used for request. """ cmd = "share-instance-export-location-list %s" % share_instance if columns is not None: cmd += " --columns " + columns export_locations_raw = self.manila(cmd, microversion=microversion) export_locations = utils.listing(export_locations_raw) return export_locations @not_found_wrapper @forbidden_wrapper def get_share_instance_export_location(self, share_instance, export_location_uuid, microversion=None): """Returns an export location by share instance and its UUID. :param share_instance: str -- Name or ID of a share instance. :param export_location_uuid: str -- UUID of an export location. :param microversion: API microversion to be used for request. """ share_raw = self.manila( 'share-instance-export-location-show ' '%(share_instance)s %(el_uuid)s' % { 'share_instance': share_instance, 'el_uuid': export_location_uuid, }, microversion=microversion) share = output_parser.details(share_raw) return share # Share servers @not_found_wrapper def get_share_server(self, share_server, microversion=None): """Returns share server by its Name or ID.""" share_server_raw = self.manila( 'share-server-show %s' % share_server, microversion=microversion) share_server = output_parser.details(share_server_raw) return share_server def list_share_servers(self, filters=None, columns=None, microversion=None): """List share servers. :param filters: dict -- filters for listing of share servers. Example, input: {'project_id': 'foo'} {'-project_id': 'foo'} {'--project_id': 'foo'} {'project-id': 'foo'} will be transformed to filter parameter "--project-id=foo" :param columns: comma separated string of columns. Example, "--columns id" """ cmd = 'share-server-list ' if columns is not None: cmd += ' --columns ' + columns if filters and isinstance(filters, dict): for k, v in filters.items(): cmd += '%(k)s=%(v)s ' % { 'k': self._stranslate_to_cli_optional_param(k), 'v': v} share_servers_raw = self.manila(cmd, microversion=microversion) share_servers = utils.listing(share_servers_raw) return share_servers @not_found_wrapper def delete_share_server(self, share_server, microversion=None): """Deletes share server by its Name or ID.""" return self.manila('share-server-delete %s' % share_server, microversion=microversion) def is_share_server_deleted(self, share_server_id, microversion=None): """Says whether share server is deleted or not. :param share_server: text -- ID of the share server """ servers = self.list_share_servers(microversion=microversion) for list_element in servers: if share_server_id == list_element['Id']: return False return True def wait_for_share_server_deletion(self, share_server, microversion=None): """Wait for share server deletion by its Name or ID. :param share_server: text -- Name or ID of share server """ self.wait_for_resource_deletion( SHARE_SERVER, res_id=share_server, interval=3, timeout=60, microversion=microversion) def unmanage_share(self, server_id): return self.manila('unmanage %s ' % server_id) def unmanage_server(self, share_server_id): return self.manila('share-server-unmanage %s ' % share_server_id) def share_server_manage(self, host, share_network, identifier, driver_options=None): if driver_options: command = ('share-server-manage %s %s %s %s' % (host, share_network, identifier, driver_options)) else: command = ('share-server-manage %s %s %s' % (host, share_network, identifier)) managed_share_server_raw = self.manila(command) managed_share_server = output_parser.details(managed_share_server_raw) return managed_share_server['id'] def manage_share(self, host, protocol, export_location, share_server): managed_share_raw = self.manila( 'manage %s %s %s --share-server-id %s' % (host, protocol, export_location, share_server)) managed_share = output_parser.details(managed_share_raw) return managed_share['id'] def share_server_migration_check(self, server_id, dest_host, writable, nondisruptive, preserve_snapshots, new_share_network=None): cmd = ('share-server-migration-check %(server_id)s %(host)s ' '--writable %(writable)s --nondisruptive %(nondisruptive)s ' '--preserve-snapshots %(preserve_snapshots)s') % { 'server_id': server_id, 'host': dest_host, 'writable': writable, 'nondisruptive': nondisruptive, 'preserve_snapshots': preserve_snapshots, } if new_share_network: cmd += ' --new-share-network %s' % new_share_network result = self.manila(cmd) return output_parser.details(result) def share_server_migration_start(self, server_id, dest_host, writable=False, nondisruptive=False, preserve_snapshots=False, new_share_network=None): cmd = ('share-server-migration-start %(server_id)s %(host)s ' '--writable %(writable)s --nondisruptive %(nondisruptive)s ' '--preserve-snapshots %(preserve_snapshots)s') % { 'server_id': server_id, 'host': dest_host, 'writable': writable, 'nondisruptive': nondisruptive, 'preserve_snapshots': preserve_snapshots, } if new_share_network: cmd += ' --new-share-network %s' % new_share_network return self.manila(cmd) def share_server_migration_complete(self, server_id): return self.manila('share-server-migration-complete %s' % server_id) def share_server_migration_cancel(self, server_id): return self.manila('share-server-migration-cancel %s' % server_id) def share_server_migration_get_progress(self, server_id): result = self.manila('share-server-migration-get-progress %s' % server_id) return output_parser.details(result) def wait_for_server_migration_task_state(self, share_server_id, dest_host, task_state_to_wait, microversion=None): """Waits for a certain server task state. """ statuses = ((task_state_to_wait,) if not isinstance(task_state_to_wait, (tuple, list, set)) else task_state_to_wait) server = self.get_share_server(share_server=share_server_id, microversion=microversion) start = int(time.time()) while server['task_state'] not in statuses: time.sleep(self.build_interval) server = self.get_share_server(share_server=share_server_id, microversion=microversion) if server['task_state'] in statuses: return server elif server['task_state'] == constants.TASK_STATE_MIGRATION_ERROR: raise exceptions.ShareServerMigrationException( server_id=server['id']) elif int(time.time()) - start >= self.build_timeout: message = ('Server %(share_server_id)s failed to reach the ' 'status in %(status)s while migrating from host ' '%(src)s to host %(dest)s within the required time ' '%(timeout)s.' % { 'src': server['host'], 'dest': dest_host, 'share_server_id': server['id'], 'timeout': self.build_timeout, 'status': str(statuses), }) raise tempest_lib_exc.TimeoutException(message) # user messages def wait_for_message(self, resource_id): """Waits until a message for a resource with given id exists""" start = int(time.time()) message = None while not message: time.sleep(self.build_interval) for msg in self.list_messages(): if msg['Resource ID'] == resource_id: return msg if int(time.time()) - start >= self.build_timeout: message = ('No message for resource with id %s was created in' ' the required time (%s s).' % (resource_id, self.build_timeout)) raise tempest_lib_exc.TimeoutException(message) def list_messages(self, columns=None, microversion=None): """List messages. :param columns: str -- comma separated string of columns. Example, "--columns id,resource_id". :param microversion: API microversion to be used for request. """ cmd = "message-list" if columns is not None: cmd += " --columns " + columns messages_raw = self.manila(cmd, microversion=microversion) messages = utils.listing(messages_raw) return messages @not_found_wrapper def get_message(self, message, microversion=None): """Returns share server by its Name or ID.""" message_raw = self.manila( 'message-show %s' % message, microversion=microversion) message = output_parser.details(message_raw) return message @not_found_wrapper def delete_message(self, message, microversion=None): """Deletes message by its ID.""" return self.manila('message-delete %s' % message, microversion=microversion) def is_message_deleted(self, message, microversion=None): """Indicates whether message is deleted or not. :param message: str -- ID of message """ try: self.get_message(message, microversion=microversion) return False except tempest_lib_exc.NotFound: return True def wait_for_message_deletion(self, message, microversion=None): """Wait for message deletion by its ID. :param message: text -- ID of message """ self.wait_for_resource_deletion( MESSAGE, res_id=message, interval=3, timeout=60, microversion=microversion) # Share replicas def create_share_replica(self, share, availability_zone=None, share_network=None, microversion=None): """Create a share replica. :param share: str -- Name or ID of a share to create a replica of """ cmd = "share-replica-create %s" % share if availability_zone is not None: cmd += " --availability_zone " + availability_zone if share_network is not None: cmd += " --share_network " + share_network replica = self.manila(cmd, microversion=microversion) return output_parser.details(replica) @not_found_wrapper def get_share_replica(self, replica, microversion=None): cmd = "share-replica-show %s" % replica replica = self.manila(cmd, microversion=microversion) return output_parser.details(replica) @not_found_wrapper @forbidden_wrapper def delete_share_replica(self, share_replica, microversion=None): """Deletes share replica by ID.""" return self.manila( "share-replica-delete %s" % share_replica, microversion=microversion) def is_share_replica_deleted(self, replica, microversion=None): """Indicates whether a share replica is deleted or not. :param replica: str -- ID of share replica """ try: self.get_share_replica(replica, microversion=microversion) return False except tempest_lib_exc.NotFound: return True def wait_for_share_replica_deletion(self, replica, microversion=None): """Wait for share replica deletion by its ID. :param replica: text -- ID of share replica """ self.wait_for_resource_deletion( SHARE_REPLICA, res_id=replica, interval=3, timeout=60, microversion=microversion) def wait_for_share_replica_status(self, share_replica, status="available", microversion=None): """Waits for a share replica to reach a given status.""" replica = self.get_share_replica(share_replica, microversion=microversion) share_replica_status = replica['status'] start = int(time.time()) while share_replica_status != status: time.sleep(self.build_interval) replica = self.get_share_replica(share_replica, microversion=microversion) share_replica_status = replica['status'] if share_replica_status == status: return replica elif 'error' in share_replica_status.lower(): raise exceptions.ShareReplicaBuildErrorException( replica=share_replica) if int(time.time()) - start >= self.build_timeout: message = ( "Share replica %(id)s failed to reach %(status)s " "status within the required time " "(%(build_timeout)s s)." % { "id": share_replica, "status": status, "build_timeout": self.build_timeout}) raise tempest_lib_exc.TimeoutException(message) return replica @not_found_wrapper @forbidden_wrapper def list_share_replica_export_locations(self, share_replica, columns=None, microversion=None): """List share replica export locations. :param share_replica: str -- ID of share replica. :param columns: str -- comma separated string of columns. Example, "--columns id,path". :param microversion: API microversion to be used for request. """ cmd = "share-replica-export-location-list %s" % share_replica if columns is not None: cmd += " --columns " + columns export_locations_raw = self.manila(cmd, microversion=microversion) export_locations = utils.listing(export_locations_raw) return export_locations @not_found_wrapper @forbidden_wrapper def get_share_replica_export_location(self, share_replica, export_location_uuid, microversion=None): """Returns an export location by share replica and export location ID. :param share_replica: str -- ID of share replica. :param export_location_uuid: str -- UUID of an export location. :param microversion: API microversion to be used for request. """ export_raw = self.manila( 'share-replica-export-location-show ' '%(share_replica)s %(el_uuid)s' % { 'share_replica': share_replica, 'el_uuid': export_location_uuid, }, microversion=microversion) export = output_parser.details(export_raw) return export