Browse Source

Manila Share Groups

Remove the experimental consistency group APIs and
replace them with the experimental Share Group APIs.

DocImpact
APIImpact
Partially-implements-blueprint: manila-share-groups

Change-Id: I79a80a62ae4e0015d6161edc2b93fd1f9ba69537
changes/93/335093/57
Alex Meade 5 years ago
committed by Valeriy Ponomaryov
parent
commit
d25f101ab4
  1. 4
      contrib/ci/post_test_hook.sh
  2. 47
      etc/manila/policy.json
  3. 12
      manila/api/openstack/api_version_request.py
  4. 4
      manila/api/openstack/rest_api_version_history.rst
  5. 36
      manila/api/v1/shares.py
  6. 94
      manila/api/v2/router.py
  7. 195
      manila/api/v2/share_group_snapshots.py
  8. 133
      manila/api/v2/share_group_type_specs.py
  9. 227
      manila/api/v2/share_group_types.py
  10. 251
      manila/api/v2/share_groups.py
  11. 10
      manila/api/v2/share_networks.py
  12. 20
      manila/api/v2/shares.py
  13. 94
      manila/api/views/share_group_snapshots.py
  14. 39
      manila/api/views/share_group_types.py
  15. 88
      manila/api/views/share_groups.py
  16. 16
      manila/api/views/shares.py
  17. 2
      manila/common/config.py
  18. 272
      manila/db/api.py
  19. 224
      manila/db/migrations/alembic/versions/03da71c0e321_convert_cgs_to_share_groups.py
  20. 146
      manila/db/migrations/alembic/versions/e1949a93157a_add_share_group_types_table.py
  21. 855
      manila/db/sqlalchemy/api.py
  22. 193
      manila/db/sqlalchemy/models.py
  23. 72
      manila/exception.py
  24. 16
      manila/scheduler/drivers/base.py
  25. 97
      manila/scheduler/drivers/filter.py
  26. 2
      manila/scheduler/filters/capacity.py
  27. 54
      manila/scheduler/filters/consistency_group.py
  28. 8
      manila/scheduler/host_manager.py
  29. 33
      manila/scheduler/manager.py
  30. 27
      manila/scheduler/rpcapi.py
  31. 1
      manila/scheduler/utils.py
  32. 124
      manila/share/api.py
  33. 301
      manila/share/driver.py
  34. 23
      manila/share/drivers/cephfs/cephfs_native.py
  35. 1
      manila/share/drivers/generic.py
  36. 1
      manila/share/drivers/lvm.py
  37. 3
      manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py
  38. 1
      manila/share/drivers/zfsonlinux/driver.py
  39. 282
      manila/share/manager.py
  40. 46
      manila/share/rpcapi.py
  41. 295
      manila/share_group/api.py
  42. 174
      manila/share_group/share_group_types.py
  43. 2
      manila/tests/api/contrib/stubs.py
  44. 11
      manila/tests/api/openstack/test_api_version_request.py
  45. 78
      manila/tests/api/v1/test_shares.py
  46. 557
      manila/tests/api/v2/test_share_group_snapshots.py
  47. 354
      manila/tests/api/v2/test_share_group_type_specs.py
  48. 592
      manila/tests/api/v2/test_share_group_types.py
  49. 742
      manila/tests/api/v2/test_share_groups.py
  50. 4
      manila/tests/api/v2/test_share_networks.py
  51. 137
      manila/tests/api/v2/test_shares.py
  52. 178
      manila/tests/db/migrations/alembic/migrations_data_checks.py
  53. 409
      manila/tests/db/sqlalchemy/test_api.py
  54. 25
      manila/tests/db_utils.py
  55. 4
      manila/tests/fake_driver.py
  56. 8
      manila/tests/fake_share.py
  57. 46
      manila/tests/policy.json
  58. 70
      manila/tests/scheduler/drivers/test_filter.py
  59. 13
      manila/tests/scheduler/fakes.py
  60. 11
      manila/tests/scheduler/test_host_manager.py
  61. 51
      manila/tests/scheduler/test_manager.py
  62. 8
      manila/tests/scheduler/test_rpcapi.py
  63. 22
      manila/tests/share/drivers/cephfs/test_cephfs_native.py
  64. 1
      manila/tests/share/drivers/dell_emc/test_driver.py
  65. 1
      manila/tests/share/drivers/glusterfs/test_glusterfs_native.py
  66. 3
      manila/tests/share/drivers/hpe/test_hpe_3par_driver.py
  67. 1
      manila/tests/share/drivers/huawei/test_huawei_nas.py
  68. 4
      manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py
  69. 2
      manila/tests/share/drivers/netapp/dataontap/fakes.py
  70. 73
      manila/tests/share/drivers/test_generic.py
  71. 1
      manila/tests/share/drivers/test_lvm.py
  72. 2
      manila/tests/share/drivers/zfsonlinux/test_driver.py
  73. 39
      manila/tests/share/test_api.py
  74. 333
      manila/tests/share/test_driver.py
  75. 656
      manila/tests/share/test_manager.py
  76. 75
      manila/tests/share/test_rpcapi.py
  77. 1691
      manila/tests/share_group/test_api.py
  78. 156
      manila/tests/share_group/test_share_group_types.py
  79. 4
      manila_tempest_tests/config.py
  80. 16
      manila_tempest_tests/tests/api/test_shares_actions.py
  81. 1
      setup.cfg

4
contrib/ci/post_test_hook.sh

@ -70,7 +70,7 @@ RUN_MANILA_QUOTA_TESTS=${RUN_MANILA_QUOTA_TESTS:-True}
RUN_MANILA_SHRINK_TESTS=${RUN_MANILA_SHRINK_TESTS:-True}
RUN_MANILA_SNAPSHOT_TESTS=${RUN_MANILA_SNAPSHOT_TESTS:-True}
RUN_MANILA_REVERT_TO_SNAPSHOT_TESTS=${RUN_MANILA_REVERT_TO_SNAPSHOT_TESTS:-False}
RUN_MANILA_CG_TESTS=${RUN_MANILA_CG_TESTS:-True}
RUN_MANILA_CG_TESTS=${RUN_MANILA_CG_TESTS:-False}
RUN_MANILA_MANAGE_TESTS=${RUN_MANILA_MANAGE_TESTS:-True}
RUN_MANILA_MANAGE_SNAPSHOT_TESTS=${RUN_MANILA_MANAGE_SNAPSHOT_TESTS:-False}
RUN_MANILA_REPLICATION_TESTS=${RUN_MANILA_REPLICATION_TESTS:-False}
@ -207,7 +207,7 @@ elif [[ "$DRIVER" == "zfsonlinux" ]]; then
iniset $TEMPEST_CONFIG share capability_snapshot_support True
elif [[ "$DRIVER" == "dummy" ]]; then
MANILA_TEMPEST_CONCURRENCY=24
RUN_MANILA_CG_TESTS=True
RUN_MANILA_CG_TESTS=False
RUN_MANILA_MANAGE_TESTS=False
RUN_MANILA_DRIVER_ASSISTED_MIGRATION_TESTS=True
RUN_MANILA_REVERT_TO_SNAPSHOT_TESTS=True

47
etc/manila/policy.json

@ -108,21 +108,21 @@
"scheduler_stats:pools:index": "rule:admin_api",
"scheduler_stats:pools:detail": "rule:admin_api",
"consistency_group:create" : "rule:default",
"consistency_group:delete": "rule:default",
"consistency_group:update": "rule:default",
"consistency_group:get": "rule:default",
"consistency_group:get_all": "rule:default",
"consistency_group:force_delete": "rule:admin_api",
"consistency_group:reset_status": "rule:admin_api",
"cgsnapshot:force_delete": "rule:admin_api",
"cgsnapshot:reset_status": "rule:admin_api",
"cgsnapshot:create" : "rule:default",
"cgsnapshot:update" : "rule:default",
"cgsnapshot:delete": "rule:default",
"cgsnapshot:get_cgsnapshot": "rule:default",
"cgsnapshot:get_all": "rule:default",
"share_group:create" : "rule:default",
"share_group:delete": "rule:default",
"share_group:update": "rule:default",
"share_group:get": "rule:default",
"share_group:get_all": "rule:default",
"share_group:force_delete": "rule:admin_api",
"share_group:reset_status": "rule:admin_api",
"share_group_snapshot:create" : "rule:default",
"share_group_snapshot:delete": "rule:default",
"share_group_snapshot:update" : "rule:default",
"share_group_snapshot:get": "rule:default",
"share_group_snapshot:get_all": "rule:default",
"share_group_snapshot:force_delete": "rule:admin_api",
"share_group_snapshot:reset_status": "rule:admin_api",
"share_replica:get_all": "rule:default",
"share_replica:show": "rule:default",
@ -132,5 +132,20 @@
"share_replica:resync": "rule:admin_api",
"share_replica:reset_status": "rule:admin_api",
"share_replica:force_delete": "rule:admin_api",
"share_replica:reset_replica_state": "rule:admin_api"
"share_replica:reset_replica_state": "rule:admin_api",
"share_group_type:index": "rule:default",
"share_group_type:show": "rule:default",
"share_group_type:default": "rule:default",
"share_group_type:create": "rule:admin_api",
"share_group_type:delete": "rule:admin_api",
"share_group_type:add_project_access": "rule:admin_api",
"share_group_type:list_project_access": "rule:admin_api",
"share_group_type:remove_project_access": "rule:admin_api",
"share_group_types_spec:create": "rule:admin_api",
"share_group_types_spec:update": "rule:admin_api",
"share_group_types_spec:show": "rule:admin_api",
"share_group_types_spec:index": "rule:admin_api",
"share_group_types_spec:delete": "rule:admin_api"
}

12
manila/api/openstack/api_version_request.py

@ -96,13 +96,14 @@ REST_API_VERSION_HISTORY = """
migration_start APIs prior to this microversion are now
unsupported.
* 2.30 - Added cast_rules_to_readonly field to share_instances.
* 2.31 - Convert consistency groups to share groups.
"""
# The minimum and maximum versions of the API supported
# The default api version request is defined to be the
# minimum version of the API supported.
_MIN_API_VERSION = "2.0"
_MAX_API_VERSION = "2.30"
_MAX_API_VERSION = "2.31"
DEFAULT_API_VERSION = _MIN_API_VERSION
@ -142,8 +143,13 @@ class APIVersionRequest(utils.ComparableMixin):
def __str__(self):
"""Debug/Logging representation of object."""
return ("API Version Request Major: %(major)s, Minor: %(minor)s"
% {'major': self._ver_major, 'minor': self._ver_minor})
params = {
'major': self._ver_major,
'minor': self._ver_minor,
'experimental': self._experimental,
}
return ("API Version Request Major: %(major)s, Minor: %(minor)s, "
"Experimental: %(experimental)s" % params)
def is_null(self):
return self._ver_major is None and self._ver_minor is None

4
manila/api/openstack/rest_api_version_history.rst

@ -183,3 +183,7 @@ user documentation.
2.30
----
Added cast_rules_to_readonly field to share_instances.
2.31
----
Convert consistency groups to share groups.

36
manila/api/v1/shares.py

@ -75,19 +75,19 @@ class ShareMixin(object):
try:
share = self.share_api.get(context, id)
# NOTE(ameade): If the share is in a consistency group, we require
# it's id be specified as a param.
if share.get('consistency_group_id'):
consistency_group_id = req.params.get('consistency_group_id')
if (share.get('consistency_group_id') and
not consistency_group_id):
msg = _("Must provide 'consistency_group_id' as a request "
"parameter when deleting a share in a consistency "
"group.")
# NOTE(ameade): If the share is in a share group, we require its
# id be specified as a param.
sg_id_key = 'share_group_id'
if share.get(sg_id_key):
share_group_id = req.params.get(sg_id_key)
if not share_group_id:
msg = _("Must provide '%s' as a request "
"parameter when deleting a share in a share "
"group.") % sg_id_key
raise exc.HTTPBadRequest(explanation=msg)
elif consistency_group_id != share.get('consistency_group_id'):
msg = _("The specified 'consistency_group_id' does not "
"match the consistency group id of the share.")
elif share_group_id != share.get(sg_id_key):
msg = _("The specified '%s' does not match "
"the share group id of the share.") % sg_id_key
raise exc.HTTPBadRequest(explanation=msg)
self.share_api.delete(context, share)
@ -161,7 +161,7 @@ class ShareMixin(object):
'display_name', 'status', 'share_server_id', 'volume_type_id',
'share_type_id', 'snapshot_id', 'host', 'share_network_id',
'is_public', 'metadata', 'extra_specs', 'sort_key', 'sort_dir',
'consistency_group_id', 'cgsnapshot_id'
'share_group_id', 'share_group_snapshot_id'
)
def update(self, req, id, body):
@ -192,8 +192,8 @@ class ShareMixin(object):
return self._view_builder.detail(req, share)
def create(self, req, body):
# Remove consistency group attributes
body.get('share', {}).pop('consistency_group_id', None)
# Remove share group attributes
body.get('share', {}).pop('share_group_id', None)
share = self._create(req, body)
return share
@ -237,7 +237,7 @@ class ShareMixin(object):
'availability_zone': availability_zone,
'metadata': share.get('metadata'),
'is_public': share.get('is_public', False),
'consistency_group_id': share.get('consistency_group_id')
'share_group_id': share.get('share_group_id')
}
snapshot_id = share.get('snapshot_id')
@ -310,10 +310,10 @@ class ShareMixin(object):
share_type = def_share_type
# Only use in create share feature. Create share from snapshot
# and create share with consistency group features not
# and create share with share group features not
# need this check.
if (not share_network_id and not snapshot
and not share.get('consistency_group_id')
and not share.get('share_group_id')
and share_type and share_type.get('extra_specs')
and (strutils.bool_from_string(share_type.get('extra_specs').
get('driver_handles_share_servers')))):

94
manila/api/v2/router.py

@ -36,6 +36,8 @@ from manila.api.v2 import quota_sets
from manila.api.v2 import services
from manila.api.v2 import share_export_locations
from manila.api.v2 import share_group_snapshots
from manila.api.v2 import share_group_type_specs
from manila.api.v2 import share_group_types
from manila.api.v2 import share_groups
from manila.api.v2 import share_instance_export_locations
from manila.api.v2 import share_instances
@ -280,21 +282,83 @@ class APIRouter(manila.api.openstack.APIRouter):
action="pools_detail",
conditions={"method": ["GET"]})
self.resources["consistency-groups"] = share_groups.create_resource()
mapper.resource("consistency-group", "consistency-groups",
controller=self.resources["consistency-groups"],
collection={"detail": "GET"})
mapper.connect("consistency-groups",
"/{project_id}/consistency-groups/{id}/action",
controller=self.resources["consistency-groups"],
action="action",
conditions={"action": ["POST"]})
self.resources["cgsnapshots"] = share_group_snapshots.create_resource()
mapper.resource("cgsnapshot", "cgsnapshots",
controller=self.resources["cgsnapshots"],
collection={"detail": "GET"},
member={"members": "GET", "action": "POST"})
self.resources["share-groups"] = share_groups.create_resource()
mapper.resource(
"share-group",
"share-groups",
controller=self.resources["share-groups"],
collection={"detail": "GET"})
mapper.connect(
"share-groups",
"/{project_id}/share-groups/{id}/action",
controller=self.resources["share-groups"],
action="action",
conditions={"method": ["POST"]})
self.resources["share-group-types"] = (
share_group_types.create_resource())
mapper.resource(
"share-group-type",
"share-group-types",
controller=self.resources["share-group-types"],
collection={"detail": "GET", "default": "GET"},
member={"action": "POST"})
mapper.connect(
"share-group-types",
"/{project_id}/share-group-types/{id}/access",
controller=self.resources["share-group-types"],
action="share_group_type_access",
conditions={"method": ["GET"]})
# NOTE(ameade): These routes can be simplified when the following
# issue is fixed: https://github.com/bbangert/routes/issues/68
self.resources["group-specs"] = (
share_group_type_specs.create_resource())
mapper.connect(
"share-group-types",
"/{project_id}/share-group-types/{id}/group-specs",
controller=self.resources["group-specs"],
action="index",
conditions={"method": ["GET"]})
mapper.connect(
"share-group-types",
"/{project_id}/share-group-types/{id}/group-specs",
controller=self.resources["group-specs"],
action="create",
conditions={"method": ["POST"]})
mapper.connect(
"share-group-types",
"/{project_id}/share-group-types/{id}/group-specs/{key}",
controller=self.resources["group-specs"],
action="show",
conditions={"method": ["GET"]})
mapper.connect(
"share-group-types",
"/{project_id}/share-group-types/{id}/group-specs/{key}",
controller=self.resources["group-specs"],
action="delete",
conditions={"method": ["DELETE"]})
mapper.connect(
"share-group-types",
"/{project_id}/share-group-types/{id}/group-specs/{key}",
controller=self.resources["group-specs"],
action="update",
conditions={"method": ["PUT"]})
self.resources["share-group-snapshots"] = (
share_group_snapshots.create_resource())
mapper.resource(
"share-group-snapshot",
"share-group-snapshots",
controller=self.resources["share-group-snapshots"],
collection={"detail": "GET"},
member={"members": "GET", "action": "POST"})
mapper.connect(
"share-group-snapshots",
"/{project_id}/share-group-snapshots/{id}/action",
controller=self.resources["share-group-snapshots"],
action="action",
conditions={"method": ["POST"]})
self.resources['share-replicas'] = share_replicas.create_resource()
mapper.resource("share-replica", "share-replicas",

195
manila/api/v2/share_group_snapshots.py

@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
"""The consistency groups snapshot API."""
from oslo_log import log
from oslo_utils import uuidutils
import six
@ -23,86 +21,85 @@ from webob import exc
from manila.api import common
from manila.api.openstack import wsgi
import manila.api.views.share_group_snapshots as sgs_views
import manila.api.views.share_group_snapshots as share_group_snapshots_views
from manila import db
from manila import exception
from manila.i18n import _, _LI
import manila.share_group.api as sg_api
import manila.share_group.api as share_group_api
LOG = log.getLogger(__name__)
class CGSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The Consistency Group Snapshots API controller for the OpenStack API."""
class ShareGroupSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The share group snapshots API controller for the OpenStack API."""
resource_name = 'cgsnapshot'
_view_builder_class = sgs_views.CGSnapshotViewBuilder
resource_name = 'share_group_snapshot'
_view_builder_class = (
share_group_snapshots_views.ShareGroupSnapshotViewBuilder)
def __init__(self):
super(CGSnapshotController, self).__init__()
self.cg_api = sg_api.API()
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.authorize('get_cgsnapshot')
def show(self, req, id):
"""Return data about the given cgsnapshot."""
context = req.environ['manila.context']
super(ShareGroupSnapshotController, self).__init__()
self.share_group_api = share_group_api.API()
def _get_share_group_snapshot(self, context, sg_snapshot_id):
try:
cg = self.cg_api.get_cgsnapshot(context, id)
return self.share_group_api.get_share_group_snapshot(
context, sg_snapshot_id)
except exception.NotFound:
msg = _("Consistency group snapshot %s not found.") % id
msg = _("Share group snapshot %s not found.") % sg_snapshot_id
raise exc.HTTPNotFound(explanation=msg)
return self._view_builder.detail(req, cg)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get')
def show(self, req, id):
"""Return data about the given share group snapshot."""
context = req.environ['manila.context']
sg_snapshot = self._get_share_group_snapshot(context, id)
return self._view_builder.detail(req, sg_snapshot)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def delete(self, req, id):
"""Delete a cgsnapshot."""
"""Delete a share group snapshot."""
context = req.environ['manila.context']
LOG.info(_LI("Delete consistency group snapshot with id: %s"), id,
context=context)
LOG.info(_LI("Delete share group snapshot with id: %s"),
id, context=context)
sg_snapshot = self._get_share_group_snapshot(context, id)
try:
snap = self.cg_api.get_cgsnapshot(context, id)
except exception.NotFound:
msg = _("Consistency group snapshot %s not found.") % id
raise exc.HTTPNotFound(explanation=msg)
try:
self.cg_api.delete_cgsnapshot(context, snap)
except exception.InvalidCGSnapshot as e:
self.share_group_api.delete_share_group_snapshot(
context, sg_snapshot)
except exception.InvalidShareGroupSnapshot as e:
raise exc.HTTPConflict(explanation=six.text_type(e))
return webob.Response(status_int=202)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get_all')
def index(self, req):
"""Returns a summary list of cgsnapshots."""
return self._get_cgs(req, is_detail=False)
"""Returns a summary list of share group snapshots."""
return self._get_share_group_snaps(req, is_detail=False)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get_all')
def detail(self, req):
"""Returns a detailed list of cgsnapshots."""
return self._get_cgs(req, is_detail=True)
"""Returns a detailed list of share group snapshots."""
return self._get_share_group_snaps(req, is_detail=True)
def _get_cgs(self, req, is_detail):
"""Returns a list of cgsnapshots."""
def _get_share_group_snaps(self, req, is_detail):
"""Returns a list of share group snapshots."""
context = req.environ['manila.context']
search_opts = {}
search_opts.update(req.GET)
# Remove keys that are not related to cg attrs
# Remove keys that are not related to group attrs
search_opts.pop('limit', None)
search_opts.pop('offset', None)
sort_key = search_opts.pop('sort_key', 'created_at')
sort_dir = search_opts.pop('sort_dir', 'desc')
snaps = self.cg_api.get_all_cgsnapshots(
context, detailed=is_detail, search_opts=search_opts)
snaps = self.share_group_api.get_all_share_group_snapshots(
context, detailed=is_detail, search_opts=search_opts,
sort_dir=sort_dir, sort_key=sort_key)
limited_list = common.limited(snaps, req)
@ -112,88 +109,76 @@ class CGSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
snaps = self._view_builder.summary_list(req, limited_list)
return snaps
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def update(self, req, id, body):
"""Update a cgsnapshot."""
"""Update a share group snapshot."""
context = req.environ['manila.context']
if not self.is_valid_body(body, 'cgsnapshot'):
msg = _("'cgsnapshot' is missing from the request body")
key = 'share_group_snapshot'
if not self.is_valid_body(body, key):
msg = _("'%s' is missing from the request body.") % key
raise exc.HTTPBadRequest(explanation=msg)
cg_data = body['cgsnapshot']
sg_snapshot_data = body[key]
valid_update_keys = {
'name',
'description',
}
invalid_fields = set(cg_data.keys()) - valid_update_keys
invalid_fields = set(sg_snapshot_data.keys()) - valid_update_keys
if invalid_fields:
msg = _("The fields %s are invalid or not allowed to be updated.")
raise exc.HTTPBadRequest(explanation=msg % invalid_fields)
try:
cg = self.cg_api.get_cgsnapshot(context, id)
except exception.NotFound:
msg = _("Consistency group snapshot %s not found.") % id
raise exc.HTTPNotFound(explanation=msg)
cg = self.cg_api.update_cgsnapshot(context, cg, cg_data)
return self._view_builder.detail(req, cg)
sg_snapshot = self._get_share_group_snapshot(context, id)
sg_snapshot = self.share_group_api.update_share_group_snapshot(
context, sg_snapshot, sg_snapshot_data)
return self._view_builder.detail(req, sg_snapshot)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.response(202)
@wsgi.Controller.authorize
def create(self, req, body):
"""Creates a new cgsnapshot."""
"""Creates a new share group snapshot."""
context = req.environ['manila.context']
if not self.is_valid_body(body, 'cgsnapshot'):
msg = _("'cgsnapshot' is missing from the request body")
if not self.is_valid_body(body, 'share_group_snapshot'):
msg = _("'share_group_snapshot' is missing from the request body.")
raise exc.HTTPBadRequest(explanation=msg)
cgsnapshot = body.get('cgsnapshot')
share_group_snapshot = body.get('share_group_snapshot', {})
if not cgsnapshot.get('consistency_group_id'):
msg = _("Must supply 'consistency_group_id' attribute.")
share_group_id = share_group_snapshot.get('share_group_id')
if not share_group_id:
msg = _("Must supply 'share_group_id' attribute.")
raise exc.HTTPBadRequest(explanation=msg)
consistency_group_id = cgsnapshot.get('consistency_group_id')
if (consistency_group_id and
not uuidutils.is_uuid_like(consistency_group_id)):
msg = _("The 'consistency_group_id' attribute must be a uuid.")
if not uuidutils.is_uuid_like(share_group_id):
msg = _("The 'share_group_id' attribute must be a uuid.")
raise exc.HTTPBadRequest(explanation=six.text_type(msg))
kwargs = {"consistency_group_id": consistency_group_id}
if 'name' in cgsnapshot:
kwargs['name'] = cgsnapshot.get('name')
if 'description' in cgsnapshot:
kwargs['description'] = cgsnapshot.get('description')
kwargs = {"share_group_id": share_group_id}
if 'name' in share_group_snapshot:
kwargs['name'] = share_group_snapshot.get('name')
if 'description' in share_group_snapshot:
kwargs['description'] = share_group_snapshot.get('description')
try:
new_snapshot = self.cg_api.create_cgsnapshot(context, **kwargs)
except exception.ConsistencyGroupNotFound as e:
new_snapshot = self.share_group_api.create_share_group_snapshot(
context, **kwargs)
except exception.ShareGroupNotFound as e:
raise exc.HTTPBadRequest(explanation=six.text_type(e))
except exception.InvalidConsistencyGroup as e:
except exception.InvalidShareGroup as e:
raise exc.HTTPConflict(explanation=six.text_type(e))
return self._view_builder.detail(req, dict(new_snapshot.items()))
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.authorize('get_cgsnapshot')
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get')
def members(self, req, id):
"""Returns a list of cgsnapshot members."""
"""Returns a list of share group snapshot members."""
context = req.environ['manila.context']
search_opts = {}
search_opts.update(req.GET)
# Remove keys that are not related to cg attrs
search_opts.pop('limit', None)
search_opts.pop('offset', None)
snaps = self.cg_api.get_all_cgsnapshot_members(context, id)
snaps = self.share_group_api.get_all_share_group_snapshot_members(
context, id)
limited_list = common.limited(snaps, req)
@ -201,34 +186,24 @@ class CGSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
return snaps
def _update(self, *args, **kwargs):
db.cgsnapshot_update(*args, **kwargs)
db.share_group_snapshot_update(*args, **kwargs)
def _get(self, *args, **kwargs):
return self.cg_api.get_cgsnapshot(*args, **kwargs)
return self.share_group_api.get_share_group_snapshot(*args, **kwargs)
def _delete(self, context, resource, force=True):
db.cgsnapshot_destroy(context.elevated(), resource['id'])
db.share_group_snapshot_destroy(context.elevated(), resource['id'])
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
@wsgi.action('os-reset_status')
def cgsnapshot_reset_status_legacy(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.7', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action('reset_status')
def cgsnapshot_reset_status(self, req, id, body):
def share_group_snapshot_reset_status(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
@wsgi.action('os-force_delete')
def cgsnapshot_force_delete_legacy(self, req, id, body):
return self._force_delete(req, id, body)
@wsgi.Controller.api_version('2.7', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action('force_delete')
def cgsnapshot_force_delete(self, req, id, body):
def share_group_snapshot_force_delete(self, req, id, body):
return self._force_delete(req, id, body)
def create_resource():
return wsgi.Resource(CGSnapshotController())
return wsgi.Resource(ShareGroupSnapshotController())

133
manila/api/v2/share_group_type_specs.py

@ -0,0 +1,133 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
import six
import webob
from manila.api import common
from manila.api.openstack import wsgi
from manila import db
from manila import exception
from manila.i18n import _
from manila.share_group import share_group_types
class ShareGroupTypeSpecsController(wsgi.Controller):
"""The share group type specs API controller for the OpenStack API."""
resource_name = 'share_group_types_spec'
def _get_group_specs(self, context, type_id):
specs = db.share_group_type_specs_get(context, type_id)
return {"group_specs": copy.deepcopy(specs)}
def _assert_share_group_type_exists(self, context, type_id):
try:
share_group_types.get(context, type_id)
except exception.NotFound as ex:
raise webob.exc.HTTPNotFound(explanation=ex.msg)
def _verify_group_specs(self, group_specs):
def is_valid_string(v):
return isinstance(v, six.string_types) and len(v) in range(1, 256)
def is_valid_spec(k, v):
valid_spec_key = is_valid_string(k)
valid_type = is_valid_string(v) or isinstance(v, bool)
return valid_spec_key and valid_type
for k, v in group_specs.items():
if is_valid_string(k) and isinstance(v, dict):
self._verify_group_specs(v)
elif not is_valid_spec(k, v):
expl = _('Invalid extra_spec: %(key)s: %(value)s') % {
'key': k, 'value': v
}
raise webob.exc.HTTPBadRequest(explanation=expl)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def index(self, req, id):
"""Returns the list of group specs for a given share group type."""
context = req.environ['manila.context']
self._assert_share_group_type_exists(context, id)
return self._get_group_specs(context, id)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def create(self, req, id, body=None):
context = req.environ['manila.context']
if not self.is_valid_body(body, 'group_specs'):
raise webob.exc.HTTPBadRequest()
self._assert_share_group_type_exists(context, id)
specs = body['group_specs']
self._verify_group_specs(specs)
self._check_key_names(specs.keys())
db.share_group_type_specs_update_or_create(context, id, specs)
return body
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def update(self, req, id, key, body=None):
context = req.environ['manila.context']
if not body:
expl = _('Request body empty.')
raise webob.exc.HTTPBadRequest(explanation=expl)
self._assert_share_group_type_exists(context, id)
if key not in body:
expl = _('Request body and URI mismatch.')
raise webob.exc.HTTPBadRequest(explanation=expl)
if len(body) > 1:
expl = _('Request body contains too many items.')
raise webob.exc.HTTPBadRequest(explanation=expl)
self._verify_group_specs(body)
db.share_group_type_specs_update_or_create(context, id, body)
return body
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def show(self, req, id, key):
"""Return a single group spec item."""
context = req.environ['manila.context']
self._assert_share_group_type_exists(context, id)
specs = self._get_group_specs(context, id)
if key in specs['group_specs']:
return {key: specs['group_specs'][key]}
else:
raise webob.exc.HTTPNotFound()
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def delete(self, req, id, key):
"""Deletes an existing group spec."""
context = req.environ['manila.context']
self._assert_share_group_type_exists(context, id)
try:
db.share_group_type_specs_delete(context, id, key)
except exception.ShareGroupTypeSpecsNotFound as error:
raise webob.exc.HTTPNotFound(explanation=error.msg)
return webob.Response(status_int=204)
def _check_key_names(self, keys):
if not common.validate_key_names(keys):
expl = _('Key names can only contain alphanumeric characters, '
'underscores, periods, colons and hyphens.')
raise webob.exc.HTTPBadRequest(explanation=expl)
def create_resource():
return wsgi.Resource(ShareGroupTypeSpecsController())

227
manila/api/v2/share_group_types.py

@ -0,0 +1,227 @@
# 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.
"""The group type API controller module."""
from oslo_utils import strutils
from oslo_utils import uuidutils
import six
import webob
from webob import exc
from manila.api.openstack import wsgi
from manila.api.views import share_group_types as views
from manila import exception
from manila.i18n import _
from manila.share_group import share_group_types
class ShareGroupTypesController(wsgi.Controller):
"""The share group types API controller for the OpenStack API."""
resource_name = 'share_group_type'
_view_builder_class = views.ShareGroupTypeViewBuilder
def _check_body(self, body, action_name):
if not self.is_valid_body(body, action_name):
raise webob.exc.HTTPBadRequest()
access = body[action_name]
project = access.get('project')
if not uuidutils.is_uuid_like(project):
msg = _("Project value (%s) must be in uuid format.") % project
raise webob.exc.HTTPBadRequest(explanation=msg)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def index(self, req):
"""Returns the list of share group types."""
limited_types = self._get_share_group_types(req)
return self._view_builder.index(req, limited_types)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def show(self, req, id):
"""Return a single share group type item."""
context = req.environ['manila.context']
try:
share_group_type = share_group_types.get(context, id)
except exception.NotFound:
msg = _("Share group type with id %s not found.")
raise exc.HTTPNotFound(explanation=msg % id)
share_group_type['id'] = six.text_type(share_group_type['id'])
return self._view_builder.show(req, share_group_type)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def default(self, req):
"""Return default share group type."""
context = req.environ['manila.context']
share_group_type = share_group_types.get_default(context)
if not share_group_type:
msg = _("Default share group type not found.")
raise exc.HTTPNotFound(explanation=msg)
share_group_type['id'] = six.text_type(share_group_type['id'])
return self._view_builder.show(req, share_group_type)
def _get_share_group_types(self, req):
"""Helper function that returns a list of share group type dicts."""
filters = {}
context = req.environ['manila.context']
if context.is_admin:
# Only admin has query access to all group types
filters['is_public'] = self._parse_is_public(
req.params.get('is_public'))
else:
filters['is_public'] = True
limited_types = share_group_types.get_all(
context, search_opts=filters).values()
return list(limited_types)
@staticmethod
def _parse_is_public(is_public):
"""Parse is_public into something usable.
:returns:
- True: API should list public share group types only
- False: API should list private share group types only
- None: API should list both public and private share group types
"""
if is_public is None:
# preserve default value of showing only public types
return True
elif six.text_type(is_public).lower() == "all":
return None
else:
try:
return strutils.bool_from_string(is_public, strict=True)
except ValueError:
msg = _('Invalid is_public filter [%s]') % is_public
raise exc.HTTPBadRequest(explanation=msg)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action("create")
@wsgi.Controller.authorize('create')
def _create(self, req, body):
"""Creates a new share group type."""
context = req.environ['manila.context']
if not self.is_valid_body(body, 'share_group_type'):
raise webob.exc.HTTPBadRequest()
share_group_type = body['share_group_type']
name = share_group_type.get('name')
specs = share_group_type.get('group_specs', {})
is_public = share_group_type.get('is_public', True)
if not share_group_type.get('share_types'):
msg = _("Supported share types must be provided.")
raise webob.exc.HTTPBadRequest(explanation=msg)
share_types = share_group_type.get('share_types')
if name is None or name == "" or len(name) > 255:
msg = _("Share group type name is not valid.")
raise webob.exc.HTTPBadRequest(explanation=msg)
try:
share_group_types.create(
context, name, share_types, specs, is_public)
share_group_type = share_group_types.get_by_name(
context, name)
except exception.ShareGroupTypeExists as err:
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
except exception.NotFound:
raise webob.exc.HTTPNotFound()
return self._view_builder.show(req, share_group_type)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action("delete")
@wsgi.Controller.authorize('delete')
def _delete(self, req, id):
"""Deletes an existing group type."""
context = req.environ['manila.context']
try:
share_group_type = share_group_types.get(context, id)
share_group_types.destroy(context, share_group_type['id'])
except exception.ShareGroupTypeInUse:
msg = _('Target share group type with id %s is still in use.')
raise webob.exc.HTTPBadRequest(explanation=msg % id)
except exception.NotFound:
raise webob.exc.HTTPNotFound()
return webob.Response(status_int=204)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('list_project_access')
def share_group_type_access(self, req, id):
context = req.environ['manila.context']
try:
share_group_type = share_group_types.get(
context, id, expected_fields=['projects'])
except exception.ShareGroupTypeNotFound:
explanation = _("Share group type %s not found.") % id
raise webob.exc.HTTPNotFound(explanation=explanation)
if share_group_type['is_public']:
expl = _("Access list not available for public share group types.")
raise webob.exc.HTTPNotFound(explanation=expl)
projects = []
for project_id in share_group_type['projects']:
projects.append(
{'share_group_type_id': share_group_type['id'],
'project_id': project_id}
)
return {'share_group_type_access': projects}
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action('addProjectAccess')
@wsgi.Controller.authorize('add_project_access')
def _add_project_access(self, req, id, body):
context = req.environ['manila.context']
self._check_body(body, 'addProjectAccess')
project = body['addProjectAccess']['project']
self._assert_non_public_share_group_type(context, id)
try:
share_group_types.add_share_group_type_access(
context, id, project)
except exception.ShareGroupTypeAccessExists as err:
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
return webob.Response(status_int=202)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action('removeProjectAccess')
@wsgi.Controller.authorize('remove_project_access')
def _remove_project_access(self, req, id, body):
context = req.environ['manila.context']
self._check_body(body, 'removeProjectAccess')
project = body['removeProjectAccess']['project']
self._assert_non_public_share_group_type(context, id)
try:
share_group_types.remove_share_group_type_access(
context, id, project)
except exception.ShareGroupTypeAccessNotFound as err:
raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
return webob.Response(status_int=202)
def _assert_non_public_share_group_type(self, context, type_id):
try:
share_group_type = share_group_types.get(
context, type_id)
if share_group_type['is_public']:
msg = _("Type access modification is not applicable to "
"public share group type.")
raise webob.exc.HTTPConflict(explanation=msg)
except exception.ShareGroupTypeNotFound as err:
raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
def create_resource():
return wsgi.Resource(ShareGroupTypesController())

251
manila/api/v2/share_groups.py

@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
"""The consistency groups API."""
from oslo_log import log
from oslo_utils import uuidutils
import six
@ -23,155 +21,156 @@ from webob import exc
from manila.api import common
from manila.api.openstack import wsgi
import manila.api.views.share_groups as share_group_views
from manila.api.views import share_groups as share_group_views
from manila import db
from manila import exception
from manila.i18n import _, _LI
from manila.share import share_types
import manila.share_group.api as sg_api
from manila.share_group import api as share_group_api
from manila.share_group import share_group_types
LOG = log.getLogger(__name__)
class CGController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The Consistency Groups API controller for the OpenStack API."""
class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The Share Groups API controller for the OpenStack API."""
resource_name = 'consistency_group'
_view_builder_class = share_group_views.CGViewBuilder
resource_name = 'consistency_group'
resource_name = 'share_group'
_view_builder_class = share_group_views.ShareGroupViewBuilder
def __init__(self):
super(CGController, self).__init__()
self.cg_api = sg_api.API()
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.authorize('get')
def show(self, req, id):
"""Return data about the given CG."""
context = req.environ['manila.context']
super(ShareGroupController, self).__init__()
self.share_group_api = share_group_api.API()
def _get_share_group(self, context, share_group_id):
try:
cg = self.cg_api.get(context, id)
return self.share_group_api.get(context, share_group_id)
except exception.NotFound:
msg = _("Consistency group %s not found.") % id
msg = _("Share group %s not found.") % share_group_id
raise exc.HTTPNotFound(explanation=msg)
return self._view_builder.detail(req, cg)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get')
def show(self, req, id):
"""Return data about the given share group."""
context = req.environ['manila.context']
share_group = self._get_share_group(context, id)
return self._view_builder.detail(req, share_group)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def delete(self, req, id):
"""Delete a CG."""
"""Delete a share group."""
context = req.environ['manila.context']
LOG.info(_LI("Delete consistency group with id: %s"), id,
context=context)
try:
cg = self.cg_api.get(context, id)
except exception.NotFound:
msg = _("Consistency group %s not found.") % id
raise exc.HTTPNotFound(explanation=msg)
LOG.info(_LI("Delete share group with id: %s"), id, context=context)
share_group = self._get_share_group(context, id)
try:
self.cg_api.delete(context, cg)
except exception.InvalidConsistencyGroup as e:
self.share_group_api.delete(context, share_group)
except exception.InvalidShareGroup as e:
raise exc.HTTPConflict(explanation=six.text_type(e))
return webob.Response(status_int=202)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get_all')
def index(self, req):
"""Returns a summary list of shares."""
return self._get_cgs(req, is_detail=False)
"""Returns a summary list of share groups."""
return self._get_share_groups(req, is_detail=False)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get_all')
def detail(self, req):
"""Returns a detailed list of shares."""
return self._get_cgs(req, is_detail=True)
"""Returns a detailed list of share groups."""
return self._get_share_groups(req, is_detail=True)
def _get_cgs(self, req, is_detail):
"""Returns a list of shares, transformed through view builder."""
def _get_share_groups(self, req, is_detail):
"""Returns a list of share groups, transformed through view builder."""
context = req.environ['manila.context']
search_opts = {}
search_opts.update(req.GET)
# Remove keys that are not related to cg attrs
# Remove keys that are not related to share group attrs
search_opts.pop('limit', None)
search_opts.pop('offset', None)
sort_key = search_opts.pop('sort_key', 'created_at')
sort_dir = search_opts.pop('sort_dir', 'desc')
if 'group_type_id' in search_opts:
search_opts['share_group_type_id'] = search_opts.pop(
'group_type_id')
cgs = self.cg_api.get_all(
context, detailed=is_detail, search_opts=search_opts)
share_groups = self.share_group_api.get_all(
context, detailed=is_detail, search_opts=search_opts,
sort_dir=sort_dir, sort_key=sort_key,
)
limited_list = common.limited(cgs, req)
limited_list = common.limited(share_groups, req)
if is_detail:
cgs = self._view_builder.detail_list(req, limited_list)
share_groups = self._view_builder.detail_list(req, limited_list)
else:
cgs = self._view_builder.summary_list(req, limited_list)
return cgs
share_groups = self._view_builder.summary_list(req, limited_list)
return share_groups
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize
def update(self, req, id, body):
"""Update a share."""
"""Update a share group."""
context = req.environ['manila.context']
if not self.is_valid_body(body, 'consistency_group'):
msg = _("'consistency_group' is missing from the request body.")
if not self.is_valid_body(body, 'share_group'):
msg = _("'share_group' is missing from the request body.")
raise exc.HTTPBadRequest(explanation=msg)
cg_data = body['consistency_group']
valid_update_keys = {
'name',
'description',
}
invalid_fields = set(cg_data.keys()) - valid_update_keys
share_group_data = body['share_group']
valid_update_keys = {'name', 'description'}
invalid_fields = set(share_group_data.keys()) - valid_update_keys
if invalid_fields:
msg = _("The fields %s are invalid or not allowed to be updated.")
raise exc.HTTPBadRequest(explanation=msg % invalid_fields)
try:
cg = self.cg_api.get(context, id)
except exception.NotFound:
msg = _("Consistency group %s not found.") % id
raise exc.HTTPNotFound(explanation=msg)
share_group = self._get_share_group(context, id)
share_group = self.share_group_api.update(
context, share_group, share_group_data)
return self._view_builder.detail(req, share_group)
cg = self.cg_api.update(context, cg, cg_data)
return self._view_builder.detail(req, cg)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.response(202)
@wsgi.Controller.authorize
def create(self, req, body):
"""Creates a new share."""
"""Creates a new share group."""
context = req.environ['manila.context']
if not self.is_valid_body(body, 'consistency_group'):
msg = _("'consistency_group' is missing from the request body.")
if not self.is_valid_body(body, 'share_group'):
msg = _("'share_group' is missing from the request body.")
raise exc.HTTPBadRequest(explanation=msg)
cg = body['consistency_group']
valid_fields = {'name', 'description', 'share_types',
'source_cgsnapshot_id', 'share_network_id'}
invalid_fields = set(cg.keys()) - valid_fields
share_group = body['share_group']
valid_fields = {
'name',
'description',
'share_types',
'share_group_type_id',
'source_share_group_snapshot_id',
'share_network_id',
}
invalid_fields = set(share_group.keys()) - valid_fields
if invalid_fields:
msg = _("The fields %s are invalid.") % invalid_fields
raise exc.HTTPBadRequest(explanation=msg)
if 'share_types' in cg and 'source_cgsnapshot_id' in cg:
if ('share_types' in share_group and
'source_share_group_snapshot_id' in share_group):
msg = _("Cannot supply both 'share_types' and "
"'source_cgsnapshot_id' attributes.")
"'source_share_group_snapshot_id' attributes.")
raise exc.HTTPBadRequest(explanation=msg)
if not cg.get('share_types') and 'source_cgsnapshot_id' not in cg:
if not (share_group.get('share_types') or
'source_share_group_snapshot_id' in share_group):
default_share_type = share_types.get_default_share_type()
if default_share_type:
cg['share_types'] = [default_share_type['id']]
share_group['share_types'] = [default_share_type['id']]
else:
msg = _("Must specify at least one share type as a default "
"share type has not been configured.")
@ -179,76 +178,94 @@ class CGController(wsgi.Controller, wsgi.AdminActionsMixin):
kwargs = {}
if 'name' in cg:
kwargs['name'] = cg.get('name')
if 'description' in cg:
kwargs['description'] = cg.get('description')
if 'name' in share_group:
kwargs['name'] = share_group.get('name')
if 'description' in share_group:
kwargs['description'] = share_group.get('description')
_share_types = cg.get('share_types')
_share_types = share_group.get('share_types')
if _share_types:
if not all([uuidutils.is_uuid_like(st) for st in _share_types]):
msg = _("The 'share_types' attribute must be a list of uuids")
raise exc.HTTPBadRequest(explanation=msg)
kwargs['share_type_ids'] = _share_types
if 'share_network_id' in cg and 'source_cgsnapshot_id' in cg:
if ('share_network_id' in share_group and
'source_share_group_snapshot_id' in share_group):
msg = _("Cannot supply both 'share_network_id' and "
"'source_cgsnapshot_id' attributes as the share network "
"is inherited from the source.")
"'source_share_group_snapshot_id' attributes as the share "
"network is inherited from the source.")
raise exc.HTTPBadRequest(explanation=msg)
if 'source_cgsnapshot_id' in cg:
source_cgsnapshot_id = cg.get('source_cgsnapshot_id')
if not uuidutils.is_uuid_like(source_cgsnapshot_id):
msg = _("The 'source_cgsnapshot_id' attribute must be a uuid.")
if 'source_share_group_snapshot_id' in share_group:
source_share_group_snapshot_id = share_group.get(
'source_share_group_snapshot_id')
if not uuidutils.is_uuid_like(source_share_group_snapshot_id):
msg = _("The 'source_share_group_snapshot_id' attribute "
"must be a uuid.")
raise exc.HTTPBadRequest(explanation=six.text_type(msg))
kwargs['source_cgsnapshot_id'] = source_cgsnapshot_id
elif 'share_network_id' in cg:
share_network_id = cg.get('share_network_id')
kwargs['source_share_group_snapshot_id'] = (
source_share_group_snapshot_id)
elif 'share_network_id' in share_group:
share_network_id = share_group.get('share_network_id')
if not uuidutils.is_uuid_like(share_network_id):
msg = _("The 'share_network_id' attribute must be a uuid.")
raise exc.HTTPBadRequest(explanation=six.text_type(msg))
kwargs['share_network_id'] = share_network_id
if 'share_group_type_id' in share_group:
share_group_type_id = share_group.get('share_group_type_id')
if not uuidutils.is_uuid_like(share_group_type_id):
msg = _("The 'share_group_type_id' attribute must be a uuid.")
raise exc.HTTPBadRequest(explanation=six.text_type(msg))
kwargs['share_group_type_id'] = share_group_type_id
else: # get default
def_share_group_type = share_group_types.get_default()
if def_share_group_type:
kwargs['share_group_type_id'] = def_share_group_type['id']
else:
msg = _("Must specify a share group type as a default "
"share group type has not been configured.")
raise exc.HTTPBadRequest(explanation=msg)
try:
new_cg = self.cg_api.create(context, **kwargs)
except exception.InvalidCGSnapshot as e:
new_share_group = self.share_group_api.create(context, **kwargs)
except exception.InvalidShareGroupSnapshot as e:
raise exc.HTTPConflict(explanation=six.text_type(e))
except (exception.CGSnapshotNotFound, exception.InvalidInput) as e:
except (exception.ShareGroupSnapshotNotFound,
exception.InvalidInput) as e:
raise exc.HTTPBadRequest(explanation=six.text_type(e))
return self._view_builder.detail(req, dict(new_cg.items()))
return self._view_builder.detail(
req, {k: v for k, v in new_share_group.items()})
def _update(self, *args, **kwargs):
db.consistency_group_update(*args, **kwargs)
db.share_group_update(*args, **kwargs)
def _get(self, *args, **kwargs):
return self.cg_api.get(*args, **kwargs)
return self.share_group_api.get(*args, **kwargs)
def _delete(self, context, resource, force=True):
db.consistency_group_destroy(context.elevated(), resource['id'])
# Delete all share group snapshots
for snap in resource['snapshots']:
db.share_group_snapshot_destroy(context, snap['id'])
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
@wsgi.action('os-reset_status')
def cg_reset_status_legacy(self, req, id, body):
return self._reset_status(req, id, body)
# Delete all shares in share group
for share in db.get_all_shares_by_share_group(context, resource['id']):
db.share_delete(context, share['id'])
db.share_group_destroy(context.elevated(), resource['id'])
@wsgi.Controller.api_version('2.7', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action('reset_status')
def cg_reset_status(self, req, id, body):
def share_group_reset_status(self, req, id, body):
return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
@wsgi.action('os-force_delete')
def cg_force_delete_legacy(self, req, id, body):
return self._force_delete(req, id, body)
@wsgi.Controller.api_version('2.7', experimental=True)
@wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.action('force_delete')
def cg_force_delete(self, req, id, body):
def share_group_force_delete(self, req, id, body):
return self._force_delete(req