Add ruff
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:
+12
-6
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
),
|
||||
)
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
@@ -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}")
|
||||
)
|
||||
|
||||
+552
-408
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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},
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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."""
|
||||