b097cec84f
OpenStack CLI option is added to support user friently export location. example command: $ openstack share create nfs 1 --name share_name --mount-point-name custom_export_path partially-implements: bp human-readable-export-locations Depends-On: I72ac7e24ddd4330d76cafd5e7f78bac2b0174883 Change-Id: If896065a74fc4ebabd09b677e5eb0329cecfaab9
784 lines
31 KiB
Python
784 lines
31 KiB
Python
# Copyright 2012 NetApp
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
"""Interface for shares extension."""
|
|
|
|
import collections
|
|
import ipaddress
|
|
from oslo_utils import uuidutils
|
|
import re
|
|
import string
|
|
|
|
from manilaclient import api_versions
|
|
from manilaclient import base
|
|
from manilaclient.common import constants
|
|
from manilaclient import exceptions
|
|
from manilaclient.v2 import share_instances
|
|
|
|
|
|
class Share(base.MetadataCapableResource):
|
|
|
|
"""A share is an extra block level storage to the OpenStack instances."""
|
|
def __repr__(self):
|
|
return "<Share: %s>" % self.id
|
|
|
|
def update(self, **kwargs):
|
|
"""Update this share."""
|
|
self.manager.update(self, **kwargs)
|
|
|
|
def unmanage(self, **kwargs):
|
|
"""Unmanage this share."""
|
|
self.manager.unmanage(self, **kwargs)
|
|
|
|
def migration_start(self, host, force_host_assisted_migration,
|
|
preserve_metadata, writable, nondisruptive,
|
|
preserve_snapshots, new_share_network_id=None,
|
|
new_share_type_id=None):
|
|
"""Migrate the share to a new host."""
|
|
self.manager.migration_start(self, host, force_host_assisted_migration,
|
|
preserve_metadata, writable,
|
|
nondisruptive, preserve_snapshots,
|
|
new_share_network_id, new_share_type_id)
|
|
|
|
def migration_complete(self):
|
|
"""Complete migration of a share."""
|
|
self.manager.migration_complete(self)
|
|
|
|
def migration_cancel(self):
|
|
"""Attempts to cancel migration of a share."""
|
|
self.manager.migration_cancel(self)
|
|
|
|
def migration_get_progress(self):
|
|
"""Obtain progress of migration of a share."""
|
|
return self.manager.migration_get_progress(self)
|
|
|
|
def reset_task_state(self, task_state):
|
|
"""Reset the task state of a given share."""
|
|
self.manager.reset_task_state(self, task_state)
|
|
|
|
def delete(self, share_group_id=None):
|
|
"""Delete this share."""
|
|
self.manager.delete(self, share_group_id=share_group_id)
|
|
|
|
def force_delete(self):
|
|
"""Delete the specified share ignoring its current state."""
|
|
self.manager.force_delete(self)
|
|
|
|
def allow(self, *args, **kwargs):
|
|
"""Allow access to a share."""
|
|
return self.manager.allow(self, *args, **kwargs)
|
|
|
|
def deny(self, id, **kwargs):
|
|
"""Deny access from IP to a share."""
|
|
return self.manager.deny(self, id, **kwargs)
|
|
|
|
def access_list(self):
|
|
"""Get access list from a share."""
|
|
return self.manager.access_list(self)
|
|
|
|
def reset_state(self, state):
|
|
"""Update the share with the provided state."""
|
|
self.manager.reset_state(self, state)
|
|
|
|
def extend(self, new_size, **kwargs):
|
|
"""Extend the size of the specified share."""
|
|
self.manager.extend(self, new_size, **kwargs)
|
|
|
|
def shrink(self, new_size):
|
|
"""Shrink the size of the specified share."""
|
|
self.manager.shrink(self, new_size)
|
|
|
|
def list_instances(self):
|
|
"""List instances of the specified share."""
|
|
self.manager.list_instances(self)
|
|
|
|
def revert_to_snapshot(self, snapshot):
|
|
"""Reverts a share (in place) to a snapshot."""
|
|
self.manager.revert_to_snapshot(self, snapshot)
|
|
|
|
def soft_delete(self):
|
|
"""Soft delete a share to recycle bin"""
|
|
self.manager.soft_delete(self)
|
|
|
|
def restore(self):
|
|
"""Restore a share from recycle bin"""
|
|
self.manager.restore(self)
|
|
|
|
|
|
class ShareManager(base.MetadataCapableManager):
|
|
"""Manage :class:`Share` resources."""
|
|
resource_class = Share
|
|
resource_path = '/shares'
|
|
|
|
def create(self, share_proto, size, snapshot_id=None, name=None,
|
|
description=None, metadata=None, share_network=None,
|
|
share_type=None, is_public=False, availability_zone=None,
|
|
share_group_id=None, scheduler_hints=None, return_raw=False,
|
|
mount_point_name=None):
|
|
"""Create a share.
|
|
|
|
:param share_proto: text - share protocol for new share available
|
|
values are NFS, CIFS, CephFS, GlusterFS, HDFS and MAPRFS.
|
|
:param size: int - size in GiB
|
|
:param snapshot_id: text - ID of the snapshot
|
|
:param name: text - name of new share
|
|
:param description: text - description of a share
|
|
:param metadata: dict - optional metadata to set on share creation
|
|
:param share_network: either instance of ShareNetwork or text with ID
|
|
:param share_type: either instance of ShareType or text with ID
|
|
:param is_public: bool, whether to set share as public or not.
|
|
:param share_group_id: text - ID of the share group to which the share
|
|
should belong
|
|
:param scheduler_hints: dict - hints for the scheduler to place share
|
|
on most appropriate host e.g. keys are same_host for affinity and
|
|
different_host for anti-affinity
|
|
:param mount_point_name: text - share human-readable mount point name.
|
|
This name will be reflected in export location once
|
|
share is created.
|
|
:rtype: :class:`Share`
|
|
"""
|
|
share_metadata = metadata if metadata is not None else dict()
|
|
scheduler_hints = (scheduler_hints if scheduler_hints is not None
|
|
else dict())
|
|
body = {
|
|
'size': size,
|
|
'snapshot_id': snapshot_id,
|
|
'name': name,
|
|
'description': description,
|
|
'metadata': share_metadata,
|
|
'share_proto': share_proto,
|
|
'share_network_id': base.getid(share_network),
|
|
'share_type': base.getid(share_type),
|
|
'is_public': is_public,
|
|
'availability_zone': availability_zone,
|
|
'scheduler_hints': scheduler_hints,
|
|
}
|
|
if share_group_id:
|
|
body['share_group_id'] = share_group_id
|
|
|
|
if mount_point_name:
|
|
body['mount_point_name'] = mount_point_name
|
|
|
|
return self._create('/shares', {'share': body}, 'share',
|
|
return_raw=return_raw)
|
|
|
|
@api_versions.wraps("2.29")
|
|
@api_versions.experimental_api
|
|
def migration_start(self, share, host, force_host_assisted_migration,
|
|
preserve_metadata, writable, nondisruptive,
|
|
preserve_snapshots, new_share_network_id=None,
|
|
new_share_type_id=None):
|
|
return self._action(
|
|
"migration_start", share, {
|
|
"host": host,
|
|
"force_host_assisted_migration": force_host_assisted_migration,
|
|
"preserve_metadata": preserve_metadata,
|
|
"preserve_snapshots": preserve_snapshots,
|
|
"writable": writable,
|
|
"nondisruptive": nondisruptive,
|
|
"new_share_network_id": new_share_network_id,
|
|
"new_share_type_id": new_share_type_id,
|
|
})
|
|
|
|
@api_versions.wraps("2.22")
|
|
@api_versions.experimental_api
|
|
def reset_task_state(self, share, task_state):
|
|
"""Update the provided share with the provided task state.
|
|
|
|
:param share: either share object or text with its ID.
|
|
:param task_state: text with new task state to set for share.
|
|
"""
|
|
return self._action('reset_task_state', share,
|
|
{"task_state": task_state})
|
|
|
|
@api_versions.wraps("2.22")
|
|
@api_versions.experimental_api
|
|
def migration_complete(self, share):
|
|
"""Completes migration for a given share.
|
|
|
|
:param share: The :class:'share' to complete migration
|
|
"""
|
|
return self._action('migration_complete', share)
|
|
|
|
@api_versions.wraps("2.22")
|
|
@api_versions.experimental_api
|
|
def migration_cancel(self, share):
|
|
"""Attempts to cancel migration for a given share.
|
|
|
|
:param share: The :class:'share' to cancel migration
|
|
"""
|
|
return self._action('migration_cancel', share)
|
|
|
|
@api_versions.wraps("2.22")
|
|
@api_versions.experimental_api
|
|
def migration_get_progress(self, share):
|
|
"""Obtains progress of share migration for a given share.
|
|
|
|
:param share: The :class:'share' to obtain migration progress
|
|
"""
|
|
return self._action('migration_get_progress', share)
|
|
|
|
def _do_manage(self, service_host, protocol, export_path,
|
|
driver_options=None, share_type=None,
|
|
name=None, description=None, is_public=None,
|
|
share_server_id=None, resource_path="/shares/manage"):
|
|
"""Manage some existing share.
|
|
|
|
:param service_host: text - host where manila share service is running
|
|
:param protocol: text - share protocol that is used
|
|
:param export_path: text - export path of share
|
|
:param driver_options: dict - custom set of key-values
|
|
:param share_type: text - share type that should be used for share
|
|
:param name: text - name of new share
|
|
:param description: - description for new share
|
|
:param is_public: - visibility for new share
|
|
:param share_server_id: text - id of share server associated with share
|
|
"""
|
|
driver_options = driver_options if driver_options else dict()
|
|
body = {
|
|
'service_host': service_host,
|
|
'share_type': share_type,
|
|
'protocol': protocol,
|
|
'export_path': export_path,
|
|
'driver_options': driver_options,
|
|
'name': name,
|
|
'description': description,
|
|
'share_server_id': share_server_id,
|
|
}
|
|
|
|
if is_public is not None:
|
|
body['is_public'] = is_public
|
|
|
|
return self._create(resource_path, {'share': body}, 'share')
|
|
|
|
@api_versions.wraps("1.0", "2.6")
|
|
def manage(self, service_host, protocol, export_path, driver_options=None,
|
|
share_type=None, name=None, description=None):
|
|
return self._do_manage(
|
|
service_host, protocol, export_path, driver_options=driver_options,
|
|
share_type=share_type, name=name, description=description,
|
|
resource_path="/os-share-manage")
|
|
|
|
@api_versions.wraps("2.7", "2.7") # noqa
|
|
def manage(self, service_host, protocol, export_path, driver_options=None, # noqa
|
|
share_type=None, name=None, description=None):
|
|
return self._do_manage(
|
|
service_host, protocol, export_path, driver_options=driver_options,
|
|
share_type=share_type, name=name, description=description,
|
|
resource_path="/shares/manage")
|
|
|
|
@api_versions.wraps("2.8", "2.48") # noqa
|
|
def manage(self, service_host, protocol, export_path, driver_options=None, # noqa
|
|
share_type=None, name=None, description=None, is_public=False):
|
|
return self._do_manage(
|
|
service_host, protocol, export_path, driver_options=driver_options,
|
|
share_type=share_type, name=name, description=description,
|
|
is_public=is_public, resource_path="/shares/manage")
|
|
|
|
@api_versions.wraps("2.49") # noqa
|
|
def manage(self, service_host, protocol, export_path, driver_options=None, # noqa
|
|
share_type=None, name=None, description=None, is_public=False,
|
|
share_server_id=None):
|
|
return self._do_manage(
|
|
service_host, protocol, export_path, driver_options=driver_options,
|
|
share_type=share_type, name=name, description=description,
|
|
is_public=is_public, share_server_id=share_server_id,
|
|
resource_path="/shares/manage")
|
|
|
|
@api_versions.wraps("1.0", "2.6")
|
|
def unmanage(self, share):
|
|
"""Unmanage a share.
|
|
|
|
:param share: either share object or text with its ID.
|
|
"""
|
|
return self.api.client.post(
|
|
"/os-share-unmanage/%s/unmanage" % base.getid(share))
|
|
|
|
@api_versions.wraps("2.7") # noqa
|
|
def unmanage(self, share): # noqa
|
|
"""Unmanage a share.
|
|
|
|
:param share: either share object or text with its ID.
|
|
"""
|
|
return self._action("unmanage", share)
|
|
|
|
@api_versions.wraps("2.27")
|
|
def revert_to_snapshot(self, share, snapshot):
|
|
"""Reverts a share (in place) to a snapshot.
|
|
|
|
The snapshot must be the most recent one known to manila.
|
|
:param share: either share object or text with its ID.
|
|
:param snapshot: either snapshot object or text with its ID.
|
|
"""
|
|
|
|
snapshot_id = base.getid(snapshot)
|
|
info = {'snapshot_id': snapshot_id}
|
|
return self._action('revert', share, info=info)
|
|
|
|
def get(self, share, return_raw=False):
|
|
"""Get a share.
|
|
|
|
:param share: either share object or text with its ID.
|
|
:rtype: :class:`Share`
|
|
"""
|
|
share_id = base.getid(share)
|
|
return self._get("/shares/%s" % share_id, "share",
|
|
return_raw=return_raw)
|
|
|
|
def update(self, share, **kwargs):
|
|
"""Updates a share.
|
|
|
|
:param share: either share object or text with its ID.
|
|
:rtype: :class:`Share`
|
|
"""
|
|
if not kwargs:
|
|
return
|
|
|
|
body = {'share': kwargs, }
|
|
share_id = base.getid(share)
|
|
return self._update("/shares/%s" % share_id, body)
|
|
|
|
@api_versions.wraps("1.0", "2.34")
|
|
def list(self, detailed=True, search_opts=None,
|
|
sort_key=None, sort_dir=None, return_raw=False):
|
|
"""Get a list of all shares."""
|
|
search_opts = search_opts or {}
|
|
search_opts.pop("export_location", None)
|
|
search_opts.pop("is_soft_deleted", None)
|
|
return self.do_list(detailed=detailed, search_opts=search_opts,
|
|
sort_key=sort_key, sort_dir=sort_dir,
|
|
return_raw=return_raw)
|
|
|
|
@api_versions.wraps("2.35", "2.68") # noqa
|
|
def list(self, detailed=True, search_opts=None, # noqa
|
|
sort_key=None, sort_dir=None, return_raw=False):
|
|
"""Get a list of all shares."""
|
|
if search_opts is None:
|
|
search_opts = {}
|
|
search_opts.pop("is_soft_deleted", None)
|
|
return self.do_list(detailed=detailed, search_opts=search_opts,
|
|
sort_key=sort_key, sort_dir=sort_dir,
|
|
return_raw=return_raw)
|
|
|
|
@api_versions.wraps("2.69") # noqa
|
|
def list(self, detailed=True, search_opts=None, # noqa
|
|
sort_key=None, sort_dir=None, return_raw=False):
|
|
"""Get a list of all shares."""
|
|
return self.do_list(detailed=detailed, search_opts=search_opts,
|
|
sort_key=sort_key, sort_dir=sort_dir,
|
|
return_raw=return_raw)
|
|
|
|
def do_list(self, detailed=True, search_opts=None,
|
|
sort_key=None, sort_dir=None, return_raw=False):
|
|
"""Get a list of all shares.
|
|
|
|
:param detailed: Whether to return detailed share info or not.
|
|
:param search_opts: dict with search options to filter out shares.
|
|
available keys are below (('name1', 'name2', ...), 'type'):
|
|
- ('all_tenants', int)
|
|
- ('is_public', bool)
|
|
- ('metadata', dict)
|
|
- ('extra_specs', dict)
|
|
- ('limit', int)
|
|
- ('offset', int)
|
|
- ('name', text)
|
|
- ('status', text)
|
|
- ('host', text)
|
|
- ('share_server_id', text)
|
|
- (('share_network_id', 'share_network'), text)
|
|
- (('share_type_id', 'share_type'), text)
|
|
- (('snapshot_id', 'snapshot'), text)
|
|
- ('is_soft_deleted', bool)
|
|
Note, that member context will have restricted set of
|
|
available search opts. For admin context filtering also available
|
|
by each share attr from its Model. So, this list is not full for
|
|
admin context.
|
|
:param sort_key: Key to be sorted (i.e. 'created_at' or 'status').
|
|
:param sort_dir: Sort direction, should be 'desc' or 'asc'.
|
|
:rtype: list of :class:`Share`
|
|
"""
|
|
if search_opts is None:
|
|
search_opts = {}
|
|
|
|
if sort_key is not None:
|
|
if sort_key in constants.SHARE_SORT_KEY_VALUES:
|
|
search_opts['sort_key'] = sort_key
|
|
# NOTE(vponomaryov): Replace aliases with appropriate keys
|
|
if sort_key == 'share_type':
|
|
search_opts['sort_key'] = 'share_type_id'
|
|
elif sort_key == 'snapshot':
|
|
search_opts['sort_key'] = 'snapshot_id'
|
|
elif sort_key == 'share_network':
|
|
search_opts['sort_key'] = 'share_network_id'
|
|
elif sort_key == 'availability_zone':
|
|
search_opts['sort_key'] = 'availability_zone_id'
|
|
else:
|
|
raise ValueError('sort_key must be one of the following: %s.'
|
|
% ', '.join(constants.SHARE_SORT_KEY_VALUES))
|
|
|
|
if sort_dir is not None:
|
|
if sort_dir in constants.SORT_DIR_VALUES:
|
|
search_opts['sort_dir'] = sort_dir
|
|
else:
|
|
raise ValueError('sort_dir must be one of the following: %s.'
|
|
% ', '.join(constants.SORT_DIR_VALUES))
|
|
|
|
if 'is_public' not in search_opts:
|
|
search_opts['is_public'] = True
|
|
|
|
export_location = search_opts.pop('export_location', None)
|
|
if export_location:
|
|
if uuidutils.is_uuid_like(export_location):
|
|
search_opts['export_location_id'] = export_location
|
|
else:
|
|
search_opts['export_location_path'] = export_location
|
|
|
|
query_string = self._build_query_string(search_opts)
|
|
|
|
if detailed:
|
|
path = "/shares/detail%s" % (query_string,)
|
|
else:
|
|
path = "/shares%s" % (query_string,)
|
|
|
|
return self._list(path, 'shares', return_raw=return_raw)
|
|
|
|
def delete(self, share, share_group_id=None):
|
|
"""Delete a share.
|
|
|
|
:param share: either share object or text with its ID.
|
|
:param share_group_id: text - ID of the share group to which the share
|
|
belongs
|
|
"""
|
|
url = "/shares/%s" % base.getid(share)
|
|
if share_group_id:
|
|
url += "?share_group_id=%s" % share_group_id
|
|
self._delete(url)
|
|
|
|
def _do_force_delete(self, share, action_name):
|
|
"""Delete a share forcibly - share status will be avoided.
|
|
|
|
:param share: either share object or text with its ID.
|
|
"""
|
|
return self._action(action_name, share)
|
|
|
|
@api_versions.wraps("1.0", "2.6")
|
|
def force_delete(self, share):
|
|
return self._do_force_delete(share, "os-force_delete")
|
|
|
|
@api_versions.wraps("2.7") # noqa
|
|
def force_delete(self, share): # noqa
|
|
return self._do_force_delete(share, "force_delete")
|
|
|
|
@api_versions.wraps("2.69")
|
|
def soft_delete(self, share):
|
|
"""Soft delete a share - share will go to recycle bin.
|
|
|
|
:param share: either share object or text with its ID.
|
|
"""
|
|
return self._action("soft_delete", share)
|
|
|
|
@api_versions.wraps("2.69")
|
|
def restore(self, share):
|
|
"""Restore a share - share will restore from recycle bin.
|
|
|
|
:param share: either share object or text with its ID.
|
|
"""
|
|
return self._action("restore", share)
|
|
|
|
@staticmethod
|
|
def _validate_common_name(access):
|
|
if len(access) == 0 or len(access) > 64:
|
|
exc_str = ('Invalid CN (common name). Must be 1-64 chars long.')
|
|
raise exceptions.CommandError(exc_str)
|
|
|
|
'''
|
|
for the reference specification for AD usernames, reference below links:
|
|
|
|
1:https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/
|
|
windows-server-2008-R2-and-2008/cc733146(v=ws.11)
|
|
2:https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/
|
|
windows-server-2000/bb726984(v=technet.10)
|
|
'''
|
|
@staticmethod
|
|
def _validate_username(access):
|
|
sole_periods_spaces_re = r'[\s|\.]+$'
|
|
valid_username_re = r'.[^\"\/\\\[\]\:\;\|\=\,\+\*\?\<\>]{3,254}$'
|
|
username = access
|
|
|
|
if re.match(sole_periods_spaces_re, username):
|
|
exc_str = ('Invalid user or group name,cannot consist solely '
|
|
'of periods or spaces.')
|
|
raise exceptions.CommandError(exc_str)
|
|
|
|
if not re.match(valid_username_re, username):
|
|
exc_str = ('Invalid user or group name. Must be 4-255 characters '
|
|
'and consist of alphanumeric characters and '
|
|
'exclude special characters "/\\[]:;|=,+*?<>')
|
|
raise exceptions.CommandError(exc_str)
|
|
|
|
@staticmethod
|
|
def _validate_cephx_id(cephx_id):
|
|
if not cephx_id:
|
|
raise exceptions.CommandError(
|
|
'Ceph IDs may not be empty.')
|
|
|
|
# This restriction may be lifted in Ceph in the future:
|
|
# http://tracker.ceph.com/issues/14626
|
|
if not set(cephx_id) <= set(string.printable):
|
|
raise exceptions.CommandError(
|
|
'Ceph IDs must consist of ASCII printable characters.')
|
|
|
|
# Periods are technically permitted, but we restrict them here
|
|
# to avoid confusion where users are unsure whether they should
|
|
# include the "client." prefix: otherwise they could accidentally
|
|
# create "client.client.foobar".
|
|
if '.' in cephx_id:
|
|
raise exceptions.CommandError(
|
|
'Ceph IDs may not contain periods.')
|
|
|
|
def _validate_access(self, access_type, access, valid_access_types=None,
|
|
enable_ipv6=False):
|
|
if not valid_access_types:
|
|
valid_access_types = ('ip', 'user', 'cert')
|
|
|
|
if access_type in valid_access_types:
|
|
if access_type == 'ip':
|
|
try:
|
|
if enable_ipv6:
|
|
ipaddress.ip_network(str(access))
|
|
else:
|
|
ipaddress.IPv4Network(str(access))
|
|
except ValueError as error:
|
|
raise exceptions.CommandError(str(error))
|
|
elif access_type == 'user':
|
|
self._validate_username(access)
|
|
elif access_type == 'cert':
|
|
# 'access' is used as the certificate's CN (common name)
|
|
# to which access is allowed or denied by the backend.
|
|
# The standard allows for just about any string in the
|
|
# common name. The meaning of a string depends on its
|
|
# interpretation and is limited to 64 characters.
|
|
self._validate_common_name(access.strip())
|
|
elif access_type == 'cephx':
|
|
self._validate_cephx_id(access.strip())
|
|
else:
|
|
msg = ('Only following access types are supported: %s' %
|
|
', '.join(valid_access_types))
|
|
raise exceptions.CommandError(msg)
|
|
|
|
def _do_allow(self, share, access_type, access, access_level, action_name,
|
|
metadata=None, lock_visibility=False,
|
|
lock_deletion=False, lock_reason=None):
|
|
"""Allow access to a share.
|
|
|
|
:param share: either share object or text with its ID.
|
|
:param access_type: string that represents access type ('ip','domain')
|
|
:param access: string that represents access ('127.0.0.1')
|
|
:param access_level: string that represents access level ('rw', 'ro')
|
|
:param metadata: A dict of key/value pairs to be set
|
|
"""
|
|
access_params = {
|
|
'access_type': access_type,
|
|
'access_to': access,
|
|
}
|
|
if access_level:
|
|
access_params['access_level'] = access_level
|
|
if metadata:
|
|
access_params['metadata'] = metadata
|
|
if lock_visibility:
|
|
access_params['lock_visibility'] = lock_visibility
|
|
if lock_deletion:
|
|
access_params['lock_deletion'] = lock_deletion
|
|
if lock_reason:
|
|
access_params['lock_reason'] = lock_reason
|
|
access = self._action(action_name, share,
|
|
access_params)[1]["access"]
|
|
return access
|
|
|
|
@api_versions.wraps("1.0", "2.6")
|
|
def allow(self, share, access_type, access, access_level, metadata=None):
|
|
self._validate_access(access_type, access)
|
|
return self._do_allow(
|
|
share, access_type, access, access_level, "os-allow_access")
|
|
|
|
@api_versions.wraps("2.7", "2.12") # noqa
|
|
def allow(self, share, access_type, access, access_level, metadata=None): # noqa
|
|
self._validate_access(access_type, access)
|
|
return self._do_allow(
|
|
share, access_type, access, access_level, "allow_access")
|
|
|
|
@api_versions.wraps("2.13", "2.37") # noqa
|
|
def allow(self, share, access_type, access, access_level, metadata=None): # noqa
|
|
valid_access_types = ('ip', 'user', 'cert', 'cephx')
|
|
self._validate_access(access_type, access, valid_access_types)
|
|
return self._do_allow(
|
|
share, access_type, access, access_level, "allow_access")
|
|
|
|
@api_versions.wraps("2.38", "2.44") # noqa
|
|
def allow(self, share, access_type, access, access_level, metadata=None): # noqa
|
|
valid_access_types = ('ip', 'user', 'cert', 'cephx')
|
|
self._validate_access(access_type, access, valid_access_types,
|
|
enable_ipv6=True)
|
|
return self._do_allow(
|
|
share, access_type, access, access_level, "allow_access")
|
|
|
|
@api_versions.wraps("2.45", "2.81") # noqa
|
|
def allow(self, share, access_type, access, access_level, metadata=None): # noqa
|
|
valid_access_types = ('ip', 'user', 'cert', 'cephx')
|
|
self._validate_access(access_type, access, valid_access_types,
|
|
enable_ipv6=True)
|
|
return self._do_allow(
|
|
share, access_type, access, access_level, "allow_access",
|
|
metadata=metadata)
|
|
|
|
@api_versions.wraps("2.82") # noqa
|
|
def allow(self, share, access_type, access, access_level, # pylint: disable=function-redefined # noqa F811
|
|
metadata=None, lock_visibility=False, lock_deletion=False,
|
|
lock_reason=None):
|
|
valid_access_types = ('ip', 'user', 'cert', 'cephx')
|
|
self._validate_access(access_type, access, valid_access_types,
|
|
enable_ipv6=True)
|
|
return self._do_allow(
|
|
share, access_type, access, access_level, "allow_access",
|
|
metadata=metadata, lock_visibility=lock_visibility,
|
|
lock_deletion=lock_deletion, lock_reason=lock_reason)
|
|
|
|
def _do_deny(self, share, access_id, action_name, unrestrict=False):
|
|
"""Deny access to a share.
|
|
|
|
:param share: either share object or text with its ID.
|
|
:param access_id: ID of share access rule
|
|
"""
|
|
body = {
|
|
"access_id": access_id,
|
|
}
|
|
if unrestrict:
|
|
body['unrestrict'] = True
|
|
return self._action(action_name, share, body)
|
|
|
|
@api_versions.wraps("1.0", "2.6")
|
|
def deny(self, share, access_id):
|
|
return self._do_deny(share, access_id, "os-deny_access")
|
|
|
|
@api_versions.wraps("2.7", "2.81") # noqa
|
|
def deny(self, share, access_id): # noqa
|
|
return self._do_deny(share, access_id, "deny_access")
|
|
|
|
@api_versions.wraps("2.82") # noqa
|
|
def deny(self, share, access_id, unrestrict=False): # noqa
|
|
return self._do_deny(share, access_id, "deny_access",
|
|
unrestrict=unrestrict)
|
|
|
|
def _do_access_list(self, share, action_name):
|
|
"""Get access list to a share.
|
|
|
|
:param share: either share object or text with its ID.
|
|
"""
|
|
access_list = self._action(action_name, share)[1]["access_list"]
|
|
if access_list:
|
|
t = collections.namedtuple('Access', list(access_list[0]))
|
|
return [t(*value.values()) for value in access_list]
|
|
else:
|
|
return []
|
|
|
|
@api_versions.wraps("1.0", "2.6")
|
|
def access_list(self, share):
|
|
return self._do_access_list(share, "os-access_list")
|
|
|
|
@api_versions.wraps("2.7", "2.44") # noqa
|
|
def access_list(self, share): # noqa
|
|
return self._do_access_list(share, "access_list")
|
|
|
|
def _action(self, action, share, info=None, **kwargs):
|
|
"""Perform a share 'action'.
|
|
|
|
:param action: text with action name.
|
|
:param share: either share object or text with its ID.
|
|
:param info: dict with data for specified 'action'.
|
|
:param kwargs: dict with data to be provided for action hooks.
|
|
"""
|
|
body = {action: info}
|
|
self.run_hooks('modify_body_for_action', body, **kwargs)
|
|
url = '/shares/%s/action' % base.getid(share)
|
|
return self.api.client.post(url, body=body)
|
|
|
|
def _do_reset_state(self, share, state, action_name):
|
|
"""Update the provided share with the provided state.
|
|
|
|
:param share: either share object or text with its ID.
|
|
:param state: text with new state to set for share.
|
|
"""
|
|
return self._action(action_name, share, {"status": state})
|
|
|
|
@api_versions.wraps("1.0", "2.6")
|
|
def reset_state(self, share, state):
|
|
return self._do_reset_state(share, state, "os-reset_status")
|
|
|
|
@api_versions.wraps("2.7") # noqa
|
|
def reset_state(self, share, state): # noqa
|
|
return self._do_reset_state(share, state, "reset_status")
|
|
|
|
def _do_extend(self, share, new_size, action_name, force=False):
|
|
"""Extend the size of the specified share.
|
|
|
|
:param share: either share object or text with its ID.
|
|
:param new_size: The desired size to extend share to.
|
|
:param force: if set to True, the scheduler's capacity decisions are
|
|
not accounted for. Setting this parameter to True does
|
|
not mean that the request will always succeed.
|
|
"""
|
|
req_body = {"new_size": new_size}
|
|
if force:
|
|
req_body['force'] = "true"
|
|
return self._action(action_name, share, req_body)
|
|
|
|
@api_versions.wraps("1.0", "2.6")
|
|
def extend(self, share, new_size):
|
|
return self._do_extend(share, new_size, "os-extend")
|
|
|
|
@api_versions.wraps("2.7", "2.63") # noqa
|
|
def extend(self, share, new_size): # noqa
|
|
return self._do_extend(share, new_size, "extend")
|
|
|
|
@api_versions.wraps("2.64") # noqa
|
|
def extend(self, share, new_size, force=False): # noqa
|
|
return self._do_extend(share, new_size, "extend", force=force)
|
|
|
|
def _do_shrink(self, share, new_size, action_name):
|
|
"""Shrink the size of the specified share.
|
|
|
|
:param share: either share object or text with its ID.
|
|
:param new_size: The desired size to shrink share to.
|
|
"""
|
|
return self._action(action_name, share, {'new_size': new_size})
|
|
|
|
@api_versions.wraps("1.0", "2.6")
|
|
def shrink(self, share, new_size):
|
|
return self._do_shrink(share, new_size, "os-shrink")
|
|
|
|
@api_versions.wraps("2.7") # noqa
|
|
def shrink(self, share, new_size): # noqa
|
|
return self._do_shrink(share, new_size, "shrink")
|
|
|
|
def list_instances(self, share):
|
|
"""List instances of the specified share.
|
|
|
|
:param share: either share object or text with its ID.
|
|
"""
|
|
return self._list(
|
|
'/shares/%s/instances' % base.getid(share),
|
|
'share_instances',
|
|
manager=share_instances.ShareInstanceManager(self)
|
|
)
|