Manila Share Groups

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

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

Change-Id: I79a80a62ae4e0015d6161edc2b93fd1f9ba69537
This commit is contained in:
Alex Meade 2016-05-20 14:15:52 -04:00 committed by Valeriy Ponomaryov
parent 3e37cd2e5a
commit d25f101ab4
81 changed files with 7259 additions and 3813 deletions

View File

@ -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

View File

@ -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"
}

View File

@ -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

View File

@ -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.

View File

@ -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')))):

View File

@ -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",

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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}

View File

@ -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

View File

@ -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]

View File

@ -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',

View File

@ -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)

View File

@ -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"])

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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():

View File

@ -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):

View File

@ -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."""

View File

@ -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] = (
"<is> %s" % extra_spec)
# Only allow pools that support consistency groups
share_type['extra_specs']['consistency_group_support'] = (
"<or> host <or> 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)

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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):

View File

@ -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)

View File

@ -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,

View File

@ -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'])

View File

@ -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': [<models.ConsistencyGroupShareTypeMapping>],
'id': 'eda52174-0442-476d-9694-a58327466c14',
'share_types': [<models.ShareGroupShareTypeMapping>],
'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': [<models.Share>], # The new shares being created
'share_types': [<models.ConsistencyGroupShareTypeMapping>],
'id': 'eda52174-0442-476d-9694-a58327466c14',
'share_types': [<models.ShareGroupShareTypeMapping>],
'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': [<models.Share>], # The new shares being created
'share_types': [<models.ConsistencyGroupShareTypeMapping>],
'id': 'eda52174-0442-476d-9694-a58327466c14',
'share_types': [<models.ShareGroupShareTypeMapping>],
'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.

View File

@ -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, []

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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',

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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)

View File

@ -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',

View File

@ -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)

View File

@ -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)

View File

@ -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'])

View File

@ -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')

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -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,

View File

@ -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': [

View File

@ -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'])

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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,
}

View File

@ -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"
}

View File

@ -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()

View File

@ -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

View File

@ -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,

View File

@ -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={})

View File

@ -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',

View File

@ -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'
})

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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,

View File

@ -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'])

View File

@ -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',

View File

@ -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)

View File

@ -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)

View File

@ -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):

View File

@ -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',

File diff suppressed because it is too large Load Diff

View File

@ -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')

View File

@ -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."),

View File

@ -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):

View File

@ -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