
The snapshot_support extra spec has always meant two things: a driver can take snapshots and create shares from snapshots. As we add alternate snapshot semantics, it is likely that some drivers will want to support snapshots and some of the new semantics while being unable to create new shares from snapshots. This work adds support to manila client for the new extra spec in manila server, create_share_from_snapshot_support. Depends-On: Ib0ad5fbfdf6297665c208149b08c8d21b3c232be Implements: blueprint add-create-share-from-snapshot-extra-spec Change-Id: I07b70f04e6fb2b5797557c4f4796c6883680eff3
1175 lines
47 KiB
Python
1175 lines
47 KiB
Python
# 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 re
|
|
import time
|
|
|
|
from oslo_utils import strutils
|
|
import six
|
|
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 import config
|
|
from manilaclient.tests.functional import exceptions
|
|
from manilaclient.tests.functional import utils
|
|
|
|
CONF = config.CONF
|
|
SHARE = 'share'
|
|
SHARE_TYPE = 'share_type'
|
|
SHARE_NETWORK = 'share_network'
|
|
SHARE_SERVER = 'share_server'
|
|
SNAPSHOT = 'snapshot'
|
|
|
|
|
|
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 ('No (\w+) with a name or ID', 'not(.*){0,5}found'):
|
|
if re.search(regexp, 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', 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):
|
|
"""Resource deletion waiter.
|
|
|
|
:param res_type: text -- type of resource. Supported only 'share_type'.
|
|
Other types support is TODO.
|
|
: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
|
|
"""
|
|
# TODO(vponomaryov): add support for other resource types
|
|
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_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
|
|
else:
|
|
raise exceptions.InvalidResource(message=res_type)
|
|
|
|
end_loop_time = time.time() + timeout
|
|
deleted = func(res_id, microversion=microversion)
|
|
|
|
while not (deleted or time.time() > end_loop_time):
|
|
time.sleep(interval)
|
|
deleted = func(res_id, microversion=microversion)
|
|
|
|
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, is_public=True,
|
|
microversion=None, extra_specs=None):
|
|
"""Creates share type.
|
|
|
|
:param name: text -- name of share type to use, if not set then
|
|
autogenerated will be used
|
|
: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.
|
|
"""
|
|
if name is None:
|
|
name = data_utils.rand_name('manilaclient_functional_test')
|
|
dhss = driver_handles_share_servers
|
|
if not isinstance(dhss, six.string_types):
|
|
dhss = six.text_type(dhss)
|
|
if not isinstance(is_public, six.string_types):
|
|
is_public = six.text_type(is_public)
|
|
|
|
cmd = ('type-create %(name)s %(dhss)s --is-public %(is_public)s ') % {
|
|
'name': name, 'dhss': dhss, 'is_public': is_public}
|
|
|
|
if snapshot_support is not None:
|
|
if not isinstance(snapshot_support, six.string_types):
|
|
snapshot_support = six.text_type(snapshot_support)
|
|
cmd += " --snapshot-support " + snapshot_support
|
|
|
|
if create_share_from_snapshot is not None:
|
|
if not isinstance(create_share_from_snapshot, six.string_types):
|
|
create_share_from_snapshot = six.text_type(
|
|
create_share_from_snapshot)
|
|
cmd += (" --create-share-from-snapshot-support " +
|
|
create_share_from_snapshot)
|
|
|
|
if extra_specs is not None:
|
|
extra_spec_str = ''
|
|
for k, v in extra_specs.items():
|
|
if not isinstance(v, six.string_types):
|
|
extra_specs[k] = six.text_type(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
|
|
|
|
@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, microversion=None):
|
|
"""List share types.
|
|
|
|
:param list_all: bool -- whether to list all share types or only public
|
|
:param columns: comma separated string of columns.
|
|
Example, "--columns id,name"
|
|
"""
|
|
cmd = 'type-list'
|
|
if list_all:
|
|
cmd += ' --all'
|
|
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
|
|
"""
|
|
share_types = self.list_share_types(True, microversion=microversion)
|
|
for st in share_types:
|
|
if share_type in (st['ID'], st['Name']):
|
|
return st
|
|
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" if "/v3" in (CONF.admin_auth_url or CONF.auth_url) else "2.0")
|
|
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, 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)
|
|
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):
|
|
"""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
|
|
cmd = ''
|
|
for key, value in data.items():
|
|
cmd += "%(k)s=%(v)s " % dict(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, six.string_types):
|
|
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 projects.
|
|
: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 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)
|
|
|
|
# Shares
|
|
|
|
def create_share(self, share_protocol, size, share_network=None,
|
|
share_type=None, name=None, description=None,
|
|
public=False, snapshot=None, metadata=None,
|
|
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 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
|
|
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, 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.
|
|
"""
|
|
if not isinstance(shares, list):
|
|
shares = [shares]
|
|
cmd = 'delete '
|
|
for share in shares:
|
|
cmd += '%s ' % share
|
|
return self.manila(cmd, microversion=microversion)
|
|
|
|
def list_shares(self, all_tenants=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 projects.
|
|
: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.
|
|
"""
|
|
cmd = 'list '
|
|
if all_tenants:
|
|
cmd += '--all-tenants '
|
|
if is_public:
|
|
cmd += '--public '
|
|
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, microversion=None):
|
|
"""List share instances.
|
|
|
|
:param share_id: ID of a share to filter by.
|
|
:param microversion: API microversion to be used for request.
|
|
"""
|
|
cmd = 'share-instance-list '
|
|
if share_id:
|
|
cmd += '--share-id %s' % share_id
|
|
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 wait_for_share_status(self, share, status, microversion=None):
|
|
"""Waits for a share to reach a given status."""
|
|
body = self.get_share(share, microversion=microversion)
|
|
share_name = body['name']
|
|
share_status = body['status']
|
|
start = int(time.time())
|
|
|
|
while share_status != status:
|
|
time.sleep(self.build_interval)
|
|
body = self.get_share(share, microversion=microversion)
|
|
share_status = body['status']
|
|
|
|
if share_status == status:
|
|
return
|
|
elif 'error' in share_status.lower():
|
|
raise exceptions.ShareBuildErrorException(share=share)
|
|
|
|
if int(time.time()) - start >= self.build_timeout:
|
|
message = (
|
|
"Share %(share_name)s failed to reach %(status)s status "
|
|
"within the required time (%(build_timeout)s s)." % {
|
|
"share_name": share_name, "status": status,
|
|
"build_timeout": self.build_timeout})
|
|
raise tempest_lib_exc.TimeoutException(message)
|
|
|
|
@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
|
|
@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, share_id, columns=None, microversion=None):
|
|
"""Returns list of access rules for a share.
|
|
|
|
:param share_id: str -- Name or ID of a share.
|
|
:param columns: comma separated string of columns.
|
|
Example, "--columns access_type,access_to"
|
|
"""
|
|
cmd = 'access-list %s ' % share_id
|
|
if columns is not None:
|
|
cmd += ' --columns ' + columns
|
|
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):
|
|
for access in self.list_access(share_id, microversion=microversion):
|
|
if access['id'] == access_id:
|
|
return access
|
|
raise tempest_lib_exc.NotFound()
|
|
|
|
@not_found_wrapper
|
|
def access_allow(self, share_id, access_type, access_to, access_level,
|
|
microversion=None):
|
|
raw_access = self.manila(
|
|
'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,
|
|
},
|
|
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):
|
|
access = self.get_access(
|
|
share_id, access_id, microversion=microversion)
|
|
|
|
start = int(time.time())
|
|
while access['state'] != state:
|
|
time.sleep(self.build_interval)
|
|
access = self.get_access(
|
|
share_id, access_id, microversion=microversion)
|
|
|
|
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):
|
|
try:
|
|
access = self.get_access(
|
|
share_id, access_id, microversion=microversion)
|
|
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)
|
|
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 (%s s)." % 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 create_security_service(self, type='ldap', name=None, description=None,
|
|
dns_ip=None, server=None, domain=None,
|
|
user=None, password=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 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.
|
|
"""
|
|
|
|
cmd = 'security-service-create %s ' % type
|
|
cmd += self. _combine_security_service_data(
|
|
name=name,
|
|
description=description,
|
|
dns_ip=dns_ip,
|
|
server=server,
|
|
domain=domain,
|
|
user=user,
|
|
password=password)
|
|
|
|
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, server=None,
|
|
domain=None, user=None, password=None,
|
|
microversion=None):
|
|
cmd = 'security-service-update %s ' % security_service
|
|
cmd += self. _combine_security_service_data(
|
|
name=name,
|
|
description=description,
|
|
dns_ip=dns_ip,
|
|
server=server,
|
|
domain=domain,
|
|
user=user,
|
|
password=password)
|
|
return output_parser.details(
|
|
self.manila(cmd, microversion=microversion))
|
|
|
|
def _combine_security_service_data(self, name=None, description=None,
|
|
dns_ip=None, server=None, domain=None,
|
|
user=None, password=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 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
|
|
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_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)
|