diff --git a/contrib/ci/post_test_hook.sh b/contrib/ci/post_test_hook.sh index a3bfb4394f..d66ea8db89 100755 --- a/contrib/ci/post_test_hook.sh +++ b/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 diff --git a/etc/manila/policy.json b/etc/manila/policy.json index 4736dd70f9..7d964fa024 100644 --- a/etc/manila/policy.json +++ b/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", + "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", - "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_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" } diff --git a/manila/api/openstack/api_version_request.py b/manila/api/openstack/api_version_request.py index 9de9bffe14..cfb467d17d 100644 --- a/manila/api/openstack/api_version_request.py +++ b/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 diff --git a/manila/api/openstack/rest_api_version_history.rst b/manila/api/openstack/rest_api_version_history.rst index a71e707067..3747cc4aad 100644 --- a/manila/api/openstack/rest_api_version_history.rst +++ b/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. diff --git a/manila/api/v1/shares.py b/manila/api/v1/shares.py index e0029c2785..944ebe3f7d 100644 --- a/manila/api/v1/shares.py +++ b/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')))): diff --git a/manila/api/v2/router.py b/manila/api/v2/router.py index 5e3c1bc125..8740d34d15 100644 --- a/manila/api/v2/router.py +++ b/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["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["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-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", diff --git a/manila/api/v2/share_group_snapshots.py b/manila/api/v2/share_group_snapshots.py index 2af9bcc0d7..629c92bc7a 100644 --- a/manila/api/v2/share_group_snapshots.py +++ b/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) + 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) - cg = self.cg_api.update_cgsnapshot(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 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()) diff --git a/manila/api/v2/share_group_type_specs.py b/manila/api/v2/share_group_type_specs.py new file mode 100644 index 0000000000..3fec6a02f9 --- /dev/null +++ b/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()) diff --git a/manila/api/v2/share_group_types.py b/manila/api/v2/share_group_types.py new file mode 100644 index 0000000000..ae2720d4a3 --- /dev/null +++ b/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()) diff --git a/manila/api/v2/share_groups.py b/manila/api/v2/share_groups.py index 77302fea24..3a926eff9b 100644 --- a/manila/api/v2/share_groups.py +++ b/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() + super(ShareGroupController, self).__init__() + self.share_group_api = share_group_api.API() - @wsgi.Controller.api_version('2.4', experimental=True) + def _get_share_group(self, context, share_group_id): + try: + return self.share_group_api.get(context, share_group_id) + except exception.NotFound: + msg = _("Share group %s not found.") % share_group_id + raise exc.HTTPNotFound(explanation=msg) + + @wsgi.Controller.api_version('2.31', experimental=True) @wsgi.Controller.authorize('get') def show(self, req, id): - """Return data about the given CG.""" + """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) - try: - cg = self.cg_api.get(context, id) - except exception.NotFound: - msg = _("Consistency group %s not found.") % id - raise exc.HTTPNotFound(explanation=msg) - - return self._view_builder.detail(req, cg) - - @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) - + LOG.info(_LI("Delete share group with id: %s"), id, context=context) + share_group = self._get_share_group(context, id) try: - cg = self.cg_api.get(context, id) - except exception.NotFound: - msg = _("Consistency group %s not found.") % id - raise exc.HTTPNotFound(explanation=msg) - - 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']) - @wsgi.Controller.api_version('2.7', experimental=True) + db.share_group_destroy(context.elevated(), resource['id']) + + @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, id, body) def create_resource(): - return wsgi.Resource(CGController()) + return wsgi.Resource(ShareGroupController()) diff --git a/manila/api/v2/share_networks.py b/manila/api/v2/share_networks.py index db155b2a1b..78a0e6ea96 100644 --- a/manila/api/v2/share_networks.py +++ b/manila/api/v2/share_networks.py @@ -79,12 +79,12 @@ class ShareNetworkController(wsgi.Controller): LOG.error(msg) raise exc.HTTPConflict(explanation=msg) - # NOTE(ameade): Do not allow deletion of share network used by CG - cg_count = db_api.count_consistency_groups_in_share_network(context, - id) - if cg_count: + # NOTE(ameade): Do not allow deletion of share network used by share + # group + sg_count = db_api.count_share_groups_in_share_network(context, id) + if sg_count: msg = _("Can not delete share network %(id)s, it has %(len)s " - "consistency group(s).") % {'id': id, 'len': cg_count} + "share group(s).") % {'id': id, 'len': sg_count} LOG.error(msg) raise exc.HTTPConflict(explanation=msg) diff --git a/manila/api/v2/shares.py b/manila/api/v2/shares.py index 0022400697..c9e6c29898 100644 --- a/manila/api/v2/shares.py +++ b/manila/api/v2/shares.py @@ -163,22 +163,22 @@ class ShareController(shares.ShareMixin, return data - @wsgi.Controller.api_version("2.0", "2.3") + @wsgi.Controller.api_version("2.31") def create(self, req, body): - # Remove consistency group attributes - body.get('share', {}).pop('consistency_group_id', None) - share = self._create(req, body) - return share + return self._create( + req, body, check_create_share_from_snapshot_support=True) - @wsgi.Controller.api_version("2.4", "2.23") # noqa - def create(self, req, body): # pylint: disable=E0102 - return self._create(req, body) - - @wsgi.Controller.api_version("2.24") # noqa + @wsgi.Controller.api_version("2.24", "2.30") # noqa def create(self, req, body): # pylint: disable=E0102 + body.get('share', {}).pop('share_group_id', None) return self._create(req, body, check_create_share_from_snapshot_support=True) + @wsgi.Controller.api_version("2.0", "2.23") # noqa + def create(self, req, body): # pylint: disable=E0102 + body.get('share', {}).pop('share_group_id', None) + return self._create(req, body) + @wsgi.Controller.api_version('2.0', '2.6') @wsgi.action('os-reset_status') def share_reset_status_legacy(self, req, id, body): diff --git a/manila/api/views/share_group_snapshots.py b/manila/api/views/share_group_snapshots.py index 6be34fa847..7e443829f9 100644 --- a/manila/api/views/share_group_snapshots.py +++ b/manila/api/views/share_group_snapshots.py @@ -13,23 +13,21 @@ # License for the specific language governing permissions and limitations # under the License. -"""The consistency groups snapshot API.""" - from manila.api import common -class CGSnapshotViewBuilder(common.ViewBuilder): - """Model a cgsnapshot API response as a python dictionary.""" +class ShareGroupSnapshotViewBuilder(common.ViewBuilder): + """Model a share group snapshot API response as a python dictionary.""" - _collection_name = 'cgsnapshot' + _collection_name = "share_group_snapshot" - def summary_list(self, request, cgs): - """Show a list of cgsnapshots without many details.""" - return self._list_view(self.summary, request, cgs) + def summary_list(self, request, group_snaps): + """Show a list of share_group_snapshots without many details.""" + return self._list_view(self.summary, request, group_snaps) - def detail_list(self, request, cgs): - """Detailed view of a list of cgsnapshots.""" - return self._list_view(self.detail, request, cgs) + def detail_list(self, request, group_snaps): + """Detailed view of a list of share_group_snapshots.""" + return self._list_view(self.detail, request, group_snaps) def member_list(self, request, members): members_list = [] @@ -40,56 +38,72 @@ class CGSnapshotViewBuilder(common.ViewBuilder): 'size': member.get('size'), 'share_protocol': member.get('share_proto'), 'project_id': member.get('project_id'), - 'share_type_id': member.get('share_type_id'), - 'cgsnapshot_id': member.get('cgsnapshot_id'), + 'share_group_snapshot_id': member.get( + 'share_group_snapshot_id'), 'share_id': member.get('share_id'), } members_list.append(member_dict) - members_links = self._get_collection_links(request, - members, - 'cgsnapshot_id') - members_dict = dict(cgsnapshot_members=members_list) + members_links = self._get_collection_links( + request, members, "share_group_snapshot_id") + members_dict = {"share_group_snapshot_members": members_list} if members_links: - members_dict['cgsnapshot_members_links'] = members_links + members_dict["share_group_snapshot_members_links"] = members_links return members_dict - def summary(self, request, cg): - """Generic, non-detailed view of a cgsnapshot.""" + def summary(self, request, share_group_snap): + """Generic, non-detailed view of a share group snapshot.""" return { - 'cgsnapshot': { - 'id': cg.get('id'), - 'name': cg.get('name'), - 'links': self._get_links(request, cg['id']) + 'share_group_snapshot': { + 'id': share_group_snap.get('id'), + 'name': share_group_snap.get('name'), + 'links': self._get_links(request, share_group_snap['id']), } } - def detail(self, request, cg): - """Detailed view of a single cgsnapshot.""" - cg_dict = { - 'id': cg.get('id'), - 'name': cg.get('name'), - 'created_at': cg.get('created_at'), - 'status': cg.get('status'), - 'description': cg.get('description'), - 'project_id': cg.get('project_id'), - 'consistency_group_id': cg.get('consistency_group_id'), - 'links': self._get_links(request, cg['id']), + def detail(self, request, share_group_snap): + """Detailed view of a single share group snapshot.""" + + members = self._format_member_list( + share_group_snap.get('share_group_snapshot_members', [])) + + share_group_snap_dict = { + 'id': share_group_snap.get('id'), + 'name': share_group_snap.get('name'), + 'created_at': share_group_snap.get('created_at'), + 'status': share_group_snap.get('status'), + 'description': share_group_snap.get('description'), + 'project_id': share_group_snap.get('project_id'), + 'share_group_id': share_group_snap.get('share_group_id'), + 'members': members, + 'links': self._get_links(request, share_group_snap['id']), } - return {'cgsnapshot': cg_dict} + return {'share_group_snapshot': share_group_snap_dict} + + def _format_member_list(self, members): + members_list = [] + for member in members: + member_dict = { + 'id': member.get('id'), + 'size': member.get('size'), + 'share_id': member.get('share_id'), + } + members_list.append(member_dict) + + return members_list def _list_view(self, func, request, snaps): - """Provide a view for a list of cgsnapshots.""" - snap_list = [func(request, snap)['cgsnapshot'] + """Provide a view for a list of share group snapshots.""" + snap_list = [func(request, snap)["share_group_snapshot"] for snap in snaps] snaps_links = self._get_collection_links(request, snaps, self._collection_name) - snaps_dict = dict(cgsnapshots=snap_list) + snaps_dict = {"share_group_snapshots": snap_list} if snaps_links: - snaps_dict['cgsnapshot_links'] = snaps_links + snaps_dict["share_group_snapshot_links"] = snaps_links return snaps_dict diff --git a/manila/api/views/share_group_types.py b/manila/api/views/share_group_types.py new file mode 100644 index 0000000000..02f23033ca --- /dev/null +++ b/manila/api/views/share_group_types.py @@ -0,0 +1,39 @@ +# 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. + +from manila.api import common + + +class ShareGroupTypeViewBuilder(common.ViewBuilder): + _collection_name = 'share_group_types' + + def show(self, request, share_group_type, brief=False): + """Trim away extraneous share group type attributes.""" + group_specs = share_group_type.get('group_specs', {}) + trimmed = { + 'id': share_group_type.get('id'), + 'name': share_group_type.get('name'), + 'is_public': share_group_type.get('is_public'), + 'group_specs': group_specs, + 'share_types': [ + st['share_type_id'] for st in share_group_type['share_types']], + } + self.update_versioned_resource_dict(request, trimmed, share_group_type) + return trimmed if brief else {"share_group_type": trimmed} + + def index(self, request, share_group_types): + """Index over trimmed share group types.""" + share_group_types_list = [ + self.show(request, share_group_type, True) + for share_group_type in share_group_types + ] + return {"share_group_types": share_group_types_list} diff --git a/manila/api/views/share_groups.py b/manila/api/views/share_groups.py index dda00ef697..1028e15ab6 100644 --- a/manila/api/views/share_groups.py +++ b/manila/api/views/share_groups.py @@ -13,65 +13,67 @@ # License for the specific language governing permissions and limitations # under the License. -"""The consistency groups API.""" - from manila.api import common -class CGViewBuilder(common.ViewBuilder): - """Model a consistency group API response as a python dictionary.""" +class ShareGroupViewBuilder(common.ViewBuilder): + """Model a share group API response as a python dictionary.""" - _collection_name = 'consistency_groups' + _collection_name = 'share_groups' - def summary_list(self, request, cgs): - """Show a list of consistency groups without many details.""" - return self._list_view(self.summary, request, cgs) + def summary_list(self, request, share_groups): + """Show a list of share groups without many details.""" + return self._list_view(self.summary, request, share_groups) - def detail_list(self, request, cgs): - """Detailed view of a list of consistency groups.""" - return self._list_view(self.detail, request, cgs) + def detail_list(self, request, share_groups): + """Detailed view of a list of share groups.""" + return self._list_view(self.detail, request, share_groups) - def summary(self, request, cg): - """Generic, non-detailed view of a consistency group.""" + def summary(self, request, share_group): + """Generic, non-detailed view of a share group.""" return { - 'consistency_group': { - 'id': cg.get('id'), - 'name': cg.get('name'), - 'links': self._get_links(request, cg['id']) + 'share_group': { + 'id': share_group.get('id'), + 'name': share_group.get('name'), + 'links': self._get_links(request, share_group['id']) } } - def detail(self, request, cg): - """Detailed view of a single consistency group.""" + def detail(self, request, share_group): + """Detailed view of a single share group.""" context = request.environ['manila.context'] - cg_dict = { - 'id': cg.get('id'), - 'name': cg.get('name'), - 'created_at': cg.get('created_at'), - 'status': cg.get('status'), - 'description': cg.get('description'), - 'project_id': cg.get('project_id'), - 'host': cg.get('host'), - 'source_cgsnapshot_id': cg.get('source_cgsnapshot_id'), - 'share_network_id': cg.get('share_network_id'), - 'share_types': [st['share_type_id'] for st in cg.get( + share_group_dict = { + 'id': share_group.get('id'), + 'name': share_group.get('name'), + 'created_at': share_group.get('created_at'), + 'status': share_group.get('status'), + 'description': share_group.get('description'), + 'project_id': share_group.get('project_id'), + 'host': share_group.get('host'), + 'share_group_type_id': share_group.get('share_group_type_id'), + 'source_share_group_snapshot_id': share_group.get( + 'source_share_group_snapshot_id'), + 'share_network_id': share_group.get('share_network_id'), + 'share_types': [st['share_type_id'] for st in share_group.get( 'share_types')], - 'links': self._get_links(request, cg['id']), + 'links': self._get_links(request, share_group['id']), } if context.is_admin: - cg_dict['share_server_id'] = cg.get('share_server_id') - return {'consistency_group': cg_dict} + share_group_dict['share_server_id'] = share_group.get( + 'share_server_id') + return {'share_group': share_group_dict} def _list_view(self, func, request, shares): - """Provide a view for a list of consistency groups.""" - cg_list = [func(request, share)['consistency_group'] - for share in shares] - cgs_links = self._get_collection_links(request, - shares, - self._collection_name) - cgs_dict = dict(consistency_groups=cg_list) + """Provide a view for a list of share groups.""" + share_group_list = [ + func(request, share)['share_group'] + for share in shares + ] + share_groups_links = self._get_collection_links( + request, shares, self._collection_name) + share_groups_dict = {"share_groups": share_group_list} - if cgs_links: - cgs_dict['consistency_groups_links'] = cgs_links + if share_groups_links: + share_groups_dict['share_groups_links'] = share_groups_links - return cgs_dict + return share_groups_dict diff --git a/manila/api/views/shares.py b/manila/api/views/shares.py index b224f452f0..6a704d77b4 100644 --- a/manila/api/views/shares.py +++ b/manila/api/views/shares.py @@ -23,7 +23,6 @@ class ViewBuilder(common.ViewBuilder): _collection_name = 'shares' _detail_version_modifiers = [ "add_snapshot_support_field", - "add_consistency_group_fields", "add_task_state_field", "modify_share_type_field", "remove_export_locations", @@ -33,6 +32,7 @@ class ViewBuilder(common.ViewBuilder): "add_create_share_from_snapshot_support_field", "add_revert_to_snapshot_support_field", "translate_access_rules_status", + "add_share_group_fields", ] def summary_list(self, request, shares): @@ -103,13 +103,6 @@ class ViewBuilder(common.ViewBuilder): def add_snapshot_support_field(self, context, share_dict, share): share_dict['snapshot_support'] = share.get('snapshot_support') - @common.ViewBuilder.versioned_method("2.4") - def add_consistency_group_fields(self, context, share_dict, share): - share_dict['consistency_group_id'] = share.get( - 'consistency_group_id') - share_dict['source_cgsnapshot_member_id'] = share.get( - 'source_cgsnapshot_member_id') - @common.ViewBuilder.versioned_method("2.5") def add_task_state_field(self, context, share_dict, share): share_dict['task_state'] = share.get('task_state') @@ -162,6 +155,13 @@ class ViewBuilder(common.ViewBuilder): constants.SHARE_INSTANCE_RULES_SYNCING): share_dict['access_rules_status'] = constants.STATUS_OUT_OF_SYNC + @common.ViewBuilder.versioned_method("2.31") + def add_share_group_fields(self, context, share_dict, share): + share_dict['share_group_id'] = share.get( + 'share_group_id') + share_dict['source_share_group_snapshot_member_id'] = share.get( + 'source_share_group_snapshot_member_id') + def _list_view(self, func, request, shares): """Provide a view for a list of shares.""" shares_list = [func(request, share)['share'] for share in shares] diff --git a/manila/common/config.py b/manila/common/config.py index 8d4e80cf5c..f9abe9148e 100644 --- a/manila/common/config.py +++ b/manila/common/config.py @@ -118,6 +118,8 @@ global_opts = [ help='Availability zone of this node.'), cfg.StrOpt('default_share_type', help='Default share type to use.'), + cfg.StrOpt('default_share_group_type', + help='Default share group type to use.'), cfg.ListOpt('memcached_servers', help='Memcached servers or None for in process cache.'), cfg.StrOpt('share_usage_audit_period', diff --git a/manila/db/api.py b/manila/db/api.py index a03a36386d..810011bfb7 100644 --- a/manila/db/api.py +++ b/manila/db/api.py @@ -322,9 +322,10 @@ def share_instances_get_all_by_share(context, share_id): return IMPL.share_instances_get_all_by_share(context, share_id) -def share_instances_get_all_by_consistency_group_id(context, cg_id): - """Returns list of share instances that belong to given cg.""" - return IMPL.share_instances_get_all_by_consistency_group_id(context, cg_id) +def share_instances_get_all_by_share_group_id(context, share_group_id): + """Returns list of share instances that belong to given share group.""" + return IMPL.share_instances_get_all_by_share_group_id( + context, share_group_id) ################### @@ -361,12 +362,12 @@ def share_get_all_by_project(context, project_id, filters=None, ) -def share_get_all_by_consistency_group_id(context, cg_id, - filters=None, sort_key=None, - sort_dir=None): - """Returns all shares with given project ID and CG id.""" - return IMPL.share_get_all_by_consistency_group_id( - context, cg_id, filters=filters, +def share_get_all_by_share_group_id(context, share_group_id, + filters=None, sort_key=None, + sort_dir=None): + """Returns all shares with given project ID and share group id.""" + return IMPL.share_get_all_by_share_group_id( + context, share_group_id, filters=filters, sort_key=sort_key, sort_dir=sort_dir) @@ -894,120 +895,148 @@ def availability_zone_get_all(context): #################### -def consistency_group_get(context, consistency_group_id): - """Get a consistency group or raise if it does not exist.""" - return IMPL.consistency_group_get(context, consistency_group_id) +def share_group_get(context, share_group_id): + """Get a share group or raise if it does not exist.""" + return IMPL.share_group_get(context, share_group_id) -def consistency_group_get_all(context, detailed=True): - """Get all consistency groups.""" - return IMPL.consistency_group_get_all(context, detailed=detailed) +def share_group_get_all(context, detailed=True, filters=None, sort_key=None, + sort_dir=None): + """Get all share groups.""" + return IMPL.share_group_get_all( + context, detailed=detailed, filters=filters, sort_key=sort_key, + sort_dir=sort_dir) -def consistency_group_create(context, values): - """Create a consistency group from the values dictionary.""" - return IMPL.consistency_group_create(context, values) +def share_group_get_all_by_host(context, host, detailed=True, filters=None, + sort_key=None, sort_dir=None): + """Get all share groups belonging to a host.""" + return IMPL.share_group_get_all_by_host( + context, host, detailed=detailed, filters=filters, sort_key=sort_key, + sort_dir=sort_dir) -def consistency_group_get_all_by_share_server(context, share_server_id): - """Get all consistency groups associated with a share server.""" - return IMPL.consistency_group_get_all_by_share_server(context, - share_server_id) +def share_group_create(context, values): + """Create a share group from the values dictionary.""" + return IMPL.share_group_create(context, values) -def consistency_group_get_all_by_project(context, project_id, detailed=True): - """Get all consistency groups belonging to a project.""" - return IMPL.consistency_group_get_all_by_project(context, project_id, - detailed=detailed) +def share_group_get_all_by_share_server(context, share_server_id, + filters=None, sort_key=None, + sort_dir=None): + """Get all share groups associated with a share server.""" + return IMPL.share_group_get_all_by_share_server( + context, share_server_id, filters=filters, sort_key=sort_key, + sort_dir=sort_dir) -def consistency_group_update(context, consistency_group_id, values): - """Set the given properties on a consistency group and update it. +def share_group_get_all_by_project(context, project_id, detailed=True, + filters=None, sort_key=None, + sort_dir=None): + """Get all share groups belonging to a project.""" + return IMPL.share_group_get_all_by_project( + context, project_id, detailed=detailed, filters=filters, + sort_key=sort_key, sort_dir=sort_dir) - Raises NotFound if consistency group does not exist. + +def share_group_update(context, share_group_id, values): + """Set the given properties on a share group and update it. + + Raises NotFound if share group does not exist. """ - return IMPL.consistency_group_update(context, consistency_group_id, values) + return IMPL.share_group_update(context, share_group_id, values) -def consistency_group_destroy(context, consistency_group_id): - """Destroy the consistency group or raise if it does not exist.""" - return IMPL.consistency_group_destroy(context, consistency_group_id) +def share_group_destroy(context, share_group_id): + """Destroy the share group or raise if it does not exist.""" + return IMPL.share_group_destroy(context, share_group_id) -def count_shares_in_consistency_group(context, consistency_group_id): - """Returns the number of undeleted shares with the specified cg.""" - return IMPL.count_shares_in_consistency_group(context, - consistency_group_id) +def count_shares_in_share_group(context, share_group_id): + """Returns the number of undeleted shares with the specified group.""" + return IMPL.count_shares_in_share_group(context, share_group_id) -def count_cgsnapshots_in_consistency_group(context, consistency_group_id): - """Returns the number of undeleted cgsnapshots with the specified cg.""" - return IMPL.count_cgsnapshots_in_consistency_group(context, - consistency_group_id) +def get_all_shares_by_share_group(context, share_group_id): + return IMPL.get_all_shares_by_share_group(context, share_group_id) -def count_consistency_groups_in_share_network(context, share_network_id, - session=None): - """Returns the number of undeleted cgs with the specified share network.""" - return IMPL.count_consistency_groups_in_share_network(context, - share_network_id) +def count_share_group_snapshots_in_share_group(context, share_group_id): + """Returns the number of sg snapshots with the specified share group.""" + return IMPL.count_share_group_snapshots_in_share_group( + context, share_group_id) -def count_cgsnapshot_members_in_share(context, share_id, session=None): - """Returns the number of cgsnapshot members linked to the share.""" - return IMPL.count_cgsnapshot_members_in_share(context, share_id) +def count_share_groups_in_share_network(context, share_network_id, + session=None): + """Return the number of groups with the specified share network.""" + return IMPL.count_share_groups_in_share_network(context, share_network_id) -def cgsnapshot_get(context, cgsnapshot_id): - """Get a cgsnapshot.""" - return IMPL.cgsnapshot_get(context, cgsnapshot_id) +def count_share_group_snapshot_members_in_share(context, share_id, + session=None): + """Returns the number of group snapshot members linked to the share.""" + return IMPL.count_share_group_snapshot_members_in_share(context, share_id) -def cgsnapshot_get_all(context, detailed=True): - """Get all cgsnapshots.""" - return IMPL.cgsnapshot_get_all(context, detailed=detailed) +def share_group_snapshot_get(context, share_group_snapshot_id): + """Get a share group snapshot.""" + return IMPL.share_group_snapshot_get(context, share_group_snapshot_id) -def cgsnapshot_get_all_by_project(context, project_id, detailed=True): - """Get all cgsnapshots belonging to a project.""" - return IMPL.cgsnapshot_get_all_by_project(context, project_id, - detailed=detailed) +def share_group_snapshot_get_all(context, detailed=True, filters=None, + sort_key=None, sort_dir=None): + """Get all share group snapshots.""" + return IMPL.share_group_snapshot_get_all( + context, detailed=detailed, filters=filters, sort_key=sort_key, + sort_dir=sort_dir) -def cgsnapshot_create(context, values): - """Create a cgsnapshot from the values dictionary.""" - return IMPL.cgsnapshot_create(context, values) +def share_group_snapshot_get_all_by_project(context, project_id, detailed=True, + filters=None, sort_key=None, + sort_dir=None): + """Get all share group snapshots belonging to a project.""" + return IMPL.share_group_snapshot_get_all_by_project( + context, project_id, detailed=detailed, filters=filters, + sort_key=sort_key, sort_dir=sort_dir) -def cgsnapshot_update(context, cgsnapshot_id, values): - """Set the given properties on a cgsnapshot and update it. +def share_group_snapshot_create(context, values): + """Create a share group snapshot from the values dictionary.""" + return IMPL.share_group_snapshot_create(context, values) - Raises NotFound if cgsnapshot does not exist. + +def share_group_snapshot_update(context, share_group_snapshot_id, values): + """Set the given properties on a share group snapshot and update it. + + Raises NotFound if share group snapshot does not exist. """ - return IMPL.cgsnapshot_update(context, cgsnapshot_id, values) + return IMPL.share_group_snapshot_update( + context, share_group_snapshot_id, values) -def cgsnapshot_destroy(context, cgsnapshot_id): - """Destroy the cgsnapshot or raise if it does not exist.""" - return IMPL.cgsnapshot_destroy(context, cgsnapshot_id) +def share_group_snapshot_destroy(context, share_group_snapshot_id): + """Destroy the share_group_snapshot or raise if it does not exist.""" + return IMPL.share_group_snapshot_destroy(context, share_group_snapshot_id) -def cgsnapshot_members_get_all(context, cgsnapshot_id): - """Return the members of a cgsnapshot.""" - return IMPL.cgsnapshot_members_get_all(context, cgsnapshot_id) +def share_group_snapshot_members_get_all(context, share_group_snapshot_id): + """Return the members of a share group snapshot.""" + return IMPL.share_group_snapshot_members_get_all( + context, share_group_snapshot_id) -def cgsnapshot_member_create(context, values): - """Create a cgsnapshot member from the values dictionary.""" - return IMPL.cgsnapshot_member_create(context, values) +def share_group_snapshot_member_create(context, values): + """Create a share group snapshot member from the values dictionary.""" + return IMPL.share_group_snapshot_member_create(context, values) -def cgsnapshot_member_update(context, member_id, values): - """Set the given properties on a cgsnapshot member and update it. +def share_group_snapshot_member_update(context, member_id, values): + """Set the given properties on a share group snapshot member and update it. - Raises NotFound if cgsnapshot member does not exist. + Raises NotFound if share_group_snapshot member does not exist. """ - return IMPL.cgsnapshot_member_update(context, member_id, values) + return IMPL.share_group_snapshot_member_update(context, member_id, values) #################### @@ -1063,3 +1092,88 @@ def purge_deleted_records(context, age_in_days): :raises: InvalidParameterValue if age_in_days is incorrect. """ return IMPL.purge_deleted_records(context, age_in_days=age_in_days) + + +#################### + + +def share_group_type_create(context, values, projects=None): + """Create a new share group type.""" + return IMPL.share_group_type_create(context, values, projects) + + +def share_group_type_get_all(context, inactive=False, filters=None): + """Get all share group types. + + :param context: context to query under + :param inactive: Include inactive share group types to the result set + :param filters: Filters for the query in the form of key/value. + :is_public: Filter share group types based on visibility: + + * **True**: List public group types only + * **False**: List private group types only + * **None**: List both public and private group types + + :returns: list of matching share group types + """ + return IMPL.share_group_type_get_all(context, inactive, filters) + + +def share_group_type_get(context, type_id, inactive=False, + expected_fields=None): + """Get share_group type by id. + + :param context: context to query under + :param type_id: group type id to get. + :param inactive: Consider inactive group types when searching + :param expected_fields: Return those additional fields. + Supported fields are: projects. + :returns: share group type + """ + return IMPL.share_group_type_get( + context, type_id, inactive, expected_fields) + + +def share_group_type_get_by_name(context, name): + """Get share group type by name.""" + return IMPL.share_group_type_get_by_name(context, name) + + +def share_group_type_access_get_all(context, type_id): + """Get all share group type access of a share group type.""" + return IMPL.share_group_type_access_get_all(context, type_id) + + +def share_group_type_access_add(context, type_id, project_id): + """Add share group type access for project.""" + return IMPL.share_group_type_access_add(context, type_id, project_id) + + +def share_group_type_access_remove(context, type_id, project_id): + """Remove share group type access for project.""" + return IMPL.share_group_type_access_remove(context, type_id, project_id) + + +def share_group_type_destroy(context, type_id): + """Delete a share group type.""" + return IMPL.share_group_type_destroy(context, type_id) + + +def share_group_type_specs_get(context, type_id): + """Get all group specs for a share group type.""" + return IMPL.share_group_type_specs_get(context, type_id) + + +def share_group_type_specs_delete(context, type_id, key): + """Delete the given group specs item.""" + return IMPL.share_group_type_specs_delete(context, type_id, key) + + +def share_group_type_specs_update_or_create(context, type_id, group_specs): + """Create or update share group type specs. + + This adds or modifies the key/value pairs specified in the group + specs dict argument. + """ + return IMPL.share_group_type_specs_update_or_create( + context, type_id, group_specs) diff --git a/manila/db/migrations/alembic/versions/03da71c0e321_convert_cgs_to_share_groups.py b/manila/db/migrations/alembic/versions/03da71c0e321_convert_cgs_to_share_groups.py new file mode 100644 index 0000000000..236d7f38e8 --- /dev/null +++ b/manila/db/migrations/alembic/versions/03da71c0e321_convert_cgs_to_share_groups.py @@ -0,0 +1,224 @@ +# 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. + +"""Convert consistency groups to share groups + +Revision ID: 03da71c0e321 +Revises: e9f79621d83f +Create Date: 2016-05-19 10:25:17.899008 + +""" + +# revision identifiers, used by Alembic. +revision = "03da71c0e321" +down_revision = "e9f79621d83f" + +from alembic import op +from oslo_log import log +import sqlalchemy as sa +from sqlalchemy import Column, String + +from manila.db.migrations import utils +from manila.i18n import _LI + +LOG = log.getLogger(__name__) + + +def upgrade(): + LOG.info(_LI("Renaming consistency group tables")) + + # Rename tables + op.rename_table("consistency_groups", "share_groups") + op.rename_table("cgsnapshots", "share_group_snapshots") + op.rename_table("cgsnapshot_members", "share_group_snapshot_members") + op.rename_table( + "consistency_group_share_type_mappings", + "share_group_share_type_mappings") + + # Update columns and foreign keys + op.drop_constraint( + "fk_shares_consistency_group_id", "shares", type_="foreignkey") + op.alter_column( + "shares", "consistency_group_id", existing_type=String(36), + existing_nullable=True, new_column_name="share_group_id") + op.alter_column( + "shares", "source_cgsnapshot_member_id", existing_type=String(36), + existing_nullable=True, + new_column_name="source_share_group_snapshot_member_id") + op.create_foreign_key( + "fk_shares_share_group_id", "shares", "share_groups", + ["share_group_id"], ["id"]) + op.drop_constraint( + "fk_cg_share_network_id", "share_groups", type_="foreignkey") + op.drop_constraint( + "fk_cg_share_server_id", "share_groups", type_="foreignkey") + op.alter_column( + "share_groups", "source_cgsnapshot_id", existing_type=String(36), + new_column_name="source_share_group_snapshot_id") + op.create_foreign_key( + "fk_share_group_share_network_id", "share_groups", "share_networks", + ["share_network_id"], ["id"]) + op.create_foreign_key( + "fk_share_group_share_server_id", "share_groups", "share_servers", + ["share_server_id"], ["id"]) + op.drop_constraint( + "fk_cgsnapshots_consistency_group_id", "share_group_snapshots", + type_="foreignkey") + op.alter_column( + "share_group_snapshots", "consistency_group_id", + existing_type=String(36), new_column_name="share_group_id") + op.create_foreign_key( + "fk_share_group_snapshots_share_group_id", "share_group_snapshots", + "share_groups", ["share_group_id"], ["id"]) + op.drop_constraint( + "fk_cgstm_cg_id", "share_group_share_type_mappings", + type_="foreignkey") + op.drop_constraint( + "fk_cgstm_share_type_id", "share_group_share_type_mappings", + type_="foreignkey") + op.alter_column( + "share_group_share_type_mappings", "consistency_group_id", + existing_type=String(36), new_column_name="share_group_id") + op.create_foreign_key( + "fk_sgstm_share_group_id", "share_group_share_type_mappings", + "share_groups", ["share_group_id"], ["id"]) + op.create_foreign_key( + "fk_sgstm_share_type_id", "share_group_share_type_mappings", + "share_types", ["share_type_id"], ["id"]) + op.drop_constraint( + "fk_cgsnapshot_members_cgsnapshot_id", "share_group_snapshot_members", + type_="foreignkey") + op.drop_constraint( + "fk_cgsnapshot_members_share_instance_id", + "share_group_snapshot_members", type_="foreignkey") + op.drop_constraint( + "fk_cgsnapshot_members_share_id", "share_group_snapshot_members", + type_="foreignkey") + op.drop_constraint( + "fk_cgsnapshot_members_share_type_id", "share_group_snapshot_members", + type_="foreignkey") + op.alter_column( + "share_group_snapshot_members", "cgsnapshot_id", + existing_type=String(36), new_column_name="share_group_snapshot_id") + op.create_foreign_key( + "fk_gsm_group_snapshot_id", "share_group_snapshot_members", + "share_group_snapshots", ["share_group_snapshot_id"], ["id"]) + op.create_foreign_key( + "fk_gsm_share_instance_id", "share_group_snapshot_members", + "share_instances", ["share_instance_id"], ["id"]) + op.create_foreign_key( + "fk_gsm_share_id", "share_group_snapshot_members", "shares", + ["share_id"], ["id"]) + op.drop_column("share_group_snapshot_members", "share_type_id") + + +def downgrade(): + meta = sa.MetaData() + meta.bind = op.get_bind() + + # Rename tables + op.rename_table("share_groups", "consistency_groups") + op.rename_table("share_group_snapshots", "cgsnapshots") + op.rename_table("share_group_snapshot_members", "cgsnapshot_members") + op.rename_table( + "share_group_share_type_mappings", + "consistency_group_share_type_mappings") + + # Update columns and foreign keys + op.drop_constraint( + "fk_shares_share_group_id", "shares", type_="foreignkey") + op.alter_column( + "shares", "share_group_id", existing_type=String(36), + new_column_name="consistency_group_id") + op.alter_column( + "shares", "source_share_group_snapshot_member_id", + existing_type=String(36), existing_nullable=True, + new_column_name="source_cgsnapshot_member_id") + op.create_foreign_key( + "fk_shares_consistency_group_id", "shares", "consistency_groups", + ["consistency_group_id"], ["id"]) + op.drop_constraint( + "fk_share_group_share_network_id", "consistency_groups", + type_="foreignkey") + op.drop_constraint( + "fk_share_group_share_server_id", "consistency_groups", + type_="foreignkey") + op.alter_column( + "consistency_groups", "source_share_group_snapshot_id", + existing_type=String(36), new_column_name="source_cgsnapshot_id") + op.create_foreign_key( + "fk_cg_share_network_id", "consistency_groups", "share_networks", + ["share_network_id"], ["id"]) + op.create_foreign_key( + "fk_cg_share_server_id", "consistency_groups", "share_servers", + ["share_server_id"], ["id"]) + op.drop_constraint( + "fk_share_group_snapshots_share_group_id", "cgsnapshots", + type_="foreignkey") + op.alter_column( + "cgsnapshots", "share_group_id", existing_type=String(36), + new_column_name="consistency_group_id") + op.create_foreign_key( + "fk_cgsnapshots_consistency_group_id", "cgsnapshots", + "consistency_groups", ["consistency_group_id"], ["id"]) + op.drop_constraint( + "fk_sgstm_share_group_id", "consistency_group_share_type_mappings", + type_="foreignkey") + op.drop_constraint( + "fk_sgstm_share_type_id", "consistency_group_share_type_mappings", + type_="foreignkey") + op.alter_column( + "consistency_group_share_type_mappings", "share_group_id", + existing_type=String(36), new_column_name="consistency_group_id") + op.create_foreign_key( + "fk_cgstm_cg_id", "consistency_group_share_type_mappings", + "consistency_groups", ["consistency_group_id"], ["id"]) + op.create_foreign_key( + "fk_cgstm_share_type_id", "consistency_group_share_type_mappings", + "share_types", ["share_type_id"], ["id"]) + op.drop_constraint( + "fk_gsm_group_snapshot_id", "cgsnapshot_members", type_="foreignkey") + op.drop_constraint( + "fk_gsm_share_instance_id", "cgsnapshot_members", type_="foreignkey") + op.drop_constraint( + "fk_gsm_share_id", "cgsnapshot_members", type_="foreignkey") + op.alter_column( + "cgsnapshot_members", "share_group_snapshot_id", + existing_type=String(36), new_column_name="cgsnapshot_id") + op.create_foreign_key( + "fk_cgsnapshot_members_cgsnapshot_id", "cgsnapshot_members", + "cgsnapshots", ["cgsnapshot_id"], ["id"]) + op.create_foreign_key( + "fk_cgsnapshot_members_share_instance_id", + "cgsnapshot_members", "share_instances", ["share_instance_id"], ["id"]) + op.create_foreign_key( + "fk_cgsnapshot_members_share_id", "cgsnapshot_members", "shares", + ["share_id"], ["id"]) + op.add_column( + "cgsnapshot_members", + Column('share_type_id', String(36), nullable=True)) + + connection = op.get_bind() + si_table = utils.load_table('share_instances', connection) + member_table = utils.load_table('cgsnapshot_members', connection) + for si_record in connection.execute(si_table.select()): + connection.execute( + member_table.update().where( + member_table.c.share_instance_id == si_record.id, + ).values({"share_type_id": si_record.share_type_id})) + + op.alter_column( + "cgsnapshot_members", + Column('share_type_id', String(36), nullable=False)) + op.create_foreign_key( + "fk_cgsnapshot_members_share_type_id", "cgsnapshot_members", + "share_types", ["share_type_id"], ["id"]) diff --git a/manila/db/migrations/alembic/versions/e1949a93157a_add_share_group_types_table.py b/manila/db/migrations/alembic/versions/e1949a93157a_add_share_group_types_table.py new file mode 100644 index 0000000000..c47b2657af --- /dev/null +++ b/manila/db/migrations/alembic/versions/e1949a93157a_add_share_group_types_table.py @@ -0,0 +1,146 @@ +# 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. + +"""Add share group types table + +Revision ID: e1949a93157a +Revises: 03da71c0e321 +Create Date: 2016-06-01 10:41:06.410945 + +""" + +# revision identifiers, used by Alembic. +revision = 'e1949a93157a' +down_revision = '03da71c0e321' + +from alembic import op +from oslo_log import log +import sqlalchemy as sql + +from manila.i18n import _LE + +LOG = log.getLogger(__name__) + + +def upgrade(): + meta = sql.MetaData() + meta.bind = op.get_bind() + + # Add share group types + try: + op.create_table( + 'share_group_types', + sql.Column( + 'id', sql.String(length=36), primary_key=True, nullable=False), + sql.Column('created_at', sql.DateTime), + sql.Column('updated_at', sql.DateTime), + sql.Column('deleted_at', sql.DateTime), + sql.Column('is_public', sql.Boolean()), + sql.Column('name', sql.String(length=255)), + sql.Column('deleted', sql.String(length=36)), + sql.UniqueConstraint( + 'name', 'deleted', name="uniq_share_group_type_name"), + mysql_engine='InnoDB', + ) + except Exception: + LOG.error(_LE("Table |%s| not created!"), 'share_group_types') + raise + + # Add share group specs + try: + op.create_table( + 'share_group_type_specs', + sql.Column('id', sql.Integer, primary_key=True, nullable=False), + sql.Column('created_at', sql.DateTime), + sql.Column('updated_at', sql.DateTime), + sql.Column('deleted_at', sql.DateTime), + sql.Column('spec_key', sql.String(length=255)), + sql.Column('spec_value', sql.String(length=255)), + sql.Column('deleted', sql.Integer), + sql.Column( + 'share_group_type_id', sql.String(36), + sql.ForeignKey( + 'share_group_types.id', name="sgtp_id_extra_specs_fk")), + mysql_engine='InnoDB', + ) + except Exception: + LOG.error(_LE("Table |%s| not created!"), 'share_group_type_specs') + raise + + # Add share group project types + try: + op.create_table( + 'share_group_type_projects', + sql.Column('id', sql.Integer, primary_key=True, nullable=False), + sql.Column('created_at', sql.DateTime), + sql.Column('updated_at', sql.DateTime), + sql.Column('deleted_at', sql.DateTime), + sql.Column( + 'share_group_type_id', sql.String(36), + sql.ForeignKey('share_group_types.id', name="sgtp_id_fk")), + sql.Column('project_id', sql.String(length=255)), + sql.Column('deleted', sql.Integer), + sql.UniqueConstraint( + 'share_group_type_id', 'project_id', 'deleted', + name="sgtp_project_id_uc"), + mysql_engine='InnoDB', + ) + except Exception: + LOG.error(_LE("Table |%s| not created!"), 'share_group_type_projects') + raise + + # Add mapping between group types and share types + + op.create_table( + 'share_group_type_share_type_mappings', + sql.Column('id', sql.String(36), primary_key=True, nullable=False), + sql.Column('created_at', sql.DateTime), + sql.Column('updated_at', sql.DateTime), + sql.Column('deleted_at', sql.DateTime), + sql.Column('deleted', sql.String(36), default='False'), + sql.Column( + 'share_group_type_id', sql.String(length=36), + sql.ForeignKey('share_group_types.id', name="sgtp_id_sgt_id_uc"), + nullable=False), + sql.Column( + 'share_type_id', sql.String(length=36), + sql.ForeignKey('share_types.id', name="sgtp_id_st_id_uc"), + nullable=False), + mysql_engine='InnoDB', + mysql_charset='utf8') + + # Add share group type for share groups + op.add_column( + 'share_groups', + sql.Column( + 'share_group_type_id', sql.String(36), + sql.ForeignKey('share_group_types.id', name="sgt_id_sg_id_uc"), + ) + ) + + # TODO(ameade): Create type for existing consistency groups + + +def downgrade(): + # Remove share group type for share groups + op.drop_constraint("sgt_id_sg_id_uc", "share_groups", type_="foreignkey") + op.drop_column('share_groups', 'share_group_type_id') + + # Drop mappings + for table_name in ('share_group_type_share_type_mappings', + 'share_group_type_projects', + 'share_group_type_specs', 'share_group_types'): + try: + op.drop_table(table_name) + except Exception: + LOG.error(_LE("%s table not dropped") % table_name) + raise diff --git a/manila/db/sqlalchemy/api.py b/manila/db/sqlalchemy/api.py index bada7429c0..120e824e2e 100644 --- a/manila/db/sqlalchemy/api.py +++ b/manila/db/sqlalchemy/api.py @@ -193,6 +193,18 @@ def require_share_instance_exists(f): return wrapper +def apply_sorting(model, query, sort_key, sort_dir): + if sort_dir.lower() not in ('desc', 'asc'): + msg = _("Wrong sorting data provided: sort key is '%(sort_key)s' " + "and sort direction is '%(sort_dir)s'.") % { + "sort_key": sort_key, "sort_dir": sort_dir} + raise exception.InvalidInput(reason=msg) + + sort_attr = getattr(model, sort_key) + sort_method = getattr(sort_attr, sort_dir.lower()) + return query.order_by(sort_method()) + + def model_query(context, model, *args, **kwargs): """Query helper that accounts for context's `read_deleted` field. @@ -1281,11 +1293,11 @@ def share_instances_get_all_by_share(context, share_id): @require_context -def share_instances_get_all_by_consistency_group_id(context, cg_id): - """Returns list of share instances that belong to given cg.""" +def share_instances_get_all_by_share_group_id(context, share_group_id): + """Returns list of share instances that belong to given share group.""" result = ( model_query(context, models.Share).filter( - models.Share.consistency_group_id == cg_id, + models.Share.share_group_id == share_group_id, ).all() ) instances = [] @@ -1533,7 +1545,7 @@ def share_get(context, share_id, session=None): def _share_get_all_with_filters(context, project_id=None, share_server_id=None, - consistency_group_id=None, filters=None, + share_group_id=None, filters=None, is_public=False, sort_key=None, sort_dir=None): """Returns sorted list of shares that satisfies filters. @@ -1570,9 +1582,9 @@ def _share_get_all_with_filters(context, project_id=None, share_server_id=None, query = query.filter( models.ShareInstance.share_server_id == share_server_id) - if consistency_group_id: + if share_group_id: query = query.filter( - models.Share.consistency_group_id == consistency_group_id) + models.Share.share_group_id == share_group_id) # Apply filters if not filters: @@ -1591,23 +1603,12 @@ def _share_get_all_with_filters(context, project_id=None, share_server_id=None, query = query.filter(or_(models.ShareTypeExtraSpecs.key == k, models.ShareTypeExtraSpecs.value == v)) - # Apply sorting - if sort_dir.lower() not in ('desc', 'asc'): - msg = _("Wrong sorting data provided: sort key is '%(sort_key)s' " - "and sort direction is '%(sort_dir)s'.") % { - "sort_key": sort_key, "sort_dir": sort_dir} - raise exception.InvalidInput(reason=msg) - - def apply_sorting(model, query): - sort_attr = getattr(model, sort_key) - sort_method = getattr(sort_attr, sort_dir.lower()) - return query.order_by(sort_method()) - try: - query = apply_sorting(models.Share, query) + query = apply_sorting(models.Share, query, sort_key, sort_dir) except AttributeError: try: - query = apply_sorting(models.ShareInstance, query) + query = apply_sorting( + models.ShareInstance, query, sort_key, sort_dir) except AttributeError: msg = _("Wrong sorting key provided - '%s'.") % sort_key raise exception.InvalidInput(reason=msg) @@ -1636,12 +1637,12 @@ def share_get_all_by_project(context, project_id, filters=None, @require_context -def share_get_all_by_consistency_group_id(context, cg_id, - filters=None, sort_key=None, - sort_dir=None): - """Returns list of shares with given CG ID.""" +def share_get_all_by_share_group_id(context, share_group_id, + filters=None, sort_key=None, + sort_dir=None): + """Returns list of shares with given group ID.""" query = _share_get_all_with_filters( - context, consistency_group_id=cg_id, + context, share_group_id=share_group_id, filters=filters, sort_key=sort_key, sort_dir=sort_dir, ) return query @@ -2877,7 +2878,7 @@ def share_server_get_all_unused_deletable(context, host, updated_before): ) result = _server_get_query(context)\ .filter_by(host=host)\ - .filter(~models.ShareServer.consistency_groups.any())\ + .filter(~models.ShareServer.share_groups.any())\ .filter(~models.ShareServer.share_instances.any())\ .filter(models.ShareServer.status.in_(valid_server_status))\ .filter(models.ShareServer.updated_at < updated_before).all() @@ -3085,21 +3086,22 @@ def network_allocation_update(context, id, values): ################### -def _dict_with_extra_specs(inst_type_query): +def _dict_with_specs(inst_type_query, specs_key='extra_specs'): """Convert type query result to dict with extra_spec and rate_limit. -Takes a volume type query returned by sqlalchemy and returns it -as a dictionary, converting the extra_specs entry from a list -of dicts: + Takes a share [group] type query returned by sqlalchemy and returns it + as a dictionary, converting the extra/group specs entry from a list + of dicts: -'extra_specs' : [{'key': 'k1', 'value': 'v1', ...}, ...] -to a single dict: -'extra_specs' : {'k1': 'v1'} -""" + 'extra_specs' : [{'key': 'k1', 'value': 'v1', ...}, ...] + 'group_specs' : [{'key': 'k1', 'value': 'v1', ...}, ...] + to a single dict: + 'extra_specs' : {'k1': 'v1'} + 'group_specs' : {'k1': 'v1'} + """ inst_type_dict = dict(inst_type_query) - extra_specs = {x['key']: x['value'] - for x in inst_type_query['extra_specs']} - inst_type_dict['extra_specs'] = extra_specs + specs = {x['key']: x['value'] for x in inst_type_query[specs_key]} + inst_type_dict[specs_key] = specs return inst_type_dict @@ -3186,7 +3188,7 @@ def share_type_get_all(context, inactive=False, filters=None): result = {} for row in rows: - result[row['name']] = _dict_with_extra_specs(row) + result[row['name']] = _dict_with_specs(row) return result @@ -3217,7 +3219,7 @@ def _share_type_get(context, id, session=None, inactive=False, if not result: raise exception.ShareTypeNotFound(share_type_id=id) - share_type = _dict_with_extra_specs(result) + share_type = _dict_with_specs(result) if 'projects' in expected_fields: share_type['projects'] = [p['project_id'] for p in result['projects']] @@ -3243,7 +3245,7 @@ def _share_type_get_by_name(context, name, session=None): if not result: raise exception.ShareTypeNotFoundByName(share_type_name=name) - return _dict_with_extra_specs(result) + return _dict_with_specs(result) @require_context @@ -3261,12 +3263,13 @@ def share_type_destroy(context, id): results = model_query(context, models.ShareInstance, session=session, read_deleted="no").\ filter_by(share_type_id=id).count() - cg_count = model_query(context, - models.ConsistencyGroupShareTypeMapping, - read_deleted="no", - session=session).\ - filter_by(share_type_id=id).count() - if results or cg_count: + share_group_count = model_query( + context, + models.ShareGroupShareTypeMapping, + read_deleted="no", + session=session, + ).filter_by(share_type_id=id).count() + if results or share_group_count: LOG.error(_LE('ShareType %s deletion failed, ShareType in use.'), id) raise exception.ShareTypeInUse(share_type_id=id) @@ -3458,311 +3461,6 @@ def availability_zone_get_all(context): ).all() -#################### - - -def _consistency_group_get(context, consistency_group_id, session=None): - session = session or get_session() - result = model_query(context, models.ConsistencyGroup, - session=session, - project_only=True, - read_deleted='no').\ - filter_by(id=consistency_group_id).\ - options(joinedload('share_types')).\ - first() - - if not result: - raise exception.ConsistencyGroupNotFound( - consistency_group_id=consistency_group_id) - - return result - - -@require_context -def consistency_group_get(context, consistency_group_id, session=None): - return _consistency_group_get(context, consistency_group_id, - session=session) - - -def _consistency_group_get_all_query(context, session=None): - session = session or get_session() - return model_query(context, models.ConsistencyGroup, session=session, - read_deleted='no') - - -@require_admin_context -def consistency_group_get_all(context, detailed=True): - query = _consistency_group_get_all_query(context) - if detailed: - return query.options(joinedload('share_types')).all() - else: - query = query.with_entities(models.ConsistencyGroup.id, - models.ConsistencyGroup.name) - values = [] - for item in query.all(): - id, name = item - values.append({"id": id, "name": name}) - return values - - -@require_context -def consistency_group_get_all_by_project(context, project_id, detailed=True): - authorize_project_context(context, project_id) - query = _consistency_group_get_all_query(context).filter_by( - project_id=project_id) - if detailed: - return query.options(joinedload('share_types')).all() - else: - query = query.with_entities(models.ConsistencyGroup.id, - models.ConsistencyGroup.name) - values = [] - for item in query.all(): - id, name = item - values.append({"id": id, "name": name}) - return values - - -@require_context -def consistency_group_get_all_by_share_server(context, share_server_id): - return _consistency_group_get_all_query(context).filter_by( - share_server_id=share_server_id).all() - - -@require_context -def consistency_group_create(context, values): - consistency_group = models.ConsistencyGroup() - if not values.get('id'): - values['id'] = six.text_type(uuidutils.generate_uuid()) - - mappings = [] - for item in values.get('share_types') or []: - mapping = models.ConsistencyGroupShareTypeMapping() - mapping['id'] = six.text_type(uuidutils.generate_uuid()) - mapping['share_type_id'] = item - mapping['consistency_group_id'] = values['id'] - mappings.append(mapping) - - values['share_types'] = mappings - - session = get_session() - with session.begin(): - consistency_group.update(values) - session.add(consistency_group) - - return _consistency_group_get(context, values['id'], session=session) - - -@require_context -def consistency_group_update(context, consistency_group_id, values): - session = get_session() - with session.begin(): - cg_ref = _consistency_group_get(context, consistency_group_id, - session=session) - - cg_ref.update(values) - cg_ref.save(session=session) - return cg_ref - - -@require_admin_context -def consistency_group_destroy(context, consistency_group_id): - session = get_session() - with session.begin(): - cg_ref = _consistency_group_get(context, consistency_group_id, - session=session) - cg_ref.soft_delete(session) - - session.query(models.ConsistencyGroupShareTypeMapping).\ - filter_by(consistency_group_id=cg_ref['id']).soft_delete() - - -@require_context -def count_shares_in_consistency_group(context, consistency_group_id, - session=None): - session = session or get_session() - return model_query( - context, models.Share, session=session, - project_only=True, read_deleted="no").\ - filter_by(consistency_group_id=consistency_group_id).\ - count() - - -@require_context -def count_cgsnapshots_in_consistency_group(context, consistency_group_id, - session=None): - session = session or get_session() - return model_query( - context, models.CGSnapshot, session=session, - project_only=True, read_deleted="no").\ - filter_by(consistency_group_id=consistency_group_id).\ - count() - - -@require_context -def count_consistency_groups_in_share_network(context, share_network_id, - session=None): - session = session or get_session() - return model_query( - context, models.ConsistencyGroup, session=session, - project_only=True, read_deleted="no").\ - filter_by(share_network_id=share_network_id).\ - count() - - -@require_context -def count_cgsnapshot_members_in_share(context, share_id, session=None): - session = session or get_session() - return model_query( - context, models.CGSnapshotMember, session=session, - project_only=True, read_deleted="no").\ - filter_by(share_id=share_id).\ - count() - - -def _cgsnapshot_get(context, cgsnapshot_id, session=None): - session = session or get_session() - result = model_query(context, models.CGSnapshot, session=session, - project_only=True, read_deleted='no').\ - options(joinedload('cgsnapshot_members')).\ - options(joinedload('consistency_group')).\ - filter_by(id=cgsnapshot_id).\ - first() - - if not result: - raise exception.CGSnapshotNotFound(cgsnapshot_id=cgsnapshot_id) - - return result - - -def _cgsnapshot_get_all_query(context, session=None): - session = session or get_session() - return model_query(context, models.CGSnapshot, session=session, - reade_deleted='no').\ - options(joinedload('cgsnapshot_members')).\ - options(joinedload('consistency_group')) - - -@require_context -def cgsnapshot_get(context, cgsnapshot_id, session=None): - session = session or get_session() - return _cgsnapshot_get(context, cgsnapshot_id, session=session) - - -@require_admin_context -def cgsnapshot_get_all(context, detailed=True): - query = _cgsnapshot_get_all_query(context) - if detailed: - return query.all() - else: - query = query.with_entities(models.CGSnapshot.id, - models.CGSnapshot.name) - values = [] - for item in query.all(): - id, name = item - values.append({"id": id, "name": name}) - return values - - -@require_context -def cgsnapshot_get_all_by_project(context, project_id, detailed=True): - authorize_project_context(context, project_id) - query = _cgsnapshot_get_all_query(context).filter_by( - project_id=project_id) - if detailed: - return query.all() - else: - query = query.with_entities(models.CGSnapshot.id, - models.CGSnapshot.name) - values = [] - for item in query.all(): - id, name = item - values.append({"id": id, "name": name}) - return values - - -@require_context -def cgsnapshot_create(context, values): - cgsnapshot = models.CGSnapshot() - if not values.get('id'): - values['id'] = six.text_type(uuidutils.generate_uuid()) - - session = get_session() - with session.begin(): - cgsnapshot.update(values) - session.add(cgsnapshot) - - return _cgsnapshot_get(context, values['id'], session=session) - - -@require_context -def cgsnapshot_update(context, cgsnapshot_id, values): - session = get_session() - with session.begin(): - cg_ref = _cgsnapshot_get(context, cgsnapshot_id, session=session) - - cg_ref.update(values) - cg_ref.save(session=session) - return cg_ref - - -@require_admin_context -def cgsnapshot_destroy(context, cgsnapshot_id): - session = get_session() - with session.begin(): - cgsnap_ref = _cgsnapshot_get(context, cgsnapshot_id, session=session) - cgsnap_ref.soft_delete(session) - - session.query(models.CGSnapshotMember).\ - filter_by(cgsnapshot_id=cgsnapshot_id).soft_delete() - - -@require_context -def cgsnapshot_members_get_all(context, cgsnapshot_id, session=None): - session = session or get_session() - query = model_query(context, models.CGSnapshotMember, - session=session, read_deleted='no').filter_by( - cgsnapshot_id=cgsnapshot_id) - return query.all() - - -@require_context -def cgsnapshot_member_get(context, member_id, session=None): - result = model_query(context, models.CGSnapshotMember, session=session, - project_only=True, read_deleted='no').\ - filter_by(id=member_id).\ - first() - - if not result: - raise exception.CGSnapshotMemberNotFound(member_id=member_id) - - return result - - -@require_context -def cgsnapshot_member_create(context, values): - member = models.CGSnapshotMember() - if not values.get('id'): - values['id'] = six.text_type(uuidutils.generate_uuid()) - - session = get_session() - with session.begin(): - member.update(values) - session.add(member) - - return cgsnapshot_member_get(context, values['id'], session=session) - - -@require_context -def cgsnapshot_member_update(context, member_id, values): - session = get_session() - with session.begin(): - member = cgsnapshot_member_get(context, member_id, session=session) - member.update(values) - session.add(member) - - return cgsnapshot_member_get(context, member_id, session=session) - - @require_admin_context def purge_deleted_records(context, age_in_days): """Purge soft-deleted records older than(and equal) age from tables.""" @@ -3810,3 +3508,696 @@ def purge_deleted_records(context, age_in_days): LOG.warning(_LW("Querying table %s's soft-deleted records " "failed, skipping."), table) session.commit() + + +#################### + + +def _share_group_get(context, share_group_id, session=None): + session = session or get_session() + result = model_query(context, models.ShareGroup, + session=session, + project_only=True, + read_deleted='no').\ + filter_by(id=share_group_id).\ + options(joinedload('share_types')).\ + first() + + if not result: + raise exception.ShareGroupNotFound(share_group_id=share_group_id) + + return result + + +@require_context +def share_group_get(context, share_group_id, session=None): + return _share_group_get(context, share_group_id, session=session) + + +def _share_group_get_all(context, project_id=None, share_server_id=None, + host=None, detailed=True, filters=None, + sort_key=None, sort_dir=None, session=None): + session = session or get_session() + sort_key = sort_key or 'created_at' + sort_dir = sort_dir or 'desc' + + query = model_query( + context, models.ShareGroup, session=session, read_deleted='no') + + # Apply filters + if not filters: + filters = {} + no_key = 'key_is_absent' + for k, v in filters.items(): + filter_attr = getattr(models.ShareGroup, k, no_key) + if filter_attr == no_key: + msg = _("Share groups cannot be filtered using '%s' key.") + raise exception.InvalidInput(reason=msg % k) + query = query.filter(filter_attr == v) + + if project_id: + query = query.filter( + models.ShareGroup.project_id == project_id) + if host: + query = query.filter( + models.ShareGroup.host == host) + if share_server_id: + query = query.filter( + models.ShareGroup.share_server_id == share_server_id) + + try: + query = apply_sorting(models.ShareGroup, query, sort_key, sort_dir) + except AttributeError: + msg = _("Wrong sorting key provided - '%s'.") % sort_key + raise exception.InvalidInput(reason=msg) + + if detailed: + return query.options(joinedload('share_types')).all() + else: + query = query.with_entities( + models.ShareGroup.id, models.ShareGroup.name) + values = [] + for sg_id, sg_name in query.all(): + values.append({"id": sg_id, "name": sg_name}) + return values + + +@require_admin_context +def share_group_get_all(context, detailed=True, filters=None, sort_key=None, + sort_dir=None): + return _share_group_get_all( + context, detailed=detailed, filters=filters, + sort_key=sort_key, sort_dir=sort_dir) + + +@require_admin_context +def share_group_get_all_by_host(context, host, detailed=True): + return _share_group_get_all(context, host=host, detailed=detailed) + + +@require_context +def share_group_get_all_by_project(context, project_id, detailed=True, + filters=None, sort_key=None, sort_dir=None): + authorize_project_context(context, project_id) + return _share_group_get_all( + context, project_id=project_id, detailed=detailed, filters=filters, + sort_key=sort_key, sort_dir=sort_dir) + + +@require_context +def share_group_get_all_by_share_server(context, share_server_id, filters=None, + sort_key=None, sort_dir=None): + return _share_group_get_all( + context, share_server_id=share_server_id, filters=filters, + sort_key=sort_key, sort_dir=sort_dir) + + +@require_context +def share_group_create(context, values): + share_group = models.ShareGroup() + if not values.get('id'): + values['id'] = six.text_type(uuidutils.generate_uuid()) + + mappings = [] + for item in values.get('share_types') or []: + mapping = models.ShareGroupShareTypeMapping() + mapping['id'] = six.text_type(uuidutils.generate_uuid()) + mapping['share_type_id'] = item + mapping['share_group_id'] = values['id'] + mappings.append(mapping) + + values['share_types'] = mappings + + session = get_session() + with session.begin(): + share_group.update(values) + session.add(share_group) + + return _share_group_get(context, values['id'], session=session) + + +@require_context +def share_group_update(context, share_group_id, values): + session = get_session() + with session.begin(): + share_group_ref = _share_group_get( + context, share_group_id, session=session) + share_group_ref.update(values) + share_group_ref.save(session=session) + return share_group_ref + + +@require_admin_context +def share_group_destroy(context, share_group_id): + session = get_session() + with session.begin(): + share_group_ref = _share_group_get( + context, share_group_id, session=session) + share_group_ref.soft_delete(session) + session.query(models.ShareGroupShareTypeMapping).filter_by( + share_group_id=share_group_ref['id']).soft_delete() + + +@require_context +def count_shares_in_share_group(context, share_group_id, session=None): + session = session or get_session() + return model_query( + context, models.Share, session=session, + project_only=True, read_deleted="no").\ + filter_by(share_group_id=share_group_id).\ + count() + + +@require_context +def get_all_shares_by_share_group(context, share_group_id, session=None): + session = session or get_session() + return model_query( + context, models.Share, session=session, + project_only=True, read_deleted="no").\ + filter_by(share_group_id=share_group_id).\ + all() + + +@require_context +def count_share_group_snapshots_in_share_group(context, share_group_id, + session=None): + session = session or get_session() + return model_query( + context, models.ShareGroupSnapshot, session=session, + project_only=True, read_deleted="no", + ).filter_by( + share_group_id=share_group_id, + ).count() + + +@require_context +def count_share_groups_in_share_network(context, share_network_id, + session=None): + session = session or get_session() + return model_query( + context, models.ShareGroup, session=session, + project_only=True, read_deleted="no").\ + filter_by(share_network_id=share_network_id).\ + count() + + +@require_context +def count_share_group_snapshot_members_in_share(context, share_id, + session=None): + session = session or get_session() + return model_query( + context, models.ShareGroupSnapshotMember, session=session, + project_only=True, read_deleted="no", + ).filter_by( + share_id=share_id, + ).count() + + +@require_context +def _share_group_snapshot_get(context, share_group_snapshot_id, session=None): + session = session or get_session() + result = model_query( + context, models.ShareGroupSnapshot, session=session, + project_only=True, read_deleted='no', + ).options( + joinedload('share_group'), + joinedload('share_group_snapshot_members'), + ).filter_by( + id=share_group_snapshot_id, + ).first() + + if not result: + raise exception.ShareGroupSnapshotNotFound( + share_group_snapshot_id=share_group_snapshot_id) + + return result + + +def _share_group_snapshot_get_all( + context, project_id=None, detailed=True, filters=None, + sort_key=None, sort_dir=None, session=None): + session = session or get_session() + if not sort_key: + sort_key = 'created_at' + if not sort_dir: + sort_dir = 'desc' + + query = model_query( + context, models.ShareGroupSnapshot, session=session, read_deleted='no', + ).options( + joinedload('share_group'), + joinedload('share_group_snapshot_members'), + ) + + # Apply filters + if not filters: + filters = {} + no_key = 'key_is_absent' + for k, v in filters.items(): + filter_attr = getattr(models.ShareGroupSnapshot, k, no_key) + if filter_attr == no_key: + msg = _("Share group snapshots cannot be filtered using '%s' key.") + raise exception.InvalidInput(reason=msg % k) + query = query.filter(filter_attr == v) + + if project_id: + query = query.filter( + models.ShareGroupSnapshot.project_id == project_id) + + try: + query = apply_sorting( + models.ShareGroupSnapshot, query, sort_key, sort_dir) + except AttributeError: + msg = _("Wrong sorting key provided - '%s'.") % sort_key + raise exception.InvalidInput(reason=msg) + + if detailed: + return query.all() + else: + query = query.with_entities(models.ShareGroupSnapshot.id, + models.ShareGroupSnapshot.name) + values = [] + for sgs_id, sgs_name in query.all(): + values.append({"id": sgs_id, "name": sgs_name}) + return values + + +@require_context +def share_group_snapshot_get(context, share_group_snapshot_id, session=None): + session = session or get_session() + return _share_group_snapshot_get( + context, share_group_snapshot_id, session=session) + + +@require_admin_context +def share_group_snapshot_get_all( + context, detailed=True, filters=None, sort_key=None, sort_dir=None): + return _share_group_snapshot_get_all( + context, filters=filters, detailed=detailed, + sort_key=sort_key, sort_dir=sort_dir) + + +@require_context +def share_group_snapshot_get_all_by_project( + context, project_id, detailed=True, filters=None, + sort_key=None, sort_dir=None): + authorize_project_context(context, project_id) + return _share_group_snapshot_get_all( + context, project_id=project_id, filters=filters, detailed=detailed, + sort_key=sort_key, sort_dir=sort_dir, + ) + + +@require_context +def share_group_snapshot_create(context, values): + share_group_snapshot = models.ShareGroupSnapshot() + if not values.get('id'): + values['id'] = six.text_type(uuidutils.generate_uuid()) + + session = get_session() + with session.begin(): + share_group_snapshot.update(values) + session.add(share_group_snapshot) + + return _share_group_snapshot_get( + context, values['id'], session=session) + + +@require_context +def share_group_snapshot_update(context, share_group_snapshot_id, values): + session = get_session() + with session.begin(): + share_group_ref = _share_group_snapshot_get( + context, share_group_snapshot_id, session=session) + share_group_ref.update(values) + share_group_ref.save(session=session) + return share_group_ref + + +@require_admin_context +def share_group_snapshot_destroy(context, share_group_snapshot_id): + session = get_session() + with session.begin(): + share_group_snap_ref = _share_group_snapshot_get( + context, share_group_snapshot_id, session=session) + share_group_snap_ref.soft_delete(session) + session.query(models.ShareGroupSnapshotMember).filter_by( + share_group_snapshot_id=share_group_snapshot_id).soft_delete() + + +@require_context +def share_group_snapshot_members_get_all(context, share_group_snapshot_id, + session=None): + session = session or get_session() + query = model_query( + context, models.ShareGroupSnapshotMember, session=session, + read_deleted='no', + ).filter_by(share_group_snapshot_id=share_group_snapshot_id) + return query.all() + + +@require_context +def share_group_snapshot_member_get(context, member_id, session=None): + result = model_query( + context, models.ShareGroupSnapshotMember, session=session, + project_only=True, read_deleted='no', + ).filter_by(id=member_id).first() + if not result: + raise exception.ShareGroupSnapshotMemberNotFound(member_id=member_id) + return result + + +@require_context +def share_group_snapshot_member_create(context, values): + member = models.ShareGroupSnapshotMember() + if not values.get('id'): + values['id'] = six.text_type(uuidutils.generate_uuid()) + + session = get_session() + with session.begin(): + member.update(values) + session.add(member) + + return share_group_snapshot_member_get( + context, values['id'], session=session) + + +@require_context +def share_group_snapshot_member_update(context, member_id, values): + session = get_session() + with session.begin(): + member = share_group_snapshot_member_get( + context, member_id, session=session) + member.update(values) + session.add(member) + return share_group_snapshot_member_get( + context, member_id, session=session) + + +#################### + + +@require_admin_context +def share_group_type_create(context, values, projects=None): + """Create a new share group type. + + In order to pass in group specs, the values dict should contain a + 'group_specs' key/value pair: + {'group_specs' : {'k1': 'v1', 'k2': 'v2', ...}} + """ + values = ensure_model_dict_has_id(values) + + projects = projects or [] + + session = get_session() + with session.begin(): + try: + values['group_specs'] = _metadata_refs( + values.get('group_specs'), models.ShareGroupTypeSpecs) + mappings = [] + for item in values.get('share_types') or []: + mapping = models.ShareGroupTypeShareTypeMapping() + mapping['id'] = six.text_type(uuidutils.generate_uuid()) + mapping['share_type_id'] = item + mapping['share_group_type_id'] = values['id'] + mappings.append(mapping) + + values['share_types'] = mappings + share_group_type_ref = models.ShareGroupTypes() + share_group_type_ref.update(values) + share_group_type_ref.save(session=session) + except db_exception.DBDuplicateEntry: + raise exception.ShareGroupTypeExists(type_id=values['name']) + except Exception as e: + raise db_exception.DBError(e) + + for project in set(projects): + access_ref = models.ShareGroupTypeProjects() + access_ref.update({"share_group_type_id": share_group_type_ref.id, + "project_id": project}) + access_ref.save(session=session) + + return share_group_type_ref + + +def _share_group_type_get_query(context, session=None, read_deleted=None, + expected_fields=None): + expected_fields = expected_fields or [] + query = model_query( + context, models.ShareGroupTypes, session=session, + read_deleted=read_deleted + ).options( + joinedload('group_specs'), + joinedload('share_types'), + ) + + if 'projects' in expected_fields: + query = query.options(joinedload('projects')) + + if not context.is_admin: + the_filter = [models.ShareGroupTypes.is_public == true()] + projects_attr = getattr(models.ShareGroupTypes, 'projects') + the_filter.extend([ + projects_attr.any(project_id=context.project_id) + ]) + query = query.filter(or_(*the_filter)) + + return query + + +@require_context +def share_group_type_get_all(context, inactive=False, filters=None): + """Returns a dict describing all share group types with name as key.""" + filters = filters or {} + read_deleted = "yes" if inactive else "no" + query = _share_group_type_get_query(context, read_deleted=read_deleted) + + if 'is_public' in filters and filters['is_public'] is not None: + the_filter = [models.ShareGroupTypes.is_public == filters['is_public']] + if filters['is_public'] and context.project_id is not None: + projects_attr = getattr(models. ShareGroupTypes, 'projects') + the_filter.extend([ + projects_attr.any( + project_id=context.project_id, deleted=0) + ]) + if len(the_filter) > 1: + query = query.filter(or_(*the_filter)) + else: + query = query.filter(the_filter[0]) + + rows = query.order_by("name").all() + + result = {} + for row in rows: + result[row['name']] = _dict_with_specs(row, 'group_specs') + + return result + + +def _share_group_type_get_id_from_share_group_type_query(context, type_id, + session=None): + return model_query( + context, models.ShareGroupTypes, read_deleted="no", session=session, + ).filter_by(id=type_id) + + +def _share_group_type_get_id_from_share_group_type(context, type_id, + session=None): + result = _share_group_type_get_id_from_share_group_type_query( + context, type_id, session=session).first() + if not result: + raise exception.ShareGroupTypeNotFound(type_id=type_id) + return result['id'] + + +@require_context +def _share_group_type_get(context, type_id, session=None, inactive=False, + expected_fields=None): + expected_fields = expected_fields or [] + read_deleted = "yes" if inactive else "no" + result = _share_group_type_get_query( + context, session, read_deleted, expected_fields, + ).filter_by(id=type_id).first() + + if not result: + raise exception.ShareGroupTypeNotFound(type_id=type_id) + + share_group_type = _dict_with_specs(result, 'group_specs') + + if 'projects' in expected_fields: + share_group_type['projects'] = [ + p['project_id'] for p in result['projects']] + + return share_group_type + + +@require_context +def share_group_type_get(context, type_id, inactive=False, + expected_fields=None): + """Return a dict describing specific share group type.""" + return _share_group_type_get( + context, type_id, session=None, inactive=inactive, + expected_fields=expected_fields) + + +@require_context +def _share_group_type_get_by_name(context, name, session=None): + result = model_query( + context, models.ShareGroupTypes, session=session, + ).options( + joinedload('group_specs'), + joinedload('share_types'), + ).filter_by( + name=name, + ).first() + if not result: + raise exception.ShareGroupTypeNotFoundByName(type_name=name) + return _dict_with_specs(result, 'group_specs') + + +@require_context +def share_group_type_get_by_name(context, name): + """Return a dict describing specific share group type.""" + return _share_group_type_get_by_name(context, name) + + +@require_admin_context +def share_group_type_destroy(context, type_id): + session = get_session() + with session.begin(): + _share_group_type_get(context, type_id, session) + results = model_query( + context, models.ShareGroup, session=session, read_deleted="no", + ).filter_by( + share_group_type_id=type_id, + ).count() + if results: + LOG.error(_LE('Share group type %s deletion failed, it in use.'), + type_id) + raise exception.ShareGroupTypeInUse(type_id=type_id) + model_query( + context, models.ShareGroupTypeSpecs, session=session, + ).filter_by( + share_group_type_id=type_id, + ).soft_delete() + model_query( + context, models.ShareGroupTypes, session=session + ).filter_by( + id=type_id, + ).soft_delete() + + +def _share_group_type_access_query(context, session=None): + return model_query(context, models.ShareGroupTypeProjects, session=session, + read_deleted="no") + + +@require_admin_context +def share_group_type_access_get_all(context, type_id): + share_group_type_id = _share_group_type_get_id_from_share_group_type( + context, type_id) + return _share_group_type_access_query(context).filter_by( + share_group_type_id=share_group_type_id, + ).all() + + +@require_admin_context +def share_group_type_access_add(context, type_id, project_id): + """Add given tenant to the share group type access list.""" + share_group_type_id = _share_group_type_get_id_from_share_group_type( + context, type_id) + access_ref = models.ShareGroupTypeProjects() + access_ref.update({"share_group_type_id": share_group_type_id, + "project_id": project_id}) + session = get_session() + with session.begin(): + try: + access_ref.save(session=session) + except db_exception.DBDuplicateEntry: + raise exception.ShareGroupTypeAccessExists( + type_id=share_group_type_id, project_id=project_id) + return access_ref + + +@require_admin_context +def share_group_type_access_remove(context, type_id, project_id): + """Remove given tenant from the share group type access list.""" + share_group_type_id = _share_group_type_get_id_from_share_group_type( + context, type_id) + count = _share_group_type_access_query(context).filter_by( + share_group_type_id=share_group_type_id, + ).filter_by( + project_id=project_id, + ).soft_delete( + synchronize_session=False, + ) + if count == 0: + raise exception.ShareGroupTypeAccessNotFound( + type_id=share_group_type_id, project_id=project_id) + + +def _share_group_type_specs_query(context, type_id, session=None): + return model_query( + context, models.ShareGroupTypeSpecs, session=session, read_deleted="no" + ).filter_by( + share_group_type_id=type_id, + ).options( + joinedload('share_group_type'), + ) + + +@require_context +def share_group_type_specs_get(context, type_id): + rows = _share_group_type_specs_query(context, type_id).all() + result = {} + for row in rows: + result[row['key']] = row['value'] + return result + + +@require_context +def share_group_type_specs_delete(context, type_id, key): + session = get_session() + with session.begin(): + _share_group_type_specs_get_item(context, type_id, key, session) + _share_group_type_specs_query( + context, type_id, session, + ).filter_by( + key=key, + ).soft_delete() + + +@require_context +def _share_group_type_specs_get_item(context, type_id, key, session=None): + result = _share_group_type_specs_query( + context, type_id, session=session, + ).filter_by( + key=key, + ).options( + joinedload('share_group_type'), + ).first() + + if not result: + raise exception.ShareGroupTypeSpecsNotFound( + specs_key=key, type_id=type_id) + + return result + + +@require_context +@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True) +def share_group_type_specs_update_or_create(context, type_id, specs): + session = get_session() + with session.begin(): + spec_ref = None + for key, value in specs.items(): + try: + spec_ref = _share_group_type_specs_get_item( + context, type_id, key, session) + except exception.ShareGroupTypeSpecsNotFound: + spec_ref = models.ShareGroupTypeSpecs() + spec_ref.update({"key": key, "value": value, + "share_group_type_id": type_id, "deleted": 0}) + spec_ref.save(session=session) + + return specs diff --git a/manila/db/sqlalchemy/models.py b/manila/db/sqlalchemy/models.py index b7270f4c24..5a870c6a77 100644 --- a/manila/db/sqlalchemy/models.py +++ b/manila/db/sqlalchemy/models.py @@ -308,11 +308,11 @@ class Share(BASE, ManilaBase): replication_type = Column(String(255), nullable=True) share_proto = Column(String(255)) is_public = Column(Boolean, default=False) - consistency_group_id = Column(String(36), - ForeignKey('consistency_groups.id'), - nullable=True) + share_group_id = Column(String(36), + ForeignKey('share_groups.id'), + nullable=True) - source_cgsnapshot_member_id = Column(String(36), nullable=True) + source_share_group_snapshot_member_id = Column(String(36), nullable=True) task_state = Column(String(255)) instances = orm.relationship( "ShareInstance", @@ -335,8 +335,8 @@ class ShareInstance(BASE, ManilaBase): _proxified_properties = ('user_id', 'project_id', 'size', 'display_name', 'display_description', 'snapshot_id', 'share_proto', 'is_public', - 'consistency_group_id', 'replication_type', - 'source_cgsnapshot_member_id') + 'share_group_id', 'replication_type', + 'source_share_group_snapshot_member_id') def set_share_data(self, share): for share_property in self._proxified_properties: @@ -832,10 +832,10 @@ class ShareServer(BASE, ManilaBase): 'ShareServer.id == ShareInstance.share_server_id,' 'ShareInstance.deleted == "False")') - consistency_groups = orm.relationship( - "ConsistencyGroup", backref='share_server', primaryjoin='and_(' - 'ShareServer.id == ConsistencyGroup.share_server_id,' - 'ConsistencyGroup.deleted == "False")') + share_groups = orm.relationship( + "ShareGroup", backref='share_server', primaryjoin='and_(' + 'ShareServer.id == ShareGroup.share_server_id,' + 'ShareGroup.deleted == "False")') _backend_details = orm.relationship( "ShareServerBackendDetails", @@ -913,95 +913,168 @@ class AvailabilityZone(BASE, ManilaBase): name = Column(String(255), nullable=False) -class ConsistencyGroup(BASE, ManilaBase): - """Represents a consistency group.""" - __tablename__ = 'consistency_groups' +class ShareGroupTypes(BASE, ManilaBase): + """Represent possible share group types of shares offered.""" + __tablename__ = "share_group_types" + __table_args__ = ( + schema.UniqueConstraint( + "name", "deleted", name="uniq_share_group_type_name"), + ) id = Column(String(36), primary_key=True) + deleted = Column(String(36), default='False') + name = Column(String(255)) + is_public = Column(Boolean, default=True) + +class ShareGroup(BASE, ManilaBase): + """Represents a share group.""" + __tablename__ = 'share_groups' + id = Column(String(36), primary_key=True) user_id = Column(String(255), nullable=False) project_id = Column(String(255), nullable=False) deleted = Column(String(36), default='False') - host = Column(String(255)) name = Column(String(255)) description = Column(String(255)) status = Column(String(255)) - source_cgsnapshot_id = Column(String(36)) - share_network_id = Column(String(36), ForeignKey('share_networks.id'), - nullable=True) - share_server_id = Column(String(36), ForeignKey('share_servers.id'), - nullable=True) + source_share_group_snapshot_id = Column(String(36)) + share_network_id = Column( + String(36), ForeignKey('share_networks.id'), nullable=True) + share_server_id = Column( + String(36), ForeignKey('share_servers.id'), nullable=True) + share_group_type_id = Column( + String(36), ForeignKey('share_group_types.id'), nullable=True) + share_group_type = orm.relationship( + ShareGroupTypes, + backref="share_groups", + foreign_keys=share_group_type_id, + primaryjoin='and_(' + 'ShareGroup.share_group_type_id == ' + 'ShareGroupTypes.id,' + 'ShareGroup.deleted == 0)') -class CGSnapshot(BASE, ManilaBase): - """Represents a cgsnapshot.""" - __tablename__ = 'cgsnapshots' +class ShareGroupTypeProjects(BASE, ManilaBase): + """Represent projects associated share group types.""" + __tablename__ = "share_group_type_projects" + __table_args__ = (schema.UniqueConstraint( + "share_group_type_id", "project_id", "deleted", + name=("uniq_share_group_type_projects0share_group_type_id" + "0project_id0deleted")), + ) + id = Column(Integer, primary_key=True) + share_group_type_id = Column( + String, ForeignKey('share_group_types.id'), nullable=False) + project_id = Column(String(255)) + share_group_type = orm.relationship( + ShareGroupTypes, + backref="projects", + foreign_keys=share_group_type_id, + primaryjoin='and_(' + 'ShareGroupTypeProjects.share_group_type_id == ' + 'ShareGroupTypes.id,' + 'ShareGroupTypeProjects.deleted == 0)') + + +class ShareGroupTypeSpecs(BASE, ManilaBase): + """Represents additional specs for a share group type.""" + __tablename__ = 'share_group_type_specs' + id = Column(Integer, primary_key=True) + key = Column("spec_key", String(255)) + value = Column("spec_value", String(255)) + share_group_type_id = Column( + String(36), ForeignKey('share_group_types.id'), nullable=False) + share_group_type = orm.relationship( + ShareGroupTypes, + backref="group_specs", + foreign_keys=share_group_type_id, + primaryjoin='and_(' + 'ShareGroupTypeSpecs.share_group_type_id == ShareGroupTypes.id,' + 'ShareGroupTypeSpecs.deleted == 0)' + ) + + +class ShareGroupSnapshot(BASE, ManilaBase): + """Represents a share group snapshot.""" + __tablename__ = 'share_group_snapshots' id = Column(String(36), primary_key=True) - - consistency_group_id = Column(String(36), - ForeignKey('consistency_groups.id')) + share_group_id = Column(String(36), ForeignKey('share_groups.id')) user_id = Column(String(255), nullable=False) project_id = Column(String(255), nullable=False) deleted = Column(String(36), default='False') - name = Column(String(255)) description = Column(String(255)) status = Column(String(255)) - - consistency_group = orm.relationship( - ConsistencyGroup, - backref="cgsnapshots", - foreign_keys=consistency_group_id, + share_group = orm.relationship( + ShareGroup, + backref="snapshots", + foreign_keys=share_group_id, primaryjoin=('and_(' - 'CGSnapshot.consistency_group_id == ConsistencyGroup.id,' - 'CGSnapshot.deleted == "False")') + 'ShareGroupSnapshot.share_group_id == ShareGroup.id,' + 'ShareGroupSnapshot.deleted == "False")') ) -class ConsistencyGroupShareTypeMapping(BASE, ManilaBase): - """Represents the share types in a consistency group.""" - __tablename__ = 'consistency_group_share_type_mappings' +class ShareGroupTypeShareTypeMapping(BASE, ManilaBase): + """Represents the share types supported by a share group type.""" + __tablename__ = 'share_group_type_share_type_mappings' id = Column(String(36), primary_key=True) deleted = Column(String(36), default='False') - consistency_group_id = Column(String(36), - ForeignKey('consistency_groups.id'), - nullable=False) - share_type_id = Column(String(36), - ForeignKey('share_types.id'), - nullable=False) - - consistency_group = orm.relationship( - ConsistencyGroup, + share_group_type_id = Column( + String(36), ForeignKey('share_group_types.id'), nullable=False) + share_type_id = Column( + String(36), ForeignKey('share_types.id'), nullable=False) + share_group_type = orm.relationship( + ShareGroupTypes, backref="share_types", - foreign_keys=consistency_group_id, + foreign_keys=share_group_type_id, primaryjoin=('and_(' - 'ConsistencyGroupShareTypeMapping.consistency_group_id ' - '== ConsistencyGroup.id,' - 'ConsistencyGroupShareTypeMapping.deleted == "False")') + 'ShareGroupTypeShareTypeMapping.share_group_type_id ' + '== ShareGroupTypes.id,' + 'ShareGroupTypeShareTypeMapping.deleted == "False")') ) -class CGSnapshotMember(BASE, ManilaBase): - """Represents the share snapshots in a consistency group snapshot.""" - __tablename__ = 'cgsnapshot_members' +class ShareGroupShareTypeMapping(BASE, ManilaBase): + """Represents the share types in a share group.""" + __tablename__ = 'share_group_share_type_mappings' id = Column(String(36), primary_key=True) - cgsnapshot_id = Column(String(36), ForeignKey('cgsnapshots.id')) + deleted = Column(String(36), default='False') + share_group_id = Column( + String(36), ForeignKey('share_groups.id'), nullable=False) + share_type_id = Column( + String(36), ForeignKey('share_types.id'), nullable=False) + share_group = orm.relationship( + ShareGroup, + backref="share_types", + foreign_keys=share_group_id, + primaryjoin=('and_(' + 'ShareGroupShareTypeMapping.share_group_id ' + '== ShareGroup.id,' + 'ShareGroupShareTypeMapping.deleted == "False")') + ) + + +class ShareGroupSnapshotMember(BASE, ManilaBase): + """Represents the share snapshots in a share group snapshot.""" + __tablename__ = 'share_group_snapshot_members' + id = Column(String(36), primary_key=True) + share_group_snapshot_id = Column( + String(36), ForeignKey('share_group_snapshots.id')) share_id = Column(String(36), ForeignKey('shares.id')) share_instance_id = Column(String(36), ForeignKey('share_instances.id')) size = Column(Integer) status = Column(String(255)) share_proto = Column(String(255)) - share_type_id = Column(String(36), ForeignKey('share_types.id'), - nullable=True) user_id = Column(String(255)) project_id = Column(String(255)) deleted = Column(String(36), default='False') - - cgsnapshot = orm.relationship( - CGSnapshot, - backref="cgsnapshot_members", - foreign_keys=cgsnapshot_id, - primaryjoin='CGSnapshot.id == CGSnapshotMember.cgsnapshot_id') + share_group_snapshot = orm.relationship( + ShareGroupSnapshot, + backref="share_group_snapshot_members", + foreign_keys=share_group_snapshot_id, + primaryjoin='ShareGroupSnapshot.id == ' + 'ShareGroupSnapshotMember.share_group_snapshot_id') def register_models(): diff --git a/manila/exception.py b/manila/exception.py index b1e49a32cc..ac2bf27e17 100644 --- a/manila/exception.py +++ b/manila/exception.py @@ -478,6 +478,10 @@ class ShareSnapshotNotSupported(ManilaException): message = _("Share %(share_name)s does not support snapshots.") +class ShareGroupSnapshotNotSupported(ManilaException): + message = _("Share group %(share_group)s does not support snapshots.") + + class ShareSnapshotIsBusy(ManilaException): message = _("Deleting snapshot %(snapshot_name)s that has " "dependent shares.") @@ -531,6 +535,10 @@ class InvalidShareType(Invalid): message = _("Invalid share type: %(reason)s.") +class InvalidShareGroupType(Invalid): + message = _("Invalid share group type: %(reason)s.") + + class InvalidExtraSpec(Invalid): message = _("Invalid extra_spec: %(reason)s.") @@ -547,21 +555,40 @@ class ShareTypeNotFound(NotFound): message = _("Share type %(share_type_id)s could not be found.") +class ShareGroupTypeNotFound(NotFound): + message = _("Share group type %(type_id)s could not be found.") + + class ShareTypeAccessNotFound(NotFound): message = _("Share type access not found for %(share_type_id)s / " "%(project_id)s combination.") +class ShareGroupTypeAccessNotFound(NotFound): + message = _("Share group type access not found for %(type_id)s / " + "%(project_id)s combination.") + + class ShareTypeNotFoundByName(ShareTypeNotFound): message = _("Share type with name %(share_type_name)s " "could not be found.") +class ShareGroupTypeNotFoundByName(ShareTypeNotFound): + message = _("Share group type with name %(type_name)s " + "could not be found.") + + class ShareTypeExtraSpecsNotFound(NotFound): message = _("Share Type %(share_type_id)s has no extra specs with " "key %(extra_specs_key)s.") +class ShareGroupTypeSpecsNotFound(NotFound): + message = _("Share group type %(type_id)s has no group specs with " + "key %(specs_key)s.") + + class ShareTypeInUse(ManilaException): message = _("Share Type %(share_type_id)s deletion is not allowed with " "shares present with the type.") @@ -571,20 +598,39 @@ class IPAddressInUse(InUse): message = _("IP address %(ip)s is already used.") +class ShareGroupTypeInUse(ManilaException): + message = _("Share group Type %(type_id)s deletion is not allowed " + "with groups present with the type.") + + class ShareTypeExists(ManilaException): message = _("Share Type %(id)s already exists.") +class ShareGroupTypeExists(ManilaException): + message = _("Share group type %(type_id)s already exists.") + + class ShareTypeAccessExists(ManilaException): message = _("Share type access for %(share_type_id)s / " "%(project_id)s combination already exists.") +class ShareGroupTypeAccessExists(ManilaException): + message = _("Share group type access for %(type_id)s / " + "%(project_id)s combination already exists.") + + class ShareTypeCreateFailed(ManilaException): message = _("Cannot create share_type with " "name %(name)s and specs %(extra_specs)s.") +class ShareGroupTypeCreateFailed(ManilaException): + message = _("Cannot create share group type with " + "name %(name)s and specs %(group_specs)s.") + + class ManageExistingShareTypeMismatch(ManilaException): message = _("Manage existing share failed due to share type mismatch: " "%(reason)s") @@ -746,27 +792,27 @@ class HNASNothingToCloneException(ManilaException): message = _("HNAS Nothing To Clone Exception: %(msg)s") -# ConsistencyGroup -class ConsistencyGroupNotFound(NotFound): - message = _("ConsistencyGroup %(consistency_group_id)s could not be " +# ShareGroup +class ShareGroupNotFound(NotFound): + message = _("Share group %(share_group_id)s could not be found.") + + +class ShareGroupSnapshotNotFound(NotFound): + message = _( + "Share group snapshot %(share_group_snapshot_id)s could not be found.") + + +class ShareGroupSnapshotMemberNotFound(NotFound): + message = _("Share group snapshot member %(member_id)s could not be " "found.") -class CGSnapshotNotFound(NotFound): - message = _("Consistency group snapshot %(cgsnapshot_id)s could not be " - "found.") +class InvalidShareGroup(Invalid): + message = _("Invalid share group: %(reason)s") -class CGSnapshotMemberNotFound(NotFound): - message = _("CG snapshot %(member_id)s could not be found.") - - -class InvalidConsistencyGroup(Invalid): - message = _("Invalid ConsistencyGroup: %(reason)s") - - -class InvalidCGSnapshot(Invalid): - message = _("Invalid CGSnapshot: %(reason)s") +class InvalidShareGroupSnapshot(Invalid): + message = _("Invalid share group snapshot: %(reason)s") class DriverNotInitialized(ManilaException): diff --git a/manila/scheduler/drivers/base.py b/manila/scheduler/drivers/base.py index cff8a1a266..d36ba62b2c 100644 --- a/manila/scheduler/drivers/base.py +++ b/manila/scheduler/drivers/base.py @@ -61,14 +61,14 @@ def share_replica_update_db(context, share_replica_id, host): return db.share_replica_update(context, share_replica_id, values) -def cg_update_db(context, cg_id, host): - '''Set the host and set the updated_at field of a consistency group. +def share_group_update_db(context, share_group_id, host): + '''Set the host and set the updated_at field of a share group. - :returns: A CG with the updated fields set properly. + :returns: A share group with the updated fields set properly. ''' now = timeutils.utcnow() values = {'host': host, 'updated_at': now} - return db.consistency_group_update(context, cg_id, values) + return db.share_group_update(context, share_group_id, values) class Scheduler(object): @@ -109,12 +109,12 @@ class Scheduler(object): """Must override schedule method for scheduler to work.""" raise NotImplementedError(_("Must implement schedule_create_share")) - def schedule_create_consistency_group(self, context, group_id, - request_spec, - filter_properties): + def schedule_create_share_group(self, context, share_group_id, + request_spec, + filter_properties): """Must override schedule method for scheduler to work.""" raise NotImplementedError(_( - "Must implement schedule_create_consistency_group")) + "Must implement schedule_create_share_group")) def get_pools(self, context, filters): """Must override schedule method for scheduler to work.""" diff --git a/manila/scheduler/drivers/filter.py b/manila/scheduler/drivers/filter.py index eea7acc679..3794dd09cd 100644 --- a/manila/scheduler/drivers/filter.py +++ b/manila/scheduler/drivers/filter.py @@ -169,17 +169,7 @@ class FilterScheduler(base.Scheduler): config_options = self._get_configuration_options() - # NOTE(ameade): If a consistency group is specified, pass the - # consistency group support level to the ConsistencyGroupFilter - # (host, pool, or False) - cg_support = None - cg = request_spec.get('consistency_group') - if cg: - temp_hosts = self.host_manager.get_all_host_states_share(elevated) - cg_host = next((host for host in temp_hosts - if host.host == cg.get('host')), None) - if cg_host: - cg_support = cg_host.consistency_group_support + share_group = request_spec.get('share_group') # NOTE(gouthamr): If 'active_replica_host' is present in the request # spec, pass that host's 'replication_domain' to the @@ -202,8 +192,7 @@ class FilterScheduler(base.Scheduler): 'config_options': config_options, 'share_type': share_type, 'resource_type': resource_type, - 'cg_support': cg_support, - 'consistency_group': cg, + 'share_group': share_group, 'replication_domain': replication_domain, }) @@ -318,27 +307,24 @@ class FilterScheduler(base.Scheduler): filter_properties['user_id'] = shr.get('user_id') filter_properties['metadata'] = shr.get('metadata') - def schedule_create_consistency_group(self, context, group_id, - request_spec, - filter_properties): + def schedule_create_share_group(self, context, share_group_id, + request_spec, filter_properties): - LOG.info(_LI("Scheduling consistency group %s") % group_id) - - host = self._get_best_host_for_consistency_group( - context, - request_spec) + LOG.info(_LI("Scheduling share group %s.") % share_group_id) + host = self._get_best_host_for_share_group(context, request_spec) if not host: - msg = _("No hosts available for consistency group %s") % group_id + msg = _("No hosts available for share group %s.") % share_group_id raise exception.NoValidHost(reason=msg) - msg = _LI("Chose host %(host)s for create_consistency_group %(cg_id)s") - LOG.info(msg % {'host': host, 'cg_id': group_id}) + msg = _LI("Chose host %(host)s for create_share_group %(group)s.") + LOG.info(msg % {'host': host, 'group': share_group_id}) - updated_group = base.cg_update_db(context, group_id, host) + updated_share_group = base.share_group_update_db( + context, share_group_id, host) - self.share_rpcapi.create_consistency_group(context, - updated_group, host) + self.share_rpcapi.create_share_group( + context, updated_share_group, host) def _get_weighted_hosts_for_share_type(self, context, request_spec, share_type): @@ -363,9 +349,6 @@ class FilterScheduler(base.Scheduler): if extra_spec is not None: share_type['extra_specs'][spec_name] = ( " %s" % extra_spec) - # Only allow pools that support consistency groups - share_type['extra_specs']['consistency_group_support'] = ( - " host pool") filter_properties = { 'context': context, @@ -393,8 +376,40 @@ class FilterScheduler(base.Scheduler): return weighed_hosts - def _get_weighted_candidates_cg(self, context, request_spec): - """Finds hosts that support the consistency group. + def _get_weighted_hosts_for_share_group_type(self, context, request_spec, + share_group_type): + config_options = self._get_configuration_options() + all_hosts = self.host_manager.get_all_host_states_share(context) + + if not all_hosts: + return [] + + filter_properties = { + 'context': context, + 'request_spec': request_spec, + 'config_options': config_options, + 'share_group_type': share_group_type, + 'resource_type': share_group_type, + } + + hosts = self.host_manager.get_filtered_hosts( + all_hosts, filter_properties) + + if not hosts: + return [] + + LOG.debug("Filtered %s" % hosts) + + weighed_hosts = self.host_manager.get_weighed_hosts( + hosts, + filter_properties) + if not weighed_hosts: + return [] + + return weighed_hosts + + def _get_weighted_candidates_share_group(self, context, request_spec): + """Finds hosts that support the share group. Returns a list of hosts that meet the required specs, ordered by their fitness. @@ -410,7 +425,7 @@ class FilterScheduler(base.Scheduler): elevated, request_spec, share_type) # NOTE(ameade): Take the intersection of hosts so we have one that - # can support all share types of the CG + # can support all share types of the share group if iteration_count == 0: weighed_hosts = temp_weighed_hosts else: @@ -423,10 +438,22 @@ class FilterScheduler(base.Scheduler): if not weighed_hosts: return [] + # NOTE(ameade): Ensure the hosts support the share group type + share_group_type = request_spec.get("resource_type", {}) + temp_weighed_group_hosts = ( + self._get_weighted_hosts_for_share_group_type( + elevated, request_spec, share_group_type)) + new_weighed_hosts = [] + for host1 in weighed_hosts: + for host2 in temp_weighed_group_hosts: + if host1.obj.host == host2.obj.host: + new_weighed_hosts.append(host1) + weighed_hosts = new_weighed_hosts + return weighed_hosts - def _get_best_host_for_consistency_group(self, context, request_spec): - weighed_hosts = self._get_weighted_candidates_cg( + def _get_best_host_for_share_group(self, context, request_spec): + weighed_hosts = self._get_weighted_candidates_share_group( context, request_spec) diff --git a/manila/scheduler/filters/capacity.py b/manila/scheduler/filters/capacity.py index 1821e3de79..ab17b81eac 100644 --- a/manila/scheduler/filters/capacity.py +++ b/manila/scheduler/filters/capacity.py @@ -33,7 +33,7 @@ class CapacityFilter(base_host.BaseHostFilter): def host_passes(self, host_state, filter_properties): """Return True if host has sufficient capacity.""" - share_size = filter_properties.get('size') + share_size = filter_properties.get('size', 0) if host_state.free_capacity_gb is None: # Fail Safe diff --git a/manila/scheduler/filters/consistency_group.py b/manila/scheduler/filters/consistency_group.py deleted file mode 100644 index e4da2fab78..0000000000 --- a/manila/scheduler/filters/consistency_group.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) 2015 Alex Meade -# 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. - - -from oslo_log import log - -from manila.scheduler.filters import base_host -from manila.share import utils as share_utils - -LOG = log.getLogger(__name__) - - -class ConsistencyGroupFilter(base_host.BaseHostFilter): - """ConsistencyGroupFilter filters host based on compatibility with CG.""" - - def host_passes(self, host_state, filter_properties): - """Return True if host will work with desired consistency group.""" - cg = filter_properties.get('consistency_group') - cg_support = filter_properties.get('cg_support') - - # NOTE(ameade): If creating a share not in a CG, then of course the - # host is valid for the cg. - if not cg: - return True - - # NOTE(ameade): If the CG host can only support shares on the same - # pool, then the only valid pool is that one. - if cg_support == 'pool' and cg.get('host') == host_state.host: - return True - - # NOTE(ameade): If the CG host can support shares on the same host, - # then any pool on that backend will work. - elif cg_support == 'host': - cg_backend = share_utils.extract_host(cg['host']) - host_backend = share_utils.extract_host(host_state.host) - return cg_backend == host_backend - - LOG.debug("Host %(host)s is not compatible with consistency " - "group %(cg)s" - % {"host": host_state.host, "cg": cg['id']}) - - return False diff --git a/manila/scheduler/host_manager.py b/manila/scheduler/host_manager.py index 0cc2dbfac0..22b49cbb43 100644 --- a/manila/scheduler/host_manager.py +++ b/manila/scheduler/host_manager.py @@ -47,7 +47,6 @@ host_manager_opts = [ 'AvailabilityZoneFilter', 'CapacityFilter', 'CapabilitiesFilter', - 'ConsistencyGroupFilter', 'DriverFilter', 'ShareReplicationFilter', ], @@ -132,7 +131,6 @@ class HostState(object): self.snapshot_support = True self.create_share_from_snapshot_support = True self.revert_to_snapshot_support = False - self.consistency_group_support = False self.dedupe = False self.compression = False self.replication_type = None @@ -304,10 +302,6 @@ class HostState(object): pool_cap['revert_to_snapshot_support'] = ( self.revert_to_snapshot_support) - if not pool_cap.get('consistency_group_support'): - pool_cap['consistency_group_support'] = \ - self.consistency_group_support - if 'dedupe' not in pool_cap: pool_cap['dedupe'] = self.dedupe @@ -332,8 +326,6 @@ class HostState(object): 'create_share_from_snapshot_support') self.revert_to_snapshot_support = capability.get( 'revert_to_snapshot_support', False) - self.consistency_group_support = capability.get( - 'consistency_group_support', False) self.updated = capability['timestamp'] self.replication_type = capability.get('replication_type') self.replication_domain = capability.get('replication_domain') diff --git a/manila/scheduler/manager.py b/manila/scheduler/manager.py index be8930f994..9bc4413308 100644 --- a/manila/scheduler/manager.py +++ b/manila/scheduler/manager.py @@ -58,7 +58,7 @@ MAPPING = { class SchedulerManager(manager.Manager): """Chooses a host to create shares.""" - RPC_API_VERSION = '1.7' + RPC_API_VERSION = '1.8' def __init__(self, scheduler_driver=None, service_name=None, *args, **kwargs): @@ -212,34 +212,29 @@ class SchedulerManager(manager.Manager): def request_service_capabilities(self, context): share_rpcapi.ShareAPI().publish_service_capabilities(context) - def _set_cg_error_state(self, method, context, ex, request_spec): + def _set_share_group_error_state(self, method, context, ex, request_spec): LOG.warning(_LW("Failed to schedule_%(method)s: %(ex)s"), {"method": method, "ex": ex}) - cg_state = {'status': constants.STATUS_ERROR} + share_group_state = {'status': constants.STATUS_ERROR} - consistency_group_id = request_spec.get('consistency_group_id') + share_group_id = request_spec.get('share_group_id') - if consistency_group_id: - db.consistency_group_update(context, - consistency_group_id, - cg_state) + if share_group_id: + db.share_group_update(context, share_group_id, share_group_state) - # TODO(ameade): add notifications - - def create_consistency_group(self, context, cg_id, request_spec=None, - filter_properties=None): + def create_share_group(self, context, share_group_id, request_spec=None, + filter_properties=None): try: - self.driver.schedule_create_consistency_group(context, cg_id, - request_spec, - filter_properties) + self.driver.schedule_create_share_group( + context, share_group_id, request_spec, filter_properties) except exception.NoValidHost as ex: - self._set_cg_error_state('create_consistency_group', - context, ex, request_spec) + self._set_share_group_error_state( + 'create_share_group', context, ex, request_spec) except Exception as ex: with excutils.save_and_reraise_exception(): - self._set_cg_error_state('create_consistency_group', - context, ex, request_spec) + self._set_share_group_error_state( + 'create_share_group', context, ex, request_spec) def _set_share_replica_error_state(self, context, method, exc, request_spec): diff --git a/manila/scheduler/rpcapi.py b/manila/scheduler/rpcapi.py index 847349cd0e..2503dbfb94 100644 --- a/manila/scheduler/rpcapi.py +++ b/manila/scheduler/rpcapi.py @@ -34,20 +34,21 @@ class SchedulerAPI(object): 1.1 - Add get_pools method 1.2 - Introduce Share Instances. Replace ``create_share()`` with ``create_share_instance()`` - 1.3 - Add create_consistency_group method + 1.3 - Add create_consistency_group method (renamed in 1.7) 1.4 - Add migrate_share_to_host method 1.5 - Add create_share_replica 1.6 - Add manage_share 1.7 - Updated migrate_share_to_host method with new parameters + 1.8 - Rename create_consistency_group -> create_share_group method """ - RPC_API_VERSION = '1.7' + RPC_API_VERSION = '1.8' def __init__(self): super(SchedulerAPI, self).__init__() target = messaging.Target(topic=CONF.scheduler_topic, version=self.RPC_API_VERSION) - self.client = rpc.get_client(target, version_cap='1.7') + self.client = rpc.get_client(target, version_cap=self.RPC_API_VERSION) def create_share_instance(self, context, request_spec=None, filter_properties=None): @@ -72,13 +73,23 @@ class SchedulerAPI(object): call_context = self.client.prepare(version='1.1') return call_context.call(context, 'get_pools', filters=filters) - def create_consistency_group(self, context, cg_id, request_spec=None, - filter_properties=None): + def create_share_group(self, context, share_group_id, request_spec=None, + filter_properties=None): + """Casts an rpc to the scheduler to create a share group. + + Example of 'request_spec' argument value:: + { + 'share_group_type_id': , + 'share_group_id': 'some_fake_uuid', + 'share_types': [models.ShareType], + 'resource_type': models.ShareGroup, + } + """ request_spec_p = jsonutils.to_primitive(request_spec) - call_context = self.client.prepare(version='1.3') + call_context = self.client.prepare(version='1.8') return call_context.cast(context, - 'create_consistency_group', - cg_id=cg_id, + 'create_share_group', + share_group_id=share_group_id, request_spec=request_spec_p, filter_properties=filter_properties) diff --git a/manila/scheduler/utils.py b/manila/scheduler/utils.py index 674d581c26..f27d43e158 100644 --- a/manila/scheduler/utils.py +++ b/manila/scheduler/utils.py @@ -40,7 +40,6 @@ def generate_stats(host_state, properties): host_state.driver_handles_share_servers, 'thin_provisioning': host_state.thin_provisioning, 'updated': host_state.updated, - 'consistency_group_support': host_state.consistency_group_support, 'dedupe': host_state.dedupe, 'compression': host_state.compression, 'snapshot_support': host_state.snapshot_support, diff --git a/manila/share/api.py b/manila/share/api.py index 7b3afd1f47..2650a8287f 100644 --- a/manila/share/api.py +++ b/manila/share/api.py @@ -70,7 +70,7 @@ class API(base.Base): def create(self, context, share_proto, size, name, description, snapshot_id=None, availability_zone=None, metadata=None, share_network_id=None, share_type=None, is_public=False, - consistency_group_id=None, cgsnapshot_member=None): + share_group_id=None, share_group_snapshot_member=None): """Create new share.""" policy.check_policy(context, 'share', 'create') @@ -171,47 +171,45 @@ class API(base.Base): except ValueError as e: raise exception.InvalidParameterValue(six.text_type(e)) - consistency_group = None - if consistency_group_id: + share_group = None + if share_group_id: try: - consistency_group = self.db.consistency_group_get( - context, consistency_group_id) + share_group = self.db.share_group_get(context, share_group_id) except exception.NotFound as e: raise exception.InvalidParameterValue(six.text_type(e)) - if (not cgsnapshot_member and - not (consistency_group['status'] == - constants.STATUS_AVAILABLE)): + if (not share_group_snapshot_member and + not (share_group['status'] == constants.STATUS_AVAILABLE)): params = { 'avail': constants.STATUS_AVAILABLE, - 'cg_status': consistency_group['status'], + 'status': share_group['status'], } - msg = _("Consistency group status must be %(avail)s, got" - "%(cg_status)s.") % params - raise exception.InvalidConsistencyGroup(message=msg) + msg = _("Share group status must be %(avail)s, got" + "%(status)s.") % params + raise exception.InvalidShareGroup(message=msg) if share_type_id: - cg_st_ids = [st['share_type_id'] for st in - consistency_group.get('share_types', [])] - if share_type_id not in cg_st_ids: + share_group_st_ids = [ + st['share_type_id'] + for st in share_group.get('share_types', [])] + if share_type_id not in share_group_st_ids: params = { 'type': share_type_id, - 'cg': consistency_group_id + 'group': share_group_id, } msg = _("The specified share type (%(type)s) is not " - "supported by the specified consistency group " - "(%(cg)s).") % params + "supported by the specified share group " + "(%(group)s).") % params raise exception.InvalidParameterValue(msg) - if (not consistency_group.get('share_network_id') - == share_network_id): + if not share_group.get('share_network_id') == share_network_id: params = { 'net': share_network_id, - 'cg': consistency_group_id + 'group': share_group_id } msg = _("The specified share network (%(net)s) is not " - "supported by the specified consistency group " - "(%(cg)s).") % params + "supported by the specified share group " + "(%(group)s).") % params raise exception.InvalidParameterValue(msg) options = { @@ -224,12 +222,13 @@ class API(base.Base): 'display_description': description, 'share_proto': share_proto, 'is_public': is_public, - 'consistency_group_id': consistency_group_id, + 'share_group_id': share_group_id, } options.update(self.get_share_attributes_from_share_type(share_type)) - if cgsnapshot_member: - options['source_cgsnapshot_member_id'] = cgsnapshot_member['id'] + if share_group_snapshot_member: + options['source_share_group_snapshot_member_id'] = ( + share_group_snapshot_member['id']) try: share = self.db.share_create(context, options, @@ -248,11 +247,14 @@ class API(base.Base): # It is common situation for different types of backends. host = snapshot['share']['instance']['host'] - self.create_instance(context, share, share_network_id=share_network_id, - host=host, availability_zone=availability_zone, - consistency_group=consistency_group, - cgsnapshot_member=cgsnapshot_member, - share_type_id=share_type_id) + elif share_group: + host = share_group['host'] + + self.create_instance( + context, share, share_network_id=share_network_id, host=host, + availability_zone=availability_zone, share_group=share_group, + share_group_snapshot_member=share_group_snapshot_member, + share_type_id=share_type_id) # Retrieve the share with instance details share = self.db.share_get(context, share['id']) @@ -316,20 +318,21 @@ class API(base.Base): def create_instance(self, context, share, share_network_id=None, host=None, availability_zone=None, - consistency_group=None, cgsnapshot_member=None, + share_group=None, share_group_snapshot_member=None, share_type_id=None): policy.check_policy(context, 'share', 'create') request_spec, share_instance = ( self.create_share_instance_and_get_request_spec( context, share, availability_zone=availability_zone, - consistency_group=consistency_group, host=host, + share_group=share_group, host=host, share_network_id=share_network_id, share_type_id=share_type_id)) - if cgsnapshot_member: - # Inherit properties from the cgsnapshot_member - member_share_instance = cgsnapshot_member['share_instance'] + if share_group_snapshot_member: + # Inherit properties from the share_group_snapshot_member + member_share_instance = share_group_snapshot_member[ + 'share_instance'] updates = { 'host': member_share_instance['host'], 'share_network_id': member_share_instance['share_network_id'], @@ -338,7 +341,8 @@ class API(base.Base): share = self.db.share_instance_update(context, share_instance['id'], updates) - # NOTE(ameade): Do not cast to driver if creating from cgsnapshot + # NOTE(ameade): Do not cast to driver if creating from share group + # snapshot return if host: @@ -360,7 +364,7 @@ class API(base.Base): def create_share_instance_and_get_request_spec( self, context, share, availability_zone=None, - consistency_group=None, host=None, share_network_id=None, + share_group=None, host=None, share_network_id=None, share_type_id=None, cast_rules_to_readonly=False): availability_zone_id = None @@ -398,9 +402,9 @@ class API(base.Base): 'share_proto': share['share_proto'], 'share_type_id': share_type_id, 'is_public': share['is_public'], - 'consistency_group_id': share['consistency_group_id'], - 'source_cgsnapshot_member_id': share[ - 'source_cgsnapshot_member_id'], + 'share_group_id': share['share_group_id'], + 'source_share_group_snapshot_member_id': share[ + 'source_share_group_snapshot_member_id'], 'snapshot_id': share['snapshot_id'], 'replication_type': share['replication_type'], } @@ -428,7 +432,7 @@ class API(base.Base): 'share_id': share['id'], 'snapshot_id': share['snapshot_id'], 'share_type': share_type, - 'consistency_group': consistency_group, + 'share_group': share_group, 'availability_zone_id': availability_zone_id, } return request_spec, share_instance @@ -440,6 +444,10 @@ class API(base.Base): msg = _("Replication not supported for share %s.") raise exception.InvalidShare(message=msg % share['id']) + if share.get('share_group_id'): + msg = _("Replication not supported for shares in a group.") + raise exception.InvalidShare(message=msg) + self._check_is_share_busy(share) active_replica = self.db.share_replicas_get_available_active_replica( @@ -642,11 +650,11 @@ class API(base.Base): 'share_proto': kwargs.get('share_proto', share.get('share_proto')), 'share_type_id': share_type['id'], 'is_public': kwargs.get('is_public', share.get('is_public')), - 'consistency_group_id': kwargs.get( - 'consistency_group_id', share.get('consistency_group_id')), - 'source_cgsnapshot_member_id': kwargs.get( - 'source_cgsnapshot_member_id', - share.get('source_cgsnapshot_member_id')), + 'share_group_id': kwargs.get( + 'share_group_id', share.get('share_group_id')), + 'source_share_group_snapshot_member_id': kwargs.get( + 'source_share_group_snapshot_member_id', + share.get('source_share_group_snapshot_member_id')), 'snapshot_id': kwargs.get('snapshot_id', share.get('snapshot_id')), } share_instance_properties = { @@ -878,14 +886,16 @@ class API(base.Base): snapshots = self.db.share_snapshot_get_all_for_share(context, share_id) if len(snapshots): - msg = _("Share still has %d dependent snapshots") % len(snapshots) + msg = _("Share still has %d dependent snapshots.") % len(snapshots) raise exception.InvalidShare(reason=msg) - cgsnapshot_members_count = self.db.count_cgsnapshot_members_in_share( - context, share_id) - if cgsnapshot_members_count: - msg = (_("Share still has %d dependent cgsnapshot members") % - cgsnapshot_members_count) + share_group_snapshot_members_count = ( + self.db.count_share_group_snapshot_members_in_share( + context, share_id)) + if share_group_snapshot_members_count: + msg = ( + _("Share still has %d dependent share group snapshot " + "members.") % share_group_snapshot_members_count) raise exception.InvalidShare(reason=msg) self._check_is_share_busy(share) @@ -955,10 +965,10 @@ class API(base.Base): if shares: raise exception.ShareServerInUse(share_server_id=server['id']) - cgs = self.db.consistency_group_get_all_by_share_server(context, - server['id']) - if cgs: - LOG.error(_LE("share server '%(ssid)s' in use by CGs"), + share_groups = self.db.share_group_get_all_by_share_server( + context, server['id']) + if share_groups: + LOG.error(_LE("share server '%(ssid)s' in use by share groups."), {'ssid': server['id']}) raise exception.ShareServerInUse(share_server_id=server['id']) diff --git a/manila/share/driver.py b/manila/share/driver.py index 02ffa203a1..6d1c1e6023 100644 --- a/manila/share/driver.py +++ b/manila/share/driver.py @@ -25,7 +25,7 @@ from oslo_config import cfg from oslo_log import log from manila import exception -from manila.i18n import _, _LE +from manila.i18n import _, _LE, _LW from manila import network from manila import utils @@ -825,7 +825,7 @@ class ShareDriver(object): def choose_share_server_compatible_with_share(self, context, share_servers, share, snapshot=None, - consistency_group=None): + share_group=None): """Method that allows driver to choose share server for provided share. If compatible share-server is not found, method should return None. @@ -834,21 +834,22 @@ class ShareDriver(object): :param share_servers: list with share-server models :param share: share model :param snapshot: snapshot model - :param consistency_group: ConsistencyGroup model with shares + :param share_group: ConsistencyGroup model with shares :returns: share-server or None """ - # If creating in a consistency group, use its share server - if consistency_group: + # If creating in a share group, use its share server + if share_group: for share_server in share_servers: - if (consistency_group.get('share_server_id') == + if (share_group.get('share_server_id') == share_server['id']): return share_server return None return share_servers[0] if share_servers else None - def choose_share_server_compatible_with_cg(self, context, share_servers, - cg_ref, cgsnapshot=None): + def choose_share_server_compatible_with_share_group( + self, context, share_servers, share_group_ref, + share_group_snapshot=None): return share_servers[0] if share_servers else None @@ -1074,6 +1075,7 @@ class ShareDriver(object): create_share_from_snapshot_support=( self.creating_shares_from_snapshots_is_supported), revert_to_snapshot_support=False, + share_group_snapshot_support=self.snapshots_are_supported, replication_domain=self.replication_domain, filter_function=self.get_filter_function(), goodness_function=self.get_goodness_function(), @@ -1089,11 +1091,11 @@ class ShareDriver(object): """ return [] - def create_consistency_group(self, context, cg_dict, share_server=None): - """Create a consistency group. + def create_share_group(self, context, share_group_dict, share_server=None): + """Create a share group. :param context: - :param cg_dict: The consistency group details + :param share_group_dict: The share group details EXAMPLE: { 'status': 'creating', @@ -1103,27 +1105,30 @@ class ShareDriver(object): 'deleted': 'False', 'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6), 'updated_at': None, - 'source_cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', - 'host': 'openstack2@cmodeSSVMNFS', + 'source_share_group_snapshot_id': 'some_fake_uuid', + 'share_group_type_id': 'some_fake_uuid', + 'host': 'hostname@backend_name', + 'share_network_id': None, + 'share_server_id': None, 'deleted_at': None, - 'share_types': [], - 'id': 'eda52174-0442-476d-9694-a58327466c14', + 'share_types': [], + 'id': 'some_fake_uuid', 'name': None } - :returns: (cg_model_update, share_update_list) - cg_model_update - a dict containing any values to be updated - for the CG in the database. This value may be None. + :returns: (share_group_model_update, share_update_list) + share_group_model_update - a dict containing any values to be + updated for the SG in the database. This value may be None. """ - raise NotImplementedError() + LOG.debug('Created a Share Group with ID: %s.', share_group_dict['id']) - def create_consistency_group_from_cgsnapshot(self, context, cg_dict, - cgsnapshot_dict, - share_server=None): - """Create a consistency group from a cgsnapshot. + def create_share_group_from_share_group_snapshot( + self, context, share_group_dict, share_group_snapshot_dict, + share_server=None): + """Create a share group from a share group snapshot. :param context: - :param cg_dict: The consistency group details + :param share_group_dict: The share group details EXAMPLE: .. code:: @@ -1135,15 +1140,16 @@ class ShareDriver(object): 'deleted': 'False', 'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6), 'updated_at': None, - 'source_cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', - 'host': 'openstack2@cmodeSSVMNFS', + 'source_share_group_snapshot_id': + 'f6aa3b59-57eb-421e-965c-4e182538e36a', + 'host': 'hostname@backend_name', 'deleted_at': None, 'shares': [], # The new shares being created - 'share_types': [], - 'id': 'eda52174-0442-476d-9694-a58327466c14', + 'share_types': [], + 'id': 'some_fake_uuid', 'name': None } - :param cgsnapshot_dict: The cgsnapshot details + :param share_group_snapshot_dict: The share group snapshot details EXAMPLE: .. code:: @@ -1155,11 +1161,10 @@ class ShareDriver(object): 'deleted': '0', 'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), 'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58), - 'consistency_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67', - 'cgsnapshot_members': [ + 'share_group_id': 'some_fake_uuid', + 'share_share_group_snapshot_members': [ { 'status': 'available', - 'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f', 'user_id': 'a0314a441ca842019b0952224aa39192', 'deleted': 'False', 'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), @@ -1167,9 +1172,9 @@ class ShareDriver(object): 'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58), 'share_proto': 'NFS', 'project_id': '13c0be6290934bd98596cfa004650049', - 'cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', + 'share_group_snapshot_id': 'some_fake_uuid', 'deleted_at': None, - 'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e', + 'id': 'some_fake_uuid', 'size': 1 } ], @@ -1177,27 +1182,48 @@ class ShareDriver(object): 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'name': None } - :return: (cg_model_update, share_update_list) - cg_model_update - a dict containing any values to be updated - for the CG in the database. This value may be None. + :return: (share_group_model_update, share_update_list) + share_group_model_update - a dict containing any values to be + updated for the share group in the database. This value may be None share_update_list - a list of dictionaries containing dicts for - every share created in the CG. Any share dicts should at a minimum - contain the 'id' key and 'export_locations'. Export locations - should be in the same format as returned by a share_create. This - list may be empty or None. - EXAMPLE: + every share created in the share group. Any share dicts should at a + minimum contain the 'id' key and 'export_locations'. + Export locations should be in the same format as returned by + a share_create. This list may be empty or None. EXAMPLE: .. code:: - [{'id': 'uuid', 'export_locations': ['export_path']}] + [{'id': 'uuid', 'export_locations': [{...}, {...}]}] """ - raise NotImplementedError() + # Ensure that the share group snapshot has members + if not share_group_snapshot_dict['share_group_snapshot_members']: + return None, None - def delete_consistency_group(self, context, cg_dict, share_server=None): - """Delete a consistency group + clone_list = self._collate_share_group_snapshot_info( + share_group_dict, share_group_snapshot_dict) + share_update_list = [] + + LOG.debug('Creating share group from group snapshot %s.', + share_group_snapshot_dict['id']) + + for clone in clone_list: + kwargs = {} + if self.driver_handles_share_servers: + kwargs['share_server'] = share_server + export_locations = ( + self.create_share_from_snapshot( + context, clone['share'], clone['snapshot'], **kwargs)) + share_update_list.append({ + 'id': clone['share']['id'], + 'export_locations': export_locations, + }) + return None, share_update_list + + def delete_share_group(self, context, share_group_dict, share_server=None): + """Delete a share group :param context: The request context - :param cg_dict: The consistency group details + :param share_group_dict: The share group details EXAMPLE: .. code:: @@ -1209,25 +1235,42 @@ class ShareDriver(object): 'deleted': 'False', 'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6), 'updated_at': None, - 'source_cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', - 'host': 'openstack2@cmodeSSVMNFS', + 'source_share_group_snapshot_id': 'some_fake_uuid', + 'share_share_group_type_id': 'some_fake_uuid', + 'host': 'hostname@backend_name', 'deleted_at': None, 'shares': [], # The new shares being created - 'share_types': [], - 'id': 'eda52174-0442-476d-9694-a58327466c14', + 'share_types': [], + 'id': 'some_fake_uuid', 'name': None } - :return: cg_model_update - cg_model_update - a dict containing any values to be updated - for the CG in the database. This value may be None. + :return: share_group_model_update + share_group_model_update - a dict containing any values to be + updated for the group in the database. This value may be None. """ - raise NotImplementedError() - def create_cgsnapshot(self, context, snap_dict, share_server=None): - """Create a consistency group snapshot. + def _cleanup_group_share_snapshot(self, context, share_snapshot, + share_server): + """Deletes the snapshot of a share belonging to a group.""" + + try: + self.delete_snapshot( + context, share_snapshot, share_server=share_server) + except exception.ManilaException: + msg = _LE('Could not delete share group snapshot member %(snap)s ' + 'for share %(share)s.') + LOG.error(msg % { + 'snap': share_snapshot['id'], + 'share': share_snapshot['share_id'], + }) + raise + + def create_share_group_snapshot(self, context, snap_dict, + share_server=None): + """Create a share group snapshot. :param context: - :param snap_dict: The cgsnapshot details + :param snap_dict: The share group snapshot details EXAMPLE: .. code:: @@ -1239,11 +1282,11 @@ class ShareDriver(object): 'deleted': '0', 'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), 'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58), - 'consistency_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67', - 'cgsnapshot_members': [ + 'share_group_id': 'some_fake_uuid', + 'share_group_snapshot_members': [ { 'status': 'available', - 'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f', + 'share_type_id': 'some_fake_uuid', 'user_id': 'a0314a441ca842019b0952224aa39192', 'deleted': 'False', 'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), @@ -1251,32 +1294,75 @@ class ShareDriver(object): 'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58), 'share_proto': 'NFS', 'project_id': '13c0be6290934bd98596cfa004650049', - 'cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', + 'share_group_snapshot_id': 'some_fake_uuid', 'deleted_at': None, - 'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e', + 'share_id': 'some_fake_uuid', + 'id': 'some_fake_uuid', 'size': 1 } ], 'deleted_at': None, - 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', + 'id': 'some_fake_uuid', 'name': None } - :return: (cgsnapshot_update, member_update_list) - cgsnapshot_update - a dict containing any values to be updated - for the CGSnapshot in the database. This value may be None. + :return: (share_group_snapshot_update, member_update_list) + share_group_snapshot_update - a dict containing any values to be + updated for the CGSnapshot in the database. This value may be None. member_update_list - a list of dictionaries containing for every - member of the cgsnapshot. Each dict should contains values to be - updated for the CGSnapshotMember in the database. This list may be - empty or None. + member of the share group snapshot. Each dict should contains + values to be updated for the ShareGroupSnapshotMember in + the database. This list may be empty or None. """ - raise NotImplementedError() + LOG.debug('Attempting to create a share group snapshot %s.', + snap_dict['id']) - def delete_cgsnapshot(self, context, snap_dict, share_server=None): - """Delete a consistency group snapshot + snapshot_members = snap_dict.get('share_group_snapshot_members', []) + if not self._stats.get('share_group_snapshot_support'): + raise exception.ShareGroupSnapshotNotSupported( + share_group=snap_dict['share_group_id']) + elif not snapshot_members: + LOG.warning(_LW('No shares in share group to create snapshot.')) + else: + share_snapshots = [] + for member in snapshot_members: + share_snapshot = { + 'snapshot_id': member['share_group_snapshot_id'], + 'share_id': member['share_id'], + 'id': member['id'], + } + try: + self.create_snapshot(context, share_snapshot, + share_server=share_server) + share_snapshots.append(share_snapshot) + except exception.ManilaException as e: + msg = _LE('Could not create share group snapshot. Failed ' + 'to create share snapshot %(snap)s for ' + 'share %(share)s.') + LOG.exception(msg % { + 'snap': share_snapshot['id'], + 'share': share_snapshot['share_id'] + }) + + # clean up any share snapshots previously created + LOG.debug( + 'Attempting to clean up snapshots due to failure.') + for share_snapshot in share_snapshots: + self._cleanup_group_share_snapshot( + context, share_snapshot, share_server) + raise e + + LOG.debug('Successfully created share group snapshot %s.', + snap_dict['id']) + + return None, None + + def delete_share_group_snapshot(self, context, snap_dict, + share_server=None): + """Delete a share group snapshot :param context: - :param snap_dict: The cgsnapshot details + :param snap_dict: The share group snapshot details EXAMPLE: .. code:: @@ -1288,12 +1374,12 @@ class ShareDriver(object): 'deleted': '0', 'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), 'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58), - 'consistency_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67', - 'cgsnapshot_members': [ + 'share_group_id': 'some_fake_uuid', + 'share_group_snapshot_members': [ { 'status': 'available', - 'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f', - 'share_id': 'e14b5174-e534-4f35-bc4f-fe81c1575d6f', + 'share_type_id': 'some_fake_uuid', + 'share_id': 'some_fake_uuid', 'user_id': 'a0314a441ca842019b0952224aa39192', 'deleted': 'False', 'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), @@ -1301,9 +1387,9 @@ class ShareDriver(object): 'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58), 'share_proto': 'NFS', 'project_id': '13c0be6290934bd98596cfa004650049', - 'cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', + 'share_group_snapshot_id': 'some_fake_uuid', 'deleted_at': None, - 'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e', + 'id': 'some_fake_uuid', 'size': 1 } ], @@ -1311,11 +1397,54 @@ class ShareDriver(object): 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'name': None } - :return: (cgsnapshot_update, member_update_list) - cgsnapshot_update - a dict containing any values to be updated - for the CGSnapshot in the database. This value may be None. + :return: (share_group_snapshot_update, member_update_list) + share_group_snapshot_update - a dict containing any values + to be updated for the ShareGroupSnapshot in the database. + This value may be None. """ - raise NotImplementedError() + snapshot_members = snap_dict.get('share_group_snapshot_members', []) + LOG.debug('Deleting share group snapshot %s.' % snap_dict['id']) + for member in snapshot_members: + share_snapshot = { + 'share_id': member['share_id'], + 'id': member['id'], + } + self.delete_snapshot( + context, share_snapshot, share_server=share_server) + + LOG.debug('Deleted share group snapshot %s.' % snap_dict['id']) + return None, None + + def _collate_share_group_snapshot_info(self, share_group_dict, + share_group_snapshot_dict): + """Collate the data for a clone of the SG snapshot. + + Given two data structures, a share group snapshot ( + share_group_snapshot_dict) and a new share to be cloned from + the snapshot (share_group_dict), match up both structures into a list + of dicts (share & snapshot) suitable for use by existing method + that clones individual share snapshots. + """ + clone_list = [] + for share in share_group_dict['shares']: + clone_info = {'share': share} + for share_group_snapshot_member in share_group_snapshot_dict[ + 'share_group_snapshot_members']: + if (share['source_share_group_snapshot_member_id'] == + share_group_snapshot_member['id']): + clone_info['snapshot'] = share_group_snapshot_member + break + + if len(clone_info) != 2: + msg = _( + "Invalid data supplied for creating share group from " + "share group snapshot " + "%s.") % share_group_snapshot_dict['id'] + raise exception.InvalidShareGroup(reason=msg) + + clone_list.append(clone_info) + + return clone_list def get_periodic_hook_data(self, context, share_instances): """Dedicated for update/extend of data for existing share instances. diff --git a/manila/share/drivers/cephfs/cephfs_native.py b/manila/share/drivers/cephfs/cephfs_native.py index b6e8b59afa..aacea9e897 100644 --- a/manila/share/drivers/cephfs/cephfs_native.py +++ b/manila/share/drivers/cephfs/cephfs_native.py @@ -94,7 +94,6 @@ class CephFSNativeDriver(driver.ShareDriver,): free_capacity_gb = stats['kb_avail'] * units.Mi data = { - 'consistency_group_support': 'pool', 'vendor_name': 'Ceph', 'driver_version': '1.0', 'share_backend_name': self.backend_name, @@ -166,7 +165,7 @@ class CephFSNativeDriver(driver.ShareDriver,): def _share_path(self, share): """Get VolumePath from Share.""" return ceph_volume_client.VolumePath( - share['consistency_group_id'], share['id']) + share['share_group_id'], share['id']) def create_share(self, context, share, share_server=None): """Create a CephFS volume. @@ -178,9 +177,11 @@ class CephFSNativeDriver(driver.ShareDriver,): """ # `share` is a Share - LOG.debug("create_share {be} name={id} size={size} cg_id={cg}".format( + msg = _("create_share {be} name={id} size={size}" + " share_group_id={group}") + LOG.debug(msg.format( be=self.backend_name, id=share['id'], size=share['size'], - cg=share['consistency_group_id'])) + group=share['share_group_id'])) extra_specs = share_types.get_extra_specs_from_share(share) data_isolated = extra_specs.get("cephfs:data_isolated", False) @@ -340,22 +341,24 @@ class CephFSNativeDriver(driver.ShareDriver,): self._share_path(snapshot['share']), '_'.join([snapshot['snapshot_id'], snapshot['id']])) - def create_consistency_group(self, context, cg_dict, share_server=None): + def create_share_group(self, context, cg_dict, share_server=None): self.volume_client.create_group(cg_dict['id']) - def delete_consistency_group(self, context, cg_dict, share_server=None): + def delete_share_group(self, context, cg_dict, share_server=None): self.volume_client.destroy_group(cg_dict['id']) - def delete_cgsnapshot(self, context, snap_dict, share_server=None): + def delete_share_group_snapshot(self, context, snap_dict, + share_server=None): self.volume_client.destroy_snapshot_group( - snap_dict['consistency_group_id'], + snap_dict['share_group_id'], snap_dict['id']) return None, [] - def create_cgsnapshot(self, context, snap_dict, share_server=None): + def create_share_group_snapshot(self, context, snap_dict, + share_server=None): self.volume_client.create_snapshot_group( - snap_dict['consistency_group_id'], + snap_dict['share_group_id'], snap_dict['id']) return None, [] diff --git a/manila/share/drivers/generic.py b/manila/share/drivers/generic.py index e94ae6ab8e..f7177e32b5 100644 --- a/manila/share/drivers/generic.py +++ b/manila/share/drivers/generic.py @@ -616,7 +616,6 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): share_backend_name=self.backend_name, storage_protocol='NFS_CIFS', reserved_percentage=self.configuration.reserved_share_percentage, - consistency_group_support=None, ) super(GenericShareDriver, self)._update_share_stats(data) diff --git a/manila/share/drivers/lvm.py b/manila/share/drivers/lvm.py index b8e7d8798b..12745aee71 100644 --- a/manila/share/drivers/lvm.py +++ b/manila/share/drivers/lvm.py @@ -188,7 +188,6 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver): 'storage_protocol': 'NFS_CIFS', 'reserved_percentage': self.configuration.reserved_share_percentage, - 'consistency_group_support': None, 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': True, diff --git a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py index c31062dda0..b782662c76 100644 --- a/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py +++ b/manila/share/drivers/netapp/dataontap/cluster_mode/lib_base.py @@ -242,7 +242,6 @@ class NetAppCmodeFileStorageLibrary(object): 'driver_version': '1.0', 'netapp_storage_family': 'ontap_cluster', 'storage_protocol': 'NFS_CIFS', - 'consistency_group_support': 'host', 'pools': self._get_pools(filter_function=filter_function, goodness_function=goodness_function), } @@ -1070,7 +1069,7 @@ class NetAppCmodeFileStorageLibrary(object): else: msg = _("Invalid data supplied for creating consistency group " "from CG snapshot %s.") % cgsnapshot_dict['id'] - raise exception.InvalidConsistencyGroup(reason=msg) + raise exception.InvalidShareGroup(reason=msg) clone_list.append(clone_info) diff --git a/manila/share/drivers/zfsonlinux/driver.py b/manila/share/drivers/zfsonlinux/driver.py index d74c719ce7..cb69e8160c 100644 --- a/manila/share/drivers/zfsonlinux/driver.py +++ b/manila/share/drivers/zfsonlinux/driver.py @@ -375,7 +375,6 @@ class ZFSonLinuxShareDriver(zfs_utils.ExecuteMixin, driver.ShareDriver): 'storage_protocol': 'NFS', 'reserved_percentage': self.configuration.reserved_share_percentage, - 'consistency_group_support': None, 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'driver_name': 'ZFS', diff --git a/manila/share/manager.py b/manila/share/manager.py index 705fdb0f19..cc54ff21b4 100644 --- a/manila/share/manager.py +++ b/manila/share/manager.py @@ -189,7 +189,7 @@ def add_hooks(f): class ShareManager(manager.SchedulerDependentManager): """Manages NAS storages.""" - RPC_API_VERSION = '1.15' + RPC_API_VERSION = '1.16' def __init__(self, share_driver=None, service_name=None, *args, **kwargs): """Load the driver from args, or from flags.""" @@ -351,7 +351,7 @@ class ShareManager(manager.SchedulerDependentManager): def _provide_share_server_for_share(self, context, share_network_id, share_instance, snapshot=None, - consistency_group=None, + share_group=None, create_on_backend=True): """Gets or creates share_server and updates share with its id. @@ -442,7 +442,7 @@ class ShareManager(manager.SchedulerDependentManager): self.driver.choose_share_server_compatible_with_share( context, available_share_servers, share_instance, snapshot=snapshot.instance if snapshot else None, - consistency_group=consistency_group + share_group=share_group ) ) except Exception as e: @@ -538,19 +538,20 @@ class ShareManager(manager.SchedulerDependentManager): if snapshot_id: snapshot_ref = self.db.share_snapshot_get(context, snapshot_id) - consistency_group_ref = None - if share_instance.get('consistency_group_id'): - consistency_group_ref = self.db.consistency_group_get( - context, share_instance['consistency_group_id']) + share_group_ref = None + if share_instance.get('share_group_id'): + share_group_ref = self.db.share_group_get( + context, share_instance['share_group_id']) share_server, share_instance = self._provide_share_server_for_share( context, share_network_id, share_instance, snapshot_ref, - consistency_group_ref, create_on_backend=False) + share_group_ref, create_on_backend=False) return share_server['id'] - def _provide_share_server_for_cg(self, context, share_network_id, - cg_ref, cgsnapshot=None): + def _provide_share_server_for_share_group(self, context, share_network_id, + share_group_ref, + share_group_snapshot=None): """Gets or creates share_server and updates share with its id. Active share_server can be deleted if there are no dependent shares @@ -567,27 +568,28 @@ class ShareManager(manager.SchedulerDependentManager): should be found or created. If share_network_id is None method use share_network_id from provided snapshot. - :param cg_ref: Consistency Group model - :param cgsnapshot: Optional -- CGSnapshot model + :param share_group_ref: Share Group model + :param share_group_snapshot: Optional -- ShareGroupSnapshot model :returns: dict, dict -- first value is share_server, that - has been chosen for consistency group schedule. - Second value is consistency group updated with + has been chosen for share group schedule. + Second value is share group updated with share_server_id. """ - if not (share_network_id or cgsnapshot): + if not (share_network_id or share_group_snapshot): msg = _("'share_network_id' parameter or 'snapshot'" " should be provided. ") raise exception.InvalidInput(reason=msg) def error(msg, *args): LOG.error(msg, *args) - self.db.consistency_group_update( - context, cg_ref['id'], {'status': constants.STATUS_ERROR}) + self.db.share_group_update( + context, share_group_ref['id'], + {'status': constants.STATUS_ERROR}) @utils.synchronized("share_manager_%s" % share_network_id, external=True) - def _provide_share_server_for_cg(): + def _provide_share_server_for_share_group(): try: available_share_servers = ( self.db.share_server_get_all_by_host_and_share_net_valid( @@ -596,14 +598,14 @@ class ShareManager(manager.SchedulerDependentManager): available_share_servers = None compatible_share_server = None + choose_share_server = ( + self.driver.choose_share_server_compatible_with_share_group) if available_share_servers: try: - compatible_share_server = ( - self.driver.choose_share_server_compatible_with_cg( - context, available_share_servers, cg_ref, - cgsnapshot=cgsnapshot - ) + compatible_share_server = choose_share_server( + context, available_share_servers, share_group_ref, + share_group_snapshot=share_group_snapshot, ) except Exception as e: with excutils.save_and_reraise_exception(): @@ -620,16 +622,16 @@ class ShareManager(manager.SchedulerDependentManager): } ) - msg = ("Using share_server %(share_server)s for consistency " - "group %(cg_id)s") + msg = ("Using share_server %(share_server)s for share " + "group %(group_id)s") LOG.debug(msg, { 'share_server': compatible_share_server['id'], - 'cg_id': cg_ref['id'] + 'group_id': share_group_ref['id'] }) - updated_cg = self.db.consistency_group_update( + updated_share_group = self.db.share_group_update( context, - cg_ref['id'], + share_group_ref['id'], {'share_server_id': compatible_share_server['id']}, ) @@ -642,9 +644,9 @@ class ShareManager(manager.SchedulerDependentManager): LOG.info(_LI("Used preexisting share server " "'%(share_server_id)s'"), {'share_server_id': compatible_share_server['id']}) - return compatible_share_server, updated_cg + return compatible_share_server, updated_share_group - return _provide_share_server_for_cg() + return _provide_share_server_for_share_group() def _get_share_server(self, context, share_instance): if share_instance['share_server_id']: @@ -1526,10 +1528,10 @@ class ShareManager(manager.SchedulerDependentManager): snapshot_ref = None parent_share_server_id = None - consistency_group_ref = None - if share_instance.get('consistency_group_id'): - consistency_group_ref = self.db.consistency_group_get( - context, share_instance['consistency_group_id']) + share_group_ref = None + if share_instance.get('share_group_id'): + share_group_ref = self.db.share_group_get( + context, share_instance['share_group_id']) if share_network_id or parent_share_server_id: try: @@ -1537,7 +1539,7 @@ class ShareManager(manager.SchedulerDependentManager): self._provide_share_server_for_share( context, share_network_id, share_instance, snapshot=snapshot_ref, - consistency_group=consistency_group_ref + share_group=share_group_ref, ) ) except Exception: @@ -3313,30 +3315,32 @@ class ShareManager(manager.SchedulerDependentManager): LOG.info(_LI("Shrink share completed successfully."), resource=share) @utils.require_driver_initialized - def create_consistency_group(self, context, cg_id): + def create_share_group(self, context, share_group_id): context = context.elevated() - group_ref = self.db.consistency_group_get(context, cg_id) - group_ref['host'] = self.host - shares = self.db.share_instances_get_all_by_consistency_group_id( - context, cg_id) + share_group_ref = self.db.share_group_get(context, share_group_id) + share_group_ref['host'] = self.host + shares = self.db.share_instances_get_all_by_share_group_id( + context, share_group_id) - source_cgsnapshot_id = group_ref.get("source_cgsnapshot_id") + source_share_group_snapshot_id = share_group_ref.get( + "source_share_group_snapshot_id") snap_ref = None parent_share_server_id = None - if source_cgsnapshot_id: - snap_ref = self.db.cgsnapshot_get(context, source_cgsnapshot_id) - for member in snap_ref['cgsnapshot_members']: + if source_share_group_snapshot_id: + snap_ref = self.db.share_group_snapshot_get( + context, source_share_group_snapshot_id) + for member in snap_ref['share_group_snapshot_members']: member['share'] = self.db.share_instance_get( context, member['share_instance_id'], with_share_data=True) member['share_id'] = member['share_instance_id'] - if 'consistency_group' in snap_ref: - parent_share_server_id = snap_ref['consistency_group'][ + if 'share_group' in snap_ref: + parent_share_server_id = snap_ref['share_group'][ 'share_server_id'] status = constants.STATUS_AVAILABLE model_update = False - share_network_id = group_ref.get('share_network_id', None) + share_network_id = share_group_ref.get('share_network_id') share_server = None if parent_share_server_id and self.driver.driver_handles_share_servers: @@ -3345,44 +3349,47 @@ class ShareManager(manager.SchedulerDependentManager): share_network_id = share_server['share_network_id'] if share_network_id and not self.driver.driver_handles_share_servers: - self.db.consistency_group_update( - context, cg_id, {'status': constants.STATUS_ERROR}) + self.db.share_group_update( + context, share_group_id, {'status': constants.STATUS_ERROR}) msg = _("Driver does not expect share-network to be provided " "with current configuration.") raise exception.InvalidInput(reason=msg) if not share_server and share_network_id: try: - share_server, group_ref = self._provide_share_server_for_cg( - context, share_network_id, group_ref, cgsnapshot=snap_ref + share_server, share_group_ref = ( + self._provide_share_server_for_share_group( + context, share_network_id, share_group_ref, + share_group_snapshot=snap_ref, + ) ) except Exception: with excutils.save_and_reraise_exception(): LOG.error(_LE("Failed to get share server" - " for consistency group creation.")) - self.db.consistency_group_update( - context, cg_id, {'status': constants.STATUS_ERROR}) + " for share group creation.")) + self.db.share_group_update( + context, share_group_id, + {'status': constants.STATUS_ERROR}) try: # TODO(ameade): Add notification for create.start - LOG.info(_LI("Consistency group %s: creating"), cg_id) + LOG.info(_LI("Share group %s: creating"), share_group_id) model_update, share_update_list = None, None - group_ref['shares'] = shares + share_group_ref['shares'] = shares if snap_ref: model_update, share_update_list = ( - self.driver.create_consistency_group_from_cgsnapshot( - context, group_ref, snap_ref, + self.driver.create_share_group_from_share_group_snapshot( + context, share_group_ref, snap_ref, share_server=share_server)) else: - model_update = self.driver.create_consistency_group( - context, group_ref, share_server=share_server) + model_update = self.driver.create_share_group( + context, share_group_ref, share_server=share_server) if model_update: - group_ref = self.db.consistency_group_update(context, - group_ref['id'], - model_update) + share_group_ref = self.db.share_group_update( + context, share_group_ref['id'], model_update) if share_update_list: for share in share_update_list: @@ -3396,134 +3403,133 @@ class ShareManager(manager.SchedulerDependentManager): except Exception: with excutils.save_and_reraise_exception(): - self.db.consistency_group_update( + self.db.share_group_update( context, - group_ref['id'], + share_group_ref['id'], {'status': constants.STATUS_ERROR}) for share in shares: self.db.share_instance_update( context, share['id'], {'status': constants.STATUS_ERROR}) - LOG.error(_LE("Consistency group %s: create failed"), cg_id) + LOG.error(_LE("Share group %s: create failed"), share_group_id) now = timeutils.utcnow() for share in shares: self.db.share_instance_update( context, share['id'], {'status': constants.STATUS_AVAILABLE}) - self.db.consistency_group_update(context, - group_ref['id'], - {'status': status, - 'created_at': now}) - LOG.info(_LI("Consistency group %s: created successfully"), cg_id) + self.db.share_group_update(context, + share_group_ref['id'], + {'status': status, + 'created_at': now}) + LOG.info(_LI("Share group %s: created successfully"), share_group_id) # TODO(ameade): Add notification for create.end - return group_ref['id'] + return share_group_ref['id'] @utils.require_driver_initialized - def delete_consistency_group(self, context, cg_id): + def delete_share_group(self, context, share_group_id): context = context.elevated() - group_ref = self.db.consistency_group_get(context, cg_id) - group_ref['host'] = self.host - group_ref['shares'] = ( - self.db.share_instances_get_all_by_consistency_group_id( - context, cg_id)) + share_group_ref = self.db.share_group_get(context, share_group_id) + share_group_ref['host'] = self.host + share_group_ref['shares'] = ( + self.db.share_instances_get_all_by_share_group_id( + context, share_group_id)) model_update = False # TODO(ameade): Add notification for delete.start try: - LOG.info(_LI("Consistency group %s: deleting"), cg_id) + LOG.info(_LI("Share group %s: deleting"), share_group_id) share_server = None - if group_ref.get('share_server_id'): + if share_group_ref.get('share_server_id'): share_server = self.db.share_server_get( - context, group_ref['share_server_id']) - model_update = self.driver.delete_consistency_group( - context, group_ref, share_server=share_server) + context, share_group_ref['share_server_id']) + model_update = self.driver.delete_share_group( + context, share_group_ref, share_server=share_server) if model_update: - group_ref = self.db.consistency_group_update( - context, group_ref['id'], model_update) + share_group_ref = self.db.share_group_update( + context, share_group_ref['id'], model_update) except Exception: with excutils.save_and_reraise_exception(): - self.db.consistency_group_update( + self.db.share_group_update( context, - group_ref['id'], + share_group_ref['id'], {'status': constants.STATUS_ERROR}) - LOG.error(_LE("Consistency group %s: delete failed"), - group_ref['id']) + LOG.error(_LE("Share group %s: delete failed"), + share_group_ref['id']) - self.db.consistency_group_destroy(context, - cg_id) - LOG.info(_LI("Consistency group %s: deleted successfully"), - cg_id) + self.db.share_group_destroy(context, share_group_id) + LOG.info(_LI("Share group %s: deleted successfully"), share_group_id) # TODO(ameade): Add notification for delete.end @utils.require_driver_initialized - def create_cgsnapshot(self, context, cgsnapshot_id): + def create_share_group_snapshot(self, context, share_group_snapshot_id): context = context.elevated() - snap_ref = self.db.cgsnapshot_get(context, cgsnapshot_id) - for member in snap_ref['cgsnapshot_members']: + snap_ref = self.db.share_group_snapshot_get( + context, share_group_snapshot_id) + for member in snap_ref['share_group_snapshot_members']: member['share'] = self.db.share_instance_get( context, member['share_instance_id'], with_share_data=True) member['share_id'] = member['share_instance_id'] status = constants.STATUS_AVAILABLE - snapshot_update = False try: - LOG.info(_LI("Consistency group snapshot %s: creating"), - cgsnapshot_id) + LOG.info(_LI("Share group snapshot %s: creating"), + share_group_snapshot_id) share_server = None - if snap_ref['consistency_group'].get('share_server_id'): + if snap_ref['share_group'].get('share_server_id'): share_server = self.db.share_server_get( - context, snap_ref['consistency_group']['share_server_id']) + context, snap_ref['share_group']['share_server_id']) snapshot_update, member_update_list = ( - self.driver.create_cgsnapshot(context, snap_ref, - share_server=share_server)) + self.driver.create_share_group_snapshot( + context, snap_ref, share_server=share_server)) if member_update_list: snapshot_update = snapshot_update or {} - snapshot_update['cgsnapshot_members'] = [] + snapshot_update['share_group_snapshot_members'] = [] for update in (member_update_list or []): - snapshot_update['cgsnapshot_members'].append(update) + snapshot_update['share_group_snapshot_members'].append( + update) if snapshot_update: - snap_ref = self.db.cgsnapshot_update( + snap_ref = self.db.share_group_snapshot_update( context, snap_ref['id'], snapshot_update) except Exception: with excutils.save_and_reraise_exception(): - self.db.cgsnapshot_update( + self.db.share_group_snapshot_update( context, snap_ref['id'], {'status': constants.STATUS_ERROR}) - LOG.error(_LE("Consistency group snapshot %s: create failed"), - cgsnapshot_id) + LOG.error(_LE("Share group snapshot %s: create failed"), + share_group_snapshot_id) now = timeutils.utcnow() - for member in (snap_ref.get('cgsnapshot_members') or []): + for member in (snap_ref.get('share_group_snapshot_members') or []): update = {'status': status, 'created_at': now} - self.db.cgsnapshot_member_update(context, member['id'], - update) + self.db.share_group_snapshot_member_update( + context, member['id'], update) - self.db.cgsnapshot_update(context, - snap_ref['id'], - {'status': status, - 'created_at': now}) - LOG.info(_LI("Consistency group snapshot %s: created successfully"), - cgsnapshot_id) + self.db.share_group_snapshot_update( + context, snap_ref['id'], + {'status': status, 'created_at': now}) + LOG.info(_LI("Share group snapshot %s: created successfully"), + share_group_snapshot_id) return snap_ref['id'] @utils.require_driver_initialized - def delete_cgsnapshot(self, context, cgsnapshot_id): + def delete_share_group_snapshot(self, context, share_group_snapshot_id): context = context.elevated() - snap_ref = self.db.cgsnapshot_get(context, cgsnapshot_id) - for member in snap_ref['cgsnapshot_members']: + snap_ref = self.db.share_group_snapshot_get( + context, share_group_snapshot_id) + for member in snap_ref['share_group_snapshot_members']: member['share'] = self.db.share_instance_get( context, member['share_instance_id'], with_share_data=True) member['share_id'] = member['share_instance_id'] @@ -3531,41 +3537,41 @@ class ShareManager(manager.SchedulerDependentManager): snapshot_update = False try: - LOG.info(_LI("Consistency group snapshot %s: deleting"), - cgsnapshot_id) + LOG.info(_LI("Share group snapshot %s: deleting"), + share_group_snapshot_id) share_server = None - if snap_ref['consistency_group'].get('share_server_id'): + if snap_ref['share_group'].get('share_server_id'): share_server = self.db.share_server_get( - context, snap_ref['consistency_group']['share_server_id']) + context, snap_ref['share_group']['share_server_id']) snapshot_update, member_update_list = ( - self.driver.delete_cgsnapshot(context, snap_ref, - share_server=share_server)) + self.driver.delete_share_group_snapshot( + context, snap_ref, share_server=share_server)) if member_update_list: snapshot_update = snapshot_update or {} - snapshot_update['cgsnapshot_members'] = [] + snapshot_update['share_group_snapshot_members'] = [] for update in (member_update_list or []): - snapshot_update['cgsnapshot_members'].append(update) + snapshot_update['share_group_snapshot_members'].append(update) if snapshot_update: - snap_ref = self.db.cgsnapshot_update( + snap_ref = self.db.share_group_snapshot_update( context, snap_ref['id'], snapshot_update) except Exception: with excutils.save_and_reraise_exception(): - self.db.cgsnapshot_update( + self.db.share_group_snapshot_update( context, snap_ref['id'], {'status': constants.STATUS_ERROR}) - LOG.error(_LE("Consistency group snapshot %s: delete failed"), + LOG.error(_LE("Share group snapshot %s: delete failed"), snap_ref['name']) - self.db.cgsnapshot_destroy(context, cgsnapshot_id) + self.db.share_group_snapshot_destroy(context, share_group_snapshot_id) - LOG.info(_LI("Consistency group snapshot %s: deleted successfully"), - cgsnapshot_id) + LOG.info(_LI("Share group snapshot %s: deleted successfully"), + share_group_snapshot_id) def _get_share_replica_dict(self, context, share_replica): # TODO(gouthamr): remove method when the db layer returns primitives @@ -3596,9 +3602,9 @@ class ShareManager(manager.SchedulerDependentManager): 'share_proto': share_replica.get('share_proto'), 'share_type_id': share_replica.get('share_type_id'), 'is_public': share_replica.get('is_public'), - 'consistency_group_id': share_replica.get('consistency_group_id'), - 'source_cgsnapshot_member_id': share_replica.get( - 'source_cgsnapshot_member_id'), + 'share_group_id': share_replica.get('share_group_id'), + 'source_share_group_snapshot_member_id': share_replica.get( + 'source_share_group_snapshot_member_id'), } return share_replica_ref diff --git a/manila/share/rpcapi.py b/manila/share/rpcapi.py index 89933ea196..848edb0af3 100644 --- a/manila/share/rpcapi.py +++ b/manila/share/rpcapi.py @@ -69,6 +69,10 @@ class ShareAPI(object): 1.14 - Add update_access() and remove allow_access() and deny_access(). 1.15 - Updated migration_start() method with new parameter "preserve_snapshots" + 1.16 - Convert create_consistency_group, delete_consistency_group + create_cgsnapshot, and delete_cgsnapshot methods to + create_share_group, delete_share_group + create_share_group_snapshot, and delete_share_group_snapshot """ BASE_RPC_API_VERSION = '1.0' @@ -77,7 +81,7 @@ class ShareAPI(object): super(ShareAPI, self).__init__() target = messaging.Target(topic=CONF.share_topic, version=self.BASE_RPC_API_VERSION) - self.client = rpc.get_client(target, version_cap='1.15') + self.client = rpc.get_client(target, version_cap='1.16') def create_share_instance(self, context, share_instance, host, request_spec, filter_properties, @@ -232,33 +236,31 @@ class ShareAPI(object): share_id=share['id'], new_size=new_size) - def create_consistency_group(self, context, cg, host): + def create_share_group(self, context, share_group, host): new_host = utils.extract_host(host) - call_context = self.client.prepare(server=new_host, version='1.5') - call_context.cast(context, - 'create_consistency_group', - cg_id=cg['id']) + call_context = self.client.prepare(server=new_host, version='1.16') + call_context.cast( + context, 'create_share_group', share_group_id=share_group['id']) - def delete_consistency_group(self, context, cg): - new_host = utils.extract_host(cg['host']) - call_context = self.client.prepare(server=new_host, version='1.5') - call_context.cast(context, - 'delete_consistency_group', - cg_id=cg['id']) + def delete_share_group(self, context, share_group): + new_host = utils.extract_host(share_group['host']) + call_context = self.client.prepare(server=new_host, version='1.16') + call_context.cast( + context, 'delete_share_group', share_group_id=share_group['id']) - def create_cgsnapshot(self, context, cgsnapshot, host): + def create_share_group_snapshot(self, context, share_group_snapshot, host): new_host = utils.extract_host(host) - call_context = self.client.prepare(server=new_host, version='1.5') - call_context.cast(context, - 'create_cgsnapshot', - cgsnapshot_id=cgsnapshot['id']) + call_context = self.client.prepare(server=new_host, version='1.16') + call_context.cast( + context, 'create_share_group_snapshot', + share_group_snapshot_id=share_group_snapshot['id']) - def delete_cgsnapshot(self, context, cgsnapshot, host): + def delete_share_group_snapshot(self, context, share_group_snapshot, host): new_host = utils.extract_host(host) - call_context = self.client.prepare(server=new_host, version='1.5') - call_context.cast(context, - 'delete_cgsnapshot', - cgsnapshot_id=cgsnapshot['id']) + call_context = self.client.prepare(server=new_host, version='1.16') + call_context.cast( + context, 'delete_share_group_snapshot', + share_group_snapshot_id=share_group_snapshot['id']) def create_share_replica(self, context, share_replica, host, request_spec, filter_properties): diff --git a/manila/share_group/api.py b/manila/share_group/api.py index 6fd5381b34..7b77a43b80 100644 --- a/manila/share_group/api.py +++ b/manila/share_group/api.py @@ -14,7 +14,7 @@ # under the License. """ -Handles all requests relating to consistency groups. +Handles all requests relating to share groups. """ from oslo_config import cfg @@ -48,28 +48,31 @@ class API(base.Base): super(API, self).__init__(db_driver) def create(self, context, name=None, description=None, - share_type_ids=None, source_cgsnapshot_id=None, - share_network_id=None): - """Create new consistency group.""" + share_type_ids=None, source_share_group_snapshot_id=None, + share_network_id=None, share_group_type_id=None): + """Create new share group.""" - cgsnapshot = None - original_cg = None - # NOTE(gouthamr): share_server_id is inherited from the parent CG if a - # CG snapshot is specified, else, it will be set in the share manager. + share_group_snapshot = None + original_share_group = None + # NOTE(gouthamr): share_server_id is inherited from the + # parent share group if a share group snapshot is specified, + # else, it will be set in the share manager. share_server_id = None - if source_cgsnapshot_id: - cgsnapshot = self.db.cgsnapshot_get(context, source_cgsnapshot_id) - if cgsnapshot['status'] != constants.STATUS_AVAILABLE: - msg = (_("Consistency group snapshot status must be %s") + if source_share_group_snapshot_id: + share_group_snapshot = self.db.share_group_snapshot_get( + context, source_share_group_snapshot_id) + if share_group_snapshot['status'] != constants.STATUS_AVAILABLE: + msg = (_("Share group snapshot status must be %s.") % constants.STATUS_AVAILABLE) - raise exception.InvalidCGSnapshot(reason=msg) + raise exception.InvalidShareGroupSnapshot(reason=msg) - original_cg = self.db.consistency_group_get(context, cgsnapshot[ - 'consistency_group_id']) - share_type_ids = [s['share_type_id'] for s in original_cg[ - 'share_types']] - share_network_id = original_cg['share_network_id'] - share_server_id = original_cg['share_server_id'] + original_share_group = self.db.share_group_get( + context, share_group_snapshot['share_group_id']) + share_type_ids = [ + s['share_type_id'] + for s in original_share_group['share_types']] + share_network_id = original_share_group['share_network_id'] + share_server_id = original_share_group['share_server_id'] # Get share_type_objects share_type_objects = [] @@ -79,7 +82,7 @@ class API(base.Base): share_type_object = share_types.get_share_type( context, share_type_id) except exception.ShareTypeNotFound: - msg = _("Share type with id %s could not be found") + msg = _("Share type with id %s could not be found.") raise exception.InvalidInput(msg % share_type_id) share_type_objects.append(share_type_object) @@ -112,14 +115,30 @@ class API(base.Base): raise exception.InvalidInput(reason=msg) if (driver_handles_share_servers and - not (source_cgsnapshot_id or share_network_id)): + not (source_share_group_snapshot_id or share_network_id)): msg = _("When using a share type with the " "driver_handles_share_servers extra spec as " "True, a share_network_id must be provided.") raise exception.InvalidInput(reason=msg) + try: + share_group_type = self.db.share_group_type_get( + context, share_group_type_id) + except exception.ShareGroupTypeNotFound: + msg = _("The specified share group type %s does not exist.") + raise exception.InvalidInput(reason=msg % share_group_type_id) + + supported_share_types = set( + [x['share_type_id'] for x in share_group_type['share_types']]) + + if not set(share_type_ids or []) <= supported_share_types: + msg = _("The specified share types must be a subset of the share " + "types supported by the share group type.") + raise exception.InvalidInput(reason=msg) + options = { - 'source_cgsnapshot_id': source_cgsnapshot_id, + 'share_group_type_id': share_group_type_id, + 'source_share_group_snapshot_id': source_share_group_snapshot_id, 'share_network_id': share_network_id, 'share_server_id': share_server_id, 'name': name, @@ -127,210 +146,216 @@ class API(base.Base): 'user_id': context.user_id, 'project_id': context.project_id, 'status': constants.STATUS_CREATING, - 'share_types': share_type_ids + 'share_types': share_type_ids or supported_share_types } - if original_cg: - options['host'] = original_cg['host'] + if original_share_group: + options['host'] = original_share_group['host'] - cg = self.db.consistency_group_create(context, options) + share_group = self.db.share_group_create(context, options) try: - if cgsnapshot: - members = self.db.cgsnapshot_members_get_all( - context, source_cgsnapshot_id) + if share_group_snapshot: + members = self.db.share_group_snapshot_members_get_all( + context, source_share_group_snapshot_id) for member in members: + share = self.db.share_get(context, member['share_id']) share_type = share_types.get_share_type( - context, member['share_type_id']) + context, share['share_type_id']) member['share_instance'] = self.db.share_instance_get( context, member['share_instance_id'], with_share_data=True) self.share_api.create(context, member['share_proto'], member['size'], None, None, - consistency_group_id=cg['id'], - cgsnapshot_member=member, + share_group_id=share_group['id'], + share_group_snapshot_member=member, share_type=share_type, share_network_id=share_network_id) except Exception: with excutils.save_and_reraise_exception(): - self.db.consistency_group_destroy(context.elevated(), cg['id']) + self.db.share_group_destroy( + context.elevated(), share_group['id']) - request_spec = {'consistency_group_id': cg['id']} + request_spec = {'share_group_id': share_group['id']} request_spec.update(options) request_spec['share_types'] = share_type_objects + request_spec['resource_type'] = share_group_type - if cgsnapshot and original_cg: - self.share_rpcapi.create_consistency_group( - context, cg, original_cg['host']) + if share_group_snapshot and original_share_group: + self.share_rpcapi.create_share_group( + context, share_group, original_share_group['host']) else: - self.scheduler_rpcapi.create_consistency_group( - context, cg_id=cg['id'], request_spec=request_spec, - filter_properties={}) + self.scheduler_rpcapi.create_share_group( + context, share_group_id=share_group['id'], + request_spec=request_spec, filter_properties={}) - return cg + return share_group - def delete(self, context, cg): - """Delete consistency group.""" + def delete(self, context, share_group): + """Delete share group.""" - cg_id = cg['id'] - if not cg['host']: - self.db.consistency_group_destroy(context.elevated(), cg_id) + share_group_id = share_group['id'] + if not share_group['host']: + self.db.share_group_destroy(context.elevated(), share_group_id) return statuses = (constants.STATUS_AVAILABLE, constants.STATUS_ERROR) - if not cg['status'] in statuses: - msg = (_("Consistency group status must be one of %(statuses)s") + if not share_group['status'] in statuses: + msg = (_("Share group status must be one of %(statuses)s") % {"statuses": statuses}) - raise exception.InvalidConsistencyGroup(reason=msg) + raise exception.InvalidShareGroup(reason=msg) - # NOTE(ameade): check for cgsnapshots in the CG - if self.db.count_cgsnapshots_in_consistency_group(context, cg_id): - msg = (_("Cannot delete a consistency group with cgsnapshots")) - raise exception.InvalidConsistencyGroup(reason=msg) + # NOTE(ameade): check for group_snapshots in the group + if self.db.count_share_group_snapshots_in_share_group( + context, share_group_id): + msg = (_("Cannot delete a share group with snapshots")) + raise exception.InvalidShareGroup(reason=msg) - # NOTE(ameade): check for shares in the CG - if self.db.count_shares_in_consistency_group(context, cg_id): - msg = (_("Cannot delete a consistency group with shares")) - raise exception.InvalidConsistencyGroup(reason=msg) + # NOTE(ameade): check for shares in the share group + if self.db.count_shares_in_share_group(context, share_group_id): + msg = (_("Cannot delete a share group with shares")) + raise exception.InvalidShareGroup(reason=msg) - cg = self.db.consistency_group_update( - context, cg_id, {'status': constants.STATUS_DELETING}) + share_group = self.db.share_group_update( + context, share_group_id, {'status': constants.STATUS_DELETING}) - self.share_rpcapi.delete_consistency_group(context, cg) + self.share_rpcapi.delete_share_group(context, share_group) - def update(self, context, cg, fields): - return self.db.consistency_group_update(context, cg['id'], fields) + def update(self, context, group, fields): + return self.db.share_group_update(context, group['id'], fields) - def get(self, context, cg_id): - return self.db.consistency_group_get(context, cg_id) + def get(self, context, share_group_id): + return self.db.share_group_get(context, share_group_id) - def get_all(self, context, detailed=True, search_opts=None): + def get_all(self, context, detailed=True, search_opts=None, sort_key=None, + sort_dir=None): if search_opts is None: search_opts = {} - LOG.debug("Searching for consistency_groups by: %s", + LOG.debug("Searching for share_groups by: %s", six.text_type(search_opts)) - # Get filtered list of consistency_groups - if context.is_admin and search_opts.get('all_tenants'): - consistency_groups = self.db.consistency_group_get_all( - context, detailed=detailed) + # Get filtered list of share_groups + if search_opts.pop('all_tenants', 0) and context.is_admin: + share_groups = self.db.share_group_get_all( + context, detailed=detailed, filters=search_opts, + sort_key=sort_key, sort_dir=sort_dir) else: - consistency_groups = self.db.consistency_group_get_all_by_project( - context, context.project_id, detailed=detailed) + share_groups = self.db.share_group_get_all_by_project( + context, context.project_id, detailed=detailed, + filters=search_opts, sort_key=sort_key, sort_dir=sort_dir) - return consistency_groups - - def create_cgsnapshot(self, context, name=None, description=None, - consistency_group_id=None): - """Create new cgsnapshot.""" + return share_groups + def create_share_group_snapshot(self, context, name=None, description=None, + share_group_id=None): + """Create new share group snapshot.""" options = { - 'consistency_group_id': consistency_group_id, + 'share_group_id': share_group_id, 'name': name, 'description': description, 'user_id': context.user_id, 'project_id': context.project_id, 'status': constants.STATUS_CREATING, } - - cg = self.db.consistency_group_get(context, consistency_group_id) - # Check status of CG, must be active - if not cg['status'] == constants.STATUS_AVAILABLE: - msg = (_("Consistency group status must be %s") + share_group = self.db.share_group_get(context, share_group_id) + # Check status of group, must be active + if not share_group['status'] == constants.STATUS_AVAILABLE: + msg = (_("Share group status must be %s") % constants.STATUS_AVAILABLE) - raise exception.InvalidConsistencyGroup(reason=msg) + raise exception.InvalidShareGroup(reason=msg) - # Create members for every share in the CG - shares = self.db.share_get_all_by_consistency_group_id( - context, consistency_group_id) + # Create members for every share in the group + shares = self.db.share_get_all_by_share_group_id( + context, share_group_id) # Check status of all shares, they must be active in order to snap - # the CG + # the group for s in shares: if not s['status'] == constants.STATUS_AVAILABLE: - msg = (_("Share %(s)s in consistency group must have status " - "of %(status)s in order to create a CG snapshot") + msg = (_("Share %(s)s in share group must have status " + "of %(status)s in order to create a group snapshot") % {"s": s['id'], "status": constants.STATUS_AVAILABLE}) - raise exception.InvalidConsistencyGroup(reason=msg) - - snap = self.db.cgsnapshot_create(context, options) + raise exception.InvalidShareGroup(reason=msg) + snap = self.db.share_group_snapshot_create(context, options) try: members = [] for s in shares: member_options = { - 'cgsnapshot_id': snap['id'], + 'share_group_snapshot_id': snap['id'], 'user_id': context.user_id, 'project_id': context.project_id, 'status': constants.STATUS_CREATING, 'size': s['size'], 'share_proto': s['share_proto'], - 'share_type_id': s['share_type_id'], 'share_id': s['id'], 'share_instance_id': s.instance['id'] } - member = self.db.cgsnapshot_member_create(context, - member_options) + member = self.db.share_group_snapshot_member_create( + context, member_options) members.append(member) # Cast to share manager - self.share_rpcapi.create_cgsnapshot(context, snap, cg['host']) + self.share_rpcapi.create_share_group_snapshot( + context, snap, share_group['host']) except Exception: with excutils.save_and_reraise_exception(): # This will delete the snapshot and all of it's members - self.db.cgsnapshot_destroy(context, snap['id']) + self.db.share_group_snapshot_destroy(context, snap['id']) return snap - def delete_cgsnapshot(self, context, snap): - """Delete consistency group snapshot.""" - + def delete_share_group_snapshot(self, context, snap): + """Delete share group snapshot.""" snap_id = snap['id'] - - cg = self.db.consistency_group_get(context, - snap['consistency_group_id']) - statuses = (constants.STATUS_AVAILABLE, constants.STATUS_ERROR) + share_group = self.db.share_group_get(context, snap['share_group_id']) if not snap['status'] in statuses: - msg = (_("Consistency group snapshot status must be one of" - " %(statuses)s") - % {"statuses": statuses}) - raise exception.InvalidCGSnapshot(reason=msg) + msg = (_("Share group snapshot status must be one of" + " %(statuses)s") % {"statuses": statuses}) + raise exception.InvalidShareGroupSnapshot(reason=msg) - self.db.cgsnapshot_update(context, snap_id, - {'status': constants.STATUS_DELETING}) + self.db.share_group_snapshot_update( + context, snap_id, {'status': constants.STATUS_DELETING}) # Cast to share manager - self.share_rpcapi.delete_cgsnapshot(context, snap, cg['host']) + self.share_rpcapi.delete_share_group_snapshot( + context, snap, share_group['host']) - def update_cgsnapshot(self, context, cg, fields): - return self.db.cgsnapshot_update(context, cg['id'], fields) + def update_share_group_snapshot(self, context, share_group_snapshot, + fields): + return self.db.share_group_snapshot_update( + context, share_group_snapshot['id'], fields) - def get_cgsnapshot(self, context, snapshot_id): - return self.db.cgsnapshot_get(context, snapshot_id) - - def get_all_cgsnapshots(self, context, detailed=True, search_opts=None): + def get_share_group_snapshot(self, context, snapshot_id): + return self.db.share_group_snapshot_get(context, snapshot_id) + def get_all_share_group_snapshots(self, context, detailed=True, + search_opts=None, sort_key=None, + sort_dir=None): if search_opts is None: search_opts = {} - - LOG.debug("Searching for consistency group snapshots by: %s", + LOG.debug("Searching for share group snapshots by: %s", six.text_type(search_opts)) - # Get filtered list of consistency_groups - if context.is_admin and search_opts.get('all_tenants'): - cgsnapshots = self.db.cgsnapshot_get_all( - context, detailed=detailed) + # Get filtered list of share group snapshots + if search_opts.pop('all_tenants', 0) and context.is_admin: + share_group_snapshots = self.db.share_group_snapshot_get_all( + context, detailed=detailed, filters=search_opts, + sort_key=sort_key, sort_dir=sort_dir) else: - cgsnapshots = self.db.cgsnapshot_get_all_by_project( - context, context.project_id, detailed=detailed) - - return cgsnapshots - - def get_all_cgsnapshot_members(self, context, cgsnapshot_id): - members = self.db.cgsnapshot_members_get_all(context, - cgsnapshot_id) + share_group_snapshots = ( + self.db.share_group_snapshot_get_all_by_project( + context, context.project_id, detailed=detailed, + filters=search_opts, sort_key=sort_key, sort_dir=sort_dir, + ) + ) + return share_group_snapshots + def get_all_share_group_snapshot_members(self, context, + share_group_snapshot_id): + members = self.db.share_group_snapshot_members_get_all( + context, share_group_snapshot_id) return members diff --git a/manila/share_group/share_group_types.py b/manila/share_group/share_group_types.py new file mode 100644 index 0000000000..d6ec36d872 --- /dev/null +++ b/manila/share_group/share_group_types.py @@ -0,0 +1,174 @@ +# 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. + +from oslo_config import cfg +from oslo_db import exception as db_exception +from oslo_log import log +from oslo_utils import uuidutils + +from manila.common import constants +from manila import context +from manila import db +from manila import exception +from manila.i18n import _ +from manila.i18n import _LE + +CONF = cfg.CONF +LOG = log.getLogger(__name__) + + +def create(context, name, share_types, group_specs=None, is_public=True, + projects=None): + """Creates share group types.""" + group_specs = group_specs or {} + projects = projects or [] + try: + type_ref = db.share_group_type_create( + context, + {"name": name, "group_specs": group_specs, "is_public": is_public, + "share_types": share_types}, + projects=projects) + except db_exception.DBError: + LOG.exception(_LE('DB error')) + raise exception.ShareGroupTypeCreateFailed( + name=name, group_specs=group_specs) + return type_ref + + +def destroy(context, type_id): + """Marks share group types as deleted.""" + if id is None: + msg = _("Share group type ID cannot be None.") + raise exception.InvalidShareGroupType(reason=msg) + else: + db.share_group_type_destroy(context, type_id) + + +def get_all(context, inactive=0, search_opts=None): + """Get all non-deleted share group types.""" + # TODO(ameade): Fix docstring + search_opts = search_opts or {} + filters = {} + + if 'is_public' in search_opts: + filters['is_public'] = search_opts.pop('is_public') + + share_group_types = db.share_group_type_get_all( + context, inactive, filters=filters) + + if search_opts: + LOG.debug("Searching by: %s", search_opts) + + def _check_group_specs_match(share_group_type, searchdict): + for k, v in searchdict.items(): + if (k not in share_group_type['group_specs'].keys() + or share_group_type['group_specs'][k] != v): + return False + return True + + # search_option to filter_name mapping. + filter_mapping = {'group_specs': _check_group_specs_match} + + result = {} + for type_name, type_args in share_group_types.items(): + # go over all filters in the list + for opt, values in search_opts.items(): + try: + filter_func = filter_mapping[opt] + except KeyError: + # no such filter - ignore it, go to next filter + continue + else: + if filter_func(type_args, values): + result[type_name] = type_args + break + share_group_types = result + return share_group_types + + +def get(ctxt, type_id, expected_fields=None): + """Retrieves single share group type by id.""" + if type_id is None: + msg = _("Share type ID cannot be None.") + raise exception.InvalidShareGroupType(reason=msg) + + if ctxt is None: + ctxt = context.get_admin_context() + + return db.share_group_type_get( + ctxt, type_id, expected_fields=expected_fields) + + +def get_by_name(context, name): + """Retrieves single share group type by name.""" + if name is None: + msg = _("name cannot be None.") + raise exception.InvalidShareGroupType(reason=msg) + + return db.share_group_type_get_by_name(context, name) + + +def get_by_name_or_id(context, share_group_type=None): + if not share_group_type: + share_group_type_ref = get_default(context) + if not share_group_type_ref: + msg = _("Default share group type not found.") + raise exception.ShareGroupTypeNotFound(reason=msg) + return share_group_type_ref + + if uuidutils.is_uuid_like(share_group_type): + return get(context, share_group_type) + else: + return get_by_name(context, share_group_type) + + +def get_default(ctxt=None): + """Get the default share group type.""" + name = CONF.default_share_group_type + if name is None: + return {} + if ctxt is None: + ctxt = context.get_admin_context() + try: + return get_by_name(ctxt, name) + except exception.ShareGroupTypeNotFoundByName: + LOG.exception( + _LE("Default share group type '%s' is not found, " + "please check 'default_share_group_type' config."), + name, + ) + + +def get_tenant_visible_group_specs(): + return constants.ExtraSpecs.TENANT_VISIBLE + + +def get_boolean_group_specs(): + return constants.ExtraSpecs.BOOLEAN + + +def add_share_group_type_access(context, share_group_type_id, project_id): + """Add access to share group type for project_id.""" + if share_group_type_id is None: + msg = _("share_group_type_id cannot be None.") + raise exception.InvalidShareGroupType(reason=msg) + return db.share_group_type_access_add( + context, share_group_type_id, project_id) + + +def remove_share_group_type_access(context, share_group_type_id, project_id): + """Remove access to share group type for project_id.""" + if share_group_type_id is None: + msg = _("share_group_type_id cannot be None.") + raise exception.InvalidShareGroupType(reason=msg) + return db.share_group_type_access_remove( + context, share_group_type_id, project_id) diff --git a/manila/tests/api/contrib/stubs.py b/manila/tests/api/contrib/stubs.py index aab6985bd8..fe53e4aad2 100644 --- a/manila/tests/api/contrib/stubs.py +++ b/manila/tests/api/contrib/stubs.py @@ -178,7 +178,7 @@ def stub_snapshot_get_all_by_project(self, context, search_opts=None, return [stub_snapshot_get(self, context, 2)] -def stub_cgsnapshot_member(id, **kwargs): +def stub_share_group_snapshot_member(id, **kwargs): member = { 'id': id, 'share_id': 'fakeshareid', diff --git a/manila/tests/api/openstack/test_api_version_request.py b/manila/tests/api/openstack/test_api_version_request.py index 204ab2482d..1940ed14ff 100644 --- a/manila/tests/api/openstack/test_api_version_request.py +++ b/manila/tests/api/openstack/test_api_version_request.py @@ -186,13 +186,14 @@ class APIVersionRequestTests(test.TestCase): self.assertRaises(ValueError, api_version_request.APIVersionRequest().get_string) - @ddt.data(('1', '0'), ('1', '1')) + @ddt.data(('1', '0', False), ('1', '1', False), ('1', '0', True)) @ddt.unpack - def test_str(self, major, minor): + def test_str(self, major, minor, experimental): request_input = '%s.%s' % (major, minor) - request = api_version_request.APIVersionRequest(request_input) + request = api_version_request.APIVersionRequest( + request_input, experimental=experimental) request_string = six.text_type(request) self.assertEqual('API Version Request ' - 'Major: %s, Minor: %s' % (major, minor), - request_string) + 'Major: %s, Minor: %s, Experimental: %s' % + (major, minor, experimental), request_string) diff --git a/manila/tests/api/v1/test_shares.py b/manila/tests/api/v1/test_shares.py index d26fc61df8..dd086f502c 100644 --- a/manila/tests/api/v1/test_shares.py +++ b/manila/tests/api/v1/test_shares.py @@ -24,7 +24,6 @@ import six import webob from manila.api import common -from manila.api.openstack import api_version_request as api_version from manila.api.v1 import shares from manila.common import constants from manila import context @@ -149,22 +148,6 @@ class ShareAPITest(test.TestCase): expected = self._get_expected_share_detailed_response(self.share) self.assertEqual(expected, res_dict) - @ddt.data("2.4", "2.5") - def test_share_create_with_consistency_group(self, microversion): - self.mock_object(share_api.API, 'create', self.create_mock) - body = {"share": copy.deepcopy(self.share)} - req = fakes.HTTPRequest.blank('/shares', version=microversion) - - res_dict = self.controller.create(req, body) - - expected = self._get_expected_share_detailed_response(self.share) - expected['share']['consistency_group_id'] = None - expected['share']['source_cgsnapshot_member_id'] = None - if (api_version.APIVersionRequest(microversion) >= - api_version.APIVersionRequest('2.5')): - expected['share']['task_state'] = None - self.assertEqual(expected, res_dict) - def test_share_create_with_valid_default_share_type(self): self.mock_object(share_types, 'get_share_type_by_name', mock.Mock(return_value=self.vt)) @@ -455,22 +438,10 @@ class ShareAPITest(test.TestCase): self.assertEqual(expected, res_dict) - def test_share_show_with_consistency_group(self): - req = fakes.HTTPRequest.blank('/shares/1', version='2.4') - expected = self._get_expected_share_detailed_response() - expected['share']['consistency_group_id'] = None - expected['share']['source_cgsnapshot_member_id'] = None - - res_dict = self.controller.show(req, '1') - - self.assertEqual(expected, res_dict) - def test_share_show_with_share_type_name(self): req = fakes.HTTPRequest.blank('/shares/1', version='2.6') res_dict = self.controller.show(req, '1') expected = self._get_expected_share_detailed_response() - expected['share']['consistency_group_id'] = None - expected['share']['source_cgsnapshot_member_id'] = None expected['share']['share_type_name'] = None expected['share']['task_state'] = None self.assertEqual(expected, res_dict) @@ -497,35 +468,6 @@ class ShareAPITest(test.TestCase): resp = self.controller.delete(req, 1) self.assertEqual(202, resp.status_int) - def test_share_delete_in_consistency_group_param_not_provided(self): - fake_share = stubs.stub_share('fake_share', - consistency_group_id='fake_cg_id') - self.mock_object(share_api.API, 'get', - mock.Mock(return_value=fake_share)) - req = fakes.HTTPRequest.blank('/shares/1') - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.delete, req, 1) - - def test_share_delete_in_consistency_group(self): - fake_share = stubs.stub_share('fake_share', - consistency_group_id='fake_cg_id') - self.mock_object(share_api.API, 'get', - mock.Mock(return_value=fake_share)) - req = fakes.HTTPRequest.blank( - '/shares/1?consistency_group_id=fake_cg_id') - resp = self.controller.delete(req, 1) - self.assertEqual(202, resp.status_int) - - def test_share_delete_in_consistency_group_wrong_id(self): - fake_share = stubs.stub_share('fake_share', - consistency_group_id='fake_cg_id') - self.mock_object(share_api.API, 'get', - mock.Mock(return_value=fake_share)) - req = fakes.HTTPRequest.blank( - '/shares/1?consistency_group_id=not_fake_cg_id') - self.assertRaises(webob.exc.HTTPBadRequest, - self.controller.delete, req, 1) - def test_share_update(self): shr = self.share body = {"share": shr} @@ -538,15 +480,6 @@ class ShareAPITest(test.TestCase): self.assertEqual(shr['is_public'], res_dict['share']['is_public']) - def test_share_update_with_consistency_group(self): - shr = self.share - body = {"share": shr} - - req = fakes.HTTPRequest.blank('/share/1', version="2.4") - res_dict = self.controller.update(req, 1, body) - self.assertIsNone(res_dict['share']["consistency_group_id"]) - self.assertIsNone(res_dict['share']["source_cgsnapshot_member_id"]) - def test_share_not_updates_size(self): req = fakes.HTTPRequest.blank('/share/1') res_dict = self.controller.update(req, 1, {"share": self.share}) @@ -787,22 +720,11 @@ class ShareAPITest(test.TestCase): expected['shares'][0].pop('snapshot_support') self._list_detail_test_common(req, expected) - def test_share_list_detail_with_consistency_group(self): - env = {'QUERY_STRING': 'name=Share+Test+Name'} - req = fakes.HTTPRequest.blank('/shares/detail', environ=env, - version="2.4") - expected = self._list_detail_common_expected() - expected['shares'][0]['consistency_group_id'] = None - expected['shares'][0]['source_cgsnapshot_member_id'] = None - self._list_detail_test_common(req, expected) - def test_share_list_detail_with_task_state(self): env = {'QUERY_STRING': 'name=Share+Test+Name'} req = fakes.HTTPRequest.blank('/shares/detail', environ=env, version="2.5") expected = self._list_detail_common_expected() - expected['shares'][0]['consistency_group_id'] = None - expected['shares'][0]['source_cgsnapshot_member_id'] = None expected['shares'][0]['task_state'] = None self._list_detail_test_common(req, expected) diff --git a/manila/tests/api/v2/test_share_group_snapshots.py b/manila/tests/api/v2/test_share_group_snapshots.py index 13d9849c10..e1eebb501e 100644 --- a/manila/tests/api/v2/test_share_group_snapshots.py +++ b/manila/tests/api/v2/test_share_group_snapshots.py @@ -1,4 +1,4 @@ -# Copyright 2015 Alex Meade +# Copyright 2016 Alex Meade # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -39,24 +39,23 @@ CONF = cfg.CONF @ddt.ddt -class CGSnapshotApiTest(test.TestCase): +class ShareGroupSnapshotAPITest(test.TestCase): def setUp(self): super(self.__class__, self).setUp() - self.controller = share_group_snapshots.CGSnapshotController() + self.controller = share_group_snapshots.ShareGroupSnapshotController() self.resource_name = self.controller.resource_name - self.api_version = '2.4' + self.api_version = '2.31' self.mock_policy_check = self.mock_object( policy, 'check_policy', mock.Mock(return_value=True)) - self.request = fakes.HTTPRequest.blank('/consistency-groups', - version=self.api_version, - experimental=True) + self.request = fakes.HTTPRequest.blank( + '/share-groups', version=self.api_version, experimental=True) self.context = self.request.environ['manila.context'] self.admin_context = context.RequestContext('admin', 'fake', True) self.member_context = context.RequestContext('fake', 'fake') self.flags(rpc_backend='manila.openstack.common.rpc.impl_fake') - def _get_fake_cgsnapshot(self, **values): + def _get_fake_share_group_snapshot(self, **values): snap = { 'id': 'fake_id', 'user_id': 'fakeuser', @@ -64,18 +63,18 @@ class CGSnapshotApiTest(test.TestCase): 'status': constants.STATUS_CREATING, 'name': None, 'description': None, - 'consistency_group_id': None, + 'share_group_id': None, 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), + 'members': [], } snap.update(**values) expected_snap = copy.deepcopy(snap) del expected_snap['user_id'] - expected_snap['links'] = mock.ANY return snap, expected_snap - def _get_fake_simple_cgsnapshot(self, **values): + def _get_fake_simple_share_group_snapshot(self, **values): snap = { 'id': 'fake_id', 'name': None, @@ -83,18 +82,16 @@ class CGSnapshotApiTest(test.TestCase): snap.update(**values) expected_snap = copy.deepcopy(snap) - expected_snap['links'] = mock.ANY return snap, expected_snap - def _get_fake_cgsnapshot_member(self, **values): + def _get_fake_share_group_snapshot_member(self, **values): member = { 'id': 'fake_id', 'user_id': 'fakeuser', 'project_id': 'fakeproject', 'status': constants.STATUS_CREATING, - 'cgsnapshot_id': None, + 'share_group_snapshot_id': None, 'share_proto': None, - 'share_type_id': None, 'share_id': None, 'size': None, 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), @@ -110,110 +107,133 @@ class CGSnapshotApiTest(test.TestCase): return member, expected_member def test_create_invalid_body(self): - body = {"not_cg_snapshot": {}} - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - self.request, body) + body = {"not_group_snapshot": {}} + + self.assertRaises( + webob.exc.HTTPBadRequest, + self.controller.create, self.request, body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_create_no_consistency_group_id(self): - body = {"cgnapshot": {}} - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - self.request, body) + def test_create_no_share_group_id(self): + body = {"share_group_snapshot": {}} + + self.assertRaises( + webob.exc.HTTPBadRequest, + self.controller.create, self.request, body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') def test_create(self): - fake_snap, expected_snap = self._get_fake_cgsnapshot() + fake_snap, expected_snap = self._get_fake_share_group_snapshot() fake_id = six.text_type(uuidutils.generate_uuid()) - self.mock_object(self.controller.cg_api, 'create_cgsnapshot', - mock.Mock(return_value=fake_snap)) - - body = {"cgsnapshot": {"consistency_group_id": fake_id}} + body = {"share_group_snapshot": {"share_group_id": fake_id}} + mock_create = self.mock_object( + self.controller.share_group_api, 'create_share_group_snapshot', + mock.Mock(return_value=fake_snap)) res_dict = self.controller.create(self.request, body) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - self.controller.cg_api.create_cgsnapshot.assert_called_once_with( - self.context, consistency_group_id=fake_id) - self.assertEqual(expected_snap, res_dict['cgsnapshot']) + mock_create.assert_called_once_with( + self.context, share_group_id=fake_id) + res_dict['share_group_snapshot'].pop('links') - def test_create_cg_does_not_exist(self): + self.assertEqual(expected_snap, res_dict['share_group_snapshot']) + + def test_create_group_does_not_exist(self): fake_id = six.text_type(uuidutils.generate_uuid()) - self.mock_object(self.controller.cg_api, 'create_cgsnapshot', - mock.Mock( - side_effect=exception.ConsistencyGroupNotFound( - consistency_group_id=six.text_type( - uuidutils.generate_uuid()) - ))) + body = {"share_group_snapshot": {"share_group_id": fake_id}} + self.mock_object( + self.controller.share_group_api, 'create_share_group_snapshot', + mock.Mock(side_effect=exception.ShareGroupNotFound( + share_group_id=six.text_type(uuidutils.generate_uuid())))) + + self.assertRaises( + webob.exc.HTTPBadRequest, + self.controller.create, self.request, body) - body = {"cgsnapshot": {"consistency_group_id": fake_id}} - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - self.request, body) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_create_cg_does_not_a_uuid(self): - self.mock_object(self.controller.cg_api, 'create_cgsnapshot', - mock.Mock( - side_effect=exception.ConsistencyGroupNotFound( - consistency_group_id='not_a_uuid' - ))) + def test_create_group_does_not_a_uuid(self): + self.mock_object( + self.controller.share_group_api, 'create_share_group_snapshot', + mock.Mock(side_effect=exception.ShareGroupNotFound( + share_group_id='not_a_uuid', + ))) + body = {"share_group_snapshot": {"share_group_id": "not_a_uuid"}} + + self.assertRaises( + webob.exc.HTTPBadRequest, + self.controller.create, self.request, body) - body = {"cgsnapshot": {"consistency_group_id": "not_a_uuid"}} - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - self.request, body) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_create_invalid_cg(self): + def test_create_invalid_share_group(self): fake_id = six.text_type(uuidutils.generate_uuid()) - self.mock_object(self.controller.cg_api, 'create_cgsnapshot', - mock.Mock( - side_effect=exception.InvalidConsistencyGroup( - reason='bad_status' - ))) + body = {"share_group_snapshot": {"share_group_id": fake_id}} + self.mock_object( + self.controller.share_group_api, 'create_share_group_snapshot', + mock.Mock(side_effect=exception.InvalidShareGroup( + reason='bad_status'))) + + self.assertRaises( + webob.exc.HTTPConflict, self.controller.create, self.request, body) - body = {"cgsnapshot": {"consistency_group_id": fake_id}} - self.assertRaises(webob.exc.HTTPConflict, self.controller.create, - self.request, body) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') def test_create_with_name(self): fake_name = 'fake_name' - fake_snap, expected_snap = self._get_fake_cgsnapshot(name=fake_name) + fake_snap, expected_snap = self._get_fake_share_group_snapshot( + name=fake_name) fake_id = six.text_type(uuidutils.generate_uuid()) - self.mock_object(self.controller.cg_api, 'create_cgsnapshot', - mock.Mock(return_value=fake_snap)) - - body = {"cgsnapshot": {"consistency_group_id": fake_id, - "name": fake_name}} + mock_create = self.mock_object( + self.controller.share_group_api, 'create_share_group_snapshot', + mock.Mock(return_value=fake_snap)) + body = { + "share_group_snapshot": { + "share_group_id": fake_id, + "name": fake_name, + } + } res_dict = self.controller.create(self.request, body) - self.controller.cg_api.create_cgsnapshot.assert_called_once_with( - self.context, consistency_group_id=fake_id, name=fake_name) - self.assertEqual(expected_snap, res_dict['cgsnapshot']) + res_dict['share_group_snapshot'].pop('links') + + mock_create.assert_called_once_with( + self.context, share_group_id=fake_id, name=fake_name) + self.assertEqual(expected_snap, res_dict['share_group_snapshot']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') def test_create_with_description(self): fake_description = 'fake_description' - fake_snap, expected_snap = self._get_fake_cgsnapshot( + fake_snap, expected_snap = self._get_fake_share_group_snapshot( description=fake_description) fake_id = six.text_type(uuidutils.generate_uuid()) - self.mock_object(self.controller.cg_api, 'create_cgsnapshot', - mock.Mock(return_value=fake_snap)) + mock_create = self.mock_object( + self.controller.share_group_api, 'create_share_group_snapshot', + mock.Mock(return_value=fake_snap)) - body = {"cgsnapshot": {"consistency_group_id": fake_id, - "description": fake_description}} + body = { + "share_group_snapshot": { + "share_group_id": fake_id, + "description": fake_description, + } + } res_dict = self.controller.create(self.request, body) - self.controller.cg_api.create_cgsnapshot.assert_called_once_with( - self.context, consistency_group_id=fake_id, - description=fake_description) - self.assertEqual(expected_snap, res_dict['cgsnapshot']) + res_dict['share_group_snapshot'].pop('links') + + mock_create.assert_called_once_with( + self.context, share_group_id=fake_id, description=fake_description) + self.assertEqual(expected_snap, res_dict['share_group_snapshot']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') @@ -221,20 +241,27 @@ class CGSnapshotApiTest(test.TestCase): fake_name = 'fake_name' fake_description = 'fake_description' fake_id = six.text_type(uuidutils.generate_uuid()) - fake_snap, expected_snap = self._get_fake_cgsnapshot( + fake_snap, expected_snap = self._get_fake_share_group_snapshot( description=fake_description, name=fake_name) - self.mock_object(self.controller.cg_api, 'create_cgsnapshot', - mock.Mock(return_value=fake_snap)) + mock_create = self.mock_object( + self.controller.share_group_api, 'create_share_group_snapshot', + mock.Mock(return_value=fake_snap)) - body = {"cgsnapshot": {"consistency_group_id": fake_id, - "description": fake_description, - "name": fake_name}} + body = { + "share_group_snapshot": { + "share_group_id": fake_id, + "description": fake_description, + "name": fake_name, + } + } res_dict = self.controller.create(self.request, body) - self.controller.cg_api.create_cgsnapshot.assert_called_once_with( - self.context, consistency_group_id=fake_id, name=fake_name, + res_dict['share_group_snapshot'].pop('links') + + mock_create.assert_called_once_with( + self.context, share_group_id=fake_id, name=fake_name, description=fake_description) - self.assertEqual(expected_snap, res_dict['cgsnapshot']) + self.assertEqual(expected_snap, res_dict['share_group_snapshot']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') @@ -242,36 +269,47 @@ class CGSnapshotApiTest(test.TestCase): fake_name = 'fake_name' fake_description = 'fake_description' fake_id = six.text_type(uuidutils.generate_uuid()) - fake_snap, expected_snap = self._get_fake_cgsnapshot( + fake_snap, expected_snap = self._get_fake_share_group_snapshot( description=fake_description, name=fake_name) - self.mock_object(self.controller.cg_api, 'get_cgsnapshot', - mock.Mock(return_value=fake_snap)) - self.mock_object(self.controller.cg_api, 'update_cgsnapshot', - mock.Mock(return_value=fake_snap)) + self.mock_object( + self.controller.share_group_api, 'get_share_group_snapshot', + mock.Mock(return_value=fake_snap)) + mock_update = self.mock_object( + self.controller.share_group_api, 'update_share_group_snapshot', + mock.Mock(return_value=fake_snap)) - body = {"cgsnapshot": {"description": fake_description, - "name": fake_name}} + body = { + "share_group_snapshot": { + "description": fake_description, + "name": fake_name, + } + } res_dict = self.controller.update(self.request, fake_id, body) - self.controller.cg_api.update_cgsnapshot.assert_called_once_with( + res_dict['share_group_snapshot'].pop('links') + + mock_update.assert_called_once_with( self.context, fake_snap, - dict(name=fake_name, description=fake_description)) - self.assertEqual(expected_snap, res_dict['cgsnapshot']) + {"name": fake_name, "description": fake_description}) + self.assertEqual(expected_snap, res_dict['share_group_snapshot']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'update') def test_update_snapshot_not_found(self): - body = {"cgsnapshot": {}} - self.mock_object(self.controller.cg_api, 'get_cgsnapshot', - mock.Mock(side_effect=exception.NotFound)) - self.assertRaises(webob.exc.HTTPNotFound, - self.controller.update, - self.request, 'fake_id', body) + body = {"share_group_snapshot": {}} + self.mock_object( + self.controller.share_group_api, 'get_share_group_snapshot', + mock.Mock(side_effect=exception.NotFound)) + + self.assertRaises( + webob.exc.HTTPNotFound, + self.controller.update, self.request, 'fake_id', body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'update') def test_update_invalid_body(self): - body = {"not_cgsnapshot": {}} + body = {"not_group_snapshot": {}} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, self.request, 'fake_id', body) @@ -279,7 +317,7 @@ class CGSnapshotApiTest(test.TestCase): self.context, self.resource_name, 'update') def test_update_invalid_body_invalid_field(self): - body = {"cgsnapshot": {"unknown_field": ""}} + body = {"share_group_snapshot": {"unknown_field": ""}} exc = self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, self.request, 'fake_id', body) @@ -288,7 +326,7 @@ class CGSnapshotApiTest(test.TestCase): self.context, self.resource_name, 'update') def test_update_invalid_body_readonly_field(self): - body = {"cgsnapshot": {"created_at": []}} + body = {"share_group_snapshot": {"created_at": []}} exc = self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, self.request, 'fake_id', body) @@ -297,116 +335,147 @@ class CGSnapshotApiTest(test.TestCase): self.context, self.resource_name, 'update') def test_list_index(self): - fake_snap, expected_snap = self._get_fake_simple_cgsnapshot() - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', - mock.Mock(return_value=[fake_snap])) + fake_snap, expected_snap = self._get_fake_simple_share_group_snapshot() + self.mock_object( + self.controller.share_group_api, 'get_all_share_group_snapshots', + mock.Mock(return_value=[fake_snap])) + res_dict = self.controller.index(self.request) - self.assertEqual([expected_snap], res_dict['cgsnapshots']) + + res_dict['share_group_snapshots'][0].pop('links') + + self.assertEqual([expected_snap], res_dict['share_group_snapshots']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'get_all') - def test_list_index_no_cgs(self): - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', - mock.Mock(return_value=[])) + def test_list_index_no_share_groups(self): + self.mock_object( + self.controller.share_group_api, 'get_all_share_group_snapshots', + mock.Mock(return_value=[])) + res_dict = self.controller.index(self.request) - self.assertEqual([], res_dict['cgsnapshots']) + + self.assertEqual([], res_dict['share_group_snapshots']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'get_all') def test_list_index_with_limit(self): - fake_snap, expected_snap = self._get_fake_simple_cgsnapshot() - fake_snap2, expected_snap2 = self._get_fake_simple_cgsnapshot( - id="fake_id2") - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', - mock.Mock(return_value=[fake_snap, fake_snap2])) - req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1', + fake_snap, expected_snap = self._get_fake_simple_share_group_snapshot() + fake_snap2, expected_snap2 = ( + self._get_fake_simple_share_group_snapshot( + id="fake_id2")) + self.mock_object( + self.controller.share_group_api, 'get_all_share_group_snapshots', + mock.Mock(return_value=[fake_snap, fake_snap2])) + req = fakes.HTTPRequest.blank('/share-group-snapshots?limit=1', version=self.api_version, experimental=True) req_context = req.environ['manila.context'] res_dict = self.controller.index(req) - self.assertEqual(1, len(res_dict['cgsnapshots'])) - self.assertEqual([expected_snap], res_dict['cgsnapshots']) + res_dict['share_group_snapshots'][0].pop('links') + + self.assertEqual(1, len(res_dict['share_group_snapshots'])) + self.assertEqual([expected_snap], res_dict['share_group_snapshots']) self.mock_policy_check.assert_called_once_with( req_context, self.resource_name, 'get_all') def test_list_index_with_limit_and_offset(self): - fake_snap, expected_snap = self._get_fake_simple_cgsnapshot() - fake_snap2, expected_snap2 = self._get_fake_simple_cgsnapshot( - id="fake_id2") - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', - mock.Mock(return_value=[fake_snap, fake_snap2])) - req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1&offset=1', - version=self.api_version, - experimental=True) + fake_snap, expected_snap = self._get_fake_simple_share_group_snapshot() + fake_snap2, expected_snap2 = ( + self._get_fake_simple_share_group_snapshot(id="fake_id2")) + self.mock_object( + self.controller.share_group_api, 'get_all_share_group_snapshots', + mock.Mock(return_value=[fake_snap, fake_snap2])) + req = fakes.HTTPRequest.blank( + '/share-group-snapshots?limit=1&offset=1', + version=self.api_version, experimental=True) req_context = req.environ['manila.context'] res_dict = self.controller.index(req) - self.assertEqual(1, len(res_dict['cgsnapshots'])) - self.assertEqual([expected_snap2], res_dict['cgsnapshots']) + res_dict['share_group_snapshots'][0].pop('links') + + self.assertEqual(1, len(res_dict['share_group_snapshots'])) + self.assertEqual([expected_snap2], res_dict['share_group_snapshots']) self.mock_policy_check.assert_called_once_with( req_context, self.resource_name, 'get_all') def test_list_detail(self): - fake_snap, expected_snap = self._get_fake_cgsnapshot() - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', - mock.Mock(return_value=[fake_snap])) + fake_snap, expected_snap = self._get_fake_share_group_snapshot() + self.mock_object( + self.controller.share_group_api, 'get_all_share_group_snapshots', + mock.Mock(return_value=[fake_snap])) + res_dict = self.controller.detail(self.request) - self.assertEqual([expected_snap], res_dict['cgsnapshots']) + + res_dict['share_group_snapshots'][0].pop('links') + + self.assertEqual(1, len(res_dict['share_group_snapshots'])) + self.assertEqual(expected_snap, res_dict['share_group_snapshots'][0]) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'get_all') - def test_list_detail_no_cgs(self): - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', - mock.Mock(return_value=[])) + def test_list_detail_no_share_groups(self): + self.mock_object( + self.controller.share_group_api, 'get_all_share_group_snapshots', + mock.Mock(return_value=[])) res_dict = self.controller.detail(self.request) - self.assertEqual([], res_dict['cgsnapshots']) + self.assertEqual([], res_dict['share_group_snapshots']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'get_all') def test_list_detail_with_limit(self): - fake_snap, expected_snap = self._get_fake_cgsnapshot() - fake_snap2, expected_snap2 = self._get_fake_cgsnapshot( + fake_snap, expected_snap = self._get_fake_share_group_snapshot() + fake_snap2, expected_snap2 = self._get_fake_share_group_snapshot( id="fake_id2") - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', - mock.Mock(return_value=[fake_snap, fake_snap2])) - req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1', + self.mock_object( + self.controller.share_group_api, 'get_all_share_group_snapshots', + mock.Mock(return_value=[fake_snap, fake_snap2])) + req = fakes.HTTPRequest.blank('/share-group-snapshots?limit=1', version=self.api_version, experimental=True) req_context = req.environ['manila.context'] res_dict = self.controller.detail(req) - self.assertEqual(1, len(res_dict['cgsnapshots'])) - self.assertEqual([expected_snap], res_dict['cgsnapshots']) + res_dict['share_group_snapshots'][0].pop('links') + + self.assertEqual(1, len(res_dict['share_group_snapshots'])) + self.assertEqual([expected_snap], res_dict['share_group_snapshots']) self.mock_policy_check.assert_called_once_with( req_context, self.resource_name, 'get_all') def test_list_detail_with_limit_and_offset(self): - fake_snap, expected_snap = self._get_fake_cgsnapshot() - fake_snap2, expected_snap2 = self._get_fake_cgsnapshot( + fake_snap, expected_snap = self._get_fake_share_group_snapshot() + fake_snap2, expected_snap2 = self._get_fake_share_group_snapshot( id="fake_id2") - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', - mock.Mock(return_value=[fake_snap, fake_snap2])) - req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1&offset=1', - version=self.api_version, - experimental=True) + self.mock_object( + self.controller.share_group_api, 'get_all_share_group_snapshots', + mock.Mock(return_value=[fake_snap, fake_snap2])) + req = fakes.HTTPRequest.blank( + '/share-group-snapshots?limit=1&offset=1', + version=self.api_version, + experimental=True) req_context = req.environ['manila.context'] res_dict = self.controller.detail(req) - self.assertEqual(1, len(res_dict['cgsnapshots'])) - self.assertEqual([expected_snap2], res_dict['cgsnapshots']) + res_dict['share_group_snapshots'][0].pop('links') + + self.assertEqual(1, len(res_dict['share_group_snapshots'])) + self.assertEqual([expected_snap2], res_dict['share_group_snapshots']) self.mock_policy_check.assert_called_once_with( req_context, self.resource_name, 'get_all') def test_delete(self): - fake_snap, expected_snap = self._get_fake_cgsnapshot() - self.mock_object(self.controller.cg_api, 'get_cgsnapshot', - mock.Mock(return_value=fake_snap)) - self.mock_object(self.controller.cg_api, 'delete_cgsnapshot') + fake_snap, expected_snap = self._get_fake_share_group_snapshot() + self.mock_object( + self.controller.share_group_api, 'get_share_group_snapshot', + mock.Mock(return_value=fake_snap)) + self.mock_object( + self.controller.share_group_api, 'delete_share_group_snapshot') res = self.controller.delete(self.request, fake_snap['id']) @@ -415,134 +484,89 @@ class CGSnapshotApiTest(test.TestCase): self.context, self.resource_name, 'delete') def test_delete_not_found(self): - fake_snap, expected_snap = self._get_fake_cgsnapshot() - self.mock_object(self.controller.cg_api, 'get_cgsnapshot', - mock.Mock(side_effect=exception.NotFound)) + fake_snap, expected_snap = self._get_fake_share_group_snapshot() + self.mock_object( + self.controller.share_group_api, 'get_share_group_snapshot', + mock.Mock(side_effect=exception.NotFound)) + + self.assertRaises( + webob.exc.HTTPNotFound, + self.controller.delete, self.request, fake_snap['id']) - self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, - self.request, fake_snap['id']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'delete') def test_delete_in_conflicting_status(self): - fake_snap, expected_snap = self._get_fake_cgsnapshot() - self.mock_object(self.controller.cg_api, 'get_cgsnapshot', - mock.Mock(return_value=fake_snap)) - self.mock_object(self.controller.cg_api, 'delete_cgsnapshot', - mock.Mock( - side_effect=exception.InvalidCGSnapshot( - reason='blah'))) + fake_snap, expected_snap = self._get_fake_share_group_snapshot() + self.mock_object( + self.controller.share_group_api, 'get_share_group_snapshot', + mock.Mock(return_value=fake_snap)) + self.mock_object( + self.controller.share_group_api, 'delete_share_group_snapshot', + mock.Mock(side_effect=exception.InvalidShareGroupSnapshot( + reason='blah'))) + + self.assertRaises( + webob.exc.HTTPConflict, + self.controller.delete, self.request, fake_snap['id']) - self.assertRaises(webob.exc.HTTPConflict, self.controller.delete, - self.request, fake_snap['id']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'delete') def test_show(self): - fake_snap, expected_snap = self._get_fake_cgsnapshot() - self.mock_object(self.controller.cg_api, 'get_cgsnapshot', - mock.Mock(return_value=fake_snap)) + fake_snap, expected_snap = self._get_fake_share_group_snapshot() + self.mock_object( + self.controller.share_group_api, 'get_share_group_snapshot', + mock.Mock(return_value=fake_snap)) res_dict = self.controller.show(self.request, fake_snap['id']) - self.assertEqual(expected_snap, res_dict['cgsnapshot']) + res_dict['share_group_snapshot'].pop('links') + + self.assertEqual(expected_snap, res_dict['share_group_snapshot']) self.mock_policy_check.assert_called_once_with( - self.context, self.resource_name, 'get_cgsnapshot') + self.context, self.resource_name, 'get') - def test_show_cg_not_found(self): - fake_snap, expected_snap = self._get_fake_cgsnapshot() - self.mock_object(self.controller.cg_api, 'get_cgsnapshot', - mock.Mock(side_effect=exception.NotFound)) + def test_show_share_group_not_found(self): + fake_snap, expected_snap = self._get_fake_share_group_snapshot() + self.mock_object( + self.controller.share_group_api, 'get_share_group_snapshot', + mock.Mock(side_effect=exception.NotFound)) + + self.assertRaises( + webob.exc.HTTPNotFound, + self.controller.show, self.request, fake_snap['id']) - self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, - self.request, fake_snap['id']) self.mock_policy_check.assert_called_once_with( - self.context, self.resource_name, 'get_cgsnapshot') - - def test_members_empty(self): - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshot_members', - mock.Mock(return_value=[])) - - res_dict = self.controller.members(self.request, 'fake_cg_id') - - self.assertEqual([], res_dict['cgsnapshot_members']) - self.mock_policy_check.assert_called_once_with( - self.context, self.resource_name, 'get_cgsnapshot') - - def test_members(self): - fake_member, expected_member = self._get_fake_cgsnapshot_member() - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshot_members', - mock.Mock(return_value=[fake_member])) - - res_dict = self.controller.members(self.request, 'fake_cg_id') - - self.assertEqual([expected_member], res_dict['cgsnapshot_members']) - self.mock_policy_check.assert_called_once_with( - self.context, self.resource_name, 'get_cgsnapshot') - - def test_members_with_limit(self): - fake_member, expected_member = self._get_fake_cgsnapshot_member() - fake_member2, expected_member2 = self._get_fake_cgsnapshot_member( - id="fake_id2") - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshot_members', - mock.Mock(return_value=[fake_member, fake_member2])) - req = fakes.HTTPRequest.blank('/members?limit=1', - version=self.api_version, - experimental=True) - req_context = req.environ['manila.context'] - - res_dict = self.controller.members(req, 'fake_cg_id') - - self.assertEqual(1, len(res_dict['cgsnapshot_members'])) - self.mock_policy_check.assert_called_once_with( - req_context, self.resource_name, 'get_cgsnapshot') - - def test_members_with_limit_and_offset(self): - fake_member, expected_member = self._get_fake_cgsnapshot_member() - fake_member2, expected_member2 = self._get_fake_cgsnapshot_member( - id="fake_id2") - self.mock_object(self.controller.cg_api, 'get_all_cgsnapshot_members', - mock.Mock(return_value=[fake_member, fake_member2])) - req = fakes.HTTPRequest.blank('/members?limit=1&offset=1', - version=self.api_version, - experimental=True) - req_context = req.environ['manila.context'] - - res_dict = self.controller.members(req, 'fake_cg_id') - - self.assertEqual(1, len(res_dict['cgsnapshot_members'])) - self.assertEqual([expected_member2], res_dict['cgsnapshot_members']) - self.mock_policy_check.assert_called_once_with( - req_context, self.resource_name, 'get_cgsnapshot') + self.context, self.resource_name, 'get') def _get_context(self, role): return getattr(self, '%s_context' % role) - def _setup_cgsnapshot_data(self, cgsnapshot=None, version='2.7'): - if cgsnapshot is None: - cgsnapshot = db_utils.create_cgsnapshot( + def _setup_share_group_snapshot_data(self, share_group_snapshot=None, + version='2.31'): + if share_group_snapshot is None: + share_group_snapshot = db_utils.create_share_group_snapshot( 'fake_id', status=constants.STATUS_AVAILABLE) - req = fakes.HTTPRequest.blank('/v2/fake/cgsnapshots/%s/action' % - cgsnapshot['id'], version=version) + req = fakes.HTTPRequest.blank( + '/v2/fake/share-group-snapshots/%s/action' % + share_group_snapshot['id'], version=version) req.headers[wsgi.API_VERSION_REQUEST_HEADER] = version req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True' - return cgsnapshot, req + return share_group_snapshot, req @ddt.data(*fakes.fixture_force_delete_with_different_roles) @ddt.unpack - def test_cgsnapshot_force_delete_with_different_roles(self, role, - resp_code, version): - cgsnap, req = self._setup_cgsnapshot_data() + def test_share_group_snapshot_force_delete_with_different_roles( + self, role, resp_code, version): + group_snap, req = self._setup_share_group_snapshot_data() ctxt = self._get_context(role) req.method = 'POST' req.headers['content-type'] = 'application/json' - if float(version) > 2.6: - action_name = 'force_delete' - else: - action_name = 'os-force_delete' + action_name = 'force_delete' body = {action_name: {'status': constants.STATUS_ERROR}} req.body = six.b(jsonutils.dumps(body)) - req.headers['X-Openstack-Manila-Api-Version'] = version + req.headers['X-Openstack-Manila-Api-Version'] = self.api_version req.environ['manila.context'] = ctxt with mock.patch.object( @@ -554,19 +578,16 @@ class CGSnapshotApiTest(test.TestCase): @ddt.data(*fakes.fixture_reset_status_with_different_roles) @ddt.unpack - def test_cgsnapshot_reset_status_with_different_roles( + def test_share_group_snapshot_reset_status_with_different_roles( self, role, valid_code, valid_status, version): ctxt = self._get_context(role) - cgsnap, req = self._setup_cgsnapshot_data(version=version) - if float(version) > 2.6: - action_name = 'reset_status' - else: - action_name = 'os-reset_status' + group_snap, req = self._setup_share_group_snapshot_data() + action_name = 'reset_status' body = {action_name: {'status': constants.STATUS_ERROR}} req.method = 'POST' req.headers['content-type'] = 'application/json' req.body = six.b(jsonutils.dumps(body)) - req.headers['X-Openstack-Manila-Api-Version'] = version + req.headers['X-Openstack-Manila-Api-Version'] = self.api_version req.environ['manila.context'] = ctxt with mock.patch.object( @@ -578,9 +599,9 @@ class CGSnapshotApiTest(test.TestCase): if valid_code == 404: self.assertRaises(exception.NotFound, - db.cgsnapshot_get, + db.share_group_snapshot_get, ctxt, - cgsnap['id']) + group_snap['id']) else: - actual_model = db.cgsnapshot_get(ctxt, cgsnap['id']) + actual_model = db.share_group_snapshot_get(ctxt, group_snap['id']) self.assertEqual(valid_status, actual_model['status']) diff --git a/manila/tests/api/v2/test_share_group_type_specs.py b/manila/tests/api/v2/test_share_group_type_specs.py new file mode 100644 index 0000000000..647271c6d7 --- /dev/null +++ b/manila/tests/api/v2/test_share_group_type_specs.py @@ -0,0 +1,354 @@ +# 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 ddt +import mock +from oslo_utils import strutils +import webob + +from manila.api.v2 import share_group_type_specs +from manila import exception +from manila import policy +from manila import test +from manila.tests.api import fakes +import manila.wsgi + +CONSISTENT_SNAPSHOTS = 'consistent_snapshots' + + +def return_create_share_group_type_specs(context, share_group_type_id, + group_specs): + return stub_share_group_type_specs() + + +def return_share_group_type_specs(context, share_group_type_id): + return stub_share_group_type_specs() + + +def return_empty_share_group_type_specs(context, share_group_type_id): + return {} + + +def delete_share_group_type_specs(context, share_group_type_id, key): + pass + + +def delete_share_group_type_specs_not_found(context, share_group_type_id, key): + raise exception.ShareGroupTypeSpecsNotFound("Not Found") + + +def stub_share_group_type_specs(): + return {"key%d" % i: "value%d" % i for i in (1, 2, 3, 4, 5)} + + +def get_large_string(): + return "s" * 256 + + +def get_group_specs_dict(group_specs, include_required=True): + + if not group_specs: + group_specs = {} + + return {'group_specs': group_specs} + + +def fake_request(url, admin=False, experimental=True, version='2.31', + **kwargs): + return fakes.HTTPRequest.blank( + url, use_admin_context=admin, experimental=experimental, + version=version, **kwargs) + + +@ddt.ddt +class ShareGroupTypesSpecsTest(test.TestCase): + + def setUp(self): + super(ShareGroupTypesSpecsTest, self).setUp() + self.flags(host='fake') + self.mock_object(manila.db, 'share_group_type_get') + self.api_path = '/v2/fake/share-group-types/1/group_specs' + self.controller = ( + share_group_type_specs.ShareGroupTypeSpecsController()) + self.resource_name = self.controller.resource_name + self.mock_policy_check = self.mock_object(policy, 'check_policy') + + def test_index(self): + self.mock_object( + manila.db, 'share_group_type_specs_get', + return_share_group_type_specs) + req = fake_request(self.api_path) + req_context = req.environ['manila.context'] + + res_dict = self.controller.index(req, 1) + + self.assertEqual('value1', res_dict['group_specs']['key1']) + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'index') + + def test_index_no_data(self): + self.mock_object(manila.db, 'share_group_type_specs_get', + return_empty_share_group_type_specs) + req = fake_request(self.api_path) + req_context = req.environ['manila.context'] + + res_dict = self.controller.index(req, 1) + + self.assertEqual(0, len(res_dict['group_specs'])) + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'index') + + def test_show(self): + self.mock_object(manila.db, 'share_group_type_specs_get', + return_share_group_type_specs) + req = fake_request(self.api_path + '/key5') + req_context = req.environ['manila.context'] + + res_dict = self.controller.show(req, 1, 'key5') + + self.assertEqual('value5', res_dict['key5']) + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'show') + + def test_show_spec_not_found(self): + self.mock_object(manila.db, 'share_group_type_specs_get', + return_empty_share_group_type_specs) + req = fake_request(self.api_path + '/key6') + req_context = req.environ['manila.context'] + + self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, + req, 1, 'key6') + + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'show') + + def test_delete(self): + self.mock_object(manila.db, 'share_group_type_specs_delete', + delete_share_group_type_specs) + req = fake_request(self.api_path + '/key5') + req_context = req.environ['manila.context'] + + self.controller.delete(req, 1, 'key5') + + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'delete') + + def test_delete_not_found(self): + self.mock_object(manila.db, 'share_group_type_specs_delete', + delete_share_group_type_specs_not_found) + req = fake_request(self.api_path + '/key6') + req_context = req.environ['manila.context'] + + self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, + req, 1, 'key6') + + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'delete') + + @ddt.data( + get_group_specs_dict({}), + {'foo': 'bar'}, + {CONSISTENT_SNAPSHOTS + 'foo': True}, + {'foo' + CONSISTENT_SNAPSHOTS: False}, + *[{CONSISTENT_SNAPSHOTS: v} + for v in strutils.TRUE_STRINGS + strutils.FALSE_STRINGS] + ) + def test_create(self, data): + body = {'group_specs': data} + mock_spec_update_or_create = self.mock_object( + manila.db, 'share_group_type_specs_update_or_create', + mock.Mock(return_value=return_create_share_group_type_specs)) + req = fake_request(self.api_path) + req_context = req.environ['manila.context'] + + res_dict = self.controller.create(req, 1, body) + + for k, v in data.items(): + self.assertIn(k, res_dict['group_specs']) + self.assertEqual(v, res_dict['group_specs'][k]) + mock_spec_update_or_create.assert_called_once_with( + req.environ['manila.context'], 1, body['group_specs']) + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'create') + + def test_create_with_too_small_key(self): + self.mock_object( + manila.db, 'share_group_type_specs_update_or_create', + mock.Mock(return_value=return_create_share_group_type_specs)) + too_small_key = "" + body = {"group_specs": {too_small_key: "value"}} + req = fake_request(self.api_path) + req_context = req.environ['manila.context'] + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, 1, body) + + self.assertFalse( + manila.db.share_group_type_specs_update_or_create.called) + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'create') + + def test_create_with_too_big_key(self): + self.mock_object( + manila.db, 'share_group_type_specs_update_or_create', + mock.Mock(return_value=return_create_share_group_type_specs)) + too_big_key = "k" * 256 + body = {"group_specs": {too_big_key: "value"}} + req = fake_request(self.api_path) + req_context = req.environ['manila.context'] + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, 1, body) + + self.assertFalse( + manila.db.share_group_type_specs_update_or_create.called) + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'create') + + def test_create_with_too_small_value(self): + self.mock_object( + manila.db, 'share_group_type_specs_update_or_create', + mock.Mock(return_value=return_create_share_group_type_specs)) + too_small_value = "" + body = {"group_specs": {"key": too_small_value}} + req = fake_request(self.api_path) + req_context = req.environ['manila.context'] + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, 1, body) + + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'create') + self.assertFalse( + manila.db.share_group_type_specs_update_or_create.called) + + def test_create_with_too_big_value(self): + self.mock_object( + manila.db, 'share_group_type_specs_update_or_create', + mock.Mock(return_value=return_create_share_group_type_specs)) + too_big_value = "v" * 256 + body = {"extra_specs": {"key": too_big_value}} + req = fake_request(self.api_path) + req_context = req.environ['manila.context'] + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, 1, body) + + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'create') + self.assertFalse( + manila.db.share_group_type_specs_update_or_create.called) + + def test_create_key_allowed_chars(self): + mock_return_value = stub_share_group_type_specs() + mock_spec_update_or_create = self.mock_object( + manila.db, 'share_group_type_specs_update_or_create', + mock.Mock(return_value=mock_return_value)) + body = get_group_specs_dict({"other_alphanum.-_:": "value1"}) + req = fake_request(self.api_path) + req_context = req.environ['manila.context'] + + res_dict = self.controller.create(req, 1, body) + + self.assertEqual(mock_return_value['key1'], + res_dict['group_specs']['other_alphanum.-_:']) + mock_spec_update_or_create.assert_called_once_with( + req.environ['manila.context'], 1, body['group_specs']) + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'create') + + def test_create_too_many_keys_allowed_chars(self): + mock_return_value = stub_share_group_type_specs() + mock_spec_update_or_create = self.mock_object( + manila.db, 'share_group_type_specs_update_or_create', + mock.Mock(return_value=mock_return_value)) + body = get_group_specs_dict({ + "other_alphanum.-_:": "value1", + "other2_alphanum.-_:": "value2", + "other3_alphanum.-_:": "value3", + }) + req = fake_request(self.api_path) + req_context = req.environ['manila.context'] + + res_dict = self.controller.create(req, 1, body) + + self.assertEqual(mock_return_value['key1'], + res_dict['group_specs']['other_alphanum.-_:']) + self.assertEqual(mock_return_value['key2'], + res_dict['group_specs']['other2_alphanum.-_:']) + self.assertEqual(mock_return_value['key3'], + res_dict['group_specs']['other3_alphanum.-_:']) + mock_spec_update_or_create.assert_called_once_with( + req_context, 1, body['group_specs']) + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'create') + + def test_update_item_too_many_keys(self): + self.mock_object(manila.db, 'share_group_type_specs_update_or_create') + body = {"key1": "value1", "key2": "value2"} + req = fake_request(self.api_path + '/key1') + req_context = req.environ['manila.context'] + + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 1, 'key1', body) + + self.assertFalse( + manila.db.share_group_type_specs_update_or_create.called) + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'update') + + def test_update_item_body_uri_mismatch(self): + self.mock_object(manila.db, 'share_group_type_specs_update_or_create') + body = {"key1": "value1"} + req = fake_request(self.api_path + '/bad') + req_context = req.environ['manila.context'] + + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, + req, 1, 'bad', body) + + self.assertFalse( + manila.db.share_group_type_specs_update_or_create.called) + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'update') + + @ddt.data(None, {}, {"group_specs": {CONSISTENT_SNAPSHOTS: ""}}) + def test_update_invalid_body(self, body): + req = fake_request('/v2/fake/share-group-types/1/group_specs') + req_context = req.environ['manila.context'] + req.method = 'POST' + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.update, req, '1', body) + + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'update') + + @ddt.data( + None, {}, {'foo': {'a': 'b'}}, {'group_specs': 'string'}, + {"group_specs": {"ke/y1": "value1"}}, + {"key1": "value1", "ke/y2": "value2", "key3": "value3"}, + {"group_specs": {CONSISTENT_SNAPSHOTS: ""}}, + {"group_specs": {"": "value"}}, + {"group_specs": {"t": get_large_string()}}, + {"group_specs": {get_large_string(): get_large_string()}}, + {"group_specs": {get_large_string(): "v"}}, + {"group_specs": {"k": ""}}) + def test_create_invalid_body(self, body): + req = fake_request('/v2/fake/share-group-types/1/group_specs') + req_context = req.environ['manila.context'] + req.method = 'POST' + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.create, req, '1', body) + + self.mock_policy_check.assert_called_once_with( + req_context, self.resource_name, 'create') diff --git a/manila/tests/api/v2/test_share_group_types.py b/manila/tests/api/v2/test_share_group_types.py new file mode 100644 index 0000000000..319289e5e3 --- /dev/null +++ b/manila/tests/api/v2/test_share_group_types.py @@ -0,0 +1,592 @@ +# 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 datetime + +import ddt +import mock +import webob + +from manila.api.v2 import share_group_types as types +from manila import exception +from manila import policy +from manila.share_group import share_group_types +from manila import test +from manila.tests.api import fakes + +PROJ1_UUID = '11111111-1111-1111-1111-111111111111' +PROJ2_UUID = '22222222-2222-2222-2222-222222222222' +PROJ3_UUID = '33333333-3333-3333-3333-333333333333' + +SHARE_TYPE_ID = '4b1e460f-8bc5-4a97-989b-739a2eceaec6' +GROUP_TYPE_1 = { + 'id': 'c8d7bf70-0db9-4b3e-8498-055dd0306461', + 'name': u'group type 1', + 'deleted': False, + 'created_at': datetime.datetime(2012, 1, 1, 1, 1, 1, 1), + 'updated_at': None, + 'deleted_at': None, + 'is_public': True, + 'group_specs': {}, + 'share_types': [], +} + +GROUP_TYPE_2 = { + 'id': 'f93f7a1f-62d7-4e7e-b9e6-72eec95a47f5', + 'name': u'group type 2', + 'deleted': False, + 'created_at': datetime.datetime(2012, 1, 1, 1, 1, 1, 1), + 'updated_at': None, + 'deleted_at': None, + 'is_public': False, + 'group_specs': {'consistent_snapshots': 'true'}, + 'share_types': [{'share_type_id': SHARE_TYPE_ID}], +} + + +def fake_request(url, admin=False, experimental=True, version='2.31', + **kwargs): + + return fakes.HTTPRequest.blank( + url, + use_admin_context=admin, + experimental=experimental, + version=version, + **kwargs + ) + + +@ddt.ddt +class ShareGroupTypesAPITest(test.TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.flags(host='fake') + self.controller = types.ShareGroupTypesController() + self.resource_name = self.controller.resource_name + self.mock_object(policy, 'check_policy', mock.Mock(return_value=True)) + + def test_share_group_types_index(self): + fake_types = {GROUP_TYPE_1['name']: GROUP_TYPE_1} + mock_get_all = self.mock_object( + share_group_types, 'get_all', mock.Mock(return_value=fake_types)) + req = fake_request('/v2/fake/share-group-types', admin=False) + expected_list = [{ + 'id': GROUP_TYPE_1['id'], + 'name': GROUP_TYPE_1['name'], + 'is_public': True, + 'group_specs': {}, + 'share_types': [], + }] + + res_dict = self.controller.index(req) + + mock_get_all.assert_called_once_with( + mock.ANY, search_opts={"is_public": True}) + self.assertEqual(1, len(res_dict['share_group_types'])) + self.assertEqual(expected_list, res_dict['share_group_types']) + + def test_share_group_types_index_as_admin(self): + fake_types = { + GROUP_TYPE_1['name']: GROUP_TYPE_1, + GROUP_TYPE_2['name']: GROUP_TYPE_2, + } + mock_get_all = self.mock_object( + share_group_types, 'get_all', + mock.Mock(return_value=fake_types)) + req = fake_request( + '/v2/fake/share-group-types?is_public=all', admin=True) + expected_type_1 = { + 'id': GROUP_TYPE_1['id'], + 'name': GROUP_TYPE_1['name'], + 'is_public': True, + 'group_specs': {}, + 'share_types': [], + } + expected_type_2 = { + 'id': GROUP_TYPE_2['id'], + 'name': GROUP_TYPE_2['name'], + 'is_public': False, + 'group_specs': {'consistent_snapshots': 'true'}, + 'share_types': [SHARE_TYPE_ID], + } + + res_dict = self.controller.index(req) + + mock_get_all.assert_called_once_with( + mock.ANY, search_opts={'is_public': None}) + self.assertEqual(2, len(res_dict['share_group_types'])) + self.assertIn(expected_type_1, res_dict['share_group_types']) + self.assertIn(expected_type_2, res_dict['share_group_types']) + + def test_share_group_types_index_as_admin_default_public_only(self): + fake_types = {} + mock_get_all = self.mock_object( + share_group_types, 'get_all', + mock.Mock(return_value=fake_types)) + req = fake_request('/v2/fake/share-group-types', admin=True) + + self.controller.index(req) + + mock_get_all.assert_called_once_with( + mock.ANY, search_opts={'is_public': True}) + + def test_share_group_types_index_not_experimental(self): + self.mock_object( + share_group_types, 'get_all', mock.Mock(return_value={})) + req = fake_request('/v2/fake/share-group-types', experimental=False) + + self.assertRaises( + exception.VersionNotFoundForAPIMethod, self.controller.index, req) + self.assertFalse(share_group_types.get_all.called) + + def test_share_group_types_index_older_api_version(self): + self.mock_object( + share_group_types, 'get_all', mock.Mock(return_value={})) + req = fake_request('/v2/fake/share-group-types', version='2.1') + + self.assertRaises( + exception.VersionNotFoundForAPIMethod, self.controller.index, req) + + @ddt.data(True, False) + def test_share_group_types_index_no_data(self, admin): + self.mock_object( + share_group_types, 'get_all', mock.Mock(return_value={})) + req = fake_request('/v2/fake/share-group-types', admin=admin) + + res_dict = self.controller.index(req) + + self.assertEqual(0, len(res_dict['share_group_types'])) + + def test_share_group_types_show(self): + mock_get = self.mock_object( + share_group_types, 'get', + mock.Mock(return_value=GROUP_TYPE_1)) + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id']) + expected_type = { + 'id': GROUP_TYPE_1['id'], + 'name': GROUP_TYPE_1['name'], + 'is_public': True, + 'group_specs': {}, + 'share_types': [], + } + + res_dict = self.controller.show(req, GROUP_TYPE_1['id']) + + mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_1['id']) + self.assertEqual(expected_type, res_dict['share_group_type']) + + def test_share_group_types_show_with_share_types(self): + mock_get = self.mock_object( + share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_2)) + req = fake_request('/v2/fake/group-types/%s' % GROUP_TYPE_2['id']) + expected_type = { + 'id': GROUP_TYPE_2['id'], + 'name': GROUP_TYPE_2['name'], + 'is_public': False, + 'group_specs': {'consistent_snapshots': 'true'}, + 'share_types': [SHARE_TYPE_ID], + } + + res_dict = self.controller.show(req, GROUP_TYPE_2['id']) + + mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_2['id']) + self.assertEqual(expected_type, res_dict['share_group_type']) + + def test_share_group_types_show_not_found(self): + mock_get = self.mock_object( + share_group_types, 'get', + mock.Mock(side_effect=exception.ShareGroupTypeNotFound( + type_id=GROUP_TYPE_2['id']))) + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id']) + + self.assertRaises( + webob.exc.HTTPNotFound, + self.controller.show, req, GROUP_TYPE_2['id']) + + mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_2['id']) + + def test_share_group_types_default(self): + mock_get = self.mock_object( + share_group_types, 'get_default', + mock.Mock(return_value=GROUP_TYPE_2)) + req = fake_request('/v2/fake/share-group-types/default') + expected_type = { + 'id': GROUP_TYPE_2['id'], + 'name': GROUP_TYPE_2['name'], + 'is_public': False, + 'group_specs': {'consistent_snapshots': 'true'}, + 'share_types': [SHARE_TYPE_ID], + } + + res_dict = self.controller.default(req) + + mock_get.assert_called_once_with(mock.ANY) + self.assertEqual(expected_type, res_dict['share_group_type']) + + def test_share_group_types_default_not_found(self): + mock_get = self.mock_object( + share_group_types, 'get_default', mock.Mock(return_value=None)) + req = fake_request('/v2/fake/share-group-types/default') + + self.assertRaises(webob.exc.HTTPNotFound, self.controller.default, req) + + mock_get.assert_called_once_with(mock.ANY) + + def test_share_group_types_delete(self): + mock_get = self.mock_object( + share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_1)) + mock_destroy = self.mock_object(share_group_types, 'destroy') + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id']) + + self.controller._delete(req, GROUP_TYPE_1['id']) + + mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_1['id']) + mock_destroy.assert_called_once_with(mock.ANY, GROUP_TYPE_1['id']) + + def test_share_group_types_delete_not_found(self): + mock_get = self.mock_object( + share_group_types, 'get', + mock.Mock(side_effect=exception.ShareGroupTypeNotFound( + type_id=GROUP_TYPE_2['id']))) + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id']) + + self.assertRaises(webob.exc.HTTPNotFound, self.controller._delete, + req, GROUP_TYPE_2['id']) + + mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_2['id']) + + def test_create_minimal(self): + fake_type = copy.deepcopy(GROUP_TYPE_1) + fake_type['share_types'] = [{'share_type_id': SHARE_TYPE_ID}] + mock_create = self.mock_object(share_group_types, 'create') + mock_get = self.mock_object( + share_group_types, 'get_by_name', + mock.Mock(return_value=fake_type)) + req = fake_request('/v2/fake/share-group-types') + fake_body = {'share_group_type': { + 'name': GROUP_TYPE_1['name'], + 'share_types': [SHARE_TYPE_ID], + }} + expected_type = { + 'id': GROUP_TYPE_1['id'], + 'name': GROUP_TYPE_1['name'], + 'is_public': True, + 'group_specs': {}, + 'share_types': [SHARE_TYPE_ID], + } + + res_dict = self.controller._create(req, fake_body) + + mock_create.assert_called_once_with( + mock.ANY, GROUP_TYPE_1['name'], + [SHARE_TYPE_ID], {}, True) + mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_1['name']) + self.assertEqual(expected_type, res_dict['share_group_type']) + + def test_create_with_group_specs(self): + fake_group_specs = {'my_fake_group_spec': 'false'} + fake_type = copy.deepcopy(GROUP_TYPE_1) + fake_type['share_types'] = [{'share_type_id': SHARE_TYPE_ID}] + fake_type['group_specs'] = fake_group_specs + mock_create = self.mock_object(share_group_types, 'create') + mock_get = self.mock_object( + share_group_types, 'get_by_name', + mock.Mock(return_value=fake_type)) + req = fake_request('/v2/fake/share-group-types') + fake_body = {'share_group_type': { + 'name': GROUP_TYPE_1['name'], + 'share_types': [SHARE_TYPE_ID], + 'group_specs': fake_group_specs, + }} + expected_type = { + 'id': GROUP_TYPE_1['id'], + 'name': GROUP_TYPE_1['name'], + 'is_public': True, + 'group_specs': fake_group_specs, + 'share_types': [SHARE_TYPE_ID], + } + + res_dict = self.controller._create(req, fake_body) + + mock_create.assert_called_once_with( + mock.ANY, GROUP_TYPE_1['name'], [SHARE_TYPE_ID], fake_group_specs, + True) + mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_1['name']) + self.assertEqual(expected_type, res_dict['share_group_type']) + + def test_create_private_share_group_type(self): + fake_type = copy.deepcopy(GROUP_TYPE_1) + fake_type['share_types'] = [{'share_type_id': SHARE_TYPE_ID}] + fake_type['is_public'] = False + mock_create = self.mock_object(share_group_types, 'create') + mock_get = self.mock_object( + share_group_types, 'get_by_name', + mock.Mock(return_value=fake_type)) + req = fake_request('/v2/fake/share-group-types') + fake_body = {'share_group_type': { + 'name': GROUP_TYPE_1['name'], + 'share_types': [SHARE_TYPE_ID], + 'is_public': False + }} + expected_type = { + 'id': GROUP_TYPE_1['id'], + 'name': GROUP_TYPE_1['name'], + 'is_public': False, + 'group_specs': {}, + 'share_types': [SHARE_TYPE_ID], + } + + res_dict = self.controller._create(req, fake_body) + + mock_create.assert_called_once_with( + mock.ANY, GROUP_TYPE_1['name'], [SHARE_TYPE_ID], {}, False) + mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_1['name']) + self.assertEqual(expected_type, res_dict['share_group_type']) + + def test_create_invalid_request_duplicate_name(self): + mock_create = self.mock_object( + share_group_types, 'create', + mock.Mock(side_effect=exception.ShareGroupTypeExists( + type_id=GROUP_TYPE_1['name']))) + req = fake_request('/v2/fake/sahre-group-types') + fake_body = {'share_group_type': { + 'name': GROUP_TYPE_1['name'], + 'share_types': [SHARE_TYPE_ID], + }} + + self.assertRaises( + webob.exc.HTTPConflict, self.controller._create, req, fake_body) + + mock_create.assert_called_once_with( + mock.ANY, GROUP_TYPE_1['name'], [SHARE_TYPE_ID], {}, True) + + def test_create_invalid_request_missing_name(self): + req = fake_request('/v2/fake/share-group-types') + fake_body = {'share_group_type': {'share_types': [SHARE_TYPE_ID]}} + + self.assertRaises( + webob.exc.HTTPBadRequest, self.controller._create, req, fake_body) + + def test_create_invalid_request_missing_share_types(self): + req = fake_request('/v2/fake/share-group-types') + fake_body = {'share_group_type': {'name': GROUP_TYPE_1['name']}} + + self.assertRaises( + webob.exc.HTTPBadRequest, + self.controller._create, req, fake_body) + + +class ShareGroupTypeAccessTest(test.TestCase): + + def setUp(self): + super(self.__class__, self).setUp() + self.controller = types.ShareGroupTypesController() + + def test_list_type_access_public(self): + self.mock_object( + share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_1)) + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'], admin=True) + + self.assertRaises( + webob.exc.HTTPNotFound, + self.controller.share_group_type_access, req, GROUP_TYPE_1['id']) + + def test_list_type_access_private(self): + fake_type = copy.deepcopy(GROUP_TYPE_2) + fake_type['projects'] = [PROJ2_UUID, PROJ3_UUID] + mock_get = self.mock_object( + share_group_types, 'get', mock.Mock(return_value=fake_type)) + expected = {'share_group_type_access': [ + {'share_group_type_id': fake_type['id'], 'project_id': PROJ2_UUID}, + {'share_group_type_id': fake_type['id'], 'project_id': PROJ3_UUID}, + ]} + req = fake_request( + '/v2/fake/share-group-types/%s' % fake_type['id'], admin=True) + + actual = self.controller.share_group_type_access(req, fake_type['id']) + + mock_get.assert_called_once_with( + mock.ANY, fake_type['id'], expected_fields=['projects']) + self.assertEqual(expected, actual) + + def test_list_type_access_type_not_found(self): + self.mock_object( + share_group_types, 'get', + mock.Mock(side_effect=exception.ShareGroupTypeNotFound( + type_id=GROUP_TYPE_2['id']))) + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True) + + self.assertRaises( + webob.exc.HTTPNotFound, + self.controller.share_group_type_access, req, GROUP_TYPE_2['id']) + + def test_add_project_access(self): + self.mock_object(share_group_types, 'get', + mock.Mock(return_value=GROUP_TYPE_2)) + mock_add_access = self.mock_object( + share_group_types, 'add_share_group_type_access') + body = {'addProjectAccess': {'project': PROJ1_UUID}} + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True) + + response = self.controller._add_project_access( + req, GROUP_TYPE_2['id'], body) + + mock_add_access.assert_called_once_with( + mock.ANY, GROUP_TYPE_2['id'], PROJ1_UUID) + self.assertEqual(202, response.status_code) + + def test_add_project_access_non_existent_type(self): + self.mock_object( + share_group_types, 'get', + mock.Mock(side_effect=exception.ShareGroupTypeNotFound( + type_id=GROUP_TYPE_2['id']))) + body = {'addProjectAccess': {'project': PROJ1_UUID}} + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True) + + self.assertRaises( + webob.exc.HTTPNotFound, + self.controller._add_project_access, req, GROUP_TYPE_2['id'], body) + + def test_add_project_access_missing_project_in_body(self): + body = {'addProjectAccess': {}} + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True) + + self.assertRaises( + webob.exc.HTTPBadRequest, + self.controller._add_project_access, req, GROUP_TYPE_2['id'], body) + + def test_add_project_access_missing_add_project_access_in_body(self): + body = {} + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True) + + self.assertRaises( + webob.exc.HTTPBadRequest, + self.controller._add_project_access, req, GROUP_TYPE_2['id'], body) + + def test_add_project_access_with_already_added_access(self): + self.mock_object( + share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_2)) + mock_add_access = self.mock_object( + share_group_types, 'add_share_group_type_access', + mock.Mock(side_effect=exception.ShareGroupTypeAccessExists( + type_id=GROUP_TYPE_2['id'], project_id=PROJ1_UUID)) + ) + body = {'addProjectAccess': {'project': PROJ1_UUID}} + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True) + + self.assertRaises( + webob.exc.HTTPConflict, + self.controller._add_project_access, req, GROUP_TYPE_2['id'], body) + + mock_add_access.assert_called_once_with( + mock.ANY, GROUP_TYPE_2['id'], PROJ1_UUID) + + def test_add_project_access_to_public_share_type(self): + self.mock_object( + share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_1)) + body = {'addProjectAccess': {'project': PROJ1_UUID}} + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'], admin=True) + + self.assertRaises( + webob.exc.HTTPConflict, + self.controller._add_project_access, req, GROUP_TYPE_1['id'], body) + + def test_remove_project_access(self): + self.mock_object( + share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_2)) + mock_remove_access = self.mock_object( + share_group_types, 'remove_share_group_type_access') + body = {'removeProjectAccess': {'project': PROJ1_UUID}} + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True) + + response = self.controller._remove_project_access( + req, GROUP_TYPE_2['id'], body) + + mock_remove_access.assert_called_once_with( + mock.ANY, GROUP_TYPE_2['id'], PROJ1_UUID) + self.assertEqual(202, response.status_code) + + def test_remove_project_access_nonexistent_rule(self): + self.mock_object( + share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_2)) + mock_remove_access = self.mock_object( + share_group_types, 'remove_share_group_type_access', mock.Mock( + side_effect=exception.ShareGroupTypeAccessNotFound( + type_id=GROUP_TYPE_2['id'], project_id=PROJ1_UUID))) + body = {'removeProjectAccess': {'project': PROJ1_UUID}} + req = fake_request('/v2/fake/group-types/%s' % GROUP_TYPE_2['id'], + admin=True) + + self.assertRaises( + webob.exc.HTTPNotFound, + self.controller._remove_project_access, + req, GROUP_TYPE_2['id'], body) + + mock_remove_access.assert_called_once_with( + mock.ANY, GROUP_TYPE_2['id'], PROJ1_UUID) + + def test_remove_project_access_from_public_share_type(self): + self.mock_object( + share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_1)) + body = {'removeProjectAccess': {'project': PROJ1_UUID}} + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'], admin=True) + + self.assertRaises(webob.exc.HTTPConflict, + self.controller._remove_project_access, req, + GROUP_TYPE_1['id'], body) + + def test_remove_project_access_non_existent_type(self): + self.mock_object( + share_group_types, 'get', + mock.Mock(side_effect=exception.ShareGroupTypeNotFound( + type_id=GROUP_TYPE_2['id']))) + body = {'removeProjectAccess': {'project': PROJ1_UUID}} + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True) + + self.assertRaises(webob.exc.HTTPNotFound, + self.controller._remove_project_access, req, + GROUP_TYPE_2['id'], body) + + def test_remove_project_access_missing_project_in_body(self): + body = {'removeProjectAccess': {}} + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True) + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller._remove_project_access, req, + GROUP_TYPE_2['id'], body) + + def test_remove_project_access_missing_remove_project_access_in_body(self): + body = {} + req = fake_request( + '/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True) + + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller._remove_project_access, req, + GROUP_TYPE_2['id'], body) diff --git a/manila/tests/api/v2/test_share_groups.py b/manila/tests/api/v2/test_share_groups.py index 73df6d0275..2a806eb686 100644 --- a/manila/tests/api/v2/test_share_groups.py +++ b/manila/tests/api/v2/test_share_groups.py @@ -25,14 +25,15 @@ import six import webob from manila.api.openstack import wsgi -from manila.api.v2 import share_groups +import manila.api.v2.share_groups as share_groups from manila.common import constants from manila import context from manila import db from manila import exception from manila import policy 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 from manila import test from manila.tests.api import fakes from manila.tests import db_utils @@ -42,45 +43,50 @@ CONF = cfg.CONF @ddt.ddt -class CGApiTest(test.TestCase): +class ShareGroupAPITest(test.TestCase): """Consistency Groups API Test suite.""" def setUp(self): super(self.__class__, self).setUp() - self.controller = share_groups.CGController() + self.controller = share_groups.ShareGroupController() self.resource_name = self.controller.resource_name self.fake_share_type = {'id': six.text_type(uuidutils.generate_uuid())} - self.api_version = '2.4' - self.request = fakes.HTTPRequest.blank('/consistency-groups', - version=self.api_version, - experimental=True) + self.fake_share_group_type = { + 'id': six.text_type(uuidutils.generate_uuid())} + self.api_version = '2.31' + self.request = fakes.HTTPRequest.blank( + '/share-groups', version=self.api_version, experimental=True) self.flags(rpc_backend='manila.openstack.common.rpc.impl_fake') self.admin_context = context.RequestContext('admin', 'fake', True) self.member_context = context.RequestContext('fake', 'fake') self.mock_policy_check = self.mock_object( policy, 'check_policy', mock.Mock(return_value=True)) self.context = self.request.environ['manila.context'] + self.mock_object(share_group_types, 'get_default', + mock.Mock(return_value=self.fake_share_group_type)) + self.mock_object(share_types, 'get_default_share_type', + mock.Mock(return_value=self.fake_share_type)) def _get_context(self, role): return getattr(self, '%s_context' % role) - def _setup_cg_data(self, cg=None, version='2.7'): - if cg is None: - cg = db_utils.create_consistency_group( + def _setup_share_group_data(self, share_group=None, version='2.31'): + if share_group is None: + share_group = db_utils.create_share_group( status=constants.STATUS_AVAILABLE) - req = fakes.HTTPRequest.blank('/v2/fake/consistency-groups/%s/action' % - cg['id'], version=version) + req = fakes.HTTPRequest.blank( + '/v2/fake/share-groups/%s/action' % share_group['id'], + version=version) req.headers[wsgi.API_VERSION_REQUEST_HEADER] = version req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True' - return cg, req - - def _get_fake_cg(self, ctxt=None, **values): + return share_group, req + def _get_fake_share_group(self, ctxt=None, **values): if ctxt is None: ctxt = self.context - cg = { + share_group_db_dict = { 'id': 'fake_id', 'user_id': 'fakeuser', 'project_id': 'fakeproject', @@ -88,141 +94,217 @@ class CGApiTest(test.TestCase): 'name': None, 'description': None, 'host': None, - 'source_cgsnapshot_id': None, + 'source_share_group_snapshot_id': None, + 'share_group_type_id': self.fake_share_group_type.get('id'), 'share_network_id': uuidutils.generate_uuid(), 'share_server_id': uuidutils.generate_uuid(), 'share_types': [], 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), } - cg.update(**values) + share_group_db_dict.update(**values) - expected_cg = copy.deepcopy(cg) - del expected_cg['user_id'] + expected_share_group = { + 'id': share_group_db_dict['id'], + 'project_id': share_group_db_dict['project_id'], + 'status': share_group_db_dict['status'], + 'name': share_group_db_dict['name'], + 'description': share_group_db_dict['description'], + 'host': share_group_db_dict['host'], + 'source_share_group_snapshot_id': share_group_db_dict[ + 'source_share_group_snapshot_id'], + 'share_group_type_id': share_group_db_dict['share_group_type_id'], + 'share_network_id': share_group_db_dict['share_network_id'], + 'share_server_id': share_group_db_dict['share_server_id'], + 'share_types': [st['share_type_id'] + for st in share_group_db_dict.get('share_types')], + 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), + 'links': mock.ANY, + } if not ctxt.is_admin: - del expected_cg['share_server_id'] - expected_cg['links'] = mock.ANY - expected_cg['share_types'] = [st['share_type_id'] - for st in cg.get('share_types')] - return cg, expected_cg + del expected_share_group['share_server_id'] - def _get_fake_simple_cg(self, **values): - cg = { - 'id': 'fake_id', - 'name': None, + return share_group_db_dict, expected_share_group + + def _get_fake_simple_share_group(self, **values): + share_group = {'id': 'fake_id', 'name': None} + share_group.update(**values) + expected_share_group = copy.deepcopy(share_group) + expected_share_group['links'] = mock.ANY + return share_group, expected_share_group + + def test_share_group_create(self): + fake, expected = self._get_fake_share_group() + self.mock_object(share_types, 'get_default_share_type', + mock.Mock(return_value=self.fake_share_type)) + self.mock_object(self.controller.share_group_api, 'create', + mock.Mock(return_value=fake)) + body = {"share_group": {}} + + res_dict = self.controller.create(self.request, body) + + self.controller.share_group_api.create.assert_called_once_with( + self.context, share_group_type_id=self.fake_share_group_type['id'], + share_type_ids=[self.fake_share_type['id']]) + self.assertEqual(expected, res_dict['share_group']) + self.mock_policy_check.assert_called_once_with( + self.context, self.resource_name, 'create') + + def test_group_create_invalid_group_snapshot_state(self): + fake_snap_id = six.text_type(uuidutils.generate_uuid()) + self.mock_object( + self.controller.share_group_api, 'create', + mock.Mock(side_effect=exception.InvalidShareGroupSnapshot( + reason='bad status', + ))) + body = { + "share_group": { + "source_share_group_snapshot_id": fake_snap_id + } } - cg.update(**values) - expected_cg = copy.deepcopy(cg) - expected_cg['links'] = mock.ANY - return cg, expected_cg - - def test_cg_create(self): - fake_cg, expected_cg = self._get_fake_cg() - self.mock_object(share_types, 'get_default_share_type', - mock.Mock(return_value=self.fake_share_type)) - self.mock_object(self.controller.cg_api, 'create', - mock.Mock(return_value=fake_cg)) - - body = {"consistency_group": {}} - res_dict = self.controller.create(self.request, body) - - self.controller.cg_api.create.assert_called_once_with( - self.context, share_type_ids=[self.fake_share_type['id']]) - self.assertEqual(expected_cg, res_dict['consistency_group']) - self.mock_policy_check.assert_called_once_with( - self.context, self.resource_name, 'create') - - def test_cg_create_invalid_cgsnapshot_state(self): - fake_snap_id = six.text_type(uuidutils.generate_uuid()) - self.mock_object(self.controller.cg_api, 'create', - mock.Mock(side_effect=exception.InvalidCGSnapshot( - reason='bad status' - ))) - - body = {"consistency_group": {"source_cgsnapshot_id": fake_snap_id}} self.assertRaises(webob.exc.HTTPConflict, self.controller.create, self.request, body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_no_default_share_type(self): - fake_cg, expected_cg = self._get_fake_cg() + def test_share_group_create_no_default_share_type(self): + fake_group, expected_group = self._get_fake_share_group() self.mock_object(share_types, 'get_default_share_type', mock.Mock(return_value=None)) - self.mock_object(self.controller.cg_api, 'create', - mock.Mock(return_value=fake_cg)) + self.mock_object(self.controller.share_group_api, 'create', + mock.Mock(return_value=fake_group)) + body = {"share_group": {}} + + self.assertRaises( + webob.exc.HTTPBadRequest, + self.controller.create, self.request, body) + + self.mock_policy_check.assert_called_once_with( + self.context, self.resource_name, 'create') + + def test_share_group_create_no_default_group_type(self): + fake_group, expected_group = self._get_fake_share_group() + self.mock_object( + share_group_types, 'get_default', mock.Mock(return_value=None)) + self.mock_object( + self.controller.share_group_api, 'create', + mock.Mock(return_value=fake_group)) + body = {"share_group": {}} + + self.assertRaises( + webob.exc.HTTPBadRequest, + self.controller.create, self.request, body) + + self.mock_policy_check.assert_called_once_with( + self.context, self.resource_name, 'create') + + def test_share_group_create_with_group_type_specified(self): + fake_share_group, expected_group = self._get_fake_share_group() + self.mock_object( + share_group_types, 'get_default', mock.Mock(return_value=None)) + self.mock_object( + self.controller.share_group_api, 'create', + mock.Mock(return_value=fake_share_group)) + body = { + "share_group": { + "share_group_type_id": self.fake_share_group_type.get('id'), + } + } + + self.controller.create(self.request, body) + + self.controller.share_group_api.create.assert_called_once_with( + self.context, + share_group_type_id=self.fake_share_group_type['id'], + share_type_ids=[self.fake_share_type['id']]) + self.mock_policy_check.assert_called_once_with( + self.context, self.resource_name, 'create') + + def test_share_group_create_with_invalid_group_type_specified(self): + fake_share_group, expected_share_group = self._get_fake_share_group() + self.mock_object( + share_group_types, 'get_default', mock.Mock(return_value=None)) + self.mock_object(self.controller.share_group_api, 'create', + mock.Mock(return_value=fake_share_group)) + body = {"share_group": {"group_type_id": "invalid"}} - body = {"consistency_group": {}} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.request, body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_with_name(self): + def test_share_group_create_with_name(self): fake_name = 'fake_name' - fake_cg, expected_cg = self._get_fake_cg(name=fake_name) - self.mock_object(share_types, 'get_default_share_type', - mock.Mock(return_value=self.fake_share_type)) - self.mock_object(self.controller.cg_api, 'create', - mock.Mock(return_value=fake_cg)) - - body = {"consistency_group": {"name": fake_name}} + fake_share_group, expected_share_group = self._get_fake_share_group( + name=fake_name) + self.mock_object(self.controller.share_group_api, 'create', + mock.Mock(return_value=fake_share_group)) + body = {"share_group": {"name": fake_name}} res_dict = self.controller.create(self.request, body) - self.controller.cg_api.create.assert_called_once_with( + self.controller.share_group_api.create.assert_called_once_with( self.context, name=fake_name, + share_group_type_id=self.fake_share_group_type['id'], share_type_ids=[self.fake_share_type['id']]) - self.assertEqual(expected_cg, res_dict['consistency_group']) + self.assertEqual(expected_share_group, res_dict['share_group']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_with_description(self): + def test_share_group_create_with_description(self): fake_description = 'fake_description' - fake_cg, expected_cg = self._get_fake_cg(description=fake_description) + fake_share_group, expected_share_group = self._get_fake_share_group( + description=fake_description) self.mock_object(share_types, 'get_default_share_type', mock.Mock(return_value=self.fake_share_type)) - self.mock_object(self.controller.cg_api, 'create', - mock.Mock(return_value=fake_cg)) - - body = {"consistency_group": {"description": fake_description}} + self.mock_object(self.controller.share_group_api, 'create', + mock.Mock(return_value=fake_share_group)) + body = {"share_group": {"description": fake_description}} res_dict = self.controller.create(self.request, body) - self.controller.cg_api.create.assert_called_once_with( + self.controller.share_group_api.create.assert_called_once_with( self.context, description=fake_description, + share_group_type_id=self.fake_share_group_type['id'], share_type_ids=[self.fake_share_type['id']]) - self.assertEqual(expected_cg, res_dict['consistency_group']) + self.assertEqual(expected_share_group, res_dict['share_group']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_with_share_types(self): + def test_share_group_create_with_share_types(self): fake_share_types = [{"share_type_id": self.fake_share_type['id']}] - fake_cg, expected_cg = self._get_fake_cg(share_types=fake_share_types) - self.mock_object(self.controller.cg_api, 'create', - mock.Mock(return_value=fake_cg)) + fake_group, expected_group = self._get_fake_share_group( + share_types=fake_share_types) + self.mock_object(self.controller.share_group_api, 'create', + mock.Mock(return_value=fake_group)) + body = { + "share_group": { + "share_types": [self.fake_share_type['id']] + } + } - body = {"consistency_group": { - "share_types": [self.fake_share_type['id']]}} res_dict = self.controller.create(self.request, body) - self.controller.cg_api.create.assert_called_once_with( - self.context, share_type_ids=[self.fake_share_type['id']]) - self.assertEqual(expected_cg, res_dict['consistency_group']) + self.controller.share_group_api.create.assert_called_once_with( + self.context, share_group_type_id=self.fake_share_group_type['id'], + share_type_ids=[self.fake_share_type['id']]) + self.assertEqual(expected_group, res_dict['share_group']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_with_source_cgsnapshot_id_and_share_network(self): + def test_sg_create_with_source_sg_snapshot_id_and_share_network(self): fake_snap_id = six.text_type(uuidutils.generate_uuid()) fake_net_id = six.text_type(uuidutils.generate_uuid()) self.mock_object(share_types, 'get_default_share_type', mock.Mock(return_value=self.fake_share_type)) - mock_api_call = self.mock_object(self.controller.cg_api, 'create') - + mock_api_call = self.mock_object( + self.controller.share_group_api, 'create') body = { - "consistency_group": { - "source_cgsnapshot_id": fake_snap_id, + "share_group": { + "source_share_group_snapshot_id": fake_snap_id, "share_network_id": fake_net_id, } } @@ -230,447 +312,509 @@ class CGApiTest(test.TestCase): self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.request, body) + self.assertFalse(mock_api_call.called) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_with_source_cgsnapshot_id(self): + def test_share_group_create_with_source_sg_snapshot_id(self): fake_snap_id = six.text_type(uuidutils.generate_uuid()) - fake_cg, expected_cg = self._get_fake_cg( - source_cgsnapshot_id=fake_snap_id) - + fake_share_group, expected_group = self._get_fake_share_group( + source_share_group_snapshot_id=fake_snap_id) self.mock_object(share_types, 'get_default_share_type', mock.Mock(return_value=self.fake_share_type)) - self.mock_object(self.controller.cg_api, 'create', - mock.Mock(return_value=fake_cg)) + self.mock_object(self.controller.share_group_api, 'create', + mock.Mock(return_value=fake_share_group)) - body = {"consistency_group": { - "source_cgsnapshot_id": fake_snap_id}} + body = { + "share_group": { + "source_share_group_snapshot_id": fake_snap_id, + } + } res_dict = self.controller.create(self.request, body) - self.controller.cg_api.create.assert_called_once_with( - self.context, source_cgsnapshot_id=fake_snap_id) - self.assertEqual(expected_cg, res_dict['consistency_group']) + self.controller.share_group_api.create.assert_called_once_with( + self.context, share_group_type_id=self.fake_share_group_type['id'], + source_share_group_snapshot_id=fake_snap_id) + self.assertEqual(expected_group, res_dict['share_group']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_with_share_network_id(self): + def test_share_group_create_with_share_network_id(self): fake_net_id = six.text_type(uuidutils.generate_uuid()) - fake_cg, expected_cg = self._get_fake_cg( + fake_group, expected_group = self._get_fake_share_group( share_network_id=fake_net_id) self.mock_object(share_types, 'get_default_share_type', mock.Mock(return_value=self.fake_share_type)) - self.mock_object(self.controller.cg_api, 'create', - mock.Mock(return_value=fake_cg)) - - body = {"consistency_group": { - "share_network_id": fake_net_id}} + self.mock_object(self.controller.share_group_api, 'create', + mock.Mock(return_value=fake_group)) + body = { + "share_group": { + "share_network_id": fake_net_id, + } + } res_dict = self.controller.create(self.request, body) - self.controller.cg_api.create.assert_called_once_with( + self.controller.share_group_api.create.assert_called_once_with( self.context, share_network_id=fake_net_id, + share_group_type_id=self.fake_share_group_type['id'], share_type_ids=mock.ANY) - self.assertEqual(expected_cg, res_dict['consistency_group']) + self.assertEqual(expected_group, res_dict['share_group']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_no_default_share_type_with_cgsnapshot(self): + def test_sg_create_no_default_share_type_with_share_group_snapshot(self): fake_snap_id = six.text_type(uuidutils.generate_uuid()) - fake_cg, expected_cg = self._get_fake_cg() + fake, expected = self._get_fake_share_group() self.mock_object(share_types, 'get_default_share_type', mock.Mock(return_value=None)) - self.mock_object(self.controller.cg_api, 'create', - mock.Mock(return_value=fake_cg)) - - body = {"consistency_group": { - "source_cgsnapshot_id": fake_snap_id}} + self.mock_object(self.controller.share_group_api, 'create', + mock.Mock(return_value=fake)) + body = { + "share_group": { + "source_share_group_snapshot_id": fake_snap_id, + } + } res_dict = self.controller.create(self.request, body) - self.controller.cg_api.create.assert_called_once_with( - self.context, source_cgsnapshot_id=fake_snap_id) - self.assertEqual(expected_cg, res_dict['consistency_group']) + self.controller.share_group_api.create.assert_called_once_with( + self.context, share_group_type_id=self.fake_share_group_type['id'], + source_share_group_snapshot_id=fake_snap_id) + self.assertEqual(expected, res_dict['share_group']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_with_name_and_description(self): + def test_share_group_create_with_name_and_description(self): fake_name = 'fake_name' fake_description = 'fake_description' - fake_cg, expected_cg = self._get_fake_cg(name=fake_name, - description=fake_description) + fake_group, expected_group = self._get_fake_share_group( + name=fake_name, description=fake_description) self.mock_object(share_types, 'get_default_share_type', mock.Mock(return_value=self.fake_share_type)) - self.mock_object(self.controller.cg_api, 'create', - mock.Mock(return_value=fake_cg)) - - body = {"consistency_group": {"name": fake_name, - "description": fake_description}} + self.mock_object(self.controller.share_group_api, 'create', + mock.Mock(return_value=fake_group)) + body = { + "share_group": { + "name": fake_name, + "description": fake_description + } + } res_dict = self.controller.create(self.request, body) - self.controller.cg_api.create.assert_called_once_with( + self.controller.share_group_api.create.assert_called_once_with( self.context, name=fake_name, description=fake_description, + share_group_type_id=self.fake_share_group_type['id'], share_type_ids=[self.fake_share_type['id']]) - self.assertEqual(expected_cg, res_dict['consistency_group']) + self.assertEqual(expected_group, res_dict['share_group']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_invalid_body(self): - body = {"not_consistency_group": {}} + def test_share_group_create_invalid_body(self): + body = {"not_group": {}} + + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, + self.request, body) + + self.mock_policy_check.assert_called_once_with( + self.context, self.resource_name, 'create') + + def test_group_create_invalid_body_share_types_and_source_group_snapshot( + self): + body = { + "share_group": { + "share_types": [], + "source_share_group_snapshot_id": "", + } + } self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.request, body) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_invalid_body_share_types_and_source_cgsnapshot(self): - body = {"consistency_group": {"share_types": [], - "source_cgsnapshot_id": ""}} - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - self.request, body) - self.mock_policy_check.assert_called_once_with( - self.context, self.resource_name, 'create') - - def test_cg_create_source_cgsnapshot_not_in_available(self): + def test_share_group_create_source_group_snapshot_not_in_available(self): fake_snap_id = six.text_type(uuidutils.generate_uuid()) - body = {"consistency_group": {"source_cgsnapshot_id": fake_snap_id}} - self.mock_object(self.controller.cg_api, 'create', mock.Mock( - side_effect=exception.InvalidCGSnapshot(reason='blah'))) + body = { + "share_group": { + "source_share_group_snapshot_id": fake_snap_id, + } + } + self.mock_object(self.controller.share_group_api, 'create', mock.Mock( + side_effect=exception.InvalidShareGroupSnapshot(reason='blah'))) + + self.assertRaises( + webob.exc.HTTPConflict, self.controller.create, self.request, body) - self.assertRaises(webob.exc.HTTPConflict, self.controller.create, - self.request, body) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_source_cgsnapshot_does_not_exist(self): + def test_share_group_create_source_group_snapshot_does_not_exist(self): fake_snap_id = six.text_type(uuidutils.generate_uuid()) - body = {"consistency_group": {"source_cgsnapshot_id": fake_snap_id}} - self.mock_object(self.controller.cg_api, 'create', mock.Mock( - side_effect=exception.CGSnapshotNotFound( - cgsnapshot_id=fake_snap_id))) + body = { + "share_group": {"source_share_group_snapshot_id": fake_snap_id} + } + self.mock_object( + self.controller.share_group_api, 'create', + mock.Mock(side_effect=exception.ShareGroupSnapshotNotFound( + share_group_snapshot_id=fake_snap_id))) + + self.assertRaises( + webob.exc.HTTPBadRequest, + self.controller.create, self.request, body) - self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, - self.request, body) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_source_cgsnapshot_not_a_uuid(self): + def test_share_group_create_source_group_snapshot_not_a_uuid(self): fake_snap_id = "Not a uuid" - body = {"consistency_group": {"source_cgsnapshot_id": fake_snap_id}} + body = { + "share_group": { + "source_share_group_snapshot_id": fake_snap_id, + } + } self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.request, body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_share_network_id_not_a_uuid(self): + def test_share_group_create_share_network_id_not_a_uuid(self): fake_net_id = "Not a uuid" - body = {"consistency_group": {"share_network_id": fake_net_id}} + body = {"share_group": {"share_network_id": fake_net_id}} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.request, body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_invalid_body_share_types_not_a_list(self): - body = {"consistency_group": {"share_types": ""}} + def test_share_group_create_invalid_body_share_types_not_a_list(self): + body = {"share_group": {"share_types": ""}} self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.request, body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_invalid_body_invalid_field(self): - body = {"consistency_group": {"unknown_field": ""}} + def test_share_group_create_invalid_body_invalid_field(self): + body = {"share_group": {"unknown_field": ""}} exc = self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.request, body) - self.assertIn('unknown_field', six.text_type(exc)) + + self.assertTrue('unknown_field' in six.text_type(exc)) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_with_invalid_share_types_field(self): - body = {"consistency_group": {"share_types": 'iamastring'}} + def test_share_group_create_with_invalid_share_types_field(self): + body = {"share_group": {"share_types": 'iamastring'}} + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.request, body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_create_with_invalid_share_types_field_not_uuids(self): - body = {"consistency_group": {"share_types": ['iamastring']}} + def test_share_group_create_with_invalid_share_types_field_not_uuids(self): + body = {"share_group": {"share_types": ['iamastring']}} + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create, self.request, body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'create') - def test_cg_update_with_name_and_description(self): + def test_share_group_update_with_name_and_description(self): fake_name = 'fake_name' fake_description = 'fake_description' - fake_cg, expected_cg = self._get_fake_cg(name=fake_name, - description=fake_description) - self.mock_object(self.controller.cg_api, 'get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.controller.cg_api, 'update', - mock.Mock(return_value=fake_cg)) - - body = {"consistency_group": {"name": fake_name, - "description": fake_description}} + fake_group, expected_group = self._get_fake_share_group( + name=fake_name, description=fake_description) + self.mock_object(self.controller.share_group_api, 'get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.controller.share_group_api, 'update', + mock.Mock(return_value=fake_group)) + body = { + "share_group": { + "name": fake_name, + "description": fake_description, + } + } context = self.request.environ['manila.context'] - res_dict = self.controller.update(self.request, fake_cg['id'], body) - self.controller.cg_api.update.assert_called_once_with( - context, fake_cg, + res_dict = self.controller.update(self.request, fake_group['id'], body) + + self.controller.share_group_api.update.assert_called_once_with( + context, fake_group, {"name": fake_name, "description": fake_description}) - self.assertEqual(expected_cg, res_dict['consistency_group']) + self.assertEqual(expected_group, res_dict['share_group']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'update') - def test_cg_update_cg_not_found(self): - body = {"consistency_group": {}} - self.mock_object(self.controller.cg_api, 'get', + def test_share_group_update_group_not_found(self): + body = {"share_group": {}} + self.mock_object(self.controller.share_group_api, 'get', mock.Mock(side_effect=exception.NotFound)) self.assertRaises(webob.exc.HTTPNotFound, self.controller.update, self.request, 'fake_id', body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'update') - def test_cg_update_invalid_body(self): - body = {"not_consistency_group": {}} + def test_share_group_update_invalid_body(self): + body = {"not_group": {}} + self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, self.request, 'fake_id', body) + self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'update') - def test_cg_update_invalid_body_invalid_field(self): - body = {"consistency_group": {"unknown_field": ""}} + def test_share_group_update_invalid_body_invalid_field(self): + body = {"share_group": {"unknown_field": ""}} + exc = self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, self.request, 'fake_id', body) - self.assertIn('unknown_field', six.text_type(exc)) + + self.assertTrue('unknown_field' in six.text_type(exc)) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'update') - def test_cg_update_invalid_body_readonly_field(self): - body = {"consistency_group": {"share_types": []}} + def test_share_group_update_invalid_body_readonly_field(self): + body = {"share_group": {"share_types": []}} + exc = self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update, self.request, 'fake_id', body) - self.assertIn('share_types', six.text_type(exc)) + + self.assertTrue('share_types' in six.text_type(exc)) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'update') - def test_cg_list_index(self): - fake_cg, expected_cg = self._get_fake_simple_cg() - self.mock_object(sg_api.API, 'get_all', - mock.Mock(return_value=[fake_cg])) + def test_share_group_list_index(self): + fake, expected = self._get_fake_simple_share_group() + self.mock_object( + share_group_api.API, 'get_all', mock.Mock(return_value=[fake])) + res_dict = self.controller.index(self.request) - self.assertEqual([expected_cg], res_dict['consistency_groups']) + + self.assertEqual([expected], res_dict['share_groups']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'get_all') - def test_cg_list_index_no_cgs(self): - self.mock_object(sg_api.API, 'get_all', - mock.Mock(return_value=[])) + def test_share_group_list_index_no_groups(self): + self.mock_object( + share_group_api.API, 'get_all', mock.Mock(return_value=[])) + res_dict = self.controller.index(self.request) - self.assertEqual([], res_dict['consistency_groups']) + + self.assertEqual([], res_dict['share_groups']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'get_all') - def test_cg_list_index_with_limit(self): - fake_cg, expected_cg = self._get_fake_simple_cg() - fake_cg2, expected_cg2 = self._get_fake_simple_cg(id="fake_id2") - self.mock_object(sg_api.API, 'get_all', - mock.Mock(return_value=[fake_cg, fake_cg2])) - req = fakes.HTTPRequest.blank('/consistency_groups?limit=1', - version=self.api_version, - experimental=True) + def test_share_group_list_index_with_limit(self): + fake, expected = self._get_fake_simple_share_group() + fake2, expected2 = self._get_fake_simple_share_group(id="fake_id2") + self.mock_object( + share_group_api.API, 'get_all', + mock.Mock(return_value=[fake, fake2])) + req = fakes.HTTPRequest.blank( + '/share-groups?limit=1', version=self.api_version, + experimental=True) req_context = req.environ['manila.context'] res_dict = self.controller.index(req) - self.assertEqual(1, len(res_dict['consistency_groups'])) - self.assertEqual([expected_cg], res_dict['consistency_groups']) + self.assertEqual(1, len(res_dict['share_groups'])) + self.assertEqual([expected], res_dict['share_groups']) self.mock_policy_check.assert_called_once_with( req_context, self.resource_name, 'get_all') - def test_cg_list_index_with_limit_and_offset(self): - fake_cg, expected_cg = self._get_fake_simple_cg() - fake_cg2, expected_cg2 = self._get_fake_simple_cg(id="fake_id2") - self.mock_object(sg_api.API, 'get_all', - mock.Mock(return_value=[fake_cg, fake_cg2])) - req = fakes.HTTPRequest.blank('/consistency_groups?limit=1&offset=1', - version=self.api_version, - experimental=True) + def test_share_group_list_index_with_limit_and_offset(self): + fake, expected = self._get_fake_simple_share_group() + fake2, expected2 = self._get_fake_simple_share_group( + id="fake_id2") + self.mock_object(share_group_api.API, 'get_all', + mock.Mock(return_value=[fake, fake2])) + req = fakes.HTTPRequest.blank( + '/share-groups?limit=1&offset=1', version=self.api_version, + experimental=True) req_context = req.environ['manila.context'] res_dict = self.controller.index(req) - self.assertEqual(1, len(res_dict['consistency_groups'])) - self.assertEqual([expected_cg2], res_dict['consistency_groups']) + self.assertEqual(1, len(res_dict['share_groups'])) + self.assertEqual([expected2], res_dict['share_groups']) self.mock_policy_check.assert_called_once_with( req_context, self.resource_name, 'get_all') - def test_cg_list_detail(self): - fake_cg, expected_cg = self._get_fake_cg() - self.mock_object(sg_api.API, 'get_all', - mock.Mock(return_value=[fake_cg])) + def test_share_group_list_detail(self): + fake, expected = self._get_fake_share_group() + self.mock_object( + share_group_api.API, 'get_all', mock.Mock(return_value=[fake])) res_dict = self.controller.detail(self.request) - self.assertEqual([expected_cg], res_dict['consistency_groups']) + self.assertEqual([expected], res_dict['share_groups']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'get_all') - def test_cg_list_detail_no_cgs(self): - self.mock_object(sg_api.API, 'get_all', - mock.Mock(return_value=[])) + def test_share_group_list_detail_no_groups(self): + self.mock_object( + share_group_api.API, 'get_all', mock.Mock(return_value=[])) res_dict = self.controller.detail(self.request) - self.assertEqual([], res_dict['consistency_groups']) + self.assertEqual([], res_dict['share_groups']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'get_all') - def test_cg_list_detail_with_limit(self): - req = fakes.HTTPRequest.blank('/consistency_groups?limit=1', + def test_share_group_list_detail_with_limit(self): + req = fakes.HTTPRequest.blank('/share-groups?limit=1', version=self.api_version, experimental=True) req_context = req.environ['manila.context'] - fake_cg, expected_cg = self._get_fake_cg(ctxt=req_context) - fake_cg2, expected_cg2 = self._get_fake_cg(ctxt=req_context, - id="fake_id2") - self.mock_object(sg_api.API, 'get_all', - mock.Mock(return_value=[fake_cg, fake_cg2])) + fake_group, expected_group = self._get_fake_share_group( + ctxt=req_context) + fake_group2, expected_group2 = self._get_fake_share_group( + ctxt=req_context, id="fake_id2") + self.mock_object(share_group_api.API, 'get_all', + mock.Mock(return_value=[fake_group, fake_group2])) res_dict = self.controller.detail(req) - self.assertEqual(1, len(res_dict['consistency_groups'])) - self.assertEqual([expected_cg], res_dict['consistency_groups']) + self.assertEqual(1, len(res_dict['share_groups'])) + self.assertEqual([expected_group], res_dict['share_groups']) self.mock_policy_check.assert_called_once_with( req_context, self.resource_name, 'get_all') - def test_cg_list_detail_with_limit_and_offset(self): - req = fakes.HTTPRequest.blank('/consistency_groups?limit=1&offset=1', + def test_share_group_list_detail_with_limit_and_offset(self): + req = fakes.HTTPRequest.blank('/share-groups?limit=1&offset=1', version=self.api_version, experimental=True) req_context = req.environ['manila.context'] - fake_cg, expected_cg = self._get_fake_cg(ctxt=req_context) - fake_cg2, expected_cg2 = self._get_fake_cg( + fake_group, expected_group = self._get_fake_share_group( + ctxt=req_context) + fake_group2, expected_group2 = self._get_fake_share_group( id="fake_id2", ctxt=req_context) - self.mock_object(sg_api.API, 'get_all', - mock.Mock(return_value=[fake_cg, fake_cg2])) + self.mock_object(share_group_api.API, 'get_all', + mock.Mock(return_value=[fake_group, fake_group2])) res_dict = self.controller.detail(req) - self.assertEqual(1, len(res_dict['consistency_groups'])) - self.assertEqual([expected_cg2], res_dict['consistency_groups']) + self.assertEqual(1, len(res_dict['share_groups'])) + self.assertEqual([expected_group2], res_dict['share_groups']) self.mock_policy_check.assert_called_once_with( req_context, self.resource_name, 'get_all') - def test_cg_delete(self): - fake_cg, expected_cg = self._get_fake_cg() - self.mock_object(sg_api.API, 'get', - mock.Mock(return_value=fake_cg)) - self.mock_object(sg_api.API, 'delete') + def test_share_group_delete(self): + fake_group, expected_group = self._get_fake_share_group() + self.mock_object(share_group_api.API, 'get', + mock.Mock(return_value=fake_group)) + self.mock_object(share_group_api.API, 'delete') - res = self.controller.delete(self.request, fake_cg['id']) + res = self.controller.delete(self.request, fake_group['id']) self.assertEqual(202, res.status_code) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'delete') - def test_cg_delete_cg_not_found(self): - fake_cg, expected_cg = self._get_fake_cg() - self.mock_object(sg_api.API, 'get', + def test_share_group_delete_group_not_found(self): + fake_group, expected_group = self._get_fake_share_group() + self.mock_object(share_group_api.API, 'get', mock.Mock(side_effect=exception.NotFound)) self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete, - self.request, fake_cg['id']) + self.request, fake_group['id']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'delete') - def test_cg_delete_in_conflicting_status(self): - fake_cg, expected_cg = self._get_fake_cg() - self.mock_object(sg_api.API, 'get', - mock.Mock(return_value=fake_cg)) - self.mock_object(sg_api.API, 'delete', mock.Mock( - side_effect=exception.InvalidConsistencyGroup(reason='blah'))) + def test_share_group_delete_in_conflicting_status(self): + fake, expected = self._get_fake_share_group() + self.mock_object( + share_group_api.API, 'get', mock.Mock(return_value=fake)) + self.mock_object(share_group_api.API, 'delete', mock.Mock( + side_effect=exception.InvalidShareGroup(reason='blah'))) + + self.assertRaises( + webob.exc.HTTPConflict, + self.controller.delete, self.request, fake['id']) - self.assertRaises(webob.exc.HTTPConflict, self.controller.delete, - self.request, fake_cg['id']) self.mock_policy_check.assert_called_once_with( self.context, self.resource_name, 'delete') - def test_cg_show(self): - fake_cg, expected_cg = self._get_fake_cg() - self.mock_object(sg_api.API, 'get', - mock.Mock(return_value=fake_cg)) + def test_share_group_show(self): + fake, expected = self._get_fake_share_group() + self.mock_object( + share_group_api.API, 'get', mock.Mock(return_value=fake)) req = fakes.HTTPRequest.blank( - '/consistency_groups/%s' % fake_cg['id'], - version=self.api_version, experimental=True) + '/share-groupss/%s' % fake['id'], version=self.api_version, + experimental=True) req_context = req.environ['manila.context'] - res_dict = self.controller.show(req, fake_cg['id']) + res_dict = self.controller.show(req, fake['id']) - self.assertEqual(expected_cg, res_dict['consistency_group']) + self.assertEqual(expected, res_dict['share_group']) self.mock_policy_check.assert_called_once_with( req_context, self.resource_name, 'get') - def test_cg_show_as_admin(self): + def test_share_group_show_as_admin(self): req = fakes.HTTPRequest.blank( - '/consistency_groups/my_cg_id', + '/share-groupss/my_group_id', version=self.api_version, experimental=True) admin_context = req.environ['manila.context'].elevated() req.environ['manila.context'] = admin_context - fake_cg, expected_cg = self._get_fake_cg( - ctxt=admin_context, id='my_cg_id') - self.mock_object(sg_api.API, 'get', - mock.Mock(return_value=fake_cg)) + fake_group, expected_group = self._get_fake_share_group( + ctxt=admin_context, id='my_group_id') + self.mock_object(share_group_api.API, 'get', + mock.Mock(return_value=fake_group)) - res_dict = self.controller.show(req, fake_cg['id']) + res_dict = self.controller.show(req, fake_group['id']) - self.assertEqual(expected_cg, res_dict['consistency_group']) - self.assertIsNotNone(res_dict['consistency_group']['share_server_id']) + self.assertEqual(expected_group, res_dict['share_group']) + self.assertIsNotNone(res_dict['share_group']['share_server_id']) self.mock_policy_check.assert_called_once_with( admin_context, self.resource_name, 'get') - def test_cg_show_cg_not_found(self): + def test_share_group_show_group_not_found(self): req = fakes.HTTPRequest.blank( - '/consistency_groups/myfakecg', + '/share-groupss/myfakegroup', version=self.api_version, experimental=True) req_context = req.environ['manila.context'] - fake_cg, expected_cg = self._get_fake_cg( - ctxt=req_context, id='myfakecg') - self.mock_object(sg_api.API, 'get', + fake, expected = self._get_fake_share_group( + ctxt=req_context, id='myfakegroup') + self.mock_object(share_group_api.API, 'get', mock.Mock(side_effect=exception.NotFound)) - self.assertRaises(webob.exc.HTTPNotFound, self.controller.show, - req, fake_cg['id']) + self.assertRaises( + webob.exc.HTTPNotFound, self.controller.show, req, fake['id']) + self.mock_policy_check.assert_called_once_with( req_context, self.resource_name, 'get') @ddt.data(*fakes.fixture_reset_status_with_different_roles) @ddt.unpack - def test_consistency_groups_reset_status_with_different_roles( + def test_share_groups_reset_status_with_different_roles( self, role, valid_code, valid_status, version): ctxt = self._get_context(role) - cg, req = self._setup_cg_data(version=version) + share_group, req = self._setup_share_group_data() - if float(version) > 2.6: - action_name = 'reset_status' - else: - action_name = 'os-reset_status' + action_name = 'reset_status' body = {action_name: {'status': constants.STATUS_ERROR}} req.method = 'POST' req.headers['content-type'] = 'application/json' req.body = six.b(jsonutils.dumps(body)) - req.headers['X-Openstack-Manila-Api-Version'] = version + req.headers['X-Openstack-Manila-Api-Version'] = self.api_version req.environ['manila.context'] = ctxt with mock.patch.object( @@ -681,29 +825,25 @@ class CGApiTest(test.TestCase): self.assertEqual(valid_code, resp.status_int) if valid_code == 404: - self.assertRaises(exception.NotFound, - db.consistency_group_get, - ctxt, - cg['id']) + self.assertRaises( + exception.NotFound, + db.share_group_get, ctxt, share_group['id']) else: - actual_model = db.consistency_group_get(ctxt, cg['id']) + actual_model = db.share_group_get(ctxt, share_group['id']) self.assertEqual(valid_status, actual_model['status']) @ddt.data(*fakes.fixture_force_delete_with_different_roles) @ddt.unpack - def test_consistency_group_force_delete_with_different_roles(self, role, - resp_code, - version): + def test_share_group_force_delete_with_different_roles(self, role, + resp_code, version): ctxt = self._get_context(role) - cg, req = self._setup_cg_data(version=version) + share_group, req = self._setup_share_group_data() req.method = 'POST' req.headers['content-type'] = 'application/json' - if float(version) > 2.6: - action_name = 'force_delete' - else: - action_name = 'os-force_delete' + action_name = 'force_delete' body = {action_name: {}} req.body = six.b(jsonutils.dumps(body)) + req.headers['X-Openstack-Manila-Api-Version'] = self.api_version req.environ['manila.context'] = ctxt with mock.patch.object( diff --git a/manila/tests/api/v2/test_share_networks.py b/manila/tests/api/v2/test_share_networks.py index bdb81e56b8..5b69494a63 100644 --- a/manila/tests/api/v2/test_share_networks.py +++ b/manila/tests/api/v2/test_share_networks.py @@ -292,11 +292,11 @@ class ShareNetworkAPITest(test.TestCase): assert_called_once_with(self.req.environ['manila.context'], share_nw['id']) - def test_delete_in_use_by_consistency_group(self): + def test_delete_in_use_by_share_group(self): share_nw = fake_share_network.copy() self.mock_object(db_api, 'share_network_get', mock.Mock(return_value=share_nw)) - self.mock_object(db_api, 'count_consistency_groups_in_share_network', + self.mock_object(db_api, 'count_share_groups_in_share_network', mock.Mock(return_value=2)) self.assertRaises(webob_exc.HTTPConflict, diff --git a/manila/tests/api/v2/test_shares.py b/manila/tests/api/v2/test_shares.py index a006d6ede1..e854f9efce 100644 --- a/manila/tests/api/v2/test_shares.py +++ b/manila/tests/api/v2/test_shares.py @@ -117,8 +117,6 @@ class ShareAPITest(test.TestCase): 'volume_type': '1', 'snapshot_support': True, 'is_public': False, - 'consistency_group_id': None, - 'source_cgsnapshot_member_id': None, 'task_state': None, 'share_type_name': None, 'links': [ @@ -406,8 +404,6 @@ class ShareAPITest(test.TestCase): expected['share'].pop('snapshot_support') expected['share'].pop('share_type_name') expected['share'].pop('task_state') - expected['share'].pop('consistency_group_id') - expected['share'].pop('source_cgsnapshot_member_id') self.assertEqual(expected, res_dict) @ddt.data("2.2", "2.3") @@ -421,23 +417,28 @@ class ShareAPITest(test.TestCase): expected = self._get_expected_share_detailed_response(self.share) expected['share'].pop('share_type_name') expected['share'].pop('task_state') - expected['share'].pop('consistency_group_id') - expected['share'].pop('source_cgsnapshot_member_id') self.assertEqual(expected, res_dict) - @ddt.data("2.4", "2.5") - def test_share_create_with_consistency_group(self, microversion): + @ddt.data("2.31") + def test_share_create_with_share_group(self, microversion): self.mock_object(share_api.API, 'create', self.create_mock) body = {"share": copy.deepcopy(self.share)} - req = fakes.HTTPRequest.blank('/shares', version=microversion) + req = fakes.HTTPRequest.blank('/shares', version=microversion, + experimental=True) res_dict = self.controller.create(req, body) expected = self._get_expected_share_detailed_response(self.share) - expected['share'].pop('share_type_name') - if (api_version.APIVersionRequest(microversion) == - api_version.APIVersionRequest('2.4')): - expected['share'].pop('task_state') + expected['share'].pop('export_location') + expected['share'].pop('export_locations') + expected['share']['access_rules_status'] = 'active' + expected['share']['replication_type'] = None + expected['share']['has_replicas'] = False + expected['share']['user_id'] = 'fakeuser' + expected['share']['create_share_from_snapshot_support'] = True + expected['share']['revert_to_snapshot_support'] = False + expected['share']['share_group_id'] = None + expected['share']['source_share_group_snapshot_member_id'] = None self.assertEqual(expected, res_dict) def test_share_create_with_valid_default_share_type(self): @@ -479,8 +480,6 @@ class ShareAPITest(test.TestCase): expected = self._get_expected_share_detailed_response(self.share) expected['share']['task_state'] = None - expected['share']['consistency_group_id'] = None - expected['share']['source_cgsnapshot_member_id'] = None expected['share']['replication_type'] = None expected['share']['share_type_name'] = None expected['share']['has_replicas'] = False @@ -515,7 +514,7 @@ class ShareAPITest(test.TestCase): res_dict = self.controller.create(req, body) expected = self._get_expected_share_detailed_response(shr) - self.assertEqual(expected, res_dict) + self.assertDictMatch(expected, res_dict) self.assertEqual("fakenetid", create_mock.call_args[1]['share_network_id']) @@ -534,8 +533,6 @@ class ShareAPITest(test.TestCase): else: self.assertNotIn('user_id', expected['share']) expected['share']['task_state'] = None - expected['share']['consistency_group_id'] = None - expected['share']['source_cgsnapshot_member_id'] = None expected['share']['replication_type'] = None expected['share']['share_type_name'] = None expected['share']['has_replicas'] = False @@ -1084,7 +1081,7 @@ class ShareAPITest(test.TestCase): req = fakes.HTTPRequest.blank('/shares', version='2.7') res_dict = self.controller.create(req, body) expected = self._get_expected_share_detailed_response(shr) - self.assertEqual(expected, res_dict) + self.assertDictMatch(expected, res_dict) self.assertEqual(parent_share_net, create_mock.call_args[1]['share_network_id']) @@ -1165,29 +1162,49 @@ class ShareAPITest(test.TestCase): expected['share'].pop('snapshot_support') expected['share'].pop('share_type_name') expected['share'].pop('task_state') - expected['share'].pop('consistency_group_id') - expected['share'].pop('source_cgsnapshot_member_id') res_dict = self.controller.show(req, '1') self.assertEqual(expected, res_dict) - def test_share_show_with_consistency_group(self): - req = fakes.HTTPRequest.blank('/shares/1', version='2.4') + def test_share_show_with_share_group(self): + req = fakes.HTTPRequest.blank( + '/shares/1', version='2.31', experimental=True) expected = self._get_expected_share_detailed_response() - expected['share'].pop('share_type_name') - expected['share'].pop('task_state') + expected['share'].pop('export_location') + expected['share'].pop('export_locations') + expected['share']['create_share_from_snapshot_support'] = True + expected['share']['revert_to_snapshot_support'] = False + expected['share']['share_group_id'] = None + expected['share']['source_share_group_snapshot_member_id'] = None + expected['share']['access_rules_status'] = 'active' + expected['share']['replication_type'] = None + expected['share']['has_replicas'] = False + expected['share']['user_id'] = 'fakeuser' res_dict = self.controller.show(req, '1') - self.assertEqual(expected, res_dict) + self.assertDictMatch(expected, res_dict) + + def test_share_show_with_share_group_earlier_version(self): + req = fakes.HTTPRequest.blank( + '/shares/1', version='2.23', experimental=True) + expected = self._get_expected_share_detailed_response() + expected['share'].pop('export_location') + expected['share'].pop('export_locations') + expected['share']['access_rules_status'] = 'active' + expected['share']['replication_type'] = None + expected['share']['has_replicas'] = False + expected['share']['user_id'] = 'fakeuser' + + res_dict = self.controller.show(req, '1') + + self.assertDictMatch(expected, res_dict) def test_share_show_with_share_type_name(self): req = fakes.HTTPRequest.blank('/shares/1', version='2.6') res_dict = self.controller.show(req, '1') expected = self._get_expected_share_detailed_response() - expected['share']['consistency_group_id'] = None - expected['share']['source_cgsnapshot_member_id'] = None expected['share']['share_type_name'] = None expected['share']['task_state'] = None self.assertEqual(expected, res_dict) @@ -1204,8 +1221,6 @@ class ShareAPITest(test.TestCase): expected['share']['user_id'] = 'fakeuser' else: self.assertNotIn('user_id', expected['share']) - expected['share']['consistency_group_id'] = None - expected['share']['source_cgsnapshot_member_id'] = None expected['share']['share_type_name'] = None expected['share']['task_state'] = None expected['share']['access_rules_status'] = 'active' @@ -1222,8 +1237,6 @@ class ShareAPITest(test.TestCase): expected['share'].pop('snapshot_support') expected['share'].pop('share_type_name') expected['share'].pop('task_state') - expected['share'].pop('consistency_group_id') - expected['share'].pop('source_cgsnapshot_member_id') res_dict = self.controller.show(req, '1') @@ -1245,8 +1258,6 @@ class ShareAPITest(test.TestCase): expected = self._get_expected_share_detailed_response() expected['share']['task_state'] = None - expected['share']['consistency_group_id'] = None - expected['share']['source_cgsnapshot_member_id'] = None expected['share']['access_rules_status'] = 'active' expected['share']['share_type_name'] = None expected['share']['replication_type'] = None @@ -1289,32 +1300,32 @@ class ShareAPITest(test.TestCase): self.assertRaises( webob.exc.HTTPConflict, self.controller.delete, req, 1) - def test_share_delete_in_consistency_group_param_not_provided(self): + def test_share_delete_in_share_group_param_not_provided(self): fake_share = stubs.stub_share('fake_share', - consistency_group_id='fake_cg_id') + share_group_id='fake_group_id') self.mock_object(share_api.API, 'get', mock.Mock(return_value=fake_share)) req = fakes.HTTPRequest.blank('/shares/1') self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, req, 1) - def test_share_delete_in_consistency_group(self): + def test_share_delete_in_share_group(self): fake_share = stubs.stub_share('fake_share', - consistency_group_id='fake_cg_id') + share_group_id='fake_group_id') self.mock_object(share_api.API, 'get', mock.Mock(return_value=fake_share)) req = fakes.HTTPRequest.blank( - '/shares/1?consistency_group_id=fake_cg_id') + '/shares/1?share_group_id=fake_group_id') resp = self.controller.delete(req, 1) self.assertEqual(202, resp.status_int) - def test_share_delete_in_consistency_group_wrong_id(self): + def test_share_delete_in_share_group_wrong_id(self): fake_share = stubs.stub_share('fake_share', - consistency_group_id='fake_cg_id') + share_group_id='fake_group_id') self.mock_object(share_api.API, 'get', mock.Mock(return_value=fake_share)) req = fakes.HTTPRequest.blank( - '/shares/1?consistency_group_id=not_fake_cg_id') + '/shares/1?share_group_id=not_fake_group_id') self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete, req, 1) @@ -1330,14 +1341,18 @@ class ShareAPITest(test.TestCase): self.assertEqual(shr['is_public'], res_dict['share']['is_public']) - def test_share_update_with_consistency_group(self): + def test_share_update_with_share_group(self): shr = self.share body = {"share": shr} - req = fakes.HTTPRequest.blank('/share/1', version="2.4") + req = fakes.HTTPRequest.blank( + '/share/1', version="2.31", experimental=True) + res_dict = self.controller.update(req, 1, body) - self.assertIsNone(res_dict['share']["consistency_group_id"]) - self.assertIsNone(res_dict['share']["source_cgsnapshot_member_id"]) + + self.assertIsNone(res_dict['share']["share_group_id"]) + self.assertIsNone( + res_dict['share']["source_share_group_snapshot_member_id"]) def test_share_not_updates_size(self): req = fakes.HTTPRequest.blank('/share/1') @@ -1570,7 +1585,7 @@ class ShareAPITest(test.TestCase): self.mock_object(share_api.API, 'get_all', stubs.stub_share_get_all_by_project) res_dict = self.controller.detail(req) - self.assertEqual(expected, res_dict) + self.assertDictListMatch(expected['shares'], res_dict['shares']) self.assertEqual(res_dict['shares'][0]['volume_type'], res_dict['shares'][0]['share_type']) @@ -1581,13 +1596,23 @@ class ShareAPITest(test.TestCase): expected['shares'][0].pop('snapshot_support') self._list_detail_test_common(req, expected) - def test_share_list_detail_with_consistency_group(self): + def test_share_list_detail_with_share_group(self): env = {'QUERY_STRING': 'name=Share+Test+Name'} - req = fakes.HTTPRequest.blank('/shares/detail', environ=env, - version="2.4") + req = fakes.HTTPRequest.blank( + '/shares/detail', environ=env, version="2.31", experimental=True) expected = self._list_detail_common_expected() - expected['shares'][0]['consistency_group_id'] = None - expected['shares'][0]['source_cgsnapshot_member_id'] = None + expected['shares'][0]['task_state'] = None + expected['shares'][0]['share_type_name'] = None + expected['shares'][0].pop('export_location') + expected['shares'][0].pop('export_locations') + expected['shares'][0]['access_rules_status'] = 'active' + expected['shares'][0]['replication_type'] = None + expected['shares'][0]['has_replicas'] = False + expected['shares'][0]['user_id'] = 'fakeuser' + expected['shares'][0]['create_share_from_snapshot_support'] = True + expected['shares'][0]['revert_to_snapshot_support'] = False + expected['shares'][0]['share_group_id'] = None + expected['shares'][0]['source_share_group_snapshot_member_id'] = None self._list_detail_test_common(req, expected) def test_share_list_detail_with_task_state(self): @@ -1595,8 +1620,6 @@ class ShareAPITest(test.TestCase): req = fakes.HTTPRequest.blank('/shares/detail', environ=env, version="2.5") expected = self._list_detail_common_expected() - expected['shares'][0]['consistency_group_id'] = None - expected['shares'][0]['source_cgsnapshot_member_id'] = None expected['shares'][0]['task_state'] = None self._list_detail_test_common(req, expected) @@ -1605,8 +1628,6 @@ class ShareAPITest(test.TestCase): req = fakes.HTTPRequest.blank('/shares/detail', environ=env, version="2.9") expected = self._list_detail_common_expected() - expected['shares'][0]['consistency_group_id'] = None - expected['shares'][0]['source_cgsnapshot_member_id'] = None expected['shares'][0]['task_state'] = None expected['shares'][0]['share_type_name'] = None expected['shares'][0].pop('export_location') @@ -1642,8 +1663,6 @@ class ShareAPITest(test.TestCase): 'share_type': '1', 'volume_type': '1', 'is_public': False, - 'consistency_group_id': None, - 'source_cgsnapshot_member_id': None, 'snapshot_support': True, 'has_replicas': False, 'replication_type': None, @@ -2463,8 +2482,6 @@ class ShareManageTest(test.TestCase): 'share_type': '1', 'volume_type': '1', 'is_public': False, - 'consistency_group_id': None, - 'source_cgsnapshot_member_id': None, 'snapshot_support': True, 'task_state': None, 'links': [ diff --git a/manila/tests/db/migrations/alembic/migrations_data_checks.py b/manila/tests/db/migrations/alembic/migrations_data_checks.py index 06d181906e..9620fe9b24 100644 --- a/manila/tests/db/migrations/alembic/migrations_data_checks.py +++ b/manila/tests/db/migrations/alembic/migrations_data_checks.py @@ -1838,3 +1838,181 @@ class AddCastRulesToReadonlyToInstances(BaseMigrationChecks): for instance in engine.execute(share_instances_table.select()): self.test_case.assertNotIn('cast_rules_to_readonly', instance) + + +@map_to_migration('03da71c0e321') +class ShareGroupMigrationChecks(BaseMigrationChecks): + + def setup_upgrade_data(self, engine): + # Create share type + self.share_type_id = uuidutils.generate_uuid() + st_fixture = { + 'deleted': "False", + 'id': self.share_type_id, + } + st_table = utils.load_table('share_types', engine) + engine.execute(st_table.insert(st_fixture)) + + # Create CG + self.cg_id = uuidutils.generate_uuid() + cg_fixture = { + 'deleted': "False", + 'id': self.cg_id, + 'user_id': 'fake_user', + 'project_id': 'fake_project_id', + } + cg_table = utils.load_table('consistency_groups', engine) + engine.execute(cg_table.insert(cg_fixture)) + + # Create share_type group mapping + self.mapping_id = uuidutils.generate_uuid() + mapping_fixture = { + 'deleted': "False", + 'id': self.mapping_id, + 'consistency_group_id': self.cg_id, + 'share_type_id': self.share_type_id, + } + mapping_table = utils.load_table( + 'consistency_group_share_type_mappings', engine) + engine.execute(mapping_table.insert(mapping_fixture)) + + # Create share + self.share_id = uuidutils.generate_uuid() + share_fixture = { + 'deleted': "False", + 'id': self.share_id, + 'consistency_group_id': self.cg_id, + 'user_id': 'fake_user', + 'project_id': 'fake_project_id', + } + share_table = utils.load_table('shares', engine) + engine.execute(share_table.insert(share_fixture)) + + # Create share instance + self.share_instance_id = uuidutils.generate_uuid() + share_instance_fixture = { + 'deleted': "False", + 'share_type_id': self.share_type_id, + 'id': self.share_instance_id, + 'share_id': self.share_id, + 'cast_rules_to_readonly': False, + } + share_instance_table = utils.load_table('share_instances', engine) + engine.execute(share_instance_table.insert(share_instance_fixture)) + + # Create cgsnapshot + self.cgsnapshot_id = uuidutils.generate_uuid() + cg_snap_fixture = { + 'deleted': "False", + 'id': self.cgsnapshot_id, + 'consistency_group_id': self.cg_id, + 'user_id': 'fake_user', + 'project_id': 'fake_project_id', + } + cgsnapshots_table = utils.load_table('cgsnapshots', engine) + engine.execute(cgsnapshots_table.insert(cg_snap_fixture)) + + # Create cgsnapshot member + self.cgsnapshot_member_id = uuidutils.generate_uuid() + cg_snap_member_fixture = { + 'deleted': "False", + 'id': self.cgsnapshot_member_id, + 'cgsnapshot_id': self.cgsnapshot_id, + 'share_type_id': self.share_type_id, + 'share_instance_id': self.share_instance_id, + 'share_id': self.share_id, + 'user_id': 'fake_user', + 'project_id': 'fake_project_id', + } + cgsnapshot_members_table = utils.load_table( + 'cgsnapshot_members', engine) + engine.execute(cgsnapshot_members_table.insert(cg_snap_member_fixture)) + + def check_upgrade(self, engine, data): + sg_table = utils.load_table("share_groups", engine) + db_result = engine.execute(sg_table.select().where( + sg_table.c.id == self.cg_id)) + self.test_case.assertEqual(1, db_result.rowcount) + sg = db_result.first() + self.test_case.assertIsNone(sg['source_share_group_snapshot_id']) + + share_table = utils.load_table("shares", engine) + share_result = engine.execute(share_table.select().where( + share_table.c.id == self.share_id)) + self.test_case.assertEqual(1, share_result.rowcount) + share = share_result.first() + self.test_case.assertEqual(self.cg_id, share['share_group_id']) + self.test_case.assertIsNone( + share['source_share_group_snapshot_member_id']) + + mapping_table = utils.load_table( + "share_group_share_type_mappings", engine) + mapping_result = engine.execute(mapping_table.select().where( + mapping_table.c.id == self.mapping_id)) + self.test_case.assertEqual(1, mapping_result.rowcount) + mapping_record = mapping_result.first() + self.test_case.assertEqual( + self.cg_id, mapping_record['share_group_id']) + self.test_case.assertEqual( + self.share_type_id, mapping_record['share_type_id']) + + sgs_table = utils.load_table("share_group_snapshots", engine) + db_result = engine.execute(sgs_table.select().where( + sgs_table.c.id == self.cgsnapshot_id)) + self.test_case.assertEqual(1, db_result.rowcount) + sgs = db_result.first() + self.test_case.assertEqual(self.cg_id, sgs['share_group_id']) + + sgsm_table = utils.load_table("share_group_snapshot_members", engine) + db_result = engine.execute(sgsm_table.select().where( + sgsm_table.c.id == self.cgsnapshot_member_id)) + self.test_case.assertEqual(1, db_result.rowcount) + sgsm = db_result.first() + self.test_case.assertEqual( + self.cgsnapshot_id, sgsm['share_group_snapshot_id']) + self.test_case.assertNotIn('share_type_id', sgsm) + + def check_downgrade(self, engine): + cg_table = utils.load_table("consistency_groups", engine) + db_result = engine.execute(cg_table.select().where( + cg_table.c.id == self.cg_id)) + self.test_case.assertEqual(1, db_result.rowcount) + cg = db_result.first() + self.test_case.assertIsNone(cg['source_cgsnapshot_id']) + + share_table = utils.load_table("shares", engine) + share_result = engine.execute(share_table.select().where( + share_table.c.id == self.share_id)) + self.test_case.assertEqual(1, share_result.rowcount) + share = share_result.first() + self.test_case.assertEqual(self.cg_id, share['consistency_group_id']) + self.test_case.assertIsNone( + share['source_cgsnapshot_member_id']) + + mapping_table = utils.load_table( + "consistency_group_share_type_mappings", engine) + mapping_result = engine.execute(mapping_table.select().where( + mapping_table.c.id == self.mapping_id)) + self.test_case.assertEqual(1, mapping_result.rowcount) + cg_st_mapping = mapping_result.first() + self.test_case.assertEqual( + self.cg_id, cg_st_mapping['consistency_group_id']) + self.test_case.assertEqual( + self.share_type_id, cg_st_mapping['share_type_id']) + + cg_snapshots_table = utils.load_table("cgsnapshots", engine) + db_result = engine.execute(cg_snapshots_table.select().where( + cg_snapshots_table.c.id == self.cgsnapshot_id)) + self.test_case.assertEqual(1, db_result.rowcount) + cgsnap = db_result.first() + self.test_case.assertEqual(self.cg_id, cgsnap['consistency_group_id']) + + cg_snap_member_table = utils.load_table("cgsnapshot_members", engine) + db_result = engine.execute(cg_snap_member_table.select().where( + cg_snap_member_table.c.id == self.cgsnapshot_member_id)) + self.test_case.assertEqual(1, db_result.rowcount) + member = db_result.first() + self.test_case.assertEqual( + self.cgsnapshot_id, member['cgsnapshot_id']) + self.test_case.assertIn('share_type_id', member) + self.test_case.assertEqual(self.share_type_id, member['share_type_id']) diff --git a/manila/tests/db/sqlalchemy/test_api.py b/manila/tests/db/sqlalchemy/test_api.py index c71e2aacec..0b24850a6d 100644 --- a/manila/tests/db/sqlalchemy/test_api.py +++ b/manila/tests/db/sqlalchemy/test_api.py @@ -266,12 +266,12 @@ class ShareDatabaseAPITestCase(test.TestCase): self.assertEqual(1, len(actual_result)) self.assertEqual(share['id'], actual_result[0].id) - def test_share_filter_all_by_consistency_group(self): - cg = db_utils.create_consistency_group() - share = db_utils.create_share(consistency_group_id=cg['id']) + def test_share_filter_all_by_share_group(self): + group = db_utils.create_share_group() + share = db_utils.create_share(share_group_id=group['id']) - actual_result = db_api.share_get_all_by_consistency_group_id( - self.ctxt, cg['id']) + actual_result = db_api.share_get_all_by_share_group_id( + self.ctxt, group['id']) self.assertEqual(1, len(actual_result)) self.assertEqual(share['id'], actual_result[0].id) @@ -322,20 +322,20 @@ class ShareDatabaseAPITestCase(test.TestCase): self.assertEqual(0, len(instances)) - def test_share_instance_get_all_by_consistency_group(self): - cg = db_utils.create_consistency_group() - db_utils.create_share(consistency_group_id=cg['id']) + def test_share_instance_get_all_by_share_group(self): + group = db_utils.create_share_group() + db_utils.create_share(share_group_id=group['id']) db_utils.create_share() - instances = db_api.share_instances_get_all_by_consistency_group_id( - self.ctxt, cg['id']) + instances = db_api.share_instances_get_all_by_share_group_id( + self.ctxt, group['id']) self.assertEqual(1, len(instances)) instance = instances[0] self.assertEqual('share-%s' % instance['id'], instance['name']) - @ddt.data('host', 'consistency_group_id') + @ddt.data('host', 'share_group_id') def test_share_get_all_sort_by_share_instance_fields(self, sort_key): shares = [db_utils.create_share(**{sort_key: n, 'size': 1}) for n in ('test1', 'test2')] @@ -384,7 +384,7 @@ class ShareDatabaseAPITestCase(test.TestCase): expected_share_keys = { 'project_id', 'share_type_id', 'display_name', 'name', 'share_proto', 'is_public', - 'source_cgsnapshot_member_id', + 'source_share_group_snapshot_member_id', } session = db_api.get_session() @@ -432,7 +432,7 @@ class ShareDatabaseAPITestCase(test.TestCase): expected_share_keys = { 'project_id', 'share_type_id', 'display_name', 'name', 'share_proto', 'is_public', - 'source_cgsnapshot_member_id', + 'source_share_group_snapshot_member_id', } session = db_api.get_session() @@ -497,7 +497,7 @@ class ShareDatabaseAPITestCase(test.TestCase): expected_share_keys = { 'project_id', 'share_type_id', 'display_name', 'name', 'share_proto', 'is_public', - 'source_cgsnapshot_member_id', + 'source_share_group_snapshot_member_id', } with session.begin(): @@ -542,7 +542,7 @@ class ShareDatabaseAPITestCase(test.TestCase): expected_extra_keys = { 'project_id', 'share_type_id', 'display_name', 'name', 'share_proto', 'is_public', - 'source_cgsnapshot_member_id', + 'source_share_group_snapshot_member_id', } share_replica = db_api.share_replica_get(self.ctxt, replica['id']) @@ -559,7 +559,7 @@ class ShareDatabaseAPITestCase(test.TestCase): expected_extra_keys = { 'project_id', 'share_type_id', 'display_name', 'name', 'share_proto', 'is_public', - 'source_cgsnapshot_member_id', + 'source_share_group_snapshot_member_id', } share_replica = db_api.share_replica_get( @@ -639,273 +639,305 @@ class ShareDatabaseAPITestCase(test.TestCase): @ddt.ddt -class ConsistencyGroupDatabaseAPITestCase(test.TestCase): - +class ShareGroupDatabaseAPITestCase(test.TestCase): def setUp(self): """Run before each test.""" - super(ConsistencyGroupDatabaseAPITestCase, self).setUp() + super(ShareGroupDatabaseAPITestCase, self).setUp() self.ctxt = context.get_admin_context() - def test_consistency_group_create_with_share_type(self): + def test_share_group_create_with_share_type(self): fake_share_types = ["fake_share_type"] - cg = db_utils.create_consistency_group(share_types=fake_share_types) - cg = db_api.consistency_group_get(self.ctxt, cg['id']) + share_group = db_utils.create_share_group(share_types=fake_share_types) + share_group = db_api.share_group_get(self.ctxt, share_group['id']) - self.assertEqual(1, len(cg['share_types'])) + self.assertEqual(1, len(share_group['share_types'])) - def test_consistency_group_get(self): - cg = db_utils.create_consistency_group() + def test_share_group_get(self): + share_group = db_utils.create_share_group() - self.assertDictMatch(dict(cg), - dict(db_api.consistency_group_get(self.ctxt, - cg['id']))) + self.assertDictMatch( + dict(share_group), + dict(db_api.share_group_get(self.ctxt, share_group['id']))) - def test_count_consistency_groups_in_share_network(self): + def test_count_share_groups_in_share_network(self): share_network = db_utils.create_share_network() - db_utils.create_consistency_group() - db_utils.create_consistency_group(share_network_id=share_network['id']) + db_utils.create_share_group() + db_utils.create_share_group(share_network_id=share_network['id']) - count = db_api.count_consistency_groups_in_share_network( + count = db_api.count_share_groups_in_share_network( self.ctxt, share_network_id=share_network['id']) self.assertEqual(1, count) - def test_consistency_group_get_all(self): - expected_cg = db_utils.create_consistency_group() + def test_share_group_get_all(self): + expected_share_group = db_utils.create_share_group() - cgs = db_api.consistency_group_get_all(self.ctxt, detailed=False) + share_groups = db_api.share_group_get_all(self.ctxt, detailed=False) - self.assertEqual(1, len(cgs)) - cg = cgs[0] - self.assertEqual(2, len(dict(cg).keys())) - self.assertEqual(expected_cg['id'], cg['id']) - self.assertEqual(expected_cg['name'], cg['name']) + self.assertEqual(1, len(share_groups)) + share_group = share_groups[0] + self.assertEqual(2, len(dict(share_group).keys())) + self.assertEqual(expected_share_group['id'], share_group['id']) + self.assertEqual(expected_share_group['name'], share_group['name']) - def test_consistency_group_get_all_with_detail(self): - expected_cg = db_utils.create_consistency_group() + def test_share_group_get_all_with_detail(self): + expected_share_group = db_utils.create_share_group() - cgs = db_api.consistency_group_get_all(self.ctxt, detailed=True) + share_groups = db_api.share_group_get_all(self.ctxt, detailed=True) - self.assertEqual(1, len(cgs)) - cg = cgs[0] - self.assertDictMatch(dict(expected_cg), dict(cg)) + self.assertEqual(1, len(share_groups)) + self.assertDictMatch(dict(expected_share_group), dict(share_groups[0])) - def test_consistency_group_get_all_by_project(self): + def test_share_group_get_all_by_host(self): + fake_host = 'my_fake_host' + expected_share_group = db_utils.create_share_group(host=fake_host) + db_utils.create_share_group() + + share_groups = db_api.share_group_get_all_by_host( + self.ctxt, fake_host, detailed=False) + + self.assertEqual(1, len(share_groups)) + share_group = share_groups[0] + self.assertEqual(2, len(dict(share_group).keys())) + self.assertEqual(expected_share_group['id'], share_group['id']) + self.assertEqual(expected_share_group['name'], share_group['name']) + + def test_share_group_get_all_by_host_with_details(self): + fake_host = 'my_fake_host' + expected_share_group = db_utils.create_share_group(host=fake_host) + db_utils.create_share_group() + + share_groups = db_api.share_group_get_all_by_host( + self.ctxt, fake_host, detailed=True) + + self.assertEqual(1, len(share_groups)) + share_group = share_groups[0] + self.assertDictMatch(dict(expected_share_group), dict(share_group)) + self.assertEqual(fake_host, share_group['host']) + + def test_share_group_get_all_by_project(self): fake_project = 'fake_project' - expected_cg = db_utils.create_consistency_group( + expected_group = db_utils.create_share_group( project_id=fake_project) - db_utils.create_consistency_group() + db_utils.create_share_group() - cgs = db_api.consistency_group_get_all_by_project(self.ctxt, - fake_project, - detailed=False) + groups = db_api.share_group_get_all_by_project(self.ctxt, + fake_project, + detailed=False) - self.assertEqual(1, len(cgs)) - cg = cgs[0] - self.assertEqual(2, len(dict(cg).keys())) - self.assertEqual(expected_cg['id'], cg['id']) - self.assertEqual(expected_cg['name'], cg['name']) + self.assertEqual(1, len(groups)) + group = groups[0] + self.assertEqual(2, len(dict(group).keys())) + self.assertEqual(expected_group['id'], group['id']) + self.assertEqual(expected_group['name'], group['name']) - def test_consistency_group_get_all_by_share_server(self): + def test_share_group_get_all_by_share_server(self): fake_server = 123 - expected_cg = db_utils.create_consistency_group( + expected_group = db_utils.create_share_group( share_server_id=fake_server) - db_utils.create_consistency_group() + db_utils.create_share_group() - cgs = db_api.consistency_group_get_all_by_share_server(self.ctxt, - fake_server) + groups = db_api.share_group_get_all_by_share_server(self.ctxt, + fake_server) - self.assertEqual(1, len(cgs)) - cg = cgs[0] - self.assertEqual(expected_cg['id'], cg['id']) - self.assertEqual(expected_cg['name'], cg['name']) + self.assertEqual(1, len(groups)) + group = groups[0] + self.assertEqual(expected_group['id'], group['id']) + self.assertEqual(expected_group['name'], group['name']) - def test_consistency_group_get_all_by_project_with_details(self): + def test_share_group_get_all_by_project_with_details(self): fake_project = 'fake_project' - expected_cg = db_utils.create_consistency_group( + expected_group = db_utils.create_share_group( project_id=fake_project) - db_utils.create_consistency_group() + db_utils.create_share_group() - cgs = db_api.consistency_group_get_all_by_project(self.ctxt, - fake_project, - detailed=True) + groups = db_api.share_group_get_all_by_project(self.ctxt, + fake_project, + detailed=True) - self.assertEqual(1, len(cgs)) - cg = cgs[0] - self.assertDictMatch(dict(expected_cg), dict(cg)) - self.assertEqual(fake_project, cg['project_id']) + self.assertEqual(1, len(groups)) + group = groups[0] + self.assertDictMatch(dict(expected_group), dict(group)) + self.assertEqual(fake_project, group['project_id']) - def test_consistency_group_update(self): + def test_share_group_update(self): fake_name = "my_fake_name" - expected_cg = db_utils.create_consistency_group() - expected_cg['name'] = fake_name + expected_group = db_utils.create_share_group() + expected_group['name'] = fake_name - db_api.consistency_group_update(self.ctxt, - expected_cg['id'], - {'name': fake_name}) + db_api.share_group_update(self.ctxt, + expected_group['id'], + {'name': fake_name}) - cg = db_api.consistency_group_get(self.ctxt, expected_cg['id']) - self.assertEqual(fake_name, cg['name']) + group = db_api.share_group_get(self.ctxt, expected_group['id']) + self.assertEqual(fake_name, group['name']) - def test_consistency_group_destroy(self): - cg = db_utils.create_consistency_group() - db_api.consistency_group_get(self.ctxt, cg['id']) + def test_share_group_destroy(self): + group = db_utils.create_share_group() + db_api.share_group_get(self.ctxt, group['id']) - db_api.consistency_group_destroy(self.ctxt, cg['id']) + db_api.share_group_destroy(self.ctxt, group['id']) - self.assertRaises(exception.NotFound, db_api.consistency_group_get, - self.ctxt, cg['id']) + self.assertRaises(exception.NotFound, db_api.share_group_get, + self.ctxt, group['id']) - def test_count_shares_in_consistency_group(self): - cg = db_utils.create_consistency_group() - db_utils.create_share(consistency_group_id=cg['id']) + def test_count_shares_in_share_group(self): + sg = db_utils.create_share_group() + db_utils.create_share(share_group_id=sg['id']) db_utils.create_share() - count = db_api.count_shares_in_consistency_group(self.ctxt, cg['id']) + count = db_api.count_shares_in_share_group(self.ctxt, sg['id']) self.assertEqual(1, count) - def test_count_cgsnapshots_in_consistency_group(self): - cg = db_utils.create_consistency_group() - db_utils.create_cgsnapshot(cg['id']) - db_utils.create_cgsnapshot(cg['id']) + def test_count_sg_snapshots_in_share_group(self): + sg = db_utils.create_share_group() + db_utils.create_share_group_snapshot(sg['id']) + db_utils.create_share_group_snapshot(sg['id']) - count = db_api.count_cgsnapshots_in_consistency_group(self.ctxt, - cg['id']) + count = db_api.count_share_group_snapshots_in_share_group( + self.ctxt, sg['id']) self.assertEqual(2, count) - def test_cgsnapshot_get(self): - cg = db_utils.create_consistency_group() - cgsnap = db_utils.create_cgsnapshot(cg['id']) + def test_share_group_snapshot_get(self): + sg = db_utils.create_share_group() + sg_snap = db_utils.create_share_group_snapshot(sg['id']) - self.assertDictMatch(dict(cgsnap), - dict(db_api.cgsnapshot_get(self.ctxt, - cgsnap['id']))) + self.assertDictMatch( + dict(sg_snap), + dict(db_api.share_group_snapshot_get(self.ctxt, sg_snap['id']))) - def test_cgsnapshot_get_all(self): - cg = db_utils.create_consistency_group() - expected_cgsnap = db_utils.create_cgsnapshot(cg['id']) + def test_share_group_snapshot_get_all(self): + sg = db_utils.create_share_group() + expected_sg_snap = db_utils.create_share_group_snapshot(sg['id']) - snaps = db_api.cgsnapshot_get_all(self.ctxt, detailed=False) + snaps = db_api.share_group_snapshot_get_all(self.ctxt, detailed=False) self.assertEqual(1, len(snaps)) snap = snaps[0] self.assertEqual(2, len(dict(snap).keys())) - self.assertEqual(expected_cgsnap['id'], snap['id']) - self.assertEqual(expected_cgsnap['name'], snap['name']) + self.assertEqual(expected_sg_snap['id'], snap['id']) + self.assertEqual(expected_sg_snap['name'], snap['name']) - def test_cgsnapshot_get_all_with_detail(self): - cg = db_utils.create_consistency_group() - expected_cgsnap = db_utils.create_cgsnapshot(cg['id']) + def test_share_group_snapshot_get_all_with_detail(self): + sg = db_utils.create_share_group() + expected_sg_snap = db_utils.create_share_group_snapshot(sg['id']) - snaps = db_api.cgsnapshot_get_all(self.ctxt, detailed=True) + snaps = db_api.share_group_snapshot_get_all(self.ctxt, detailed=True) self.assertEqual(1, len(snaps)) snap = snaps[0] - self.assertDictMatch(dict(expected_cgsnap), dict(snap)) + self.assertDictMatch(dict(expected_sg_snap), dict(snap)) - def test_cgsnapshot_get_all_by_project(self): - fake_project = 'fake_project' - cg = db_utils.create_consistency_group() - expected_cgsnap = db_utils.create_cgsnapshot(cg['id'], - project_id=fake_project) + def test_share_group_snapshot_get_all_by_project(self): + fake_project = uuidutils.generate_uuid() + sg = db_utils.create_share_group() + expected_sg_snap = db_utils.create_share_group_snapshot( + sg['id'], project_id=fake_project) - snaps = db_api.cgsnapshot_get_all_by_project(self.ctxt, - fake_project, - detailed=False) + snaps = db_api.share_group_snapshot_get_all_by_project( + self.ctxt, fake_project, detailed=False) self.assertEqual(1, len(snaps)) snap = snaps[0] self.assertEqual(2, len(dict(snap).keys())) - self.assertEqual(expected_cgsnap['id'], snap['id']) - self.assertEqual(expected_cgsnap['name'], snap['name']) + self.assertEqual(expected_sg_snap['id'], snap['id']) + self.assertEqual(expected_sg_snap['name'], snap['name']) - def test_cgsnapshot_get_all_by_project_with_details(self): - fake_project = 'fake_project' - cg = db_utils.create_consistency_group() - expected_cgsnap = db_utils.create_cgsnapshot(cg['id'], - project_id=fake_project) + def test_share_group_snapshot_get_all_by_project_with_details(self): + fake_project = uuidutils.generate_uuid() + sg = db_utils.create_share_group() + expected_sg_snap = db_utils.create_share_group_snapshot( + sg['id'], project_id=fake_project) - snaps = db_api.cgsnapshot_get_all_by_project(self.ctxt, - fake_project, - detailed=True) + snaps = db_api.share_group_snapshot_get_all_by_project( + self.ctxt, fake_project, detailed=True) self.assertEqual(1, len(snaps)) snap = snaps[0] - self.assertDictMatch(dict(expected_cgsnap), dict(snap)) + self.assertDictMatch(dict(expected_sg_snap), dict(snap)) self.assertEqual(fake_project, snap['project_id']) - def test_cgsnapshot_update(self): + def test_share_group_snapshot_update(self): fake_name = "my_fake_name" - cg = db_utils.create_consistency_group() - expected_cgsnap = db_utils.create_cgsnapshot(cg['id']) - expected_cgsnap['name'] = fake_name + sg = db_utils.create_share_group() + expected_sg_snap = db_utils.create_share_group_snapshot(sg['id']) + expected_sg_snap['name'] = fake_name - db_api.cgsnapshot_update(self.ctxt, expected_cgsnap['id'], - {'name': fake_name}) + db_api.share_group_snapshot_update( + self.ctxt, expected_sg_snap['id'], {'name': fake_name}) - cgsnap = db_api.cgsnapshot_get(self.ctxt, expected_cgsnap['id']) - self.assertEqual(fake_name, cgsnap['name']) + sg_snap = db_api.share_group_snapshot_get( + self.ctxt, expected_sg_snap['id']) + self.assertEqual(fake_name, sg_snap['name']) - def test_cgsnapshot_destroy(self): - cg = db_utils.create_consistency_group() - cgsnap = db_utils.create_cgsnapshot(cg['id']) - db_api.cgsnapshot_get(self.ctxt, cgsnap['id']) + def test_share_group_snapshot_destroy(self): + sg = db_utils.create_share_group() + sg_snap = db_utils.create_share_group_snapshot(sg['id']) + db_api.share_group_snapshot_get(self.ctxt, sg_snap['id']) - db_api.cgsnapshot_destroy(self.ctxt, cgsnap['id']) + db_api.share_group_snapshot_destroy(self.ctxt, sg_snap['id']) - self.assertRaises(exception.NotFound, db_api.cgsnapshot_get, - self.ctxt, cgsnap['id']) + self.assertRaises( + exception.NotFound, + db_api.share_group_snapshot_get, self.ctxt, sg_snap['id']) - def test_cgsnapshot_members_get_all(self): - cg = db_utils.create_consistency_group() - cgsnap = db_utils.create_cgsnapshot(cg['id']) - expected_member = db_utils.create_cgsnapshot_member(cgsnap['id']) + def test_share_group_snapshot_members_get_all(self): + sg = db_utils.create_share_group() + sg_snap = db_utils.create_share_group_snapshot(sg['id']) + expected_member = db_utils.create_share_group_snapshot_member( + sg_snap['id']) - members = db_api.cgsnapshot_members_get_all(self.ctxt, cgsnap['id']) + members = db_api.share_group_snapshot_members_get_all( + self.ctxt, sg_snap['id']) self.assertEqual(1, len(members)) - member = members[0] - self.assertDictMatch(dict(expected_member), dict(member)) + self.assertDictMatch(dict(expected_member), dict(members[0])) - def test_count_cgsnapshot_members_in_share(self): + def test_count_share_group_snapshot_members_in_share(self): share = db_utils.create_share() share2 = db_utils.create_share() - cg = db_utils.create_consistency_group() - cgsnap = db_utils.create_cgsnapshot(cg['id']) - db_utils.create_cgsnapshot_member(cgsnap['id'], share_id=share['id']) - db_utils.create_cgsnapshot_member(cgsnap['id'], share_id=share2['id']) + sg = db_utils.create_share_group() + sg_snap = db_utils.create_share_group_snapshot(sg['id']) + db_utils.create_share_group_snapshot_member( + sg_snap['id'], share_id=share['id']) + db_utils.create_share_group_snapshot_member( + sg_snap['id'], share_id=share2['id']) - count = db_api.count_cgsnapshot_members_in_share( + count = db_api.count_share_group_snapshot_members_in_share( self.ctxt, share['id']) self.assertEqual(1, count) - def test_cgsnapshot_members_get(self): - cg = db_utils.create_consistency_group() - cgsnap = db_utils.create_cgsnapshot(cg['id']) - expected_member = db_utils.create_cgsnapshot_member(cgsnap['id']) + def test_share_group_snapshot_members_get(self): + sg = db_utils.create_share_group() + sg_snap = db_utils.create_share_group_snapshot(sg['id']) + expected_member = db_utils.create_share_group_snapshot_member( + sg_snap['id']) - member = db_api.cgsnapshot_member_get(self.ctxt, - expected_member['id']) + member = db_api.share_group_snapshot_member_get( + self.ctxt, expected_member['id']) self.assertDictMatch(dict(expected_member), dict(member)) - def test_cgsnapshot_members_get_not_found(self): - self.assertRaises(exception.CGSnapshotMemberNotFound, - db_api.cgsnapshot_member_get, self.ctxt, 'fake_id') + def test_share_group_snapshot_members_get_not_found(self): + self.assertRaises( + exception.ShareGroupSnapshotMemberNotFound, + db_api.share_group_snapshot_member_get, self.ctxt, 'fake_id') - def test_cgsnapshot_member_update(self): - cg = db_utils.create_consistency_group() - cgsnap = db_utils.create_cgsnapshot(cg['id']) - expected_member = db_utils.create_cgsnapshot_member(cgsnap['id']) + def test_share_group_snapshot_member_update(self): + sg = db_utils.create_share_group() + sg_snap = db_utils.create_share_group_snapshot(sg['id']) + expected_member = db_utils.create_share_group_snapshot_member( + sg_snap['id']) - db_api.cgsnapshot_member_update( + db_api.share_group_snapshot_member_update( self.ctxt, expected_member['id'], {'status': constants.STATUS_AVAILABLE}) - member = db_api.cgsnapshot_member_get(self.ctxt, expected_member['id']) + member = db_api.share_group_snapshot_member_get( + self.ctxt, expected_member['id']) self.assertEqual(constants.STATUS_AVAILABLE, member['status']) @@ -2360,19 +2392,6 @@ class PurgeDeletedTest(test.TestCase): 'deleted_at': self._days_ago(start, end)}, create_snapshot_instance=False) - # create consistency group - cg = db_utils.create_consistency_group( - deleted_at=self._days_ago(start, end)) - # create cg snapshot - db_utils.create_cgsnapshot( - cg.id, deleted_at=self._days_ago(start, end)) - # create cgsnapshot member - db_api.cgsnapshot_member_create( - self.context, - {'id': uuidutils.generate_uuid(), - 'share_id': share.id, - 'share_instance_id': s_instance.id, - 'deleted_at': self._days_ago(start, end)}) # update share instance db_api.share_instance_update( self.context, @@ -2384,9 +2403,7 @@ class PurgeDeletedTest(test.TestCase): for model in [models.ShareTypes, models.Share, models.ShareNetwork, models.ShareAccessMapping, models.ShareInstance, models.ShareServer, - models.ShareSnapshot, models.ConsistencyGroup, - models.CGSnapshot, models.SecurityService, - models.CGSnapshotMember]: + models.ShareSnapshot, models.SecurityService]: rows = db_api.model_query(self.context, model).count() self.assertEqual(num_left, rows) diff --git a/manila/tests/db_utils.py b/manila/tests/db_utils.py index f160ca17de..043513b678 100644 --- a/manila/tests/db_utils.py +++ b/manila/tests/db_utils.py @@ -29,9 +29,9 @@ def _create_db_row(method, default_values, custom_values): return method(context.get_admin_context(), default_values) -def create_consistency_group(**kwargs): - """Create a consistency group object.""" - cg = { +def create_share_group(**kwargs): + """Create a share group object.""" + share_group = { 'share_network_id': None, 'share_server_id': None, 'user_id': 'fake', @@ -39,22 +39,22 @@ def create_consistency_group(**kwargs): 'status': constants.STATUS_CREATING, 'host': 'fake_host' } - return _create_db_row(db.consistency_group_create, cg, kwargs) + return _create_db_row(db.share_group_create, share_group, kwargs) -def create_cgsnapshot(cg_id, **kwargs): - """Create a cgsnapshot object.""" +def create_share_group_snapshot(share_group_id, **kwargs): + """Create a share group snapshot object.""" snapshot = { - 'consistency_group_id': cg_id, + 'share_group_id': share_group_id, 'user_id': 'fake', 'project_id': 'fake', 'status': constants.STATUS_CREATING, } - return _create_db_row(db.cgsnapshot_create, snapshot, kwargs) + return _create_db_row(db.share_group_snapshot_create, snapshot, kwargs) -def create_cgsnapshot_member(cgsnapshot_id, **kwargs): - """Create a cgsnapshot member object.""" +def create_share_group_snapshot_member(share_group_snapshot_id, **kwargs): + """Create a share group snapshot member object.""" member = { 'share_proto': "NFS", 'size': 0, @@ -63,9 +63,10 @@ def create_cgsnapshot_member(cgsnapshot_id, **kwargs): 'user_id': 'fake', 'project_id': 'fake', 'status': 'creating', - 'cgsnapshot_id': cgsnapshot_id, + 'share_group_snapshot_id': share_group_snapshot_id, } - return _create_db_row(db.cgsnapshot_member_create, member, kwargs) + return _create_db_row( + db.share_group_snapshot_member_create, member, kwargs) def create_share_access(**kwargs): diff --git a/manila/tests/fake_driver.py b/manila/tests/fake_driver.py index 222d99dfed..177bc9fef9 100644 --- a/manila/tests/fake_driver.py +++ b/manila/tests/fake_driver.py @@ -104,8 +104,8 @@ class FakeShareDriver(driver.ShareDriver): return super(FakeShareDriver, self)._verify_share_server_handling( driver_handles_share_servers) - def create_consistency_group(self, context, cg_id): + def create_share_group(self, context, group_id, share_server=None): pass - def delete_consistency_group(self, context, cg_id): + def delete_share_group(self, context, group_id, share_server=None): pass diff --git a/manila/tests/fake_share.py b/manila/tests/fake_share.py index 253a528e8d..14ffdea802 100644 --- a/manila/tests/fake_share.py +++ b/manila/tests/fake_share.py @@ -38,7 +38,7 @@ def fake_share(**kwargs): 'snapshot_support': 'True', 'replication_type': None, 'is_busy': False, - 'consistency_group_id': 'fakecgid', + 'share_group_id': None, 'instance': {'host': 'fakehost'}, } share.update(kwargs) @@ -256,8 +256,8 @@ def fake_replica(id=None, as_primitive=True, for_manager=False, **kwargs): 'snapshot_id': None, 'share_proto': None, 'is_public': None, - 'consistency_group_id': None, - 'source_cgsnapshot_member_id': None, + 'share_group_id': None, + 'source_share_group_snapshot_member_id': None, 'availability_zone': 'fake_az', }) replica.update(kwargs) @@ -278,7 +278,7 @@ def fake_replica_request_spec(as_primitive=True, **kwargs): 'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f', 'snapshot_id': None, 'share_type': 'fake_share_type', - 'consistency_group': None, + 'share_group': None, 'active_replica_host': 'fake_active_replica_host', 'all_replica_hosts': all_replica_hosts, } diff --git a/manila/tests/policy.json b/manila/tests/policy.json index a6cc950f3b..167cc2ed56 100644 --- a/manila/tests/policy.json +++ b/manila/tests/policy.json @@ -91,20 +91,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:create_cgsnapshot" : "rule:default", - "consistency_group:delete_cgsnapshot": "rule:default", - "consistency_group:force_delete": "rule:admin_api", - "consistency_group:reset_status": "rule:admin_api", - "consistency_group:get_cgsnapshot": "rule:default", - "consistency_group:get_all_cgsnapshots": "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", - "cgsnapshot:force_delete": "rule:admin_api", - "cgsnapshot: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", @@ -114,5 +115,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" } diff --git a/manila/tests/scheduler/drivers/test_filter.py b/manila/tests/scheduler/drivers/test_filter.py index 61eaafb58d..802049ac1b 100644 --- a/manila/tests/scheduler/drivers/test_filter.py +++ b/manila/tests/scheduler/drivers/test_filter.py @@ -208,34 +208,6 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase): self.assertIsNone(weighed_host) self.assertTrue(_mock_service_get_all_by_topic.called) - @mock.patch('manila.db.service_get_all_by_topic') - def test_schedule_share_with_cg_pool_support( - self, _mock_service_get_all_by_topic): - sched = fakes.FakeFilterScheduler() - sched.host_manager = fakes.FakeHostManager() - fake_context = context.RequestContext('user', 'project', - is_admin=True) - fakes.mock_host_manager_db_calls(_mock_service_get_all_by_topic) - request_spec = { - 'share_type': { - 'name': 'NFS', - 'extra_specs': {'consistency_group_support': 'pool'} - }, - 'share_properties': {'project_id': 1, 'size': 1}, - 'share_instance_properties': {'project_id': 1, 'size': 1}, - 'consistency_group': { - 'id': 'fake-cg-id', - 'host': 'host5#_pool0', - } - } - - weighed_host = sched._schedule_share(fake_context, request_spec, {}) - - self.assertIsNotNone(weighed_host) - self.assertIsNotNone(weighed_host.obj) - self.assertEqual('host5#_pool0', weighed_host.obj.host) - self.assertTrue(_mock_service_get_all_by_topic.called) - def _setup_dedupe_fakes(self, extra_specs): sched = fakes.FakeFilterScheduler() sched.host_manager = fakes.FakeHostManager() @@ -401,41 +373,41 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase): filter_properties['retry']['hosts'][0]) self.assertEqual(1024, host_state.total_capacity_gb) - def test_schedule_create_consistency_group(self): + def test_schedule_create_share_group(self): # Ensure empty hosts/child_zones result in NoValidHosts exception. sched = fakes.FakeFilterScheduler() fake_context = context.RequestContext('user', 'project') fake_host = 'fake_host' request_spec = {'share_types': [{'id': 'NFS'}]} - self.mock_object(sched, "_get_best_host_for_consistency_group", + self.mock_object(sched, "_get_best_host_for_share_group", mock.Mock(return_value=fake_host)) fake_updated_group = mock.Mock() - self.mock_object(base, "cg_update_db", mock.Mock( + self.mock_object(base, "share_group_update_db", mock.Mock( return_value=fake_updated_group)) - self.mock_object(sched.share_rpcapi, "create_consistency_group") + self.mock_object(sched.share_rpcapi, "create_share_group") - sched.schedule_create_consistency_group(fake_context, 'fake_id', - request_spec, {}) + sched.schedule_create_share_group(fake_context, 'fake_id', + request_spec, {}) - sched._get_best_host_for_consistency_group.assert_called_once_with( + sched._get_best_host_for_share_group.assert_called_once_with( fake_context, request_spec) - base.cg_update_db.assert_called_once_with( + base.share_group_update_db.assert_called_once_with( fake_context, 'fake_id', fake_host) - sched.share_rpcapi.create_consistency_group.assert_called_once_with( + sched.share_rpcapi.create_share_group.assert_called_once_with( fake_context, fake_updated_group, fake_host) - def test_create_cg_no_hosts(self): + def test_create_group_no_hosts(self): # Ensure empty hosts/child_zones result in NoValidHosts exception. sched = fakes.FakeFilterScheduler() fake_context = context.RequestContext('user', 'project') request_spec = {'share_types': [{'id': 'NFS'}]} self.assertRaises(exception.NoValidHost, - sched.schedule_create_consistency_group, + sched.schedule_create_share_group, fake_context, 'fake_id', request_spec, {}) @mock.patch('manila.db.service_get_all_by_topic') - def test_get_weighted_candidates_for_consistency_group( + def test_get_weighted_candidates_for_share_group( self, _mock_service_get_all_by_topic): sched = fakes.FakeFilterScheduler() sched.host_manager = fakes.FakeHostManager() @@ -446,13 +418,13 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase): SNAPSHOT_SUPPORT: 'True', }}]} - hosts = sched._get_weighted_candidates_cg(fake_context, - request_spec) + hosts = sched._get_weighted_candidates_share_group( + fake_context, request_spec) self.assertTrue(hosts) @mock.patch('manila.db.service_get_all_by_topic') - def test_get_weighted_candidates_for_consistency_group_no_hosts( + def test_get_weighted_candidates_for_share_group_no_hosts( self, _mock_service_get_all_by_topic): sched = fakes.FakeFilterScheduler() sched.host_manager = fakes.FakeHostManager() @@ -463,13 +435,13 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase): SNAPSHOT_SUPPORT: 'False', }}]} - hosts = sched._get_weighted_candidates_cg(fake_context, - request_spec) + hosts = sched._get_weighted_candidates_share_group( + fake_context, request_spec) self.assertEqual([], hosts) @mock.patch('manila.db.service_get_all_by_topic') - def test_get_weighted_candidates_for_consistency_group_many_hosts( + def test_get_weighted_candidates_for_share_group_many_hosts( self, _mock_service_get_all_by_topic): sched = fakes.FakeFilterScheduler() sched.host_manager = fakes.FakeHostManager() @@ -480,10 +452,10 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase): SNAPSHOT_SUPPORT: 'True', }}]} - hosts = sched._get_weighted_candidates_cg(fake_context, - request_spec) + hosts = sched._get_weighted_candidates_share_group( + fake_context, request_spec) - self.assertEqual(2, len(hosts)) + self.assertEqual(6, len(hosts)) def _host_passes_filters_setup(self, mock_obj): sched = fakes.FakeFilterScheduler() diff --git a/manila/tests/scheduler/fakes.py b/manila/tests/scheduler/fakes.py index 2385bb674c..64a8de36dc 100644 --- a/manila/tests/scheduler/fakes.py +++ b/manila/tests/scheduler/fakes.py @@ -122,8 +122,7 @@ SHARE_SERVICE_STATES_WITH_POOLS = { reserved_percentage=0, provisioned_capacity_gb=100, max_over_subscription_ratio=20.0, - thin_provisioning=True, - consistency_group_support='pool')]), + thin_provisioning=True)]), 'host4@DDD': dict(share_backend_name='DDD', timestamp=None, reserved_percentage=0, driver_handles_share_servers=False, @@ -137,16 +136,14 @@ SHARE_SERVICE_STATES_WITH_POOLS = { reserved_percentage=0, provisioned_capacity_gb=800, max_over_subscription_ratio=2.0, - thin_provisioning=True, - consistency_group_support='host'), + thin_provisioning=True), dict(pool_name='pool4b', total_capacity_gb=542, free_capacity_gb=442, reserved_percentage=0, provisioned_capacity_gb=2000, max_over_subscription_ratio=10.0, - thin_provisioning=True, - consistency_group_support='host')]), + thin_provisioning=True)]), 'host5@EEE': dict(share_backend_name='EEE', timestamp=None, reserved_percentage=0, driver_handles_share_servers=False, @@ -233,7 +230,6 @@ class FakeHostManager(host_manager.HostManager): 'provisioned_capacity_gb': 256, 'max_over_subscription_ratio': 2.0, 'thin_provisioning': [False], - 'consistency_group_support': 'host', 'reserved_percentage': 0, 'snapshot_support': True, 'create_share_from_snapshot_support': True, @@ -262,7 +258,6 @@ class FakeHostManager(host_manager.HostManager): 'timestamp': None, 'snapshot_support': True, 'create_share_from_snapshot_support': True, - 'consistency_group_support': 'pool', 'replication_type': None, }, 'host6': {'total_capacity_gb': 'unknown', @@ -348,7 +343,7 @@ def fake_replica_request_spec(**kwargs): 'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f', 'snapshot_id': None, 'share_type': 'fake_share_type', - 'consistency_group': None, + 'share_group': None, } request_spec.update(kwargs) return request_spec diff --git a/manila/tests/scheduler/test_host_manager.py b/manila/tests/scheduler/test_host_manager.py index 9d6d759063..593a7199ff 100644 --- a/manila/tests/scheduler/test_host_manager.py +++ b/manila/tests/scheduler/test_host_manager.py @@ -212,7 +212,6 @@ class HostManagerTestCase(test.TestCase): 'snapshot_support': False, 'create_share_from_snapshot_support': False, 'revert_to_snapshot_support': True, - 'consistency_group_support': False, 'dedupe': False, 'compression': False, 'replication_type': None, @@ -239,7 +238,6 @@ class HostManagerTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': False, - 'consistency_group_support': False, 'dedupe': False, 'compression': False, 'replication_type': None, @@ -266,7 +264,6 @@ class HostManagerTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': False, - 'consistency_group_support': False, 'dedupe': False, 'compression': False, 'replication_type': None, @@ -315,7 +312,6 @@ class HostManagerTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': True, - 'consistency_group_support': False, 'dedupe': False, 'compression': False, 'replication_type': None, @@ -343,7 +339,6 @@ class HostManagerTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': False, - 'consistency_group_support': False, 'dedupe': False, 'compression': False, 'replication_type': None, @@ -371,7 +366,6 @@ class HostManagerTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': False, - 'consistency_group_support': 'pool', 'dedupe': False, 'compression': False, 'replication_type': None, @@ -399,7 +393,6 @@ class HostManagerTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': False, - 'consistency_group_support': 'host', 'dedupe': False, 'compression': False, 'replication_type': None, @@ -427,7 +420,6 @@ class HostManagerTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': False, - 'consistency_group_support': 'host', 'dedupe': False, 'compression': False, 'replication_type': None, @@ -488,7 +480,6 @@ class HostManagerTestCase(test.TestCase): 'provisioned_capacity_gb': 312, 'max_over_subscription_ratio': 1.0, 'thin_provisioning': False, - 'consistency_group_support': False, 'dedupe': False, 'compression': False, 'replication_type': None, @@ -515,7 +506,6 @@ class HostManagerTestCase(test.TestCase): 'provisioned_capacity_gb': 400, 'max_over_subscription_ratio': 2.0, 'thin_provisioning': True, - 'consistency_group_support': False, 'dedupe': False, 'compression': False, 'replication_type': None, @@ -570,7 +560,6 @@ class HostManagerTestCase(test.TestCase): 'thin_provisioning': True, 'vendor_name': None, 'storage_protocol': None, - 'consistency_group_support': False, 'dedupe': False, 'compression': False, 'replication_type': None, diff --git a/manila/tests/scheduler/test_manager.py b/manila/tests/scheduler/test_manager.py index 9794e9eff4..4ef53550f8 100644 --- a/manila/tests/scheduler/test_manager.py +++ b/manila/tests/scheduler/test_manager.py @@ -175,44 +175,45 @@ class SchedulerManagerTestCase(test.TestCase): mock_get_pools.assert_called_once_with(self.context, 'fake_filters') self.assertEqual('fake_pools', result) - @mock.patch.object(db, 'consistency_group_update', mock.Mock()) - def test_create_cg_no_valid_host_puts_cg_in_error_state(self): - """Test that NoValidHost is raised for create_consistency_group. + @mock.patch.object(db, 'share_group_update', mock.Mock()) + def test_create_group_no_valid_host_puts_group_in_error_state(self): + """Test that NoValidHost is raised for create_share_group. Puts the share in 'error' state and eats the exception. """ - fake_cg_id = 1 - cg_id = fake_cg_id - request_spec = {"consistency_group_id": cg_id} + fake_group_id = 1 + group_id = fake_group_id + request_spec = {"share_group_id": group_id} with mock.patch.object( - self.manager.driver, 'schedule_create_consistency_group', + self.manager.driver, 'schedule_create_share_group', mock.Mock(side_effect=self.raise_no_valid_host)): - self.manager.create_consistency_group(self.context, - fake_cg_id, - request_spec=request_spec, - filter_properties={}) - db.consistency_group_update.assert_called_once_with( - self.context, fake_cg_id, {'status': 'error'}) - (self.manager.driver.schedule_create_consistency_group. - assert_called_once_with(self.context, cg_id, request_spec, {})) + self.manager.create_share_group(self.context, + fake_group_id, + request_spec=request_spec, + filter_properties={}) + db.share_group_update.assert_called_once_with( + self.context, fake_group_id, {'status': 'error'}) + (self.manager.driver.schedule_create_share_group. + assert_called_once_with(self.context, group_id, request_spec, + {})) - @mock.patch.object(db, 'consistency_group_update', mock.Mock()) - def test_create_cg_exception_puts_cg_in_error_state(self): - """Test that exceptions for create_consistency_group. + @mock.patch.object(db, 'share_group_update', mock.Mock()) + def test_create_group_exception_puts_group_in_error_state(self): + """Test that exceptions for create_share_group. Puts the share in 'error' state and raises the exception. """ - fake_cg_id = 1 - cg_id = fake_cg_id - request_spec = {"consistency_group_id": cg_id} + fake_group_id = 1 + group_id = fake_group_id + request_spec = {"share_group_id": group_id} with mock.patch.object(self.manager.driver, - 'schedule_create_consistency_group', + 'schedule_create_share_group', mock.Mock(side_effect=exception.NotFound)): self.assertRaises(exception.NotFound, - self.manager.create_consistency_group, - self.context, fake_cg_id, + self.manager.create_share_group, + self.context, fake_group_id, request_spec=request_spec, filter_properties={}) diff --git a/manila/tests/scheduler/test_rpcapi.py b/manila/tests/scheduler/test_rpcapi.py index aed09fd959..d0e8abc147 100644 --- a/manila/tests/scheduler/test_rpcapi.py +++ b/manila/tests/scheduler/test_rpcapi.py @@ -93,13 +93,13 @@ class SchedulerRpcAPITestCase(test.TestCase): filters=None, version='1.1') - def test_create_consistency_group(self): - self._test_scheduler_api('create_consistency_group', + def test_create_share_group(self): + self._test_scheduler_api('create_share_group', rpc_method='cast', - cg_id='cg_id', + share_group_id='fake_share_group_id', request_spec='fake_request_spec', filter_properties='filter_properties', - version='1.3') + version='1.8') def test_migrate_share_to_host(self): self._test_scheduler_api('migrate_share_to_host', diff --git a/manila/tests/share/drivers/cephfs/test_cephfs_native.py b/manila/tests/share/drivers/cephfs/test_cephfs_native.py index 04a1e6eb91..2b182a8a3b 100644 --- a/manila/tests/share/drivers/cephfs/test_cephfs_native.py +++ b/manila/tests/share/drivers/cephfs/test_cephfs_native.py @@ -380,32 +380,30 @@ class CephFSNativeDriverTestCase(test.TestCase): self._driver._share_path(self._share), "snappy1_instance1")) - def test_create_consistency_group(self): - self._driver.create_consistency_group(self._context, {"id": "grp1"}, - None) + def test_create_share_group(self): + self._driver.create_share_group(self._context, {"id": "grp1"}, None) self._driver._volume_client.create_group.assert_called_once_with( "grp1") - def test_delete_consistency_group(self): - self._driver.delete_consistency_group(self._context, {"id": "grp1"}, - None) + def test_delete_share_group(self): + self._driver.delete_share_group(self._context, {"id": "grp1"}, None) self._driver._volume_client.destroy_group.assert_called_once_with( "grp1") - def test_create_cg_snapshot(self): - self._driver.create_cgsnapshot(self._context, { - 'consistency_group_id': 'cgid', + def test_create_share_snapshot(self): + self._driver.create_share_group_snapshot(self._context, { + 'share_group_id': 'cgid', 'id': 'snapid' }) (self._driver._volume_client.create_snapshot_group. assert_called_once_with("cgid", "snapid")) - def test_delete_cgsnapshot(self): - self._driver.delete_cgsnapshot(self._context, { - 'consistency_group_id': 'cgid', + def test_delete_share_group_snapshot(self): + self._driver.delete_share_group_snapshot(self._context, { + 'share_group_id': 'cgid', 'id': 'snapid' }) diff --git a/manila/tests/share/drivers/dell_emc/test_driver.py b/manila/tests/share/drivers/dell_emc/test_driver.py index fd1c479868..9d754a0cad 100644 --- a/manila/tests/share/drivers/dell_emc/test_driver.py +++ b/manila/tests/share/drivers/dell_emc/test_driver.py @@ -126,6 +126,7 @@ class EMCShareFrameworkTestCase(test.TestCase): data['snapshot_support'] = True data['create_share_from_snapshot_support'] = True data['revert_to_snapshot_support'] = False + data['share_group_snapshot_support'] = True data['replication_domain'] = None data['filter_function'] = None data['goodness_function'] = None diff --git a/manila/tests/share/drivers/glusterfs/test_glusterfs_native.py b/manila/tests/share/drivers/glusterfs/test_glusterfs_native.py index bcf5f4c8b0..f1b3f10ddd 100644 --- a/manila/tests/share/drivers/glusterfs/test_glusterfs_native.py +++ b/manila/tests/share/drivers/glusterfs/test_glusterfs_native.py @@ -258,6 +258,7 @@ class GlusterfsNativeShareDriverTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': False, + 'share_group_snapshot_support': True, 'replication_domain': None, 'filter_function': None, 'goodness_function': None, diff --git a/manila/tests/share/drivers/hpe/test_hpe_3par_driver.py b/manila/tests/share/drivers/hpe/test_hpe_3par_driver.py index 5669d72b9a..d9fc94749c 100644 --- a/manila/tests/share/drivers/hpe/test_hpe_3par_driver.py +++ b/manila/tests/share/drivers/hpe/test_hpe_3par_driver.py @@ -735,6 +735,7 @@ class HPE3ParDriverTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': False, + 'share_group_snapshot_support': True, 'storage_protocol': 'NFS_CIFS', 'thin_provisioning': True, 'total_capacity_gb': 0, @@ -811,6 +812,7 @@ class HPE3ParDriverTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': False, + 'share_group_snapshot_support': True, 'replication_domain': None, 'filter_function': None, 'goodness_function': None, @@ -849,6 +851,7 @@ class HPE3ParDriverTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': False, + 'share_group_snapshot_support': True, 'replication_domain': None, 'filter_function': None, 'goodness_function': None, diff --git a/manila/tests/share/drivers/huawei/test_huawei_nas.py b/manila/tests/share/drivers/huawei/test_huawei_nas.py index 35fdf714d8..d0f5c43d9b 100644 --- a/manila/tests/share/drivers/huawei/test_huawei_nas.py +++ b/manila/tests/share/drivers/huawei/test_huawei_nas.py @@ -2425,6 +2425,7 @@ class HuaweiShareDriverTestCase(test.TestCase): "snapshot_support": snapshot_support, "create_share_from_snapshot_support": snapshot_support, "revert_to_snapshot_support": False, + "share_group_snapshot_support": True, "replication_domain": None, "filter_function": None, "goodness_function": None, diff --git a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py index 9a94a7aca1..21e3f4004e 100644 --- a/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py +++ b/manila/tests/share/drivers/netapp/dataontap/cluster_mode/test_lib_base.py @@ -366,7 +366,6 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): 'driver_version': '1.0', 'netapp_storage_family': 'ontap_cluster', 'storage_protocol': 'NFS_CIFS', - 'consistency_group_support': 'host', 'pools': fake.POOLS, } self.assertDictEqual(expected, result) @@ -390,7 +389,6 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): 'driver_version': '1.0', 'netapp_storage_family': 'ontap_cluster', 'storage_protocol': 'NFS_CIFS', - 'consistency_group_support': 'host', 'replication_type': 'dr', 'replication_domain': 'fake_domain', 'pools': fake.POOLS, @@ -1825,7 +1823,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase): fake_cg_snapshot = copy.deepcopy(fake.CG_SNAPSHOT) fake_cg_snapshot['cgsnapshot_members'] = [] - self.assertRaises(exception.InvalidConsistencyGroup, + self.assertRaises(exception.InvalidShareGroup, self.library._collate_cg_snapshot_info, fake.CONSISTENCY_GROUP_DEST, fake_cg_snapshot) diff --git a/manila/tests/share/drivers/netapp/dataontap/fakes.py b/manila/tests/share/drivers/netapp/dataontap/fakes.py index 147b5e9239..b1dc570e26 100644 --- a/manila/tests/share/drivers/netapp/dataontap/fakes.py +++ b/manila/tests/share/drivers/netapp/dataontap/fakes.py @@ -399,7 +399,7 @@ CG_SNAPSHOT_MEMBER_2 = { CG_SNAPSHOT = { 'cgsnapshot_members': [CG_SNAPSHOT_MEMBER_1, CG_SNAPSHOT_MEMBER_2], - 'consistency_group': CONSISTENCY_GROUP, + 'share_group': CONSISTENCY_GROUP, 'consistency_group_id': CONSISTENCY_GROUP_ID, 'id': CG_SNAPSHOT_ID, 'project_id': TENANT_ID, diff --git a/manila/tests/share/drivers/test_generic.py b/manila/tests/share/drivers/test_generic.py index 119fc7066c..d822a5319f 100644 --- a/manila/tests/share/drivers/test_generic.py +++ b/manila/tests/share/drivers/test_generic.py @@ -66,27 +66,6 @@ def get_fake_snap_dict(): 'created_at': '2015-08-10 00:05:58', 'updated_at': '2015-08-10 00:05:58', 'consistency_group_id': None, - 'cgsnapshot_members': [ - { - 'status': 'available', - 'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f', - 'share_id': 'e14b5174-e534-4f35-bc4f-fe81c1575d6f', - 'user_id': 'a0314a441ca842019b0952224aa39192', - 'deleted': 'False', - 'created_at': '2015-08-10 00:05:58', - 'share': { - 'id': '03e2f06e-14f2-45a5-9631-0949d1937bd8', - 'deleted': False, - }, - 'updated_at': '2015-08-10 00:05:58', - 'share_proto': 'NFS', - 'project_id': '13c0be6290934bd98596cfa004650049', - 'cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', - 'deleted_at': None, - 'id': '03e2f06e-14f2-45a5-9631-0949d1937bd8', - 'size': 1, - }, - ], 'deleted_at': None, 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'name': None, @@ -94,58 +73,6 @@ def get_fake_snap_dict(): return snap_dict -def get_fake_cg_dict(): - cg_dict = { - 'status': 'creating', - 'project_id': '13c0be6290934bd98596cfa004650049', - 'user_id': 'a0314a441ca842019b0952224aa39192', - 'description': None, - 'deleted': 'False', - 'created_at': '2015-08-10 00:07:58', - 'updated_at': None, - 'source_cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', - 'host': 'openstack2@cmodeSSVMNFS', - 'deleted_at': None, - 'shares': [ - { - 'id': '02a32f06e-14f2-45a5-9631-7483f1937bd8', - 'deleted': False, - 'source_cgsnapshot_member_id': - '03e2f06e-14f2-45a5-9631-0949d1937bd8', - }, - - ], - 'share_types': [ - { - 'id': 'f6aa3b56-45a5-9631-02a32f06e1937b', - 'deleted': False, - 'consistency_group_id': None, - 'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f', - }, - ], - 'id': 'eda52174-0442-476d-9694-a58327466c14', - 'name': None - } - return cg_dict - - -def get_fake_collated_cg_snap_info(): - fake_collated_cg_snap_info = [ - { - 'share': { - 'id': '02a32f06e-14f2-45a5-9631-7483f1937bd8', - 'deleted': False, - 'source_cgsnapshot_member_id': - '03e2f06e-14f2-45a5-9631-0949d1937bd8', - }, - 'snapshot': { - 'id': '03e2f06e-14f2-45a5-9631-0949d1937bd8', - }, - }, - ] - return fake_collated_cg_snap_info - - def get_fake_access_rule(access_to, access_level, access_type='ip'): return { 'access_type': access_type, diff --git a/manila/tests/share/drivers/test_lvm.py b/manila/tests/share/drivers/test_lvm.py index 69d93496fc..7a7f265c2b 100644 --- a/manila/tests/share/drivers/test_lvm.py +++ b/manila/tests/share/drivers/test_lvm.py @@ -520,7 +520,6 @@ class LVMShareDriverTestCase(test.TestCase): self.assertEqual('LVM', self._driver._stats['share_backend_name']) self.assertEqual('NFS_CIFS', self._driver._stats['storage_protocol']) self.assertEqual(50, self._driver._stats['reserved_percentage']) - self.assertIsNone(self._driver._stats['consistency_group_support']) self.assertTrue(self._driver._stats['snapshot_support']) self.assertEqual('LVMShareDriver', self._driver._stats['driver_name']) self.assertEqual('test-pool', self._driver._stats['pools']) diff --git a/manila/tests/share/drivers/zfsonlinux/test_driver.py b/manila/tests/share/drivers/zfsonlinux/test_driver.py index f0f9542b48..376aebd0cd 100644 --- a/manila/tests/share/drivers/zfsonlinux/test_driver.py +++ b/manila/tests/share/drivers/zfsonlinux/test_driver.py @@ -343,7 +343,6 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase): self.mock_object(self.driver, '_get_pools_info') self.assertEqual({}, self.driver._stats) expected = { - 'consistency_group_support': None, 'driver_handles_share_servers': False, 'driver_name': 'ZFS', 'driver_version': '1.0', @@ -356,6 +355,7 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase): 'snapshot_support': True, 'create_share_from_snapshot_support': True, 'revert_to_snapshot_support': False, + 'share_group_snapshot_support': True, 'storage_protocol': 'NFS', 'total_capacity_gb': 'unknown', 'vendor_name': 'Open Source', diff --git a/manila/tests/share/test_api.py b/manila/tests/share/test_api.py index 6a24ccd554..50e67e834f 100644 --- a/manila/tests/share/test_api.py +++ b/manila/tests/share/test_api.py @@ -708,7 +708,7 @@ class ShareAPITestCase(test.TestCase): self.context, request_spec=mock.ANY, filter_properties={}) self.assertFalse(self.api.share_rpcapi.create_share_instance.called) - def test_create_instance_cgsnapshot_member(self): + def test_create_instance_share_group_snapshot_member(self): fake_req_spec = { 'share_properties': 'fake_share_properties', 'share_instance_properties': 'fake_share_instance_properties', @@ -722,7 +722,7 @@ class ShareAPITestCase(test.TestCase): fake_instance = fakes.fake_share_instance( share_id=share['id'], **member_info) - cgsnapmember = {'share_instance': fake_instance} + sg_snap_member = {'share_instance': fake_instance} self.mock_policy_check = self.mock_object( policy, 'check_policy', mock.Mock(return_value=True)) mock_share_rpcapi_call = self.mock_object(self.share_rpcapi, @@ -735,8 +735,9 @@ class ShareAPITestCase(test.TestCase): share_api.API, 'create_share_instance_and_get_request_spec', mock.Mock(return_value=(fake_req_spec, fake_instance))) - retval = self.api.create_instance(self.context, fakes.fake_share(), - cgsnapshot_member=cgsnapmember) + retval = self.api.create_instance( + self.context, fakes.fake_share(), + share_group_snapshot_member=sg_snap_member) self.assertIsNone(retval) mock_db_share_instance_update.assert_called_once_with( @@ -895,11 +896,11 @@ class ShareAPITestCase(test.TestCase): 'share_proto': kwargs.get('share_proto', share.get('share_proto')), 'share_type_id': share_type['id'], 'is_public': kwargs.get('is_public', share.get('is_public')), - 'consistency_group_id': kwargs.get( - 'consistency_group_id', share.get('consistency_group_id')), - 'source_cgsnapshot_member_id': kwargs.get( - 'source_cgsnapshot_member_id', - share.get('source_cgsnapshot_member_id')), + 'share_group_id': kwargs.get( + 'share_group_id', share.get('share_group_id')), + 'source_share_group_snapshot_member_id': kwargs.get( + 'source_share_group_snapshot_member_id', + share.get('source_share_group_snapshot_member_id')), 'snapshot_id': kwargs.get('snapshot_id', share.get('snapshot_id')), } share_instance_properties = { @@ -1406,7 +1407,7 @@ class ShareAPITestCase(test.TestCase): @mock.patch.object(db_api, 'share_instances_get_all_by_share_server', mock.Mock(return_value=[])) - @mock.patch.object(db_api, 'consistency_group_get_all_by_share_server', + @mock.patch.object(db_api, 'share_group_get_all_by_share_server', mock.Mock(return_value=[])) def test_delete_share_server_no_dependent_shares(self): server = {'id': 'fake_share_server_id'} @@ -1418,14 +1419,14 @@ class ShareAPITestCase(test.TestCase): self.api.delete_share_server(self.context, server) db_api.share_instances_get_all_by_share_server.assert_called_once_with( self.context, server['id']) - db_api.consistency_group_get_all_by_share_server.\ + db_api.share_group_get_all_by_share_server.\ assert_called_once_with(self.context, server['id']) self.share_rpcapi.delete_share_server.assert_called_once_with( self.context, server_returned) @mock.patch.object(db_api, 'share_instances_get_all_by_share_server', mock.Mock(return_value=['fake_share', ])) - @mock.patch.object(db_api, 'consistency_group_get_all_by_share_server', + @mock.patch.object(db_api, 'share_group_get_all_by_share_server', mock.Mock(return_value=[])) def test_delete_share_server_dependent_share_exists(self): server = {'id': 'fake_share_server_id'} @@ -1438,9 +1439,9 @@ class ShareAPITestCase(test.TestCase): @mock.patch.object(db_api, 'share_instances_get_all_by_share_server', mock.Mock(return_value=[])) - @mock.patch.object(db_api, 'consistency_group_get_all_by_share_server', - mock.Mock(return_value=['fake_cg', ])) - def test_delete_share_server_dependent_cg_exists(self): + @mock.patch.object(db_api, 'share_group_get_all_by_share_server', + mock.Mock(return_value=['fake_group', ])) + def test_delete_share_server_dependent_group_exists(self): server = {'id': 'fake_share_server_id'} self.assertRaises(exception.ShareServerInUse, self.api.delete_share_server, @@ -1449,7 +1450,7 @@ class ShareAPITestCase(test.TestCase): db_api.share_instances_get_all_by_share_server.assert_called_once_with( self.context, server['id']) - db_api.consistency_group_get_all_by_share_server.\ + db_api.share_group_get_all_by_share_server.\ assert_called_once_with(self.context, server['id']) @mock.patch.object(db_api, 'share_snapshot_instance_update', mock.Mock()) @@ -1597,7 +1598,7 @@ class ShareAPITestCase(test.TestCase): self.context, share, share_network_id=share['share_network_id'], host=valid_host, share_type_id=share_type['id'], availability_zone=snapshot['share']['availability_zone'], - consistency_group=None, cgsnapshot_member=None) + share_group=None, share_group_snapshot_member=None) share_api.policy.check_policy.assert_has_calls([ mock.call(self.context, 'share', 'create'), mock.call(self.context, 'share_snapshot', 'get_snapshot')]) @@ -1713,9 +1714,9 @@ class ShareAPITestCase(test.TestCase): self.assertRaises(exception.Conflict, self.api.delete, self.context, share) - @mock.patch.object(db_api, 'count_cgsnapshot_members_in_share', + @mock.patch.object(db_api, 'count_share_group_snapshot_members_in_share', mock.Mock(return_value=2)) - def test_delete_dependent_cgsnapshot_members(self): + def test_delete_dependent_share_group_snapshot_members(self): share_server_id = 'fake-ss-id' share = self._setup_delete_mocks(constants.STATUS_AVAILABLE, share_server_id) diff --git a/manila/tests/share/test_driver.py b/manila/tests/share/test_driver.py index 9de67e688c..9890d3baa9 100644 --- a/manila/tests/share/test_driver.py +++ b/manila/tests/share/test_driver.py @@ -676,3 +676,336 @@ class ShareDriverTestCase(test.TestCase): share_driver.update_replicated_snapshot, 'fake_context', ['r1', 'r2'], 'r1', ['s1', 's2'], 's1') + + @ddt.data(True, False) + def test_share_group_snapshot_support_exists_and_equals_snapshot_support( + self, snapshots_are_supported): + driver.CONF.set_default('driver_handles_share_servers', True) + child_class_instance = driver.ShareDriver(True) + child_class_instance._snapshots_are_supported = snapshots_are_supported + self.mock_object(child_class_instance, "configuration") + + child_class_instance._update_share_stats() + + self.assertEqual( + snapshots_are_supported, + child_class_instance._stats["snapshot_support"]) + self.assertEqual( + snapshots_are_supported, + child_class_instance._stats["share_group_snapshot_support"]) + self.assertTrue(child_class_instance.configuration.safe_get.called) + + def test_create_share_group_from_share_group_snapshot(self): + share_driver = self._instantiate_share_driver(None, False) + fake_shares = [ + {'id': 'fake_share_%d' % i, + 'source_share_group_snapshot_member_id': 'fake_member_%d' % i} + for i in (1, 2)] + fake_share_group_dict = { + 'source_share_group_snapshot_id': 'some_fake_uuid_abc', + 'shares': fake_shares, + 'id': 'some_fake_uuid_def', + } + fake_share_group_snapshot_dict = { + 'share_group_snapshot_members': [ + {'id': 'fake_member_1'}, {'id': 'fake_member_2'}], + 'id': 'fake_share_group_snapshot_id', + } + mock_create = self.mock_object( + share_driver, 'create_share_from_snapshot', + mock.Mock(side_effect=['fake_export1', 'fake_export2'])) + expected_share_updates = [ + { + 'id': 'fake_share_1', + 'export_locations': 'fake_export1', + }, + { + 'id': 'fake_share_2', + 'export_locations': 'fake_export2', + }, + ] + + share_group_update, share_update = ( + share_driver.create_share_group_from_share_group_snapshot( + 'fake_context', fake_share_group_dict, + fake_share_group_snapshot_dict)) + + mock_create.assert_has_calls([ + mock.call( + 'fake_context', + {'id': 'fake_share_1', + 'source_share_group_snapshot_member_id': 'fake_member_1'}, + {'id': 'fake_member_1'}), + mock.call( + 'fake_context', + {'id': 'fake_share_2', + 'source_share_group_snapshot_member_id': 'fake_member_2'}, + {'id': 'fake_member_2'}) + ]) + self.assertIsNone(share_group_update) + self.assertEqual(expected_share_updates, share_update) + + def test_create_share_group_from_share_group_snapshot_dhss(self): + share_driver = self._instantiate_share_driver(None, True) + mock_share_server = mock.Mock() + fake_shares = [ + {'id': 'fake_share_1', + 'source_share_group_snapshot_member_id': 'foo_member_1'}, + {'id': 'fake_share_2', + 'source_share_group_snapshot_member_id': 'foo_member_2'}] + fake_share_group_dict = { + 'source_share_group_snapshot_id': 'some_fake_uuid', + 'shares': fake_shares, + 'id': 'eda52174-0442-476d-9694-a58327466c14', + } + fake_share_group_snapshot_dict = { + 'share_group_snapshot_members': [ + {'id': 'foo_member_1'}, {'id': 'foo_member_2'}], + 'id': 'fake_share_group_snapshot_id' + } + mock_create = self.mock_object( + share_driver, 'create_share_from_snapshot', + mock.Mock(side_effect=['fake_export1', 'fake_export2'])) + expected_share_updates = [ + {'id': 'fake_share_1', 'export_locations': 'fake_export1'}, + {'id': 'fake_share_2', 'export_locations': 'fake_export2'}, + ] + + share_group_update, share_update = ( + share_driver.create_share_group_from_share_group_snapshot( + 'fake_context', + fake_share_group_dict, + fake_share_group_snapshot_dict, share_server=mock_share_server, + ) + ) + + mock_create.assert_has_calls([ + mock.call( + 'fake_context', + {'id': 'fake_share_%d' % i, + 'source_share_group_snapshot_member_id': 'foo_member_%d' % i}, + {'id': 'foo_member_%d' % i}, + share_server=mock_share_server) + for i in (1, 2) + ]) + self.assertIsNone(share_group_update) + self.assertEqual(expected_share_updates, share_update) + + def test_create_share_group_from_sg_snapshot_with_no_members(self): + share_driver = self._instantiate_share_driver(None, False) + fake_share_group_dict = {} + fake_share_group_snapshot_dict = {'share_group_snapshot_members': []} + + share_group_update, share_update = ( + share_driver.create_share_group_from_share_group_snapshot( + 'fake_context', fake_share_group_dict, + fake_share_group_snapshot_dict)) + + self.assertIsNone(share_group_update) + self.assertIsNone(share_update) + + def test_create_share_group_snapshot(self): + fake_snap_member_1 = { + 'share_group_snapshot_id': 'fake_sg_snap_id', + 'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e', + 'share_id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2', + 'status': 'bar', + } + fake_snap_member_2 = { + 'share_group_snapshot_id': 'fake_sg_snap_id', + 'id': '1e010dfe-545b-432d-ab95-4ef03cd82f89', + 'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296', + 'status': 'foo', + } + fake_snap_dict = { + 'status': 'available', + 'project_id': '13c0be6290934bd98596cfa004650049', + 'user_id': 'a0314a441ca842019b0952224aa39192', + 'description': None, + 'deleted': '0', + 'share_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67', + 'share_group_snapshot_members': [ + fake_snap_member_1, fake_snap_member_2], + 'deleted_at': None, + 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', + 'name': None + } + share_driver = self._instantiate_share_driver(None, False) + share_driver._stats['share_group_snapshot_support'] = True + mock_create_snap = self.mock_object(share_driver, 'create_snapshot') + + share_group_snapshot_update, member_update_list = ( + share_driver.create_share_group_snapshot( + 'fake_context', fake_snap_dict)) + + mock_create_snap.assert_has_calls([ + mock.call( + 'fake_context', + {'id': fake_snap_member_1['id'], + 'share_id': fake_snap_member_1['share_id'], + 'snapshot_id': fake_snap_member_1['share_group_snapshot_id']}, + share_server=None), + mock.call( + 'fake_context', + {'id': fake_snap_member_2['id'], + 'share_id': fake_snap_member_2['share_id'], + 'snapshot_id': fake_snap_member_2['share_group_snapshot_id']}, + share_server=None), + ]) + self.assertIsNone(share_group_snapshot_update) + self.assertIsNone(member_update_list) + + def test_create_share_group_snapshot_failed_snapshot(self): + fake_snap_member_1 = { + 'share_group_snapshot_id': 'fake_sg_snap_id', + 'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e', + 'share_id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2', + 'status': 'bar', + } + fake_snap_member_2 = { + 'share_group_snapshot_id': 'fake_sg_snap_id', + 'id': '1e010dfe-545b-432d-ab95-4ef03cd82f89', + 'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296', + 'status': 'foo', + } + fake_snap_dict = { + 'status': 'available', + 'project_id': '13c0be6290934bd98596cfa004650049', + 'user_id': 'a0314a441ca842019b0952224aa39192', + 'description': None, + 'deleted': '0', + 'share_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67', + 'share_group_snapshot_members': [ + fake_snap_member_1, fake_snap_member_2], + 'deleted_at': None, + 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', + 'name': None + } + expected_exception = exception.ManilaException + + share_driver = self._instantiate_share_driver(None, False) + share_driver._stats['share_group_snapshot_support'] = True + mock_create_snap = self.mock_object( + share_driver, 'create_snapshot', + mock.Mock(side_effect=[None, expected_exception])) + mock_delete_snap = self.mock_object(share_driver, 'delete_snapshot') + + self.assertRaises( + expected_exception, + share_driver.create_share_group_snapshot, + 'fake_context', fake_snap_dict) + + fake_snap_member_1_expected = { + 'id': fake_snap_member_1['id'], + 'share_id': fake_snap_member_1['share_id'], + 'snapshot_id': fake_snap_member_1['share_group_snapshot_id'], + } + mock_create_snap.assert_has_calls([ + mock.call( + 'fake_context', fake_snap_member_1_expected, share_server=None, + ), + mock.call( + 'fake_context', + {'id': fake_snap_member_2['id'], + 'share_id': fake_snap_member_2['share_id'], + 'snapshot_id': fake_snap_member_2['share_group_snapshot_id']}, + share_server=None), + ]) + mock_delete_snap.assert_called_with( + 'fake_context', fake_snap_member_1_expected, share_server=None) + + def test_create_share_group_snapshot_no_support(self): + fake_snap_dict = { + 'status': 'available', + 'project_id': '13c0be6290934bd98596cfa004650049', + 'user_id': 'a0314a441ca842019b0952224aa39192', + 'description': None, + 'deleted': '0', + 'share_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67', + 'share_group_snapshot_members': [ + { + 'status': 'available', + 'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f', + 'user_id': 'a0314a441ca842019b0952224aa39192', + 'deleted': 'False', + 'share_proto': 'NFS', + 'project_id': '13c0be6290934bd98596cfa004650049', + 'share_group_snapshot_id': + 'f6aa3b59-57eb-421e-965c-4e182538e36a', + 'deleted_at': None, + 'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e', + 'size': 1 + }, + ], + 'deleted_at': None, + 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', + 'name': None + } + share_driver = self._instantiate_share_driver(None, False) + share_driver._stats['share_group_snapshot_support'] = False + + self.assertRaises( + exception.ShareGroupSnapshotNotSupported, + share_driver.create_share_group_snapshot, + 'fake_context', fake_snap_dict) + + def test_create_share_group_snapshot_no_members(self): + fake_snap_dict = { + 'status': 'available', + 'project_id': '13c0be6290934bd98596cfa004650049', + 'user_id': 'a0314a441ca842019b0952224aa39192', + 'description': None, + 'deleted': '0', + 'share_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67', + 'share_group_snapshot_members': [], + 'deleted_at': None, + 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', + 'name': None + } + share_driver = self._instantiate_share_driver(None, False) + share_driver._stats['share_group_snapshot_support'] = True + + share_group_snapshot_update, member_update_list = ( + share_driver.create_share_group_snapshot( + 'fake_context', fake_snap_dict)) + + self.assertIsNone(share_group_snapshot_update) + self.assertIsNone(member_update_list) + + def test_delete_share_group_snapshot(self): + fake_snap_member_1 = { + 'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e', + 'share_id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2' + } + fake_snap_member_2 = { + 'id': '1e010dfe-545b-432d-ab95-4ef03cd82f89', + 'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296' + } + fake_snap_dict = { + 'status': 'available', + 'project_id': '13c0be6290934bd98596cfa004650049', + 'user_id': 'a0314a441ca842019b0952224aa39192', + 'description': None, + 'deleted': '0', + 'share_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67', + 'share_group_snapshot_members': [ + fake_snap_member_1, fake_snap_member_2], + 'deleted_at': None, + 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', + 'name': None + } + + share_driver = self._instantiate_share_driver(None, False) + share_driver._stats['share_group_snapshot_support'] = True + mock_delete_snap = self.mock_object(share_driver, 'delete_snapshot') + + share_group_snapshot_update, member_update_list = ( + share_driver.delete_share_group_snapshot( + 'fake_context', fake_snap_dict)) + + mock_delete_snap.assert_has_calls([ + mock.call('fake_context', fake_snap_member_1, share_server=None), + mock.call('fake_context', fake_snap_member_2, share_server=None), + ]) + self.assertIsNone(share_group_snapshot_update) + self.assertIsNone(member_update_list) diff --git a/manila/tests/share/test_manager.py b/manila/tests/share/test_manager.py index c95fff3009..6c0b99a3ff 100644 --- a/manila/tests/share/test_manager.py +++ b/manila/tests/share/test_manager.py @@ -200,10 +200,10 @@ class ShareManagerTestCase(test.TestCase): "delete_share_server", "extend_share", "shrink_share", - "create_consistency_group", - "delete_consistency_group", - "create_cgsnapshot", - "delete_cgsnapshot", + "create_share_group", + "delete_share_group", + "create_share_group_snapshot", + "delete_share_group_snapshot", "create_share_replica", "delete_share_replica", "promote_share_replica", @@ -1943,7 +1943,7 @@ class ShareManagerTestCase(test.TestCase): ) driver_method_mock.assert_called_once_with( self.context, [fake_share_server], share.instance, snapshot=None, - consistency_group=None) + share_group=None) def test_provide_share_server_for_share_invalid_arguments(self): self.assertRaises(ValueError, @@ -1994,34 +1994,36 @@ class ShareManagerTestCase(test.TestCase): db.share_server_get.assert_called_once_with( self.context, fake_parent_id) - def test_provide_share_server_for_cg_incompatible_servers(self): + def test_provide_share_server_for_share_group_incompatible_servers(self): fake_exception = exception.ManilaException("fake") fake_share_server = {'id': 'fake'} - cg = db_utils.create_consistency_group() + sg = db_utils.create_share_group() self.mock_object(db, 'share_server_get_all_by_host_and_share_net_valid', mock.Mock(return_value=[fake_share_server])) self.mock_object( self.share_manager.driver, - "choose_share_server_compatible_with_cg", + "choose_share_server_compatible_with_share_group", mock.Mock(side_effect=fake_exception) ) - self.assertRaises(exception.ManilaException, - self.share_manager._provide_share_server_for_cg, - self.context, "fake_id", cg) + self.assertRaises( + exception.ManilaException, + self.share_manager._provide_share_server_for_share_group, + self.context, "fake_id", sg) + driver_mock = self.share_manager.driver driver_method_mock = ( - driver_mock.choose_share_server_compatible_with_cg - ) + driver_mock.choose_share_server_compatible_with_share_group) driver_method_mock.assert_called_once_with( - self.context, [fake_share_server], cg, cgsnapshot=None) + self.context, [fake_share_server], sg, share_group_snapshot=None) - def test_provide_share_server_for_cg_invalid_arguments(self): - self.assertRaises(exception.InvalidInput, - self.share_manager._provide_share_server_for_cg, - self.context, None, None) + def test_provide_share_server_for_share_group_invalid_arguments(self): + self.assertRaises( + exception.InvalidInput, + self.share_manager._provide_share_server_for_share_group, + self.context, None, None) def test_manage_share_invalid_driver(self): self.mock_object(self.share_manager, 'driver', mock.Mock()) @@ -3144,19 +3146,19 @@ class ShareManagerTestCase(test.TestCase): self.assertEqual(old_capabilities, self.share_manager.last_capabilities) - def test_create_consistency_group(self): - fake_cg = {'id': 'fake_id'} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'consistency_group_update', - mock.Mock(return_value=fake_cg)) + def test_create_share_group(self): + fake_group = {'id': 'fake_id'} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_update', + mock.Mock(return_value=fake_group)) self.mock_object(self.share_manager.driver, - 'create_consistency_group', + 'create_share_group', mock.Mock(return_value=None)) - self.share_manager.create_consistency_group(self.context, "fake_id") + self.share_manager.create_share_group(self.context, "fake_id") - self.share_manager.db.consistency_group_update.\ + self.share_manager.db.share_group_update.\ assert_called_once_with(mock.ANY, 'fake_id', {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) @@ -3166,207 +3168,226 @@ class ShareManagerTestCase(test.TestCase): self.mock_object( self.share_manager.driver.configuration, 'safe_get', mock.Mock(return_value=False)) - cg_id = 'fake_cg_id' + cg_id = 'fake_group_id' share_network_id = 'fake_sn' - fake_cg = {'id': 'fake_id', 'share_network_id': share_network_id} + fake_group = {'id': 'fake_id', 'share_network_id': share_network_id} self.mock_object( - self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'consistency_group_update') + self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_update') self.assertRaises( exception.ManilaException, - self.share_manager.create_consistency_group, self.context, cg_id) + self.share_manager.create_share_group, self.context, cg_id) - self.share_manager.db.consistency_group_get.assert_called_once_with( + self.share_manager.db.share_group_get.assert_called_once_with( utils.IsAMatcher(context.RequestContext), cg_id) - self.share_manager.db.consistency_group_update.assert_called_once_with( + self.share_manager.db.share_group_update.assert_called_once_with( utils.IsAMatcher(context.RequestContext), cg_id, {'status': constants.STATUS_ERROR}) - def test_create_cg_with_share_network_driver_handles_servers(self): + def test_create_sg_with_share_network_driver_handles_servers(self): manager.CONF.set_default('driver_handles_share_servers', True) self.mock_object( self.share_manager.driver.configuration, 'safe_get', mock.Mock(return_value=True)) share_network_id = 'fake_sn' - fake_cg = {'id': 'fake_id', 'share_network_id': share_network_id, - 'host': "fake_host"} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'consistency_group_update', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager, '_provide_share_server_for_cg', - mock.Mock(return_value=({}, fake_cg))) + fake_group = { + 'id': 'fake_id', + 'share_network_id': share_network_id, + 'host': "fake_host", + } + self.mock_object( + self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object( + self.share_manager.db, 'share_group_update', + mock.Mock(return_value=fake_group)) + self.mock_object( + self.share_manager, '_provide_share_server_for_share_group', + mock.Mock(return_value=({}, fake_group))) + self.mock_object( + self.share_manager.driver, 'create_share_group', + mock.Mock(return_value=None)) + + self.share_manager.create_share_group(self.context, "fake_id") + + self.share_manager.db.share_group_update.assert_called_once_with( + mock.ANY, 'fake_id', + {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) + + def test_create_share_group_with_update(self): + fake_group = {'id': 'fake_id'} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_update', + mock.Mock(return_value=fake_group)) self.mock_object(self.share_manager.driver, - 'create_consistency_group', - mock.Mock(return_value=None)) - - self.share_manager.create_consistency_group(self.context, "fake_id") - - self.share_manager.db.consistency_group_update.\ - assert_called_once_with(mock.ANY, 'fake_id', - {'status': constants.STATUS_AVAILABLE, - 'created_at': mock.ANY}) - - def test_create_consistency_group_with_update(self): - fake_cg = {'id': 'fake_id'} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'consistency_group_update', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.driver, - 'create_consistency_group', + 'create_share_group', mock.Mock(return_value={'foo': 'bar'})) - self.share_manager.create_consistency_group(self.context, "fake_id") + self.share_manager.create_share_group(self.context, "fake_id") - self.share_manager.db.consistency_group_update.\ + self.share_manager.db.share_group_update.\ assert_any_call(mock.ANY, 'fake_id', {'foo': 'bar'}) - self.share_manager.db.consistency_group_update.\ + self.share_manager.db.share_group_update.\ assert_any_call(mock.ANY, 'fake_id', {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) - def test_create_consistency_group_with_error(self): - fake_cg = {'id': 'fake_id'} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'consistency_group_update', - mock.Mock(return_value=fake_cg)) + def test_create_share_group_with_error(self): + fake_group = {'id': 'fake_id'} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_update', + mock.Mock(return_value=fake_group)) self.mock_object(self.share_manager.driver, - 'create_consistency_group', + 'create_share_group', mock.Mock(side_effect=exception.Error)) self.assertRaises(exception.Error, - self.share_manager.create_consistency_group, + self.share_manager.create_share_group, self.context, "fake_id") - self.share_manager.db.consistency_group_update.\ - assert_called_once_with(mock.ANY, 'fake_id', - {'status': constants.STATUS_ERROR}) + self.share_manager.db.share_group_update.assert_called_once_with( + mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR}) - def test_create_consistency_group_from_cgsnapshot(self): - fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', - 'shares': [], 'share_server_id': 'fake_ss_id'} + def test_create_share_group_from_sg_snapshot(self): + fake_group = { + 'id': 'fake_id', + 'source_share_group_snapshot_id': 'fake_snap_id', + 'shares': [], + 'share_server_id': 'fake_ss_id', + } fake_ss = {'id': 'fake_ss_id', 'share_network_id': 'fake_sn'} - fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': [], - 'consistency_group': {'share_server_id': fake_ss['id']}} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'cgsnapshot_get', + fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': [], + 'share_group': {'share_server_id': fake_ss['id']}} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_snapshot_get', mock.Mock(return_value=fake_snap)) self.mock_object(self.share_manager.db, 'share_server_get', mock.Mock( return_value=fake_ss)) - self.mock_object(self.share_manager.db, 'consistency_group_update', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.driver, - 'create_consistency_group_from_cgsnapshot', - mock.Mock(return_value=(None, None))) + self.mock_object(self.share_manager.db, 'share_group_update', + mock.Mock(return_value=fake_group)) + mock_create_sg_from_sg_snap = self.mock_object( + self.share_manager.driver, + 'create_share_group_from_share_group_snapshot', + mock.Mock(return_value=(None, None))) - self.share_manager.create_consistency_group(self.context, "fake_id") + self.share_manager.create_share_group(self.context, "fake_id") - self.share_manager.db.consistency_group_update.\ - assert_called_once_with(mock.ANY, 'fake_id', - {'status': constants.STATUS_AVAILABLE, - 'created_at': mock.ANY}) + self.share_manager.db.share_group_update.assert_called_once_with( + mock.ANY, 'fake_id', + {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) self.share_manager.db.share_server_get(mock.ANY, 'fake_ss_id') - self.share_manager.driver.create_consistency_group_from_cgsnapshot.\ - assert_called_once_with( - mock.ANY, fake_cg, fake_snap, share_server=fake_ss) + mock_create_sg_from_sg_snap.assert_called_once_with( + mock.ANY, fake_group, fake_snap, share_server=fake_ss) - def test_create_cg_cgsnapshot_share_network_driver_not_handles_servers( - self): + def test_create_sg_snapshot_share_network_driver_not_handles_servers(self): manager.CONF.set_default('driver_handles_share_servers', False) self.mock_object( self.share_manager.driver.configuration, 'safe_get', mock.Mock(return_value=False)) - cg_id = 'fake_cg_id' + sg_id = 'fake_share_group_id' share_network_id = 'fake_sn' - fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', - 'shares': [], 'share_network_id': share_network_id, - 'host': "fake_host"} + fake_group = { + 'id': 'fake_id', + 'source_share_group_snapshot_id': 'fake_snap_id', + 'shares': [], + 'share_network_id': share_network_id, + 'host': "fake_host", + } self.mock_object( - self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} - self.mock_object(self.share_manager.db, 'cgsnapshot_get', + self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []} + self.mock_object(self.share_manager.db, 'share_group_snapshot_get', mock.Mock(return_value=fake_snap)) - self.mock_object(self.share_manager.db, 'consistency_group_update') + self.mock_object(self.share_manager.db, 'share_group_update') self.assertRaises(exception.ManilaException, - self.share_manager.create_consistency_group, - self.context, cg_id) + self.share_manager.create_share_group, + self.context, sg_id) - self.share_manager.db.consistency_group_get.assert_called_once_with( - utils.IsAMatcher(context.RequestContext), cg_id) - self.share_manager.db.consistency_group_update.assert_called_once_with( - utils.IsAMatcher(context.RequestContext), cg_id, + self.share_manager.db.share_group_get.assert_called_once_with( + utils.IsAMatcher(context.RequestContext), sg_id) + self.share_manager.db.share_group_update.assert_called_once_with( + utils.IsAMatcher(context.RequestContext), sg_id, {'status': constants.STATUS_ERROR}) - def test_create_cg_from_cgsnapshot_share_network_driver_handles_servers( - self): + def test_create_share_group_from_sg_snapshot_share_network_dhss(self): manager.CONF.set_default('driver_handles_share_servers', True) self.mock_object(self.share_manager.driver.configuration, 'safe_get', mock.Mock(return_value=True)) share_network_id = 'fake_sn' - fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', - 'shares': [], 'share_network_id': share_network_id} - fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'cgsnapshot_get', + fake_group = { + 'id': 'fake_id', + 'source_share_group_snapshot_id': 'fake_snap_id', + 'shares': [], + 'share_network_id': share_network_id, + } + fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_snapshot_get', mock.Mock(return_value=fake_snap)) - self.mock_object(self.share_manager.db, 'consistency_group_update', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager, '_provide_share_server_for_cg', - mock.Mock(return_value=({}, fake_cg))) - self.mock_object(self.share_manager.driver, - 'create_consistency_group_from_cgsnapshot', - mock.Mock(return_value=(None, None))) + self.mock_object(self.share_manager.db, 'share_group_update', + mock.Mock(return_value=fake_group)) + self.mock_object( + self.share_manager, '_provide_share_server_for_share_group', + mock.Mock(return_value=({}, fake_group))) + self.mock_object( + self.share_manager.driver, + 'create_share_group_from_share_group_snapshot', + mock.Mock(return_value=(None, None))) - self.share_manager.create_consistency_group(self.context, "fake_id") + self.share_manager.create_share_group(self.context, "fake_id") - self.share_manager.db.consistency_group_update.\ - assert_called_once_with(mock.ANY, 'fake_id', - {'status': constants.STATUS_AVAILABLE, - 'created_at': mock.ANY}) + self.share_manager.db.share_group_update.assert_called_once_with( + mock.ANY, 'fake_id', + {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) - def test_create_consistency_group_from_cgsnapshot_with_update(self): - fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', - 'shares': []} - fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'cgsnapshot_get', + def test_create_share_group_from_share_group_snapshot_with_update(self): + fake_group = { + 'id': 'fake_id', + 'source_share_group_snapshot_id': 'fake_snap_id', + 'shares': [], + } + fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_snapshot_get', mock.Mock(return_value=fake_snap)) - self.mock_object(self.share_manager.db, 'consistency_group_update', - mock.Mock(return_value=fake_cg)) + self.mock_object(self.share_manager.db, 'share_group_update', + mock.Mock(return_value=fake_group)) self.mock_object(self.share_manager.driver, - 'create_consistency_group_from_cgsnapshot', + 'create_share_group_from_share_group_snapshot', mock.Mock(return_value=({'foo': 'bar'}, None))) - self.share_manager.create_consistency_group(self.context, "fake_id") + self.share_manager.create_share_group(self.context, "fake_id") - self.share_manager.db.consistency_group_update.\ - assert_any_call(mock.ANY, 'fake_id', {'foo': 'bar'}) - self.share_manager.db.consistency_group_update.\ - assert_any_call(mock.ANY, 'fake_id', - {'status': constants.STATUS_AVAILABLE, - 'created_at': mock.ANY}) + self.share_manager.db.share_group_update.assert_any_call( + mock.ANY, 'fake_id', {'foo': 'bar'}) + self.share_manager.db.share_group_update.assert_any_call( + mock.ANY, 'fake_id', + {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) - def test_create_consistency_group_from_cgsnapshot_with_share_update(self): + def test_create_share_group_from_sg_snapshot_with_share_update(self): fake_share = {'id': 'fake_share_id'} fake_export_locations = ['my_export_location'] - fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', - 'shares': [fake_share]} - fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'cgsnapshot_get', + fake_group = { + 'id': 'fake_id', + 'source_share_group_snapshot_id': 'fake_snap_id', + 'shares': [fake_share], + } + fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_snapshot_get', mock.Mock(return_value=fake_snap)) - self.mock_object(self.share_manager.db, 'consistency_group_update') + self.mock_object(self.share_manager.db, 'share_group_update') self.mock_object(self.share_manager.db, 'share_instance_update') self.mock_object(self.share_manager.db, 'share_export_locations_update') @@ -3374,216 +3395,227 @@ class ShareManagerTestCase(test.TestCase): 'foo': 'bar', 'export_locations': fake_export_locations}] self.mock_object(self.share_manager.driver, - 'create_consistency_group_from_cgsnapshot', + 'create_share_group_from_share_group_snapshot', mock.Mock( return_value=(None, fake_share_update_list))) - self.share_manager.create_consistency_group(self.context, "fake_id") + self.share_manager.create_share_group(self.context, "fake_id") - self.share_manager.db.share_instance_update.\ - assert_any_call(mock.ANY, 'fake_share_id', {'foo': 'bar'}) - self.share_manager.db.share_export_locations_update.\ - assert_any_call(mock.ANY, 'fake_share_id', fake_export_locations) - self.share_manager.db.consistency_group_update.\ - assert_any_call(mock.ANY, 'fake_id', - {'status': constants.STATUS_AVAILABLE, - 'created_at': mock.ANY}) + self.share_manager.db.share_instance_update.assert_any_call( + mock.ANY, 'fake_share_id', {'foo': 'bar'}) + self.share_manager.db.share_export_locations_update.assert_any_call( + mock.ANY, 'fake_share_id', fake_export_locations) + self.share_manager.db.share_group_update.assert_any_call( + mock.ANY, 'fake_id', + {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) - def test_create_consistency_group_from_cgsnapshot_with_error(self): - fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', - 'shares': []} - fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'cgsnapshot_get', + def test_create_share_group_from_sg_snapshot_with_error(self): + fake_group = { + 'id': 'fake_id', + 'source_share_group_snapshot_id': 'fake_snap_id', + 'shares': [], + } + fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_snapshot_get', mock.Mock(return_value=fake_snap)) self.mock_object(self.share_manager.db, - 'share_instances_get_all_by_consistency_group_id', + 'share_instances_get_all_by_share_group_id', mock.Mock(return_value=[])) - self.mock_object(self.share_manager.db, 'consistency_group_update', - mock.Mock(return_value=fake_cg)) + self.mock_object(self.share_manager.db, 'share_group_update', + mock.Mock(return_value=fake_group)) self.mock_object(self.share_manager.driver, - 'create_consistency_group_from_cgsnapshot', + 'create_share_group_from_share_group_snapshot', mock.Mock(side_effect=exception.Error)) self.assertRaises(exception.Error, - self.share_manager.create_consistency_group, + self.share_manager.create_share_group, self.context, "fake_id") - self.share_manager.db.consistency_group_update.\ - assert_called_once_with(mock.ANY, 'fake_id', - {'status': constants.STATUS_ERROR}) + self.share_manager.db.share_group_update.assert_called_once_with( + mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR}) - def test_create_consistency_group_from_cgsnapshot_with_share_error(self): + def test_create_share_group_from_sg_snapshot_with_share_error(self): fake_share = {'id': 'fake_share_id'} - fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', - 'shares': [fake_share]} - fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'cgsnapshot_get', + fake_group = { + 'id': 'fake_id', + 'source_share_group_snapshot_id': 'fake_snap_id', + 'shares': [fake_share], + } + fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_snapshot_get', mock.Mock(return_value=fake_snap)) self.mock_object(self.share_manager.db, - 'share_instances_get_all_by_consistency_group_id', + 'share_instances_get_all_by_share_group_id', mock.Mock(return_value=[fake_share])) - self.mock_object(self.share_manager.db, 'consistency_group_update') + self.mock_object(self.share_manager.db, 'share_group_update') self.mock_object(self.share_manager.db, 'share_instance_update') self.mock_object(self.share_manager.driver, - 'create_consistency_group_from_cgsnapshot', + 'create_share_group_from_share_group_snapshot', mock.Mock(side_effect=exception.Error)) self.assertRaises(exception.Error, - self.share_manager.create_consistency_group, + self.share_manager.create_share_group, self.context, "fake_id") - self.share_manager.db.share_instance_update.\ - assert_any_call(mock.ANY, 'fake_share_id', - {'status': constants.STATUS_ERROR}) - self.share_manager.db.consistency_group_update.\ - assert_called_once_with(mock.ANY, 'fake_id', - {'status': constants.STATUS_ERROR}) + self.share_manager.db.share_instance_update.assert_any_call( + mock.ANY, 'fake_share_id', {'status': constants.STATUS_ERROR}) + self.share_manager.db.share_group_update.assert_called_once_with( + mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR}) - def test_delete_consistency_group(self): - fake_cg = {'id': 'fake_id'} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'consistency_group_update', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'consistency_group_destroy', - mock.Mock(return_value=fake_cg)) + def test_delete_share_group(self): + fake_group = {'id': 'fake_id'} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_update', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_destroy', + mock.Mock(return_value=fake_group)) self.mock_object(self.share_manager.driver, - 'delete_consistency_group', + 'delete_share_group', mock.Mock(return_value=None)) - self.share_manager.delete_consistency_group(self.context, "fake_id") + self.share_manager.delete_share_group(self.context, "fake_id") - self.share_manager.db.consistency_group_destroy.\ - assert_called_once_with(mock.ANY, 'fake_id') + self.share_manager.db.share_group_destroy.assert_called_once_with( + mock.ANY, 'fake_id') - def test_delete_consistency_group_with_update(self): - fake_cg = {'id': 'fake_id'} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'consistency_group_update', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'consistency_group_destroy', - mock.Mock(return_value=fake_cg)) + def test_delete_share_group_with_update(self): + fake_group = {'id': 'fake_id'} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_update', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_destroy', + mock.Mock(return_value=fake_group)) self.mock_object(self.share_manager.driver, - 'delete_consistency_group', + 'delete_share_group', mock.Mock(return_value={'foo': 'bar'})) - self.share_manager.delete_consistency_group(self.context, "fake_id") + self.share_manager.delete_share_group(self.context, "fake_id") - self.share_manager.db.consistency_group_update.\ - assert_called_once_with(mock.ANY, 'fake_id', {'foo': 'bar'}) - self.share_manager.db.consistency_group_destroy.\ - assert_called_once_with(mock.ANY, 'fake_id') + self.share_manager.db.share_group_update.assert_called_once_with( + mock.ANY, 'fake_id', {'foo': 'bar'}) + self.share_manager.db.share_group_destroy.assert_called_once_with( + mock.ANY, 'fake_id') - def test_delete_consistency_group_with_error(self): - fake_cg = {'id': 'fake_id'} - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=fake_cg)) - self.mock_object(self.share_manager.db, 'consistency_group_update', - mock.Mock(return_value=fake_cg)) + def test_delete_share_group_with_error(self): + fake_group = {'id': 'fake_id'} + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=fake_group)) + self.mock_object(self.share_manager.db, 'share_group_update', + mock.Mock(return_value=fake_group)) self.mock_object(self.share_manager.driver, - 'delete_consistency_group', + 'delete_share_group', mock.Mock(side_effect=exception.Error)) self.assertRaises(exception.Error, - self.share_manager.delete_consistency_group, + self.share_manager.delete_share_group, self.context, "fake_id") - self.share_manager.db.consistency_group_update.\ - assert_called_once_with(mock.ANY, 'fake_id', - {'status': constants.STATUS_ERROR}) + self.share_manager.db.share_group_update.assert_called_once_with( + mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR}) - def test_create_cgsnapshot(self): - fake_snap = {'id': 'fake_snap_id', 'consistency_group': {}, - 'cgsnapshot_members': []} - self.mock_object(self.share_manager.db, 'cgsnapshot_get', - mock.Mock(return_value=fake_snap)) - self.mock_object(self.share_manager.db, 'cgsnapshot_update', - mock.Mock(return_value=fake_snap)) - self.mock_object(self.share_manager.driver, - 'create_cgsnapshot', - mock.Mock(return_value=(None, None))) + def test_create_share_group_snapshot(self): + fake_snap = { + 'id': 'fake_snap_id', + 'share_group': {}, + 'share_group_snapshot_members': [], + } + self.mock_object( + self.share_manager.db, 'share_group_snapshot_get', + mock.Mock(return_value=fake_snap)) + mock_sg_snap_update = self.mock_object( + self.share_manager.db, 'share_group_snapshot_update', + mock.Mock(return_value=fake_snap)) + self.mock_object( + self.share_manager.driver, + 'create_share_group_snapshot', + mock.Mock(return_value=(None, None))) - self.share_manager.create_cgsnapshot(self.context, fake_snap['id']) + self.share_manager.create_share_group_snapshot( + self.context, fake_snap['id']) - self.share_manager.db.cgsnapshot_update.\ - assert_called_once_with(mock.ANY, fake_snap['id'], - {'status': constants.STATUS_AVAILABLE, - 'created_at': mock.ANY}) - - def test_create_cgsnapshot_with_update(self): - fake_snap = {'id': 'fake_snap_id', 'consistency_group': {}, - 'cgsnapshot_members': []} - self.mock_object(self.share_manager.db, 'cgsnapshot_get', - mock.Mock(return_value=fake_snap)) - self.mock_object(self.share_manager.db, 'cgsnapshot_update', - mock.Mock(return_value=fake_snap)) - self.mock_object(self.share_manager.driver, - 'create_cgsnapshot', - mock.Mock(return_value=({'foo': 'bar'}, None))) - - self.share_manager.create_cgsnapshot(self.context, fake_snap['id']) - - self.share_manager.db.cgsnapshot_update.\ - assert_any_call(mock.ANY, 'fake_snap_id', {'foo': 'bar'}) - self.share_manager.db.cgsnapshot_update.assert_any_call( + mock_sg_snap_update.assert_called_once_with( mock.ANY, fake_snap['id'], {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) - def test_create_cgsnapshot_with_member_update(self): - fake_member = { - 'id': 'fake_member_id', - 'share_instance_id': 'blah', - } - fake_member_update = { - 'id': 'fake_member_id', - 'foo': 'bar' - } - fake_snap = {'id': 'fake_snap_id', 'consistency_group': {}, - 'cgsnapshot_members': [fake_member]} - self.mock_object(self.share_manager.db, 'cgsnapshot_get', + def test_create_share_group_snapshot_with_update(self): + fake_snap = {'id': 'fake_snap_id', 'share_group': {}, + 'share_group_snapshot_members': []} + self.mock_object(self.share_manager.db, 'share_group_snapshot_get', mock.Mock(return_value=fake_snap)) - self.mock_object(self.share_manager.db, 'cgsnapshot_update', - mock.Mock(return_value=fake_snap)) - self.mock_object(self.share_manager.db, 'cgsnapshot_member_update') - self.mock_object(self.share_manager.db, 'share_instance_get', - mock.Mock(return_value={'id': 'blah'})) - self.mock_object(self.share_manager.driver, 'create_cgsnapshot', - mock.Mock(return_value=(None, [fake_member_update]))) - - self.share_manager.create_cgsnapshot(self.context, fake_snap['id']) - - self.share_manager.db.cgsnapshot_update.assert_any_call( - mock.ANY, fake_snap['id'], - {'cgsnapshot_members': [fake_member_update]}) - self.share_manager.db.cgsnapshot_update.\ - assert_any_call(mock.ANY, fake_snap['id'], - {'status': constants.STATUS_AVAILABLE, - 'created_at': mock.ANY}) - self.assertTrue(self.share_manager.db.cgsnapshot_member_update.called) - - def test_create_cgsnapshot_with_error(self): - fake_snap = {'id': 'fake_snap_id', 'consistency_group': {}, - 'cgsnapshot_members': []} - self.mock_object(self.share_manager.db, 'cgsnapshot_get', - mock.Mock(return_value=fake_snap)) - self.mock_object(self.share_manager.db, 'cgsnapshot_update', + self.mock_object(self.share_manager.db, 'share_group_snapshot_update', mock.Mock(return_value=fake_snap)) self.mock_object(self.share_manager.driver, - 'create_cgsnapshot', - mock.Mock(side_effect=exception.Error)) + 'create_share_group_snapshot', + mock.Mock(return_value=({'foo': 'bar'}, None))) - self.assertRaises(exception.Error, - self.share_manager.create_cgsnapshot, - self.context, fake_snap['id']) + self.share_manager.create_share_group_snapshot( + self.context, fake_snap['id']) - self.share_manager.db.cgsnapshot_update.\ - assert_called_once_with(mock.ANY, fake_snap['id'], - {'status': constants.STATUS_ERROR}) + self.share_manager.db.share_group_snapshot_update.assert_any_call( + mock.ANY, 'fake_snap_id', {'foo': 'bar'}) + self.share_manager.db.share_group_snapshot_update.assert_any_call( + mock.ANY, fake_snap['id'], + {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) + + def test_create_share_group_snapshot_with_member_update(self): + fake_member = {'id': 'fake_member_id', 'share_instance_id': 'blah'} + fake_member_update = {'id': 'fake_member_id', 'foo': 'bar'} + fake_snap = {'id': 'fake_snap_id', 'share_group': {}, + 'share_group_snapshot_members': [fake_member]} + self.mock_object( + self.share_manager.db, 'share_group_snapshot_get', + mock.Mock(return_value=fake_snap)) + self.mock_object( + self.share_manager.db, 'share_group_snapshot_update', + mock.Mock(return_value=fake_snap)) + self.mock_object( + self.share_manager.db, 'share_group_snapshot_member_update') + self.mock_object( + self.share_manager.db, 'share_instance_get', + mock.Mock(return_value={'id': 'blah'})) + self.mock_object( + self.share_manager.driver, 'create_share_group_snapshot', + mock.Mock(return_value=(None, [fake_member_update]))) + + self.share_manager.create_share_group_snapshot( + self.context, fake_snap['id']) + + self.share_manager.db.share_group_snapshot_update.assert_any_call( + mock.ANY, fake_snap['id'], + {'share_group_snapshot_members': [fake_member_update]}) + self.share_manager.db.share_group_snapshot_update.assert_any_call( + mock.ANY, fake_snap['id'], + {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) + self.assertTrue( + self.share_manager.db.share_group_snapshot_member_update.called) + + def test_create_group_snapshot_with_error(self): + fake_snap = {'id': 'fake_snap_id', 'share_group': {}, + 'share_group_snapshot_members': []} + self.mock_object( + self.share_manager.db, 'share_group_snapshot_get', + mock.Mock(return_value=fake_snap)) + mock_sg_snap_update = self.mock_object( + self.share_manager.db, 'share_group_snapshot_update', + mock.Mock(return_value=fake_snap)) + self.mock_object( + self.share_manager.driver, + 'create_share_group_snapshot', + mock.Mock(side_effect=exception.Error)) + + self.assertRaises( + exception.Error, + self.share_manager.create_share_group_snapshot, + self.context, fake_snap['id']) + + mock_sg_snap_update.assert_called_once_with( + mock.ANY, fake_snap['id'], {'status': constants.STATUS_ERROR}) def test_connection_get_info(self): share_instance = {'share_server_id': 'fake_server_id'} @@ -4857,9 +4889,9 @@ class ShareManagerTestCase(test.TestCase): def test_provide_share_server(self): instance = db_utils.create_share_instance(share_id='fake_id', - consistency_group_id='cg_id') + share_group_id='sg_id') snapshot = db_utils.create_snapshot(with_share=True) - cg = db_utils.create_consistency_group() + group = db_utils.create_share_group() server = db_utils.create_share_server() # mocks @@ -4867,8 +4899,8 @@ class ShareManagerTestCase(test.TestCase): mock.Mock(return_value=instance)) self.mock_object(self.share_manager.db, 'share_snapshot_get', mock.Mock(return_value=snapshot)) - self.mock_object(self.share_manager.db, 'consistency_group_get', - mock.Mock(return_value=cg)) + self.mock_object(self.share_manager.db, 'share_group_get', + mock.Mock(return_value=group)) self.mock_object(self.share_manager, '_provide_share_server_for_share', mock.Mock(return_value=(server, instance))) @@ -4882,11 +4914,11 @@ class ShareManagerTestCase(test.TestCase): self.context, 'ins_id', with_share_data=True) self.share_manager.db.share_snapshot_get.assert_called_once_with( self.context, 'snap_id') - self.share_manager.db.consistency_group_get.assert_called_once_with( - self.context, 'cg_id') + self.share_manager.db.share_group_get.assert_called_once_with( + self.context, 'sg_id') (self.share_manager._provide_share_server_for_share. assert_called_once_with(self.context, 'net_id', instance, snapshot, - cg, create_on_backend=False)) + group, create_on_backend=False)) def test_create_share_server(self): diff --git a/manila/tests/share/test_rpcapi.py b/manila/tests/share/test_rpcapi.py index ed03d4fa5d..7ae52e8489 100644 --- a/manila/tests/share/test_rpcapi.py +++ b/manila/tests/share/test_rpcapi.py @@ -46,8 +46,8 @@ class ShareRpcAPITestCase(test.TestCase): host='fake_host', ) share_server = db_utils.create_share_server() - cg = {'id': 'fake_cg_id', 'host': 'fake_host'} - cgsnapshot = {'id': 'fake_cg_id'} + share_group = {'id': 'fake_share_group_id', 'host': 'fake_host'} + share_group_snapshot = {'id': 'fake_share_group_id'} host = 'fake_host' self.fake_share = jsonutils.to_primitive(share) # mock out the getattr on the share db model object since jsonutils @@ -56,8 +56,9 @@ class ShareRpcAPITestCase(test.TestCase): self.fake_share_replica = jsonutils.to_primitive(share_replica) self.fake_snapshot = jsonutils.to_primitive(snapshot) self.fake_share_server = jsonutils.to_primitive(share_server) - self.fake_cg = jsonutils.to_primitive(cg) - self.fake_cgsnapshot = jsonutils.to_primitive(cgsnapshot) + self.fake_share_group = jsonutils.to_primitive(share_group) + self.fake_share_group_snapshot = jsonutils.to_primitive( + share_group_snapshot) self.fake_host = jsonutils.to_primitive(host) self.ctxt = context.RequestContext('fake_user', 'fake_project') self.rpcapi = share_rpcapi.ShareAPI() @@ -79,14 +80,14 @@ class ShareRpcAPITestCase(test.TestCase): if 'share_instance' in expected_msg: share_instance = expected_msg.pop('share_instance', None) expected_msg['share_instance_id'] = share_instance['id'] - if 'cg' in expected_msg: - cg = expected_msg['cg'] - del expected_msg['cg'] - expected_msg['cg_id'] = cg['id'] - if 'cgsnapshot' in expected_msg: - snap = expected_msg['cgsnapshot'] - del expected_msg['cgsnapshot'] - expected_msg['cgsnapshot_id'] = snap['id'] + if 'share_group' in expected_msg: + share_group = expected_msg['share_group'] + del expected_msg['share_group'] + expected_msg['share_group_id'] = share_group['id'] + if 'share_group_snapshot' in expected_msg: + snap = expected_msg['share_group_snapshot'] + del expected_msg['share_group_snapshot'] + expected_msg['share_group_snapshot_id'] = snap['id'] if 'host' in expected_msg: del expected_msg['host'] if 'snapshot' in expected_msg: @@ -113,8 +114,8 @@ class ShareRpcAPITestCase(test.TestCase): if 'host' in kwargs: host = kwargs['host'] - elif 'cg' in kwargs: - host = kwargs['cg']['host'] + elif 'share_group' in kwargs: + host = kwargs['share_group']['host'] elif 'share_instance' in kwargs: host = kwargs['share_instance']['host'] elif 'share_server' in kwargs: @@ -213,32 +214,34 @@ class ShareRpcAPITestCase(test.TestCase): share=self.fake_share, new_size=123) - def test_create_consistency_group(self): - self._test_share_api('create_consistency_group', - version='1.5', + def test_create_share_group(self): + self._test_share_api('create_share_group', + version='1.16', rpc_method='cast', - cg=self.fake_cg, + share_group=self.fake_share_group, host='fake_host1') - def test_delete_consistency_group(self): - self._test_share_api('delete_consistency_group', - version='1.5', + def test_delete_share_group(self): + self._test_share_api('delete_share_group', + version='1.16', rpc_method='cast', - cg=self.fake_cg) + share_group=self.fake_share_group) - def test_create_cgsnapshot(self): - self._test_share_api('create_cgsnapshot', - version='1.5', - rpc_method='cast', - cgsnapshot=self.fake_cgsnapshot, - host='fake_host1') + def test_create_share_group_snapshot(self): + self._test_share_api( + 'create_share_group_snapshot', + version='1.16', + rpc_method='cast', + share_group_snapshot=self.fake_share_group_snapshot, + host='fake_host1') - def test_delete_cgsnapshot(self): - self._test_share_api('delete_cgsnapshot', - version='1.5', - rpc_method='cast', - cgsnapshot=self.fake_cgsnapshot, - host='fake_host1') + def test_delete_share_group_snapshot(self): + self._test_share_api( + 'delete_share_group_snapshot', + version='1.16', + rpc_method='cast', + share_group_snapshot=self.fake_share_group_snapshot, + host='fake_host1') def test_migration_start(self): self._test_share_api('migration_start', diff --git a/manila/tests/share_group/test_api.py b/manila/tests/share_group/test_api.py index 24cf5899c0..0f237cefe0 100644 --- a/manila/tests/share_group/test_api.py +++ b/manila/tests/share_group/test_api.py @@ -1,4 +1,4 @@ -# Copyright 2015 Alex Meade +# Copyright 2016 Alex Meade # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -27,15 +27,15 @@ from manila import context from manila import db as db_driver from manila import exception from manila.share import share_types -import manila.share_group.api as sg_api +import manila.share_group.api as share_group_api from manila import test from manila.tests.api.contrib import stubs CONF = cfg.CONF -def fake_cg(id, **kwargs): - cg = { +def fake_share_group(id, **kwargs): + share_group = { 'id': id, 'user_id': 'fakeuser', 'project_id': 'fakeproject', @@ -43,22 +43,23 @@ def fake_cg(id, **kwargs): 'name': None, 'description': None, 'host': None, - 'source_cgsnapshot_id': None, + 'share_group_type_id': None, + 'source_share_group_snapshot_id': None, 'share_network_id': None, 'share_server_id': None, - 'share_types': None, + 'share_types': mock.ANY, 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), } - if 'source_cgsnapshot_id' in kwargs: - cg['share_network_id'] = 'fake_share_network_id' - cg['share_server_id'] = 'fake_share_server_id' + if 'source_share_group_snapshot_id' in kwargs: + share_group['share_network_id'] = 'fake_share_network_id' + share_group['share_server_id'] = 'fake_share_server_id' - cg.update(kwargs) - return cg + share_group.update(kwargs) + return share_group -def fake_cgsnapshot(id, **kwargs): +def fake_share_group_snapshot(id, **kwargs): snap = { 'id': id, 'user_id': 'fakeuser', @@ -66,7 +67,7 @@ def fake_cgsnapshot(id, **kwargs): 'status': constants.STATUS_CREATING, 'name': None, 'description': None, - 'consistency_group_id': None, + 'share_group_id': None, 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), } snap.update(kwargs) @@ -74,174 +75,166 @@ def fake_cgsnapshot(id, **kwargs): @ddt.ddt -class CGAPITestCase(test.TestCase): - +class ShareGroupsAPITestCase(test.TestCase): def setUp(self): - super(CGAPITestCase, self).setUp() + super(ShareGroupsAPITestCase, self).setUp() self.context = context.get_admin_context() self.scheduler_rpcapi = mock.Mock() self.share_rpcapi = mock.Mock() self.share_api = mock.Mock() - self.api = sg_api.API() + self.api = share_group_api.API() self.mock_object(self.api, 'share_rpcapi', self.share_rpcapi) self.mock_object(self.api, 'share_api', self.share_api) self.mock_object(self.api, 'scheduler_rpcapi', self.scheduler_rpcapi) dt_utc = datetime.datetime.utcnow() self.mock_object(timeutils, 'utcnow', mock.Mock(return_value=dt_utc)) + self.fake_share_type = { + 'name': 'default', + 'extra_specs': {'driver_handles_share_servers': 'False'}, + 'is_public': True, + 'id': 'c01990c1-448f-435a-9de6-c7c894bb6df9' + } + self.fake_share_type_2 = { + 'name': 'default2', + 'extra_specs': {'driver_handles_share_servers': 'False'}, + 'is_public': True, + 'id': 'c01990c1-448f-435a-9de6-c7c894bb7dfd' + } + self.fake_share_group_type = { + 'share_types': [ + {'share_type_id': self.fake_share_type['id']}, + {'share_type_id': self.fake_share_type_2['id']}, + ] + } + self.mock_object(db_driver, 'share_group_type_get', + mock.Mock(return_value=self.fake_share_group_type)) def test_create_empty_request(self): - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - - expected_values = cg.copy() + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + expected_values = share_group.copy() for name in ('id', 'host', 'created_at'): expected_values.pop(name, None) + self.mock_object(db_driver, 'share_group_create', + mock.Mock(return_value=share_group)) - self.mock_object(db_driver, 'consistency_group_create', - mock.Mock(return_value=cg)) self.api.create(self.context) - db_driver.consistency_group_create.assert_called_once_with( + db_driver.share_group_create.assert_called_once_with( self.context, expected_values) def test_create_request_spec(self): """Ensure the correct values are sent to the scheduler.""" - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = cg.copy() + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + expected_values = share_group.copy() for name in ('id', 'host', 'created_at'): expected_values.pop(name, None) - expected_request_spec = { - 'consistency_group_id': cg['id'], - } - expected_request_spec.update(cg) + expected_request_spec = {'share_group_id': share_group['id']} + expected_request_spec.update(share_group) del expected_request_spec['id'] del expected_request_spec['created_at'] del expected_request_spec['host'] - expected_request_spec['share_types'] = [] - - self.mock_object(db_driver, 'consistency_group_create', - mock.Mock(return_value=cg)) + expected_request_spec['resource_type'] = self.fake_share_group_type + self.mock_object(db_driver, 'share_group_create', + mock.Mock(return_value=share_group)) self.api.create(self.context) - self.scheduler_rpcapi.create_consistency_group.assert_called_once_with( - self.context, cg_id=cg['id'], request_spec=expected_request_spec, - filter_properties={} - ) + self.scheduler_rpcapi.create_share_group.assert_called_once_with( + self.context, share_group_id=share_group['id'], + request_spec=expected_request_spec, filter_properties={}) def test_create_with_name(self): fake_name = 'fake_name' - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = cg.copy() + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + expected_values = share_group.copy() for name in ('id', 'host', 'created_at'): expected_values.pop(name, None) expected_values['name'] = fake_name - - self.mock_object(db_driver, 'consistency_group_create', - mock.Mock(return_value=cg)) + self.mock_object(db_driver, 'share_group_create', + mock.Mock(return_value=share_group)) self.mock_object(db_driver, 'share_network_get') self.api.create(self.context, name=fake_name) - db_driver.consistency_group_create.assert_called_once_with( + db_driver.share_group_create.assert_called_once_with( self.context, expected_values) - self.scheduler_rpcapi.create_consistency_group.assert_called_once_with( - self.context, cg_id=cg['id'], request_spec=mock.ANY, - filter_properties={} - ) + self.scheduler_rpcapi.create_share_group.assert_called_once_with( + self.context, share_group_id=share_group['id'], + request_spec=mock.ANY, filter_properties={}) def test_create_with_description(self): fake_desc = 'fake_desc' - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = cg.copy() + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + expected_values = share_group.copy() for name in ('id', 'host', 'created_at'): expected_values.pop(name, None) expected_values['description'] = fake_desc - - self.mock_object(db_driver, 'consistency_group_create', - mock.Mock(return_value=cg)) + self.mock_object(db_driver, 'share_group_create', + mock.Mock(return_value=share_group)) self.api.create(self.context, description=fake_desc) - db_driver.consistency_group_create.assert_called_once_with( + db_driver.share_group_create.assert_called_once_with( self.context, expected_values) def test_create_with_multiple_share_types(self): - fake_share_type = {'name': 'default', - 'extra_specs': { - 'driver_handles_share_servers': 'False'}, - 'is_public': True, - 'id': 'c01990c1-448f-435a-9de6-c7c894bb6df9'} - fake_share_type_2 = {'name': 'default2', - 'extra_specs': { - 'driver_handles_share_servers': 'False'}, - 'is_public': True, - 'id': 'c01990c1-448f-435a-9de6-c7c894bb7df9'} - fake_share_types = [fake_share_type, fake_share_type_2] + fake_share_types = [self.fake_share_type, self.fake_share_type_2] + fake_share_type_ids = [x['id'] for x in fake_share_types] self.mock_object(share_types, 'get_share_type') - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = cg.copy() + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + expected_values = share_group.copy() for name in ('id', 'host', 'created_at'): expected_values.pop(name, None) - expected_values['share_types'] = fake_share_types + expected_values['share_types'] = fake_share_type_ids - self.mock_object(db_driver, 'consistency_group_create', - mock.Mock(return_value=cg)) + self.mock_object( + db_driver, 'share_group_create', + mock.Mock(return_value=share_group)) self.mock_object(db_driver, 'share_network_get') - self.api.create(self.context, share_type_ids=fake_share_types) + self.api.create(self.context, share_type_ids=fake_share_type_ids) - db_driver.consistency_group_create.assert_called_once_with( + db_driver.share_group_create.assert_called_once_with( self.context, expected_values) def test_create_with_share_type_not_found(self): - fake_share_type = {'name': 'default', - 'extra_specs': { - 'driver_handles_share_servers': 'False'}, - 'is_public': True, - 'id': 'c01990c1-448f-435a-9de6-c7c894bb6df9'} - fake_share_types = [fake_share_type] self.mock_object(share_types, 'get_share_type', mock.Mock(side_effect=exception.ShareTypeNotFound( - share_type_id=fake_share_type['id']))) - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = cg.copy() + share_type_id=self.fake_share_type['id']))) + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + expected_values = share_group.copy() for name in ('id', 'host', 'created_at'): expected_values.pop(name, None) - expected_values['share_types'] = fake_share_types + expected_values['share_types'] = self.fake_share_type['id'] + self.mock_object(db_driver, 'share_group_create', + mock.Mock(return_value=share_group)) - self.mock_object(db_driver, 'consistency_group_create', - mock.Mock(return_value=cg)) - - self.assertRaises(exception.InvalidInput, self.api.create, - self.context, share_type_ids=[fake_share_type['id']]) + self.assertRaises( + exception.InvalidInput, + self.api.create, + self.context, share_type_ids=[self.fake_share_type['id']]) def test_create_driver_handles_share_servers_is_false_with_net_id(self): - fake_share_type = {'name': 'default', - 'extra_specs': { - 'driver_handles_share_servers': 'False'}, - 'is_public': False, - 'id': 'c01990c1-448f-435a-9de6-c7c894bb6df9'} - - fake_share_types = [fake_share_type] + fake_share_types = [self.fake_share_type] self.mock_object(share_types, 'get_share_type') self.assertRaises(exception.InvalidInput, self.api.create, @@ -249,942 +242,936 @@ class CGAPITestCase(test.TestCase): share_network_id="fake_share_network") def test_create_with_conflicting_share_types(self): - fake_share_type = {'name': 'default', - 'extra_specs': { - 'driver_handles_share_servers': 'True'}, - 'is_public': True, - 'id': 'c01990c1-448f-435a-9de6-c7c894bb6df9'} - fake_share_type_2 = {'name': 'default2', - 'extra_specs': { - 'driver_handles_share_servers': 'False'}, - 'is_public': True, - 'id': 'c01990c1-448f-435a-9de6-c7c894bb7df9'} + fake_share_type = { + 'name': 'default', + 'extra_specs': {'driver_handles_share_servers': 'True'}, + 'is_public': True, + 'id': 'c01990c1-448f-435a-9de6-c7c894bb6df9', + } + fake_share_type_2 = { + 'name': 'default2', + 'extra_specs': {'driver_handles_share_servers': 'False'}, + 'is_public': True, + 'id': 'c01990c1-448f-435a-9de6-c7c894bb7df9', + } fake_share_types = [fake_share_type, fake_share_type_2] + fake_share_type_ids = [x['id'] for x in fake_share_types] self.mock_object(share_types, 'get_share_type', mock.Mock(side_effect=[fake_share_type, fake_share_type_2])) - self.assertRaises(exception.InvalidInput, self.api.create, - self.context, share_type_ids=fake_share_types) + self.assertRaises( + exception.InvalidInput, + self.api.create, + self.context, share_type_ids=fake_share_type_ids) def test_create_with_conflicting_share_type_and_share_network(self): - fake_share_type = {'name': 'default', - 'extra_specs': { - 'driver_handles_share_servers': 'False'}, - 'is_public': True, - 'id': 'c01990c1-448f-435a-9de6-c7c894bb6df9'} + fake_share_type = { + 'name': 'default', + 'extra_specs': {'driver_handles_share_servers': 'False'}, + 'is_public': True, + 'id': 'c01990c1-448f-435a-9de6-c7c894bb6df9', + } fake_share_types = [fake_share_type] self.mock_object(share_types, 'get_share_type', mock.Mock(return_value=fake_share_type)) - self.assertRaises(exception.InvalidInput, self.api.create, - self.context, share_type_ids=fake_share_types, - share_network_id="fake_sn") + self.assertRaises( + exception.InvalidInput, + self.api.create, + self.context, share_type_ids=fake_share_types, + share_network_id="fake_sn") - def test_create_with_source_cgsnapshot_id(self): - snap = fake_cgsnapshot("fake_source_cgsnapshot_id", - status=constants.STATUS_AVAILABLE) - fake_share_type_mapping = {'share_type_id': "fake_share_type_id"} - orig_cg = fake_cg('fakeorigid', - user_id=self.context.user_id, - project_id=self.context.project_id, - share_types=[fake_share_type_mapping], - status=constants.STATUS_AVAILABLE, - host='fake_original_host', - share_network_id='fake_network_id', - share_server_id='fake_server_id') + def test_create_with_source_share_group_snapshot_id(self): + snap = fake_share_group_snapshot( + "fake_source_share_group_snapshot_id", + status=constants.STATUS_AVAILABLE) + fake_share_type_mapping = {'share_type_id': self.fake_share_type['id']} + orig_share_group = fake_share_group( + 'fakeorigid', user_id=self.context.user_id, + project_id=self.context.project_id, + share_types=[fake_share_type_mapping], + status=constants.STATUS_AVAILABLE, + host='fake_original_host', + share_network_id='fake_network_id', + share_server_id='fake_server_id') - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - share_types=[fake_share_type_mapping], - status=constants.STATUS_CREATING, - host='fake_original_host', - share_network_id='fake_network_id', - share_server_id='fake_server_id') - expected_values = cg.copy() + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + share_types=[fake_share_type_mapping], + status=constants.STATUS_CREATING, + host='fake_original_host', + share_network_id='fake_network_id', + share_server_id='fake_server_id') + expected_values = share_group.copy() for name in ('id', 'created_at', 'share_network_id', 'share_server_id'): expected_values.pop(name, None) - expected_values['source_cgsnapshot_id'] = snap['id'] - expected_values['share_types'] = ["fake_share_type_id"] + expected_values['source_share_group_snapshot_id'] = snap['id'] + expected_values['share_types'] = [self.fake_share_type['id']] expected_values['share_network_id'] = 'fake_network_id' expected_values['share_server_id'] = 'fake_server_id' - self.mock_object(db_driver, 'cgsnapshot_get', - mock.Mock(return_value=snap)) - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=orig_cg)) - self.mock_object(db_driver, 'consistency_group_create', - mock.Mock(return_value=cg)) - self.mock_object(share_types, 'get_share_type', - mock.Mock(return_value={"id": "fake_share_type_id"})) + self.mock_object( + db_driver, 'share_group_snapshot_get', + mock.Mock(return_value=snap)) + self.mock_object( + db_driver, 'share_group_get', + mock.Mock(return_value=orig_share_group)) + self.mock_object( + db_driver, 'share_group_create', + mock.Mock(return_value=share_group)) + self.mock_object( + db_driver, 'share_get', + mock.Mock(return_value=stubs.stub_share('fake_share'))) + self.mock_object( + share_types, 'get_share_type', + mock.Mock(return_value={"id": self.fake_share_type['id']})) self.mock_object(db_driver, 'share_network_get') - self.mock_object(db_driver, 'cgsnapshot_members_get_all', - mock.Mock(return_value=[])) + self.mock_object( + db_driver, 'share_group_snapshot_members_get_all', + mock.Mock(return_value=[])) - self.api.create(self.context, - source_cgsnapshot_id=snap['id']) + self.api.create( + self.context, source_share_group_snapshot_id=snap['id']) - db_driver.consistency_group_create.assert_called_once_with( + db_driver.share_group_create.assert_called_once_with( self.context, expected_values) - self.share_rpcapi.create_consistency_group.\ - assert_called_once_with(self.context, cg, orig_cg['host']) + self.share_rpcapi.create_share_group.assert_called_once_with( + self.context, share_group, orig_share_group['host']) - def test_create_with_source_cgsnapshot_id_with_member(self): - snap = fake_cgsnapshot("fake_source_cgsnapshot_id", - status=constants.STATUS_AVAILABLE) + def test_create_with_source_share_group_snapshot_id_with_member(self): + snap = fake_share_group_snapshot( + "fake_source_share_group_snapshot_id", + status=constants.STATUS_AVAILABLE) share = stubs.stub_share('fakeshareid') - member = stubs.stub_cgsnapshot_member('fake_member_id') - fake_share_type_mapping = {'share_type_id': "fake_share_type_id"} - orig_cg = fake_cg('fakeorigid', - user_id=self.context.user_id, - project_id=self.context.project_id, - share_types=[fake_share_type_mapping], - status=constants.STATUS_AVAILABLE, - share_network_id='fake_network_id', - share_server_id='fake_server_id') - - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - share_types=[fake_share_type_mapping], - status=constants.STATUS_CREATING, - share_network_id='fake_network_id', - share_server_id='fake_server_id') - expected_values = cg.copy() + member = stubs.stub_share_group_snapshot_member('fake_member_id') + fake_share_type_mapping = {'share_type_id': self.fake_share_type['id']} + orig_share_group = fake_share_group( + 'fakeorigid', user_id=self.context.user_id, + project_id=self.context.project_id, + share_types=[fake_share_type_mapping], + status=constants.STATUS_AVAILABLE, + share_network_id='fake_network_id', + share_server_id='fake_server_id') + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + share_types=[fake_share_type_mapping], + status=constants.STATUS_CREATING, + share_network_id='fake_network_id', + share_server_id='fake_server_id') + expected_values = share_group.copy() for name in ('id', 'created_at', 'fake_network_id', 'fake_share_server_id'): expected_values.pop(name, None) - expected_values['source_cgsnapshot_id'] = snap['id'] - expected_values['share_types'] = ["fake_share_type_id"] + expected_values['source_share_group_snapshot_id'] = snap['id'] + expected_values['share_types'] = [self.fake_share_type['id']] expected_values['share_network_id'] = 'fake_network_id' expected_values['share_server_id'] = 'fake_server_id' - self.mock_object(db_driver, 'cgsnapshot_get', - mock.Mock(return_value=snap)) - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=orig_cg)) - self.mock_object(db_driver, 'consistency_group_create', - mock.Mock(return_value=cg)) - self.mock_object(share_types, 'get_share_type', - mock.Mock(return_value={"id": "fake_share_type_id"})) + self.mock_object( + db_driver, 'share_group_snapshot_get', + mock.Mock(return_value=snap)) + self.mock_object( + db_driver, 'share_group_get', + mock.Mock(return_value=orig_share_group)) + self.mock_object( + db_driver, 'share_group_create', + mock.Mock(return_value=share_group)) + self.mock_object( + db_driver, 'share_get', + mock.Mock(return_value=stubs.stub_share('fakeshare'))) + self.mock_object( + share_types, 'get_share_type', + mock.Mock(return_value={"id": self.fake_share_type['id']})) self.mock_object(db_driver, 'share_network_get') - self.mock_object(db_driver, 'share_instance_get', - mock.Mock(return_value=share)) - self.mock_object(db_driver, 'cgsnapshot_members_get_all', - mock.Mock(return_value=[member])) + self.mock_object( + db_driver, 'share_instance_get', mock.Mock(return_value=share)) + self.mock_object( + db_driver, 'share_group_snapshot_members_get_all', + mock.Mock(return_value=[member])) self.mock_object(self.share_api, 'create') - self.api.create(self.context, - source_cgsnapshot_id=snap['id']) + self.api.create( + self.context, source_share_group_snapshot_id=snap['id']) - db_driver.consistency_group_create.assert_called_once_with( + db_driver.share_group_create.assert_called_once_with( self.context, expected_values) self.assertTrue(self.share_api.create.called) - self.share_rpcapi.create_consistency_group.\ - assert_called_once_with(self.context, cg, orig_cg['host']) + self.share_rpcapi.create_share_group.assert_called_once_with( + self.context, share_group, orig_share_group['host']) - def test_create_with_source_cgsnapshot_id_with_members_error(self): - snap = fake_cgsnapshot("fake_source_cgsnapshot_id", - status=constants.STATUS_AVAILABLE) - member = stubs.stub_cgsnapshot_member('fake_member_id') - member_2 = stubs.stub_cgsnapshot_member('fake_member2_id') + def test_create_with_source_sg_snapshot_id_with_members_error(self): + snap = fake_share_group_snapshot( + "fake_source_share_group_snapshot_id", + status=constants.STATUS_AVAILABLE) + member = stubs.stub_share_group_snapshot_member('fake_member_id') + member_2 = stubs.stub_share_group_snapshot_member('fake_member2_id') share = stubs.stub_share('fakeshareid') - fake_share_type_mapping = {'share_type_id': "fake_share_type_id"} - orig_cg = fake_cg('fakeorigid', - user_id=self.context.user_id, - project_id=self.context.project_id, - share_types=[fake_share_type_mapping], - status=constants.STATUS_AVAILABLE, - share_network_id='fake_network_id', - share_server_id='fake_server_id') - - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - share_types=[fake_share_type_mapping], - status=constants.STATUS_CREATING, - share_network_id='fake_network_id', - share_server_id='fake_server_id') - expected_values = cg.copy() + fake_share_type_mapping = {'share_type_id': self.fake_share_type['id']} + orig_share_group = fake_share_group( + 'fakeorigid', + user_id=self.context.user_id, + project_id=self.context.project_id, + share_types=[fake_share_type_mapping], + status=constants.STATUS_AVAILABLE, + share_network_id='fake_network_id', + share_server_id='fake_server_id') + share_group = fake_share_group( + 'fakeid', + user_id=self.context.user_id, + project_id=self.context.project_id, + share_types=[fake_share_type_mapping], + status=constants.STATUS_CREATING, + share_network_id='fake_network_id', + share_server_id='fake_server_id') + expected_values = share_group.copy() for name in ('id', 'created_at', 'share_network_id', 'share_server_id'): expected_values.pop(name, None) - expected_values['source_cgsnapshot_id'] = snap['id'] - expected_values['share_types'] = ["fake_share_type_id"] + expected_values['source_share_group_snapshot_id'] = snap['id'] + expected_values['share_types'] = [self.fake_share_type['id']] expected_values['share_network_id'] = 'fake_network_id' expected_values['share_server_id'] = 'fake_server_id' - self.mock_object(db_driver, 'cgsnapshot_get', + self.mock_object(db_driver, 'share_group_snapshot_get', mock.Mock(return_value=snap)) - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=orig_cg)) + self.mock_object(db_driver, 'share_group_get', + mock.Mock(return_value=orig_share_group)) self.mock_object(db_driver, 'share_network_get') self.mock_object(db_driver, 'share_instance_get', mock.Mock(return_value=share)) - self.mock_object(db_driver, 'consistency_group_create', - mock.Mock(return_value=cg)) + self.mock_object(db_driver, 'share_group_create', + mock.Mock(return_value=share_group)) + self.mock_object(db_driver, 'share_get', + mock.Mock(return_value=stubs.stub_share('fakeshare'))) self.mock_object(share_types, 'get_share_type', - mock.Mock(return_value={"id": "fake_share_type_id"})) - self.mock_object(db_driver, 'cgsnapshot_members_get_all', + mock.Mock(return_value={ + "id": self.fake_share_type['id']})) + self.mock_object(db_driver, 'share_group_snapshot_members_get_all', mock.Mock(return_value=[member, member_2])) self.mock_object(self.share_api, 'create', mock.Mock(side_effect=[None, exception.Error])) - self.mock_object(db_driver, 'consistency_group_destroy') + self.mock_object(db_driver, 'share_group_destroy') self.assertRaises(exception.Error, self.api.create, self.context, - source_cgsnapshot_id=snap['id']) + source_share_group_snapshot_id=snap['id']) - db_driver.consistency_group_create.assert_called_once_with( + db_driver.share_group_create.assert_called_once_with( self.context, expected_values) self.assertEqual(2, self.share_api.create.call_count) - self.assertEqual(1, db_driver.consistency_group_destroy.call_count) + self.assertEqual(1, db_driver.share_group_destroy.call_count) - def test_create_with_source_cgsnapshot_id_error_snapshot_status(self): - snap = fake_cgsnapshot("fake_source_cgsnapshot_id", - status=constants.STATUS_ERROR) - self.mock_object(db_driver, 'cgsnapshot_get', - mock.Mock(return_value=snap)) + def test_create_with_source_sg_snapshot_id_error_snapshot_status(self): + snap = fake_share_group_snapshot( + "fake_source_share_group_snapshot_id", + status=constants.STATUS_ERROR) + self.mock_object( + db_driver, 'share_group_snapshot_get', + mock.Mock(return_value=snap)) - self.assertRaises(exception.InvalidCGSnapshot, self.api.create, - self.context, source_cgsnapshot_id=snap['id']) + self.assertRaises( + exception.InvalidShareGroupSnapshot, + self.api.create, + self.context, source_share_group_snapshot_id=snap['id']) - def test_create_with_source_cgsnapshot_id_snap_not_found(self): - snap = fake_cgsnapshot("fake_source_cgsnapshot_id", - status=constants.STATUS_ERROR) - self.mock_object(db_driver, 'cgsnapshot_get', - mock.Mock(side_effect=exception.CGSnapshotNotFound( - cgsnapshot_id='fake_source_cgsnapshot_id' - ))) + def test_create_with_source_sg_snapshot_id_snap_not_found(self): + snap = fake_share_group_snapshot( + "fake_source_share_group_snapshot_id", + status=constants.STATUS_ERROR) + self.mock_object( + db_driver, 'share_group_snapshot_get', + mock.Mock(side_effect=exception.ShareGroupSnapshotNotFound( + share_group_snapshot_id='fake_source_sg_snapshot_id'))) - self.assertRaises(exception.CGSnapshotNotFound, self.api.create, - self.context, source_cgsnapshot_id=snap['id']) + self.assertRaises( + exception.ShareGroupSnapshotNotFound, + self.api.create, + self.context, source_share_group_snapshot_id=snap['id']) def test_create_with_multiple_fields(self): fake_desc = 'fake_desc' fake_name = 'fake_name' - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = cg.copy() + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + expected_values = share_group.copy() for name in ('id', 'host', 'created_at'): expected_values.pop(name, None) expected_values['name'] = fake_name expected_values['description'] = fake_desc - - self.mock_object(db_driver, 'consistency_group_create', - mock.Mock(return_value=cg)) + self.mock_object(db_driver, 'share_group_create', + mock.Mock(return_value=share_group)) self.api.create(self.context, name=fake_name, description=fake_desc) - db_driver.consistency_group_create.assert_called_once_with( + db_driver.share_group_create.assert_called_once_with( self.context, expected_values) def test_create_with_error_on_creation(self): - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = cg.copy() + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + expected_values = share_group.copy() for name in ('id', 'host', 'created_at'): expected_values.pop(name, None) - - self.mock_object(db_driver, 'consistency_group_create', + self.mock_object(db_driver, 'share_group_create', mock.Mock(side_effect=exception.Error)) self.assertRaises(exception.Error, self.api.create, self.context) - db_driver.consistency_group_create.assert_called_once_with( + db_driver.share_group_create.assert_called_once_with( self.context, expected_values) def test_delete_creating_no_host(self): - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - self.mock_object(db_driver, 'consistency_group_destroy') + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + self.mock_object(db_driver, 'share_group_destroy') - self.api.delete(self.context, cg) + self.api.delete(self.context, share_group) - db_driver.consistency_group_destroy.assert_called_once_with( - mock.ANY, cg['id']) + db_driver.share_group_destroy.assert_called_once_with( + mock.ANY, share_group['id']) def test_delete_creating_with_host(self): - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING, - host="fake_host") + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING, host="fake_host") - self.assertRaises(exception.InvalidConsistencyGroup, self.api.delete, - self.context, cg) + self.assertRaises( + exception.InvalidShareGroup, + self.api.delete, self.context, share_group) def test_delete_available(self): - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_AVAILABLE, - host="fake_host") - deleted_cg = copy.deepcopy(cg) - deleted_cg['status'] = constants.STATUS_DELETING - self.mock_object(db_driver, 'consistency_group_update', - mock.Mock(return_value=deleted_cg)) - self.mock_object(db_driver, 'count_shares_in_consistency_group', + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_AVAILABLE, host="fake_host") + deleted_share_group = copy.deepcopy(share_group) + deleted_share_group['status'] = constants.STATUS_DELETING + self.mock_object(db_driver, 'share_group_update', + mock.Mock(return_value=deleted_share_group)) + self.mock_object(db_driver, 'count_shares_in_share_group', mock.Mock(return_value=0)) - self.api.delete(self.context, cg) + self.api.delete(self.context, share_group) - db_driver.consistency_group_update.assert_called_once_with( - self.context, cg['id'], {'status': constants.STATUS_DELETING}) - self.share_rpcapi.delete_consistency_group.assert_called_once_with( - self.context, deleted_cg - ) + db_driver.share_group_update.assert_called_once_with( + self.context, share_group['id'], + {'status': constants.STATUS_DELETING}) + self.share_rpcapi.delete_share_group.assert_called_once_with( + self.context, deleted_share_group) def test_delete_error_with_host(self): - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_ERROR, - host="fake_host") - deleted_cg = copy.deepcopy(cg) - deleted_cg['status'] = constants.STATUS_DELETING + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_ERROR, host="fake_host") + deleted_share_group = copy.deepcopy(share_group) + deleted_share_group['status'] = constants.STATUS_DELETING self.mock_object(self.api, 'share_rpcapi') - self.mock_object(db_driver, 'consistency_group_update', - mock.Mock(return_value=deleted_cg)) - self.mock_object(db_driver, 'count_shares_in_consistency_group', + self.mock_object(db_driver, 'share_group_update', + mock.Mock(return_value=deleted_share_group)) + self.mock_object(db_driver, 'count_shares_in_share_group', mock.Mock(return_value=0)) - self.api.delete(self.context, cg) + self.api.delete(self.context, share_group) - db_driver.consistency_group_update.assert_called_once_with( - self.context, cg['id'], {'status': constants.STATUS_DELETING}) - self.api.share_rpcapi.delete_consistency_group.assert_called_once_with( - self.context, deleted_cg - ) + db_driver.share_group_update.assert_called_once_with( + self.context, share_group['id'], + {'status': constants.STATUS_DELETING}) + self.api.share_rpcapi.delete_share_group.assert_called_once_with( + self.context, deleted_share_group) def test_delete_error_without_host(self): - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_ERROR) - self.mock_object(db_driver, 'consistency_group_destroy') + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_ERROR) + self.mock_object(db_driver, 'share_group_destroy') - self.api.delete(self.context, cg) + self.api.delete(self.context, share_group) - db_driver.consistency_group_destroy.assert_called_once_with( - mock.ANY, cg['id']) + db_driver.share_group_destroy.assert_called_once_with( + mock.ANY, share_group['id']) def test_delete_with_shares(self): - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_AVAILABLE, - host="fake_host") - self.mock_object(db_driver, 'count_shares_in_consistency_group', - mock.Mock(return_value=1)) + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_AVAILABLE, host="fake_host") + self.mock_object( + db_driver, 'count_shares_in_share_group', + mock.Mock(return_value=1)) - self.assertRaises(exception.InvalidConsistencyGroup, self.api.delete, - self.context, cg) + self.assertRaises( + exception.InvalidShareGroup, + self.api.delete, self.context, share_group) - def test_delete_with_cgsnapshots(self): - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_AVAILABLE, - host="fake_host") - self.mock_object(db_driver, 'count_cgsnapshots_in_consistency_group', - mock.Mock(return_value=1)) + def test_delete_with_share_group_snapshots(self): + share_group = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_AVAILABLE, host="fake_host") + self.mock_object( + db_driver, 'count_share_group_snapshots_in_share_group', + mock.Mock(return_value=1)) - self.assertRaises(exception.InvalidConsistencyGroup, self.api.delete, - self.context, cg) + self.assertRaises( + exception.InvalidShareGroup, + self.api.delete, self.context, share_group) - def test_update_no_values(self): - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = {} - self.mock_object(db_driver, 'consistency_group_update', - mock.Mock(return_value=cg)) + @ddt.data({}, {"name": "fake_name"}, {"description": "fake_description"}) + def test_update(self, expected_values): + share_group = fake_share_group( + 'fakeid', + user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + self.mock_object( + db_driver, 'share_group_update', + mock.Mock(return_value=share_group)) - self.api.update(self.context, cg, expected_values) + self.api.update(self.context, share_group, expected_values) - db_driver.consistency_group_update.assert_called_once_with( - self.context, cg['id'], expected_values) - - def test_update_with_name(self): - fake_name = 'fake_name' - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = {'description': fake_name} - - self.mock_object(db_driver, 'consistency_group_update', - mock.Mock(return_value=cg)) - - self.api.update(self.context, cg, expected_values) - - db_driver.consistency_group_update.assert_called_once_with( - self.context, cg['id'], expected_values) - - def test_update_with_description(self): - fake_desc = 'fake_desc' - cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = {'description': fake_desc} - - self.mock_object(db_driver, 'consistency_group_update', - mock.Mock(return_value=cg)) - - self.api.update(self.context, cg, expected_values) - - db_driver.consistency_group_update.assert_called_once_with( - self.context, cg['id'], expected_values) + db_driver.share_group_update.assert_called_once_with( + self.context, share_group['id'], expected_values) def test_get(self): - expected_cg = fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=expected_cg)) + expected = fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + self.mock_object( + db_driver, 'share_group_get', mock.Mock(return_value=expected)) - actual_cg = self.api.get(self.context, expected_cg['id']) - self.assertEqual(expected_cg, actual_cg) + actual = self.api.get(self.context, expected['id']) - def test_get_all_no_cgs(self): + self.assertEqual(expected, actual) - self.mock_object(db_driver, 'consistency_group_get_all', - mock.Mock(return_value=[])) + def test_get_all_no_groups(self): + self.mock_object( + db_driver, 'share_group_get_all', mock.Mock(return_value=[])) - actual_cg = self.api.get_all(self.context) - self.assertEqual([], actual_cg) + actual_group = self.api.get_all(self.context) + + self.assertEqual([], actual_group) def test_get_all(self): - expected_cgs = [fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING)] - self.mock_object(db_driver, 'consistency_group_get_all_by_project', - mock.Mock(return_value=expected_cgs)) + expected = [fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING)] + self.mock_object( + db_driver, 'share_group_get_all_by_project', + mock.Mock(return_value=expected)) - actual_cg = self.api.get_all(self.context, detailed=True) - self.assertEqual(expected_cgs, actual_cg) + actual = self.api.get_all(self.context, detailed=True) + + self.assertEqual(expected, actual) def test_get_all_all_tenants_not_admin(self): - cxt = context.RequestContext(user_id=None, - project_id=None, - is_admin=False) - expected_cgs = [fake_cg('fakeid', - user_id=cxt.user_id, - project_id=cxt.project_id, - status=constants.STATUS_CREATING)] - self.mock_object(db_driver, 'consistency_group_get_all_by_project', - mock.Mock(return_value=expected_cgs)) + cxt = context.RequestContext( + user_id=None, project_id=None, is_admin=False) + expected = [fake_share_group( + 'fakeid', user_id=cxt.user_id, project_id=cxt.project_id, + status=constants.STATUS_CREATING)] + self.mock_object(db_driver, 'share_group_get_all_by_project', + mock.Mock(return_value=expected)) - actual_cgs = self.api.get_all(cxt, - search_opts={'all_tenants': True}) - self.assertEqual(expected_cgs, actual_cgs) + actual = self.api.get_all(cxt, search_opts={'all_tenants': True}) + + self.assertEqual(expected, actual) def test_get_all_all_tenants_as_admin(self): - expected_cgs = [fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING)] - self.mock_object(db_driver, 'consistency_group_get_all', - mock.Mock(return_value=expected_cgs)) + expected = [fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING)] + self.mock_object(db_driver, 'share_group_get_all', + mock.Mock(return_value=expected)) - actual_cgs = self.api.get_all(self.context, - search_opts={'all_tenants': True}) - self.assertEqual(expected_cgs, actual_cgs) - db_driver.consistency_group_get_all.assert_called_once_with( - self.context, detailed=True) + actual = self.api.get_all( + self.context, search_opts={'all_tenants': True}) - def test_create_cgsnapshot_minimal_request_no_members(self): - cg = fake_cg('fake_cg_id', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_AVAILABLE) - snap = fake_cgsnapshot('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - consistency_group_id=cg['id'], - status=constants.STATUS_CREATING) + self.assertEqual(expected, actual) + db_driver.share_group_get_all.assert_called_once_with( + self.context, detailed=True, filters={}, + sort_dir=None, sort_key=None) + + def test_create_share_group_snapshot_minimal_request_no_members(self): + share_group = fake_share_group( + 'fake_group_id', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_AVAILABLE) + snap = fake_share_group_snapshot( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + share_group_id=share_group['id'], + status=constants.STATUS_CREATING) expected_values = snap.copy() for name in ('id', 'created_at'): expected_values.pop(name, None) - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=cg)) - self.mock_object(db_driver, 'cgsnapshot_create', - mock.Mock(return_value=snap)) - self.mock_object(db_driver, 'share_get_all_by_consistency_group_id', - mock.Mock(return_value=[])) + self.mock_object( + db_driver, 'share_group_get', mock.Mock(return_value=share_group)) + self.mock_object( + db_driver, 'share_group_snapshot_create', + mock.Mock(return_value=snap)) + self.mock_object( + db_driver, 'share_get_all_by_share_group_id', + mock.Mock(return_value=[])) - self.api.create_cgsnapshot(self.context, consistency_group_id=cg['id']) + self.api.create_share_group_snapshot( + self.context, share_group_id=share_group['id']) - db_driver.consistency_group_get.assert_called_once_with( - self.context, cg['id'] - ) - db_driver.cgsnapshot_create.assert_called_once_with( + db_driver.share_group_get.assert_called_once_with( + self.context, share_group['id']) + db_driver.share_group_snapshot_create.assert_called_once_with( self.context, expected_values) - self.share_rpcapi.create_cgsnapshot.assert_called_once_with( - self.context, snap, cg['host'] - ) + self.share_rpcapi.create_share_group_snapshot.assert_called_once_with( + self.context, snap, share_group['host']) - def test_create_cgsnapshot_minimal_request_no_members_with_name(self): + def test_create_sg_snapshot_minimal_request_no_members_with_name(self): fake_name = 'fake_name' - cg = fake_cg('fake_cg_id', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_AVAILABLE) - snap = fake_cgsnapshot('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - consistency_group_id=cg['id'], - name=fake_name, - status=constants.STATUS_CREATING) + share_group = fake_share_group( + 'fake_group_id', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_AVAILABLE) + snap = fake_share_group_snapshot( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + share_group_id=share_group['id'], name=fake_name, + status=constants.STATUS_CREATING) expected_values = snap.copy() for name in ('id', 'created_at'): expected_values.pop(name, None) - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=cg)) - self.mock_object(db_driver, 'cgsnapshot_create', - mock.Mock(return_value=snap)) - self.mock_object(db_driver, 'share_get_all_by_consistency_group_id', - mock.Mock(return_value=[])) + self.mock_object( + db_driver, 'share_group_get', mock.Mock(return_value=share_group)) + self.mock_object( + db_driver, 'share_group_snapshot_create', + mock.Mock(return_value=snap)) + self.mock_object( + db_driver, 'share_get_all_by_share_group_id', + mock.Mock(return_value=[])) - self.api.create_cgsnapshot(self.context, consistency_group_id=cg['id'], - name=fake_name) + self.api.create_share_group_snapshot( + self.context, share_group_id=share_group['id'], name=fake_name) - db_driver.consistency_group_get.assert_called_once_with( - self.context, cg['id'] - ) - db_driver.cgsnapshot_create.assert_called_once_with( + db_driver.share_group_get.assert_called_once_with( + self.context, share_group['id']) + db_driver.share_group_snapshot_create.assert_called_once_with( self.context, expected_values) - self.share_rpcapi.create_cgsnapshot.assert_called_once_with( - self.context, snap, cg['host'] - ) + self.share_rpcapi.create_share_group_snapshot.assert_called_once_with( + self.context, snap, share_group['host']) - def test_create_cgsnapshot_minimal_request_no_members_with_desc(self): + def test_create_group_snapshot_minimal_request_no_members_with_desc(self): fake_description = 'fake_description' - cg = fake_cg('fake_cg_id', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_AVAILABLE) - snap = fake_cgsnapshot('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - consistency_group_id=cg['id'], - description=fake_description, - status=constants.STATUS_CREATING) + share_group = fake_share_group( + 'fake_group_id', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_AVAILABLE) + snap = fake_share_group_snapshot( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + share_group_id=share_group['id'], + description=fake_description, + status=constants.STATUS_CREATING) expected_values = snap.copy() for name in ('id', 'created_at'): expected_values.pop(name, None) - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=cg)) - self.mock_object(db_driver, 'cgsnapshot_create', - mock.Mock(return_value=snap)) - self.mock_object(db_driver, 'share_get_all_by_consistency_group_id', - mock.Mock(return_value=[])) + self.mock_object( + db_driver, 'share_group_get', mock.Mock(return_value=share_group)) + self.mock_object( + db_driver, 'share_group_snapshot_create', + mock.Mock(return_value=snap)) + self.mock_object( + db_driver, 'share_get_all_by_share_group_id', + mock.Mock(return_value=[])) - self.api.create_cgsnapshot(self.context, consistency_group_id=cg['id'], - description=fake_description) + self.api.create_share_group_snapshot( + self.context, share_group_id=share_group['id'], + description=fake_description) - db_driver.consistency_group_get.assert_called_once_with( - self.context, cg['id'] - ) - db_driver.cgsnapshot_create.assert_called_once_with( + db_driver.share_group_get.assert_called_once_with( + self.context, share_group['id']) + db_driver.share_group_snapshot_create.assert_called_once_with( self.context, expected_values) - self.share_rpcapi.create_cgsnapshot.assert_called_once_with( - self.context, snap, cg['host'] - ) + self.share_rpcapi.create_share_group_snapshot.assert_called_once_with( + self.context, snap, share_group['host']) - def test_create_cgsnapshot_cg_does_not_exist(self): - cg = fake_cg('fake_cg_id', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - snap = fake_cgsnapshot('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - consistency_group_id=cg['id'], - status=constants.STATUS_CREATING) + def test_create_share_group_snapshot_group_does_not_exist(self): + share_group = fake_share_group( + 'fake_group_id', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + snap = fake_share_group_snapshot( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + share_group_id=share_group['id'], + status=constants.STATUS_CREATING) expected_values = snap.copy() for name in ('id', 'created_at'): expected_values.pop(name, None) - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=cg)) - self.mock_object(db_driver, 'cgsnapshot_create', - mock.Mock(return_value=snap)) - self.mock_object(db_driver, 'share_get_all_by_consistency_group_id', - mock.Mock(return_value=[])) + self.mock_object( + db_driver, 'share_group_get', mock.Mock(return_value=share_group)) + self.mock_object( + db_driver, 'share_group_snapshot_create', + mock.Mock(return_value=snap)) + self.mock_object( + db_driver, 'share_get_all_by_share_group_id', + mock.Mock(return_value=[])) - self.assertRaises(exception.InvalidConsistencyGroup, - self.api.create_cgsnapshot, - self.context, - consistency_group_id=cg['id']) + self.assertRaises( + exception.InvalidShareGroup, + self.api.create_share_group_snapshot, + self.context, share_group_id=share_group['id']) - db_driver.consistency_group_get.assert_called_once_with( - self.context, cg['id'] - ) + db_driver.share_group_get.assert_called_once_with( + self.context, share_group['id']) - def test_create_cgsnapshot_cg_in_creating(self): - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock( - side_effect=exception.ConsistencyGroupNotFound( - consistency_group_id='fake_id' - ))) + def test_create_share_group_snapshot_group_in_creating(self): + self.mock_object( + db_driver, 'share_group_get', + mock.Mock(side_effect=exception.ShareGroupNotFound( + share_group_id='fake_id'))) - self.assertRaises(exception.ConsistencyGroupNotFound, - self.api.create_cgsnapshot, - self.context, - consistency_group_id="fake_id") + self.assertRaises( + exception.ShareGroupNotFound, + self.api.create_share_group_snapshot, + self.context, share_group_id="fake_id") - db_driver.consistency_group_get.assert_called_once_with( - self.context, "fake_id" - ) + db_driver.share_group_get.assert_called_once_with( + self.context, "fake_id") - def test_create_cgsnapshot_with_member(self): - cg = fake_cg('fake_cg_id', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_AVAILABLE) - snap = fake_cgsnapshot('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - consistency_group_id=cg['id'], - status=constants.STATUS_CREATING) - share = stubs.stub_share('fake_share_id', - status=constants.STATUS_AVAILABLE) + def test_create_share_group_snapshot_with_member(self): + share_group = fake_share_group( + 'fake_group_id', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_AVAILABLE) + snap = fake_share_group_snapshot( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + share_group_id=share_group['id'], + status=constants.STATUS_CREATING) + share = stubs.stub_share( + 'fake_share_id', status=constants.STATUS_AVAILABLE) expected_values = snap.copy() for name in ('id', 'created_at'): expected_values.pop(name, None) expected_member_values = { - 'cgsnapshot_id': snap['id'], + 'share_group_snapshot_id': snap['id'], 'user_id': self.context.user_id, 'project_id': self.context.project_id, 'status': constants.STATUS_CREATING, 'size': share['size'], 'share_proto': share['share_proto'], - 'share_type_id': share['share_type_id'], 'share_id': share['id'], 'share_instance_id': mock.ANY, } - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=cg)) - self.mock_object(db_driver, 'cgsnapshot_create', - mock.Mock(return_value=snap)) - self.mock_object(db_driver, 'cgsnapshot_member_create', - mock.Mock()) - self.mock_object(db_driver, 'share_get_all_by_consistency_group_id', - mock.Mock(return_value=[share])) + self.mock_object( + db_driver, 'share_group_get', + mock.Mock(return_value=share_group)) + self.mock_object( + db_driver, 'share_group_snapshot_create', + mock.Mock(return_value=snap)) + self.mock_object(db_driver, 'share_group_snapshot_member_create') + self.mock_object( + db_driver, 'share_get_all_by_share_group_id', + mock.Mock(return_value=[share])) - self.api.create_cgsnapshot(self.context, consistency_group_id=cg['id']) + self.api.create_share_group_snapshot( + self.context, share_group_id=share_group['id']) - db_driver.consistency_group_get.assert_called_once_with( - self.context, cg['id'] - ) - db_driver.cgsnapshot_create.assert_called_once_with( + db_driver.share_group_get.assert_called_once_with( + self.context, share_group['id']) + db_driver.share_group_snapshot_create.assert_called_once_with( self.context, expected_values) - db_driver.cgsnapshot_member_create.assert_called_once_with( - self.context, expected_member_values - ) - self.share_rpcapi.create_cgsnapshot.assert_called_once_with( - self.context, snap, cg['host'] - ) + db_driver.share_group_snapshot_member_create.assert_called_once_with( + self.context, expected_member_values) + self.share_rpcapi.create_share_group_snapshot.assert_called_once_with( + self.context, snap, share_group['host']) - def test_create_cgsnapshot_with_member_share_in_creating(self): - cg = fake_cg('fake_cg_id', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_AVAILABLE) - share = stubs.stub_share('fake_share_id', - status=constants.STATUS_CREATING) - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=cg)) - self.mock_object(db_driver, 'share_get_all_by_consistency_group_id', - mock.Mock(return_value=[share])) + def test_create_share_group_snapshot_with_member_share_in_creating(self): + share_group = fake_share_group( + 'fake_group_id', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_AVAILABLE) + share = stubs.stub_share( + 'fake_share_id', status=constants.STATUS_CREATING) + self.mock_object( + db_driver, 'share_group_get', mock.Mock(return_value=share_group)) + self.mock_object( + db_driver, 'share_get_all_by_share_group_id', + mock.Mock(return_value=[share])) - self.assertRaises(exception.InvalidConsistencyGroup, - self.api.create_cgsnapshot, - self.context, - consistency_group_id=cg['id']) + self.assertRaises( + exception.InvalidShareGroup, + self.api.create_share_group_snapshot, + self.context, share_group_id=share_group['id']) - db_driver.consistency_group_get.assert_called_once_with( - self.context, cg['id'] - ) + db_driver.share_group_get.assert_called_once_with( + self.context, share_group['id']) - def test_create_cgsnapshot_with_two_members(self): - cg = fake_cg('fake_cg_id', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_AVAILABLE) - snap = fake_cgsnapshot('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - consistency_group_id=cg['id'], - status=constants.STATUS_CREATING) - share = stubs.stub_share('fake_share_id', - status=constants.STATUS_AVAILABLE) - share_2 = stubs.stub_share('fake_share2_id', - status=constants.STATUS_AVAILABLE) + def test_create_share_group_snapshot_with_two_members(self): + share_group = fake_share_group( + 'fake_group_id', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_AVAILABLE) + snap = fake_share_group_snapshot( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + share_group_id=share_group['id'], + status=constants.STATUS_CREATING) + share = stubs.stub_share( + 'fake_share_id', status=constants.STATUS_AVAILABLE) + share_2 = stubs.stub_share( + 'fake_share2_id', status=constants.STATUS_AVAILABLE) expected_values = snap.copy() for name in ('id', 'created_at'): expected_values.pop(name, None) expected_member_1_values = { - 'cgsnapshot_id': snap['id'], + 'share_group_snapshot_id': snap['id'], 'user_id': self.context.user_id, 'project_id': self.context.project_id, 'status': constants.STATUS_CREATING, 'size': share['size'], 'share_proto': share['share_proto'], - 'share_type_id': share['share_type_id'], 'share_id': share['id'], 'share_instance_id': mock.ANY, } expected_member_2_values = { - 'cgsnapshot_id': snap['id'], + 'share_group_snapshot_id': snap['id'], 'user_id': self.context.user_id, 'project_id': self.context.project_id, 'status': constants.STATUS_CREATING, 'size': share_2['size'], 'share_proto': share_2['share_proto'], - 'share_type_id': share_2['share_type_id'], 'share_id': share_2['id'], 'share_instance_id': mock.ANY, } - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=cg)) - self.mock_object(db_driver, 'cgsnapshot_create', - mock.Mock(return_value=snap)) - self.mock_object(db_driver, 'share_get_all_by_consistency_group_id', - mock.Mock(return_value=[share, share_2])) - self.mock_object(db_driver, 'cgsnapshot_member_create', - mock.Mock()) + self.mock_object( + db_driver, 'share_group_get', + mock.Mock(return_value=share_group)) + self.mock_object( + db_driver, 'share_group_snapshot_create', + mock.Mock(return_value=snap)) + self.mock_object( + db_driver, 'share_get_all_by_share_group_id', + mock.Mock(return_value=[share, share_2])) + self.mock_object(db_driver, 'share_group_snapshot_member_create') - self.api.create_cgsnapshot(self.context, consistency_group_id=cg['id']) + self.api.create_share_group_snapshot( + self.context, share_group_id=share_group['id']) - db_driver.consistency_group_get.assert_called_once_with( - self.context, cg['id'] - ) - db_driver.cgsnapshot_create.assert_called_once_with( + db_driver.share_group_get.assert_called_once_with( + self.context, share_group['id']) + db_driver.share_group_snapshot_create.assert_called_once_with( self.context, expected_values) + db_driver.share_group_snapshot_member_create.assert_any_call( + self.context, expected_member_1_values) + db_driver.share_group_snapshot_member_create.assert_any_call( + self.context, expected_member_2_values) + self.share_rpcapi.create_share_group_snapshot.assert_called_once_with( + self.context, snap, share_group['host']) - db_driver.cgsnapshot_member_create.assert_any_call( - self.context, expected_member_1_values - ) - db_driver.cgsnapshot_member_create.assert_any_call( - self.context, expected_member_2_values - ) - self.share_rpcapi.create_cgsnapshot.assert_called_once_with( - self.context, snap, cg['host'] - ) - - def test_create_cgsnapshot_error_creating_member(self): - cg = fake_cg('fake_cg_id', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_AVAILABLE) - snap = fake_cgsnapshot('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - consistency_group_id=cg['id'], - status=constants.STATUS_CREATING) - share = stubs.stub_share('fake_share_id', - status=constants.STATUS_AVAILABLE) + def test_create_share_group_snapshot_error_creating_member(self): + share_group = fake_share_group( + 'fake_group_id', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_AVAILABLE) + snap = fake_share_group_snapshot( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + share_group_id=share_group['id'], + status=constants.STATUS_CREATING) + share = stubs.stub_share( + 'fake_share_id', status=constants.STATUS_AVAILABLE) expected_values = snap.copy() for name in ('id', 'created_at'): expected_values.pop(name, None) expected_member_values = { - 'cgsnapshot_id': snap['id'], + 'share_group_snapshot_id': snap['id'], 'user_id': self.context.user_id, 'project_id': self.context.project_id, 'status': constants.STATUS_CREATING, 'size': share['size'], 'share_proto': share['share_proto'], - 'share_type_id': share['share_type_id'], 'share_id': share['id'], 'share_instance_id': mock.ANY, } - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=cg)) - self.mock_object(db_driver, 'cgsnapshot_create', - mock.Mock(return_value=snap)) - self.mock_object(db_driver, 'cgsnapshot_destroy') - self.mock_object(db_driver, 'cgsnapshot_member_create', - mock.Mock(side_effect=exception.Error)) - self.mock_object(db_driver, 'share_get_all_by_consistency_group_id', - mock.Mock(return_value=[share])) + self.mock_object( + db_driver, 'share_group_get', + mock.Mock(return_value=share_group)) + self.mock_object( + db_driver, 'share_group_snapshot_create', + mock.Mock(return_value=snap)) + self.mock_object(db_driver, 'share_group_snapshot_destroy') + self.mock_object( + db_driver, 'share_group_snapshot_member_create', + mock.Mock(side_effect=exception.Error)) + self.mock_object( + db_driver, 'share_get_all_by_share_group_id', + mock.Mock(return_value=[share])) - self.assertRaises(exception.Error, self.api.create_cgsnapshot, - self.context, consistency_group_id=cg['id']) + self.assertRaises( + exception.Error, + self.api.create_share_group_snapshot, + self.context, share_group_id=share_group['id']) - db_driver.consistency_group_get.assert_called_once_with( - self.context, cg['id'] - ) - db_driver.cgsnapshot_create.assert_called_once_with( + db_driver.share_group_get.assert_called_once_with( + self.context, share_group['id']) + db_driver.share_group_snapshot_create.assert_called_once_with( self.context, expected_values) - db_driver.cgsnapshot_member_create.assert_called_once_with( - self.context, expected_member_values - ) - db_driver.cgsnapshot_destroy.assert_called_once_with( - self.context, snap['id'] - ) + db_driver.share_group_snapshot_member_create.assert_called_once_with( + self.context, expected_member_values) + db_driver.share_group_snapshot_destroy.assert_called_once_with( + self.context, snap['id']) - def test_delete_cgsnapshot(self): - cg = fake_cg('fake_id', host="fake_host") - snap = fake_cgsnapshot('fake_cgsnap_id', - consistency_group_id='fake_id', - status=constants.STATUS_AVAILABLE) - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock(return_value=cg)) - self.mock_object(db_driver, 'cgsnapshot_update') + def test_delete_share_group_snapshot(self): + share_group = fake_share_group('fake_id', host="fake_host") + sg_snap = fake_share_group_snapshot( + 'fake_groupsnap_id', share_group_id='fake_id', + status=constants.STATUS_AVAILABLE) + self.mock_object(db_driver, 'share_group_get', + mock.Mock(return_value=share_group)) + self.mock_object(db_driver, 'share_group_snapshot_update') - self.api.delete_cgsnapshot(self.context, snap) + self.api.delete_share_group_snapshot(self.context, sg_snap) - db_driver.consistency_group_get.assert_called_once_with( - self.context, "fake_id" - ) - db_driver.cgsnapshot_update.assert_called_once_with( - self.context, snap['id'], {'status': constants.STATUS_DELETING} - ) - self.share_rpcapi.delete_cgsnapshot.assert_called_once_with( - self.context, snap, cg['host']) + db_driver.share_group_get.assert_called_once_with( + self.context, "fake_id") + db_driver.share_group_snapshot_update.assert_called_once_with( + self.context, sg_snap['id'], {'status': constants.STATUS_DELETING}) + self.share_rpcapi.delete_share_group_snapshot.assert_called_once_with( + self.context, sg_snap, share_group['host']) - def test_delete_cgsnapshot_cg_does_not_exist(self): - snap = fake_cgsnapshot('fake_cgsnap_id', - consistency_group_id='fake_id') - self.mock_object(db_driver, 'consistency_group_get', - mock.Mock( - side_effect=exception.ConsistencyGroupNotFound( - consistency_group_id='fake_id' - ))) + def test_delete_share_group_snapshot_group_does_not_exist(self): + snap = fake_share_group_snapshot( + 'fake_groupsnap_id', share_group_id='fake_id') + self.mock_object( + db_driver, 'share_group_get', + mock.Mock(side_effect=exception.ShareGroupNotFound( + share_group_id='fake_id'))) - self.assertRaises(exception.ConsistencyGroupNotFound, - self.api.delete_cgsnapshot, - self.context, - snap) + self.assertRaises( + exception.ShareGroupNotFound, + self.api.delete_share_group_snapshot, self.context, snap) - db_driver.consistency_group_get.assert_called_once_with( - self.context, "fake_id" - ) + db_driver.share_group_get.assert_called_once_with( + self.context, "fake_id") - def test_delete_cgsnapshot_creating_status(self): - snap = fake_cgsnapshot('fake_cgsnap_id', - consistency_group_id='fake_id', - status=constants.STATUS_CREATING) - self.mock_object(db_driver, 'consistency_group_get') + def test_delete_share_group_snapshot_creating_status(self): + snap = fake_share_group_snapshot( + 'fake_groupsnap_id', share_group_id='fake_id', + status=constants.STATUS_CREATING) + self.mock_object(db_driver, 'share_group_get') - self.assertRaises(exception.InvalidCGSnapshot, - self.api.delete_cgsnapshot, - self.context, - snap) + self.assertRaises( + exception.InvalidShareGroupSnapshot, + self.api.delete_share_group_snapshot, self.context, snap) - db_driver.consistency_group_get.assert_called_once_with( - self.context, "fake_id" - ) + db_driver.share_group_get.assert_called_once_with( + self.context, snap['share_group_id']) - def test_update_cgsnapshot_no_values(self): - snap = fake_cgsnapshot('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = {} - self.mock_object(db_driver, 'cgsnapshot_update', - mock.Mock(return_value=snap)) + @ddt.data({}, {"name": "fake_name"}) + def test_update_share_group_snapshot_no_values(self, expected_values): + snap = fake_share_group_snapshot( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + self.mock_object( + db_driver, 'share_group_snapshot_update', + mock.Mock(return_value=snap)) - self.api.update_cgsnapshot(self.context, snap, expected_values) + self.api.update_share_group_snapshot( + self.context, snap, expected_values) - db_driver.cgsnapshot_update.assert_called_once_with( + db_driver.share_group_snapshot_update.assert_called_once_with( self.context, snap['id'], expected_values) - def test_update_cgsnapshot_with_name(self): - fake_name = 'fake_name' - snap = fake_cgsnapshot('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - expected_values = {'description': fake_name} + def test_share_group_snapshot_get(self): + expected = fake_share_group_snapshot( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING) + self.mock_object( + db_driver, 'share_group_snapshot_get', + mock.Mock(return_value=expected)) - self.mock_object(db_driver, 'cgsnapshot_update', - mock.Mock(return_value=snap)) + actual = self.api.get_share_group_snapshot( + self.context, expected['id']) - self.api.update_cgsnapshot(self.context, snap, expected_values) + self.assertEqual(expected, actual) - db_driver.cgsnapshot_update.assert_called_once_with( - self.context, snap['id'], expected_values) + def test_share_group_snapshot_get_all_no_groups(self): + self.mock_object( + db_driver, 'share_group_snapshot_get_all', + mock.Mock(return_value=[])) - def test_cgsnapshot_get(self): - expected_snap = fake_cgsnapshot('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING) - self.mock_object(db_driver, 'cgsnapshot_get', - mock.Mock(return_value=expected_snap)) + actual = self.api.get_all_share_group_snapshots(self.context) - actual_cg = self.api.get_cgsnapshot(self.context, expected_snap['id']) - self.assertEqual(expected_snap, actual_cg) + self.assertEqual([], actual) - def test_cgsnapshot_get_all_no_cgs(self): + def test_share_group_snapshot_get_all(self): + expected = [fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING)] + self.mock_object( + db_driver, 'share_group_snapshot_get_all_by_project', + mock.Mock(return_value=expected)) - self.mock_object(db_driver, 'cgsnapshot_get_all', - mock.Mock(return_value=[])) - - actual_cg = self.api.get_all_cgsnapshots(self.context) - self.assertEqual([], actual_cg) - - def test_cgsnapshot_get_all(self): - expected_snaps = [fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING)] - self.mock_object(db_driver, 'cgsnapshot_get_all_by_project', - mock.Mock(return_value=expected_snaps)) - - actual_cg = self.api.get_all_cgsnapshots(self.context, detailed=True) - self.assertEqual(expected_snaps, actual_cg) - - def test_cgsnapshot_get_all_all_tenants_not_admin(self): - cxt = context.RequestContext(user_id=None, - project_id=None, - is_admin=False) - expected_snaps = [fake_cg('fakeid', - user_id=cxt.user_id, - project_id=cxt.project_id, - status=constants.STATUS_CREATING)] - self.mock_object(db_driver, 'cgsnapshot_get_all_by_project', - mock.Mock(return_value=expected_snaps)) - - actual_cgs = self.api.get_all_cgsnapshots( - cxt, search_opts={'all_tenants': True}) - self.assertEqual(expected_snaps, actual_cgs) - - def test_cgsnapshot_get_all_all_tenants_as_admin(self): - expected_snaps = [fake_cg('fakeid', - user_id=self.context.user_id, - project_id=self.context.project_id, - status=constants.STATUS_CREATING)] - self.mock_object(db_driver, 'cgsnapshot_get_all', - mock.Mock(return_value=expected_snaps)) - - actual_cgs = self.api.get_all_cgsnapshots( - self.context, search_opts={'all_tenants': True}) - self.assertEqual(expected_snaps, actual_cgs) - db_driver.cgsnapshot_get_all.assert_called_once_with( + actual = self.api.get_all_share_group_snapshots( self.context, detailed=True) - def test_get_all_cgsnapshot_members(self): - self.mock_object(db_driver, 'cgsnapshot_members_get_all', - mock.Mock(return_value=[])) + self.assertEqual(expected, actual) - self.api.get_all_cgsnapshot_members(self.context, 'fake_id') + def test_share_group_snapshot_get_all_all_tenants_not_admin(self): + cxt = context.RequestContext( + user_id=None, project_id=None, is_admin=False) + expected = [fake_share_group( + 'fakeid', user_id=cxt.user_id, project_id=cxt.project_id, + status=constants.STATUS_CREATING)] + self.mock_object( + db_driver, 'share_group_snapshot_get_all_by_project', + mock.Mock(return_value=expected)) - db_driver.cgsnapshot_members_get_all.assert_called_once_with( - self.context, 'fake_id' - ) + actual = self.api.get_all_share_group_snapshots( + cxt, search_opts={'all_tenants': True}) + + self.assertEqual(expected, actual) + + def test_share_group_snapshot_get_all_all_tenants_as_admin(self): + expected = [fake_share_group( + 'fakeid', user_id=self.context.user_id, + project_id=self.context.project_id, + status=constants.STATUS_CREATING)] + self.mock_object( + db_driver, 'share_group_snapshot_get_all', + mock.Mock(return_value=expected)) + + actual = self.api.get_all_share_group_snapshots( + self.context, search_opts={'all_tenants': True}) + + self.assertEqual(expected, actual) + db_driver.share_group_snapshot_get_all.assert_called_once_with( + self.context, detailed=True, filters={}, + sort_dir=None, sort_key=None) + + def test_get_all_share_group_snapshot_members(self): + self.mock_object( + db_driver, 'share_group_snapshot_members_get_all', + mock.Mock(return_value=[])) + + self.api.get_all_share_group_snapshot_members(self.context, 'fake_id') + + db_driver.share_group_snapshot_members_get_all.assert_called_once_with( + self.context, 'fake_id') diff --git a/manila/tests/share_group/test_share_group_types.py b/manila/tests/share_group/test_share_group_types.py new file mode 100644 index 0000000000..9915a638f6 --- /dev/null +++ b/manila/tests/share_group/test_share_group_types.py @@ -0,0 +1,156 @@ +# 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. + + +"""Test of Share Type methods for Manila.""" +import copy +import datetime + +import ddt +import mock + +from manila.common import constants +from manila import context +from manila import db +from manila import exception +from manila.share_group import share_group_types +from manila import test + + +def create_share_group_type_dict(group_specs=None): + return { + 'fake_type': { + 'name': 'fake1', + 'group_specs': group_specs + } + } + + +@ddt.ddt +class ShareGroupTypesTestCase(test.TestCase): + + fake_type = { + 'test': { + 'created_at': datetime.datetime(2015, 1, 22, 11, 43, 24), + 'deleted': '0', + 'deleted_at': None, + 'group_specs': {}, + 'id': u'fooid-1', + 'name': u'test', + 'updated_at': None + } + } + fake_group_specs = {u'gold': u'True'} + fake_share_group_type_id = u'fooid-2' + fake_type_w_extra = { + 'test_with_extra': { + 'created_at': datetime.datetime(2015, 1, 22, 11, 45, 31), + 'deleted': '0', + 'deleted_at': None, + 'group_specs': fake_group_specs, + 'id': fake_share_group_type_id, + 'name': u'test_with_extra', + 'updated_at': None + } + } + + fake_type_w_valid_extra = { + 'test_with_extra': { + 'created_at': datetime.datetime(2015, 1, 22, 11, 45, 31), + 'deleted': '0', + 'deleted_at': None, + 'group_specs': { + constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS: 'true' + }, + 'id': u'fooid-2', + 'name': u'test_with_extra', + 'updated_at': None + } + } + + fake_types = fake_type.copy() + fake_types.update(fake_type_w_extra) + fake_types.update(fake_type_w_valid_extra) + + fake_share_group = { + 'id': u'fooid-1', + 'share_group_type_id': fake_share_group_type_id, + } + + def setUp(self): + super(ShareGroupTypesTestCase, self).setUp() + self.context = context.get_admin_context() + + @ddt.data({}, fake_type, fake_type_w_extra, fake_types) + def test_get_all_types(self, share_group_type): + self.mock_object( + db, 'share_group_type_get_all', + mock.Mock(return_value=copy.deepcopy(share_group_type))) + + returned_type = share_group_types.get_all(self.context) + + self.assertItemsEqual(share_group_type, returned_type) + + def test_get_all_types_search(self): + share_group_type = self.fake_type_w_extra + search_filter = {"group_specs": {"gold": "True"}, 'is_public': True} + self.mock_object( + db, 'share_group_type_get_all', + mock.Mock(return_value=share_group_type)) + + returned_type = share_group_types.get_all( + self.context, search_opts=search_filter) + + db.share_group_type_get_all.assert_called_once_with( + mock.ANY, 0, filters={'is_public': True}) + self.assertItemsEqual(share_group_type, returned_type) + search_filter = {"group_specs": {"gold": "False"}} + returned_type = share_group_types.get_all( + self.context, search_opts=search_filter) + self.assertEqual({}, returned_type) + + def test_add_access(self): + project_id = '456' + share_group_type = share_group_types.create(self.context, 'type2', []) + share_group_type_id = share_group_type.get('id') + + share_group_types.add_share_group_type_access( + self.context, share_group_type_id, project_id) + stype_access = db.share_group_type_access_get_all( + self.context, share_group_type_id) + + self.assertIn(project_id, [a.project_id for a in stype_access]) + + def test_add_access_invalid(self): + self.assertRaises( + exception.InvalidShareGroupType, + share_group_types.add_share_group_type_access, + 'fake', None, 'fake') + + def test_remove_access(self): + project_id = '456' + share_group_type = share_group_types.create( + self.context, 'type3', [], projects=['456']) + share_group_type_id = share_group_type.get('id') + + share_group_types.remove_share_group_type_access( + self.context, share_group_type_id, project_id) + stype_access = db.share_group_type_access_get_all( + self.context, share_group_type_id) + + self.assertNotIn(project_id, stype_access) + + def test_remove_access_invalid(self): + self.assertRaises( + exception.InvalidShareGroupType, + share_group_types.remove_share_group_type_access, + 'fake', None, 'fake') diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py index 590912ef91..d25cb8dd68 100644 --- a/manila_tempest_tests/config.py +++ b/manila_tempest_tests/config.py @@ -30,7 +30,7 @@ ShareGroup = [ help="The minimum api microversion is configured to be the " "value of the minimum microversion supported by Manila."), cfg.StrOpt("max_api_microversion", - default="2.30", + default="2.31", help="The maximum api microversion is configured to be the " "value of the latest microversion supported by Manila."), cfg.StrOpt("region", @@ -171,7 +171,7 @@ ShareGroup = [ "to snapshots or not. Enable this feature if used " "driver supports it."), cfg.BoolOpt("run_consistency_group_tests", - default=True, + default=False, help="Defines whether to run consistency group tests or not. " "Disable this feature if used driver doesn't support " "it."), diff --git a/manila_tempest_tests/tests/api/test_shares_actions.py b/manila_tempest_tests/tests/api/test_shares_actions.py index 0f904fc91c..cfebdd31e2 100644 --- a/manila_tempest_tests/tests/api/test_shares_actions.py +++ b/manila_tempest_tests/tests/api/test_shares_actions.py @@ -94,9 +94,6 @@ class SharesActionsTest(base.BaseSharesTest): expected_keys.extend(["export_location", "export_locations"]) if utils.is_microversion_ge(version, '2.2'): expected_keys.append("snapshot_support") - if utils.is_microversion_ge(version, '2.4'): - expected_keys.extend(["consistency_group_id", - "source_cgsnapshot_member_id"]) if utils.is_microversion_ge(version, '2.5'): expected_keys.append("share_type_name") if utils.is_microversion_ge(version, '2.10'): @@ -136,11 +133,6 @@ class SharesActionsTest(base.BaseSharesTest): def test_get_share_with_snapshot_support_key(self): self._get_share('2.2') - @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) - @utils.skip_if_microversion_not_supported('2.4') - def test_get_share_with_consistency_groups_keys(self): - self._get_share('2.4') - @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) @utils.skip_if_microversion_not_supported('2.6') def test_get_share_with_share_type_name_key(self): @@ -210,9 +202,6 @@ class SharesActionsTest(base.BaseSharesTest): keys.extend(["export_location", "export_locations"]) if utils.is_microversion_ge(version, '2.2'): keys.append("snapshot_support") - if utils.is_microversion_ge(version, '2.4'): - keys.extend(["consistency_group_id", - "source_cgsnapshot_member_id"]) if utils.is_microversion_ge(version, '2.6'): keys.append("share_type_name") if utils.is_microversion_ge(version, '2.10'): @@ -242,11 +231,6 @@ class SharesActionsTest(base.BaseSharesTest): def test_list_shares_with_detail_and_snapshot_support_key(self): self._list_shares_with_detail('2.2') - @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) - @utils.skip_if_microversion_not_supported('2.4') - def test_list_shares_with_detail_consistency_groups_keys(self): - self._list_shares_with_detail('2.4') - @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND) @utils.skip_if_microversion_not_supported('2.6') def test_list_shares_with_detail_share_type_name_key(self): diff --git a/setup.cfg b/setup.cfg index 420051e066..bc1b554f5f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,7 +37,6 @@ manila.scheduler.filters = AvailabilityZoneFilter = manila.scheduler.filters.availability_zone:AvailabilityZoneFilter CapabilitiesFilter = manila.scheduler.filters.capabilities:CapabilitiesFilter CapacityFilter = manila.scheduler.filters.capacity:CapacityFilter - ConsistencyGroupFilter = manila.scheduler.filters.consistency_group:ConsistencyGroupFilter DriverFilter = manila.scheduler.filters.driver:DriverFilter IgnoreAttemptedHostsFilter = manila.scheduler.filters.ignore_attempted_hosts:IgnoreAttemptedHostsFilter JsonFilter = manila.scheduler.filters.json:JsonFilter