This is mostly auto-generated, save for having to manually move some
'noqa' lines and move some printf arguments outside of localized string
calls.

Change-Id: I48cd5ead0953d7d9b03535172a60f4727e95e935
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
This commit is contained in:
Stephen Finucane
2025-10-10 10:51:18 +01:00
parent b255a5c28d
commit 0ef57c0f49
231 changed files with 21099 additions and 15465 deletions
+12 -6
View File
@@ -14,6 +14,18 @@ repos:
- id: check-yaml
files: .*\.(yaml|yml)$
exclude: '^(zuul.d|rally-jobs)/.*$'
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.0
hooks:
- id: ruff-check
args: ['--fix', '--unsafe-fixes']
- id: ruff-format
- repo: https://opendev.org/openstack/hacking
rev: 7.0.0
hooks:
- id: hacking
additional_dependencies: []
exclude: '^(doc|releasenotes|tools)/.*$'
- repo: https://github.com/PyCQA/doc8
rev: v2.0.0
hooks:
@@ -24,9 +36,3 @@ repos:
hooks:
- id: bashate
args: ['--ignore', 'E006,E042,E043']
- repo: https://opendev.org/openstack/hacking
rev: 7.0.0
hooks:
- id: hacking
additional_dependencies: []
exclude: '^(doc|releasenotes|tools)/.*$'
+13 -6
View File
@@ -29,10 +29,12 @@ sys.path.insert(0, ROOT)
# Add any Sphinx extension module names here, as strings. They can be
# extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc',
'openstackdocstheme',
'sphinxcontrib.programoutput',
'cliff.sphinxext']
extensions = [
'sphinx.ext.autodoc',
'openstackdocstheme',
'sphinxcontrib.programoutput',
'cliff.sphinxext',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -87,8 +89,13 @@ latex_use_xindy = False
# (source start file, target name, title, author, documentclass
# [howto/manual]).
latex_documents = [
('index', 'Manila-client.tex', 'Manila Python Client Documentation',
'Manila contributors', 'manual'),
(
'index',
'Manila-client.tex',
'Manila Python Client Documentation',
'Manila contributors',
'manual',
),
]
# The name of an image file (relative to this directory) to place at the top of
+2 -1
View File
@@ -32,4 +32,5 @@ except AttributeError:
API_MAX_VERSION = api_versions.APIVersion(api_versions.MAX_VERSION)
API_MIN_VERSION = api_versions.APIVersion(api_versions.MIN_VERSION)
API_DEPRECATED_VERSION = api_versions.APIVersion(
api_versions.DEPRECATED_VERSION)
api_versions.DEPRECATED_VERSION
)
+131 -79
View File
@@ -33,7 +33,7 @@ DEPRECATED_VERSION = '1.0'
_VERSIONED_METHOD_MAP = {}
class APIVersion(object):
class APIVersion:
"""Top level object to support Manila API Versioning.
This class represents an API Version with convenience
@@ -54,44 +54,57 @@ class APIVersion(object):
self.ver_major = int(match.group(1))
self.ver_minor = int(match.group(2))
else:
msg = _("Invalid format of client version '%s'. "
msg = (
_(
"Invalid format of client version '%s'. "
"Expected format 'X.Y', where X is a major part and Y "
"is a minor part of version.") % version_str
"is a minor part of version."
)
% version_str
)
raise exceptions.UnsupportedVersion(msg)
def __str__(self):
"""Debug/Logging representation of object."""
return ("API Version Major: %s, Minor: %s"
% (self.ver_major, self.ver_minor))
return f"API Version Major: {self.ver_major}, Minor: {self.ver_minor}"
def __repr__(self):
if self.is_null():
return "<APIVersion: null>"
else:
return "<APIVersion: %s>" % self.get_string()
return f"<APIVersion: {self.get_string()}>"
def __lt__(self, other):
if not isinstance(other, APIVersion):
raise TypeError(self.TYPE_ERROR_MSG % {"other": other,
"cls": self.__class__})
raise TypeError(
self.TYPE_ERROR_MSG % {"other": other, "cls": self.__class__}
)
return ((self.ver_major, self.ver_minor) <
(other.ver_major, other.ver_minor))
return (self.ver_major, self.ver_minor) < (
other.ver_major,
other.ver_minor,
)
def __eq__(self, other):
if not isinstance(other, APIVersion):
raise TypeError(self.TYPE_ERROR_MSG % {"other": other,
"cls": self.__class__})
raise TypeError(
self.TYPE_ERROR_MSG % {"other": other, "cls": self.__class__}
)
return ((self.ver_major, self.ver_minor) ==
(other.ver_major, other.ver_minor))
return (self.ver_major, self.ver_minor) == (
other.ver_major,
other.ver_minor,
)
def __gt__(self, other):
if not isinstance(other, APIVersion):
raise TypeError(self.TYPE_ERROR_MSG % {"other": other,
"cls": self.__class__})
return ((self.ver_major, self.ver_minor) >
(other.ver_major, other.ver_minor))
raise TypeError(
self.TYPE_ERROR_MSG % {"other": other, "cls": self.__class__}
)
return (self.ver_major, self.ver_minor) > (
other.ver_major,
other.ver_minor,
)
def __le__(self, other):
return self < other or self == other
@@ -139,15 +152,15 @@ class APIVersion(object):
"""String representation of an APIVersion object."""
if self.is_null():
raise ValueError(
_("Null APIVersion cannot be converted to string."))
return "%s.%s" % (self.ver_major, self.ver_minor)
_("Null APIVersion cannot be converted to string.")
)
return f"{self.ver_major}.{self.ver_minor}"
def get_major_version(self):
return "%s" % self.ver_major
return f"{self.ver_major}"
class VersionedMethod(object):
class VersionedMethod:
def __init__(self, name, start_version, end_version, func):
"""Versioning information for a single method
@@ -164,11 +177,10 @@ class VersionedMethod(object):
self.func = func
def __str__(self):
return ("Version Method %s: min: %s, max: %s"
% (self.name, self.start_version, self.end_version))
return f"Version Method {self.name}: min: {self.start_version}, max: {self.end_version}"
def __repr__(self):
return "<VersionedMethod %s>" % self.name
return f"<VersionedMethod {self.name}>"
def check_version_supported(api_version):
@@ -176,23 +188,27 @@ def check_version_supported(api_version):
:warn Sends warning if version is not supported.
"""
if (check_version_matches_min_max(api_version) or
check_version_deprecated(api_version)):
if check_version_matches_min_max(api_version) or check_version_deprecated(
api_version
):
return True
return False
def check_version_matches_min_max(api_version):
"""Returns True if the API version is within the supported range."""
if (not api_version.matches(
manilaclient.API_MIN_VERSION,
manilaclient.API_MAX_VERSION)):
msg = _("Invalid client version '%(version)s'. "
"Current version range is '%(min)s' through "
" '%(max)s'") % {
if not api_version.matches(
manilaclient.API_MIN_VERSION, manilaclient.API_MAX_VERSION
):
msg = _(
"Invalid client version '%(version)s'. "
"Current version range is '%(min)s' through "
" '%(max)s'"
) % {
"version": api_version.get_string(),
"min": manilaclient.API_MIN_VERSION.get_string(),
"max": manilaclient.API_MAX_VERSION.get_string()}
"max": manilaclient.API_MAX_VERSION.get_string(),
}
warnings.warn(msg)
return False
return True
@@ -202,7 +218,8 @@ def check_version_deprecated(api_version):
"""Returns True if API version is deprecated."""
if api_version == manilaclient.API_DEPRECATED_VERSION:
msg = _("Client version '%(version)s' is deprecated.") % {
"version": api_version.get_string()}
"version": api_version.get_string()
}
warnings.warn(msg)
return True
return False
@@ -249,27 +266,29 @@ def discover_version(client, requested_version):
:returns: APIVersion
"""
server_start_version, server_end_version = _get_server_version_range(
client)
client
)
valid_version = requested_version
if server_start_version.is_null() and server_end_version.is_null():
msg = ("Server does not support microversions. Changing server "
"version to %(min_version)s.")
msg = (
"Server does not support microversions. Changing server "
"version to %(min_version)s."
)
LOG.debug(msg, {"min_version": DEPRECATED_VERSION})
valid_version = APIVersion(DEPRECATED_VERSION)
else:
valid_version = _validate_requested_version(
requested_version,
server_start_version,
server_end_version)
requested_version, server_start_version, server_end_version
)
_validate_server_version(server_start_version, server_end_version)
return valid_version
def _validate_requested_version(requested_version,
server_start_version,
server_end_version):
def _validate_requested_version(
requested_version, server_start_version, server_end_version
):
"""Validates the requested version.
Checks 'requested_version' is within the min/max range supported by the
@@ -284,21 +303,34 @@ def _validate_requested_version(requested_version,
valid_version = requested_version
if not requested_version.matches(server_start_version, server_end_version):
if server_end_version <= requested_version:
if (manilaclient.API_MIN_VERSION <= server_end_version and
server_end_version <= manilaclient.API_MAX_VERSION):
msg = _("Requested version %(requested_version)s is "
"not supported. Downgrading requested version "
"to %(server_end_version)s.")
LOG.debug(msg, {
"requested_version": requested_version,
"server_end_version": server_end_version})
if (
manilaclient.API_MIN_VERSION <= server_end_version
and server_end_version <= manilaclient.API_MAX_VERSION
):
msg = _(
"Requested version %(requested_version)s is "
"not supported. Downgrading requested version "
"to %(server_end_version)s."
)
LOG.debug(
msg,
{
"requested_version": requested_version,
"server_end_version": server_end_version,
},
)
valid_version = server_end_version
else:
raise exceptions.UnsupportedVersion(
_("The specified version isn't supported by server. The valid "
"version range is '%(min)s' to '%(max)s'") % {
_(
"The specified version isn't supported by server. The valid "
"version range is '%(min)s' to '%(max)s'"
)
% {
"min": server_start_version.get_string(),
"max": server_end_version.get_string()})
"max": server_end_version.get_string(),
}
)
return valid_version
@@ -316,22 +348,32 @@ def _validate_server_version(server_start_version, server_end_version):
"""
if manilaclient.API_MIN_VERSION > server_end_version:
raise exceptions.UnsupportedVersion(
_("Server's version is too old. The client's valid version range "
"is '%(client_min)s' to '%(client_max)s'. The server valid "
"version range is '%(server_min)s' to '%(server_max)s'.") % {
'client_min': manilaclient.API_MIN_VERSION.get_string(),
'client_max': manilaclient.API_MAX_VERSION.get_string(),
'server_min': server_start_version.get_string(),
'server_max': server_end_version.get_string()})
_(
"Server's version is too old. The client's valid version range "
"is '%(client_min)s' to '%(client_max)s'. The server valid "
"version range is '%(server_min)s' to '%(server_max)s'."
)
% {
'client_min': manilaclient.API_MIN_VERSION.get_string(),
'client_max': manilaclient.API_MAX_VERSION.get_string(),
'server_min': server_start_version.get_string(),
'server_max': server_end_version.get_string(),
}
)
elif manilaclient.API_MAX_VERSION < server_start_version:
raise exceptions.UnsupportedVersion(
_("Server's version is too new. The client's valid version range "
"is '%(client_min)s' to '%(client_max)s'. The server valid "
"version range is '%(server_min)s' to '%(server_max)s'.") % {
'client_min': manilaclient.API_MIN_VERSION.get_string(),
'client_max': manilaclient.API_MAX_VERSION.get_string(),
'server_min': server_start_version.get_string(),
'server_max': server_end_version.get_string()})
_(
"Server's version is too new. The client's valid version range "
"is '%(client_min)s' to '%(client_max)s'. The server valid "
"version range is '%(server_min)s' to '%(server_max)s'."
)
% {
'client_min': manilaclient.API_MIN_VERSION.get_string(),
'client_max': manilaclient.API_MAX_VERSION.get_string(),
'server_min': server_start_version.get_string(),
'server_max': server_end_version.get_string(),
}
)
def add_versioned_method(versioned_method):
@@ -342,8 +384,11 @@ def add_versioned_method(versioned_method):
def get_versioned_methods(func_name, api_version=None):
versioned_methods = _VERSIONED_METHOD_MAP.get(func_name, [])
if api_version and not api_version.is_null():
return [m for m in versioned_methods
if api_version.matches(m.start_version, m.end_version)]
return [
m
for m in versioned_methods
if api_version.matches(m.start_version, m.end_version)
]
return versioned_methods
@@ -353,11 +398,13 @@ def experimental_api(f):
@functools.wraps(f)
def _wrapper(*args, **kwargs):
client = args[0]
if (isinstance(client, manilaclient.v2.client.Client) or
hasattr(client, 'client')):
if isinstance(client, manilaclient.v2.client.Client) or hasattr(
client, 'client'
):
dh = client.client.default_headers
dh[constants.EXPERIMENTAL_HTTP_HEADER] = 'true'
return f(*args, **kwargs)
return _wrapper
@@ -378,8 +425,9 @@ def wraps(start_version, end_version=MAX_VERSION):
def decor(func):
func.versioned = True
name = utils.get_function_name(func)
versioned_method = VersionedMethod(name, start_version,
end_version, func)
versioned_method = VersionedMethod(
name, start_version, end_version, func
)
add_versioned_method(versioned_method)
@functools.wraps(func)
@@ -388,11 +436,15 @@ def wraps(start_version, end_version=MAX_VERSION):
if not methods:
raise exceptions.UnsupportedVersion(
_("API version '%(version)s' is not supported on "
"'%(method)s' method.") % {
_(
"API version '%(version)s' is not supported on "
"'%(method)s' method."
)
% {
"version": obj.api_version.get_string(),
"method": name,
})
}
)
method = max(methods, key=lambda f: f.start_version)
+67 -52
View File
@@ -54,6 +54,7 @@ class Manager(utils.HookableMixin):
Managers interact with a particular type of API (shares, snapshots,
etc.) and provide CRUD operations for them.
"""
resource_class = None
def __init__(self, api):
@@ -64,8 +65,9 @@ class Manager(utils.HookableMixin):
def api_version(self):
return self.api.api_version
def _list(self, url, response_key, manager=None, body=None,
return_raw=None):
def _list(
self, url, response_key, manager=None, body=None, return_raw=None
):
"""List the collection.
:param url: a partial URL, e.g., '/shares'
@@ -100,8 +102,9 @@ class Manager(utils.HookableMixin):
with self.completion_cache('uuid', obj_class, mode="w"):
if return_raw:
return data
resource = [obj_class(manager, res, loaded=True)
for res in data if res]
resource = [
obj_class(manager, res, loaded=True) for res in data if res
]
if 'count' in body:
return resource, body['count']
else:
@@ -121,16 +124,19 @@ class Manager(utils.HookableMixin):
Delete is not handled because listings are assumed to be performed
often enough to keep the cache reasonably up-to-date.
"""
base_dir = cliutils.env('manilaclient_UUID_CACHE_DIR',
'MANILACLIENT_UUID_CACHE_DIR',
default="~/.cache/manilaclient")
base_dir = cliutils.env(
'manilaclient_UUID_CACHE_DIR',
'MANILACLIENT_UUID_CACHE_DIR',
default="~/.cache/manilaclient",
)
# NOTE(sirp): Keep separate UUID caches for each username + endpoint
# pair
username = cliutils.env('OS_USERNAME', 'MANILA_USERNAME')
url = cliutils.env('OS_URL', 'MANILA_URL')
uniqifier = hashlib.sha256(username.encode('utf-8') +
url.encode('utf-8')).hexdigest()
uniqifier = hashlib.sha256(
username.encode('utf-8') + url.encode('utf-8')
).hexdigest()
cache_dir = os.path.expanduser(os.path.join(base_dir, uniqifier))
@@ -143,14 +149,14 @@ class Manager(utils.HookableMixin):
pass
resource = obj_class.__name__.lower()
filename = "%s-%s-cache" % (resource, cache_type.replace('_', '-'))
filename = "{}-{}-cache".format(resource, cache_type.replace('_', '-'))
path = os.path.join(cache_dir, filename)
cache_attr = "_%s_cache" % cache_type
cache_attr = f"_{cache_type}_cache"
try:
setattr(self, cache_attr, open(path, mode))
except IOError:
except OSError:
# NOTE(kiall): This is typically a permission denied while
# attempting to write the cache file.
pass
@@ -164,10 +170,10 @@ class Manager(utils.HookableMixin):
delattr(self, cache_attr)
def write_to_completion_cache(self, cache_type, val):
cache = getattr(self, "_%s_cache" % cache_type, None)
cache = getattr(self, f"_{cache_type}_cache", None)
if cache:
try:
cache.write("%s\n" % val)
cache.write(f"{val}\n")
except UnicodeEncodeError:
pass
@@ -183,8 +189,11 @@ class Manager(utils.HookableMixin):
def _get_with_base_url(self, url, response_key=None):
resp, body = self.api.client.get_with_base_url(url)
if response_key:
return [self.resource_class(self, res, loaded=True)
for res in body[response_key] if res]
return [
self.resource_class(self, res, loaded=True)
for res in body[response_key]
if res
]
else:
return self.resource_class(self, body, loaded=True)
@@ -215,14 +224,14 @@ class Manager(utils.HookableMixin):
def _build_query_string(self, search_opts):
search_opts = search_opts or {}
params = sorted(
[(k, v) for (k, v) in search_opts.items() if v])
query_string = "?%s" % utils.safe_urlencode(params) if params else ''
params = sorted([(k, v) for (k, v) in search_opts.items() if v])
query_string = f"?{utils.safe_urlencode(params)}" if params else ''
return query_string
class ManagerWithFind(Manager):
"""Like a `Manager`, but with additional `find()`/`findall()` methods."""
def find(self, **kwargs):
"""Find a single item with attributes matching ``**kwargs``.
@@ -232,7 +241,7 @@ class ManagerWithFind(Manager):
matches = self.findall(**kwargs)
num_matches = len(matches)
if num_matches == 0:
msg = "No %s matching %s." % (self.resource_class.__name__, kwargs)
msg = f"No {self.resource_class.__name__} matching {kwargs}."
raise exceptions.NotFound(404, msg)
elif num_matches > 1:
raise exceptions.NoUniqueMatch
@@ -250,16 +259,17 @@ class ManagerWithFind(Manager):
search_opts = {'all_tenants': 1}
resources = self.list(search_opts=search_opts)
if ('v2.shares.ShareManager' in str(self.__class__) and
self.api_version >= api_versions.APIVersion("2.69")):
search_opts_2 = {'all_tenants': 1,
'is_soft_deleted': True}
if 'v2.shares.ShareManager' in str(
self.__class__
) and self.api_version >= api_versions.APIVersion("2.69"):
search_opts_2 = {'all_tenants': 1, 'is_soft_deleted': True}
shares_soft_deleted = self.list(search_opts=search_opts_2)
resources += shares_soft_deleted
for obj in resources:
try:
if all(getattr(obj, attr) == value
for (attr, value) in searches):
if all(
getattr(obj, attr) == value for (attr, value) in searches
):
found.append(obj)
except AttributeError:
continue
@@ -270,7 +280,7 @@ class ManagerWithFind(Manager):
raise NotImplementedError
class Resource(object):
class Resource:
"""Base class for OpenStack resources (tenant, user, etc.).
This is pretty much just a bag for attributes.
@@ -292,11 +302,11 @@ class Resource(object):
self._loaded = loaded
def __repr__(self):
reprkeys = sorted(k
for k in self.__dict__.keys()
if k[0] != '_' and k != 'manager')
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
return "<%s %s>" % (self.__class__.__name__, info)
reprkeys = sorted(
k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager'
)
info = ", ".join(f"{k}={getattr(self, k)}" for k in reprkeys)
return f"<{self.__class__.__name__} {info}>"
@property
def human_id(self):
@@ -308,7 +318,7 @@ class Resource(object):
return None
def _add_details(self, info):
for (k, v) in info.items():
for k, v in info.items():
try:
setattr(self, k, v)
self._info[k] = v
@@ -366,7 +376,6 @@ class Resource(object):
class MetadataCapableResource(Resource, metaclass=abc.ABCMeta):
superresource = None
def _get_subresource_and_resource(self, superresource):
@@ -388,7 +397,8 @@ class MetadataCapableResource(Resource, metaclass=abc.ABCMeta):
"""
resource, subresource = self._get_subresource_and_resource(
superresource)
superresource
)
return self.manager.get_metadata(resource, subresource=subresource)
@@ -403,10 +413,12 @@ class MetadataCapableResource(Resource, metaclass=abc.ABCMeta):
by default
"""
resource, subresource = self._get_subresource_and_resource(
superresource)
superresource
)
return self.manager.set_metadata(resource, metadata,
subresource=subresource)
return self.manager.set_metadata(
resource, metadata, subresource=subresource
)
def delete_metadata(self, keys, superresource=None):
"""Delete specified keys from the given resource.
@@ -418,10 +430,12 @@ class MetadataCapableResource(Resource, metaclass=abc.ABCMeta):
by default
"""
resource, subresource = self._get_subresource_and_resource(
superresource)
superresource
)
return self.manager.delete_metadata(resource, keys,
subresource=subresource)
return self.manager.delete_metadata(
resource, keys, subresource=subresource
)
def update_all_metadata(self, metadata, superresource=None):
"""Update all metadata for this resource.
@@ -434,11 +448,12 @@ class MetadataCapableResource(Resource, metaclass=abc.ABCMeta):
by default
"""
resource, subresource = self._get_subresource_and_resource(
superresource)
superresource
)
return self.manager.update_all_metadata(resource,
metadata,
subresource=subresource)
return self.manager.update_all_metadata(
resource, metadata, subresource=subresource
)
class MetadataCapableManager(ManagerWithFind, metaclass=abc.ABCMeta):
@@ -458,8 +473,9 @@ class MetadataCapableManager(ManagerWithFind, metaclass=abc.ABCMeta):
subresource = getid(subresource)
resource = f"{resource}{self.subresource_path}/{subresource}"
return self._get(f"{self.resource_path}/{resource}/metadata",
"metadata")
return self._get(
f"{self.resource_path}/{resource}/metadata", "metadata"
)
def set_metadata(self, resource, metadata, subresource=None):
"""Set or update metadata for resource.
@@ -475,9 +491,9 @@ class MetadataCapableManager(ManagerWithFind, metaclass=abc.ABCMeta):
subresource = getid(subresource)
resource = f"{resource}{self.subresource_path}/{subresource}"
return self._create(f"{self.resource_path}/{resource}/metadata",
body,
"metadata")
return self._create(
f"{self.resource_path}/{resource}/metadata", body, "metadata"
)
def delete_metadata(self, resource, keys, subresource=None):
"""Delete specified keys from resource metadata.
@@ -508,5 +524,4 @@ class MetadataCapableManager(ManagerWithFind, metaclass=abc.ABCMeta):
subresource = getid(subresource)
resource = f"{resource}{self.subresource_path}/{subresource}"
return self._update(f"{self.resource_path}/{resource}/metadata",
body)
return self._update(f"{self.resource_path}/{resource}/metadata", body)
+7 -5
View File
@@ -34,15 +34,15 @@ def get_client_class(version):
try:
client_path = version_map[str(version)]
except (KeyError, ValueError):
msg = "Invalid client version '%s'. must be one of: %s" % (
(version, ', '.join(version_map)))
msg = "Invalid client version '{}'. must be one of: {}".format(
version, ', '.join(version_map)
)
raise exceptions.UnsupportedVersion(msg)
return importutils.import_class(client_path)
def Client(client_version, *args, **kwargs):
def _convert_to_api_version(version):
"""Convert version to an APIVersion object unless it already is one."""
@@ -51,7 +51,8 @@ def Client(client_version, *args, **kwargs):
else:
if version in ('1', '1.0'):
api_version = api_versions.APIVersion(
api_versions.DEPRECATED_VERSION)
api_versions.DEPRECATED_VERSION
)
elif version == '2':
api_version = api_versions.APIVersion(api_versions.MIN_VERSION)
else:
@@ -64,6 +65,7 @@ def Client(client_version, *args, **kwargs):
# Make sure the kwarg api_version is set with an APIVersion object.
# 1st choice is to use the incoming kwarg. 2nd choice is the positional.
kwargs['api_version'] = _convert_to_api_version(
kwargs.get('api_version', api_version))
kwargs.get('api_version', api_version)
)
return client_class(*args, **kwargs)
+65 -16
View File
@@ -41,87 +41,107 @@ from manilaclient.common._i18n import _
class ClientException(Exception):
"""The base exception class for all exceptions this library raises."""
pass
class ValidationError(ClientException):
"""Error in validation on API client side."""
pass
class UnsupportedVersion(ClientException):
"""User is trying to use an unsupported version of the API."""
pass
class CommandError(ClientException):
"""Error in CLI tool."""
pass
class AuthorizationFailure(ClientException):
"""Cannot authorize API client."""
pass
class ConnectionError(ClientException):
"""Cannot connect to API service."""
pass
class ConnectionRefused(ConnectionError):
"""Connection refused while trying to connect to API service."""
pass
class AuthPluginOptionsMissing(AuthorizationFailure):
"""Auth plugin misses some options."""
def __init__(self, opt_names):
super(AuthPluginOptionsMissing, self).__init__(
_("Authentication failed. Missing options: %s") %
", ".join(opt_names))
super().__init__(
_("Authentication failed. Missing options: %s")
% ", ".join(opt_names)
)
self.opt_names = opt_names
class AuthSystemNotFound(AuthorizationFailure):
"""User has specified an AuthSystem that is not installed."""
def __init__(self, auth_system):
super(AuthSystemNotFound, self).__init__(
_("AuthSystemNotFound: %r") % auth_system)
super().__init__(_("AuthSystemNotFound: %r") % auth_system)
self.auth_system = auth_system
class NoUniqueMatch(ClientException):
"""Multiple entities found instead of one."""
pass
class EndpointException(ClientException):
"""Something is rotten in Service Catalog."""
pass
class EndpointNotFound(EndpointException):
"""Could not find requested endpoint in Service Catalog."""
pass
class AmbiguousEndpoints(EndpointException):
"""Found more than one matching endpoint in Service Catalog."""
def __init__(self, endpoints=None):
super(AmbiguousEndpoints, self).__init__(
_("AmbiguousEndpoints: %r") % endpoints)
super().__init__(_("AmbiguousEndpoints: %r") % endpoints)
self.endpoints = endpoints
class HttpError(ClientException):
"""The base exception class for all HTTP exceptions."""
http_status = 0
message = _("HTTP Error")
def __init__(self, message=None, details=None,
response=None, request_id=None,
url=None, method=None, http_status=None):
def __init__(
self,
message=None,
details=None,
response=None,
request_id=None,
url=None,
method=None,
http_status=None,
):
self.http_status = http_status or self.http_status
self.message = message or self.message
self.details = details
@@ -129,14 +149,15 @@ class HttpError(ClientException):
self.response = response
self.url = url
self.method = method
formatted_string = "%s (HTTP %s)" % (self.message, self.http_status)
formatted_string = f"{self.message} (HTTP {self.http_status})"
if request_id:
formatted_string += " (Request-ID: %s)" % request_id
super(HttpError, self).__init__(formatted_string)
formatted_string += f" (Request-ID: {request_id})"
super().__init__(formatted_string)
class HTTPRedirection(HttpError):
"""HTTP Redirection."""
message = _("HTTP Redirection")
@@ -145,6 +166,7 @@ class HTTPClientError(HttpError):
Exception for cases in which the client seems to have erred.
"""
message = _("HTTP Client Error")
@@ -154,6 +176,7 @@ class HttpServerError(HttpError):
Exception for cases in which the server is aware that it has
erred or is incapable of performing the request.
"""
message = _("HTTP Server Error")
@@ -172,6 +195,7 @@ class BadRequest(HTTPClientError):
The request cannot be fulfilled due to bad syntax.
"""
http_status = 400
message = _("Bad Request")
@@ -182,6 +206,7 @@ class Unauthorized(HTTPClientError):
Similar to 403 Forbidden, but specifically for use when authentication
is required and has failed or has not yet been provided.
"""
http_status = 401
message = _("Unauthorized")
@@ -191,6 +216,7 @@ class PaymentRequired(HTTPClientError):
Reserved for future use.
"""
http_status = 402
message = _("Payment Required")
@@ -201,6 +227,7 @@ class Forbidden(HTTPClientError):
The request was a valid request, but the server is refusing to respond
to it.
"""
http_status = 403
message = _("Forbidden")
@@ -211,6 +238,7 @@ class NotFound(HTTPClientError):
The requested resource could not be found but may be available again
in the future.
"""
http_status = 404
message = _("Not Found")
@@ -221,6 +249,7 @@ class MethodNotAllowed(HTTPClientError):
A request was made of a resource using a request method not supported
by that resource.
"""
http_status = 405
message = _("Method Not Allowed")
@@ -231,6 +260,7 @@ class NotAcceptable(HTTPClientError):
The requested resource is only capable of generating content not
acceptable according to the Accept headers sent in the request.
"""
http_status = 406
message = _("Not Acceptable")
@@ -240,6 +270,7 @@ class ProxyAuthenticationRequired(HTTPClientError):
The client must first authenticate itself with the proxy.
"""
http_status = 407
message = _("Proxy Authentication Required")
@@ -249,6 +280,7 @@ class RequestTimeout(HTTPClientError):
The server timed out waiting for the request.
"""
http_status = 408
message = _("Request Timeout")
@@ -259,6 +291,7 @@ class Conflict(HTTPClientError):
Indicates that the request could not be processed because of conflict
in the request, such as an edit conflict.
"""
http_status = 409
message = _("Conflict")
@@ -269,6 +302,7 @@ class Gone(HTTPClientError):
Indicates that the resource requested is no longer available and will
not be available again.
"""
http_status = 410
message = _("Gone")
@@ -279,6 +313,7 @@ class LengthRequired(HTTPClientError):
The request did not specify the length of its content, which is
required by the requested resource.
"""
http_status = 411
message = _("Length Required")
@@ -289,6 +324,7 @@ class PreconditionFailed(HTTPClientError):
The server does not meet one of the preconditions that the requester
put on the request.
"""
http_status = 412
message = _("Precondition Failed")
@@ -298,6 +334,7 @@ class RequestEntityTooLarge(HTTPClientError):
The request is larger than the server is willing or able to process.
"""
http_status = 413
message = _("Request Entity Too Large")
@@ -307,7 +344,7 @@ class RequestEntityTooLarge(HTTPClientError):
except (KeyError, ValueError):
self.retry_after = 0
super(RequestEntityTooLarge, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
class RequestUriTooLong(HTTPClientError):
@@ -315,6 +352,7 @@ class RequestUriTooLong(HTTPClientError):
The URI provided was too long for the server to process.
"""
http_status = 414
message = _("Request-URI Too Long")
@@ -325,6 +363,7 @@ class UnsupportedMediaType(HTTPClientError):
The request entity has a media type which the server or resource does
not support.
"""
http_status = 415
message = _("Unsupported Media Type")
@@ -335,6 +374,7 @@ class RequestedRangeNotSatisfiable(HTTPClientError):
The client has asked for a portion of the file, but the server cannot
supply that portion.
"""
http_status = 416
message = _("Requested Range Not Satisfiable")
@@ -344,6 +384,7 @@ class ExpectationFailed(HTTPClientError):
The server cannot meet the requirements of the Expect request-header field.
"""
http_status = 417
message = _("Expectation Failed")
@@ -354,6 +395,7 @@ class UnprocessableEntity(HTTPClientError):
The request was well-formed but was unable to be followed due to semantic
errors.
"""
http_status = 422
message = _("Unprocessable Entity")
@@ -363,6 +405,7 @@ class InternalServerError(HttpServerError):
A generic error message, given when no more specific message is suitable.
"""
http_status = 500
message = _("Internal Server Error")
@@ -374,6 +417,7 @@ class HttpNotImplemented(HttpServerError):
The server either does not recognize the request method, or it lacks
the ability to fulfill the request.
"""
http_status = 501
message = _("Not Implemented")
@@ -384,6 +428,7 @@ class BadGateway(HttpServerError):
The server was acting as a gateway or proxy and received an invalid
response from the upstream server.
"""
http_status = 502
message = _("Bad Gateway")
@@ -393,6 +438,7 @@ class ServiceUnavailable(HttpServerError):
The server is currently unavailable.
"""
http_status = 503
message = _("Service Unavailable")
@@ -403,6 +449,7 @@ class GatewayTimeout(HttpServerError):
The server was acting as a gateway or proxy and did not receive a timely
response from the upstream server.
"""
http_status = 504
message = _("Gateway Timeout")
@@ -412,6 +459,7 @@ class HttpVersionNotSupported(HttpServerError):
The server does not support the HTTP protocol version used in the request.
"""
http_status = 505
message = _("HTTP Version Not Supported")
@@ -456,8 +504,9 @@ def from_response(response, method, url):
if isinstance(body, dict):
error = body.get(list(body)[0])
if isinstance(error, dict):
kwargs["message"] = (error.get("message") or
error.get("faultstring"))
kwargs["message"] = error.get("message") or error.get(
"faultstring"
)
kwargs["details"] = error.get("details") or str(body)
elif content_type.startswith("text/"):
kwargs["details"] = response.text
+14 -9
View File
@@ -27,7 +27,7 @@ def find_resource(manager, name_or_id, **find_args):
.. code-block:: python
def _find_hypervisor(cs, hypervisor):
#Get a hypervisor by name or ID.
# Get a hypervisor by name or ID.
return cliutils.find_resource(cs.hypervisors, hypervisor)
"""
# first try to get entity as integer id
@@ -66,14 +66,19 @@ def find_resource(manager, name_or_id, **find_args):
kwargs.update(find_args)
return manager.find(**kwargs)
except exceptions.NotFound:
msg = _("No %(name)s with a name or "
"ID of '%(name_or_id)s' exists.") % \
{"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id}
msg = _(
"No %(name)s with a name or ID of '%(name_or_id)s' exists."
) % {
"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id,
}
raise exceptions.CommandError(msg)
except exceptions.NoUniqueMatch:
msg = _("Multiple %(name)s matches found for "
"'%(name_or_id)s', use an ID to be more specific.") % \
{"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id}
msg = _(
"Multiple %(name)s matches found for "
"'%(name_or_id)s', use an ID to be more specific."
) % {
"name": manager.resource_class.__name__.lower(),
"name_or_id": name_or_id,
}
raise exceptions.CommandError(msg)
+25 -11
View File
@@ -31,10 +31,11 @@ from manilaclient.common._i18n import _
class MissingArgs(Exception):
"""Supplied arguments are not sufficient for calling a function."""
def __init__(self, missing):
self.missing = missing
msg = _("Missing arguments: %s") % ", ".join(missing)
super(MissingArgs, self).__init__(msg)
super().__init__(msg)
def validate_args(fn, *args, **kwargs):
@@ -56,7 +57,7 @@ def validate_args(fn, *args, **kwargs):
argspec = inspect.getfullargspec(fn)
num_defaults = len(argspec.defaults or [])
required_args = argspec.args[:len(argspec.args) - num_defaults]
required_args = argspec.args[: len(argspec.args) - num_defaults]
def isbound(method):
return getattr(method, '__self__', None) is not None
@@ -65,7 +66,7 @@ def validate_args(fn, *args, **kwargs):
required_args.pop(0)
missing = [arg for arg in required_args if arg not in kwargs]
missing = missing[len(args):]
missing = missing[len(args) :]
if missing:
raise MissingArgs(missing)
@@ -79,9 +80,11 @@ def arg(*args, **kwargs):
... def entity_create(args):
... pass
"""
def _decorator(func):
add_arg(func, *args, **kwargs)
return func
return _decorator
@@ -134,8 +137,14 @@ def isunauthenticated(func):
return getattr(func, 'unauthenticated', False)
def print_list(objs, fields, formatters=None, sortby_index=0,
mixed_case_fields=None, field_labels=None):
def print_list(
objs,
fields,
formatters=None,
sortby_index=0,
mixed_case_fields=None,
field_labels=None,
):
"""Print a list or objects as a table, one row per object.
:param objs: iterable of :class:`Resource`
@@ -151,9 +160,13 @@ def print_list(objs, fields, formatters=None, sortby_index=0,
mixed_case_fields = mixed_case_fields or []
field_labels = field_labels or fields
if len(field_labels) != len(fields):
raise ValueError(_("Field labels list %(labels)s has different number "
"of elements than fields list %(fields)s"),
{'labels': field_labels, 'fields': fields})
raise ValueError(
_(
"Field labels list %(labels)s has different number "
"of elements than fields list %(fields)s"
),
{'labels': field_labels, 'fields': fields},
)
if sortby_index is None:
kwargs = {}
@@ -240,9 +253,11 @@ def service_type(stype):
def mymethod(f):
...
"""
def inner(f):
f.service_type = stype
return f
return inner
@@ -252,7 +267,7 @@ def get_service_type(f):
def pretty_choice_list(choices):
return ', '.join("'%s'" % choice for choice in choices)
return ', '.join(f"'{choice}'" for choice in choices)
def exit(msg=''):
@@ -273,6 +288,5 @@ def convert_dict_list_to_string(data, ignored_keys=None):
datum_dict = datum
for k, v in datum_dict.items():
if k not in ignored_keys:
data_string += '\n%(k)s = %(v)s' % {
'k': k, 'v': v}
data_string += f'\n{k} = {v}'
return data_string
+32 -16
View File
@@ -19,14 +19,25 @@
SORT_DIR_VALUES = ('asc', 'desc')
SHARE_SORT_KEY_VALUES = (
'id', 'status', 'size', 'host', 'share_proto',
'availability_zone_id', 'availability_zone',
'user_id', 'project_id',
'created_at', 'updated_at',
'display_name', 'name',
'share_type_id', 'share_type',
'share_network_id', 'share_network',
'snapshot_id', 'snapshot',
'id',
'status',
'size',
'host',
'share_proto',
'availability_zone_id',
'availability_zone',
'user_id',
'project_id',
'created_at',
'updated_at',
'display_name',
'name',
'share_type_id',
'share_type',
'share_network_id',
'share_network',
'snapshot_id',
'snapshot',
)
SNAPSHOT_SORT_KEY_VALUES = (
@@ -85,8 +96,7 @@ RESOURCE_LOCK_SORT_KEY_VALUES = (
'updated_at',
'resource_id',
'resource_type',
'resource_action'
'lock_reason',
'resource_actionlock_reason',
)
BACKUP_SORT_KEY_VALUES = (
@@ -119,9 +129,17 @@ SERVICE_TYPES = {'1': V1_SERVICE_TYPE, '2': V2_SERVICE_TYPE}
EXTENSION_PLUGIN_NAMESPACE = 'manilaclient.common.apiclient.auth'
MESSAGE_SORT_KEY_VALUES = (
'id', 'project_id', 'request_id', 'resource_type', 'action_id',
'detail_id', 'resource_id', 'message_level', 'expires_at',
'request_id', 'created_at'
'id',
'project_id',
'request_id',
'resource_type',
'action_id',
'detail_id',
'resource_id',
'message_level',
'expires_at',
'request_id',
'created_at',
)
STATUS_AVAILABLE = 'available'
@@ -147,9 +165,7 @@ BOOL_SPECS = (
)
# share group types
GROUP_BOOL_SPECS = (
CONSISTENT_SNAPSHOT_SUPPORT,
)
GROUP_BOOL_SPECS = (CONSISTENT_SNAPSHOT_SUPPORT,)
REPLICA_GRADUATION_VERSION = '2.56'
REPLICA_PRE_GRADUATION_VERSION = '2.55'
+44 -28
View File
@@ -36,7 +36,7 @@ except Exception:
pass
class HTTPClient(object):
class HTTPClient:
"""HTTP Client class used by multiple clients.
The imported Requests module caches and reuses objects with the same
@@ -45,18 +45,30 @@ class HTTPClient(object):
execution. This class is shared by multiple client versions so that the
client can be changed to another version during execution.
"""
API_VERSION_HEADER = "X-Openstack-Manila-Api-Version"
def __init__(self, endpoint_url, token, user_agent, api_version,
insecure=False, cacert=None, timeout=None, retries=None,
http_log_debug=False, cert=None):
def __init__(
self,
endpoint_url,
token,
user_agent,
api_version,
insecure=False,
cacert=None,
timeout=None,
retries=None,
http_log_debug=False,
cert=None,
):
self.endpoint_url = endpoint_url
self.base_url = self._get_base_url(self.endpoint_url)
self.retries = int(retries or 0)
self.http_log_debug = http_log_debug
self.request_options = self._set_request_options(
insecure, cacert, timeout, cert)
insecure, cacert, timeout, cert
)
self.default_headers = {
'X-Auth-Token': token,
@@ -85,9 +97,13 @@ class HTTPClient(object):
"""Truncates url and returns base endpoint"""
service_endpoint = parse.urlparse(url)
service_endpoint_base_path = re.search(
r'(.+?)/v([0-9]+|[0-9]+\.[0-9]+)(/.*|$)', service_endpoint.path)
base_path = (service_endpoint_base_path.group(1)
if service_endpoint_base_path else '')
r'(.+?)/v([0-9]+|[0-9]+\.[0-9]+)(/.*|$)', service_endpoint.path
)
base_path = (
service_endpoint_base_path.group(1)
if service_endpoint_base_path
else ''
)
base_url = service_endpoint._replace(path=base_path)
return parse.urlunparse(base_url) + '/'
@@ -138,15 +154,13 @@ class HTTPClient(object):
def _cs_request(self, url, method, **kwargs):
return self._cs_request_with_retries(
self.endpoint_url + url,
method,
**kwargs)
self.endpoint_url + url, method, **kwargs
)
def _cs_request_base_url(self, url, method, **kwargs):
return self._cs_request_with_retries(
self.base_url + url,
method,
**kwargs)
self.base_url + url, method, **kwargs
)
def _cs_request_with_retries(self, url, method, **kwargs):
attempts = 0
@@ -156,9 +170,11 @@ class HTTPClient(object):
try:
resp, body = self.request(url, method, **kwargs)
return resp, body
except (exceptions.BadRequest,
requests.exceptions.RequestException,
exceptions.ClientException) as e:
except (
exceptions.BadRequest,
requests.exceptions.RequestException,
exceptions.ClientException,
) as e:
if attempts > self.retries:
raise
@@ -166,11 +182,9 @@ class HTTPClient(object):
self._logger.debug(
"Failed attempt(%(current)s of %(total)s), "
" retrying in %(sec)s seconds", {
'current': attempts,
'total': self.retries,
'sec': timeout
})
" retrying in %(sec)s seconds",
{'current': attempts, 'total': self.retries, 'sec': timeout},
)
sleep(timeout)
timeout *= 2
@@ -193,24 +207,26 @@ class HTTPClient(object):
if not self.http_log_debug:
return
string_parts = ['curl -i', ' -X %s' % method, ' %s' % url]
string_parts = ['curl -i', f' -X {method}', f' {url}']
for element in headers:
header = ' -H "%s: %s"' % (element, headers[element])
header = f' -H "{element}: {headers[element]}"'
string_parts.append(header)
if data:
if "password" in data:
data = strutils.mask_password(data)
string_parts.append(" -d '%s'" % data)
string_parts.append(f" -d '{data}'")
self._logger.debug("\nREQ: %s\n", "".join(string_parts))
def log_response(self, resp):
if not self.http_log_debug:
return
self._logger.debug(
"RESP: [%(code)s] %(headers)s\nRESP BODY: %(body)s\n", {
"RESP: [%(code)s] %(headers)s\nRESP BODY: %(body)s\n",
{
'code': resp.status_code,
'headers': resp.headers,
'body': resp.text
})
'body': resp.text,
},
)
+250 -165
View File
@@ -27,171 +27,255 @@ from manilaclient import api_versions
# directory "%project_root%/manilaclient/tests/functional"
auth_opts = [
# Options for user with 'member' role.
cfg.StrOpt("username",
help="This should be the username of a user WITHOUT "
"administrative privileges."),
cfg.StrOpt("tenant_name",
help="The non-administrative user's tenant name."),
cfg.StrOpt("password",
secret=True,
help="The non-administrative user's password."),
cfg.StrOpt("auth_url",
help="URL for where to find the OpenStack Identity public "
"API endpoint."),
cfg.StrOpt("project_domain_name",
help=("Project domain Name of user with 'member' role "
"as specified for auth v3.")),
cfg.StrOpt("project_domain_id",
help=("Project domain ID of user with 'member' role "
"as specified for auth v3.")),
cfg.StrOpt("user_domain_name",
help=("User domain Name of user with 'member' role "
"as specified for auth v3.")),
cfg.StrOpt("user_domain_id",
help=("User domain ID of user with 'member' role "
"as specified for auth v3.")),
cfg.StrOpt(
"username",
help="This should be the username of a user WITHOUT "
"administrative privileges.",
),
cfg.StrOpt(
"tenant_name", help="The non-administrative user's tenant name."
),
cfg.StrOpt(
"password", secret=True, help="The non-administrative user's password."
),
cfg.StrOpt(
"auth_url",
help="URL for where to find the OpenStack Identity public "
"API endpoint.",
),
cfg.StrOpt(
"project_domain_name",
help=(
"Project domain Name of user with 'member' role "
"as specified for auth v3."
),
),
cfg.StrOpt(
"project_domain_id",
help=(
"Project domain ID of user with 'member' role "
"as specified for auth v3."
),
),
cfg.StrOpt(
"user_domain_name",
help=(
"User domain Name of user with 'member' role "
"as specified for auth v3."
),
),
cfg.StrOpt(
"user_domain_id",
help=(
"User domain ID of user with 'member' role "
"as specified for auth v3."
),
),
# Options for user with 'admin' role.
cfg.StrOpt("admin_username",
help="This should be the username of a user WITH "
"administrative privileges."),
cfg.StrOpt("admin_tenant_name",
help="The administrative user's tenant name."),
cfg.StrOpt("admin_password",
secret=True,
help="The administrative user's password."),
cfg.StrOpt("admin_auth_url",
help="URL for where to find the OpenStack Identity admin "
"API endpoint."),
cfg.StrOpt("admin_project_domain_name",
help=("Project domain Name of user with 'admin' role "
"as specified for auth v3.")),
cfg.StrOpt("admin_project_domain_id",
help=("Project domain ID of user with 'admin' role "
"as specified for auth v3.")),
cfg.StrOpt("admin_user_domain_name",
help=("User domain Name of user with 'admin' role "
"as specified for auth v3.")),
cfg.StrOpt("admin_user_domain_id",
help=("User domain ID of user with 'admin' role "
"as specified for auth v3.")),
cfg.StrOpt(
"admin_username",
help="This should be the username of a user WITH "
"administrative privileges.",
),
cfg.StrOpt(
"admin_tenant_name", help="The administrative user's tenant name."
),
cfg.StrOpt(
"admin_password",
secret=True,
help="The administrative user's password.",
),
cfg.StrOpt(
"admin_auth_url",
help="URL for where to find the OpenStack Identity admin "
"API endpoint.",
),
cfg.StrOpt(
"admin_project_domain_name",
help=(
"Project domain Name of user with 'admin' role "
"as specified for auth v3."
),
),
cfg.StrOpt(
"admin_project_domain_id",
help=(
"Project domain ID of user with 'admin' role "
"as specified for auth v3."
),
),
cfg.StrOpt(
"admin_user_domain_name",
help=(
"User domain Name of user with 'admin' role "
"as specified for auth v3."
),
),
cfg.StrOpt(
"admin_user_domain_id",
help=(
"User domain ID of user with 'admin' role "
"as specified for auth v3."
),
),
# Other auth options
cfg.BoolOpt("insecure",
default=False,
help="Disable SSL certificate verification."),
cfg.BoolOpt(
"insecure", default=False, help="Disable SSL certificate verification."
),
]
base_opts = [
cfg.StrOpt("manila_exec_dir",
default=os.environ.get(
'OS_MANILA_EXEC_DIR',
os.path.join(os.path.abspath('.'), '.tox/functional/bin')),
help="The path to manilaclient to be executed."),
cfg.BoolOpt("suppress_errors_in_cleanup",
default=True,
help="Whether to suppress errors with clean up operation "
"or not."),
cfg.StrOpt(
"manila_exec_dir",
default=os.environ.get(
'OS_MANILA_EXEC_DIR',
os.path.join(os.path.abspath('.'), '.tox/functional/bin'),
),
help="The path to manilaclient to be executed.",
),
cfg.BoolOpt(
"suppress_errors_in_cleanup",
default=True,
help="Whether to suppress errors with clean up operation or not.",
),
]
share_opts = [
cfg.StrOpt("min_api_microversion",
default="1.0",
help="The minimum API microversion is configured to be the "
"value of the minimum microversion supported by "
"Manilaclient functional tests. Defaults to 1.0."),
cfg.StrOpt("max_api_microversion",
default=api_versions.MAX_VERSION,
help="The maximum API microversion is configured to be the "
"value of the latest microversion supported by "
"Manilaclient functional tests. Defaults to "
"manilaclient's max supported API microversion."),
cfg.StrOpt("share_network",
help="Share network Name or ID, that will be used for shares. "
"Some backend drivers require a share network for share "
"creation."),
cfg.StrOpt("share_network_subnet",
help="Share network subnet ID. Some backend drivers require a "
"share network for share creation."),
cfg.StrOpt("admin_share_network",
help="Share network Name or ID, that will be used for shares "
"in admin tenant."),
cfg.StrOpt("share_type",
help="Share type Name or ID, that will be used with share "
"creation scheduling. Optional."),
cfg.ListOpt("enable_protocols",
default=["nfs", "cifs"],
help="List of all enabled protocols. The first protocol in "
"the list will be used as the default protocol."),
cfg.IntOpt("build_interval",
default=3,
help="Time in seconds between share availability checks."),
cfg.IntOpt("build_timeout",
default=500,
help="Timeout in seconds to wait for a share to become "
"available."),
cfg.DictOpt('access_types_mapping',
default={'nfs': 'ip', 'cifs': 'ip'},
help="Dict contains access types mapping to share "
"protocol. It will be used to create access rules "
"for shares. Format: '<protocol>: <type1> <type2>',..."
"Allowed share protocols: nfs, cifs, cephfs, glusterfs, "
"hdfs."),
cfg.DictOpt('access_levels_mapping',
default={'nfs': 'rw ro', 'cifs': 'rw'},
help="Dict contains access levels mapping to share "
"protocol. It will be used to create access rules for "
"shares. Format: '<protocol>: <level1> <level2>',... "
"Allowed share protocols: nfs, cifs, cephfs, glusterfs, "
"hdfs."),
cfg.StrOpt("username_for_user_rules",
default="stack",
help="Username, that will be used in share access tests for "
"user type of access."),
cfg.StrOpt("replication_type",
default="readable",
choices=["readable", "writable", "dr"],
help="Replication type to be used when running replication "
"tests. This option is ignored if run_replication_tests "
"is set to False."),
cfg.BoolOpt("run_replication_tests",
default=True,
help="Defines whether to run tests for share replication "
"or not. Disable this feature if manila driver used "
"doesn't support share replication."),
cfg.BoolOpt("run_snapshot_tests",
default=True,
help="Defines whether to run tests that use share snapshots "
"or not. Disable this feature if used driver doesn't "
"support it."),
cfg.BoolOpt("run_share_servers_tests",
default=True,
help="Defines whether to run tests that use share servers "
"or not. Disable this feature if used driver doesn't "
"support it or when autodeletion of share servers "
"is enabled."),
cfg.BoolOpt("run_migration_tests",
default=False,
help="Defines whether to run share migration tests or not. "
"Disable this feature if there is no more than one "
"storage pool being tested or if used driver does not "
"support it."),
cfg.BoolOpt("run_mount_snapshot_tests",
default=False,
help="Defines whether to run mountable snapshots tests or "
"not. Disable this feature if used driver doesn't "
"support it."),
cfg.BoolOpt("run_manage_tests",
default=False,
help="Defines whether to run manage/unmanage tests or "
"not. Disable this feature if used driver does not "
"support it."),
cfg.BoolOpt("run_share_servers_migration_tests",
default=False,
help="Defines whether to run share server migration tests or "
"not. Disable this feature if used driver does not "
"support it."),
cfg.StrOpt(
"min_api_microversion",
default="1.0",
help="The minimum API microversion is configured to be the "
"value of the minimum microversion supported by "
"Manilaclient functional tests. Defaults to 1.0.",
),
cfg.StrOpt(
"max_api_microversion",
default=api_versions.MAX_VERSION,
help="The maximum API microversion is configured to be the "
"value of the latest microversion supported by "
"Manilaclient functional tests. Defaults to "
"manilaclient's max supported API microversion.",
),
cfg.StrOpt(
"share_network",
help="Share network Name or ID, that will be used for shares. "
"Some backend drivers require a share network for share "
"creation.",
),
cfg.StrOpt(
"share_network_subnet",
help="Share network subnet ID. Some backend drivers require a "
"share network for share creation.",
),
cfg.StrOpt(
"admin_share_network",
help="Share network Name or ID, that will be used for shares "
"in admin tenant.",
),
cfg.StrOpt(
"share_type",
help="Share type Name or ID, that will be used with share "
"creation scheduling. Optional.",
),
cfg.ListOpt(
"enable_protocols",
default=["nfs", "cifs"],
help="List of all enabled protocols. The first protocol in "
"the list will be used as the default protocol.",
),
cfg.IntOpt(
"build_interval",
default=3,
help="Time in seconds between share availability checks.",
),
cfg.IntOpt(
"build_timeout",
default=500,
help="Timeout in seconds to wait for a share to become available.",
),
cfg.DictOpt(
'access_types_mapping',
default={'nfs': 'ip', 'cifs': 'ip'},
help="Dict contains access types mapping to share "
"protocol. It will be used to create access rules "
"for shares. Format: '<protocol>: <type1> <type2>',..."
"Allowed share protocols: nfs, cifs, cephfs, glusterfs, "
"hdfs.",
),
cfg.DictOpt(
'access_levels_mapping',
default={'nfs': 'rw ro', 'cifs': 'rw'},
help="Dict contains access levels mapping to share "
"protocol. It will be used to create access rules for "
"shares. Format: '<protocol>: <level1> <level2>',... "
"Allowed share protocols: nfs, cifs, cephfs, glusterfs, "
"hdfs.",
),
cfg.StrOpt(
"username_for_user_rules",
default="stack",
help="Username, that will be used in share access tests for "
"user type of access.",
),
cfg.StrOpt(
"replication_type",
default="readable",
choices=["readable", "writable", "dr"],
help="Replication type to be used when running replication "
"tests. This option is ignored if run_replication_tests "
"is set to False.",
),
cfg.BoolOpt(
"run_replication_tests",
default=True,
help="Defines whether to run tests for share replication "
"or not. Disable this feature if manila driver used "
"doesn't support share replication.",
),
cfg.BoolOpt(
"run_snapshot_tests",
default=True,
help="Defines whether to run tests that use share snapshots "
"or not. Disable this feature if used driver doesn't "
"support it.",
),
cfg.BoolOpt(
"run_share_servers_tests",
default=True,
help="Defines whether to run tests that use share servers "
"or not. Disable this feature if used driver doesn't "
"support it or when autodeletion of share servers "
"is enabled.",
),
cfg.BoolOpt(
"run_migration_tests",
default=False,
help="Defines whether to run share migration tests or not. "
"Disable this feature if there is no more than one "
"storage pool being tested or if used driver does not "
"support it.",
),
cfg.BoolOpt(
"run_mount_snapshot_tests",
default=False,
help="Defines whether to run mountable snapshots tests or "
"not. Disable this feature if used driver doesn't "
"support it.",
),
cfg.BoolOpt(
"run_manage_tests",
default=False,
help="Defines whether to run manage/unmanage tests or "
"not. Disable this feature if used driver does not "
"support it.",
),
cfg.BoolOpt(
"run_share_servers_migration_tests",
default=False,
help="Defines whether to run share server migration tests or "
"not. Disable this feature if used driver does not "
"support it.",
),
]
# 2. Generate config
@@ -199,16 +283,17 @@ share_opts = [
PROJECT_NAME = 'manilaclient'
DEFAULT_CONFIG_FILE = (
os.environ.get('OS_%s_CONFIG_FILE' % PROJECT_NAME.upper()) or
'%s.conf' % PROJECT_NAME)
DEFAULT_CONFIG_DIR = (
os.environ.get('OS_%s_CONFIG_DIR' % PROJECT_NAME.upper()) or
os.path.join(os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
"etc/manilaclient")
os.environ.get(f'OS_{PROJECT_NAME.upper()}_CONFIG_FILE')
or f'{PROJECT_NAME}.conf'
)
DEFAULT_CONFIG_DIR = os.environ.get(
f'OS_{PROJECT_NAME.upper()}_CONFIG_DIR'
) or os.path.join(
os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
"etc/manilaclient",
)
DEFAULT_CONFIG_PATH = os.path.join(DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE)
FAILOVER_CONFIG_PATH = '/etc/%(pn)s/%(cn)s' % {
'pn': PROJECT_NAME, 'cn': DEFAULT_CONFIG_FILE}
FAILOVER_CONFIG_PATH = f'/etc/{PROJECT_NAME}/{DEFAULT_CONFIG_FILE}'
CONFIG_FILES = []
if os.path.isfile(DEFAULT_CONFIG_PATH):
+4
View File
@@ -23,6 +23,7 @@ from manilaclient.common.apiclient.exceptions import * # noqa
class ManilaclientException(Exception):
"""A generic client error."""
message = _("An unexpected error occured.")
def __init__(self, message):
@@ -34,11 +35,13 @@ class ManilaclientException(Exception):
class ResourceInErrorState(ManilaclientException):
"""A resource is in an unexpected 'error' state."""
message = _("Resource is in error state")
class TimeoutException(ManilaclientException):
"""A request has timed out"""
message = _("Request has timed out")
@@ -48,6 +51,7 @@ class NoTokenLookupException(ClientException): # noqa: F405
This form of authentication does not support looking up
endpoints from an existing token.
"""
pass
+1 -1
View File
@@ -36,4 +36,4 @@ class Extension(utils.HookableMixin):
self.manager_class = attr_value
def __repr__(self):
return "<Extension '%s'>" % self.name
return f"<Extension '{self.name}'>"
+43 -31
View File
@@ -34,26 +34,30 @@ LATEST_MINOR_VERSION = api_versions.MAX_VERSION.split('.')[-1]
API_VERSIONS = {
'2.%d' % i: CLIENT_CLASS
for i in range(0, int(LATEST_MINOR_VERSION) + 1)
'2.%d' % i: CLIENT_CLASS for i in range(0, int(LATEST_MINOR_VERSION) + 1)
}
def _get_manila_url_from_service_catalog(instance):
service_type = constants.SFS_SERVICE_TYPE
url = instance.get_endpoint_for_service_type(
constants.SFS_SERVICE_TYPE, region_name=instance._region_name,
interface=instance.interface)
constants.SFS_SERVICE_TYPE,
region_name=instance._region_name,
interface=instance.interface,
)
# Fallback if cloud is using an older service type name
if not url:
url = instance.get_endpoint_for_service_type(
constants.V2_SERVICE_TYPE, region_name=instance._region_name,
interface=instance.interface)
constants.V2_SERVICE_TYPE,
region_name=instance._region_name,
interface=instance.interface,
)
service_type = constants.V2_SERVICE_TYPE
if url is None:
raise exceptions.EndpointNotFound(
message="Could not find manila / shared-file-system endpoint in "
"the service catalog.")
"the service catalog."
)
return service_type, url
@@ -62,38 +66,45 @@ def make_client(instance):
requested_api_version = instance._api_version[API_NAME]
service_type, manila_endpoint_url = _get_manila_url_from_service_catalog(
instance)
instance
)
instance.setup_auth()
debugging_enabled = instance._cli_options.debug
client_args = dict(session=instance.session,
service_catalog_url=manila_endpoint_url,
endpoint_type=instance.interface,
region_name=instance.region_name,
service_type=service_type,
auth=instance.auth,
http_log_debug=debugging_enabled,
cacert=instance.cacert,
cert=instance.cert,
insecure=not instance.verify)
client_args = dict(
session=instance.session,
service_catalog_url=manila_endpoint_url,
endpoint_type=instance.interface,
region_name=instance.region_name,
service_type=service_type,
auth=instance.auth,
http_log_debug=debugging_enabled,
cacert=instance.cacert,
cert=instance.cert,
insecure=not instance.verify,
)
# Cast the API version into an object for further processing
requested_api_version = api_versions.APIVersion(
version_str=requested_api_version)
version_str=requested_api_version
)
max_version = api_versions.APIVersion(api_versions.MAX_VERSION)
client_args.update(dict(api_version=max_version))
temp_client = client.Client(max_version, **client_args)
discovered_version = api_versions.discover_version(temp_client,
requested_api_version)
discovered_version = api_versions.discover_version(
temp_client, requested_api_version
)
shared_file_system_client = utils.get_client_class(
API_NAME, discovered_version.get_string(), API_VERSIONS)
API_NAME, discovered_version.get_string(), API_VERSIONS
)
LOG.debug('Instantiating Shared File System (share) client: %s',
shared_file_system_client)
LOG.debug('Shared File System API version: %s',
discovered_version)
LOG.debug(
'Instantiating Shared File System (share) client: %s',
shared_file_system_client,
)
LOG.debug('Shared File System API version: %s', discovered_version)
client_args.update(dict(api_version=discovered_version))
return shared_file_system_client(**client_args)
@@ -107,11 +118,12 @@ def build_option_parser(parser):
metavar='<shared-file-system-api-version>',
default=default_api_version,
choices=sorted(
API_VERSIONS,
key=lambda k: [int(x) for x in k.split('.')]),
help='Shared File System API version, default=' + default_api_version +
'version supported by both the client and the server). '
'(Env: OS_SHARE_API_VERSION)',
API_VERSIONS, key=lambda k: [int(x) for x in k.split('.')]
),
help='Shared File System API version, default='
+ default_api_version
+ 'version supported by both the client and the server). '
'(Env: OS_SHARE_API_VERSION)',
)
parser.add_argument(
"--os-endpoint-override",
+24 -19
View File
@@ -36,7 +36,7 @@ def extract_key_value_options(pairs):
if pairs and len(duplicate_options) > 0:
duplicate_str = ', '.join(duplicate_options)
msg = "Following options were duplicated: %s" % duplicate_str
msg = f"Following options were duplicated: {duplicate_str}"
raise exceptions.CommandError(msg)
return result_dict
@@ -46,7 +46,7 @@ def format_properties(properties):
formatted_data = []
for item in properties:
formatted_data.append("%s : %s" % (item, properties[item]))
formatted_data.append(f"{item} : {properties[item]}")
return "\n".join(formatted_data)
@@ -57,7 +57,8 @@ def extract_properties(properties):
(key, value) = item.split('=', 1)
if key in result_dict:
raise exceptions.CommandError(
"Argument '%s' is specified twice." % key)
f"Argument '{key}' is specified twice."
)
else:
result_dict[key] = value
except ValueError:
@@ -67,42 +68,44 @@ def extract_properties(properties):
return result_dict
def extract_extra_specs(extra_specs, specs_to_add,
bool_specs=constants.BOOL_SPECS):
def extract_extra_specs(
extra_specs, specs_to_add, bool_specs=constants.BOOL_SPECS
):
try:
for item in specs_to_add:
(key, value) = item.split('=', 1)
if key in extra_specs:
msg = ("Argument '%s' value specified twice." % key)
msg = f"Argument '{key}' value specified twice."
raise exceptions.CommandError(msg)
elif key in bool_specs:
if strutils.is_valid_boolstr(value):
extra_specs[key] = value.capitalize()
else:
msg = (
"Argument '%s' is of boolean "
"type and has invalid value: %s"
% (key, str(value)))
f"Argument '{key}' is of boolean "
f"type and has invalid value: {str(value)}"
)
raise exceptions.CommandError(msg)
else:
extra_specs[key] = value
except ValueError:
msg = LOG.error(_(
"Wrong format: specs should be key=value pairs."))
msg = LOG.error(_("Wrong format: specs should be key=value pairs."))
raise exceptions.CommandError(msg)
return extra_specs
def extract_group_specs(extra_specs, specs_to_add):
return extract_extra_specs(extra_specs,
specs_to_add, constants.GROUP_BOOL_SPECS)
return extract_extra_specs(
extra_specs, specs_to_add, constants.GROUP_BOOL_SPECS
)
def format_column_headers(columns):
column_headers = []
for column in columns:
column_headers.append(
column.replace('_', ' ').title().replace('Id', 'ID'))
column.replace('_', ' ').title().replace('Id', 'ID')
)
return column_headers
@@ -112,13 +115,15 @@ def format_share_group_type(share_group_type, formatter='table'):
is_public = printable_share_group_type.pop('is_public')
printable_share_group_type['visibility'] = (
'public' if is_public else 'private')
'public' if is_public else 'private'
)
if formatter == 'table':
printable_share_group_type['group_specs'] = (
format_properties(share_group_type.group_specs))
printable_share_group_type['share_types'] = (
"\n".join(printable_share_group_type['share_types'])
printable_share_group_type['group_specs'] = format_properties(
share_group_type.group_specs
)
printable_share_group_type['share_types'] = "\n".join(
printable_share_group_type['share_types']
)
return printable_share_group_type
+8 -4
View File
@@ -23,8 +23,7 @@ class ShareAvailabilityZoneList(command.Lister):
_description = _("List all availability zones")
def get_parser(self, prog_name):
parser = super(ShareAvailabilityZoneList, self).get_parser(
prog_name)
parser = super().get_parser(prog_name)
return parser
def take_action(self, parsed_args):
@@ -33,5 +32,10 @@ class ShareAvailabilityZoneList(command.Lister):
fields = ("Id", "Name", "Created At", "Updated At")
return (fields, (oscutils.get_item_properties
(s, fields) for s in availability_zones))
return (
fields,
(
oscutils.get_item_properties(s, fields)
for s in availability_zones
),
)
+71 -41
View File
@@ -33,21 +33,23 @@ MESSAGE_ATTRIBUTES = [
'detail_id',
'created_at',
'expires_at',
'request_id'
'request_id',
]
class DeleteMessage(command.Command):
"""Remove one or more messages."""
_description = _("Remove one or more messages")
def get_parser(self, prog_name):
parser = super(DeleteMessage, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'message',
metavar='<message>',
nargs='+',
help=_('ID of the message(s).'))
help=_('ID of the message(s).'),
)
return parser
def take_action(self, parsed_args):
@@ -57,78 +59,98 @@ class DeleteMessage(command.Command):
for message in parsed_args.message:
try:
message_ref = apiutils.find_resource(
share_client.messages,
message)
share_client.messages, message
)
share_client.messages.delete(message_ref)
except Exception as e:
failure_count += 1
LOG.error(_(
"Delete for message %(message)s failed: %(e)s"),
{'message': message, 'e': e})
LOG.error(
_("Delete for message %(message)s failed: %(e)s"),
{'message': message, 'e': e},
)
if failure_count > 0:
raise exceptions.CommandError(_(
"Unable to delete some or all of the specified messages."))
raise exceptions.CommandError(
_("Unable to delete some or all of the specified messages.")
)
class ListMessage(command.Lister):
"""Lists all messages."""
_description = _("Lists all messages")
def get_parser(self, prog_name):
parser = super(ListMessage, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'--resource-id',
metavar='<resource-id>',
default=None,
help=_('Filters results by a resource uuid. Default=None.'))
help=_('Filters results by a resource uuid. Default=None.'),
)
parser.add_argument(
'--resource-type',
metavar='<resource-type>',
default=None,
help=_('Filters results by a resource type. Default=None. '
'Example: "openstack message list --resource-type share"'))
help=_(
'Filters results by a resource type. Default=None. '
'Example: "openstack message list --resource-type share"'
),
)
parser.add_argument(
'--action-id',
metavar='<action-id>',
default=None,
help=_('Filters results by action id. Default=None.'))
help=_('Filters results by action id. Default=None.'),
)
parser.add_argument(
'--detail-id',
metavar='<detail-id>',
default=None,
help=_('Filters results by detail id. Default=None.'))
help=_('Filters results by detail id. Default=None.'),
)
parser.add_argument(
'--request-id',
metavar='<request-id>',
default=None,
help=_('Filters results by request id. Default=None.'))
help=_('Filters results by request id. Default=None.'),
)
parser.add_argument(
'--message-level',
metavar='<message-level>',
default=None,
help=_('Filters results by the message level. Default=None. '
'Example: "openstack message list --message-level ERROR".'))
help=_(
'Filters results by the message level. Default=None. '
'Example: "openstack message list --message-level ERROR".'
),
)
parser.add_argument(
'--limit',
metavar='<limit>',
type=int,
default=None,
help=_('Maximum number of messages to return. (Default=None)'))
help=_('Maximum number of messages to return. (Default=None)'),
)
parser.add_argument(
'--since',
metavar='<since>',
default=None,
help=_('Return only user messages created since given date. '
'The date format must be conforming to ISO8601. '
'Available only for microversion >= 2.52.'))
help=_(
'Return only user messages created since given date. '
'The date format must be conforming to ISO8601. '
'Available only for microversion >= 2.52.'
),
)
parser.add_argument(
'--before',
metavar='<before>',
default=None,
help=_('Return only user messages created before given date. '
'The date format must be conforming to ISO8601. '
'Available only for microversion >= 2.52.'))
help=_(
'Return only user messages created before given date. '
'The date format must be conforming to ISO8601. '
'Available only for microversion >= 2.52.'
),
)
return parser
def take_action(self, parsed_args):
@@ -141,14 +163,17 @@ class ListMessage(command.Lister):
'resource_id': parsed_args.resource_id,
'action_id': parsed_args.action_id,
'detail_id': parsed_args.detail_id,
'message_level': parsed_args.message_level
'message_level': parsed_args.message_level,
}
if share_client.api_version < api_versions.APIVersion("2.52"):
if getattr(parsed_args, 'since') or getattr(parsed_args, 'before'):
raise exceptions.CommandError(_(
"Filtering messages by 'since' and 'before'"
" is possible only with Manila API version >=2.52"))
raise exceptions.CommandError(
_(
"Filtering messages by 'since' and 'before'"
" is possible only with Manila API version >=2.52"
)
)
else:
search_opts['created_since'] = parsed_args.since
search_opts['created_before'] = parsed_args.before
@@ -161,30 +186,35 @@ class ListMessage(command.Lister):
'Action ID',
'User Message',
'Detail ID',
'Created At']
'Created At',
]
return (columns, (oscutils.get_item_properties
(m, columns) for m in messages))
return (
columns,
(oscutils.get_item_properties(m, columns) for m in messages),
)
class ShowMessage(command.ShowOne):
"""Show details about a message."""
_description = _("Show details about a message")
def get_parser(self, prog_name):
parser = super(ShowMessage, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'message',
metavar='<message>',
help=_('ID of the message.'))
'message', metavar='<message>', help=_('ID of the message.')
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
message = apiutils.find_resource(
share_client.messages,
parsed_args.message)
share_client.messages, parsed_args.message
)
return (MESSAGE_ATTRIBUTES, oscutils.get_dict_properties(
message._info, MESSAGE_ATTRIBUTES))
return (
MESSAGE_ATTRIBUTES,
oscutils.get_dict_properties(message._info, MESSAGE_ATTRIBUTES),
)
+176 -115
View File
@@ -24,131 +24,150 @@ class QuotaSet(command.Command):
It can be used to set the default class for all projects.
"""
_description = _("Set Quota for a project, or project/user or "
"project/share-type or a class.")
_description = _(
"Set Quota for a project, or project/user or "
"project/share-type or a class."
)
def get_parser(self, prog_name):
parser = super(QuotaSet, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
quota_type = parser.add_mutually_exclusive_group()
parser.add_argument(
'project',
metavar='<project/class>',
help=_("A project (name/ID) or a class (e.g.: default).")
help=_("A project (name/ID) or a class (e.g.: default)."),
)
quota_type.add_argument(
'--class',
dest='quota_class',
action='store_true',
default=False,
help=_("Update class quota to all projects. "
"Mutually exclusive with '--user' and '--share-type'.")
help=_(
"Update class quota to all projects. "
"Mutually exclusive with '--user' and '--share-type'."
),
)
quota_type.add_argument(
'--user',
metavar='<user>',
default=None,
help=_("Name or ID of a user to set the quotas for. "
"Mutually exclusive with '--share-type' and '--class'.")
help=_(
"Name or ID of a user to set the quotas for. "
"Mutually exclusive with '--share-type' and '--class'."
),
)
quota_type.add_argument(
'--share-type',
metavar='<share-type>',
type=str,
default=None,
help=_("Name or ID of a share type to set the quotas for. "
"Mutually exclusive with '--user' and '--class'. "
"Available only for microversion >= 2.39")
help=_(
"Name or ID of a share type to set the quotas for. "
"Mutually exclusive with '--user' and '--class'. "
"Available only for microversion >= 2.39"
),
)
parser.add_argument(
'--shares',
metavar='<shares>',
type=int,
default=None,
help=_('New value for the "shares" quota.')
help=_('New value for the "shares" quota.'),
)
parser.add_argument(
'--snapshots',
metavar='<snapshots>',
type=int,
default=None,
help=_('New value for the "snapshots" quota.')
help=_('New value for the "snapshots" quota.'),
)
parser.add_argument(
'--gigabytes',
metavar='<gigabytes>',
type=int,
default=None,
help=_('New value for the "gigabytes" quota.')
help=_('New value for the "gigabytes" quota.'),
)
parser.add_argument(
'--snapshot-gigabytes',
metavar='<snapshot-gigabytes>',
type=int,
default=None,
help=_('New value for the "snapshot-gigabytes" quota.')
help=_('New value for the "snapshot-gigabytes" quota.'),
)
parser.add_argument(
'--share-networks',
metavar='<share-networks>',
type=int,
default=None,
help=_('New value for the "share-networks" quota.')
help=_('New value for the "share-networks" quota.'),
)
parser.add_argument(
'--share-groups',
metavar='<share-groups>',
type=int,
default=None,
help=_('New value for the "share-groups" quota. '
'Available only for microversion >= 2.40')
help=_(
'New value for the "share-groups" quota. '
'Available only for microversion >= 2.40'
),
)
parser.add_argument(
'--share-group-snapshots',
metavar='<share-group-snapshots>',
type=int,
default=None,
help=_('New value for the "share-group-snapshots" quota. '
'Available only for microversion >= 2.40')
help=_(
'New value for the "share-group-snapshots" quota. '
'Available only for microversion >= 2.40'
),
)
parser.add_argument(
'--share-replicas',
metavar='<share-replicas>',
type=int,
default=None,
help=_("Number of share replicas. "
"Available only for microversion >= 2.53")
help=_(
"Number of share replicas. "
"Available only for microversion >= 2.53"
),
)
parser.add_argument(
'--replica-gigabytes',
metavar='<replica-gigabytes>',
type=int,
default=None,
help=_("Capacity of share replicas in total. "
"Available only for microversion >= 2.53")
help=_(
"Capacity of share replicas in total. "
"Available only for microversion >= 2.53"
),
)
parser.add_argument(
'--per-share-gigabytes',
metavar='<per-share-gigabytes>',
type=int,
default=None,
help=_("New value for the 'per-share-gigabytes' quota."
"Available only for microversion >= 2.62")
help=_(
"New value for the 'per-share-gigabytes' quota."
"Available only for microversion >= 2.62"
),
)
parser.add_argument(
'--encryption-keys',
metavar='<encryption-keys>',
type=int,
default=None,
help=_("New value for the 'encryption-keys' quota."
"Available only for microversion >= 2.90")
help=_(
"New value for the 'encryption-keys' quota."
"Available only for microversion >= 2.90"
),
)
parser.add_argument(
'--force',
dest='force',
action="store_true",
default=None,
help=_('Force update the quota. '
'Not applicable for class update.')
help=_('Force update the quota. Not applicable for class update.'),
)
return parser
@@ -159,8 +178,8 @@ class QuotaSet(command.Command):
user_id = None
if parsed_args.user:
user_id = utils.find_resource(
identity_client.users,
parsed_args.user).id
identity_client.users, parsed_args.user
).id
kwargs = {
"shares": parsed_args.shares,
@@ -172,130 +191,164 @@ class QuotaSet(command.Command):
if parsed_args.share_type is not None:
if share_client.api_version < api_versions.APIVersion('2.39'):
raise exceptions.CommandError(_(
"'share type' quotas are available only starting with "
"'2.39' API microversion."))
raise exceptions.CommandError(
_(
"'share type' quotas are available only starting with "
"'2.39' API microversion."
)
)
kwargs["share_type"] = parsed_args.share_type
if parsed_args.share_groups is not None:
if share_client.api_version < api_versions.APIVersion('2.40'):
raise exceptions.CommandError(_(
"'share group' quotas are available only starting with "
"'2.40' API microversion."))
raise exceptions.CommandError(
_(
"'share group' quotas are available only starting with "
"'2.40' API microversion."
)
)
kwargs["share_groups"] = parsed_args.share_groups
if parsed_args.share_group_snapshots is not None:
if share_client.api_version < api_versions.APIVersion('2.40'):
raise exceptions.CommandError(_(
"'share group snapshots' quotas are available only "
"starting with '2.40' API microversion."))
raise exceptions.CommandError(
_(
"'share group snapshots' quotas are available only "
"starting with '2.40' API microversion."
)
)
kwargs["share_group_snapshots"] = parsed_args.share_group_snapshots
if parsed_args.share_replicas is not None:
if share_client.api_version < api_versions.APIVersion('2.53'):
raise exceptions.CommandError(_(
"setting the number of 'share replicas' is available only "
"starting with API microversion '2.53'."))
raise exceptions.CommandError(
_(
"setting the number of 'share replicas' is available only "
"starting with API microversion '2.53'."
)
)
kwargs["share_replicas"] = parsed_args.share_replicas
if parsed_args.replica_gigabytes is not None:
if share_client.api_version < api_versions.APIVersion('2.53'):
raise exceptions.CommandError(_(
"setting the capacity of share replicas in total "
"is available only starting with API microversion '2.53'.")
raise exceptions.CommandError(
_(
"setting the capacity of share replicas in total "
"is available only starting with API microversion '2.53'."
)
)
kwargs["replica_gigabytes"] = parsed_args.replica_gigabytes
if parsed_args.per_share_gigabytes is not None:
if share_client.api_version < api_versions.APIVersion('2.62'):
raise exceptions.CommandError(_(
"'per share gigabytes' quotas are available only "
"starting with '2.62' API microversion.")
raise exceptions.CommandError(
_(
"'per share gigabytes' quotas are available only "
"starting with '2.62' API microversion."
)
)
kwargs["per_share_gigabytes"] = parsed_args.per_share_gigabytes
if parsed_args.encryption_keys is not None:
if share_client.api_version < api_versions.APIVersion('2.90'):
raise exceptions.CommandError(_(
"'encryption keys' quotas are available only "
"starting with '2.90' API microversion.")
raise exceptions.CommandError(
_(
"'encryption keys' quotas are available only "
"starting with '2.90' API microversion."
)
)
kwargs["encryption_keys"] = parsed_args.encryption_keys
if all(value is None for value in kwargs.values()):
raise exceptions.CommandError(_(
"Nothing to set. "
"New quota must be specified to at least one of the following "
"resources: 'shares', 'snapshots', 'gigabytes', "
"'snapshot-gigabytes', 'share-networks', 'share-type', "
"'share-groups', 'share-group-snapshots', 'share-replicas', "
"'replica-gigabytes', 'per-share-gigabytes', "
"'encryption_keys'"))
raise exceptions.CommandError(
_(
"Nothing to set. "
"New quota must be specified to at least one of the following "
"resources: 'shares', 'snapshots', 'gigabytes', "
"'snapshot-gigabytes', 'share-networks', 'share-type', "
"'share-groups', 'share-group-snapshots', 'share-replicas', "
"'replica-gigabytes', 'per-share-gigabytes', "
"'encryption_keys'"
)
)
if parsed_args.quota_class:
kwargs.update({
"class_name": parsed_args.project,
})
kwargs.update(
{
"class_name": parsed_args.project,
}
)
try:
share_client.quota_classes.update(**kwargs)
except Exception as e:
raise exceptions.CommandError(_(
"Failed to set quotas for %s class: '%s'")
% (parsed_args.project, e))
raise exceptions.CommandError(
_("Failed to set quotas for %s class: '%s'")
% (parsed_args.project, e)
)
else:
project_id = utils.find_resource(
identity_client.projects,
parsed_args.project).id
identity_client.projects, parsed_args.project
).id
kwargs.update({
"tenant_id": project_id,
"force": parsed_args.force,
"user_id": user_id
})
kwargs.update(
{
"tenant_id": project_id,
"force": parsed_args.force,
"user_id": user_id,
}
)
try:
share_client.quotas.update(**kwargs)
except Exception as e:
raise exceptions.CommandError(_(
"Failed to set quotas for project '%s' : '%s'")
% (parsed_args.project, e))
raise exceptions.CommandError(
_("Failed to set quotas for project '%s' : '%s'")
% (parsed_args.project, e)
)
class QuotaShow(command.ShowOne):
"""List the quotas for a project or project/user or project/share-type."""
_description = _("Show Quota")
def get_parser(self, prog_name):
parser = super(QuotaShow, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
quota_type = parser.add_mutually_exclusive_group()
parser.add_argument(
'project',
metavar='<project>',
help=_('Name or ID of the project to list quotas for.')
help=_('Name or ID of the project to list quotas for.'),
)
quota_type.add_argument(
'--user',
metavar='<user>',
default=None,
help=_("Name or ID of user to list the quotas for. Optional. "
"Mutually exclusive with '--share-type'.")
help=_(
"Name or ID of user to list the quotas for. Optional. "
"Mutually exclusive with '--share-type'."
),
)
quota_type.add_argument(
'--share-type',
metavar='<share-type>',
type=str,
default=None,
help=_("Name or ID of a share type to list the quotas for. "
"Optional. "
"Mutually exclusive with '--user'. "
"Available only for microversion >= 2.39")
help=_(
"Name or ID of a share type to list the quotas for. "
"Optional. "
"Mutually exclusive with '--user'. "
"Available only for microversion >= 2.39"
),
)
parser.add_argument(
'--detail',
action='store_true',
default=False,
help=_('Optional flag to indicate whether to show quota in detail.'
' Default false, available only for microversion >= 2.25.')
help=_(
'Optional flag to indicate whether to show quota in detail.'
' Default false, available only for microversion >= 2.25.'
),
)
parser.add_argument(
'--defaults',
action='store_true',
default=False,
help=_('Show the default quotas for the project.')
help=_('Show the default quotas for the project.'),
)
return parser
@@ -306,12 +359,12 @@ class QuotaShow(command.ShowOne):
user_id = None
if parsed_args.user:
user_id = utils.find_resource(
identity_client.users,
parsed_args.user).id
identity_client.users, parsed_args.user
).id
project_id = utils.find_resource(
identity_client.projects,
parsed_args.project).id
identity_client.projects, parsed_args.project
).id
quotas = {}
if parsed_args.defaults:
@@ -324,9 +377,12 @@ class QuotaShow(command.ShowOne):
}
if parsed_args.share_type is not None:
if share_client.api_version < api_versions.APIVersion("2.39"):
raise exceptions.CommandError(_(
"'share type' quotas are available only starting with "
"'2.39' API microversion."))
raise exceptions.CommandError(
_(
"'share type' quotas are available only starting with "
"'2.39' API microversion."
)
)
kwargs["share_type"] = parsed_args.share_type
quotas = share_client.quotas.get(**kwargs)
@@ -335,7 +391,8 @@ class QuotaShow(command.ShowOne):
for quota_k, quota_v in sorted(quotas.to_dict().items()):
if isinstance(quota_v, dict):
quota_v = '\n'.join(
['%s = %s' % (k, v) for k, v in sorted(quota_v.items())])
[f'{k} = {v}' for k, v in sorted(quota_v.items())]
)
printable_quotas[quota_k] = quota_v
return self.dict2columns(printable_quotas)
@@ -350,29 +407,33 @@ class QuotaDelete(command.Command):
_description = _("Delete Quota")
def get_parser(self, prog_name):
parser = super(QuotaDelete, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
quota_type = parser.add_mutually_exclusive_group()
parser.add_argument(
'project',
metavar='<project>',
help=_('Name or ID of the project to delete quotas for.')
help=_('Name or ID of the project to delete quotas for.'),
)
quota_type.add_argument(
'--user',
metavar='<user>',
default=None,
help=_("Name or ID of user to delete the quotas for. Optional. "
"Mutually exclusive with '--share-type'.")
help=_(
"Name or ID of user to delete the quotas for. Optional. "
"Mutually exclusive with '--share-type'."
),
)
quota_type.add_argument(
'--share-type',
metavar='<share-type>',
type=str,
default=None,
help=_("Name or ID of a share type to delete the quotas for. "
"Optional. "
"Mutually exclusive with '--user'. "
"Available only for microversion >= 2.39")
help=_(
"Name or ID of a share type to delete the quotas for. "
"Optional. "
"Mutually exclusive with '--user'. "
"Available only for microversion >= 2.39"
),
)
return parser
@@ -383,22 +444,22 @@ class QuotaDelete(command.Command):
user_id = None
if parsed_args.user:
user_id = utils.find_resource(
identity_client.users,
parsed_args.user).id
identity_client.users, parsed_args.user
).id
project_id = utils.find_resource(
identity_client.projects,
parsed_args.project).id
identity_client.projects, parsed_args.project
).id
kwargs = {
"tenant_id": project_id,
"user_id": user_id
}
kwargs = {"tenant_id": project_id, "user_id": user_id}
if parsed_args.share_type:
if share_client.api_version < api_versions.APIVersion("2.39"):
raise exceptions.CommandError(_(
"'share type' quotas are available only starting with "
"API microversion '2.39'."))
raise exceptions.CommandError(
_(
"'share type' quotas are available only starting with "
"API microversion '2.39'."
)
)
kwargs["share_type"] = parsed_args.share_type
share_client.quotas.delete(**kwargs)
+113 -82
View File
@@ -48,36 +48,41 @@ LOCK_SUMMARY_ATTRIBUTES = [
RESOURCE_TYPE_MANAGERS = {
'share': 'shares',
'access_rule': 'share_access_rules'
'access_rule': 'share_access_rules',
}
class CreateResourceLock(command.ShowOne):
"""Create a new resource lock."""
_description = _("Lock a resource action from occurring on a resource")
def get_parser(self, prog_name):
parser = super(CreateResourceLock, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'resource',
metavar='<resource_name_or_id>',
help='Name or ID of resource to lock.')
help='Name or ID of resource to lock.',
)
parser.add_argument(
'resource_type',
metavar='<resource_type>',
help='Type of the resource (e.g.: share, access).')
help='Type of the resource (e.g.: share, access).',
)
parser.add_argument(
'--resource-action',
'--resource_action',
metavar='<resource_action>',
default='delete',
help='Action to lock on the resource (default="delete")')
help='Action to lock on the resource (default="delete")',
)
parser.add_argument(
'--lock-reason',
'--lock_reason',
'--reason',
metavar='<lock_reason>',
help='Reason for the resource lock.')
help='Reason for the resource lock.',
)
return parser
def take_action(self, parsed_args):
@@ -87,13 +92,14 @@ class CreateResourceLock(command.ShowOne):
raise exceptions.CommandError(_("Unsupported resource type"))
res_manager = RESOURCE_TYPE_MANAGERS[resource_type]
resource = osc_utils.find_resource(getattr(share_client, res_manager),
parsed_args.resource)
resource = osc_utils.find_resource(
getattr(share_client, res_manager), parsed_args.resource
)
resource_lock = share_client.resource_locks.create(
resource.id,
resource_type,
parsed_args.resource_action,
parsed_args.lock_reason
parsed_args.lock_reason,
)
resource_lock._info.pop('links', None)
@@ -103,15 +109,14 @@ class CreateResourceLock(command.ShowOne):
class DeleteResourceLock(command.Command):
"""Remove one or more resource locks."""
_description = _("Remove one or more resource locks")
def get_parser(self, prog_name):
parser = super(DeleteResourceLock, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'lock',
metavar='<lock>',
nargs='+',
help='ID(s) of the lock(s).')
'lock', metavar='<lock>', nargs='+', help='ID(s) of the lock(s).'
)
return parser
def take_action(self, parsed_args):
@@ -121,50 +126,57 @@ class DeleteResourceLock(command.Command):
for lock in parsed_args.lock:
try:
lock = apiutils.find_resource(
share_client.resource_locks,
lock
share_client.resource_locks, lock
)
lock.delete()
except Exception as e:
failure_count += 1
LOG.error(_(
"Failed to delete %(lock)s: %(e)s"),
{'lock': lock, 'e': e})
LOG.error(
_("Failed to delete %(lock)s: %(e)s"),
{'lock': lock, 'e': e},
)
if failure_count > 0:
raise exceptions.CommandError(_(
"Unable to delete some or all of the specified locks."))
raise exceptions.CommandError(
_("Unable to delete some or all of the specified locks.")
)
class ListResourceLock(command.Lister):
"""Lists all resource locks."""
_description = _("Lists all resource locks")
def get_parser(self, prog_name):
parser = super(ListResourceLock, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'--all-projects',
action='store_true',
help=_("Filter resource locks for all projects. (Admin only).")
help=_("Filter resource locks for all projects. (Admin only)."),
)
parser.add_argument(
'--project',
default=None,
help=_("Filter resource locks for specific project by name or ID, "
"combine with --all-projects (Admin only).")
help=_(
"Filter resource locks for specific project by name or ID, "
"combine with --all-projects (Admin only)."
),
)
parser.add_argument(
'--user',
default=None,
help=_("Filter resource locks for specific user by name or ID, "
"combine with --all-projects to search across projects "
"(Admin only).")
help=_(
"Filter resource locks for specific user by name or ID, "
"combine with --all-projects to search across projects "
"(Admin only)."
),
)
parser.add_argument(
'--id',
metavar='<id>',
default=None,
help='Filter resource locks by ID. Default=None.')
help='Filter resource locks by ID. Default=None.',
)
parser.add_argument(
'--resource',
'--resource-id',
@@ -172,22 +184,24 @@ class ListResourceLock(command.Lister):
default=None,
metavar='<resource-id>',
dest='resource',
help=_("Filter resource locks for a resource by ID, specify "
"--resource-type to look up by name.")
help=_(
"Filter resource locks for a resource by ID, specify "
"--resource-type to look up by name."
),
)
parser.add_argument(
'--resource-type',
'--resource_type',
default=None,
metavar='<resource_type>',
help=_("Filter resource locks by type of resource.")
help=_("Filter resource locks by type of resource."),
)
parser.add_argument(
'--resource-action',
'--resource_action',
default=None,
metavar='<resource_action>',
help=_("Filter resource locks by resource action.")
help=_("Filter resource locks by resource action."),
)
parser.add_argument(
@@ -197,52 +211,60 @@ class ListResourceLock(command.Lister):
default=None,
choices=['user', 'admin', 'service'],
metavar='<lock_context>',
help=_("Filter resource locks by context.")
help=_("Filter resource locks by context."),
)
parser.add_argument(
'--since',
default=None,
metavar='<created_since>',
help=_("Filter resource locks created since given date. "
"The date format must be conforming to ISO8601. ")
help=_(
"Filter resource locks created since given date. "
"The date format must be conforming to ISO8601. "
),
)
parser.add_argument(
'--before',
default=None,
metavar='<created_before>',
help=_("Filter resource locks created before given date. "
"The date format must be conforming to ISO8601. ")
help=_(
"Filter resource locks created before given date. "
"The date format must be conforming to ISO8601. "
),
)
parser.add_argument(
'--limit',
metavar='<limit>',
type=int,
default=None,
help=_("Number of resource locks to list. (Default=None)"))
help=_("Number of resource locks to list. (Default=None)"),
)
parser.add_argument(
'--offset',
metavar="<offset>",
default=None,
help='Starting position of resource lock records '
'in a paginated list.')
'in a paginated list.',
)
parser.add_argument(
'--sort-key', '--sort_key',
'--sort-key',
'--sort_key',
metavar='<sort_key>',
type=str,
default=None,
choices=constants.RESOURCE_LOCK_SORT_KEY_VALUES,
help='Key to be sorted, available keys are %(keys)s. '
'Default=None.'
% {'keys': constants.RESOURCE_LOCK_SORT_KEY_VALUES})
help=f'Key to be sorted, available keys are {constants.RESOURCE_LOCK_SORT_KEY_VALUES}. '
'Default=None.',
)
parser.add_argument(
'--sort-dir', '--sort_dir',
'--sort-dir',
'--sort_dir',
metavar='<sort_dir>',
type=str,
default=None,
choices=constants.SORT_DIR_VALUES,
help='Sort direction, available values are %(values)s. '
'OPTIONAL: Default=None.' % {
'values': constants.SORT_DIR_VALUES})
help=f'Sort direction, available values are {constants.SORT_DIR_VALUES}. '
'OPTIONAL: Default=None.',
)
parser.add_argument(
'--detailed',
dest='detailed',
@@ -251,7 +273,8 @@ class ListResourceLock(command.Lister):
type=int,
const=1,
default=0,
help="Show detailed information about filtered resource locks.")
help="Show detailed information about filtered resource locks.",
)
return parser
def take_action(self, parsed_args):
@@ -270,11 +293,12 @@ class ListResourceLock(command.Lister):
project_id = identity_common.find_project(
identity_client,
parsed_args.project,
parsed_args.project_domain).id
parsed_args.project_domain,
).id
if parsed_args.user:
user_id = identity_common.find_user(identity_client,
parsed_args.user,
parsed_args.user_domain).id
user_id = identity_common.find_user(
identity_client, parsed_args.user, parsed_args.user_domain
).id
# set all_projects when using project option
all_projects = bool(parsed_args.project) or parsed_args.all_projects
@@ -286,12 +310,12 @@ class ListResourceLock(command.Lister):
if resource_id is not None:
res_manager = RESOURCE_TYPE_MANAGERS[resource_type]
resource_id = osc_utils.find_resource(
getattr(share_client, res_manager),
parsed_args.resource
getattr(share_client, res_manager), parsed_args.resource
).id
elif resource_id and not uuidutils.is_uuid_like(resource_id):
raise exceptions.CommandError(
_("Provide resource ID or specify --resource-type."))
_("Provide resource ID or specify --resource-type.")
)
search_opts = {
'all_projects': all_projects,
@@ -311,60 +335,68 @@ class ListResourceLock(command.Lister):
resource_locks = share_client.resource_locks.list(
search_opts=search_opts,
sort_key=parsed_args.sort_key,
sort_dir=parsed_args.sort_dir
sort_dir=parsed_args.sort_dir,
)
return (columns, (osc_utils.get_item_properties
(m, columns) for m in resource_locks))
return (
columns,
(
osc_utils.get_item_properties(m, columns)
for m in resource_locks
),
)
class ShowResourceLock(command.ShowOne):
"""Show details about a resource lock."""
_description = _("Show details about a resource lock")
def get_parser(self, prog_name):
parser = super(ShowResourceLock, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'lock',
metavar='<lock>',
help=_('ID of resource lock to show.'))
'lock', metavar='<lock>', help=_('ID of resource lock to show.')
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
resource_lock = apiutils.find_resource(
share_client.resource_locks,
parsed_args.lock)
share_client.resource_locks, parsed_args.lock
)
return (
LOCK_DETAIL_ATTRIBUTES,
osc_utils.get_dict_properties(resource_lock._info,
LOCK_DETAIL_ATTRIBUTES)
osc_utils.get_dict_properties(
resource_lock._info, LOCK_DETAIL_ATTRIBUTES
),
)
class SetResourceLock(command.Command):
"""Set resource lock properties."""
_description = _("Update resource lock properties")
def get_parser(self, prog_name):
parser = super(SetResourceLock, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'lock',
metavar='<lock>',
help='ID of lock to update.')
'lock', metavar='<lock>', help='ID of lock to update.'
)
parser.add_argument(
'--resource-action',
'--resource_action',
metavar='<resource_action>',
help='Resource action to set in the resource lock')
help='Resource action to set in the resource lock',
)
parser.add_argument(
'--lock-reason',
'--lock_reason',
'--reason',
dest='lock_reason',
help="Reason for the resource lock")
help="Reason for the resource lock",
)
return parser
def take_action(self, parsed_args):
@@ -377,21 +409,20 @@ class SetResourceLock(command.Command):
update_kwargs['lock_reason'] = parsed_args.lock_reason
if update_kwargs:
share_client.resource_locks.update(
parsed_args.lock,
**update_kwargs
parsed_args.lock, **update_kwargs
)
class UnsetResourceLock(command.Command):
"""Unsets a property on a resource lock."""
_description = _("Remove resource lock properties")
def get_parser(self, prog_name):
parser = super(UnsetResourceLock, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'lock',
metavar='<lock>',
help='ID of resource lock to update.')
'lock', metavar='<lock>', help='ID of resource lock to update.'
)
parser.add_argument(
'--lock-reason',
'--lock_reason',
@@ -399,7 +430,8 @@ class UnsetResourceLock(command.Command):
dest='lock_reason',
action='store_true',
default=False,
help="Unset the lock reason. (Default=False)")
help="Unset the lock reason. (Default=False)",
)
return parser
def take_action(self, parsed_args):
@@ -407,6 +439,5 @@ class UnsetResourceLock(command.Command):
if parsed_args.lock_reason:
share_client.resource_locks.update(
parsed_args.lock,
lock_reason=None
parsed_args.lock, lock_reason=None
)
+190 -122
View File
@@ -25,76 +25,85 @@ LOG = logging.getLogger(__name__)
class CreateShareSecurityService(command.ShowOne):
"""Create security service used by project."""
_description = _("Create security service used by project.")
def get_parser(self, prog_name):
parser = super(CreateShareSecurityService, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'type',
metavar='<type>',
default=None,
choices=['ldap', 'kerberos', 'active_directory'],
help=_("Security service type. Possible options are: "
"'ldap', 'kerberos', 'active_directory'.")
help=_(
"Security service type. Possible options are: "
"'ldap', 'kerberos', 'active_directory'."
),
)
parser.add_argument(
'--dns-ip',
metavar='<dns-ip>',
default=None,
help=_("DNS IP address of the security service used "
"inside project's network.")
help=_(
"DNS IP address of the security service used "
"inside project's network."
),
)
parser.add_argument(
'--ou',
metavar='<ou>',
default=None,
help=_("Security service OU (Organizational Unit). "
"Available only for microversion >= 2.44.")
help=_(
"Security service OU (Organizational Unit). "
"Available only for microversion >= 2.44."
),
)
parser.add_argument(
'--server',
metavar='<server>',
default=None,
help=_("Security service IP address or hostname.")
help=_("Security service IP address or hostname."),
)
parser.add_argument(
'--domain',
metavar='<domain>',
default=None,
help=_("Security service domain.")
help=_("Security service domain."),
)
parser.add_argument(
'--user',
metavar='<user',
default=None,
help=_("Security service user or group used by project.")
help=_("Security service user or group used by project."),
)
parser.add_argument(
'--password',
metavar='<password>',
default=None,
help=_("Password used by user.")
help=_("Password used by user."),
)
parser.add_argument(
'--name',
metavar='<name>',
default=None,
help=_("Security service name.")
help=_("Security service name."),
)
parser.add_argument(
'--description',
metavar='<description>',
default=None,
help=_("Security service description.")
help=_("Security service description."),
)
parser.add_argument(
'--default-ad-site',
metavar='<default_ad_site>',
dest='default_ad_site',
default=None,
help=_("Default AD site. Available only for "
"microversion >= 2.76. Can be provided in the "
"place of '--server' but not along with it.")
help=_(
"Default AD site. Available only for "
"microversion >= 2.76. Can be provided in the "
"place of '--server' but not along with it."
),
)
return parser
@@ -116,14 +125,16 @@ class CreateShareSecurityService(command.ShowOne):
elif parsed_args.ou:
raise exceptions.CommandError(
"Defining a security service Organizational Unit is "
"available only for microversion >= 2.44")
"available only for microversion >= 2.44"
)
if share_client.api_version >= api_versions.APIVersion("2.76"):
kwargs['default_ad_site'] = parsed_args.default_ad_site
elif parsed_args.default_ad_site:
raise exceptions.CommandError(
"Defining a security service Default AD site is "
"available only for microversion >= 2.76")
"available only for microversion >= 2.76"
)
if parsed_args.type == 'active_directory':
server = parsed_args.server
@@ -132,25 +143,28 @@ class CreateShareSecurityService(command.ShowOne):
raise exceptions.CommandError(
"Cannot create security service because both "
"server and 'default_ad_site' were provided. "
"Specify either server or 'default_ad_site'.")
"Specify either server or 'default_ad_site'."
)
security_service = share_client.security_services.create(
parsed_args.type, **kwargs)
parsed_args.type, **kwargs
)
return self.dict2columns(security_service._info)
class DeleteShareSecurityService(command.Command):
"""Delete one or more security services."""
_description = _("Delete one or more security services.")
def get_parser(self, prog_name):
parser = super(DeleteShareSecurityService, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'security_service',
metavar='<security-service>',
nargs="+",
help=_("Name or ID of the security service(s) to delete.")
help=_("Name or ID of the security service(s) to delete."),
)
return parser
@@ -161,33 +175,36 @@ class DeleteShareSecurityService(command.Command):
for security_service in parsed_args.security_service:
try:
security_service_obj = oscutils.find_resource(
share_client.security_services,
security_service)
share_client.security_services.delete(
security_service_obj)
share_client.security_services, security_service
)
share_client.security_services.delete(security_service_obj)
except Exception as e:
result += 1
LOG.error(f"Failed to delete security service with "
f"name or ID {security_service}: {e}")
LOG.error(
f"Failed to delete security service with "
f"name or ID {security_service}: {e}"
)
if result > 0:
total = len(parsed_args.security_service)
msg = (f"{result} of {total} security services failed "
f"to be deleted.")
msg = (
f"{result} of {total} security services failed to be deleted."
)
raise exceptions.CommandError(msg)
class ShowShareSecurityService(command.ShowOne):
"""Show security service."""
_description = _("Show security service.")
def get_parser(self, prog_name):
parser = super(ShowShareSecurityService, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'security_service',
metavar='<security-service>',
help=_("Security service name or ID to show.")
help=_("Security service name or ID to show."),
)
return parser
@@ -195,85 +212,88 @@ class ShowShareSecurityService(command.ShowOne):
share_client = self.app.client_manager.share
security_service = oscutils.find_resource(
share_client.security_services,
parsed_args.security_service)
share_client.security_services, parsed_args.security_service
)
data = security_service._info
if parsed_args.formatter == 'table':
if 'share_networks' in data.keys():
data['share_networks'] = "\n".join(
data['share_networks'])
data['share_networks'] = "\n".join(data['share_networks'])
return self.dict2columns(data)
class SetShareSecurityService(command.Command):
"""Set security service."""
_description = _("Set security service.")
def get_parser(self, prog_name):
parser = super(SetShareSecurityService, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'security_service',
metavar='<security-service>',
help=_("Security service name or ID.")
help=_("Security service name or ID."),
)
parser.add_argument(
'--dns-ip',
metavar='<dns-ip>',
default=None,
help=_("Set DNS IP address used inside project's network.")
help=_("Set DNS IP address used inside project's network."),
)
parser.add_argument(
'--ou',
metavar='<ou>',
default=None,
help=_("Set security service OU (Organizational Unit). "
"Available only for microversion >= 2.44.")
help=_(
"Set security service OU (Organizational Unit). "
"Available only for microversion >= 2.44."
),
)
parser.add_argument(
'--server',
metavar='<server>',
default=None,
help=_("Set security service IP address or hostname.")
help=_("Set security service IP address or hostname."),
)
parser.add_argument(
'--domain',
metavar='<domain>',
default=None,
help=_("Set security service domain.")
help=_("Set security service domain."),
)
parser.add_argument(
'--user',
metavar='<user',
default=None,
help=_("Set security service user or group used by project.")
help=_("Set security service user or group used by project."),
)
parser.add_argument(
'--password',
metavar='<password>',
default=None,
help=_("Set password used by user.")
help=_("Set password used by user."),
)
parser.add_argument(
'--name',
metavar='<name>',
default=None,
help=_("Set security service name.")
help=_("Set security service name."),
)
parser.add_argument(
'--description',
metavar='<description>',
default=None,
help=_("Set security service description.")
help=_("Set security service description."),
)
parser.add_argument(
'--default-ad-site',
metavar='<default_ad_site>',
dest='default_ad_site',
default=None,
help=_("Default AD site. "
"Available only for microversion >= 2.76.")
help=_(
"Default AD site. Available only for microversion >= 2.76."
),
)
return parser
@@ -281,8 +301,8 @@ class SetShareSecurityService(command.Command):
share_client = self.app.client_manager.share
security_service = oscutils.find_resource(
share_client.security_services,
parsed_args.security_service)
share_client.security_services, parsed_args.security_service
)
kwargs = {
'dns_ip': parsed_args.dns_ip,
@@ -297,16 +317,20 @@ class SetShareSecurityService(command.Command):
if share_client.api_version >= api_versions.APIVersion("2.44"):
kwargs['ou'] = parsed_args.ou
elif parsed_args.ou:
raise exceptions.CommandError(_(
"Setting a security service Organizational Unit is "
"available only for microversion >= 2.44"))
raise exceptions.CommandError(
_(
"Setting a security service Organizational Unit is "
"available only for microversion >= 2.44"
)
)
if share_client.api_version >= api_versions.APIVersion("2.76"):
kwargs['default_ad_site'] = parsed_args.default_ad_site
elif parsed_args.default_ad_site:
raise exceptions.CommandError(
"Defining a security service Default AD site is "
"available only for microversion >= 2.76")
"available only for microversion >= 2.76"
)
if security_service.type == 'active_directory':
server = parsed_args.server
@@ -315,72 +339,78 @@ class SetShareSecurityService(command.Command):
raise exceptions.CommandError(
"Cannot set security service because both "
"server and 'default_ad_site' were provided. "
"Specify either server or 'default_ad_site'.")
"Specify either server or 'default_ad_site'."
)
try:
security_service.update(**kwargs)
except Exception as e:
raise exceptions.CommandError(
f"One or more set operations failed: {e}")
f"One or more set operations failed: {e}"
)
class UnsetShareSecurityService(command.Command):
"""Unset security service."""
_description = _("Unset security service.")
def get_parser(self, prog_name):
parser = super(UnsetShareSecurityService, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'security_service',
metavar='<security-service>',
help=_("Security service name or ID.")
help=_("Security service name or ID."),
)
parser.add_argument(
'--dns-ip',
action='store_true',
help=_("Unset DNS IP address used inside project's network.")
help=_("Unset DNS IP address used inside project's network."),
)
parser.add_argument(
'--ou',
action='store_true',
help=_("Unset security service OU (Organizational Unit). "
"Available only for microversion >= 2.44.")
help=_(
"Unset security service OU (Organizational Unit). "
"Available only for microversion >= 2.44."
),
)
parser.add_argument(
'--server',
action='store_true',
help=_("Unset security service IP address or hostname.")
help=_("Unset security service IP address or hostname."),
)
parser.add_argument(
'--domain',
action='store_true',
help=_("Unset security service domain.")
help=_("Unset security service domain."),
)
parser.add_argument(
'--user',
action='store_true',
help=_("Unset security service user or group used by project.")
help=_("Unset security service user or group used by project."),
)
parser.add_argument(
'--password',
action='store_true',
help=_("Unset password used by user.")
help=_("Unset password used by user."),
)
parser.add_argument(
'--name',
action='store_true',
help=_("Unset security service name.")
help=_("Unset security service name."),
)
parser.add_argument(
'--description',
action='store_true',
help=_("Unset security service description.")
help=_("Unset security service description."),
)
parser.add_argument(
'--default-ad-site',
dest='default_ad_site',
action='store_true',
help=_("Default AD site. "
"Available only for microversion >= 2.76.")
help=_(
"Default AD site. Available only for microversion >= 2.76."
),
)
return parser
@@ -388,126 +418,155 @@ class UnsetShareSecurityService(command.Command):
share_client = self.app.client_manager.share
security_service = oscutils.find_resource(
share_client.security_services,
parsed_args.security_service)
share_client.security_services, parsed_args.security_service
)
kwargs = {}
args = ['dns_ip', 'server', 'domain', 'user', 'password',
'name', 'description']
args = [
'dns_ip',
'server',
'domain',
'user',
'password',
'name',
'description',
]
for arg in args:
if getattr(parsed_args, arg):
# the SDK unsets a value if it is an empty string
kwargs[arg] = ''
if (parsed_args.ou and
share_client.api_version >= api_versions.APIVersion("2.44")):
if (
parsed_args.ou
and share_client.api_version >= api_versions.APIVersion("2.44")
):
# the SDK unsets a value if it is an empty string
kwargs['ou'] = ''
elif parsed_args.ou:
raise exceptions.CommandError(_(
"Unsetting a security service Organizational Unit is "
"available only for microversion >= 2.44"))
raise exceptions.CommandError(
_(
"Unsetting a security service Organizational Unit is "
"available only for microversion >= 2.44"
)
)
if (parsed_args.default_ad_site and
share_client.api_version >= api_versions.APIVersion("2.76")):
if (
parsed_args.default_ad_site
and share_client.api_version >= api_versions.APIVersion("2.76")
):
# the SDK unsets a value if it is an empty string
kwargs['default_ad_site'] = ''
elif parsed_args.default_ad_site:
raise exceptions.CommandError(_(
"Unsetting a security service Default AD site is "
"available only for microversion >= 2.76"))
raise exceptions.CommandError(
_(
"Unsetting a security service Default AD site is "
"available only for microversion >= 2.76"
)
)
try:
security_service.update(**kwargs)
except Exception as e:
raise exceptions.CommandError(
f"One or more unset operations failed: {e}")
f"One or more unset operations failed: {e}"
)
class ListShareSecurityService(command.Lister):
"""List security services."""
_description = _("List security services.")
def get_parser(self, prog_name):
parser = super(ListShareSecurityService, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'--all-projects',
action='store_true',
help=_("Display information from all projects (Admin only).")
help=_("Display information from all projects (Admin only)."),
)
parser.add_argument(
'--share-network',
metavar='<share-network>',
default=None,
help=_("Filter results by share network name or ID.")
help=_("Filter results by share network name or ID."),
)
parser.add_argument(
'--status',
metavar='<status>',
default=None,
help=_("Filter results by status.")
help=_("Filter results by status."),
)
parser.add_argument(
'--name',
metavar='<name>',
default=None,
help=_("Filter results by security service name.")
help=_("Filter results by security service name."),
)
parser.add_argument(
'--type',
metavar='<type>',
default=None,
help=_("Filter results by security service type.")
help=_("Filter results by security service type."),
)
parser.add_argument(
'--user',
metavar='<user',
default=None,
help=_("Filter results by security service user or group "
"used by project.")
help=_(
"Filter results by security service user or group "
"used by project."
),
)
parser.add_argument(
'--dns-ip',
metavar='<dns-ip>',
default=None,
help=_("Filter results by DNS IP address used inside "
"project's network.")
help=_(
"Filter results by DNS IP address used inside "
"project's network."
),
)
parser.add_argument(
'--ou',
metavar='<ou>',
default=None,
help=_("Filter results by security service OU "
"(Organizational Unit). "
"Available only for microversion >= 2.44.")
help=_(
"Filter results by security service OU "
"(Organizational Unit). "
"Available only for microversion >= 2.44."
),
)
parser.add_argument(
'--default-ad-site',
metavar='<default_ad_site>',
dest='default_ad_site',
default=None,
help=_("Filter results by security service default_ad_site. "
"Available only for microversion >= 2.76.")
help=_(
"Filter results by security service default_ad_site. "
"Available only for microversion >= 2.76."
),
)
parser.add_argument(
'--server',
metavar='<server>',
default=None,
help=_("Filter results by security service IP "
"address or hostname.")
help=_(
"Filter results by security service IP address or hostname."
),
)
parser.add_argument(
'--domain',
metavar='<domain>',
default=None,
help=_("Filter results by security service domain.")
help=_("Filter results by security service domain."),
)
parser.add_argument(
'--detail',
action='store_true',
help=_("Show detailed information about filtered "
"security services.")
help=_(
"Show detailed information about filtered security services."
),
)
parser.add_argument(
"--limit",
@@ -515,12 +574,12 @@ class ListShareSecurityService(command.Lister):
type=int,
default=None,
action=parseractions.NonNegativeAction,
help=_("Limit the number of security services returned")
help=_("Limit the number of security services returned"),
)
parser.add_argument(
"--marker",
metavar="<security-service>",
help=_("The last security service ID of the previous page")
help=_("The last security service ID of the previous page"),
)
return parser
@@ -548,34 +607,43 @@ class ListShareSecurityService(command.Lister):
'limit': parsed_args.limit,
}
if (parsed_args.ou and
share_client.api_version >= api_versions.APIVersion("2.44")):
if (
parsed_args.ou
and share_client.api_version >= api_versions.APIVersion("2.44")
):
search_opts['ou'] = parsed_args.ou
elif parsed_args.ou:
raise exceptions.CommandError(_(
"Filtering results by security service Organizational Unit is "
"available only for microversion >= 2.44"))
raise exceptions.CommandError(
_(
"Filtering results by security service Organizational Unit is "
"available only for microversion >= 2.44"
)
)
if (parsed_args.default_ad_site and
share_client.api_version >= api_versions.APIVersion("2.76")):
if (
parsed_args.default_ad_site
and share_client.api_version >= api_versions.APIVersion("2.76")
):
search_opts['default_ad_site'] = parsed_args.default_ad_site
elif parsed_args.default_ad_site:
raise exceptions.CommandError(_(
"Filtering results by security service Default AD site is "
"available only for microversion >= 2.76"))
raise exceptions.CommandError(
_(
"Filtering results by security service Default AD site is "
"available only for microversion >= 2.76"
)
)
if parsed_args.share_network:
search_opts['share_network_id'] = oscutils.find_resource(
share_client.share_networks,
parsed_args.share_network).id
share_client.share_networks, parsed_args.share_network
).id
data = share_client.security_services.list(
search_opts=search_opts,
detailed=parsed_args.detail
search_opts=search_opts, detailed=parsed_args.detail
)
return (
columns,
(oscutils.get_item_properties(s, columns) for s in data)
(oscutils.get_item_properties(s, columns) for s in data),
)
+51 -31
View File
@@ -19,20 +19,23 @@ from manilaclient.common._i18n import _
class SetShareService(command.Command):
"""Enable/disable share service (Admin only)."""
_description = _("Enable/Disable share service (Admin only).")
def get_parser(self, prog_name):
parser = super(SetShareService, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'host',
metavar='<host>',
help=_("Host name as 'example_host@example_backend'.")
help=_("Host name as 'example_host@example_backend'."),
)
parser.add_argument(
'binary',
metavar='<binary>',
help=_("Service binary, could be 'manila-share', "
"'manila-scheduler' or 'manila-data'")
help=_(
"Service binary, could be 'manila-share', "
"'manila-scheduler' or 'manila-data'"
),
)
enable_group = parser.add_mutually_exclusive_group()
enable_group.add_argument(
@@ -48,15 +51,19 @@ class SetShareService(command.Command):
parser.add_argument(
"--disable-reason",
metavar="<reason>",
help=_("Reason for disabling the service "
"(should be used with --disable option)")
help=_(
"Reason for disabling the service "
"(should be used with --disable option)"
),
)
return parser
def take_action(self, parsed_args):
if parsed_args.disable_reason and not parsed_args.disable:
msg = _("Cannot specify option --disable-reason without "
"--disable specified.")
msg = _(
"Cannot specify option --disable-reason without "
"--disable specified."
)
raise exceptions.CommandError(msg)
share_client = self.app.client_manager.share
@@ -64,66 +71,74 @@ class SetShareService(command.Command):
if parsed_args.enable:
try:
share_client.services.enable(
parsed_args.host, parsed_args.binary)
parsed_args.host, parsed_args.binary
)
except Exception as e:
raise exceptions.CommandError(_(
"Failed to enable service: %s" % e))
raise exceptions.CommandError(
_(f"Failed to enable service: {e}")
)
if parsed_args.disable:
if parsed_args.disable_reason:
if share_client.api_version < api_versions.APIVersion("2.83"):
raise exceptions.CommandError(
"Service disable reason can be specified only with "
"manila API version >= 2.83")
"manila API version >= 2.83"
)
try:
if parsed_args.disable_reason:
share_client.services.disable(
parsed_args.host, parsed_args.binary,
disable_reason=parsed_args.disable_reason)
parsed_args.host,
parsed_args.binary,
disable_reason=parsed_args.disable_reason,
)
else:
share_client.services.disable(
parsed_args.host, parsed_args.binary)
parsed_args.host, parsed_args.binary
)
except Exception as e:
raise exceptions.CommandError(_(
"Failed to disable service: %s" % e))
raise exceptions.CommandError(
_(f"Failed to disable service: {e}")
)
class ListShareService(command.Lister):
"""List share services (Admin only)."""
_description = _("List share services (Admin only).")
def get_parser(self, prog_name):
parser = super(ListShareService, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"--host",
metavar="<host>",
default=None,
help=_("Filter services by name of the host.")
help=_("Filter services by name of the host."),
)
parser.add_argument(
"--binary",
metavar="<binary>",
default=None,
help=_("Filter services by the name of the service.")
help=_("Filter services by the name of the service."),
)
parser.add_argument(
"--status",
metavar="<status>",
default=None,
help=_("Filter results by status.")
help=_("Filter results by status."),
)
parser.add_argument(
"--state",
metavar="<state>",
default=None,
choices=['up', 'down'],
help=_("Filter results by state.")
help=_("Filter results by state."),
)
parser.add_argument(
"--zone",
metavar="<zone>",
default=None,
help=_("Filter services by their availability zone.")
help=_("Filter services by their availability zone."),
)
return parser
@@ -147,30 +162,34 @@ class ListShareService(command.Lister):
'Zone',
'Status',
'State',
'Updated At'
'Updated At',
]
if share_client.api_version >= api_versions.APIVersion("2.83"):
columns.append('Disabled Reason')
if share_client.api_version >= api_versions.APIVersion("2.86"):
columns.append('Ensuring')
data = (osc_utils.get_dict_properties(
service._info, columns) for service in services)
data = (
osc_utils.get_dict_properties(service._info, columns)
for service in services
)
return (columns, data)
class EnsureShareService(command.Command):
"""Run ensure shares in a back end (Admin only)."""
_description = _("Run ensure shares in a back end (Admin only).")
def get_parser(self, prog_name):
parser = super(EnsureShareService, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'host',
metavar='<host>',
help=_("Host to run ensure shares. "
"'example_host@example_backend'.")
help=_(
"Host to run ensure shares. 'example_host@example_backend'."
),
)
return parser
@@ -180,11 +199,12 @@ class EnsureShareService(command.Command):
if share_client.api_version < api_versions.APIVersion("2.86"):
raise exceptions.CommandError(
"Ensure shares API is only available in "
"manila API version >= 2.86")
"manila API version >= 2.86"
)
try:
share_client.services.ensure_shares(parsed_args.host)
except Exception as e:
raise exceptions.CommandError(
_("Failed to run ensure shares: %s" % e)
_(f"Failed to run ensure shares: {e}")
)
File diff suppressed because it is too large Load Diff
+181 -121
View File
@@ -32,7 +32,7 @@ ACCESS_RULE_ATTRIBUTES = [
'access_key',
'created_at',
'updated_at',
'properties'
'properties',
]
LOG = logging.getLogger(__name__)
@@ -40,25 +40,28 @@ LOG = logging.getLogger(__name__)
class ShareAccessAllow(command.ShowOne):
"""Create a new share access rule."""
_description = _("Create new share access rule")
def get_parser(self, prog_name):
parser = super(ShareAccessAllow, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'share',
metavar="<share>",
help=_('Name or ID of the NAS share to modify.')
help=_('Name or ID of the NAS share to modify.'),
)
parser.add_argument(
'access_type',
metavar="<access_type>",
help=_('Access rule type (only "ip", "user" (user or group), '
'"cert" or "cephx" are supported).')
help=_(
'Access rule type (only "ip", "user" (user or group), '
'"cert" or "cephx" are supported).'
),
)
parser.add_argument(
'access_to',
metavar="<access_to>",
help=_('Value that defines access.')
help=_('Value that defines access.'),
)
# metadata --> properties in osc
parser.add_argument(
@@ -66,9 +69,11 @@ class ShareAccessAllow(command.ShowOne):
type=str,
nargs='*',
metavar='<key=value>',
help=_('Space separated list of key=value pairs of properties. '
'OPTIONAL: Default=None. '
'Available only for API microversion >= 2.45.'),
help=_(
'Space separated list of key=value pairs of properties. '
'OPTIONAL: Default=None. '
'Available only for API microversion >= 2.45.'
),
)
parser.add_argument(
'--access-level',
@@ -76,45 +81,52 @@ class ShareAccessAllow(command.ShowOne):
type=str,
default=None,
choices=['rw', 'ro'],
help=_('Share access level ("rw" and "ro" access levels '
'are supported). Defaults to rw.')
help=_(
'Share access level ("rw" and "ro" access levels '
'are supported). Defaults to rw.'
),
)
parser.add_argument(
"--wait",
action='store_true',
help=_("Wait for share access rule creation.")
help=_("Wait for share access rule creation."),
)
parser.add_argument(
"--lock-visibility",
action='store_true',
default=False,
help=_("Whether the sensitive fields of the access rule redacted "
"to other users. Only available with API version >= 2.82.")
help=_(
"Whether the sensitive fields of the access rule redacted "
"to other users. Only available with API version >= 2.82."
),
)
parser.add_argument(
"--lock-deletion",
action='store_true',
default=False,
help=_("When enabled, a 'delete' lock will be placed against the "
"rule and the rule cannot be deleted while the lock "
"exists. Only available with API version >= 2.82.")
help=_(
"When enabled, a 'delete' lock will be placed against the "
"rule and the rule cannot be deleted while the lock "
"exists. Only available with API version >= 2.82."
),
)
parser.add_argument(
'--lock-reason',
metavar="<lock_reason>",
type=str,
default=None,
help=_("Reason for locking the access rule. Should only be "
"provided alongside a deletion or visibility lock. "
"Only available with API version >= 2.82.")
help=_(
"Reason for locking the access rule. Should only be "
"provided alongside a deletion or visibility lock. "
"Only available with API version >= 2.82."
),
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
share = apiutils.find_resource(share_client.shares,
parsed_args.share)
share = apiutils.find_resource(share_client.shares, parsed_args.share)
lock_kwargs = {}
if parsed_args.lock_visibility:
lock_kwargs['lock_visibility'] = parsed_args.lock_visibility
@@ -123,19 +135,22 @@ class ShareAccessAllow(command.ShowOne):
if parsed_args.lock_reason:
lock_kwargs['lock_reason'] = parsed_args.lock_reason
if (lock_kwargs
and share_client.api_version < api_versions.APIVersion(
"2.82")):
if lock_kwargs and share_client.api_version < api_versions.APIVersion(
"2.82"
):
raise exceptions.CommandError(
'Restricted access rules are only available starting '
'from API version 2.82.')
'from API version 2.82.'
)
if (lock_kwargs.get('lock_reason', None)
and not (lock_kwargs.get('lock_visibility', None)
or lock_kwargs.get('lock_deletion', None))):
if lock_kwargs.get('lock_reason', None) and not (
lock_kwargs.get('lock_visibility', None)
or lock_kwargs.get('lock_deletion', None)
):
raise exceptions.CommandError(
'Lock reason can only be set while locking the deletion or '
'visibility.')
'visibility.'
)
properties = {}
if parsed_args.properties:
@@ -144,83 +159,91 @@ class ShareAccessAllow(command.ShowOne):
else:
raise exceptions.CommandError(
"Adding properties to access rules is supported only "
"with API microversion 2.45 and beyond")
"with API microversion 2.45 and beyond"
)
try:
share_access_rule = share.allow(
access_type=parsed_args.access_type,
access=parsed_args.access_to,
access_level=parsed_args.access_level,
metadata=properties,
**lock_kwargs
**lock_kwargs,
)
if parsed_args.wait:
if not oscutils.wait_for_status(
status_f=share_client.share_access_rules.get,
res_id=share_access_rule['id'],
status_field='state'
status_field='state',
):
LOG.error(_("ERROR: Share access rule is in error state."))
share_access_rule = oscutils.find_resource(
share_client.share_access_rules,
share_access_rule['id'])._info
share_client.share_access_rules, share_access_rule['id']
)._info
share_access_rule.update(
{
'properties': utils.format_properties(
share_access_rule.pop('metadata', {}))
share_access_rule.pop('metadata', {})
)
}
)
return (ACCESS_RULE_ATTRIBUTES, oscutils.get_dict_properties(
share_access_rule,
ACCESS_RULE_ATTRIBUTES))
return (
ACCESS_RULE_ATTRIBUTES,
oscutils.get_dict_properties(
share_access_rule, ACCESS_RULE_ATTRIBUTES
),
)
except Exception as e:
raise exceptions.CommandError(
"Failed to create access to share "
"'%s': %s" % (share, e))
f"Failed to create access to share '{share}': {e}"
)
class ShareAccessDeny(command.Command):
"""Delete a share access rule."""
_description = _("Delete a share access rule")
def get_parser(self, prog_name):
parser = super(ShareAccessDeny, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'share',
metavar="<share>",
help=_('Name or ID of the NAS share to modify.')
help=_('Name or ID of the NAS share to modify.'),
)
parser.add_argument(
'id',
metavar="<id>",
help=_('ID of the access rule to be deleted.')
help=_('ID of the access rule to be deleted.'),
)
parser.add_argument(
"--wait",
action='store_true',
default=False,
help=_("Wait for share access rule deletion")
help=_("Wait for share access rule deletion"),
)
parser.add_argument(
"--unrestrict",
action='store_true',
default=False,
help=_("Seek access rule deletion despite restrictions. Only "
"available with API version >= 2.82.")
help=_(
"Seek access rule deletion despite restrictions. Only "
"available with API version >= 2.82."
),
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
share = apiutils.find_resource(share_client.shares,
parsed_args.share)
share = apiutils.find_resource(share_client.shares, parsed_args.share)
kwargs = {}
if parsed_args.unrestrict:
if share_client.api_version < api_versions.APIVersion("2.82"):
raise exceptions.CommandError(
'Restricted access rules are only available starting from '
'API version 2.82.')
'API version 2.82.'
)
kwargs['unrestrict'] = True
error = None
@@ -228,28 +251,32 @@ class ShareAccessDeny(command.Command):
share.deny(parsed_args.id, **kwargs)
if parsed_args.wait:
if not oscutils.wait_for_delete(
manager=share_client.share_access_rules,
res_id=parsed_args.id):
error = _("Failed to delete share access rule with ID: %s"
% parsed_args.id)
manager=share_client.share_access_rules,
res_id=parsed_args.id,
):
error = _(
f"Failed to delete share access rule with ID: {parsed_args.id}"
)
except Exception as e:
error = e
if error:
raise exceptions.CommandError(_(
"Failed to delete share access rule for share "
"'%s': %s" % (share, error)))
raise exceptions.CommandError(
_(
"Failed to delete share access rule for share "
f"'{share}': {error}"
)
)
class ListShareAccess(command.Lister):
"""List share access rules."""
_description = _("List share access rule")
def get_parser(self, prog_name):
parser = super(ListShareAccess, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'share',
metavar="<share>",
help=_('Name or ID of the share.')
'share', metavar="<share>", help=_('Name or ID of the share.')
)
# metadata --> properties in osc
@@ -259,41 +286,42 @@ class ListShareAccess(command.Lister):
nargs='*',
metavar='<key=value>',
default=None,
help=_('Filters results by properties (key=value). '
'OPTIONAL: Default=None. '
'Available only for API microversion >= 2.45'),
help=_(
'Filters results by properties (key=value). '
'OPTIONAL: Default=None. '
'Available only for API microversion >= 2.45'
),
)
parser.add_argument(
"--access-type",
metavar="<access_type>",
default=None,
help=_("Filter access rules by the access type.")
help=_("Filter access rules by the access type."),
)
parser.add_argument(
"--access-key",
metavar="<access_key>",
default=None,
help=_("Filter access rules by the access key.")
help=_("Filter access rules by the access key."),
)
parser.add_argument(
"--access-to",
metavar="<access_to>",
default=None,
help=_("Filter access rules by the access to field.")
help=_("Filter access rules by the access to field."),
)
parser.add_argument(
"--access-level",
metavar="<access_level>",
default=None,
help=_("Filter access rules by the access level.")
help=_("Filter access rules by the access level."),
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
share = apiutils.find_resource(share_client.shares,
parsed_args.share)
share = apiutils.find_resource(share_client.shares, parsed_args.share)
access_type = parsed_args.access_type
access_key = parsed_args.access_key
access_to = parsed_args.access_to
@@ -303,16 +331,17 @@ class ListShareAccess(command.Lister):
'access_type': access_type,
'access_key': access_key,
'access_to': access_to,
'access_level': access_level
'access_level': access_level,
}
if (any(extended_filter_keys.values())
and share_client.api_version < api_versions.APIVersion(
"2.82")):
if any(
extended_filter_keys.values()
) and share_client.api_version < api_versions.APIVersion("2.82"):
raise exceptions.CommandError(
'Filtering access rules by access_type, access_key, access_to '
'and access_level is available starting from API version '
'2.82.')
'2.82.'
)
search_opts = {}
if share_client.api_version >= api_versions.APIVersion("2.82"):
@@ -324,15 +353,17 @@ class ListShareAccess(command.Lister):
if parsed_args.properties:
search_opts = {
'metadata': utils.extract_properties(
parsed_args.properties)
parsed_args.properties
)
}
access_list = share_client.share_access_rules.access_list(
share,
search_opts)
share, search_opts
)
elif parsed_args.properties:
raise exceptions.CommandError(
"Filtering access rules by properties is supported only "
"with API microversion 2.45 and beyond.")
"with API microversion 2.45 and beyond."
)
else:
access_list = share.access_list()
@@ -341,7 +372,7 @@ class ListShareAccess(command.Lister):
'Access Type',
'Access To',
'Access Level',
'State'
'State',
]
if share_client.api_version >= api_versions.APIVersion("2.21"):
@@ -351,24 +382,27 @@ class ListShareAccess(command.Lister):
list_of_keys.append('Created At')
list_of_keys.append('Updated At')
values = (oscutils.get_item_properties(
a, list_of_keys) for a in access_list)
values = (
oscutils.get_item_properties(a, list_of_keys) for a in access_list
)
return (list_of_keys, values)
class ShowShareAccess(command.ShowOne):
"""Display a share access rule."""
_description = _(
"Display a share access rule. "
"Available for API microversion 2.45 and higher")
"Available for API microversion 2.45 and higher"
)
def get_parser(self, prog_name):
parser = super(ShowShareAccess, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'access_id',
metavar="<access_id>",
help=_('ID of the NAS share access rule.')
help=_('ID of the NAS share access rule.'),
)
return parser
@@ -382,30 +416,37 @@ class ShowShareAccess(command.ShowOne):
access_rule._info.update(
{
'properties': utils.format_properties(
access_rule._info.pop('metadata', {}))
access_rule._info.pop('metadata', {})
)
}
)
return (ACCESS_RULE_ATTRIBUTES, oscutils.get_dict_properties(
access_rule._info,
ACCESS_RULE_ATTRIBUTES))
return (
ACCESS_RULE_ATTRIBUTES,
oscutils.get_dict_properties(
access_rule._info, ACCESS_RULE_ATTRIBUTES
),
)
else:
raise exceptions.CommandError(
"Displaying share access rule details is only available "
"with API microversion 2.45 and higher.")
"with API microversion 2.45 and higher."
)
class SetShareAccess(command.Command):
"""Set properties to share access rule."""
_description = _(
"Set properties to share access rule. "
"Available for API microversion 2.45 and higher")
"Available for API microversion 2.45 and higher"
)
def get_parser(self, prog_name):
parser = super(SetShareAccess, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'access_id',
metavar="<access_id>",
help=_('ID of the NAS share access rule.')
help=_('ID of the NAS share access rule.'),
)
# metadata --> properties in osc
parser.add_argument(
@@ -413,34 +454,44 @@ class SetShareAccess(command.Command):
metavar='<key=value>',
default={},
action=parseractions.KeyValueAction,
help=_('Set a property to this share access rule. '
'(Repeat option to set multiple properties) '
'Available only for API microversion >= 2.45.'),
help=_(
'Set a property to this share access rule. '
'(Repeat option to set multiple properties) '
'Available only for API microversion >= 2.45.'
),
)
parser.add_argument(
"--access-level",
metavar="<access_level>",
default=None,
choices=['rw', 'ro'],
help=_('Share access level ("rw" and "ro" access levels '
'are supported) to set.')
help=_(
'Share access level ("rw" and "ro" access levels '
'are supported) to set.'
),
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
if (parsed_args.property and
share_client.api_version < api_versions.APIVersion("2.45")):
if (
parsed_args.property
and share_client.api_version < api_versions.APIVersion("2.45")
):
raise exceptions.CommandError(
"Setting properties to access rule is supported only "
"with API microversion 2.45 and higher")
"with API microversion 2.45 and higher"
)
if (parsed_args.access_level and
share_client.api_version < api_versions.APIVersion("2.88")):
if (
parsed_args.access_level
and share_client.api_version < api_versions.APIVersion("2.88")
):
raise exceptions.CommandError(
"Setting access level to access rule is supported only "
"with API microversion 2.88 and higher")
"with API microversion 2.88 and higher"
)
access_rule = share_client.share_access_rules.get(
parsed_args.access_id
@@ -448,45 +499,51 @@ class SetShareAccess(command.Command):
if parsed_args.property:
try:
share_client.share_access_rules.set_metadata(
access_rule,
parsed_args.property)
access_rule, parsed_args.property
)
except Exception as e:
raise exceptions.CommandError(
"Failed to set properties to share access rule with ID "
"'%s': %s" % (access_rule.id, e))
f"'{access_rule.id}': {e}"
)
if parsed_args.access_level:
try:
share_client.share_access_rules.set_access_level(
access_rule,
parsed_args.access_level)
access_rule, parsed_args.access_level
)
except Exception as e:
raise exceptions.CommandError(
"Failed to set access level to share access rule with ID "
"'%s': %s" % (access_rule.id, e))
f"'{access_rule.id}': {e}"
)
class UnsetShareAccess(command.Command):
"""Unset properties of share access rule."""
_description = _(
"Unset properties of share access rule. "
"Available for API microversion 2.45 and higher")
"Available for API microversion 2.45 and higher"
)
def get_parser(self, prog_name):
parser = super(UnsetShareAccess, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'access_id',
metavar="<access_id>",
help=_('ID of the NAS share access rule.')
help=_('ID of the NAS share access rule.'),
)
# metadata --> properties in osc
parser.add_argument(
'--property',
metavar='<key>',
action='append',
help=_('Remove property from share access rule. '
'(Repeat option to remove multiple properties) '
'Available only for API microversion >= 2.45.'),
help=_(
'Remove property from share access rule. '
'(Repeat option to remove multiple properties) '
'Available only for API microversion >= 2.45.'
),
)
return parser
@@ -500,17 +557,20 @@ class UnsetShareAccess(command.Command):
if parsed_args.property:
try:
share_client.share_access_rules.unset_metadata(
access_rule,
parsed_args.property)
access_rule, parsed_args.property
)
except Exception as e:
raise exceptions.CommandError(
"Failed to unset properties for share access rule "
"with ID '%s': %s" % (access_rule.id, e))
f"with ID '{access_rule.id}': {e}"
)
else:
raise exceptions.CommandError(
"Please specify '--property <key>' to unset a property. ")
"Please specify '--property <key>' to unset a property. "
)
else:
raise exceptions.CommandError(
"Option to unset properties of access rule is available only "
"for API microversion 2.45 and higher")
"for API microversion 2.45 and higher"
)
+147 -107
View File
@@ -31,46 +31,49 @@ LOG = logging.getLogger(__name__)
class CreateShareBackup(command.ShowOne):
"""Create a share backup."""
_description = _("Create a backup of the given share")
def get_parser(self, prog_name):
parser = super(CreateShareBackup, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"share",
metavar="<share>",
help=_("Name or ID of the share to backup.")
help=_("Name or ID of the share to backup."),
)
parser.add_argument(
'--name',
metavar='<name>',
default=None,
help=_('Optional share backup name. (Default=None).')
help=_('Optional share backup name. (Default=None).'),
)
parser.add_argument(
'--description',
metavar='<description>',
default=None,
help=_('Optional share backup description. (Default=None).')
help=_('Optional share backup description. (Default=None).'),
)
parser.add_argument(
"--backup-options",
metavar="<key=value>",
default={},
action=parseractions.KeyValueAction,
help=_("Backup driver option key=value pairs (Optional, "
"Default=None)."),
help=_(
"Backup driver option key=value pairs (Optional, "
"Default=None)."
),
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
share = osc_utils.find_resource(
share_client.shares, parsed_args.share)
share = osc_utils.find_resource(share_client.shares, parsed_args.share)
body = {}
if parsed_args.backup_options:
body['backup_options'] = utils.extract_key_value_options(
parsed_args.backup_options)
parsed_args.backup_options
)
if parsed_args.description:
body['description'] = parsed_args.description
if parsed_args.name:
@@ -83,21 +86,22 @@ class CreateShareBackup(command.ShowOne):
class DeleteShareBackup(command.Command):
"""Delete one or more share backups."""
_description = _("Delete one or more share backups")
def get_parser(self, prog_name):
parser = super(DeleteShareBackup, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"backup",
metavar="<backup>",
nargs="+",
help=_("Name or ID of the backup(s) to delete")
help=_("Name or ID of the backup(s) to delete"),
)
parser.add_argument(
"--wait",
action='store_true',
default=False,
help=_("Wait for share backup deletion")
help=_("Wait for share backup deletion"),
)
return parser
@@ -108,70 +112,78 @@ class DeleteShareBackup(command.Command):
for backup in parsed_args.backup:
try:
share_backup_obj = osc_utils.find_resource(
share_client.share_backups, backup)
share_client.share_backups, backup
)
share_client.share_backups.delete(share_backup_obj)
if parsed_args.wait:
if not osc_utils.wait_for_delete(
manager=share_client.share_backups,
res_id=share_backup_obj.id):
manager=share_client.share_backups,
res_id=share_backup_obj.id,
):
result += 1
except Exception as e:
result += 1
LOG.error(_(
"Failed to delete a share backup with "
"name or ID '%(backup)s': %(e)s"),
{'backup': backup, 'e': e})
LOG.error(
_(
"Failed to delete a share backup with "
"name or ID '%(backup)s': %(e)s"
),
{'backup': backup, 'e': e},
)
if result > 0:
total = len(parsed_args.backup)
msg = (_("%(result)s of %(total)s backups failed "
"to delete.") % {'result': result, 'total': total})
msg = _("%(result)s of %(total)s backups failed to delete.") % {
'result': result,
'total': total,
}
raise exceptions.CommandError(msg)
class ListShareBackup(command.Lister):
"""List share backups."""
_description = _("List share backups")
def get_parser(self, prog_name):
parser = super(ListShareBackup, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"--share",
metavar="<share>",
default=None,
help=_("Name or ID of the share to list backups for.")
help=_("Name or ID of the share to list backups for."),
)
parser.add_argument(
"--name",
metavar="<name>",
default=None,
help=_("Filter results by name. Default=None.")
help=_("Filter results by name. Default=None."),
)
parser.add_argument(
'--description',
metavar="<description>",
default=None,
help=_("Filter results by description. Default=None.")
help=_("Filter results by description. Default=None."),
)
parser.add_argument(
"--name~",
metavar="<name~>",
default=None,
help=_("Filter results matching a share backup name pattern. ")
help=_("Filter results matching a share backup name pattern. "),
)
parser.add_argument(
'--description~',
metavar="<description~>",
default=None,
help=_("Filter results matching a share backup description ")
help=_("Filter results matching a share backup description "),
)
parser.add_argument(
'--status',
metavar="<status>",
default=None,
help=_('Filter results by status. Default=None.')
help=_('Filter results by status. Default=None.'),
)
parser.add_argument(
"--limit",
@@ -179,29 +191,32 @@ class ListShareBackup(command.Lister):
type=int,
default=None,
action=parseractions.NonNegativeAction,
help=_("Limit the number of backups returned. Default=None.")
help=_("Limit the number of backups returned. Default=None."),
)
parser.add_argument(
'--offset',
metavar="<offset>",
default=None,
help='Start position of backup records listing.')
help='Start position of backup records listing.',
)
parser.add_argument(
'--sort-key', '--sort_key',
'--sort-key',
'--sort_key',
metavar='<sort_key>',
type=str,
default=None,
help='Key to be sorted, available keys are %(keys)s. '
'Default=None.'
% {'keys': constants.BACKUP_SORT_KEY_VALUES})
help=f'Key to be sorted, available keys are {constants.BACKUP_SORT_KEY_VALUES}. '
'Default=None.',
)
parser.add_argument(
'--sort-dir', '--sort_dir',
'--sort-dir',
'--sort_dir',
metavar='<sort_dir>',
type=str,
default=None,
help='Sort direction, available values are %(values)s. '
'OPTIONAL: Default=None.' % {
'values': constants.SORT_DIR_VALUES})
help=f'Sort direction, available values are {constants.SORT_DIR_VALUES}. '
'OPTIONAL: Default=None.',
)
parser.add_argument(
'--detail',
dest='detail',
@@ -210,7 +225,8 @@ class ListShareBackup(command.Lister):
type=int,
const=1,
default=0,
help="Show detailed information about share backups.")
help="Show detailed information about share backups.",
)
return parser
def take_action(self, parsed_args):
@@ -218,19 +234,25 @@ class ListShareBackup(command.Lister):
share_id = None
if parsed_args.share:
share_id = osc_utils.find_resource(share_client.shares,
parsed_args.share).id
columns = [
'ID',
'Name',
'Share ID',
'Status'
]
share_id = osc_utils.find_resource(
share_client.shares, parsed_args.share
).id
columns = ['ID', 'Name', 'Share ID', 'Status']
if parsed_args.detail:
columns.extend(['Description', 'Size', 'Created At',
'Updated At', 'Availability Zone', 'Progress',
'Restore Progress', 'Host', 'Topic'])
columns.extend(
[
'Description',
'Size',
'Created At',
'Updated At',
'Availability Zone',
'Progress',
'Restore Progress',
'Host',
'Topic',
]
)
search_opts = {
'limit': parsed_args.limit,
@@ -245,56 +267,62 @@ class ListShareBackup(command.Lister):
search_opts['description~'] = getattr(parsed_args, 'description~')
backups = share_client.share_backups.list(
detailed=parsed_args.detail, search_opts=search_opts,
sort_key=parsed_args.sort_key, sort_dir=parsed_args.sort_dir)
detailed=parsed_args.detail,
search_opts=search_opts,
sort_key=parsed_args.sort_key,
sort_dir=parsed_args.sort_dir,
)
return (columns,
(osc_utils.get_item_properties(b, columns) for b in backups))
return (
columns,
(osc_utils.get_item_properties(b, columns) for b in backups),
)
class ShowShareBackup(command.ShowOne):
"""Show share backup."""
_description = _("Show details of a backup")
def get_parser(self, prog_name):
parser = super(ShowShareBackup, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"backup",
metavar="<backup>",
help=_("ID of the share backup. ")
"backup", metavar="<backup>", help=_("ID of the share backup. ")
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
backup = osc_utils.find_resource(share_client.share_backups,
parsed_args.backup)
backup = osc_utils.find_resource(
share_client.share_backups, parsed_args.backup
)
backup._info.pop('links', None)
return self.dict2columns(backup._info)
class RestoreShareBackup(command.Command):
"""Restore share backup to share"""
_description = _("Attempt to restore share backup")
def get_parser(self, prog_name):
parser = super(RestoreShareBackup, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"backup",
metavar="<backup>",
help=_('ID of backup to restore.')
"backup", metavar="<backup>", help=_('ID of backup to restore.')
)
parser.add_argument(
"--target-share",
metavar="<target-share>",
default=None,
help=_('share to restore backup to. Source share if none supplied')
help=_(
'share to restore backup to. Source share if none supplied'
),
)
parser.add_argument(
'--wait',
action='store_true',
default=False,
help=_('Wait for restore conclusion')
help=_('Wait for restore conclusion'),
)
return parser
@@ -303,19 +331,18 @@ class RestoreShareBackup(command.Command):
kwargs = {}
share_backup = osc_utils.find_resource(
share_client.share_backups,
parsed_args.backup
share_client.share_backups, parsed_args.backup
)
target_share_id = None
if parsed_args.target_share is not None:
if share_client.api_version < api_versions.APIVersion('2.91'):
raise exceptions.CommandError(
'performing targeted restores is only available '
'for API microversion >= 2.91')
'for API microversion >= 2.91'
)
else:
target_share_id = osc_utils.find_resource(
share_client.shares,
parsed_args.target_share
share_client.shares, parsed_args.target_share
).id
kwargs['target_share_id'] = target_share_id
@@ -326,42 +353,50 @@ class RestoreShareBackup(command.Command):
status_f=share_client.shares.get,
res_id=(target_share_id or share_backup.share_id),
success_status=['available'],
error_status=['error', 'backup_restoring_error']
error_status=['error', 'backup_restoring_error'],
):
LOG.error(_("ERROR: share is in error state."))
class SetShareBackup(command.Command):
"""Set share backup properties."""
_description = _("Set share backup properties")
def get_parser(self, prog_name):
parser = super(SetShareBackup, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"backup",
metavar="<backup>",
help=_('Name or ID of the backup to set a property for')
help=_('Name or ID of the backup to set a property for'),
)
parser.add_argument(
"--name",
metavar="<name>",
default=None,
help=_("Set a name to the backup.")
help=_("Set a name to the backup."),
)
parser.add_argument(
"--description",
metavar="<description>",
default=None,
help=_("Set a description to the backup.")
help=_("Set a description to the backup."),
)
parser.add_argument(
"--status",
metavar="<status>",
choices=['available', 'error', 'creating', 'deleting',
'restoring'],
help=_("Assign a status to the backup(Admin only). "
"Options include : available, error, creating, "
"deleting, restoring.")
choices=[
'available',
'error',
'creating',
'deleting',
'restoring',
],
help=_(
"Assign a status to the backup(Admin only). "
"Options include : available, error, creating, "
"deleting, restoring."
),
)
return parser
@@ -370,8 +405,8 @@ class SetShareBackup(command.Command):
result = 0
share_backup = osc_utils.find_resource(
share_client.share_backups,
parsed_args.backup)
share_client.share_backups, parsed_args.backup
)
kwargs = {}
@@ -384,57 +419,60 @@ class SetShareBackup(command.Command):
share_client.share_backups.update(share_backup, **kwargs)
except Exception as e:
result += 1
LOG.error(_(
"Failed to set share backup properties "
"'%(properties)s': %(exception)s"),
{'properties': kwargs,
'exception': e})
LOG.error(
_(
"Failed to set share backup properties "
"'%(properties)s': %(exception)s"
),
{'properties': kwargs, 'exception': e},
)
if parsed_args.status:
try:
share_client.share_backups.reset_status(
share_backup,
parsed_args.status
share_backup, parsed_args.status
)
except Exception as e:
result += 1
LOG.error(_(
"Failed to update backup status to "
"'%(status)s': %(e)s"),
{'status': parsed_args.status, 'e': e})
LOG.error(
_("Failed to update backup status to '%(status)s': %(e)s"),
{'status': parsed_args.status, 'e': e},
)
if result > 0:
raise exceptions.CommandError(_("One or more of the "
"set operations failed"))
raise exceptions.CommandError(
_("One or more of the set operations failed")
)
class UnsetShareBackup(command.Command):
"""Unset share backup properties."""
_description = _("Unset share backup properties")
def get_parser(self, prog_name):
parser = super(UnsetShareBackup, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"backup",
metavar="<backup>",
help=_('Name or ID of the backup to unset a property for')
help=_('Name or ID of the backup to unset a property for'),
)
parser.add_argument(
"--name",
action='store_true',
help=_("Unset a name to the backup.")
help=_("Unset a name to the backup."),
)
parser.add_argument(
"--description",
action='store_true',
help=_("Unset a description to the backup.")
help=_("Unset a description to the backup."),
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
share_backup = osc_utils.find_resource(
share_client.share_backups,
parsed_args.backup)
share_client.share_backups, parsed_args.backup
)
kwargs = {}
if parsed_args.name:
@@ -448,8 +486,10 @@ class UnsetShareBackup(command.Command):
try:
share_client.share_backups.update(share_backup, **kwargs)
except Exception as e:
LOG.error(_(
"Failed to unset share backup properties "
"'%(properties)s': %(exception)s"),
{'properties': kwargs,
'exception': e})
LOG.error(
_(
"Failed to unset share backup properties "
"'%(properties)s': %(exception)s"
),
{'properties': kwargs, 'exception': e},
)
+114 -86
View File
@@ -25,35 +25,35 @@ LOG = logging.getLogger(__name__)
class CreateShareGroupSnapshot(command.ShowOne):
"""Create a share group snapshot."""
_description = _(
"Create a share group snapshot of the given share group")
_description = _("Create a share group snapshot of the given share group")
def get_parser(self, prog_name):
parser = super(
CreateShareGroupSnapshot, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"share_group",
metavar="<share-group>",
help=_("Name or ID of the share group.")
help=_("Name or ID of the share group."),
)
parser.add_argument(
"--name",
metavar="<name>",
default=None,
help=_("Optional share group snapshot name. (Default=None)")
help=_("Optional share group snapshot name. (Default=None)"),
)
parser.add_argument(
"--description",
metavar="<description>",
default=None,
help=_("Optional share group snapshot description. "
"(Default=None)")
help=_(
"Optional share group snapshot description. (Default=None)"
),
)
parser.add_argument(
'--wait',
action='store_true',
default=False,
help=_('Wait for share group snapshot creation')
help=_('Wait for share group snapshot creation'),
)
return parser
@@ -61,8 +61,8 @@ class CreateShareGroupSnapshot(command.ShowOne):
share_client = self.app.client_manager.share
share_group = osc_utils.find_resource(
share_client.share_groups,
parsed_args.share_group)
share_client.share_groups, parsed_args.share_group
)
share_group_snapshot = share_client.share_group_snapshots.create(
share_group,
@@ -73,13 +73,13 @@ class CreateShareGroupSnapshot(command.ShowOne):
if not osc_utils.wait_for_status(
status_f=share_client.share_group_snapshots.get,
res_id=share_group_snapshot.id,
success_status=['available']
success_status=['available'],
):
LOG.error(_("ERROR: Share group snapshot is in error state."))
share_group_snapshot = osc_utils.find_resource(
share_client.share_group_snapshots,
share_group_snapshot.id)
share_client.share_group_snapshots, share_group_snapshot.id
)
data = share_group_snapshot._info
data.pop('links', None)
@@ -90,28 +90,31 @@ class CreateShareGroupSnapshot(command.ShowOne):
class DeleteShareGroupSnapshot(command.Command):
"""Delete one or more share group snapshots."""
_description = _("Delete one or more share group snapshot")
def get_parser(self, prog_name):
parser = super(DeleteShareGroupSnapshot, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"share_group_snapshot",
metavar="<share-group-snapshot>",
nargs="+",
help=_("Name or ID of the group snapshot(s) to delete")
help=_("Name or ID of the group snapshot(s) to delete"),
)
parser.add_argument(
"--force",
action='store_true',
default=False,
help=_("Attempt to force delete the share group snapshot(s) "
"(Default=False) (Admin only).")
help=_(
"Attempt to force delete the share group snapshot(s) "
"(Default=False) (Admin only)."
),
)
parser.add_argument(
"--wait",
action='store_true',
default=False,
help=_("Wait for share group snapshot deletion")
help=_("Wait for share group snapshot deletion"),
)
return parser
@@ -122,43 +125,46 @@ class DeleteShareGroupSnapshot(command.Command):
for share_group_snapshot in parsed_args.share_group_snapshot:
try:
share_group_snapshot_obj = osc_utils.find_resource(
share_client.share_group_snapshots,
share_group_snapshot)
share_client.share_group_snapshots, share_group_snapshot
)
share_client.share_group_snapshots.delete(
share_group_snapshot_obj,
force=parsed_args.force)
share_group_snapshot_obj, force=parsed_args.force
)
if parsed_args.wait:
if not osc_utils.wait_for_delete(
manager=share_client.share_group_snapshots,
res_id=share_group_snapshot_obj.id):
manager=share_client.share_group_snapshots,
res_id=share_group_snapshot_obj.id,
):
result += 1
except Exception as e:
result += 1
LOG.error(
'Failed to delete a share group snapshot with '
f'name or ID {share_group_snapshot}: {e}')
f'name or ID {share_group_snapshot}: {e}'
)
if result > 0:
total = len(parsed_args.share_group_snapshot)
msg = (f'{result} of {total} share group snapshots failed '
'to delete.')
msg = (
f'{result} of {total} share group snapshots failed to delete.'
)
raise exceptions.CommandError(msg)
class ShowShareGroupSnapshot(command.ShowOne):
"""Display a share group snapshot"""
_description = _(
"Show details about a share group snapshot")
_description = _("Show details about a share group snapshot")
def get_parser(self, prog_name):
parser = super(ShowShareGroupSnapshot, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"share_group_snapshot",
metavar="<share-group-snapshot>",
help=_("Name or ID of the share group snapshot to display")
help=_("Name or ID of the share group snapshot to display"),
)
return parser
@@ -167,7 +173,8 @@ class ShowShareGroupSnapshot(command.ShowOne):
share_group_snapshot = osc_utils.find_resource(
share_client.share_group_snapshots,
parsed_args.share_group_snapshot)
parsed_args.share_group_snapshot,
)
data = share_group_snapshot._info
data.pop('links', None)
@@ -178,36 +185,44 @@ class ShowShareGroupSnapshot(command.ShowOne):
class SetShareGroupSnapshot(command.Command):
"""Set share group snapshot properties."""
_description = _("Set share group snapshot properties")
def get_parser(self, prog_name):
parser = super(SetShareGroupSnapshot, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"share_group_snapshot",
metavar="<share-group-snapshot>",
help=_('Name or ID of the snapshot to set a property for')
help=_('Name or ID of the snapshot to set a property for'),
)
parser.add_argument(
"--name",
metavar="<name>",
default=None,
help=_("Set a name to the snapshot.")
help=_("Set a name to the snapshot."),
)
parser.add_argument(
"--description",
metavar="<description>",
default=None,
help=_("Set a description to the snapshot.")
help=_("Set a description to the snapshot."),
)
parser.add_argument(
"--status",
metavar="<status>",
choices=['available', 'error', 'creating',
'deleting', 'error_deleting'],
help=_("Explicitly set the state of a share group snapshot"
"(Admin only). "
"Options include : available, error, creating, "
"deleting, error_deleting.")
choices=[
'available',
'error',
'creating',
'deleting',
'error_deleting',
],
help=_(
"Explicitly set the state of a share group snapshot"
"(Admin only). "
"Options include : available, error, creating, "
"deleting, error_deleting."
),
)
return parser
@@ -217,7 +232,8 @@ class SetShareGroupSnapshot(command.Command):
share_group_snapshot = osc_utils.find_resource(
share_client.share_group_snapshots,
parsed_args.share_group_snapshot)
parsed_args.share_group_snapshot,
)
kwargs = {}
@@ -229,42 +245,46 @@ class SetShareGroupSnapshot(command.Command):
if kwargs:
try:
share_client.share_group_snapshots.update(
share_group_snapshot,
**kwargs
share_group_snapshot, **kwargs
)
except Exception as e:
result += 1
LOG.error('Failed to set name or desciption for '
'share group snapshot with ID '
f'{share_group_snapshot.id}: {e}')
LOG.error(
'Failed to set name or desciption for '
'share group snapshot with ID '
f'{share_group_snapshot.id}: {e}'
)
if parsed_args.status:
try:
share_client.share_group_snapshots.reset_state(
share_group_snapshot,
parsed_args.status
share_group_snapshot, parsed_args.status
)
except Exception as e:
result += 1
LOG.error('Failed to set status for '
'share group snapshot with ID '
f'{share_group_snapshot.id}: {e}')
LOG.error(
'Failed to set status for '
'share group snapshot with ID '
f'{share_group_snapshot.id}: {e}'
)
if result > 0:
raise exceptions.CommandError(_(
"One or more of the set operations failed"))
raise exceptions.CommandError(
_("One or more of the set operations failed")
)
class UnsetShareGroupSnapshot(command.Command):
"""Unset a share group snapshot property."""
_description = _("Unset a share group snapshot property")
def get_parser(self, prog_name):
parser = super(UnsetShareGroupSnapshot, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"share_group_snapshot",
metavar="<share-group-snapshot>",
help=_("Name or ID of the group snapshot to unset a property of")
help=_("Name or ID of the group snapshot to unset a property of"),
)
parser.add_argument(
"--name",
@@ -283,7 +303,8 @@ class UnsetShareGroupSnapshot(command.Command):
share_group_snapshot = osc_utils.find_resource(
share_client.share_group_snapshots,
parsed_args.share_group_snapshot)
parsed_args.share_group_snapshot,
)
kwargs = {}
if parsed_args.name:
@@ -295,44 +316,45 @@ class UnsetShareGroupSnapshot(command.Command):
if kwargs:
try:
share_client.share_group_snapshots.update(
share_group_snapshot,
**kwargs
share_group_snapshot, **kwargs
)
except Exception as e:
raise exceptions.CommandError(
'Failed to unset name or description for '
f'share group snapshot : {e}')
f'share group snapshot : {e}'
)
class ListShareGroupSnapshot(command.Lister):
"""List share group snapshots."""
_description = _("List share group snapshots")
def get_parser(self, prog_name):
parser = super(ListShareGroupSnapshot, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"--all-projects",
action='store_true',
default=False,
help=_("Display information from all projects (Admin only).")
help=_("Display information from all projects (Admin only)."),
)
parser.add_argument(
"--name",
metavar="<name>",
default=None,
help=_("Filter results by name.")
help=_("Filter results by name."),
)
parser.add_argument(
"--status",
metavar="<status>",
default=None,
help=_("Filter results by status.")
help=_("Filter results by status."),
)
parser.add_argument(
"--share-group",
metavar="<share-group>",
default=None,
help=_("Filter results by share group name or ID.")
help=_("Filter results by share group name or ID."),
)
parser.add_argument(
"--limit",
@@ -340,26 +362,27 @@ class ListShareGroupSnapshot(command.Lister):
type=int,
default=None,
action=parseractions.NonNegativeAction,
help=_("Limit the number of share groups returned")
help=_("Limit the number of share groups returned"),
)
parser.add_argument(
"--marker",
metavar="<marker>",
help=_("The last share group snapshot ID of the "
"previous page")
help=_("The last share group snapshot ID of the previous page"),
)
parser.add_argument(
'--sort',
metavar="<key>[:<direction>]",
default='name:asc',
help=_("Sort output by selected keys and directions(asc or desc) "
"(default: name:asc), multiple keys and directions can be "
"specified separated by comma")
help=_(
"Sort output by selected keys and directions(asc or desc) "
"(default: name:asc), multiple keys and directions can be "
"specified separated by comma"
),
)
parser.add_argument(
"--detailed",
action="store_true",
help=_("Show detailed information about share group snapshot. ")
help=_("Show detailed information about share group snapshot. "),
)
return parser
@@ -369,8 +392,8 @@ class ListShareGroupSnapshot(command.Lister):
share_group_id = None
if parsed_args.share_group:
share_group_id = osc_utils.find_resource(
share_client.share_groups,
parsed_args.share_group).id
share_client.share_groups, parsed_args.share_group
).id
columns = [
'ID',
@@ -389,19 +412,23 @@ class ListShareGroupSnapshot(command.Lister):
}
if parsed_args.detailed:
columns.extend([
'Created At',
'Share Group ID',
])
columns.extend(
[
'Created At',
'Share Group ID',
]
)
if parsed_args.all_projects:
columns.append('Project ID')
share_group_snapshots = share_client.share_group_snapshots.list(
search_opts=search_opts)
search_opts=search_opts
)
share_group_snapshots = utils.sort_items(
share_group_snapshots, parsed_args.sort, str)
share_group_snapshots, parsed_args.sort, str
)
data = (
osc_utils.get_dict_properties(share_group_snapshot._info, columns)
@@ -413,15 +440,15 @@ class ListShareGroupSnapshot(command.Lister):
class ListShareGroupSnapshotMembers(command.Lister):
"""List members for share group snapshot."""
_description = _("List members of share group snapshot")
def get_parser(self, prog_name):
parser = super(
ListShareGroupSnapshotMembers, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
"share_group_snapshot",
metavar="<share-group-snapshot>",
help=_("Name or ID of the group snapshot to list members for")
help=_("Name or ID of the group snapshot to list members for"),
)
return parser
@@ -432,7 +459,8 @@ class ListShareGroupSnapshotMembers(command.Lister):
share_group_snapshot = osc_utils.find_resource(
share_client.share_group_snapshots,
parsed_args.share_group_snapshot)
parsed_args.share_group_snapshot,
)
data = (
osc_utils.get_dict_properties(member, columns)
+65 -41
View File
@@ -25,21 +25,23 @@ LOG = logging.getLogger(__name__)
class ShareGroupTypeAccessAllow(command.Command):
"""Allow a project to access a share group type."""
_description = _("Allow a project to access a share group type "
"(Admin only).")
_description = _(
"Allow a project to access a share group type (Admin only)."
)
def get_parser(self, prog_name):
parser = super(ShareGroupTypeAccessAllow, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'share_group_type',
metavar="<share-group-type>",
help=_("Share group type name or ID to allow access to.")
help=_("Share group type name or ID to allow access to."),
)
parser.add_argument(
'projects',
metavar="<project>",
nargs="+",
help=_("Project Name or ID to add share group type access for.")
help=_("Project Name or ID to add share group type access for."),
)
identity_common.add_project_domain_option_to_parser(parser)
return parser
@@ -50,42 +52,52 @@ class ShareGroupTypeAccessAllow(command.Command):
result = 0
share_group_type = apiutils.find_resource(
share_client.share_group_types, parsed_args.share_group_type)
share_client.share_group_types, parsed_args.share_group_type
)
for project in parsed_args.projects:
try:
project_obj = identity_common.find_project(
identity_client, project, parsed_args.project_domain)
identity_client, project, parsed_args.project_domain
)
share_client.share_group_type_access.add_project_access(
share_group_type, project_obj.id)
share_group_type, project_obj.id
)
except Exception as e:
result += 1
LOG.error(_(
"Failed to allow access for project '%(project)s' "
"to share group type with name or ID "
"'%(share_group_type)s': %(e)s"),
{'project': project,
'share_group_type': share_group_type, 'e': e})
LOG.error(
_(
"Failed to allow access for project '%(project)s' "
"to share group type with name or ID "
"'%(share_group_type)s': %(e)s"
),
{
'project': project,
'share_group_type': share_group_type,
'e': e,
},
)
if result > 0:
total = len(parsed_args.projects)
msg = (_("Failed to allow access to "
"%(result)s of %(total)s projects") % {'result': result,
'total': total})
msg = _(
"Failed to allow access to %(result)s of %(total)s projects"
) % {'result': result, 'total': total}
raise exceptions.CommandError(msg)
class ListShareGroupTypeAccess(command.Lister):
"""Get access list for share group type."""
_description = _("Get access list for share group type (Admin only).")
def get_parser(self, prog_name):
parser = super(ListShareGroupTypeAccess, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'share_group_type',
metavar="<share-group-type>",
help=_("Filter results by share group type name or ID.")
help=_("Filter results by share group type name or ID."),
)
return parser
@@ -93,11 +105,13 @@ class ListShareGroupTypeAccess(command.Lister):
share_client = self.app.client_manager.share
share_group_type = apiutils.find_resource(
share_client.share_group_types, parsed_args.share_group_type)
share_client.share_group_types, parsed_args.share_group_type
)
if share_group_type._info.get('is_public'):
raise exceptions.CommandError(
'Forbidden to get access list for public share group type.')
'Forbidden to get access list for public share group type.'
)
data = share_client.share_group_type_access.list(share_group_type)
@@ -109,22 +123,25 @@ class ListShareGroupTypeAccess(command.Lister):
class ShareGroupTypeAccessDeny(command.Command):
"""Deny a project to access a share group type."""
_description = _("Deny a project to access a share group type "
"(Admin only).")
_description = _(
"Deny a project to access a share group type (Admin only)."
)
def get_parser(self, prog_name):
parser = super(ShareGroupTypeAccessDeny, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'share_group_type',
metavar="<share-group-type>",
help=_("Share group type name or ID to deny access from")
help=_("Share group type name or ID to deny access from"),
)
parser.add_argument(
'projects',
metavar="<project>",
nargs="+",
help=_("Project Name(s) or ID(s) "
"to deny share group type access for.")
help=_(
"Project Name(s) or ID(s) to deny share group type access for."
),
)
identity_common.add_project_domain_option_to_parser(parser)
return parser
@@ -135,29 +152,36 @@ class ShareGroupTypeAccessDeny(command.Command):
result = 0
share_group_type = apiutils.find_resource(
share_client.share_group_types, parsed_args.share_group_type)
share_client.share_group_types, parsed_args.share_group_type
)
for project in parsed_args.projects:
try:
project_obj = identity_common.find_project(
identity_client,
project,
parsed_args.project_domain)
identity_client, project, parsed_args.project_domain
)
share_client.share_group_type_access.remove_project_access(
share_group_type, project_obj.id)
share_group_type, project_obj.id
)
except Exception as e:
result += 1
LOG.error(_(
"Failed to deny access for project '%(project)s' "
"to share group type with name or ID "
"'%(share_group_type)s': %(e)s"),
{'project': project,
'share_group_type': share_group_type, 'e': e})
LOG.error(
_(
"Failed to deny access for project '%(project)s' "
"to share group type with name or ID "
"'%(share_group_type)s': %(e)s"
),
{
'project': project,
'share_group_type': share_group_type,
'e': e,
},
)
if result > 0:
total = len(parsed_args.projects)
msg = (_("Failed to deny access to "
"%(result)s of %(total)s projects") % {'result': result,
'total': total})
msg = _(
"Failed to deny access to %(result)s of %(total)s projects"
) % {'result': result, 'total': total}
raise exceptions.CommandError(msg)
+113 -72
View File
@@ -29,32 +29,34 @@ ATTRIBUTES = [
'share_types',
'visibility',
'is_default',
'group_specs'
'group_specs',
]
class CreateShareGroupType(command.ShowOne):
"""Create new share group type."""
_description = _(
"Create new share group type")
_description = _("Create new share group type")
log = logging.getLogger(__name__ + ".CreateShareGroupType")
def get_parser(self, prog_name):
parser = super(CreateShareGroupType, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'name',
metavar="<name>",
default=None,
help=_('Share group type name')
help=_('Share group type name'),
)
parser.add_argument(
"share_types",
metavar="<share-types>",
nargs="+",
default=None,
help=_("List of share type names or IDs. Example:"
" my-share-type-1 my-share-type-2"),
help=_(
"List of share type names or IDs. Example:"
" my-share-type-1 my-share-type-2"
),
)
parser.add_argument(
"--group-specs",
@@ -62,44 +64,49 @@ class CreateShareGroupType(command.ShowOne):
nargs='*',
metavar='<key=value>',
default=None,
help=_("Share Group type extra specs by key and value."
" OPTIONAL: Default=None. Example:"
" --group-specs consistent_snapshot_support=host."),
help=_(
"Share Group type extra specs by key and value."
" OPTIONAL: Default=None. Example:"
" --group-specs consistent_snapshot_support=host."
),
)
parser.add_argument(
'--public',
metavar="<public>",
default=True,
help=_('Make type accessible to the public (default true).')
help=_('Make type accessible to the public (default true).'),
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
kwargs = {
'name': parsed_args.name
}
kwargs = {'name': parsed_args.name}
share_types_list = []
for share_type in parsed_args.share_types:
try:
share_type_obj = apiutils.find_resource(
share_client.share_types,
share_type)
share_client.share_types, share_type
)
share_types_list.append(share_type_obj.name)
except Exception as e:
msg = LOG.error(_("Failed to find the share type with "
"name or ID '%(share_type)s': %(e)s"),
{'share_type': share_type, 'e': e})
msg = LOG.error(
_(
"Failed to find the share type with "
"name or ID '%(share_type)s': %(e)s"
),
{'share_type': share_type, 'e': e},
)
raise exceptions.CommandError(msg)
kwargs['share_types'] = share_types_list
if parsed_args.public:
kwargs['is_public'] = strutils.bool_from_string(
parsed_args.public, default=True)
parsed_args.public, default=True
)
group_specs = {}
if parsed_args.group_specs:
@@ -113,25 +120,29 @@ class CreateShareGroupType(command.ShowOne):
formatter = parsed_args.formatter
formatted_group_type = utils.format_share_group_type(
share_group_type, formatter)
share_group_type, formatter
)
return (ATTRIBUTES, oscutils.get_dict_properties(
formatted_group_type, ATTRIBUTES))
return (
ATTRIBUTES,
oscutils.get_dict_properties(formatted_group_type, ATTRIBUTES),
)
class DeleteShareGroupType(command.Command):
"""Delete a share group type."""
_description = _("Delete a share group type")
log = logging.getLogger(__name__ + ".DeleteShareGroupType")
def get_parser(self, prog_name):
parser = super(DeleteShareGroupType, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'share_group_types',
metavar="<share-group-types>",
nargs="+",
help=_("Name or ID of the share group type(s) to delete")
help=_("Name or ID of the share group type(s) to delete"),
)
return parser
@@ -142,38 +153,45 @@ class DeleteShareGroupType(command.Command):
for share_group_type in parsed_args.share_group_types:
try:
share_group_type_obj = apiutils.find_resource(
share_client.share_group_types,
share_group_type)
share_client.share_group_types, share_group_type
)
share_client.share_group_types.delete(share_group_type_obj)
except Exception as e:
result += 1
LOG.error(_(
"Failed to delete share group type with "
"name or ID '%(share_group_type)s': %(e)s"),
{'share_group_type': share_group_type, 'e': e})
LOG.error(
_(
"Failed to delete share group type with "
"name or ID '%(share_group_type)s': %(e)s"
),
{'share_group_type': share_group_type, 'e': e},
)
if result > 0:
total = len(parsed_args.share_group_types)
msg = (_("%(result)s of %(total)s share group types failed "
"to delete.") % {'result': result, 'total': total})
msg = _(
"%(result)s of %(total)s share group types failed to delete."
) % {'result': result, 'total': total}
raise exceptions.CommandError(msg)
class ListShareGroupType(command.Lister):
"""List Share Group Types."""
_description = _("List share types")
log = logging.getLogger(__name__ + ".ListShareGroupType")
def get_parser(self, prog_name):
parser = super(ListShareGroupType, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'--all',
action='store_true',
default=False,
help=_('Display all share group types whether public or private. '
'Default=False. (Admin only)'),
help=_(
'Display all share group types whether public or private. '
'Default=False. (Admin only)'
),
)
parser.add_argument(
'--group-specs',
@@ -192,40 +210,44 @@ class ListShareGroupType(command.Lister):
if parsed_args.group_specs:
search_opts = {
'group_specs': utils.extract_group_specs(
extra_specs={},
specs_to_add=parsed_args.group_specs)
extra_specs={}, specs_to_add=parsed_args.group_specs
)
}
formatter = parsed_args.formatter
share_group_types = share_client.share_group_types.list(
search_opts=search_opts,
show_all=parsed_args.all)
search_opts=search_opts, show_all=parsed_args.all
)
formatted_types = []
for share_group_type in share_group_types:
formatted_types.append(utils.format_share_group_type(
share_group_type, formatter))
formatted_types.append(
utils.format_share_group_type(share_group_type, formatter)
)
column_headers = utils.format_column_headers(ATTRIBUTES)
values = (oscutils.get_dict_properties(
sgt, ATTRIBUTES) for sgt in formatted_types)
values = (
oscutils.get_dict_properties(sgt, ATTRIBUTES)
for sgt in formatted_types
)
return (column_headers, values)
class ShowShareGroupType(command.ShowOne):
"""Show Share Group Types."""
_description = _("Show share group types")
log = logging.getLogger(__name__ + ".ShowShareGroupType")
def get_parser(self, prog_name):
parser = super(ShowShareGroupType, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'share_group_type',
metavar="<share-group-type>",
help=_("Name or ID of the share group type to show")
help=_("Name or ID of the share group type to show"),
)
return parser
@@ -233,32 +255,38 @@ class ShowShareGroupType(command.ShowOne):
share_client = self.app.client_manager.share
share_group_type = apiutils.find_resource(
share_client.share_group_types, parsed_args.share_group_type)
share_client.share_group_types, parsed_args.share_group_type
)
share_group_type_obj = share_client.share_group_types.get(
share_group_type)
share_group_type
)
formatter = parsed_args.formatter
formatted_group_type = utils.format_share_group_type(
share_group_type_obj, formatter)
share_group_type_obj, formatter
)
return (ATTRIBUTES, oscutils.get_dict_properties(
formatted_group_type, ATTRIBUTES))
return (
ATTRIBUTES,
oscutils.get_dict_properties(formatted_group_type, ATTRIBUTES),
)
class SetShareGroupType(command.Command):
"""Set share type properties."""
_description = _("Set share group type properties")
log = logging.getLogger(__name__ + ".SetShareGroupType")
def get_parser(self, prog_name):
parser = super(SetShareGroupType, self).get_parser(prog_name)
parser = super().get_parser(prog_name)
parser.add_argument(
'share_group_type',
metavar="<share-group-type>",
help=_("Name or ID of the share group type to modify")
help=_("Name or ID of the share group type to modify"),
)
parser.add_argument(
"--group-specs",
@@ -266,9 +294,11 @@ class SetShareGroupType(command.Command):
nargs='*',
metavar='<key=value>',
default=None,
help=_("Extra specs key and value of share group type that will be"
" used for share type creation. OPTIONAL: Default=None."
" Example: --group-specs consistent-snapshot-support=True"),
help=_(
"Extra specs key and value of share group type that will be"
" used for share type creation. OPTIONAL: Default=None."
" Example: --group-specs consistent-snapshot-support=True"
),
)
return parser
@@ -277,12 +307,16 @@ class SetShareGroupType(command.Command):
try:
share_group_type_obj = apiutils.find_resource(
share_client.share_group_types, parsed_args.share_group_type)
share_client.share_group_types, parsed_args.share_group_type
)
except Exception as e:
msg = LOG.error(_(
"Failed to find the share group type with "
"name or ID '%(share_group_type)s': %(e)s"),
{'share_group_type': parsed_args.share_group_type, 'e': e})
msg = LOG.error(
_(
"Failed to find the share group type with "
"name or ID '%(share_group_type)s': %(e)s"
),
{'share_group_type': parsed_args.share_group_type, 'e': e},
)
raise exceptions.CommandError(msg)
kwargs = {}
@@ -291,27 +325,29 @@ class SetShareGroupType(command.Command):
if parsed_args.group_specs:
group_specs = utils.extract_group_specs(
extra_specs={},
specs_to_add=parsed_args.group_specs)
extra_specs={}, specs_to_add=parsed_args.group_specs
)
try:
share_group_type_obj.set_keys(group_specs)
except Exception as e:
raise exceptions.CommandError(
"Failed to set share group type key: %s" % e)
f"Failed to set share group type key: {e}"
)
class UnsetShareGroupType(command.Command):
"""Unset share group type extra specs."""