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_SHRINK_TESTS=${RUN_MANILA_SHRINK_TESTS:-True}
RUN_MANILA_SNAPSHOT_TESTS=${RUN_MANILA_SNAPSHOT_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_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_TESTS=${RUN_MANILA_MANAGE_TESTS:-True}
RUN_MANILA_MANAGE_SNAPSHOT_TESTS=${RUN_MANILA_MANAGE_SNAPSHOT_TESTS:-False} RUN_MANILA_MANAGE_SNAPSHOT_TESTS=${RUN_MANILA_MANAGE_SNAPSHOT_TESTS:-False}
RUN_MANILA_REPLICATION_TESTS=${RUN_MANILA_REPLICATION_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 iniset $TEMPEST_CONFIG share capability_snapshot_support True
elif [[ "$DRIVER" == "dummy" ]]; then elif [[ "$DRIVER" == "dummy" ]]; then
MANILA_TEMPEST_CONCURRENCY=24 MANILA_TEMPEST_CONCURRENCY=24
RUN_MANILA_CG_TESTS=True RUN_MANILA_CG_TESTS=False
RUN_MANILA_MANAGE_TESTS=False RUN_MANILA_MANAGE_TESTS=False
RUN_MANILA_DRIVER_ASSISTED_MIGRATION_TESTS=True RUN_MANILA_DRIVER_ASSISTED_MIGRATION_TESTS=True
RUN_MANILA_REVERT_TO_SNAPSHOT_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:index": "rule:admin_api",
"scheduler_stats:pools:detail": "rule:admin_api", "scheduler_stats:pools:detail": "rule:admin_api",
"consistency_group:create" : "rule:default", "share_group:create" : "rule:default",
"consistency_group:delete": "rule:default", "share_group:delete": "rule:default",
"consistency_group:update": "rule:default", "share_group:update": "rule:default",
"consistency_group:get": "rule:default", "share_group:get": "rule:default",
"consistency_group:get_all": "rule:default", "share_group:get_all": "rule:default",
"consistency_group:force_delete": "rule:admin_api", "share_group:force_delete": "rule:admin_api",
"consistency_group:reset_status": "rule:admin_api", "share_group:reset_status": "rule:admin_api",
"cgsnapshot:force_delete": "rule:admin_api", "share_group_snapshot:create" : "rule:default",
"cgsnapshot:reset_status": "rule:admin_api", "share_group_snapshot:delete": "rule:default",
"cgsnapshot:create" : "rule:default", "share_group_snapshot:update" : "rule:default",
"cgsnapshot:update" : "rule:default", "share_group_snapshot:get": "rule:default",
"cgsnapshot:delete": "rule:default", "share_group_snapshot:get_all": "rule:default",
"cgsnapshot:get_cgsnapshot": "rule:default", "share_group_snapshot:force_delete": "rule:admin_api",
"cgsnapshot:get_all": "rule:default", "share_group_snapshot:reset_status": "rule:admin_api",
"share_replica:get_all": "rule:default", "share_replica:get_all": "rule:default",
"share_replica:show": "rule:default", "share_replica:show": "rule:default",
@ -132,5 +132,20 @@
"share_replica:resync": "rule:admin_api", "share_replica:resync": "rule:admin_api",
"share_replica:reset_status": "rule:admin_api", "share_replica:reset_status": "rule:admin_api",
"share_replica:force_delete": "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 migration_start APIs prior to this microversion are now
unsupported. unsupported.
* 2.30 - Added cast_rules_to_readonly field to share_instances. * 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 minimum and maximum versions of the API supported
# The default api version request is defined to be the # The default api version request is defined to be the
# minimum version of the API supported. # minimum version of the API supported.
_MIN_API_VERSION = "2.0" _MIN_API_VERSION = "2.0"
_MAX_API_VERSION = "2.30" _MAX_API_VERSION = "2.31"
DEFAULT_API_VERSION = _MIN_API_VERSION DEFAULT_API_VERSION = _MIN_API_VERSION
@ -142,8 +143,13 @@ class APIVersionRequest(utils.ComparableMixin):
def __str__(self): def __str__(self):
"""Debug/Logging representation of object.""" """Debug/Logging representation of object."""
return ("API Version Request Major: %(major)s, Minor: %(minor)s" params = {
% {'major': self._ver_major, 'minor': self._ver_minor}) '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): def is_null(self):
return self._ver_major is None and self._ver_minor is None return self._ver_major is None and self._ver_minor is None

View File

@ -183,3 +183,7 @@ user documentation.
2.30 2.30
---- ----
Added cast_rules_to_readonly field to share_instances. 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: try:
share = self.share_api.get(context, id) share = self.share_api.get(context, id)
# NOTE(ameade): If the share is in a consistency group, we require # NOTE(ameade): If the share is in a share group, we require its
# it's id be specified as a param. # id be specified as a param.
if share.get('consistency_group_id'): sg_id_key = 'share_group_id'
consistency_group_id = req.params.get('consistency_group_id') if share.get(sg_id_key):
if (share.get('consistency_group_id') and share_group_id = req.params.get(sg_id_key)
not consistency_group_id): if not share_group_id:
msg = _("Must provide 'consistency_group_id' as a request " msg = _("Must provide '%s' as a request "
"parameter when deleting a share in a consistency " "parameter when deleting a share in a share "
"group.") "group.") % sg_id_key
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
elif consistency_group_id != share.get('consistency_group_id'): elif share_group_id != share.get(sg_id_key):
msg = _("The specified 'consistency_group_id' does not " msg = _("The specified '%s' does not match "
"match the consistency group id of the share.") "the share group id of the share.") % sg_id_key
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
self.share_api.delete(context, share) self.share_api.delete(context, share)
@ -161,7 +161,7 @@ class ShareMixin(object):
'display_name', 'status', 'share_server_id', 'volume_type_id', 'display_name', 'status', 'share_server_id', 'volume_type_id',
'share_type_id', 'snapshot_id', 'host', 'share_network_id', 'share_type_id', 'snapshot_id', 'host', 'share_network_id',
'is_public', 'metadata', 'extra_specs', 'sort_key', 'sort_dir', '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): def update(self, req, id, body):
@ -192,8 +192,8 @@ class ShareMixin(object):
return self._view_builder.detail(req, share) return self._view_builder.detail(req, share)
def create(self, req, body): def create(self, req, body):
# Remove consistency group attributes # Remove share group attributes
body.get('share', {}).pop('consistency_group_id', None) body.get('share', {}).pop('share_group_id', None)
share = self._create(req, body) share = self._create(req, body)
return share return share
@ -237,7 +237,7 @@ class ShareMixin(object):
'availability_zone': availability_zone, 'availability_zone': availability_zone,
'metadata': share.get('metadata'), 'metadata': share.get('metadata'),
'is_public': share.get('is_public', False), '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') snapshot_id = share.get('snapshot_id')
@ -310,10 +310,10 @@ class ShareMixin(object):
share_type = def_share_type share_type = def_share_type
# Only use in create share feature. Create share from snapshot # 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. # need this check.
if (not share_network_id and not snapshot 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 share_type and share_type.get('extra_specs')
and (strutils.bool_from_string(share_type.get('extra_specs'). and (strutils.bool_from_string(share_type.get('extra_specs').
get('driver_handles_share_servers')))): 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 services
from manila.api.v2 import share_export_locations from manila.api.v2 import share_export_locations
from manila.api.v2 import share_group_snapshots 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_groups
from manila.api.v2 import share_instance_export_locations from manila.api.v2 import share_instance_export_locations
from manila.api.v2 import share_instances from manila.api.v2 import share_instances
@ -280,21 +282,83 @@ class APIRouter(manila.api.openstack.APIRouter):
action="pools_detail", action="pools_detail",
conditions={"method": ["GET"]}) conditions={"method": ["GET"]})
self.resources["consistency-groups"] = share_groups.create_resource() self.resources["share-groups"] = share_groups.create_resource()
mapper.resource("consistency-group", "consistency-groups", mapper.resource(
controller=self.resources["consistency-groups"], "share-group",
collection={"detail": "GET"}) "share-groups",
mapper.connect("consistency-groups", controller=self.resources["share-groups"],
"/{project_id}/consistency-groups/{id}/action", collection={"detail": "GET"})
controller=self.resources["consistency-groups"], mapper.connect(
action="action", "share-groups",
conditions={"action": ["POST"]}) "/{project_id}/share-groups/{id}/action",
controller=self.resources["share-groups"],
action="action",
conditions={"method": ["POST"]})
self.resources["cgsnapshots"] = share_group_snapshots.create_resource() self.resources["share-group-types"] = (
mapper.resource("cgsnapshot", "cgsnapshots", share_group_types.create_resource())
controller=self.resources["cgsnapshots"], mapper.resource(
collection={"detail": "GET"}, "share-group-type",
member={"members": "GET", "action": "POST"}) "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() self.resources['share-replicas'] = share_replicas.create_resource()
mapper.resource("share-replica", "share-replicas", mapper.resource("share-replica", "share-replicas",

View File

@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
"""The consistency groups snapshot API."""
from oslo_log import log from oslo_log import log
from oslo_utils import uuidutils from oslo_utils import uuidutils
import six import six
@ -23,86 +21,85 @@ from webob import exc
from manila.api import common from manila.api import common
from manila.api.openstack import wsgi 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 db
from manila import exception from manila import exception
from manila.i18n import _, _LI 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__) LOG = log.getLogger(__name__)
class CGSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin): class ShareGroupSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The Consistency Group Snapshots API controller for the OpenStack API.""" """The share group snapshots API controller for the OpenStack API."""
resource_name = 'cgsnapshot' resource_name = 'share_group_snapshot'
_view_builder_class = sgs_views.CGSnapshotViewBuilder _view_builder_class = (
share_group_snapshots_views.ShareGroupSnapshotViewBuilder)
def __init__(self): def __init__(self):
super(CGSnapshotController, self).__init__() super(ShareGroupSnapshotController, self).__init__()
self.cg_api = sg_api.API() self.share_group_api = share_group_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']
def _get_share_group_snapshot(self, context, sg_snapshot_id):
try: 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: 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) 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 @wsgi.Controller.authorize
def delete(self, req, id): def delete(self, req, id):
"""Delete a cgsnapshot.""" """Delete a share group snapshot."""
context = req.environ['manila.context'] context = req.environ['manila.context']
LOG.info(_LI("Delete share group snapshot with id: %s"),
LOG.info(_LI("Delete consistency group snapshot with id: %s"), id, id, context=context)
context=context) sg_snapshot = self._get_share_group_snapshot(context, id)
try: try:
snap = self.cg_api.get_cgsnapshot(context, id) self.share_group_api.delete_share_group_snapshot(
except exception.NotFound: context, sg_snapshot)
msg = _("Consistency group snapshot %s not found.") % id except exception.InvalidShareGroupSnapshot as e:
raise exc.HTTPNotFound(explanation=msg)
try:
self.cg_api.delete_cgsnapshot(context, snap)
except exception.InvalidCGSnapshot as e:
raise exc.HTTPConflict(explanation=six.text_type(e)) raise exc.HTTPConflict(explanation=six.text_type(e))
return webob.Response(status_int=202) 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') @wsgi.Controller.authorize('get_all')
def index(self, req): def index(self, req):
"""Returns a summary list of cgsnapshots.""" """Returns a summary list of share group snapshots."""
return self._get_cgs(req, is_detail=False) 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') @wsgi.Controller.authorize('get_all')
def detail(self, req): def detail(self, req):
"""Returns a detailed list of cgsnapshots.""" """Returns a detailed list of share group snapshots."""
return self._get_cgs(req, is_detail=True) return self._get_share_group_snaps(req, is_detail=True)
def _get_cgs(self, req, is_detail): def _get_share_group_snaps(self, req, is_detail):
"""Returns a list of cgsnapshots.""" """Returns a list of share group snapshots."""
context = req.environ['manila.context'] context = req.environ['manila.context']
search_opts = {} search_opts = {}
search_opts.update(req.GET) 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('limit', None)
search_opts.pop('offset', 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( snaps = self.share_group_api.get_all_share_group_snapshots(
context, detailed=is_detail, search_opts=search_opts) context, detailed=is_detail, search_opts=search_opts,
sort_dir=sort_dir, sort_key=sort_key)
limited_list = common.limited(snaps, req) 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) snaps = self._view_builder.summary_list(req, limited_list)
return snaps return snaps
@wsgi.Controller.api_version('2.4', experimental=True) @wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize @wsgi.Controller.authorize
def update(self, req, id, body): def update(self, req, id, body):
"""Update a cgsnapshot.""" """Update a share group snapshot."""
context = req.environ['manila.context'] context = req.environ['manila.context']
key = 'share_group_snapshot'
if not self.is_valid_body(body, 'cgsnapshot'): if not self.is_valid_body(body, key):
msg = _("'cgsnapshot' is missing from the request body") msg = _("'%s' is missing from the request body.") % key
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
cg_data = body['cgsnapshot'] sg_snapshot_data = body[key]
valid_update_keys = { valid_update_keys = {
'name', 'name',
'description', 'description',
} }
invalid_fields = set(cg_data.keys()) - valid_update_keys invalid_fields = set(sg_snapshot_data.keys()) - valid_update_keys
if invalid_fields: if invalid_fields:
msg = _("The fields %s are invalid or not allowed to be updated.") msg = _("The fields %s are invalid or not allowed to be updated.")
raise exc.HTTPBadRequest(explanation=msg % invalid_fields) raise exc.HTTPBadRequest(explanation=msg % invalid_fields)
try: sg_snapshot = self._get_share_group_snapshot(context, id)
cg = self.cg_api.get_cgsnapshot(context, id) sg_snapshot = self.share_group_api.update_share_group_snapshot(
except exception.NotFound: context, sg_snapshot, sg_snapshot_data)
msg = _("Consistency group snapshot %s not found.") % id return self._view_builder.detail(req, sg_snapshot)
raise exc.HTTPNotFound(explanation=msg)
cg = self.cg_api.update_cgsnapshot(context, cg, cg_data) @wsgi.Controller.api_version('2.31', experimental=True)
return self._view_builder.detail(req, cg)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.response(202) @wsgi.response(202)
@wsgi.Controller.authorize @wsgi.Controller.authorize
def create(self, req, body): def create(self, req, body):
"""Creates a new cgsnapshot.""" """Creates a new share group snapshot."""
context = req.environ['manila.context'] context = req.environ['manila.context']
if not self.is_valid_body(body, 'cgsnapshot'): if not self.is_valid_body(body, 'share_group_snapshot'):
msg = _("'cgsnapshot' is missing from the request body") msg = _("'share_group_snapshot' is missing from the request body.")
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
cgsnapshot = body.get('cgsnapshot') share_group_snapshot = body.get('share_group_snapshot', {})
if not cgsnapshot.get('consistency_group_id'): share_group_id = share_group_snapshot.get('share_group_id')
msg = _("Must supply 'consistency_group_id' attribute.") if not share_group_id:
msg = _("Must supply 'share_group_id' attribute.")
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
if not uuidutils.is_uuid_like(share_group_id):
consistency_group_id = cgsnapshot.get('consistency_group_id') msg = _("The 'share_group_id' attribute must be a uuid.")
if (consistency_group_id and
not uuidutils.is_uuid_like(consistency_group_id)):
msg = _("The 'consistency_group_id' attribute must be a uuid.")
raise exc.HTTPBadRequest(explanation=six.text_type(msg)) raise exc.HTTPBadRequest(explanation=six.text_type(msg))
kwargs = {"consistency_group_id": consistency_group_id} kwargs = {"share_group_id": share_group_id}
if 'name' in share_group_snapshot:
if 'name' in cgsnapshot: kwargs['name'] = share_group_snapshot.get('name')
kwargs['name'] = cgsnapshot.get('name') if 'description' in share_group_snapshot:
if 'description' in cgsnapshot: kwargs['description'] = share_group_snapshot.get('description')
kwargs['description'] = cgsnapshot.get('description')
try: try:
new_snapshot = self.cg_api.create_cgsnapshot(context, **kwargs) new_snapshot = self.share_group_api.create_share_group_snapshot(
except exception.ConsistencyGroupNotFound as e: context, **kwargs)
except exception.ShareGroupNotFound as e:
raise exc.HTTPBadRequest(explanation=six.text_type(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)) raise exc.HTTPConflict(explanation=six.text_type(e))
return self._view_builder.detail(req, dict(new_snapshot.items())) return self._view_builder.detail(req, dict(new_snapshot.items()))
@wsgi.Controller.api_version('2.4', experimental=True) @wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize('get_cgsnapshot') @wsgi.Controller.authorize('get')
def members(self, req, id): def members(self, req, id):
"""Returns a list of cgsnapshot members.""" """Returns a list of share group snapshot members."""
context = req.environ['manila.context'] context = req.environ['manila.context']
search_opts = {} snaps = self.share_group_api.get_all_share_group_snapshot_members(
search_opts.update(req.GET) context, id)
# 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)
limited_list = common.limited(snaps, req) limited_list = common.limited(snaps, req)
@ -201,34 +186,24 @@ class CGSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
return snaps return snaps
def _update(self, *args, **kwargs): def _update(self, *args, **kwargs):
db.cgsnapshot_update(*args, **kwargs) db.share_group_snapshot_update(*args, **kwargs)
def _get(self, *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): 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.Controller.api_version('2.31', 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.action('reset_status') @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) return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', '2.6', experimental=True) @wsgi.Controller.api_version('2.31', 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.action('force_delete') @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) return self._force_delete(req, id, body)
def create_resource(): 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 # License for the specific language governing permissions and limitations
# under the License. # under the License.
"""The consistency groups API."""
from oslo_log import log from oslo_log import log
from oslo_utils import uuidutils from oslo_utils import uuidutils
import six import six
@ -23,155 +21,156 @@ from webob import exc
from manila.api import common from manila.api import common
from manila.api.openstack import wsgi 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 db
from manila import exception from manila import exception
from manila.i18n import _, _LI from manila.i18n import _, _LI
from manila.share import share_types 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__) LOG = log.getLogger(__name__)
class CGController(wsgi.Controller, wsgi.AdminActionsMixin): class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):
"""The Consistency Groups API controller for the OpenStack API.""" """The Share Groups API controller for the OpenStack API."""
resource_name = 'consistency_group' resource_name = 'share_group'
_view_builder_class = share_group_views.CGViewBuilder _view_builder_class = share_group_views.ShareGroupViewBuilder
resource_name = 'consistency_group'
def __init__(self): def __init__(self):
super(CGController, self).__init__() super(ShareGroupController, self).__init__()
self.cg_api = sg_api.API() 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') @wsgi.Controller.authorize('get')
def show(self, req, id): def show(self, req, id):
"""Return data about the given CG.""" """Return data about the given share group."""
context = req.environ['manila.context'] context = req.environ['manila.context']
share_group = self._get_share_group(context, id)
return self._view_builder.detail(req, share_group)
try: @wsgi.Controller.api_version('2.31', experimental=True)
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.authorize @wsgi.Controller.authorize
def delete(self, req, id): def delete(self, req, id):
"""Delete a CG.""" """Delete a share group."""
context = req.environ['manila.context'] context = req.environ['manila.context']
LOG.info(_LI("Delete consistency group with id: %s"), id, LOG.info(_LI("Delete share group with id: %s"), id, context=context)
context=context) share_group = self._get_share_group(context, id)
try: try:
cg = self.cg_api.get(context, id) self.share_group_api.delete(context, share_group)
except exception.NotFound: except exception.InvalidShareGroup as e:
msg = _("Consistency group %s not found.") % id
raise exc.HTTPNotFound(explanation=msg)
try:
self.cg_api.delete(context, cg)
except exception.InvalidConsistencyGroup as e:
raise exc.HTTPConflict(explanation=six.text_type(e)) raise exc.HTTPConflict(explanation=six.text_type(e))
return webob.Response(status_int=202) 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') @wsgi.Controller.authorize('get_all')
def index(self, req): def index(self, req):
"""Returns a summary list of shares.""" """Returns a summary list of share groups."""
return self._get_cgs(req, is_detail=False) 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') @wsgi.Controller.authorize('get_all')
def detail(self, req): def detail(self, req):
"""Returns a detailed list of shares.""" """Returns a detailed list of share groups."""
return self._get_cgs(req, is_detail=True) return self._get_share_groups(req, is_detail=True)
def _get_cgs(self, req, is_detail): def _get_share_groups(self, req, is_detail):
"""Returns a list of shares, transformed through view builder.""" """Returns a list of share groups, transformed through view builder."""
context = req.environ['manila.context'] context = req.environ['manila.context']
search_opts = {} search_opts = {}
search_opts.update(req.GET) 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('limit', None)
search_opts.pop('offset', 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( share_groups = self.share_group_api.get_all(
context, detailed=is_detail, search_opts=search_opts) 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: if is_detail:
cgs = self._view_builder.detail_list(req, limited_list) share_groups = self._view_builder.detail_list(req, limited_list)
else: else:
cgs = self._view_builder.summary_list(req, limited_list) share_groups = self._view_builder.summary_list(req, limited_list)
return cgs return share_groups
@wsgi.Controller.api_version('2.4', experimental=True) @wsgi.Controller.api_version('2.31', experimental=True)
@wsgi.Controller.authorize @wsgi.Controller.authorize
def update(self, req, id, body): def update(self, req, id, body):
"""Update a share.""" """Update a share group."""
context = req.environ['manila.context'] context = req.environ['manila.context']
if not self.is_valid_body(body, 'consistency_group'): if not self.is_valid_body(body, 'share_group'):
msg = _("'consistency_group' is missing from the request body.") msg = _("'share_group' is missing from the request body.")
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
cg_data = body['consistency_group'] share_group_data = body['share_group']
valid_update_keys = { valid_update_keys = {'name', 'description'}
'name', invalid_fields = set(share_group_data.keys()) - valid_update_keys
'description',
}
invalid_fields = set(cg_data.keys()) - valid_update_keys
if invalid_fields: if invalid_fields:
msg = _("The fields %s are invalid or not allowed to be updated.") msg = _("The fields %s are invalid or not allowed to be updated.")
raise exc.HTTPBadRequest(explanation=msg % invalid_fields) raise exc.HTTPBadRequest(explanation=msg % invalid_fields)
try: share_group = self._get_share_group(context, id)
cg = self.cg_api.get(context, id) share_group = self.share_group_api.update(
except exception.NotFound: context, share_group, share_group_data)
msg = _("Consistency group %s not found.") % id return self._view_builder.detail(req, share_group)
raise exc.HTTPNotFound(explanation=msg)
cg = self.cg_api.update(context, cg, cg_data) @wsgi.Controller.api_version('2.31', experimental=True)
return self._view_builder.detail(req, cg)
@wsgi.Controller.api_version('2.4', experimental=True)
@wsgi.response(202) @wsgi.response(202)
@wsgi.Controller.authorize @wsgi.Controller.authorize
def create(self, req, body): def create(self, req, body):
"""Creates a new share.""" """Creates a new share group."""
context = req.environ['manila.context'] context = req.environ['manila.context']
if not self.is_valid_body(body, 'consistency_group'): if not self.is_valid_body(body, 'share_group'):
msg = _("'consistency_group' is missing from the request body.") msg = _("'share_group' is missing from the request body.")
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
cg = body['consistency_group'] share_group = body['share_group']
valid_fields = {
valid_fields = {'name', 'description', 'share_types', 'name',
'source_cgsnapshot_id', 'share_network_id'} 'description',
invalid_fields = set(cg.keys()) - valid_fields '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: if invalid_fields:
msg = _("The fields %s are invalid.") % invalid_fields msg = _("The fields %s are invalid.") % invalid_fields
raise exc.HTTPBadRequest(explanation=msg) 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 " msg = _("Cannot supply both 'share_types' and "
"'source_cgsnapshot_id' attributes.") "'source_share_group_snapshot_id' attributes.")
raise exc.HTTPBadRequest(explanation=msg) 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() default_share_type = share_types.get_default_share_type()
if default_share_type: if default_share_type:
cg['share_types'] = [default_share_type['id']] share_group['share_types'] = [default_share_type['id']]
else: else:
msg = _("Must specify at least one share type as a default " msg = _("Must specify at least one share type as a default "
"share type has not been configured.") "share type has not been configured.")
@ -179,76 +178,94 @@ class CGController(wsgi.Controller, wsgi.AdminActionsMixin):
kwargs = {} kwargs = {}
if 'name' in cg: if 'name' in share_group:
kwargs['name'] = cg.get('name') kwargs['name'] = share_group.get('name')
if 'description' in cg: if 'description' in share_group:
kwargs['description'] = cg.get('description') kwargs['description'] = share_group.get('description')
_share_types = cg.get('share_types') _share_types = share_group.get('share_types')
if _share_types: if _share_types:
if not all([uuidutils.is_uuid_like(st) for st in _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") msg = _("The 'share_types' attribute must be a list of uuids")
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
kwargs['share_type_ids'] = _share_types 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 " msg = _("Cannot supply both 'share_network_id' and "
"'source_cgsnapshot_id' attributes as the share network " "'source_share_group_snapshot_id' attributes as the share "
"is inherited from the source.") "network is inherited from the source.")
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
if 'source_cgsnapshot_id' in cg: if 'source_share_group_snapshot_id' in share_group:
source_cgsnapshot_id = cg.get('source_cgsnapshot_id') source_share_group_snapshot_id = share_group.get(
if not uuidutils.is_uuid_like(source_cgsnapshot_id): 'source_share_group_snapshot_id')
msg = _("The 'source_cgsnapshot_id' attribute must be a uuid.") 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)) raise exc.HTTPBadRequest(explanation=six.text_type(msg))
kwargs['source_cgsnapshot_id'] = source_cgsnapshot_id kwargs['source_share_group_snapshot_id'] = (
source_share_group_snapshot_id)
elif 'share_network_id' in cg: elif 'share_network_id' in share_group:
share_network_id = cg.get('share_network_id') share_network_id = share_group.get('share_network_id')
if not uuidutils.is_uuid_like(share_network_id): if not uuidutils.is_uuid_like(share_network_id):
msg = _("The 'share_network_id' attribute must be a uuid.") msg = _("The 'share_network_id' attribute must be a uuid.")
raise exc.HTTPBadRequest(explanation=six.text_type(msg)) raise exc.HTTPBadRequest(explanation=six.text_type(msg))
kwargs['share_network_id'] = share_network_id 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: try:
new_cg = self.cg_api.create(context, **kwargs) new_share_group = self.share_group_api.create(context, **kwargs)
except exception.InvalidCGSnapshot as e: except exception.InvalidShareGroupSnapshot as e:
raise exc.HTTPConflict(explanation=six.text_type(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)) 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): def _update(self, *args, **kwargs):
db.consistency_group_update(*args, **kwargs) db.share_group_update(*args, **kwargs)
def _get(self, *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): 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) # Delete all shares in share group
@wsgi.action('os-reset_status') for share in db.get_all_shares_by_share_group(context, resource['id']):
def cg_reset_status_legacy(self, req, id, body): db.share_delete(context, share['id'])
return self._reset_status(req, id, body)
@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') @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) return self._reset_status(req, id, body)
@wsgi.Controller.api_version('2.4', '2.6', experimental=True) @wsgi.Controller.api_version('2.31', 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.action('force_delete') @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) return self._force_delete(req, id, body)
def create_resource(): def create_resource():
return wsgi.Resource(CGController()) return wsgi.Resource(ShareGroupController())

View File

@ -79,12 +79,12 @@ class ShareNetworkController(wsgi.Controller):
LOG.error(msg) LOG.error(msg)
raise exc.HTTPConflict(explanation=msg) raise exc.HTTPConflict(explanation=msg)
# NOTE(ameade): Do not allow deletion of share network used by CG # NOTE(ameade): Do not allow deletion of share network used by share
cg_count = db_api.count_consistency_groups_in_share_network(context, # group
id) sg_count = db_api.count_share_groups_in_share_network(context, id)
if cg_count: if sg_count:
msg = _("Can not delete share network %(id)s, it has %(len)s " 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) LOG.error(msg)
raise exc.HTTPConflict(explanation=msg) raise exc.HTTPConflict(explanation=msg)

View File

@ -163,22 +163,22 @@ class ShareController(shares.ShareMixin,
return data return data
@wsgi.Controller.api_version("2.0", "2.3") @wsgi.Controller.api_version("2.31")
def create(self, req, body): def create(self, req, body):
# Remove consistency group attributes return self._create(
body.get('share', {}).pop('consistency_group_id', None) req, body, check_create_share_from_snapshot_support=True)
share = self._create(req, body)
return share
@wsgi.Controller.api_version("2.4", "2.23") # noqa @wsgi.Controller.api_version("2.24", "2.30") # noqa
def create(self, req, body): # pylint: disable=E0102
return self._create(req, body)
@wsgi.Controller.api_version("2.24") # noqa
def create(self, req, body): # pylint: disable=E0102 def create(self, req, body): # pylint: disable=E0102
body.get('share', {}).pop('share_group_id', None)
return self._create(req, body, return self._create(req, body,
check_create_share_from_snapshot_support=True) 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.Controller.api_version('2.0', '2.6')
@wsgi.action('os-reset_status') @wsgi.action('os-reset_status')
def share_reset_status_legacy(self, req, id, body): def share_reset_status_legacy(self, req, id, body):

View File

@ -13,23 +13,21 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
"""The consistency groups snapshot API."""
from manila.api import common from manila.api import common
class CGSnapshotViewBuilder(common.ViewBuilder): class ShareGroupSnapshotViewBuilder(common.ViewBuilder):
"""Model a cgsnapshot API response as a python dictionary.""" """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): def summary_list(self, request, group_snaps):
"""Show a list of cgsnapshots without many details.""" """Show a list of share_group_snapshots without many details."""
return self._list_view(self.summary, request, cgs) return self._list_view(self.summary, request, group_snaps)
def detail_list(self, request, cgs): def detail_list(self, request, group_snaps):
"""Detailed view of a list of cgsnapshots.""" """Detailed view of a list of share_group_snapshots."""
return self._list_view(self.detail, request, cgs) return self._list_view(self.detail, request, group_snaps)
def member_list(self, request, members): def member_list(self, request, members):
members_list = [] members_list = []
@ -40,56 +38,72 @@ class CGSnapshotViewBuilder(common.ViewBuilder):
'size': member.get('size'), 'size': member.get('size'),
'share_protocol': member.get('share_proto'), 'share_protocol': member.get('share_proto'),
'project_id': member.get('project_id'), 'project_id': member.get('project_id'),
'share_type_id': member.get('share_type_id'), 'share_group_snapshot_id': member.get(
'cgsnapshot_id': member.get('cgsnapshot_id'), 'share_group_snapshot_id'),
'share_id': member.get('share_id'), 'share_id': member.get('share_id'),
} }
members_list.append(member_dict) members_list.append(member_dict)
members_links = self._get_collection_links(request, members_links = self._get_collection_links(
members, request, members, "share_group_snapshot_id")
'cgsnapshot_id') members_dict = {"share_group_snapshot_members": members_list}
members_dict = dict(cgsnapshot_members=members_list)
if members_links: if members_links:
members_dict['cgsnapshot_members_links'] = members_links members_dict["share_group_snapshot_members_links"] = members_links
return members_dict return members_dict
def summary(self, request, cg): def summary(self, request, share_group_snap):
"""Generic, non-detailed view of a cgsnapshot.""" """Generic, non-detailed view of a share group snapshot."""
return { return {
'cgsnapshot': { 'share_group_snapshot': {
'id': cg.get('id'), 'id': share_group_snap.get('id'),
'name': cg.get('name'), 'name': share_group_snap.get('name'),
'links': self._get_links(request, cg['id']) 'links': self._get_links(request, share_group_snap['id']),
} }
} }
def detail(self, request, cg): def detail(self, request, share_group_snap):
"""Detailed view of a single cgsnapshot.""" """Detailed view of a single share group snapshot."""
cg_dict = {
'id': cg.get('id'), members = self._format_member_list(
'name': cg.get('name'), share_group_snap.get('share_group_snapshot_members', []))
'created_at': cg.get('created_at'),
'status': cg.get('status'), share_group_snap_dict = {
'description': cg.get('description'), 'id': share_group_snap.get('id'),
'project_id': cg.get('project_id'), 'name': share_group_snap.get('name'),
'consistency_group_id': cg.get('consistency_group_id'), 'created_at': share_group_snap.get('created_at'),
'links': self._get_links(request, cg['id']), '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): def _list_view(self, func, request, snaps):
"""Provide a view for a list of cgsnapshots.""" """Provide a view for a list of share group snapshots."""
snap_list = [func(request, snap)['cgsnapshot'] snap_list = [func(request, snap)["share_group_snapshot"]
for snap in snaps] for snap in snaps]
snaps_links = self._get_collection_links(request, snaps_links = self._get_collection_links(request,
snaps, snaps,
self._collection_name) self._collection_name)
snaps_dict = dict(cgsnapshots=snap_list) snaps_dict = {"share_group_snapshots": snap_list}
if snaps_links: if snaps_links:
snaps_dict['cgsnapshot_links'] = snaps_links snaps_dict["share_group_snapshot_links"] = snaps_links
return snaps_dict 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 # License for the specific language governing permissions and limitations
# under the License. # under the License.
"""The consistency groups API."""
from manila.api import common from manila.api import common
class CGViewBuilder(common.ViewBuilder): class ShareGroupViewBuilder(common.ViewBuilder):
"""Model a consistency group API response as a python dictionary.""" """Model a share group API response as a python dictionary."""
_collection_name = 'consistency_groups' _collection_name = 'share_groups'
def summary_list(self, request, cgs): def summary_list(self, request, share_groups):
"""Show a list of consistency groups without many details.""" """Show a list of share groups without many details."""
return self._list_view(self.summary, request, cgs) return self._list_view(self.summary, request, share_groups)
def detail_list(self, request, cgs): def detail_list(self, request, share_groups):
"""Detailed view of a list of consistency groups.""" """Detailed view of a list of share groups."""
return self._list_view(self.detail, request, cgs) return self._list_view(self.detail, request, share_groups)
def summary(self, request, cg): def summary(self, request, share_group):
"""Generic, non-detailed view of a consistency group.""" """Generic, non-detailed view of a share group."""
return { return {
'consistency_group': { 'share_group': {
'id': cg.get('id'), 'id': share_group.get('id'),
'name': cg.get('name'), 'name': share_group.get('name'),
'links': self._get_links(request, cg['id']) 'links': self._get_links(request, share_group['id'])
} }
} }
def detail(self, request, cg): def detail(self, request, share_group):
"""Detailed view of a single consistency group.""" """Detailed view of a single share group."""
context = request.environ['manila.context'] context = request.environ['manila.context']
cg_dict = { share_group_dict = {
'id': cg.get('id'), 'id': share_group.get('id'),
'name': cg.get('name'), 'name': share_group.get('name'),
'created_at': cg.get('created_at'), 'created_at': share_group.get('created_at'),
'status': cg.get('status'), 'status': share_group.get('status'),
'description': cg.get('description'), 'description': share_group.get('description'),
'project_id': cg.get('project_id'), 'project_id': share_group.get('project_id'),
'host': cg.get('host'), 'host': share_group.get('host'),
'source_cgsnapshot_id': cg.get('source_cgsnapshot_id'), 'share_group_type_id': share_group.get('share_group_type_id'),
'share_network_id': cg.get('share_network_id'), 'source_share_group_snapshot_id': share_group.get(
'share_types': [st['share_type_id'] for st in cg.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')], 'share_types')],
'links': self._get_links(request, cg['id']), 'links': self._get_links(request, share_group['id']),
} }
if context.is_admin: if context.is_admin:
cg_dict['share_server_id'] = cg.get('share_server_id') share_group_dict['share_server_id'] = share_group.get(
return {'consistency_group': cg_dict} 'share_server_id')
return {'share_group': share_group_dict}
def _list_view(self, func, request, shares): def _list_view(self, func, request, shares):
"""Provide a view for a list of consistency groups.""" """Provide a view for a list of share groups."""
cg_list = [func(request, share)['consistency_group'] share_group_list = [
for share in shares] func(request, share)['share_group']
cgs_links = self._get_collection_links(request, for share in shares
shares, ]
self._collection_name) share_groups_links = self._get_collection_links(
cgs_dict = dict(consistency_groups=cg_list) request, shares, self._collection_name)
share_groups_dict = {"share_groups": share_group_list}
if cgs_links: if share_groups_links:
cgs_dict['consistency_groups_links'] = cgs_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' _collection_name = 'shares'
_detail_version_modifiers = [ _detail_version_modifiers = [
"add_snapshot_support_field", "add_snapshot_support_field",
"add_consistency_group_fields",
"add_task_state_field", "add_task_state_field",
"modify_share_type_field", "modify_share_type_field",
"remove_export_locations", "remove_export_locations",
@ -33,6 +32,7 @@ class ViewBuilder(common.ViewBuilder):
"add_create_share_from_snapshot_support_field", "add_create_share_from_snapshot_support_field",
"add_revert_to_snapshot_support_field", "add_revert_to_snapshot_support_field",
"translate_access_rules_status", "translate_access_rules_status",
"add_share_group_fields",
] ]
def summary_list(self, request, shares): def summary_list(self, request, shares):
@ -103,13 +103,6 @@ class ViewBuilder(common.ViewBuilder):
def add_snapshot_support_field(self, context, share_dict, share): def add_snapshot_support_field(self, context, share_dict, share):
share_dict['snapshot_support'] = share.get('snapshot_support') 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") @common.ViewBuilder.versioned_method("2.5")
def add_task_state_field(self, context, share_dict, share): def add_task_state_field(self, context, share_dict, share):
share_dict['task_state'] = share.get('task_state') share_dict['task_state'] = share.get('task_state')
@ -162,6 +155,13 @@ class ViewBuilder(common.ViewBuilder):
constants.SHARE_INSTANCE_RULES_SYNCING): constants.SHARE_INSTANCE_RULES_SYNCING):
share_dict['access_rules_status'] = constants.STATUS_OUT_OF_SYNC 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): def _list_view(self, func, request, shares):
"""Provide a view for a list of shares.""" """Provide a view for a list of shares."""
shares_list = [func(request, share)['share'] for share in 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.'), help='Availability zone of this node.'),
cfg.StrOpt('default_share_type', cfg.StrOpt('default_share_type',
help='Default share type to use.'), help='Default share type to use.'),
cfg.StrOpt('default_share_group_type',
help='Default share group type to use.'),
cfg.ListOpt('memcached_servers', cfg.ListOpt('memcached_servers',
help='Memcached servers or None for in process cache.'), help='Memcached servers or None for in process cache.'),
cfg.StrOpt('share_usage_audit_period', 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) return IMPL.share_instances_get_all_by_share(context, share_id)
def 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 cg.""" """Returns list of share instances that belong to given share group."""
return IMPL.share_instances_get_all_by_consistency_group_id(context, cg_id) 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, def share_get_all_by_share_group_id(context, share_group_id,
filters=None, sort_key=None, filters=None, sort_key=None,
sort_dir=None): sort_dir=None):
"""Returns all shares with given project ID and CG id.""" """Returns all shares with given project ID and share group id."""
return IMPL.share_get_all_by_consistency_group_id( return IMPL.share_get_all_by_share_group_id(
context, cg_id, filters=filters, context, share_group_id, filters=filters,
sort_key=sort_key, sort_dir=sort_dir) 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): def share_group_get(context, share_group_id):
"""Get a consistency group or raise if it does not exist.""" """Get a share group or raise if it does not exist."""
return IMPL.consistency_group_get(context, consistency_group_id) return IMPL.share_group_get(context, share_group_id)
def consistency_group_get_all(context, detailed=True): def share_group_get_all(context, detailed=True, filters=None, sort_key=None,
"""Get all consistency groups.""" sort_dir=None):
return IMPL.consistency_group_get_all(context, detailed=detailed) """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): def share_group_get_all_by_host(context, host, detailed=True, filters=None,
"""Create a consistency group from the values dictionary.""" sort_key=None, sort_dir=None):
return IMPL.consistency_group_create(context, values) """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): def share_group_create(context, values):
"""Get all consistency groups associated with a share server.""" """Create a share group from the values dictionary."""
return IMPL.consistency_group_get_all_by_share_server(context, return IMPL.share_group_create(context, values)
share_server_id)
def consistency_group_get_all_by_project(context, project_id, detailed=True): def share_group_get_all_by_share_server(context, share_server_id,
"""Get all consistency groups belonging to a project.""" filters=None, sort_key=None,
return IMPL.consistency_group_get_all_by_project(context, project_id, sort_dir=None):
detailed=detailed) """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): def share_group_get_all_by_project(context, project_id, detailed=True,
"""Set the given properties on a consistency group and update it. 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): def share_group_destroy(context, share_group_id):
"""Destroy the consistency group or raise if it does not exist.""" """Destroy the share group or raise if it does not exist."""
return IMPL.consistency_group_destroy(context, consistency_group_id) return IMPL.share_group_destroy(context, share_group_id)
def 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 cg.""" """Returns the number of undeleted shares with the specified group."""
return IMPL.count_shares_in_consistency_group(context, return IMPL.count_shares_in_share_group(context, share_group_id)
consistency_group_id)
def count_cgsnapshots_in_consistency_group(context, consistency_group_id): def get_all_shares_by_share_group(context, share_group_id):
"""Returns the number of undeleted cgsnapshots with the specified cg.""" return IMPL.get_all_shares_by_share_group(context, share_group_id)
return IMPL.count_cgsnapshots_in_consistency_group(context,
consistency_group_id)
def count_consistency_groups_in_share_network(context, share_network_id, def count_share_group_snapshots_in_share_group(context, share_group_id):
session=None): """Returns the number of sg snapshots with the specified share group."""
"""Returns the number of undeleted cgs with the specified share network.""" return IMPL.count_share_group_snapshots_in_share_group(
return IMPL.count_consistency_groups_in_share_network(context, context, share_group_id)
share_network_id)
def count_cgsnapshot_members_in_share(context, share_id, session=None): def count_share_groups_in_share_network(context, share_network_id,
"""Returns the number of cgsnapshot members linked to the share.""" session=None):
return IMPL.count_cgsnapshot_members_in_share(context, share_id) """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): def count_share_group_snapshot_members_in_share(context, share_id,
"""Get a cgsnapshot.""" session=None):
return IMPL.cgsnapshot_get(context, cgsnapshot_id) """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): def share_group_snapshot_get(context, share_group_snapshot_id):
"""Get all cgsnapshots.""" """Get a share group snapshot."""
return IMPL.cgsnapshot_get_all(context, detailed=detailed) return IMPL.share_group_snapshot_get(context, share_group_snapshot_id)
def cgsnapshot_get_all_by_project(context, project_id, detailed=True): def share_group_snapshot_get_all(context, detailed=True, filters=None,
"""Get all cgsnapshots belonging to a project.""" sort_key=None, sort_dir=None):
return IMPL.cgsnapshot_get_all_by_project(context, project_id, """Get all share group snapshots."""
detailed=detailed) 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): def share_group_snapshot_get_all_by_project(context, project_id, detailed=True,
"""Create a cgsnapshot from the values dictionary.""" filters=None, sort_key=None,
return IMPL.cgsnapshot_create(context, values) 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): def share_group_snapshot_create(context, values):
"""Set the given properties on a cgsnapshot and update it. """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): def share_group_snapshot_destroy(context, share_group_snapshot_id):
"""Destroy the cgsnapshot or raise if it does not exist.""" """Destroy the share_group_snapshot or raise if it does not exist."""
return IMPL.cgsnapshot_destroy(context, cgsnapshot_id) return IMPL.share_group_snapshot_destroy(context, share_group_snapshot_id)
def cgsnapshot_members_get_all(context, cgsnapshot_id): def share_group_snapshot_members_get_all(context, share_group_snapshot_id):
"""Return the members of a cgsnapshot.""" """Return the members of a share group snapshot."""
return IMPL.cgsnapshot_members_get_all(context, cgsnapshot_id) return IMPL.share_group_snapshot_members_get_all(
context, share_group_snapshot_id)
def cgsnapshot_member_create(context, values): def share_group_snapshot_member_create(context, values):
"""Create a cgsnapshot member from the values dictionary.""" """Create a share group snapshot member from the values dictionary."""
return IMPL.cgsnapshot_member_create(context, values) return IMPL.share_group_snapshot_member_create(context, values)
def cgsnapshot_member_update(context, member_id, values): def share_group_snapshot_member_update(context, member_id, values):
"""Set the given properties on a cgsnapshot member and update it. """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. :raises: InvalidParameterValue if age_in_days is incorrect.
""" """
return IMPL.purge_deleted_records(context, age_in_days=age_in_days) 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) replication_type = Column(String(255), nullable=True)
share_proto = Column(String(255)) share_proto = Column(String(255))
is_public = Column(Boolean, default=False) is_public = Column(Boolean, default=False)
consistency_group_id = Column(String(36), share_group_id = Column(String(36),
ForeignKey('consistency_groups.id'), ForeignKey('share_groups.id'),
nullable=True) 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)) task_state = Column(String(255))
instances = orm.relationship( instances = orm.relationship(
"ShareInstance", "ShareInstance",
@ -335,8 +335,8 @@ class ShareInstance(BASE, ManilaBase):
_proxified_properties = ('user_id', 'project_id', 'size', _proxified_properties = ('user_id', 'project_id', 'size',
'display_name', 'display_description', 'display_name', 'display_description',
'snapshot_id', 'share_proto', 'is_public', 'snapshot_id', 'share_proto', 'is_public',
'consistency_group_id', 'replication_type', 'share_group_id', 'replication_type',
'source_cgsnapshot_member_id') 'source_share_group_snapshot_member_id')
def set_share_data(self, share): def set_share_data(self, share):
for share_property in self._proxified_properties: for share_property in self._proxified_properties:
@ -832,10 +832,10 @@ class ShareServer(BASE, ManilaBase):
'ShareServer.id == ShareInstance.share_server_id,' 'ShareServer.id == ShareInstance.share_server_id,'
'ShareInstance.deleted == "False")') 'ShareInstance.deleted == "False")')
consistency_groups = orm.relationship( share_groups = orm.relationship(
"ConsistencyGroup", backref='share_server', primaryjoin='and_(' "ShareGroup", backref='share_server', primaryjoin='and_('
'ShareServer.id == ConsistencyGroup.share_server_id,' 'ShareServer.id == ShareGroup.share_server_id,'
'ConsistencyGroup.deleted == "False")') 'ShareGroup.deleted == "False")')
_backend_details = orm.relationship( _backend_details = orm.relationship(
"ShareServerBackendDetails", "ShareServerBackendDetails",
@ -913,95 +913,168 @@ class AvailabilityZone(BASE, ManilaBase):
name = Column(String(255), nullable=False) name = Column(String(255), nullable=False)
class ConsistencyGroup(BASE, ManilaBase): class ShareGroupTypes(BASE, ManilaBase):
"""Represents a consistency group.""" """Represent possible share group types of shares offered."""
__tablename__ = 'consistency_groups' __tablename__ = "share_group_types"
__table_args__ = (
schema.UniqueConstraint(
"name", "deleted", name="uniq_share_group_type_name"),
)
id = Column(String(36), primary_key=True) 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) user_id = Column(String(255), nullable=False)
project_id = Column(String(255), nullable=False) project_id = Column(String(255), nullable=False)
deleted = Column(String(36), default='False') deleted = Column(String(36), default='False')
host = Column(String(255)) host = Column(String(255))
name = Column(String(255)) name = Column(String(255))
description = Column(String(255)) description = Column(String(255))
status = Column(String(255)) status = Column(String(255))
source_cgsnapshot_id = Column(String(36)) source_share_group_snapshot_id = Column(String(36))
share_network_id = Column(String(36), ForeignKey('share_networks.id'), share_network_id = Column(
nullable=True) String(36), ForeignKey('share_networks.id'), nullable=True)
share_server_id = Column(String(36), ForeignKey('share_servers.id'), share_server_id = Column(
nullable=True) 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): class ShareGroupTypeProjects(BASE, ManilaBase):
"""Represents a cgsnapshot.""" """Represent projects associated share group types."""
__tablename__ = 'cgsnapshots' __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) id = Column(String(36), primary_key=True)
share_group_id = Column(String(36), ForeignKey('share_groups.id'))
consistency_group_id = Column(String(36),
ForeignKey('consistency_groups.id'))
user_id = Column(String(255), nullable=False) user_id = Column(String(255), nullable=False)
project_id = Column(String(255), nullable=False) project_id = Column(String(255), nullable=False)
deleted = Column(String(36), default='False') deleted = Column(String(36), default='False')
name = Column(String(255)) name = Column(String(255))
description = Column(String(255)) description = Column(String(255))
status = Column(String(255)) status = Column(String(255))
share_group = orm.relationship(
consistency_group = orm.relationship( ShareGroup,
ConsistencyGroup, backref="snapshots",
backref="cgsnapshots", foreign_keys=share_group_id,
foreign_keys=consistency_group_id,
primaryjoin=('and_(' primaryjoin=('and_('
'CGSnapshot.consistency_group_id == ConsistencyGroup.id,' 'ShareGroupSnapshot.share_group_id == ShareGroup.id,'
'CGSnapshot.deleted == "False")') 'ShareGroupSnapshot.deleted == "False")')
) )
class ConsistencyGroupShareTypeMapping(BASE, ManilaBase): class ShareGroupTypeShareTypeMapping(BASE, ManilaBase):
"""Represents the share types in a consistency group.""" """Represents the share types supported by a share group type."""
__tablename__ = 'consistency_group_share_type_mappings' __tablename__ = 'share_group_type_share_type_mappings'
id = Column(String(36), primary_key=True) id = Column(String(36), primary_key=True)
deleted = Column(String(36), default='False') deleted = Column(String(36), default='False')
consistency_group_id = Column(String(36), share_group_type_id = Column(
ForeignKey('consistency_groups.id'), String(36), ForeignKey('share_group_types.id'), nullable=False)
nullable=False) share_type_id = Column(
share_type_id = Column(String(36), String(36), ForeignKey('share_types.id'), nullable=False)
ForeignKey('share_types.id'), share_group_type = orm.relationship(
nullable=False) ShareGroupTypes,
consistency_group = orm.relationship(
ConsistencyGroup,
backref="share_types", backref="share_types",
foreign_keys=consistency_group_id, foreign_keys=share_group_type_id,
primaryjoin=('and_(' primaryjoin=('and_('
'ConsistencyGroupShareTypeMapping.consistency_group_id ' 'ShareGroupTypeShareTypeMapping.share_group_type_id '
'== ConsistencyGroup.id,' '== ShareGroupTypes.id,'
'ConsistencyGroupShareTypeMapping.deleted == "False")') 'ShareGroupTypeShareTypeMapping.deleted == "False")')
) )
class CGSnapshotMember(BASE, ManilaBase): class ShareGroupShareTypeMapping(BASE, ManilaBase):
"""Represents the share snapshots in a consistency group snapshot.""" """Represents the share types in a share group."""
__tablename__ = 'cgsnapshot_members' __tablename__ = 'share_group_share_type_mappings'
id = Column(String(36), primary_key=True) 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_id = Column(String(36), ForeignKey('shares.id'))
share_instance_id = Column(String(36), ForeignKey('share_instances.id')) share_instance_id = Column(String(36), ForeignKey('share_instances.id'))
size = Column(Integer) size = Column(Integer)
status = Column(String(255)) status = Column(String(255))
share_proto = 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)) user_id = Column(String(255))
project_id = Column(String(255)) project_id = Column(String(255))
deleted = Column(String(36), default='False') deleted = Column(String(36), default='False')
share_group_snapshot = orm.relationship(
cgsnapshot = orm.relationship( ShareGroupSnapshot,
CGSnapshot, backref="share_group_snapshot_members",
backref="cgsnapshot_members", foreign_keys=share_group_snapshot_id,
foreign_keys=cgsnapshot_id, primaryjoin='ShareGroupSnapshot.id == '
primaryjoin='CGSnapshot.id == CGSnapshotMember.cgsnapshot_id') 'ShareGroupSnapshotMember.share_group_snapshot_id')
def register_models(): def register_models():

View File

@ -478,6 +478,10 @@ class ShareSnapshotNotSupported(ManilaException):
message = _("Share %(share_name)s does not support snapshots.") 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): class ShareSnapshotIsBusy(ManilaException):
message = _("Deleting snapshot %(snapshot_name)s that has " message = _("Deleting snapshot %(snapshot_name)s that has "
"dependent shares.") "dependent shares.")
@ -531,6 +535,10 @@ class InvalidShareType(Invalid):
message = _("Invalid share type: %(reason)s.") message = _("Invalid share type: %(reason)s.")
class InvalidShareGroupType(Invalid):
message = _("Invalid share group type: %(reason)s.")
class InvalidExtraSpec(Invalid): class InvalidExtraSpec(Invalid):
message = _("Invalid extra_spec: %(reason)s.") message = _("Invalid extra_spec: %(reason)s.")
@ -547,21 +555,40 @@ class ShareTypeNotFound(NotFound):
message = _("Share type %(share_type_id)s could not be found.") 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): class ShareTypeAccessNotFound(NotFound):
message = _("Share type access not found for %(share_type_id)s / " message = _("Share type access not found for %(share_type_id)s / "
"%(project_id)s combination.") "%(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): class ShareTypeNotFoundByName(ShareTypeNotFound):
message = _("Share type with name %(share_type_name)s " message = _("Share type with name %(share_type_name)s "
"could not be found.") "could not be found.")
class ShareGroupTypeNotFoundByName(ShareTypeNotFound):
message = _("Share group type with name %(type_name)s "
"could not be found.")
class ShareTypeExtraSpecsNotFound(NotFound): class ShareTypeExtraSpecsNotFound(NotFound):
message = _("Share Type %(share_type_id)s has no extra specs with " message = _("Share Type %(share_type_id)s has no extra specs with "
"key %(extra_specs_key)s.") "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): class ShareTypeInUse(ManilaException):
message = _("Share Type %(share_type_id)s deletion is not allowed with " message = _("Share Type %(share_type_id)s deletion is not allowed with "
"shares present with the type.") "shares present with the type.")
@ -571,20 +598,39 @@ class IPAddressInUse(InUse):
message = _("IP address %(ip)s is already used.") 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): class ShareTypeExists(ManilaException):
message = _("Share Type %(id)s already exists.") message = _("Share Type %(id)s already exists.")
class ShareGroupTypeExists(ManilaException):
message = _("Share group type %(type_id)s already exists.")
class ShareTypeAccessExists(ManilaException): class ShareTypeAccessExists(ManilaException):
message = _("Share type access for %(share_type_id)s / " message = _("Share type access for %(share_type_id)s / "
"%(project_id)s combination already exists.") "%(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): class ShareTypeCreateFailed(ManilaException):
message = _("Cannot create share_type with " message = _("Cannot create share_type with "
"name %(name)s and specs %(extra_specs)s.") "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): class ManageExistingShareTypeMismatch(ManilaException):
message = _("Manage existing share failed due to share type mismatch: " message = _("Manage existing share failed due to share type mismatch: "
"%(reason)s") "%(reason)s")
@ -746,27 +792,27 @@ class HNASNothingToCloneException(ManilaException):
message = _("HNAS Nothing To Clone Exception: %(msg)s") message = _("HNAS Nothing To Clone Exception: %(msg)s")
# ConsistencyGroup # ShareGroup
class ConsistencyGroupNotFound(NotFound): class ShareGroupNotFound(NotFound):
message = _("ConsistencyGroup %(consistency_group_id)s could not be " 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.") "found.")
class CGSnapshotNotFound(NotFound): class InvalidShareGroup(Invalid):
message = _("Consistency group snapshot %(cgsnapshot_id)s could not be " message = _("Invalid share group: %(reason)s")
"found.")
class CGSnapshotMemberNotFound(NotFound): class InvalidShareGroupSnapshot(Invalid):
message = _("CG snapshot %(member_id)s could not be found.") message = _("Invalid share group snapshot: %(reason)s")
class InvalidConsistencyGroup(Invalid):
message = _("Invalid ConsistencyGroup: %(reason)s")
class InvalidCGSnapshot(Invalid):
message = _("Invalid CGSnapshot: %(reason)s")
class DriverNotInitialized(ManilaException): 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) return db.share_replica_update(context, share_replica_id, values)
def cg_update_db(context, cg_id, host): def share_group_update_db(context, share_group_id, host):
'''Set the host and set the updated_at field of a consistency group. '''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() now = timeutils.utcnow()
values = {'host': host, 'updated_at': now} 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): class Scheduler(object):
@ -109,12 +109,12 @@ class Scheduler(object):
"""Must override schedule method for scheduler to work.""" """Must override schedule method for scheduler to work."""
raise NotImplementedError(_("Must implement schedule_create_share")) raise NotImplementedError(_("Must implement schedule_create_share"))
def schedule_create_consistency_group(self, context, group_id, def schedule_create_share_group(self, context, share_group_id,
request_spec, request_spec,
filter_properties): filter_properties):
"""Must override schedule method for scheduler to work.""" """Must override schedule method for scheduler to work."""
raise NotImplementedError(_( raise NotImplementedError(_(
"Must implement schedule_create_consistency_group")) "Must implement schedule_create_share_group"))
def get_pools(self, context, filters): def get_pools(self, context, filters):
"""Must override schedule method for scheduler to work.""" """Must override schedule method for scheduler to work."""

View File

@ -169,17 +169,7 @@ class FilterScheduler(base.Scheduler):
config_options = self._get_configuration_options() config_options = self._get_configuration_options()
# NOTE(ameade): If a consistency group is specified, pass the share_group = request_spec.get('share_group')
# 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
# NOTE(gouthamr): If 'active_replica_host' is present in the request # NOTE(gouthamr): If 'active_replica_host' is present in the request
# spec, pass that host's 'replication_domain' to the # spec, pass that host's 'replication_domain' to the
@ -202,8 +192,7 @@ class FilterScheduler(base.Scheduler):
'config_options': config_options, 'config_options': config_options,
'share_type': share_type, 'share_type': share_type,
'resource_type': resource_type, 'resource_type': resource_type,
'cg_support': cg_support, 'share_group': share_group,
'consistency_group': cg,
'replication_domain': replication_domain, 'replication_domain': replication_domain,
}) })
@ -318,27 +307,24 @@ class FilterScheduler(base.Scheduler):
filter_properties['user_id'] = shr.get('user_id') filter_properties['user_id'] = shr.get('user_id')
filter_properties['metadata'] = shr.get('metadata') filter_properties['metadata'] = shr.get('metadata')
def schedule_create_consistency_group(self, context, group_id, def schedule_create_share_group(self, context, share_group_id,
request_spec, request_spec, filter_properties):
filter_properties):
LOG.info(_LI("Scheduling consistency group %s") % group_id) LOG.info(_LI("Scheduling share group %s.") % share_group_id)
host = self._get_best_host_for_share_group(context, request_spec)
host = self._get_best_host_for_consistency_group(
context,
request_spec)
if not host: 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) raise exception.NoValidHost(reason=msg)
msg = _LI("Chose host %(host)s for create_consistency_group %(cg_id)s") msg = _LI("Chose host %(host)s for create_share_group %(group)s.")
LOG.info(msg % {'host': host, 'cg_id': group_id}) 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, self.share_rpcapi.create_share_group(
updated_group, host) context, updated_share_group, host)
def _get_weighted_hosts_for_share_type(self, context, request_spec, def _get_weighted_hosts_for_share_type(self, context, request_spec,
share_type): share_type):
@ -363,9 +349,6 @@ class FilterScheduler(base.Scheduler):
if extra_spec is not None: if extra_spec is not None:
share_type['extra_specs'][spec_name] = ( share_type['extra_specs'][spec_name] = (
"<is> %s" % extra_spec) "<is> %s" % extra_spec)
# Only allow pools that support consistency groups
share_type['extra_specs']['consistency_group_support'] = (
"<or> host <or> pool")
filter_properties = { filter_properties = {
'context': context, 'context': context,
@ -393,8 +376,40 @@ class FilterScheduler(base.Scheduler):
return weighed_hosts return weighed_hosts
def _get_weighted_candidates_cg(self, context, request_spec): def _get_weighted_hosts_for_share_group_type(self, context, request_spec,
"""Finds hosts that support the consistency group. 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, Returns a list of hosts that meet the required specs,
ordered by their fitness. ordered by their fitness.
@ -410,7 +425,7 @@ class FilterScheduler(base.Scheduler):
elevated, request_spec, share_type) elevated, request_spec, share_type)
# NOTE(ameade): Take the intersection of hosts so we have one that # 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: if iteration_count == 0:
weighed_hosts = temp_weighed_hosts weighed_hosts = temp_weighed_hosts
else: else:
@ -423,10 +438,22 @@ class FilterScheduler(base.Scheduler):
if not weighed_hosts: if not weighed_hosts:
return [] 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 return weighed_hosts
def _get_best_host_for_consistency_group(self, context, request_spec): def _get_best_host_for_share_group(self, context, request_spec):
weighed_hosts = self._get_weighted_candidates_cg( weighed_hosts = self._get_weighted_candidates_share_group(
context, context,
request_spec) request_spec)

View File

@ -33,7 +33,7 @@ class CapacityFilter(base_host.BaseHostFilter):
def host_passes(self, host_state, filter_properties): def host_passes(self, host_state, filter_properties):
"""Return True if host has sufficient capacity.""" """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: if host_state.free_capacity_gb is None:
# Fail Safe # 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', 'AvailabilityZoneFilter',
'CapacityFilter', 'CapacityFilter',
'CapabilitiesFilter', 'CapabilitiesFilter',
'ConsistencyGroupFilter',
'DriverFilter', 'DriverFilter',
'ShareReplicationFilter', 'ShareReplicationFilter',
], ],
@ -132,7 +131,6 @@ class HostState(object):
self.snapshot_support = True self.snapshot_support = True
self.create_share_from_snapshot_support = True self.create_share_from_snapshot_support = True
self.revert_to_snapshot_support = False self.revert_to_snapshot_support = False
self.consistency_group_support = False
self.dedupe = False self.dedupe = False
self.compression = False self.compression = False
self.replication_type = None self.replication_type = None
@ -304,10 +302,6 @@ class HostState(object):
pool_cap['revert_to_snapshot_support'] = ( pool_cap['revert_to_snapshot_support'] = (
self.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: if 'dedupe' not in pool_cap:
pool_cap['dedupe'] = self.dedupe pool_cap['dedupe'] = self.dedupe
@ -332,8 +326,6 @@ class HostState(object):
'create_share_from_snapshot_support') 'create_share_from_snapshot_support')
self.revert_to_snapshot_support = capability.get( self.revert_to_snapshot_support = capability.get(
'revert_to_snapshot_support', False) 'revert_to_snapshot_support', False)
self.consistency_group_support = capability.get(
'consistency_group_support', False)
self.updated = capability['timestamp'] self.updated = capability['timestamp']
self.replication_type = capability.get('replication_type') self.replication_type = capability.get('replication_type')
self.replication_domain = capability.get('replication_domain') self.replication_domain = capability.get('replication_domain')

View File

@ -58,7 +58,7 @@ MAPPING = {
class SchedulerManager(manager.Manager): class SchedulerManager(manager.Manager):
"""Chooses a host to create shares.""" """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, def __init__(self, scheduler_driver=None, service_name=None,
*args, **kwargs): *args, **kwargs):
@ -212,34 +212,29 @@ class SchedulerManager(manager.Manager):
def request_service_capabilities(self, context): def request_service_capabilities(self, context):
share_rpcapi.ShareAPI().publish_service_capabilities(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"), LOG.warning(_LW("Failed to schedule_%(method)s: %(ex)s"),
{"method": method, "ex": ex}) {"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: if share_group_id:
db.consistency_group_update(context, db.share_group_update(context, share_group_id, share_group_state)
consistency_group_id,
cg_state)
# TODO(ameade): add notifications def create_share_group(self, context, share_group_id, request_spec=None,
filter_properties=None):
def create_consistency_group(self, context, cg_id, request_spec=None,
filter_properties=None):
try: try:
self.driver.schedule_create_consistency_group(context, cg_id, self.driver.schedule_create_share_group(
request_spec, context, share_group_id, request_spec, filter_properties)
filter_properties)
except exception.NoValidHost as ex: except exception.NoValidHost as ex:
self._set_cg_error_state('create_consistency_group', self._set_share_group_error_state(
context, ex, request_spec) 'create_share_group', context, ex, request_spec)
except Exception as ex: except Exception as ex:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self._set_cg_error_state('create_consistency_group', self._set_share_group_error_state(
context, ex, request_spec) 'create_share_group', context, ex, request_spec)
def _set_share_replica_error_state(self, context, method, exc, def _set_share_replica_error_state(self, context, method, exc,
request_spec): request_spec):

View File

@ -34,20 +34,21 @@ class SchedulerAPI(object):
1.1 - Add get_pools method 1.1 - Add get_pools method
1.2 - Introduce Share Instances. Replace ``create_share()`` with 1.2 - Introduce Share Instances. Replace ``create_share()`` with
``create_share_instance()`` ``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.4 - Add migrate_share_to_host method
1.5 - Add create_share_replica 1.5 - Add create_share_replica
1.6 - Add manage_share 1.6 - Add manage_share
1.7 - Updated migrate_share_to_host method with new parameters 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): def __init__(self):
super(SchedulerAPI, self).__init__() super(SchedulerAPI, self).__init__()
target = messaging.Target(topic=CONF.scheduler_topic, target = messaging.Target(topic=CONF.scheduler_topic,
version=self.RPC_API_VERSION) 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, def create_share_instance(self, context, request_spec=None,
filter_properties=None): filter_properties=None):
@ -72,13 +73,23 @@ class SchedulerAPI(object):
call_context = self.client.prepare(version='1.1') call_context = self.client.prepare(version='1.1')
return call_context.call(context, 'get_pools', filters=filters) return call_context.call(context, 'get_pools', filters=filters)
def create_consistency_group(self, context, cg_id, request_spec=None, def create_share_group(self, context, share_group_id, request_spec=None,
filter_properties=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) 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, return call_context.cast(context,
'create_consistency_group', 'create_share_group',
cg_id=cg_id, share_group_id=share_group_id,
request_spec=request_spec_p, request_spec=request_spec_p,
filter_properties=filter_properties) filter_properties=filter_properties)

View File

@ -40,7 +40,6 @@ def generate_stats(host_state, properties):
host_state.driver_handles_share_servers, host_state.driver_handles_share_servers,
'thin_provisioning': host_state.thin_provisioning, 'thin_provisioning': host_state.thin_provisioning,
'updated': host_state.updated, 'updated': host_state.updated,
'consistency_group_support': host_state.consistency_group_support,
'dedupe': host_state.dedupe, 'dedupe': host_state.dedupe,
'compression': host_state.compression, 'compression': host_state.compression,
'snapshot_support': host_state.snapshot_support, '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, def create(self, context, share_proto, size, name, description,
snapshot_id=None, availability_zone=None, metadata=None, snapshot_id=None, availability_zone=None, metadata=None,
share_network_id=None, share_type=None, is_public=False, 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.""" """Create new share."""
policy.check_policy(context, 'share', 'create') policy.check_policy(context, 'share', 'create')
@ -171,47 +171,45 @@ class API(base.Base):
except ValueError as e: except ValueError as e:
raise exception.InvalidParameterValue(six.text_type(e)) raise exception.InvalidParameterValue(six.text_type(e))
consistency_group = None share_group = None
if consistency_group_id: if share_group_id:
try: try:
consistency_group = self.db.consistency_group_get( share_group = self.db.share_group_get(context, share_group_id)
context, consistency_group_id)
except exception.NotFound as e: except exception.NotFound as e:
raise exception.InvalidParameterValue(six.text_type(e)) raise exception.InvalidParameterValue(six.text_type(e))
if (not cgsnapshot_member and if (not share_group_snapshot_member and
not (consistency_group['status'] == not (share_group['status'] == constants.STATUS_AVAILABLE)):
constants.STATUS_AVAILABLE)):
params = { params = {
'avail': constants.STATUS_AVAILABLE, 'avail': constants.STATUS_AVAILABLE,
'cg_status': consistency_group['status'], 'status': share_group['status'],
} }
msg = _("Consistency group status must be %(avail)s, got" msg = _("Share group status must be %(avail)s, got"
"%(cg_status)s.") % params "%(status)s.") % params
raise exception.InvalidConsistencyGroup(message=msg) raise exception.InvalidShareGroup(message=msg)
if share_type_id: if share_type_id:
cg_st_ids = [st['share_type_id'] for st in share_group_st_ids = [
consistency_group.get('share_types', [])] st['share_type_id']
if share_type_id not in cg_st_ids: for st in share_group.get('share_types', [])]
if share_type_id not in share_group_st_ids:
params = { params = {
'type': share_type_id, 'type': share_type_id,
'cg': consistency_group_id 'group': share_group_id,
} }
msg = _("The specified share type (%(type)s) is not " msg = _("The specified share type (%(type)s) is not "
"supported by the specified consistency group " "supported by the specified share group "
"(%(cg)s).") % params "(%(group)s).") % params
raise exception.InvalidParameterValue(msg) raise exception.InvalidParameterValue(msg)
if (not consistency_group.get('share_network_id') if not share_group.get('share_network_id') == share_network_id:
== share_network_id):
params = { params = {
'net': share_network_id, 'net': share_network_id,
'cg': consistency_group_id 'group': share_group_id
} }
msg = _("The specified share network (%(net)s) is not " msg = _("The specified share network (%(net)s) is not "
"supported by the specified consistency group " "supported by the specified share group "
"(%(cg)s).") % params "(%(group)s).") % params
raise exception.InvalidParameterValue(msg) raise exception.InvalidParameterValue(msg)
options = { options = {
@ -224,12 +222,13 @@ class API(base.Base):
'display_description': description, 'display_description': description,
'share_proto': share_proto, 'share_proto': share_proto,
'is_public': is_public, '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)) options.update(self.get_share_attributes_from_share_type(share_type))
if cgsnapshot_member: if share_group_snapshot_member:
options['source_cgsnapshot_member_id'] = cgsnapshot_member['id'] options['source_share_group_snapshot_member_id'] = (
share_group_snapshot_member['id'])
try: try:
share = self.db.share_create(context, options, share = self.db.share_create(context, options,
@ -248,11 +247,14 @@ class API(base.Base):
# It is common situation for different types of backends. # It is common situation for different types of backends.
host = snapshot['share']['instance']['host'] host = snapshot['share']['instance']['host']
self.create_instance(context, share, share_network_id=share_network_id, elif share_group:
host=host, availability_zone=availability_zone, host = share_group['host']
consistency_group=consistency_group,
cgsnapshot_member=cgsnapshot_member, self.create_instance(
share_type_id=share_type_id) 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 # Retrieve the share with instance details
share = self.db.share_get(context, share['id']) 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, def create_instance(self, context, share, share_network_id=None,
host=None, availability_zone=None, host=None, availability_zone=None,
consistency_group=None, cgsnapshot_member=None, share_group=None, share_group_snapshot_member=None,
share_type_id=None): share_type_id=None):
policy.check_policy(context, 'share', 'create') policy.check_policy(context, 'share', 'create')
request_spec, share_instance = ( request_spec, share_instance = (
self.create_share_instance_and_get_request_spec( self.create_share_instance_and_get_request_spec(
context, share, availability_zone=availability_zone, 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_network_id=share_network_id,
share_type_id=share_type_id)) share_type_id=share_type_id))
if cgsnapshot_member: if share_group_snapshot_member:
# Inherit properties from the cgsnapshot_member # Inherit properties from the share_group_snapshot_member
member_share_instance = cgsnapshot_member['share_instance'] member_share_instance = share_group_snapshot_member[
'share_instance']
updates = { updates = {
'host': member_share_instance['host'], 'host': member_share_instance['host'],
'share_network_id': member_share_instance['share_network_id'], '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 = self.db.share_instance_update(context,
share_instance['id'], share_instance['id'],
updates) 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 return
if host: if host:
@ -360,7 +364,7 @@ class API(base.Base):
def create_share_instance_and_get_request_spec( def create_share_instance_and_get_request_spec(
self, context, share, availability_zone=None, 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): share_type_id=None, cast_rules_to_readonly=False):
availability_zone_id = None availability_zone_id = None
@ -398,9 +402,9 @@ class API(base.Base):
'share_proto': share['share_proto'], 'share_proto': share['share_proto'],
'share_type_id': share_type_id, 'share_type_id': share_type_id,
'is_public': share['is_public'], 'is_public': share['is_public'],
'consistency_group_id': share['consistency_group_id'], 'share_group_id': share['share_group_id'],
'source_cgsnapshot_member_id': share[ 'source_share_group_snapshot_member_id': share[
'source_cgsnapshot_member_id'], 'source_share_group_snapshot_member_id'],
'snapshot_id': share['snapshot_id'], 'snapshot_id': share['snapshot_id'],
'replication_type': share['replication_type'], 'replication_type': share['replication_type'],
} }
@ -428,7 +432,7 @@ class API(base.Base):
'share_id': share['id'], 'share_id': share['id'],
'snapshot_id': share['snapshot_id'], 'snapshot_id': share['snapshot_id'],
'share_type': share_type, 'share_type': share_type,
'consistency_group': consistency_group, 'share_group': share_group,
'availability_zone_id': availability_zone_id, 'availability_zone_id': availability_zone_id,
} }
return request_spec, share_instance return request_spec, share_instance
@ -440,6 +444,10 @@ class API(base.Base):
msg = _("Replication not supported for share %s.") msg = _("Replication not supported for share %s.")
raise exception.InvalidShare(message=msg % share['id']) 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) self._check_is_share_busy(share)
active_replica = self.db.share_replicas_get_available_active_replica( 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_proto': kwargs.get('share_proto', share.get('share_proto')),
'share_type_id': share_type['id'], 'share_type_id': share_type['id'],
'is_public': kwargs.get('is_public', share.get('is_public')), 'is_public': kwargs.get('is_public', share.get('is_public')),
'consistency_group_id': kwargs.get( 'share_group_id': kwargs.get(
'consistency_group_id', share.get('consistency_group_id')), 'share_group_id', share.get('share_group_id')),
'source_cgsnapshot_member_id': kwargs.get( 'source_share_group_snapshot_member_id': kwargs.get(
'source_cgsnapshot_member_id', 'source_share_group_snapshot_member_id',
share.get('source_cgsnapshot_member_id')), share.get('source_share_group_snapshot_member_id')),
'snapshot_id': kwargs.get('snapshot_id', share.get('snapshot_id')), 'snapshot_id': kwargs.get('snapshot_id', share.get('snapshot_id')),
} }
share_instance_properties = { share_instance_properties = {
@ -878,14 +886,16 @@ class API(base.Base):
snapshots = self.db.share_snapshot_get_all_for_share(context, share_id) snapshots = self.db.share_snapshot_get_all_for_share(context, share_id)
if len(snapshots): 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) raise exception.InvalidShare(reason=msg)
cgsnapshot_members_count = self.db.count_cgsnapshot_members_in_share( share_group_snapshot_members_count = (
context, share_id) self.db.count_share_group_snapshot_members_in_share(
if cgsnapshot_members_count: context, share_id))
msg = (_("Share still has %d dependent cgsnapshot members") % if share_group_snapshot_members_count:
cgsnapshot_members_count) msg = (
_("Share still has %d dependent share group snapshot "
"members.") % share_group_snapshot_members_count)
raise exception.InvalidShare(reason=msg) raise exception.InvalidShare(reason=msg)
self._check_is_share_busy(share) self._check_is_share_busy(share)
@ -955,10 +965,10 @@ class API(base.Base):
if shares: if shares:
raise exception.ShareServerInUse(share_server_id=server['id']) raise exception.ShareServerInUse(share_server_id=server['id'])
cgs = self.db.consistency_group_get_all_by_share_server(context, share_groups = self.db.share_group_get_all_by_share_server(
server['id']) context, server['id'])
if cgs: if share_groups:
LOG.error(_LE("share server '%(ssid)s' in use by CGs"), LOG.error(_LE("share server '%(ssid)s' in use by share groups."),
{'ssid': server['id']}) {'ssid': server['id']})
raise exception.ShareServerInUse(share_server_id=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 oslo_log import log
from manila import exception from manila import exception
from manila.i18n import _, _LE from manila.i18n import _, _LE, _LW
from manila import network from manila import network
from manila import utils from manila import utils
@ -825,7 +825,7 @@ class ShareDriver(object):
def choose_share_server_compatible_with_share(self, context, share_servers, def choose_share_server_compatible_with_share(self, context, share_servers,
share, snapshot=None, share, snapshot=None,
consistency_group=None): share_group=None):
"""Method that allows driver to choose share server for provided share. """Method that allows driver to choose share server for provided share.
If compatible share-server is not found, method should return None. 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_servers: list with share-server models
:param share: share model :param share: share model
:param snapshot: snapshot model :param snapshot: snapshot model
:param consistency_group: ConsistencyGroup model with shares :param share_group: ConsistencyGroup model with shares
:returns: share-server or None :returns: share-server or None
""" """
# If creating in a consistency group, use its share server # If creating in a share group, use its share server
if consistency_group: if share_group:
for share_server in share_servers: for share_server in share_servers:
if (consistency_group.get('share_server_id') == if (share_group.get('share_server_id') ==
share_server['id']): share_server['id']):
return share_server return share_server
return None return None
return share_servers[0] if share_servers else None return share_servers[0] if share_servers else None
def choose_share_server_compatible_with_cg(self, context, share_servers, def choose_share_server_compatible_with_share_group(
cg_ref, cgsnapshot=None): self, context, share_servers, share_group_ref,
share_group_snapshot=None):
return share_servers[0] if share_servers else None return share_servers[0] if share_servers else None
@ -1074,6 +1075,7 @@ class ShareDriver(object):
create_share_from_snapshot_support=( create_share_from_snapshot_support=(
self.creating_shares_from_snapshots_is_supported), self.creating_shares_from_snapshots_is_supported),
revert_to_snapshot_support=False, revert_to_snapshot_support=False,
share_group_snapshot_support=self.snapshots_are_supported,
replication_domain=self.replication_domain, replication_domain=self.replication_domain,
filter_function=self.get_filter_function(), filter_function=self.get_filter_function(),
goodness_function=self.get_goodness_function(), goodness_function=self.get_goodness_function(),
@ -1089,11 +1091,11 @@ class ShareDriver(object):
""" """
return [] return []
def create_consistency_group(self, context, cg_dict, share_server=None): def create_share_group(self, context, share_group_dict, share_server=None):
"""Create a consistency group. """Create a share group.
:param context: :param context:
:param cg_dict: The consistency group details :param share_group_dict: The share group details
EXAMPLE: EXAMPLE:
{ {
'status': 'creating', 'status': 'creating',
@ -1103,27 +1105,30 @@ class ShareDriver(object):
'deleted': 'False', 'deleted': 'False',
'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6), 'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6),
'updated_at': None, 'updated_at': None,
'source_cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'source_share_group_snapshot_id': 'some_fake_uuid',
'host': 'openstack2@cmodeSSVMNFS', 'share_group_type_id': 'some_fake_uuid',
'host': 'hostname@backend_name',
'share_network_id': None,
'share_server_id': None,
'deleted_at': None, 'deleted_at': None,
'share_types': [<models.ConsistencyGroupShareTypeMapping>], 'share_types': [<models.ShareGroupShareTypeMapping>],
'id': 'eda52174-0442-476d-9694-a58327466c14', 'id': 'some_fake_uuid',
'name': None 'name': None
} }
:returns: (cg_model_update, share_update_list) :returns: (share_group_model_update, share_update_list)
cg_model_update - a dict containing any values to be updated share_group_model_update - a dict containing any values to be
for the CG in the database. This value may be None. 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, def create_share_group_from_share_group_snapshot(
cgsnapshot_dict, self, context, share_group_dict, share_group_snapshot_dict,
share_server=None): share_server=None):
"""Create a consistency group from a cgsnapshot. """Create a share group from a share group snapshot.
:param context: :param context:
:param cg_dict: The consistency group details :param share_group_dict: The share group details
EXAMPLE: EXAMPLE:
.. code:: .. code::
@ -1135,15 +1140,16 @@ class ShareDriver(object):
'deleted': 'False', 'deleted': 'False',
'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6), 'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6),
'updated_at': None, 'updated_at': None,
'source_cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'source_share_group_snapshot_id':
'host': 'openstack2@cmodeSSVMNFS', 'f6aa3b59-57eb-421e-965c-4e182538e36a',
'host': 'hostname@backend_name',
'deleted_at': None, 'deleted_at': None,
'shares': [<models.Share>], # The new shares being created 'shares': [<models.Share>], # The new shares being created
'share_types': [<models.ConsistencyGroupShareTypeMapping>], 'share_types': [<models.ShareGroupShareTypeMapping>],
'id': 'eda52174-0442-476d-9694-a58327466c14', 'id': 'some_fake_uuid',
'name': None 'name': None
} }
:param cgsnapshot_dict: The cgsnapshot details :param share_group_snapshot_dict: The share group snapshot details
EXAMPLE: EXAMPLE:
.. code:: .. code::
@ -1155,11 +1161,10 @@ class ShareDriver(object):
'deleted': '0', 'deleted': '0',
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), 'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
'updated_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', 'share_group_id': 'some_fake_uuid',
'cgsnapshot_members': [ 'share_share_group_snapshot_members': [
{ {
'status': 'available', 'status': 'available',
'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f',
'user_id': 'a0314a441ca842019b0952224aa39192', 'user_id': 'a0314a441ca842019b0952224aa39192',
'deleted': 'False', 'deleted': 'False',
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), '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), 'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
'share_proto': 'NFS', 'share_proto': 'NFS',
'project_id': '13c0be6290934bd98596cfa004650049', 'project_id': '13c0be6290934bd98596cfa004650049',
'cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'share_group_snapshot_id': 'some_fake_uuid',
'deleted_at': None, 'deleted_at': None,
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e', 'id': 'some_fake_uuid',
'size': 1 'size': 1
} }
], ],
@ -1177,27 +1182,48 @@ class ShareDriver(object):
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
'name': None 'name': None
} }
:return: (cg_model_update, share_update_list) :return: (share_group_model_update, share_update_list)
cg_model_update - a dict containing any values to be updated share_group_model_update - a dict containing any values to be
for the CG in the database. This value may be None. updated for the share group in the database. This value may be None
share_update_list - a list of dictionaries containing dicts for share_update_list - a list of dictionaries containing dicts for
every share created in the CG. Any share dicts should at a minimum every share created in the share group. Any share dicts should at a
contain the 'id' key and 'export_locations'. Export locations minimum contain the 'id' key and 'export_locations'.
should be in the same format as returned by a share_create. This Export locations should be in the same format as returned by
list may be empty or None. a share_create. This list may be empty or None. EXAMPLE:
EXAMPLE:
.. code:: .. 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): clone_list = self._collate_share_group_snapshot_info(
"""Delete a consistency group 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 context: The request context
:param cg_dict: The consistency group details :param share_group_dict: The share group details
EXAMPLE: EXAMPLE:
.. code:: .. code::
@ -1209,25 +1235,42 @@ class ShareDriver(object):
'deleted': 'False', 'deleted': 'False',
'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6), 'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6),
'updated_at': None, 'updated_at': None,
'source_cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'source_share_group_snapshot_id': 'some_fake_uuid',
'host': 'openstack2@cmodeSSVMNFS', 'share_share_group_type_id': 'some_fake_uuid',
'host': 'hostname@backend_name',
'deleted_at': None, 'deleted_at': None,
'shares': [<models.Share>], # The new shares being created 'shares': [<models.Share>], # The new shares being created
'share_types': [<models.ConsistencyGroupShareTypeMapping>], 'share_types': [<models.ShareGroupShareTypeMapping>],
'id': 'eda52174-0442-476d-9694-a58327466c14', 'id': 'some_fake_uuid',
'name': None 'name': None
} }
:return: cg_model_update :return: share_group_model_update
cg_model_update - a dict containing any values to be updated share_group_model_update - a dict containing any values to be
for the CG in the database. This value may be None. updated for the group in the database. This value may be None.
""" """
raise NotImplementedError()
def create_cgsnapshot(self, context, snap_dict, share_server=None): def _cleanup_group_share_snapshot(self, context, share_snapshot,
"""Create a consistency group 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 context:
:param snap_dict: The cgsnapshot details :param snap_dict: The share group snapshot details
EXAMPLE: EXAMPLE:
.. code:: .. code::
@ -1239,11 +1282,11 @@ class ShareDriver(object):
'deleted': '0', 'deleted': '0',
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), 'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
'updated_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', 'share_group_id': 'some_fake_uuid',
'cgsnapshot_members': [ 'share_group_snapshot_members': [
{ {
'status': 'available', 'status': 'available',
'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f', 'share_type_id': 'some_fake_uuid',
'user_id': 'a0314a441ca842019b0952224aa39192', 'user_id': 'a0314a441ca842019b0952224aa39192',
'deleted': 'False', 'deleted': 'False',
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), '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), 'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
'share_proto': 'NFS', 'share_proto': 'NFS',
'project_id': '13c0be6290934bd98596cfa004650049', 'project_id': '13c0be6290934bd98596cfa004650049',
'cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'share_group_snapshot_id': 'some_fake_uuid',
'deleted_at': None, 'deleted_at': None,
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e', 'share_id': 'some_fake_uuid',
'id': 'some_fake_uuid',
'size': 1 'size': 1
} }
], ],
'deleted_at': None, 'deleted_at': None,
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'id': 'some_fake_uuid',
'name': None 'name': None
} }
:return: (cgsnapshot_update, member_update_list) :return: (share_group_snapshot_update, member_update_list)
cgsnapshot_update - a dict containing any values to be updated share_group_snapshot_update - a dict containing any values to be
for the CGSnapshot in the database. This value may be None. updated for the CGSnapshot in the database. This value may be None.
member_update_list - a list of dictionaries containing for every member_update_list - a list of dictionaries containing for every
member of the cgsnapshot. Each dict should contains values to be member of the share group snapshot. Each dict should contains
updated for the CGSnapshotMember in the database. This list may be values to be updated for the ShareGroupSnapshotMember in
empty or None. 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): snapshot_members = snap_dict.get('share_group_snapshot_members', [])
"""Delete a consistency group snapshot 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 context:
:param snap_dict: The cgsnapshot details :param snap_dict: The share group snapshot details
EXAMPLE: EXAMPLE:
.. code:: .. code::
@ -1288,12 +1374,12 @@ class ShareDriver(object):
'deleted': '0', 'deleted': '0',
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), 'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
'updated_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', 'share_group_id': 'some_fake_uuid',
'cgsnapshot_members': [ 'share_group_snapshot_members': [
{ {
'status': 'available', 'status': 'available',
'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f', 'share_type_id': 'some_fake_uuid',
'share_id': 'e14b5174-e534-4f35-bc4f-fe81c1575d6f', 'share_id': 'some_fake_uuid',
'user_id': 'a0314a441ca842019b0952224aa39192', 'user_id': 'a0314a441ca842019b0952224aa39192',
'deleted': 'False', 'deleted': 'False',
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58), '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), 'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
'share_proto': 'NFS', 'share_proto': 'NFS',
'project_id': '13c0be6290934bd98596cfa004650049', 'project_id': '13c0be6290934bd98596cfa004650049',
'cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'share_group_snapshot_id': 'some_fake_uuid',
'deleted_at': None, 'deleted_at': None,
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e', 'id': 'some_fake_uuid',
'size': 1 'size': 1
} }
], ],
@ -1311,11 +1397,54 @@ class ShareDriver(object):
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
'name': None 'name': None
} }
:return: (cgsnapshot_update, member_update_list) :return: (share_group_snapshot_update, member_update_list)
cgsnapshot_update - a dict containing any values to be updated share_group_snapshot_update - a dict containing any values
for the CGSnapshot in the database. This value may be None. 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): def get_periodic_hook_data(self, context, share_instances):
"""Dedicated for update/extend of data for existing 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 free_capacity_gb = stats['kb_avail'] * units.Mi
data = { data = {
'consistency_group_support': 'pool',
'vendor_name': 'Ceph', 'vendor_name': 'Ceph',
'driver_version': '1.0', 'driver_version': '1.0',
'share_backend_name': self.backend_name, 'share_backend_name': self.backend_name,
@ -166,7 +165,7 @@ class CephFSNativeDriver(driver.ShareDriver,):
def _share_path(self, share): def _share_path(self, share):
"""Get VolumePath from Share.""" """Get VolumePath from Share."""
return ceph_volume_client.VolumePath( 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): def create_share(self, context, share, share_server=None):
"""Create a CephFS volume. """Create a CephFS volume.
@ -178,9 +177,11 @@ class CephFSNativeDriver(driver.ShareDriver,):
""" """
# `share` is a Share # `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'], 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) extra_specs = share_types.get_extra_specs_from_share(share)
data_isolated = extra_specs.get("cephfs:data_isolated", False) data_isolated = extra_specs.get("cephfs:data_isolated", False)
@ -340,22 +341,24 @@ class CephFSNativeDriver(driver.ShareDriver,):
self._share_path(snapshot['share']), self._share_path(snapshot['share']),
'_'.join([snapshot['snapshot_id'], snapshot['id']])) '_'.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']) 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']) 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( self.volume_client.destroy_snapshot_group(
snap_dict['consistency_group_id'], snap_dict['share_group_id'],
snap_dict['id']) snap_dict['id'])
return None, [] 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( self.volume_client.create_snapshot_group(
snap_dict['consistency_group_id'], snap_dict['share_group_id'],
snap_dict['id']) snap_dict['id'])
return None, [] return None, []

View File

@ -616,7 +616,6 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver):
share_backend_name=self.backend_name, share_backend_name=self.backend_name,
storage_protocol='NFS_CIFS', storage_protocol='NFS_CIFS',
reserved_percentage=self.configuration.reserved_share_percentage, reserved_percentage=self.configuration.reserved_share_percentage,
consistency_group_support=None,
) )
super(GenericShareDriver, self)._update_share_stats(data) super(GenericShareDriver, self)._update_share_stats(data)

View File

@ -188,7 +188,6 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
'storage_protocol': 'NFS_CIFS', 'storage_protocol': 'NFS_CIFS',
'reserved_percentage': 'reserved_percentage':
self.configuration.reserved_share_percentage, self.configuration.reserved_share_percentage,
'consistency_group_support': None,
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': True, 'revert_to_snapshot_support': True,

View File

@ -242,7 +242,6 @@ class NetAppCmodeFileStorageLibrary(object):
'driver_version': '1.0', 'driver_version': '1.0',
'netapp_storage_family': 'ontap_cluster', 'netapp_storage_family': 'ontap_cluster',
'storage_protocol': 'NFS_CIFS', 'storage_protocol': 'NFS_CIFS',
'consistency_group_support': 'host',
'pools': self._get_pools(filter_function=filter_function, 'pools': self._get_pools(filter_function=filter_function,
goodness_function=goodness_function), goodness_function=goodness_function),
} }
@ -1070,7 +1069,7 @@ class NetAppCmodeFileStorageLibrary(object):
else: else:
msg = _("Invalid data supplied for creating consistency group " msg = _("Invalid data supplied for creating consistency group "
"from CG snapshot %s.") % cgsnapshot_dict['id'] "from CG snapshot %s.") % cgsnapshot_dict['id']
raise exception.InvalidConsistencyGroup(reason=msg) raise exception.InvalidShareGroup(reason=msg)
clone_list.append(clone_info) clone_list.append(clone_info)

View File

@ -375,7 +375,6 @@ class ZFSonLinuxShareDriver(zfs_utils.ExecuteMixin, driver.ShareDriver):
'storage_protocol': 'NFS', 'storage_protocol': 'NFS',
'reserved_percentage': 'reserved_percentage':
self.configuration.reserved_share_percentage, self.configuration.reserved_share_percentage,
'consistency_group_support': None,
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'driver_name': 'ZFS', 'driver_name': 'ZFS',

View File

@ -189,7 +189,7 @@ def add_hooks(f):
class ShareManager(manager.SchedulerDependentManager): class ShareManager(manager.SchedulerDependentManager):
"""Manages NAS storages.""" """Manages NAS storages."""
RPC_API_VERSION = '1.15' RPC_API_VERSION = '1.16'
def __init__(self, share_driver=None, service_name=None, *args, **kwargs): def __init__(self, share_driver=None, service_name=None, *args, **kwargs):
"""Load the driver from args, or from flags.""" """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, def _provide_share_server_for_share(self, context, share_network_id,
share_instance, snapshot=None, share_instance, snapshot=None,
consistency_group=None, share_group=None,
create_on_backend=True): create_on_backend=True):
"""Gets or creates share_server and updates share with its id. """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( self.driver.choose_share_server_compatible_with_share(
context, available_share_servers, share_instance, context, available_share_servers, share_instance,
snapshot=snapshot.instance if snapshot else None, snapshot=snapshot.instance if snapshot else None,
consistency_group=consistency_group share_group=share_group
) )
) )
except Exception as e: except Exception as e:
@ -538,19 +538,20 @@ class ShareManager(manager.SchedulerDependentManager):
if snapshot_id: if snapshot_id:
snapshot_ref = self.db.share_snapshot_get(context, snapshot_id) snapshot_ref = self.db.share_snapshot_get(context, snapshot_id)
consistency_group_ref = None share_group_ref = None
if share_instance.get('consistency_group_id'): if share_instance.get('share_group_id'):
consistency_group_ref = self.db.consistency_group_get( share_group_ref = self.db.share_group_get(
context, share_instance['consistency_group_id']) context, share_instance['share_group_id'])
share_server, share_instance = self._provide_share_server_for_share( share_server, share_instance = self._provide_share_server_for_share(
context, share_network_id, share_instance, snapshot_ref, 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'] return share_server['id']
def _provide_share_server_for_cg(self, context, share_network_id, def _provide_share_server_for_share_group(self, context, share_network_id,
cg_ref, cgsnapshot=None): share_group_ref,
share_group_snapshot=None):
"""Gets or creates share_server and updates share with its id. """Gets or creates share_server and updates share with its id.
Active share_server can be deleted if there are no dependent shares 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 should be found or created. If
share_network_id is None method use share_network_id is None method use
share_network_id from provided snapshot. share_network_id from provided snapshot.
:param cg_ref: Consistency Group model :param share_group_ref: Share Group model
:param cgsnapshot: Optional -- CGSnapshot model :param share_group_snapshot: Optional -- ShareGroupSnapshot model
:returns: dict, dict -- first value is share_server, that :returns: dict, dict -- first value is share_server, that
has been chosen for consistency group schedule. has been chosen for share group schedule.
Second value is consistency group updated with Second value is share group updated with
share_server_id. 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'" msg = _("'share_network_id' parameter or 'snapshot'"
" should be provided. ") " should be provided. ")
raise exception.InvalidInput(reason=msg) raise exception.InvalidInput(reason=msg)
def error(msg, *args): def error(msg, *args):
LOG.error(msg, *args) LOG.error(msg, *args)
self.db.consistency_group_update( self.db.share_group_update(
context, cg_ref['id'], {'status': constants.STATUS_ERROR}) context, share_group_ref['id'],
{'status': constants.STATUS_ERROR})
@utils.synchronized("share_manager_%s" % share_network_id, @utils.synchronized("share_manager_%s" % share_network_id,
external=True) external=True)
def _provide_share_server_for_cg(): def _provide_share_server_for_share_group():
try: try:
available_share_servers = ( available_share_servers = (
self.db.share_server_get_all_by_host_and_share_net_valid( self.db.share_server_get_all_by_host_and_share_net_valid(
@ -596,14 +598,14 @@ class ShareManager(manager.SchedulerDependentManager):
available_share_servers = None available_share_servers = None
compatible_share_server = None compatible_share_server = None
choose_share_server = (
self.driver.choose_share_server_compatible_with_share_group)
if available_share_servers: if available_share_servers:
try: try:
compatible_share_server = ( compatible_share_server = choose_share_server(
self.driver.choose_share_server_compatible_with_cg( context, available_share_servers, share_group_ref,
context, available_share_servers, cg_ref, share_group_snapshot=share_group_snapshot,
cgsnapshot=cgsnapshot
)
) )
except Exception as e: except Exception as e:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
@ -620,16 +622,16 @@ class ShareManager(manager.SchedulerDependentManager):
} }
) )
msg = ("Using share_server %(share_server)s for consistency " msg = ("Using share_server %(share_server)s for share "
"group %(cg_id)s") "group %(group_id)s")
LOG.debug(msg, { LOG.debug(msg, {
'share_server': compatible_share_server['id'], '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, context,
cg_ref['id'], share_group_ref['id'],
{'share_server_id': compatible_share_server['id']}, {'share_server_id': compatible_share_server['id']},
) )
@ -642,9 +644,9 @@ class ShareManager(manager.SchedulerDependentManager):
LOG.info(_LI("Used preexisting share server " LOG.info(_LI("Used preexisting share server "
"'%(share_server_id)s'"), "'%(share_server_id)s'"),
{'share_server_id': compatible_share_server['id']}) {'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): def _get_share_server(self, context, share_instance):
if share_instance['share_server_id']: if share_instance['share_server_id']:
@ -1526,10 +1528,10 @@ class ShareManager(manager.SchedulerDependentManager):
snapshot_ref = None snapshot_ref = None
parent_share_server_id = None parent_share_server_id = None
consistency_group_ref = None share_group_ref = None
if share_instance.get('consistency_group_id'): if share_instance.get('share_group_id'):
consistency_group_ref = self.db.consistency_group_get( share_group_ref = self.db.share_group_get(
context, share_instance['consistency_group_id']) context, share_instance['share_group_id'])
if share_network_id or parent_share_server_id: if share_network_id or parent_share_server_id:
try: try:
@ -1537,7 +1539,7 @@ class ShareManager(manager.SchedulerDependentManager):
self._provide_share_server_for_share( self._provide_share_server_for_share(
context, share_network_id, share_instance, context, share_network_id, share_instance,
snapshot=snapshot_ref, snapshot=snapshot_ref,
consistency_group=consistency_group_ref share_group=share_group_ref,
) )
) )
except Exception: except Exception:
@ -3313,30 +3315,32 @@ class ShareManager(manager.SchedulerDependentManager):
LOG.info(_LI("Shrink share completed successfully."), resource=share) LOG.info(_LI("Shrink share completed successfully."), resource=share)
@utils.require_driver_initialized @utils.require_driver_initialized
def create_consistency_group(self, context, cg_id): def create_share_group(self, context, share_group_id):
context = context.elevated() context = context.elevated()
group_ref = self.db.consistency_group_get(context, cg_id) share_group_ref = self.db.share_group_get(context, share_group_id)
group_ref['host'] = self.host share_group_ref['host'] = self.host
shares = self.db.share_instances_get_all_by_consistency_group_id( shares = self.db.share_instances_get_all_by_share_group_id(
context, cg_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 snap_ref = None
parent_share_server_id = None parent_share_server_id = None
if source_cgsnapshot_id: if source_share_group_snapshot_id:
snap_ref = self.db.cgsnapshot_get(context, source_cgsnapshot_id) snap_ref = self.db.share_group_snapshot_get(
for member in snap_ref['cgsnapshot_members']: context, source_share_group_snapshot_id)
for member in snap_ref['share_group_snapshot_members']:
member['share'] = self.db.share_instance_get( member['share'] = self.db.share_instance_get(
context, member['share_instance_id'], with_share_data=True) context, member['share_instance_id'], with_share_data=True)
member['share_id'] = member['share_instance_id'] member['share_id'] = member['share_instance_id']
if 'consistency_group' in snap_ref: if 'share_group' in snap_ref:
parent_share_server_id = snap_ref['consistency_group'][ parent_share_server_id = snap_ref['share_group'][
'share_server_id'] 'share_server_id']
status = constants.STATUS_AVAILABLE status = constants.STATUS_AVAILABLE
model_update = False 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 share_server = None
if parent_share_server_id and self.driver.driver_handles_share_servers: 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'] share_network_id = share_server['share_network_id']
if share_network_id and not self.driver.driver_handles_share_servers: if share_network_id and not self.driver.driver_handles_share_servers:
self.db.consistency_group_update( self.db.share_group_update(
context, cg_id, {'status': constants.STATUS_ERROR}) context, share_group_id, {'status': constants.STATUS_ERROR})
msg = _("Driver does not expect share-network to be provided " msg = _("Driver does not expect share-network to be provided "
"with current configuration.") "with current configuration.")
raise exception.InvalidInput(reason=msg) raise exception.InvalidInput(reason=msg)
if not share_server and share_network_id: if not share_server and share_network_id:
try: try:
share_server, group_ref = self._provide_share_server_for_cg( share_server, share_group_ref = (
context, share_network_id, group_ref, cgsnapshot=snap_ref self._provide_share_server_for_share_group(
context, share_network_id, share_group_ref,
share_group_snapshot=snap_ref,
)
) )
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
LOG.error(_LE("Failed to get share server" LOG.error(_LE("Failed to get share server"
" for consistency group creation.")) " for share group creation."))
self.db.consistency_group_update( self.db.share_group_update(
context, cg_id, {'status': constants.STATUS_ERROR}) context, share_group_id,
{'status': constants.STATUS_ERROR})
try: try:
# TODO(ameade): Add notification for create.start # 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 model_update, share_update_list = None, None
group_ref['shares'] = shares share_group_ref['shares'] = shares
if snap_ref: if snap_ref:
model_update, share_update_list = ( model_update, share_update_list = (
self.driver.create_consistency_group_from_cgsnapshot( self.driver.create_share_group_from_share_group_snapshot(
context, group_ref, snap_ref, context, share_group_ref, snap_ref,
share_server=share_server)) share_server=share_server))
else: else:
model_update = self.driver.create_consistency_group( model_update = self.driver.create_share_group(
context, group_ref, share_server=share_server) context, share_group_ref, share_server=share_server)
if model_update: if model_update:
group_ref = self.db.consistency_group_update(context, share_group_ref = self.db.share_group_update(
group_ref['id'], context, share_group_ref['id'], model_update)
model_update)
if share_update_list: if share_update_list:
for share in share_update_list: for share in share_update_list:
@ -3396,134 +3403,133 @@ class ShareManager(manager.SchedulerDependentManager):
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self.db.consistency_group_update( self.db.share_group_update(
context, context,
group_ref['id'], share_group_ref['id'],
{'status': constants.STATUS_ERROR}) {'status': constants.STATUS_ERROR})
for share in shares: for share in shares:
self.db.share_instance_update( self.db.share_instance_update(
context, share['id'], context, share['id'],
{'status': constants.STATUS_ERROR}) {'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() now = timeutils.utcnow()
for share in shares: for share in shares:
self.db.share_instance_update( self.db.share_instance_update(
context, share['id'], {'status': constants.STATUS_AVAILABLE}) context, share['id'], {'status': constants.STATUS_AVAILABLE})
self.db.consistency_group_update(context, self.db.share_group_update(context,
group_ref['id'], share_group_ref['id'],
{'status': status, {'status': status,
'created_at': now}) 'created_at': now})
LOG.info(_LI("Consistency group %s: created successfully"), cg_id) LOG.info(_LI("Share group %s: created successfully"), share_group_id)
# TODO(ameade): Add notification for create.end # TODO(ameade): Add notification for create.end
return group_ref['id'] return share_group_ref['id']
@utils.require_driver_initialized @utils.require_driver_initialized
def delete_consistency_group(self, context, cg_id): def delete_share_group(self, context, share_group_id):
context = context.elevated() context = context.elevated()
group_ref = self.db.consistency_group_get(context, cg_id) share_group_ref = self.db.share_group_get(context, share_group_id)
group_ref['host'] = self.host share_group_ref['host'] = self.host
group_ref['shares'] = ( share_group_ref['shares'] = (
self.db.share_instances_get_all_by_consistency_group_id( self.db.share_instances_get_all_by_share_group_id(
context, cg_id)) context, share_group_id))
model_update = False model_update = False
# TODO(ameade): Add notification for delete.start # TODO(ameade): Add notification for delete.start
try: try:
LOG.info(_LI("Consistency group %s: deleting"), cg_id) LOG.info(_LI("Share group %s: deleting"), share_group_id)
share_server = None 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( share_server = self.db.share_server_get(
context, group_ref['share_server_id']) context, share_group_ref['share_server_id'])
model_update = self.driver.delete_consistency_group( model_update = self.driver.delete_share_group(
context, group_ref, share_server=share_server) context, share_group_ref, share_server=share_server)
if model_update: if model_update:
group_ref = self.db.consistency_group_update( share_group_ref = self.db.share_group_update(
context, group_ref['id'], model_update) context, share_group_ref['id'], model_update)
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self.db.consistency_group_update( self.db.share_group_update(
context, context,
group_ref['id'], share_group_ref['id'],
{'status': constants.STATUS_ERROR}) {'status': constants.STATUS_ERROR})
LOG.error(_LE("Consistency group %s: delete failed"), LOG.error(_LE("Share group %s: delete failed"),
group_ref['id']) share_group_ref['id'])
self.db.consistency_group_destroy(context, self.db.share_group_destroy(context, share_group_id)
cg_id) LOG.info(_LI("Share group %s: deleted successfully"), share_group_id)
LOG.info(_LI("Consistency group %s: deleted successfully"),
cg_id)
# TODO(ameade): Add notification for delete.end # TODO(ameade): Add notification for delete.end
@utils.require_driver_initialized @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() context = context.elevated()
snap_ref = self.db.cgsnapshot_get(context, cgsnapshot_id) snap_ref = self.db.share_group_snapshot_get(
for member in snap_ref['cgsnapshot_members']: context, share_group_snapshot_id)
for member in snap_ref['share_group_snapshot_members']:
member['share'] = self.db.share_instance_get( member['share'] = self.db.share_instance_get(
context, member['share_instance_id'], with_share_data=True) context, member['share_instance_id'], with_share_data=True)
member['share_id'] = member['share_instance_id'] member['share_id'] = member['share_instance_id']
status = constants.STATUS_AVAILABLE status = constants.STATUS_AVAILABLE
snapshot_update = False
try: try:
LOG.info(_LI("Consistency group snapshot %s: creating"), LOG.info(_LI("Share group snapshot %s: creating"),
cgsnapshot_id) share_group_snapshot_id)
share_server = None 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( 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 = ( snapshot_update, member_update_list = (
self.driver.create_cgsnapshot(context, snap_ref, self.driver.create_share_group_snapshot(
share_server=share_server)) context, snap_ref, share_server=share_server))
if member_update_list: if member_update_list:
snapshot_update = snapshot_update or {} snapshot_update = snapshot_update or {}
snapshot_update['cgsnapshot_members'] = [] snapshot_update['share_group_snapshot_members'] = []
for update in (member_update_list or []): for update in (member_update_list or []):
snapshot_update['cgsnapshot_members'].append(update) snapshot_update['share_group_snapshot_members'].append(
update)
if snapshot_update: if snapshot_update:
snap_ref = self.db.cgsnapshot_update( snap_ref = self.db.share_group_snapshot_update(
context, snap_ref['id'], snapshot_update) context, snap_ref['id'], snapshot_update)
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self.db.cgsnapshot_update( self.db.share_group_snapshot_update(
context, context,
snap_ref['id'], snap_ref['id'],
{'status': constants.STATUS_ERROR}) {'status': constants.STATUS_ERROR})
LOG.error(_LE("Consistency group snapshot %s: create failed"), LOG.error(_LE("Share group snapshot %s: create failed"),
cgsnapshot_id) share_group_snapshot_id)
now = timeutils.utcnow() 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} update = {'status': status, 'created_at': now}
self.db.cgsnapshot_member_update(context, member['id'], self.db.share_group_snapshot_member_update(
update) context, member['id'], update)
self.db.cgsnapshot_update(context, self.db.share_group_snapshot_update(
snap_ref['id'], context, snap_ref['id'],
{'status': status, {'status': status, 'created_at': now})
'created_at': now}) LOG.info(_LI("Share group snapshot %s: created successfully"),
LOG.info(_LI("Consistency group snapshot %s: created successfully"), share_group_snapshot_id)
cgsnapshot_id)
return snap_ref['id'] return snap_ref['id']
@utils.require_driver_initialized @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() context = context.elevated()
snap_ref = self.db.cgsnapshot_get(context, cgsnapshot_id) snap_ref = self.db.share_group_snapshot_get(
for member in snap_ref['cgsnapshot_members']: context, share_group_snapshot_id)
for member in snap_ref['share_group_snapshot_members']:
member['share'] = self.db.share_instance_get( member['share'] = self.db.share_instance_get(
context, member['share_instance_id'], with_share_data=True) context, member['share_instance_id'], with_share_data=True)
member['share_id'] = member['share_instance_id'] member['share_id'] = member['share_instance_id']
@ -3531,41 +3537,41 @@ class ShareManager(manager.SchedulerDependentManager):
snapshot_update = False snapshot_update = False
try: try:
LOG.info(_LI("Consistency group snapshot %s: deleting"), LOG.info(_LI("Share group snapshot %s: deleting"),
cgsnapshot_id) share_group_snapshot_id)
share_server = None 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( 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 = ( snapshot_update, member_update_list = (
self.driver.delete_cgsnapshot(context, snap_ref, self.driver.delete_share_group_snapshot(
share_server=share_server)) context, snap_ref, share_server=share_server))
if member_update_list: if member_update_list:
snapshot_update = snapshot_update or {} snapshot_update = snapshot_update or {}
snapshot_update['cgsnapshot_members'] = [] snapshot_update['share_group_snapshot_members'] = []
for update in (member_update_list or []): for update in (member_update_list or []):
snapshot_update['cgsnapshot_members'].append(update) snapshot_update['share_group_snapshot_members'].append(update)
if snapshot_update: if snapshot_update:
snap_ref = self.db.cgsnapshot_update( snap_ref = self.db.share_group_snapshot_update(
context, snap_ref['id'], snapshot_update) context, snap_ref['id'], snapshot_update)
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self.db.cgsnapshot_update( self.db.share_group_snapshot_update(
context, context,
snap_ref['id'], snap_ref['id'],
{'status': constants.STATUS_ERROR}) {'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']) 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"), LOG.info(_LI("Share group snapshot %s: deleted successfully"),
cgsnapshot_id) share_group_snapshot_id)
def _get_share_replica_dict(self, context, share_replica): def _get_share_replica_dict(self, context, share_replica):
# TODO(gouthamr): remove method when the db layer returns primitives # 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_proto': share_replica.get('share_proto'),
'share_type_id': share_replica.get('share_type_id'), 'share_type_id': share_replica.get('share_type_id'),
'is_public': share_replica.get('is_public'), 'is_public': share_replica.get('is_public'),
'consistency_group_id': share_replica.get('consistency_group_id'), 'share_group_id': share_replica.get('share_group_id'),
'source_cgsnapshot_member_id': share_replica.get( 'source_share_group_snapshot_member_id': share_replica.get(
'source_cgsnapshot_member_id'), 'source_share_group_snapshot_member_id'),
} }
return share_replica_ref 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.14 - Add update_access() and remove allow_access() and deny_access().
1.15 - Updated migration_start() method with new parameter 1.15 - Updated migration_start() method with new parameter
"preserve_snapshots" "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' BASE_RPC_API_VERSION = '1.0'
@ -77,7 +81,7 @@ class ShareAPI(object):
super(ShareAPI, self).__init__() super(ShareAPI, self).__init__()
target = messaging.Target(topic=CONF.share_topic, target = messaging.Target(topic=CONF.share_topic,
version=self.BASE_RPC_API_VERSION) 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, def create_share_instance(self, context, share_instance, host,
request_spec, filter_properties, request_spec, filter_properties,
@ -232,33 +236,31 @@ class ShareAPI(object):
share_id=share['id'], share_id=share['id'],
new_size=new_size) 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) new_host = utils.extract_host(host)
call_context = self.client.prepare(server=new_host, version='1.5') call_context = self.client.prepare(server=new_host, version='1.16')
call_context.cast(context, call_context.cast(
'create_consistency_group', context, 'create_share_group', share_group_id=share_group['id'])
cg_id=cg['id'])
def delete_consistency_group(self, context, cg): def delete_share_group(self, context, share_group):
new_host = utils.extract_host(cg['host']) new_host = utils.extract_host(share_group['host'])
call_context = self.client.prepare(server=new_host, version='1.5') call_context = self.client.prepare(server=new_host, version='1.16')
call_context.cast(context, call_context.cast(
'delete_consistency_group', context, 'delete_share_group', share_group_id=share_group['id'])
cg_id=cg['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) new_host = utils.extract_host(host)
call_context = self.client.prepare(server=new_host, version='1.5') call_context = self.client.prepare(server=new_host, version='1.16')
call_context.cast(context, call_context.cast(
'create_cgsnapshot', context, 'create_share_group_snapshot',
cgsnapshot_id=cgsnapshot['id']) 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) new_host = utils.extract_host(host)
call_context = self.client.prepare(server=new_host, version='1.5') call_context = self.client.prepare(server=new_host, version='1.16')
call_context.cast(context, call_context.cast(
'delete_cgsnapshot', context, 'delete_share_group_snapshot',
cgsnapshot_id=cgsnapshot['id']) share_group_snapshot_id=share_group_snapshot['id'])
def create_share_replica(self, context, share_replica, host, def create_share_replica(self, context, share_replica, host,
request_spec, filter_properties): request_spec, filter_properties):

View File

@ -14,7 +14,7 @@
# under the License. # under the License.
""" """
Handles all requests relating to consistency groups. Handles all requests relating to share groups.
""" """
from oslo_config import cfg from oslo_config import cfg
@ -48,28 +48,31 @@ class API(base.Base):
super(API, self).__init__(db_driver) super(API, self).__init__(db_driver)
def create(self, context, name=None, description=None, def create(self, context, name=None, description=None,
share_type_ids=None, source_cgsnapshot_id=None, share_type_ids=None, source_share_group_snapshot_id=None,
share_network_id=None): share_network_id=None, share_group_type_id=None):
"""Create new consistency group.""" """Create new share group."""
cgsnapshot = None share_group_snapshot = None
original_cg = None original_share_group = None
# NOTE(gouthamr): share_server_id is inherited from the parent CG if a # NOTE(gouthamr): share_server_id is inherited from the
# CG snapshot is specified, else, it will be set in the share manager. # parent share group if a share group snapshot is specified,
# else, it will be set in the share manager.
share_server_id = None share_server_id = None
if source_cgsnapshot_id: if source_share_group_snapshot_id:
cgsnapshot = self.db.cgsnapshot_get(context, source_cgsnapshot_id) share_group_snapshot = self.db.share_group_snapshot_get(
if cgsnapshot['status'] != constants.STATUS_AVAILABLE: context, source_share_group_snapshot_id)
msg = (_("Consistency group snapshot status must be %s") if share_group_snapshot['status'] != constants.STATUS_AVAILABLE:
msg = (_("Share group snapshot status must be %s.")
% constants.STATUS_AVAILABLE) % constants.STATUS_AVAILABLE)
raise exception.InvalidCGSnapshot(reason=msg) raise exception.InvalidShareGroupSnapshot(reason=msg)
original_cg = self.db.consistency_group_get(context, cgsnapshot[ original_share_group = self.db.share_group_get(
'consistency_group_id']) context, share_group_snapshot['share_group_id'])
share_type_ids = [s['share_type_id'] for s in original_cg[ share_type_ids = [
'share_types']] s['share_type_id']
share_network_id = original_cg['share_network_id'] for s in original_share_group['share_types']]
share_server_id = original_cg['share_server_id'] share_network_id = original_share_group['share_network_id']
share_server_id = original_share_group['share_server_id']
# Get share_type_objects # Get share_type_objects
share_type_objects = [] share_type_objects = []
@ -79,7 +82,7 @@ class API(base.Base):
share_type_object = share_types.get_share_type( share_type_object = share_types.get_share_type(
context, share_type_id) context, share_type_id)
except exception.ShareTypeNotFound: 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) raise exception.InvalidInput(msg % share_type_id)
share_type_objects.append(share_type_object) share_type_objects.append(share_type_object)
@ -112,14 +115,30 @@ class API(base.Base):
raise exception.InvalidInput(reason=msg) raise exception.InvalidInput(reason=msg)
if (driver_handles_share_servers and 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 " msg = _("When using a share type with the "
"driver_handles_share_servers extra spec as " "driver_handles_share_servers extra spec as "
"True, a share_network_id must be provided.") "True, a share_network_id must be provided.")
raise exception.InvalidInput(reason=msg) 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 = { 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_network_id': share_network_id,
'share_server_id': share_server_id, 'share_server_id': share_server_id,
'name': name, 'name': name,
@ -127,210 +146,216 @@ class API(base.Base):
'user_id': context.user_id, 'user_id': context.user_id,
'project_id': context.project_id, 'project_id': context.project_id,
'status': constants.STATUS_CREATING, 'status': constants.STATUS_CREATING,
'share_types': share_type_ids 'share_types': share_type_ids or supported_share_types
} }
if original_cg: if original_share_group:
options['host'] = original_cg['host'] options['host'] = original_share_group['host']
cg = self.db.consistency_group_create(context, options) share_group = self.db.share_group_create(context, options)
try: try:
if cgsnapshot: if share_group_snapshot:
members = self.db.cgsnapshot_members_get_all( members = self.db.share_group_snapshot_members_get_all(
context, source_cgsnapshot_id) context, source_share_group_snapshot_id)
for member in members: for member in members:
share = self.db.share_get(context, member['share_id'])
share_type = share_types.get_share_type( 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( member['share_instance'] = self.db.share_instance_get(
context, member['share_instance_id'], context, member['share_instance_id'],
with_share_data=True) with_share_data=True)
self.share_api.create(context, member['share_proto'], self.share_api.create(context, member['share_proto'],
member['size'], None, None, member['size'], None, None,
consistency_group_id=cg['id'], share_group_id=share_group['id'],
cgsnapshot_member=member, share_group_snapshot_member=member,
share_type=share_type, share_type=share_type,
share_network_id=share_network_id) share_network_id=share_network_id)
except Exception: except Exception:
with excutils.save_and_reraise_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.update(options)
request_spec['share_types'] = share_type_objects request_spec['share_types'] = share_type_objects
request_spec['resource_type'] = share_group_type
if cgsnapshot and original_cg: if share_group_snapshot and original_share_group:
self.share_rpcapi.create_consistency_group( self.share_rpcapi.create_share_group(
context, cg, original_cg['host']) context, share_group, original_share_group['host'])
else: else:
self.scheduler_rpcapi.create_consistency_group( self.scheduler_rpcapi.create_share_group(
context, cg_id=cg['id'], request_spec=request_spec, context, share_group_id=share_group['id'],
filter_properties={}) request_spec=request_spec, filter_properties={})
return cg return share_group
def delete(self, context, cg): def delete(self, context, share_group):
"""Delete consistency group.""" """Delete share group."""
cg_id = cg['id'] share_group_id = share_group['id']
if not cg['host']: if not share_group['host']:
self.db.consistency_group_destroy(context.elevated(), cg_id) self.db.share_group_destroy(context.elevated(), share_group_id)
return return
statuses = (constants.STATUS_AVAILABLE, constants.STATUS_ERROR) statuses = (constants.STATUS_AVAILABLE, constants.STATUS_ERROR)
if not cg['status'] in statuses: if not share_group['status'] in statuses:
msg = (_("Consistency group status must be one of %(statuses)s") msg = (_("Share group status must be one of %(statuses)s")
% {"statuses": statuses}) % {"statuses": statuses})
raise exception.InvalidConsistencyGroup(reason=msg) raise exception.InvalidShareGroup(reason=msg)
# NOTE(ameade): check for cgsnapshots in the CG # NOTE(ameade): check for group_snapshots in the group
if self.db.count_cgsnapshots_in_consistency_group(context, cg_id): if self.db.count_share_group_snapshots_in_share_group(
msg = (_("Cannot delete a consistency group with cgsnapshots")) context, share_group_id):
raise exception.InvalidConsistencyGroup(reason=msg) msg = (_("Cannot delete a share group with snapshots"))
raise exception.InvalidShareGroup(reason=msg)
# NOTE(ameade): check for shares in the CG # NOTE(ameade): check for shares in the share group
if self.db.count_shares_in_consistency_group(context, cg_id): if self.db.count_shares_in_share_group(context, share_group_id):
msg = (_("Cannot delete a consistency group with shares")) msg = (_("Cannot delete a share group with shares"))
raise exception.InvalidConsistencyGroup(reason=msg) raise exception.InvalidShareGroup(reason=msg)
cg = self.db.consistency_group_update( share_group = self.db.share_group_update(
context, cg_id, {'status': constants.STATUS_DELETING}) 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): def update(self, context, group, fields):
return self.db.consistency_group_update(context, cg['id'], fields) return self.db.share_group_update(context, group['id'], fields)
def get(self, context, cg_id): def get(self, context, share_group_id):
return self.db.consistency_group_get(context, cg_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: if search_opts is None:
search_opts = {} search_opts = {}
LOG.debug("Searching for consistency_groups by: %s", LOG.debug("Searching for share_groups by: %s",
six.text_type(search_opts)) six.text_type(search_opts))
# Get filtered list of consistency_groups # Get filtered list of share_groups
if context.is_admin and search_opts.get('all_tenants'): if search_opts.pop('all_tenants', 0) and context.is_admin:
consistency_groups = self.db.consistency_group_get_all( share_groups = self.db.share_group_get_all(
context, detailed=detailed) context, detailed=detailed, filters=search_opts,
sort_key=sort_key, sort_dir=sort_dir)
else: else:
consistency_groups = self.db.consistency_group_get_all_by_project( share_groups = self.db.share_group_get_all_by_project(
context, context.project_id, detailed=detailed) context, context.project_id, detailed=detailed,
filters=search_opts, sort_key=sort_key, sort_dir=sort_dir)
return consistency_groups return share_groups
def create_cgsnapshot(self, context, name=None, description=None,
consistency_group_id=None):
"""Create new cgsnapshot."""
def create_share_group_snapshot(self, context, name=None, description=None,
share_group_id=None):
"""Create new share group snapshot."""
options = { options = {
'consistency_group_id': consistency_group_id, 'share_group_id': share_group_id,
'name': name, 'name': name,
'description': description, 'description': description,
'user_id': context.user_id, 'user_id': context.user_id,
'project_id': context.project_id, 'project_id': context.project_id,
'status': constants.STATUS_CREATING, 'status': constants.STATUS_CREATING,
} }
share_group = self.db.share_group_get(context, share_group_id)
cg = self.db.consistency_group_get(context, consistency_group_id) # Check status of group, must be active
# Check status of CG, must be active if not share_group['status'] == constants.STATUS_AVAILABLE:
if not cg['status'] == constants.STATUS_AVAILABLE: msg = (_("Share group status must be %s")
msg = (_("Consistency group status must be %s")
% constants.STATUS_AVAILABLE) % constants.STATUS_AVAILABLE)
raise exception.InvalidConsistencyGroup(reason=msg) raise exception.InvalidShareGroup(reason=msg)
# Create members for every share in the CG # Create members for every share in the group
shares = self.db.share_get_all_by_consistency_group_id( shares = self.db.share_get_all_by_share_group_id(
context, consistency_group_id) context, share_group_id)
# Check status of all shares, they must be active in order to snap # Check status of all shares, they must be active in order to snap
# the CG # the group
for s in shares: for s in shares:
if not s['status'] == constants.STATUS_AVAILABLE: if not s['status'] == constants.STATUS_AVAILABLE:
msg = (_("Share %(s)s in consistency group must have status " msg = (_("Share %(s)s in share group must have status "
"of %(status)s in order to create a CG snapshot") "of %(status)s in order to create a group snapshot")
% {"s": s['id'], % {"s": s['id'],
"status": constants.STATUS_AVAILABLE}) "status": constants.STATUS_AVAILABLE})
raise exception.InvalidConsistencyGroup(reason=msg) raise exception.InvalidShareGroup(reason=msg)
snap = self.db.cgsnapshot_create(context, options)
snap = self.db.share_group_snapshot_create(context, options)
try: try:
members = [] members = []
for s in shares: for s in shares:
member_options = { member_options = {
'cgsnapshot_id': snap['id'], 'share_group_snapshot_id': snap['id'],
'user_id': context.user_id, 'user_id': context.user_id,
'project_id': context.project_id, 'project_id': context.project_id,
'status': constants.STATUS_CREATING, 'status': constants.STATUS_CREATING,
'size': s['size'], 'size': s['size'],
'share_proto': s['share_proto'], 'share_proto': s['share_proto'],
'share_type_id': s['share_type_id'],
'share_id': s['id'], 'share_id': s['id'],
'share_instance_id': s.instance['id'] 'share_instance_id': s.instance['id']
} }
member = self.db.cgsnapshot_member_create(context, member = self.db.share_group_snapshot_member_create(
member_options) context, member_options)
members.append(member) members.append(member)
# Cast to share manager # 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: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
# This will delete the snapshot and all of it's members # 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 return snap
def delete_cgsnapshot(self, context, snap): def delete_share_group_snapshot(self, context, snap):
"""Delete consistency group snapshot.""" """Delete share group snapshot."""
snap_id = snap['id'] snap_id = snap['id']
cg = self.db.consistency_group_get(context,
snap['consistency_group_id'])
statuses = (constants.STATUS_AVAILABLE, constants.STATUS_ERROR) 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: if not snap['status'] in statuses:
msg = (_("Consistency group snapshot status must be one of" msg = (_("Share group snapshot status must be one of"
" %(statuses)s") " %(statuses)s") % {"statuses": statuses})
% {"statuses": statuses}) raise exception.InvalidShareGroupSnapshot(reason=msg)
raise exception.InvalidCGSnapshot(reason=msg)
self.db.cgsnapshot_update(context, snap_id, self.db.share_group_snapshot_update(
{'status': constants.STATUS_DELETING}) context, snap_id, {'status': constants.STATUS_DELETING})
# Cast to share manager # 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): def update_share_group_snapshot(self, context, share_group_snapshot,
return self.db.cgsnapshot_update(context, cg['id'], fields) fields):
return self.db.share_group_snapshot_update(
context, share_group_snapshot['id'], fields)
def get_cgsnapshot(self, context, snapshot_id): def get_share_group_snapshot(self, context, snapshot_id):
return self.db.cgsnapshot_get(context, snapshot_id) return self.db.share_group_snapshot_get(context, snapshot_id)
def get_all_cgsnapshots(self, context, detailed=True, search_opts=None):
def get_all_share_group_snapshots(self, context, detailed=True,
search_opts=None, sort_key=None,
sort_dir=None):
if search_opts is None: if search_opts is None:
search_opts = {} search_opts = {}
LOG.debug("Searching for share group snapshots by: %s",
LOG.debug("Searching for consistency group snapshots by: %s",
six.text_type(search_opts)) six.text_type(search_opts))
# Get filtered list of consistency_groups # Get filtered list of share group snapshots
if context.is_admin and search_opts.get('all_tenants'): if search_opts.pop('all_tenants', 0) and context.is_admin:
cgsnapshots = self.db.cgsnapshot_get_all( share_group_snapshots = self.db.share_group_snapshot_get_all(
context, detailed=detailed) context, detailed=detailed, filters=search_opts,
sort_key=sort_key, sort_dir=sort_dir)
else: else:
cgsnapshots = self.db.cgsnapshot_get_all_by_project( share_group_snapshots = (
context, context.project_id, detailed=detailed) self.db.share_group_snapshot_get_all_by_project(
context, context.project_id, detailed=detailed,
return cgsnapshots filters=search_opts, sort_key=sort_key, sort_dir=sort_dir,
)
def get_all_cgsnapshot_members(self, context, cgsnapshot_id): )
members = self.db.cgsnapshot_members_get_all(context, return share_group_snapshots
cgsnapshot_id)
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 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)] return [stub_snapshot_get(self, context, 2)]
def stub_cgsnapshot_member(id, **kwargs): def stub_share_group_snapshot_member(id, **kwargs):
member = { member = {
'id': id, 'id': id,
'share_id': 'fakeshareid', 'share_id': 'fakeshareid',

View File

@ -186,13 +186,14 @@ class APIVersionRequestTests(test.TestCase):
self.assertRaises(ValueError, self.assertRaises(ValueError,
api_version_request.APIVersionRequest().get_string) 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 @ddt.unpack
def test_str(self, major, minor): def test_str(self, major, minor, experimental):
request_input = '%s.%s' % (major, minor) 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) request_string = six.text_type(request)
self.assertEqual('API Version Request ' self.assertEqual('API Version Request '
'Major: %s, Minor: %s' % (major, minor), 'Major: %s, Minor: %s, Experimental: %s' %
request_string) (major, minor, experimental), request_string)

View File

@ -24,7 +24,6 @@ import six
import webob import webob
from manila.api import common from manila.api import common
from manila.api.openstack import api_version_request as api_version
from manila.api.v1 import shares from manila.api.v1 import shares
from manila.common import constants from manila.common import constants
from manila import context from manila import context
@ -149,22 +148,6 @@ class ShareAPITest(test.TestCase):
expected = self._get_expected_share_detailed_response(self.share) expected = self._get_expected_share_detailed_response(self.share)
self.assertEqual(expected, res_dict) 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): def test_share_create_with_valid_default_share_type(self):
self.mock_object(share_types, 'get_share_type_by_name', self.mock_object(share_types, 'get_share_type_by_name',
mock.Mock(return_value=self.vt)) mock.Mock(return_value=self.vt))
@ -455,22 +438,10 @@ class ShareAPITest(test.TestCase):
self.assertEqual(expected, res_dict) 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): def test_share_show_with_share_type_name(self):
req = fakes.HTTPRequest.blank('/shares/1', version='2.6') req = fakes.HTTPRequest.blank('/shares/1', version='2.6')
res_dict = self.controller.show(req, '1') res_dict = self.controller.show(req, '1')
expected = self._get_expected_share_detailed_response() 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']['share_type_name'] = None
expected['share']['task_state'] = None expected['share']['task_state'] = None
self.assertEqual(expected, res_dict) self.assertEqual(expected, res_dict)
@ -497,35 +468,6 @@ class ShareAPITest(test.TestCase):
resp = self.controller.delete(req, 1) resp = self.controller.delete(req, 1)
self.assertEqual(202, resp.status_int) 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): def test_share_update(self):
shr = self.share shr = self.share
body = {"share": shr} body = {"share": shr}
@ -538,15 +480,6 @@ class ShareAPITest(test.TestCase):
self.assertEqual(shr['is_public'], self.assertEqual(shr['is_public'],
res_dict['share']['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): def test_share_not_updates_size(self):
req = fakes.HTTPRequest.blank('/share/1') req = fakes.HTTPRequest.blank('/share/1')
res_dict = self.controller.update(req, 1, {"share": self.share}) res_dict = self.controller.update(req, 1, {"share": self.share})
@ -787,22 +720,11 @@ class ShareAPITest(test.TestCase):
expected['shares'][0].pop('snapshot_support') expected['shares'][0].pop('snapshot_support')
self._list_detail_test_common(req, expected) 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): def test_share_list_detail_with_task_state(self):
env = {'QUERY_STRING': 'name=Share+Test+Name'} env = {'QUERY_STRING': 'name=Share+Test+Name'}
req = fakes.HTTPRequest.blank('/shares/detail', environ=env, req = fakes.HTTPRequest.blank('/shares/detail', environ=env,
version="2.5") version="2.5")
expected = self._list_detail_common_expected() 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]['task_state'] = None
self._list_detail_test_common(req, expected) self._list_detail_test_common(req, expected)

View File

@ -1,4 +1,4 @@
# Copyright 2015 Alex Meade # Copyright 2016 Alex Meade
# All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -39,24 +39,23 @@ CONF = cfg.CONF
@ddt.ddt @ddt.ddt
class CGSnapshotApiTest(test.TestCase): class ShareGroupSnapshotAPITest(test.TestCase):
def setUp(self): def setUp(self):
super(self.__class__, self).setUp() 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.resource_name = self.controller.resource_name
self.api_version = '2.4' self.api_version = '2.31'
self.mock_policy_check = self.mock_object( self.mock_policy_check = self.mock_object(
policy, 'check_policy', mock.Mock(return_value=True)) policy, 'check_policy', mock.Mock(return_value=True))
self.request = fakes.HTTPRequest.blank('/consistency-groups', self.request = fakes.HTTPRequest.blank(
version=self.api_version, '/share-groups', version=self.api_version, experimental=True)
experimental=True)
self.context = self.request.environ['manila.context'] self.context = self.request.environ['manila.context']
self.admin_context = context.RequestContext('admin', 'fake', True) self.admin_context = context.RequestContext('admin', 'fake', True)
self.member_context = context.RequestContext('fake', 'fake') self.member_context = context.RequestContext('fake', 'fake')
self.flags(rpc_backend='manila.openstack.common.rpc.impl_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 = { snap = {
'id': 'fake_id', 'id': 'fake_id',
'user_id': 'fakeuser', 'user_id': 'fakeuser',
@ -64,18 +63,18 @@ class CGSnapshotApiTest(test.TestCase):
'status': constants.STATUS_CREATING, 'status': constants.STATUS_CREATING,
'name': None, 'name': None,
'description': None, 'description': None,
'consistency_group_id': None, 'share_group_id': None,
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
'members': [],
} }
snap.update(**values) snap.update(**values)
expected_snap = copy.deepcopy(snap) expected_snap = copy.deepcopy(snap)
del expected_snap['user_id'] del expected_snap['user_id']
expected_snap['links'] = mock.ANY
return snap, expected_snap return snap, expected_snap
def _get_fake_simple_cgsnapshot(self, **values): def _get_fake_simple_share_group_snapshot(self, **values):
snap = { snap = {
'id': 'fake_id', 'id': 'fake_id',
'name': None, 'name': None,
@ -83,18 +82,16 @@ class CGSnapshotApiTest(test.TestCase):
snap.update(**values) snap.update(**values)
expected_snap = copy.deepcopy(snap) expected_snap = copy.deepcopy(snap)
expected_snap['links'] = mock.ANY
return snap, expected_snap return snap, expected_snap
def _get_fake_cgsnapshot_member(self, **values): def _get_fake_share_group_snapshot_member(self, **values):
member = { member = {
'id': 'fake_id', 'id': 'fake_id',
'user_id': 'fakeuser', 'user_id': 'fakeuser',
'project_id': 'fakeproject', 'project_id': 'fakeproject',
'status': constants.STATUS_CREATING, 'status': constants.STATUS_CREATING,
'cgsnapshot_id': None, 'share_group_snapshot_id': None,
'share_proto': None, 'share_proto': None,
'share_type_id': None,
'share_id': None, 'share_id': None,
'size': None, 'size': None,
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
@ -110,110 +107,133 @@ class CGSnapshotApiTest(test.TestCase):
return member, expected_member return member, expected_member
def test_create_invalid_body(self): def test_create_invalid_body(self):
body = {"not_cg_snapshot": {}} body = {"not_group_snapshot": {}}
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
self.request, body) self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller.create, self.request, body)
self.mock_policy_check.assert_called_once_with( self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create') self.context, self.resource_name, 'create')
def test_create_no_consistency_group_id(self): def test_create_no_share_group_id(self):
body = {"cgnapshot": {}} body = {"share_group_snapshot": {}}
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
self.request, body) self.assertRaises(
webob.exc.HTTPBadRequest,
self.controller.create, self.request, body)
self.mock_policy_check.assert_called_once_with( self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create') self.context, self.resource_name, 'create')
def test_create(self): 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()) fake_id = six.text_type(uuidutils.generate_uuid())
self.mock_object(self.controller.cg_api, 'create_cgsnapshot', body = {"share_group_snapshot": {"share_group_id": fake_id}}
mock.Mock(return_value=fake_snap)) mock_create = self.mock_object(
self.controller.share_group_api, 'create_share_group_snapshot',
body = {"cgsnapshot": {"consistency_group_id": fake_id}} mock.Mock(return_value=fake_snap))
res_dict = self.controller.create(self.request, body) res_dict = self.controller.create(self.request, body)
self.mock_policy_check.assert_called_once_with( self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create') self.context, self.resource_name, 'create')
self.controller.cg_api.create_cgsnapshot.assert_called_once_with( mock_create.assert_called_once_with(
self.context, consistency_group_id=fake_id) self.context, share_group_id=fake_id)
self.assertEqual(expected_snap, res_dict['cgsnapshot']) 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()) fake_id = six.text_type(uuidutils.generate_uuid())
self.mock_object(self.controller.cg_api, 'create_cgsnapshot', body = {"share_group_snapshot": {"share_group_id": fake_id}}
mock.Mock( self.mock_object(
side_effect=exception.ConsistencyGroupNotFound( self.controller.share_group_api, 'create_share_group_snapshot',
consistency_group_id=six.text_type( mock.Mock(side_effect=exception.ShareGroupNotFound(
uuidutils.generate_uuid()) 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create') self.context, self.resource_name, 'create')
def test_create_cg_does_not_a_uuid(self): def test_create_group_does_not_a_uuid(self):
self.mock_object(self.controller.cg_api, 'create_cgsnapshot', self.mock_object(
mock.Mock( self.controller.share_group_api, 'create_share_group_snapshot',
side_effect=exception.ConsistencyGroupNotFound( mock.Mock(side_effect=exception.ShareGroupNotFound(
consistency_group_id='not_a_uuid' 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create') 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()) fake_id = six.text_type(uuidutils.generate_uuid())
self.mock_object(self.controller.cg_api, 'create_cgsnapshot', body = {"share_group_snapshot": {"share_group_id": fake_id}}
mock.Mock( self.mock_object(
side_effect=exception.InvalidConsistencyGroup( self.controller.share_group_api, 'create_share_group_snapshot',
reason='bad_status' 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create') self.context, self.resource_name, 'create')
def test_create_with_name(self): def test_create_with_name(self):
fake_name = 'fake_name' 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()) fake_id = six.text_type(uuidutils.generate_uuid())
self.mock_object(self.controller.cg_api, 'create_cgsnapshot', mock_create = self.mock_object(
mock.Mock(return_value=fake_snap)) self.controller.share_group_api, 'create_share_group_snapshot',
mock.Mock(return_value=fake_snap))
body = {"cgsnapshot": {"consistency_group_id": fake_id, body = {
"name": fake_name}} "share_group_snapshot": {
"share_group_id": fake_id,
"name": fake_name,
}
}
res_dict = self.controller.create(self.request, body) res_dict = self.controller.create(self.request, body)
self.controller.cg_api.create_cgsnapshot.assert_called_once_with( res_dict['share_group_snapshot'].pop('links')
self.context, consistency_group_id=fake_id, name=fake_name)
self.assertEqual(expected_snap, res_dict['cgsnapshot']) 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create') self.context, self.resource_name, 'create')
def test_create_with_description(self): def test_create_with_description(self):
fake_description = 'fake_description' 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) description=fake_description)
fake_id = six.text_type(uuidutils.generate_uuid()) fake_id = six.text_type(uuidutils.generate_uuid())
self.mock_object(self.controller.cg_api, 'create_cgsnapshot', mock_create = self.mock_object(
mock.Mock(return_value=fake_snap)) self.controller.share_group_api, 'create_share_group_snapshot',
mock.Mock(return_value=fake_snap))
body = {"cgsnapshot": {"consistency_group_id": fake_id, body = {
"description": fake_description}} "share_group_snapshot": {
"share_group_id": fake_id,
"description": fake_description,
}
}
res_dict = self.controller.create(self.request, body) res_dict = self.controller.create(self.request, body)
self.controller.cg_api.create_cgsnapshot.assert_called_once_with( res_dict['share_group_snapshot'].pop('links')
self.context, consistency_group_id=fake_id,
description=fake_description) mock_create.assert_called_once_with(
self.assertEqual(expected_snap, res_dict['cgsnapshot']) 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create') self.context, self.resource_name, 'create')
@ -221,20 +241,27 @@ class CGSnapshotApiTest(test.TestCase):
fake_name = 'fake_name' fake_name = 'fake_name'
fake_description = 'fake_description' fake_description = 'fake_description'
fake_id = six.text_type(uuidutils.generate_uuid()) 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) description=fake_description, name=fake_name)
self.mock_object(self.controller.cg_api, 'create_cgsnapshot', mock_create = self.mock_object(
mock.Mock(return_value=fake_snap)) self.controller.share_group_api, 'create_share_group_snapshot',
mock.Mock(return_value=fake_snap))
body = {"cgsnapshot": {"consistency_group_id": fake_id, body = {
"description": fake_description, "share_group_snapshot": {
"name": fake_name}} "share_group_id": fake_id,
"description": fake_description,
"name": fake_name,
}
}
res_dict = self.controller.create(self.request, body) res_dict = self.controller.create(self.request, body)
self.controller.cg_api.create_cgsnapshot.assert_called_once_with( res_dict['share_group_snapshot'].pop('links')
self.context, consistency_group_id=fake_id, name=fake_name,
mock_create.assert_called_once_with(
self.context, share_group_id=fake_id, name=fake_name,
description=fake_description) 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'create') self.context, self.resource_name, 'create')
@ -242,36 +269,47 @@ class CGSnapshotApiTest(test.TestCase):
fake_name = 'fake_name' fake_name = 'fake_name'
fake_description = 'fake_description' fake_description = 'fake_description'
fake_id = six.text_type(uuidutils.generate_uuid()) 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) description=fake_description, name=fake_name)
self.mock_object(self.controller.cg_api, 'get_cgsnapshot', self.mock_object(
mock.Mock(return_value=fake_snap)) self.controller.share_group_api, 'get_share_group_snapshot',
self.mock_object(self.controller.cg_api, 'update_cgsnapshot', mock.Mock(return_value=fake_snap))
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, body = {
"name": fake_name}} "share_group_snapshot": {
"description": fake_description,
"name": fake_name,
}
}
res_dict = self.controller.update(self.request, fake_id, body) 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, self.context, fake_snap,
dict(name=fake_name, description=fake_description)) {"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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'update') self.context, self.resource_name, 'update')
def test_update_snapshot_not_found(self): def test_update_snapshot_not_found(self):
body = {"cgsnapshot": {}} body = {"share_group_snapshot": {}}
self.mock_object(self.controller.cg_api, 'get_cgsnapshot', self.mock_object(
mock.Mock(side_effect=exception.NotFound)) self.controller.share_group_api, 'get_share_group_snapshot',
self.assertRaises(webob.exc.HTTPNotFound, mock.Mock(side_effect=exception.NotFound))
self.controller.update,
self.request, 'fake_id', body) self.assertRaises(
webob.exc.HTTPNotFound,
self.controller.update, self.request, 'fake_id', body)
self.mock_policy_check.assert_called_once_with( self.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'update') self.context, self.resource_name, 'update')
def test_update_invalid_body(self): def test_update_invalid_body(self):
body = {"not_cgsnapshot": {}} body = {"not_group_snapshot": {}}
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.update, self.controller.update,
self.request, 'fake_id', body) self.request, 'fake_id', body)
@ -279,7 +317,7 @@ class CGSnapshotApiTest(test.TestCase):
self.context, self.resource_name, 'update') self.context, self.resource_name, 'update')
def test_update_invalid_body_invalid_field(self): def test_update_invalid_body_invalid_field(self):
body = {"cgsnapshot": {"unknown_field": ""}} body = {"share_group_snapshot": {"unknown_field": ""}}
exc = self.assertRaises(webob.exc.HTTPBadRequest, exc = self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.update, self.controller.update,
self.request, 'fake_id', body) self.request, 'fake_id', body)
@ -288,7 +326,7 @@ class CGSnapshotApiTest(test.TestCase):
self.context, self.resource_name, 'update') self.context, self.resource_name, 'update')
def test_update_invalid_body_readonly_field(self): def test_update_invalid_body_readonly_field(self):
body = {"cgsnapshot": {"created_at": []}} body = {"share_group_snapshot": {"created_at": []}}
exc = self.assertRaises(webob.exc.HTTPBadRequest, exc = self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.update, self.controller.update,
self.request, 'fake_id', body) self.request, 'fake_id', body)
@ -297,116 +335,147 @@ class CGSnapshotApiTest(test.TestCase):
self.context, self.resource_name, 'update') self.context, self.resource_name, 'update')
def test_list_index(self): def test_list_index(self):
fake_snap, expected_snap = self._get_fake_simple_cgsnapshot() fake_snap, expected_snap = self._get_fake_simple_share_group_snapshot()
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', self.mock_object(
mock.Mock(return_value=[fake_snap])) self.controller.share_group_api, 'get_all_share_group_snapshots',
mock.Mock(return_value=[fake_snap]))
res_dict = self.controller.index(self.request) 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'get_all') self.context, self.resource_name, 'get_all')
def test_list_index_no_cgs(self): def test_list_index_no_share_groups(self):
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', self.mock_object(
mock.Mock(return_value=[])) self.controller.share_group_api, 'get_all_share_group_snapshots',
mock.Mock(return_value=[]))
res_dict = self.controller.index(self.request) 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'get_all') self.context, self.resource_name, 'get_all')
def test_list_index_with_limit(self): def test_list_index_with_limit(self):
fake_snap, expected_snap = self._get_fake_simple_cgsnapshot() fake_snap, expected_snap = self._get_fake_simple_share_group_snapshot()
fake_snap2, expected_snap2 = self._get_fake_simple_cgsnapshot( fake_snap2, expected_snap2 = (
id="fake_id2") self._get_fake_simple_share_group_snapshot(
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', id="fake_id2"))
mock.Mock(return_value=[fake_snap, fake_snap2])) self.mock_object(
req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1', 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, version=self.api_version,
experimental=True) experimental=True)
req_context = req.environ['manila.context'] req_context = req.environ['manila.context']
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
self.assertEqual(1, len(res_dict['cgsnapshots'])) res_dict['share_group_snapshots'][0].pop('links')
self.assertEqual([expected_snap], res_dict['cgsnapshots'])
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( self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'get_all') req_context, self.resource_name, 'get_all')
def test_list_index_with_limit_and_offset(self): def test_list_index_with_limit_and_offset(self):
fake_snap, expected_snap = self._get_fake_simple_cgsnapshot() fake_snap, expected_snap = self._get_fake_simple_share_group_snapshot()
fake_snap2, expected_snap2 = self._get_fake_simple_cgsnapshot( fake_snap2, expected_snap2 = (
id="fake_id2") self._get_fake_simple_share_group_snapshot(id="fake_id2"))
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', self.mock_object(
mock.Mock(return_value=[fake_snap, fake_snap2])) self.controller.share_group_api, 'get_all_share_group_snapshots',
req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1&offset=1', mock.Mock(return_value=[fake_snap, fake_snap2]))
version=self.api_version, req = fakes.HTTPRequest.blank(
experimental=True) '/share-group-snapshots?limit=1&offset=1',
version=self.api_version, experimental=True)
req_context = req.environ['manila.context'] req_context = req.environ['manila.context']
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
self.assertEqual(1, len(res_dict['cgsnapshots'])) res_dict['share_group_snapshots'][0].pop('links')
self.assertEqual([expected_snap2], res_dict['cgsnapshots'])
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( self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'get_all') req_context, self.resource_name, 'get_all')
def test_list_detail(self): def test_list_detail(self):
fake_snap, expected_snap = self._get_fake_cgsnapshot() fake_snap, expected_snap = self._get_fake_share_group_snapshot()
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', self.mock_object(
mock.Mock(return_value=[fake_snap])) self.controller.share_group_api, 'get_all_share_group_snapshots',
mock.Mock(return_value=[fake_snap]))
res_dict = self.controller.detail(self.request) 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'get_all') self.context, self.resource_name, 'get_all')
def test_list_detail_no_cgs(self): def test_list_detail_no_share_groups(self):
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', self.mock_object(
mock.Mock(return_value=[])) self.controller.share_group_api, 'get_all_share_group_snapshots',
mock.Mock(return_value=[]))
res_dict = self.controller.detail(self.request) 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'get_all') self.context, self.resource_name, 'get_all')
def test_list_detail_with_limit(self): def test_list_detail_with_limit(self):
fake_snap, expected_snap = self._get_fake_cgsnapshot() fake_snap, expected_snap = self._get_fake_share_group_snapshot()
fake_snap2, expected_snap2 = self._get_fake_cgsnapshot( fake_snap2, expected_snap2 = self._get_fake_share_group_snapshot(
id="fake_id2") id="fake_id2")
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', self.mock_object(
mock.Mock(return_value=[fake_snap, fake_snap2])) self.controller.share_group_api, 'get_all_share_group_snapshots',
req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1', mock.Mock(return_value=[fake_snap, fake_snap2]))
req = fakes.HTTPRequest.blank('/share-group-snapshots?limit=1',
version=self.api_version, version=self.api_version,
experimental=True) experimental=True)
req_context = req.environ['manila.context'] req_context = req.environ['manila.context']
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
self.assertEqual(1, len(res_dict['cgsnapshots'])) res_dict['share_group_snapshots'][0].pop('links')
self.assertEqual([expected_snap], res_dict['cgsnapshots'])
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( self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'get_all') req_context, self.resource_name, 'get_all')
def test_list_detail_with_limit_and_offset(self): def test_list_detail_with_limit_and_offset(self):
fake_snap, expected_snap = self._get_fake_cgsnapshot() fake_snap, expected_snap = self._get_fake_share_group_snapshot()
fake_snap2, expected_snap2 = self._get_fake_cgsnapshot( fake_snap2, expected_snap2 = self._get_fake_share_group_snapshot(
id="fake_id2") id="fake_id2")
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots', self.mock_object(
mock.Mock(return_value=[fake_snap, fake_snap2])) self.controller.share_group_api, 'get_all_share_group_snapshots',
req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1&offset=1', mock.Mock(return_value=[fake_snap, fake_snap2]))
version=self.api_version, req = fakes.HTTPRequest.blank(
experimental=True) '/share-group-snapshots?limit=1&offset=1',
version=self.api_version,
experimental=True)
req_context = req.environ['manila.context'] req_context = req.environ['manila.context']
res_dict = self.controller.detail(req) res_dict = self.controller.detail(req)
self.assertEqual(1, len(res_dict['cgsnapshots'])) res_dict['share_group_snapshots'][0].pop('links')
self.assertEqual([expected_snap2], res_dict['cgsnapshots'])
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( self.mock_policy_check.assert_called_once_with(
req_context, self.resource_name, 'get_all') req_context, self.resource_name, 'get_all')
def test_delete(self): def test_delete(self):
fake_snap, expected_snap = self._get_fake_cgsnapshot() fake_snap, expected_snap = self._get_fake_share_group_snapshot()
self.mock_object(self.controller.cg_api, 'get_cgsnapshot', self.mock_object(
mock.Mock(return_value=fake_snap)) self.controller.share_group_api, 'get_share_group_snapshot',
self.mock_object(self.controller.cg_api, 'delete_cgsnapshot') 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']) res = self.controller.delete(self.request, fake_snap['id'])
@ -415,134 +484,89 @@ class CGSnapshotApiTest(test.TestCase):
self.context, self.resource_name, 'delete') self.context, self.resource_name, 'delete')
def test_delete_not_found(self): def test_delete_not_found(self):
fake_snap, expected_snap = self._get_fake_cgsnapshot() fake_snap, expected_snap = self._get_fake_share_group_snapshot()
self.mock_object(self.controller.cg_api, 'get_cgsnapshot', self.mock_object(
mock.Mock(side_effect=exception.NotFound)) 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'delete') self.context, self.resource_name, 'delete')
def test_delete_in_conflicting_status(self): def test_delete_in_conflicting_status(self):
fake_snap, expected_snap = self._get_fake_cgsnapshot() fake_snap, expected_snap = self._get_fake_share_group_snapshot()
self.mock_object(self.controller.cg_api, 'get_cgsnapshot', self.mock_object(
mock.Mock(return_value=fake_snap)) self.controller.share_group_api, 'get_share_group_snapshot',
self.mock_object(self.controller.cg_api, 'delete_cgsnapshot', mock.Mock(return_value=fake_snap))
mock.Mock( self.mock_object(
side_effect=exception.InvalidCGSnapshot( self.controller.share_group_api, 'delete_share_group_snapshot',
reason='blah'))) 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'delete') self.context, self.resource_name, 'delete')
def test_show(self): def test_show(self):
fake_snap, expected_snap = self._get_fake_cgsnapshot() fake_snap, expected_snap = self._get_fake_share_group_snapshot()
self.mock_object(self.controller.cg_api, 'get_cgsnapshot', self.mock_object(
mock.Mock(return_value=fake_snap)) 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']) 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.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): def test_show_share_group_not_found(self):
fake_snap, expected_snap = self._get_fake_cgsnapshot() fake_snap, expected_snap = self._get_fake_share_group_snapshot()
self.mock_object(self.controller.cg_api, 'get_cgsnapshot', self.mock_object(
mock.Mock(side_effect=exception.NotFound)) 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.mock_policy_check.assert_called_once_with(
self.context, self.resource_name, 'get_cgsnapshot') self.context, self.resource_name, 'get')
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')
def _get_context(self, role): def _get_context(self, role):
return getattr(self, '%s_context' % role) return getattr(self, '%s_context' % role)
def _setup_cgsnapshot_data(self, cgsnapshot=None, version='2.7'): def _setup_share_group_snapshot_data(self, share_group_snapshot=None,
if cgsnapshot is None: version='2.31'):
cgsnapshot = db_utils.create_cgsnapshot( if share_group_snapshot is None:
share_group_snapshot = db_utils.create_share_group_snapshot(
'fake_id', status=constants.STATUS_AVAILABLE) 'fake_id', status=constants.STATUS_AVAILABLE)
req = fakes.HTTPRequest.blank('/v2/fake/cgsnapshots/%s/action' % req = fakes.HTTPRequest.blank(
cgsnapshot['id'], version=version) '/v2/fake/share-group-snapshots/%s/action' %
share_group_snapshot['id'], version=version)
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = version req.headers[wsgi.API_VERSION_REQUEST_HEADER] = version
req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True' 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.data(*fakes.fixture_force_delete_with_different_roles)
@ddt.unpack @ddt.unpack
def test_cgsnapshot_force_delete_with_different_roles(self, role, def test_share_group_snapshot_force_delete_with_different_roles(
resp_code, version): self, role, resp_code, version):
cgsnap, req = self._setup_cgsnapshot_data() group_snap, req = self._setup_share_group_snapshot_data()
ctxt = self._get_context(role) ctxt = self._get_context(role)
req.method = 'POST' req.method = 'POST'
req.headers['content-type'] = 'application/json' req.headers['content-type'] = 'application/json'
if float(version) > 2.6: action_name = 'force_delete'
action_name = 'force_delete'
else:
action_name = 'os-force_delete'
body = {action_name: {'status': constants.STATUS_ERROR}} body = {action_name: {'status': constants.STATUS_ERROR}}
req.body = six.b(jsonutils.dumps(body)) 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 req.environ['manila.context'] = ctxt
with mock.patch.object( with mock.patch.object(
@ -554,19 +578,16 @@ class CGSnapshotApiTest(test.TestCase):
@ddt.data(*fakes.fixture_reset_status_with_different_roles) @ddt.data(*fakes.fixture_reset_status_with_different_roles)
@ddt.unpack @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): self, role, valid_code, valid_status, version):
ctxt = self._get_context(role) ctxt = self._get_context(role)
cgsnap, req = self._setup_cgsnapshot_data(version=version) group_snap, req = self._setup_share_group_snapshot_data()
if float(version) > 2.6: action_name = 'reset_status'
action_name = 'reset_status'
else:
action_name = 'os-reset_status'
body = {action_name: {'status': constants.STATUS_ERROR}} body = {action_name: {'status': constants.STATUS_ERROR}}
req.method = 'POST' req.method = 'POST'
req.headers['content-type'] = 'application/json' req.headers['content-type'] = 'application/json'
req.body = six.b(jsonutils.dumps(body)) 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 req.environ['manila.context'] = ctxt
with mock.patch.object( with mock.patch.object(
@ -578,9 +599,9 @@ class CGSnapshotApiTest(test.TestCase):
if valid_code == 404: if valid_code == 404:
self.assertRaises(exception.NotFound, self.assertRaises(exception.NotFound,
db.cgsnapshot_get, db.share_group_snapshot_get,
ctxt, ctxt,
cgsnap['id']) group_snap['id'])
else: 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']) 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'], assert_called_once_with(self.req.environ['manila.context'],
share_nw['id']) 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() share_nw = fake_share_network.copy()
self.mock_object(db_api, 'share_network_get', self.mock_object(db_api, 'share_network_get',
mock.Mock(return_value=share_nw)) 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)) mock.Mock(return_value=2))
self.assertRaises(webob_exc.HTTPConflict, self.assertRaises(webob_exc.HTTPConflict,

View File

@ -117,8 +117,6 @@ class ShareAPITest(test.TestCase):
'volume_type': '1', 'volume_type': '1',
'snapshot_support': True, 'snapshot_support': True,
'is_public': False, 'is_public': False,
'consistency_group_id': None,
'source_cgsnapshot_member_id': None,
'task_state': None, 'task_state': None,
'share_type_name': None, 'share_type_name': None,
'links': [ 'links': [
@ -406,8 +404,6 @@ class ShareAPITest(test.TestCase):
expected['share'].pop('snapshot_support') expected['share'].pop('snapshot_support')
expected['share'].pop('share_type_name') expected['share'].pop('share_type_name')
expected['share'].pop('task_state') expected['share'].pop('task_state')
expected['share'].pop('consistency_group_id')
expected['share'].pop('source_cgsnapshot_member_id')
self.assertEqual(expected, res_dict) self.assertEqual(expected, res_dict)
@ddt.data("2.2", "2.3") @ddt.data("2.2", "2.3")
@ -421,23 +417,28 @@ class ShareAPITest(test.TestCase):
expected = self._get_expected_share_detailed_response(self.share) expected = self._get_expected_share_detailed_response(self.share)
expected['share'].pop('share_type_name') expected['share'].pop('share_type_name')
expected['share'].pop('task_state') expected['share'].pop('task_state')
expected['share'].pop('consistency_group_id')
expected['share'].pop('source_cgsnapshot_member_id')
self.assertEqual(expected, res_dict) self.assertEqual(expected, res_dict)
@ddt.data("2.4", "2.5") @ddt.data("2.31")
def test_share_create_with_consistency_group(self, microversion): def test_share_create_with_share_group(self, microversion):
self.mock_object(share_api.API, 'create', self.create_mock) self.mock_object(share_api.API, 'create', self.create_mock)
body = {"share": copy.deepcopy(self.share)} 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) res_dict = self.controller.create(req, body)
expected = self._get_expected_share_detailed_response(self.share) expected = self._get_expected_share_detailed_response(self.share)
expected['share'].pop('share_type_name') expected['share'].pop('export_location')
if (api_version.APIVersionRequest(microversion) == expected['share'].pop('export_locations')
api_version.APIVersionRequest('2.4')): expected['share']['access_rules_status'] = 'active'
expected['share'].pop('task_state') 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) self.assertEqual(expected, res_dict)
def test_share_create_with_valid_default_share_type(self): 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 = self._get_expected_share_detailed_response(self.share)
expected['share']['task_state'] = None 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']['replication_type'] = None
expected['share']['share_type_name'] = None expected['share']['share_type_name'] = None
expected['share']['has_replicas'] = False expected['share']['has_replicas'] = False
@ -515,7 +514,7 @@ class ShareAPITest(test.TestCase):
res_dict = self.controller.create(req, body) res_dict = self.controller.create(req, body)
expected = self._get_expected_share_detailed_response(shr) expected = self._get_expected_share_detailed_response(shr)
self.assertEqual(expected, res_dict) self.assertDictMatch(expected, res_dict)
self.assertEqual("fakenetid", self.assertEqual("fakenetid",
create_mock.call_args[1]['share_network_id']) create_mock.call_args[1]['share_network_id'])
@ -534,8 +533,6 @@ class ShareAPITest(test.TestCase):
else: else:
self.assertNotIn('user_id', expected['share']) self.assertNotIn('user_id', expected['share'])
expected['share']['task_state'] = None 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']['replication_type'] = None
expected['share']['share_type_name'] = None expected['share']['share_type_name'] = None
expected['share']['has_replicas'] = False expected['share']['has_replicas'] = False
@ -1084,7 +1081,7 @@ class ShareAPITest(test.TestCase):
req = fakes.HTTPRequest.blank('/shares', version='2.7') req = fakes.HTTPRequest.blank('/shares', version='2.7')
res_dict = self.controller.create(req, body) res_dict = self.controller.create(req, body)
expected = self._get_expected_share_detailed_response(shr) expected = self._get_expected_share_detailed_response(shr)
self.assertEqual(expected, res_dict) self.assertDictMatch(expected, res_dict)
self.assertEqual(parent_share_net, self.assertEqual(parent_share_net,
create_mock.call_args[1]['share_network_id']) create_mock.call_args[1]['share_network_id'])
@ -1165,29 +1162,49 @@ class ShareAPITest(test.TestCase):
expected['share'].pop('snapshot_support') expected['share'].pop('snapshot_support')
expected['share'].pop('share_type_name') expected['share'].pop('share_type_name')
expected['share'].pop('task_state') 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') res_dict = self.controller.show(req, '1')
self.assertEqual(expected, res_dict) self.assertEqual(expected, res_dict)
def test_share_show_with_consistency_group(self): def test_share_show_with_share_group(self):
req = fakes.HTTPRequest.blank('/shares/1', version='2.4') req = fakes.HTTPRequest.blank(
'/shares/1', version='2.31', experimental=True)
expected = self._get_expected_share_detailed_response() expected = self._get_expected_share_detailed_response()
expected['share'].pop('share_type_name') expected['share'].pop('export_location')
expected['share'].pop('task_state') 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') 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): def test_share_show_with_share_type_name(self):
req = fakes.HTTPRequest.blank('/shares/1', version='2.6') req = fakes.HTTPRequest.blank('/shares/1', version='2.6')
res_dict = self.controller.show(req, '1') res_dict = self.controller.show(req, '1')
expected = self._get_expected_share_detailed_response() 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']['share_type_name'] = None
expected['share']['task_state'] = None expected['share']['task_state'] = None
self.assertEqual(expected, res_dict) self.assertEqual(expected, res_dict)
@ -1204,8 +1221,6 @@ class ShareAPITest(test.TestCase):
expected['share']['user_id'] = 'fakeuser' expected['share']['user_id'] = 'fakeuser'
else: else:
self.assertNotIn('user_id', expected['share']) 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']['share_type_name'] = None
expected['share']['task_state'] = None expected['share']['task_state'] = None
expected['share']['access_rules_status'] = 'active' expected['share']['access_rules_status'] = 'active'
@ -1222,8 +1237,6 @@ class ShareAPITest(test.TestCase):
expected['share'].pop('snapshot_support') expected['share'].pop('snapshot_support')
expected['share'].pop('share_type_name') expected['share'].pop('share_type_name')
expected['share'].pop('task_state') 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') res_dict = self.controller.show(req, '1')
@ -1245,8 +1258,6 @@ class ShareAPITest(test.TestCase):
expected = self._get_expected_share_detailed_response() expected = self._get_expected_share_detailed_response()
expected['share']['task_state'] = None 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']['access_rules_status'] = 'active'
expected['share']['share_type_name'] = None expected['share']['share_type_name'] = None
expected['share']['replication_type'] = None expected['share']['replication_type'] = None
@ -1289,32 +1300,32 @@ class ShareAPITest(test.TestCase):
self.assertRaises( self.assertRaises(
webob.exc.HTTPConflict, self.controller.delete, req, 1) 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', 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', self.mock_object(share_api.API, 'get',
mock.Mock(return_value=fake_share)) mock.Mock(return_value=fake_share))
req = fakes.HTTPRequest.blank('/shares/1') req = fakes.HTTPRequest.blank('/shares/1')
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete, req, 1) 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', 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', self.mock_object(share_api.API, 'get',
mock.Mock(return_value=fake_share)) mock.Mock(return_value=fake_share))
req = fakes.HTTPRequest.blank( 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) resp = self.controller.delete(req, 1)
self.assertEqual(202, resp.status_int) 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', 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', self.mock_object(share_api.API, 'get',
mock.Mock(return_value=fake_share)) mock.Mock(return_value=fake_share))
req = fakes.HTTPRequest.blank( 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.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete, req, 1) self.controller.delete, req, 1)
@ -1330,14 +1341,18 @@ class ShareAPITest(test.TestCase):
self.assertEqual(shr['is_public'], self.assertEqual(shr['is_public'],
res_dict['share']['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 shr = self.share
body = {"share": shr} 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) 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): def test_share_not_updates_size(self):
req = fakes.HTTPRequest.blank('/share/1') req = fakes.HTTPRequest.blank('/share/1')
@ -1570,7 +1585,7 @@ class ShareAPITest(test.TestCase):
self.mock_object(share_api.API, 'get_all', self.mock_object(share_api.API, 'get_all',
stubs.stub_share_get_all_by_project) stubs.stub_share_get_all_by_project)
res_dict = self.controller.detail(req) 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'], self.assertEqual(res_dict['shares'][0]['volume_type'],
res_dict['shares'][0]['share_type']) res_dict['shares'][0]['share_type'])
@ -1581,13 +1596,23 @@ class ShareAPITest(test.TestCase):
expected['shares'][0].pop('snapshot_support') expected['shares'][0].pop('snapshot_support')
self._list_detail_test_common(req, expected) 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'} env = {'QUERY_STRING': 'name=Share+Test+Name'}
req = fakes.HTTPRequest.blank('/shares/detail', environ=env, req = fakes.HTTPRequest.blank(
version="2.4") '/shares/detail', environ=env, version="2.31", experimental=True)
expected = self._list_detail_common_expected() expected = self._list_detail_common_expected()
expected['shares'][0]['consistency_group_id'] = None expected['shares'][0]['task_state'] = None
expected['shares'][0]['source_cgsnapshot_member_id'] = 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) self._list_detail_test_common(req, expected)
def test_share_list_detail_with_task_state(self): 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, req = fakes.HTTPRequest.blank('/shares/detail', environ=env,
version="2.5") version="2.5")
expected = self._list_detail_common_expected() 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]['task_state'] = None
self._list_detail_test_common(req, expected) self._list_detail_test_common(req, expected)
@ -1605,8 +1628,6 @@ class ShareAPITest(test.TestCase):
req = fakes.HTTPRequest.blank('/shares/detail', environ=env, req = fakes.HTTPRequest.blank('/shares/detail', environ=env,
version="2.9") version="2.9")
expected = self._list_detail_common_expected() 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]['task_state'] = None
expected['shares'][0]['share_type_name'] = None expected['shares'][0]['share_type_name'] = None
expected['shares'][0].pop('export_location') expected['shares'][0].pop('export_location')
@ -1642,8 +1663,6 @@ class ShareAPITest(test.TestCase):
'share_type': '1', 'share_type': '1',
'volume_type': '1', 'volume_type': '1',
'is_public': False, 'is_public': False,
'consistency_group_id': None,
'source_cgsnapshot_member_id': None,
'snapshot_support': True, 'snapshot_support': True,
'has_replicas': False, 'has_replicas': False,
'replication_type': None, 'replication_type': None,
@ -2463,8 +2482,6 @@ class ShareManageTest(test.TestCase):
'share_type': '1', 'share_type': '1',
'volume_type': '1', 'volume_type': '1',
'is_public': False, 'is_public': False,
'consistency_group_id': None,
'source_cgsnapshot_member_id': None,
'snapshot_support': True, 'snapshot_support': True,
'task_state': None, 'task_state': None,
'links': [ 'links': [

View File

@ -1838,3 +1838,181 @@ class AddCastRulesToReadonlyToInstances(BaseMigrationChecks):
for instance in engine.execute(share_instances_table.select()): for instance in engine.execute(share_instances_table.select()):
self.test_case.assertNotIn('cast_rules_to_readonly', instance) 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(1, len(actual_result))
self.assertEqual(share['id'], actual_result[0].id) self.assertEqual(share['id'], actual_result[0].id)
def test_share_filter_all_by_consistency_group(self): def test_share_filter_all_by_share_group(self):
cg = db_utils.create_consistency_group() group = db_utils.create_share_group()
share = db_utils.create_share(consistency_group_id=cg['id']) share = db_utils.create_share(share_group_id=group['id'])
actual_result = db_api.share_get_all_by_consistency_group_id( actual_result = db_api.share_get_all_by_share_group_id(
self.ctxt, cg['id']) self.ctxt, group['id'])
self.assertEqual(1, len(actual_result)) self.assertEqual(1, len(actual_result))
self.assertEqual(share['id'], actual_result[0].id) self.assertEqual(share['id'], actual_result[0].id)
@ -322,20 +322,20 @@ class ShareDatabaseAPITestCase(test.TestCase):
self.assertEqual(0, len(instances)) self.assertEqual(0, len(instances))
def test_share_instance_get_all_by_consistency_group(self): def test_share_instance_get_all_by_share_group(self):
cg = db_utils.create_consistency_group() group = db_utils.create_share_group()
db_utils.create_share(consistency_group_id=cg['id']) db_utils.create_share(share_group_id=group['id'])
db_utils.create_share() db_utils.create_share()
instances = db_api.share_instances_get_all_by_consistency_group_id( instances = db_api.share_instances_get_all_by_share_group_id(
self.ctxt, cg['id']) self.ctxt, group['id'])
self.assertEqual(1, len(instances)) self.assertEqual(1, len(instances))
instance = instances[0] instance = instances[0]
self.assertEqual('share-%s' % instance['id'], instance['name']) 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): def test_share_get_all_sort_by_share_instance_fields(self, sort_key):
shares = [db_utils.create_share(**{sort_key: n, 'size': 1}) shares = [db_utils.create_share(**{sort_key: n, 'size': 1})
for n in ('test1', 'test2')] for n in ('test1', 'test2')]
@ -384,7 +384,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
expected_share_keys = { expected_share_keys = {
'project_id', 'share_type_id', 'display_name', 'project_id', 'share_type_id', 'display_name',
'name', 'share_proto', 'is_public', 'name', 'share_proto', 'is_public',
'source_cgsnapshot_member_id', 'source_share_group_snapshot_member_id',
} }
session = db_api.get_session() session = db_api.get_session()
@ -432,7 +432,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
expected_share_keys = { expected_share_keys = {
'project_id', 'share_type_id', 'display_name', 'project_id', 'share_type_id', 'display_name',
'name', 'share_proto', 'is_public', 'name', 'share_proto', 'is_public',
'source_cgsnapshot_member_id', 'source_share_group_snapshot_member_id',
} }
session = db_api.get_session() session = db_api.get_session()
@ -497,7 +497,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
expected_share_keys = { expected_share_keys = {
'project_id', 'share_type_id', 'display_name', 'project_id', 'share_type_id', 'display_name',
'name', 'share_proto', 'is_public', 'name', 'share_proto', 'is_public',
'source_cgsnapshot_member_id', 'source_share_group_snapshot_member_id',
} }
with session.begin(): with session.begin():
@ -542,7 +542,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
expected_extra_keys = { expected_extra_keys = {
'project_id', 'share_type_id', 'display_name', 'project_id', 'share_type_id', 'display_name',
'name', 'share_proto', 'is_public', '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']) share_replica = db_api.share_replica_get(self.ctxt, replica['id'])
@ -559,7 +559,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
expected_extra_keys = { expected_extra_keys = {
'project_id', 'share_type_id', 'display_name', 'project_id', 'share_type_id', 'display_name',
'name', 'share_proto', 'is_public', 'name', 'share_proto', 'is_public',
'source_cgsnapshot_member_id', 'source_share_group_snapshot_member_id',
} }
share_replica = db_api.share_replica_get( share_replica = db_api.share_replica_get(
@ -639,273 +639,305 @@ class ShareDatabaseAPITestCase(test.TestCase):
@ddt.ddt @ddt.ddt
class ConsistencyGroupDatabaseAPITestCase(test.TestCase): class ShareGroupDatabaseAPITestCase(test.TestCase):
def setUp(self): def setUp(self):
"""Run before each test.""" """Run before each test."""
super(ConsistencyGroupDatabaseAPITestCase, self).setUp() super(ShareGroupDatabaseAPITestCase, self).setUp()
self.ctxt = context.get_admin_context() 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"] fake_share_types = ["fake_share_type"]
cg = db_utils.create_consistency_group(share_types=fake_share_types) share_group = db_utils.create_share_group(share_types=fake_share_types)
cg = db_api.consistency_group_get(self.ctxt, cg['id']) 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): def test_share_group_get(self):
cg = db_utils.create_consistency_group() share_group = db_utils.create_share_group()
self.assertDictMatch(dict(cg), self.assertDictMatch(
dict(db_api.consistency_group_get(self.ctxt, dict(share_group),
cg['id']))) 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() share_network = db_utils.create_share_network()
db_utils.create_consistency_group() db_utils.create_share_group()
db_utils.create_consistency_group(share_network_id=share_network['id']) 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.ctxt, share_network_id=share_network['id'])
self.assertEqual(1, count) self.assertEqual(1, count)
def test_consistency_group_get_all(self): def test_share_group_get_all(self):
expected_cg = db_utils.create_consistency_group() 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)) self.assertEqual(1, len(share_groups))
cg = cgs[0] share_group = share_groups[0]
self.assertEqual(2, len(dict(cg).keys())) self.assertEqual(2, len(dict(share_group).keys()))
self.assertEqual(expected_cg['id'], cg['id']) self.assertEqual(expected_share_group['id'], share_group['id'])
self.assertEqual(expected_cg['name'], cg['name']) self.assertEqual(expected_share_group['name'], share_group['name'])
def test_consistency_group_get_all_with_detail(self): def test_share_group_get_all_with_detail(self):
expected_cg = db_utils.create_consistency_group() 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)) self.assertEqual(1, len(share_groups))
cg = cgs[0] self.assertDictMatch(dict(expected_share_group), dict(share_groups[0]))
self.assertDictMatch(dict(expected_cg), dict(cg))
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' fake_project = 'fake_project'
expected_cg = db_utils.create_consistency_group( expected_group = db_utils.create_share_group(
project_id=fake_project) 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, groups = db_api.share_group_get_all_by_project(self.ctxt,
fake_project, fake_project,
detailed=False) detailed=False)
self.assertEqual(1, len(cgs)) self.assertEqual(1, len(groups))
cg = cgs[0] group = groups[0]
self.assertEqual(2, len(dict(cg).keys())) self.assertEqual(2, len(dict(group).keys()))
self.assertEqual(expected_cg['id'], cg['id']) self.assertEqual(expected_group['id'], group['id'])
self.assertEqual(expected_cg['name'], cg['name']) 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 fake_server = 123
expected_cg = db_utils.create_consistency_group( expected_group = db_utils.create_share_group(
share_server_id=fake_server) 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, groups = db_api.share_group_get_all_by_share_server(self.ctxt,
fake_server) fake_server)
self.assertEqual(1, len(cgs)) self.assertEqual(1, len(groups))
cg = cgs[0] group = groups[0]
self.assertEqual(expected_cg['id'], cg['id']) self.assertEqual(expected_group['id'], group['id'])
self.assertEqual(expected_cg['name'], cg['name']) 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' fake_project = 'fake_project'
expected_cg = db_utils.create_consistency_group( expected_group = db_utils.create_share_group(
project_id=fake_project) 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, groups = db_api.share_group_get_all_by_project(self.ctxt,
fake_project, fake_project,
detailed=True) detailed=True)
self.assertEqual(1, len(cgs)) self.assertEqual(1, len(groups))
cg = cgs[0] group = groups[0]
self.assertDictMatch(dict(expected_cg), dict(cg)) self.assertDictMatch(dict(expected_group), dict(group))
self.assertEqual(fake_project, cg['project_id']) self.assertEqual(fake_project, group['project_id'])
def test_consistency_group_update(self): def test_share_group_update(self):
fake_name = "my_fake_name" fake_name = "my_fake_name"
expected_cg = db_utils.create_consistency_group() expected_group = db_utils.create_share_group()
expected_cg['name'] = fake_name expected_group['name'] = fake_name
db_api.consistency_group_update(self.ctxt, db_api.share_group_update(self.ctxt,
expected_cg['id'], expected_group['id'],
{'name': fake_name}) {'name': fake_name})
cg = db_api.consistency_group_get(self.ctxt, expected_cg['id']) group = db_api.share_group_get(self.ctxt, expected_group['id'])
self.assertEqual(fake_name, cg['name']) self.assertEqual(fake_name, group['name'])
def test_consistency_group_destroy(self): def test_share_group_destroy(self):
cg = db_utils.create_consistency_group() group = db_utils.create_share_group()
db_api.consistency_group_get(self.ctxt, cg['id']) 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.assertRaises(exception.NotFound, db_api.share_group_get,
self.ctxt, cg['id']) self.ctxt, group['id'])
def test_count_shares_in_consistency_group(self): def test_count_shares_in_share_group(self):
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
db_utils.create_share(consistency_group_id=cg['id']) db_utils.create_share(share_group_id=sg['id'])
db_utils.create_share() 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) self.assertEqual(1, count)
def test_count_cgsnapshots_in_consistency_group(self): def test_count_sg_snapshots_in_share_group(self):
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
db_utils.create_cgsnapshot(cg['id']) db_utils.create_share_group_snapshot(sg['id'])
db_utils.create_cgsnapshot(cg['id']) db_utils.create_share_group_snapshot(sg['id'])
count = db_api.count_cgsnapshots_in_consistency_group(self.ctxt, count = db_api.count_share_group_snapshots_in_share_group(
cg['id']) self.ctxt, sg['id'])
self.assertEqual(2, count) self.assertEqual(2, count)
def test_cgsnapshot_get(self): def test_share_group_snapshot_get(self):
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
cgsnap = db_utils.create_cgsnapshot(cg['id']) sg_snap = db_utils.create_share_group_snapshot(sg['id'])
self.assertDictMatch(dict(cgsnap), self.assertDictMatch(
dict(db_api.cgsnapshot_get(self.ctxt, dict(sg_snap),
cgsnap['id']))) dict(db_api.share_group_snapshot_get(self.ctxt, sg_snap['id'])))
def test_cgsnapshot_get_all(self): def test_share_group_snapshot_get_all(self):
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
expected_cgsnap = db_utils.create_cgsnapshot(cg['id']) 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)) self.assertEqual(1, len(snaps))
snap = snaps[0] snap = snaps[0]
self.assertEqual(2, len(dict(snap).keys())) self.assertEqual(2, len(dict(snap).keys()))
self.assertEqual(expected_cgsnap['id'], snap['id']) self.assertEqual(expected_sg_snap['id'], snap['id'])
self.assertEqual(expected_cgsnap['name'], snap['name']) self.assertEqual(expected_sg_snap['name'], snap['name'])
def test_cgsnapshot_get_all_with_detail(self): def test_share_group_snapshot_get_all_with_detail(self):
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
expected_cgsnap = db_utils.create_cgsnapshot(cg['id']) 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)) self.assertEqual(1, len(snaps))
snap = snaps[0] 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): def test_share_group_snapshot_get_all_by_project(self):
fake_project = 'fake_project' fake_project = uuidutils.generate_uuid()
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'], expected_sg_snap = db_utils.create_share_group_snapshot(
project_id=fake_project) sg['id'], project_id=fake_project)
snaps = db_api.cgsnapshot_get_all_by_project(self.ctxt, snaps = db_api.share_group_snapshot_get_all_by_project(
fake_project, self.ctxt, fake_project, detailed=False)
detailed=False)
self.assertEqual(1, len(snaps)) self.assertEqual(1, len(snaps))
snap = snaps[0] snap = snaps[0]
self.assertEqual(2, len(dict(snap).keys())) self.assertEqual(2, len(dict(snap).keys()))
self.assertEqual(expected_cgsnap['id'], snap['id']) self.assertEqual(expected_sg_snap['id'], snap['id'])
self.assertEqual(expected_cgsnap['name'], snap['name']) self.assertEqual(expected_sg_snap['name'], snap['name'])
def test_cgsnapshot_get_all_by_project_with_details(self): def test_share_group_snapshot_get_all_by_project_with_details(self):
fake_project = 'fake_project' fake_project = uuidutils.generate_uuid()
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'], expected_sg_snap = db_utils.create_share_group_snapshot(
project_id=fake_project) sg['id'], project_id=fake_project)
snaps = db_api.cgsnapshot_get_all_by_project(self.ctxt, snaps = db_api.share_group_snapshot_get_all_by_project(
fake_project, self.ctxt, fake_project, detailed=True)
detailed=True)
self.assertEqual(1, len(snaps)) self.assertEqual(1, len(snaps))
snap = snaps[0] 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']) self.assertEqual(fake_project, snap['project_id'])
def test_cgsnapshot_update(self): def test_share_group_snapshot_update(self):
fake_name = "my_fake_name" fake_name = "my_fake_name"
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
expected_cgsnap = db_utils.create_cgsnapshot(cg['id']) expected_sg_snap = db_utils.create_share_group_snapshot(sg['id'])
expected_cgsnap['name'] = fake_name expected_sg_snap['name'] = fake_name
db_api.cgsnapshot_update(self.ctxt, expected_cgsnap['id'], db_api.share_group_snapshot_update(
{'name': fake_name}) self.ctxt, expected_sg_snap['id'], {'name': fake_name})
cgsnap = db_api.cgsnapshot_get(self.ctxt, expected_cgsnap['id']) sg_snap = db_api.share_group_snapshot_get(
self.assertEqual(fake_name, cgsnap['name']) self.ctxt, expected_sg_snap['id'])
self.assertEqual(fake_name, sg_snap['name'])
def test_cgsnapshot_destroy(self): def test_share_group_snapshot_destroy(self):
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
cgsnap = db_utils.create_cgsnapshot(cg['id']) sg_snap = db_utils.create_share_group_snapshot(sg['id'])
db_api.cgsnapshot_get(self.ctxt, cgsnap['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.assertRaises(
self.ctxt, cgsnap['id']) exception.NotFound,
db_api.share_group_snapshot_get, self.ctxt, sg_snap['id'])
def test_cgsnapshot_members_get_all(self): def test_share_group_snapshot_members_get_all(self):
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
cgsnap = db_utils.create_cgsnapshot(cg['id']) sg_snap = db_utils.create_share_group_snapshot(sg['id'])
expected_member = db_utils.create_cgsnapshot_member(cgsnap['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)) self.assertEqual(1, len(members))
member = members[0] self.assertDictMatch(dict(expected_member), dict(members[0]))
self.assertDictMatch(dict(expected_member), dict(member))
def test_count_cgsnapshot_members_in_share(self): def test_count_share_group_snapshot_members_in_share(self):
share = db_utils.create_share() share = db_utils.create_share()
share2 = db_utils.create_share() share2 = db_utils.create_share()
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
cgsnap = db_utils.create_cgsnapshot(cg['id']) sg_snap = db_utils.create_share_group_snapshot(sg['id'])
db_utils.create_cgsnapshot_member(cgsnap['id'], share_id=share['id']) db_utils.create_share_group_snapshot_member(
db_utils.create_cgsnapshot_member(cgsnap['id'], share_id=share2['id']) 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.ctxt, share['id'])
self.assertEqual(1, count) self.assertEqual(1, count)
def test_cgsnapshot_members_get(self): def test_share_group_snapshot_members_get(self):
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
cgsnap = db_utils.create_cgsnapshot(cg['id']) sg_snap = db_utils.create_share_group_snapshot(sg['id'])
expected_member = db_utils.create_cgsnapshot_member(cgsnap['id']) expected_member = db_utils.create_share_group_snapshot_member(
sg_snap['id'])
member = db_api.cgsnapshot_member_get(self.ctxt, member = db_api.share_group_snapshot_member_get(
expected_member['id']) self.ctxt, expected_member['id'])
self.assertDictMatch(dict(expected_member), dict(member)) self.assertDictMatch(dict(expected_member), dict(member))
def test_cgsnapshot_members_get_not_found(self): def test_share_group_snapshot_members_get_not_found(self):
self.assertRaises(exception.CGSnapshotMemberNotFound, self.assertRaises(
db_api.cgsnapshot_member_get, self.ctxt, 'fake_id') exception.ShareGroupSnapshotMemberNotFound,
db_api.share_group_snapshot_member_get, self.ctxt, 'fake_id')
def test_cgsnapshot_member_update(self): def test_share_group_snapshot_member_update(self):
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
cgsnap = db_utils.create_cgsnapshot(cg['id']) sg_snap = db_utils.create_share_group_snapshot(sg['id'])
expected_member = db_utils.create_cgsnapshot_member(cgsnap['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'], self.ctxt, expected_member['id'],
{'status': constants.STATUS_AVAILABLE}) {'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']) self.assertEqual(constants.STATUS_AVAILABLE, member['status'])
@ -2360,19 +2392,6 @@ class PurgeDeletedTest(test.TestCase):
'deleted_at': self._days_ago(start, 'deleted_at': self._days_ago(start,
end)}, end)},
create_snapshot_instance=False) 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 # update share instance
db_api.share_instance_update( db_api.share_instance_update(
self.context, self.context,
@ -2384,9 +2403,7 @@ class PurgeDeletedTest(test.TestCase):
for model in [models.ShareTypes, models.Share, for model in [models.ShareTypes, models.Share,
models.ShareNetwork, models.ShareAccessMapping, models.ShareNetwork, models.ShareAccessMapping,
models.ShareInstance, models.ShareServer, models.ShareInstance, models.ShareServer,
models.ShareSnapshot, models.ConsistencyGroup, models.ShareSnapshot, models.SecurityService]:
models.CGSnapshot, models.SecurityService,
models.CGSnapshotMember]:
rows = db_api.model_query(self.context, model).count() rows = db_api.model_query(self.context, model).count()
self.assertEqual(num_left, rows) 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) return method(context.get_admin_context(), default_values)
def create_consistency_group(**kwargs): def create_share_group(**kwargs):
"""Create a consistency group object.""" """Create a share group object."""
cg = { share_group = {
'share_network_id': None, 'share_network_id': None,
'share_server_id': None, 'share_server_id': None,
'user_id': 'fake', 'user_id': 'fake',
@ -39,22 +39,22 @@ def create_consistency_group(**kwargs):
'status': constants.STATUS_CREATING, 'status': constants.STATUS_CREATING,
'host': 'fake_host' '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): def create_share_group_snapshot(share_group_id, **kwargs):
"""Create a cgsnapshot object.""" """Create a share group snapshot object."""
snapshot = { snapshot = {
'consistency_group_id': cg_id, 'share_group_id': share_group_id,
'user_id': 'fake', 'user_id': 'fake',
'project_id': 'fake', 'project_id': 'fake',
'status': constants.STATUS_CREATING, '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): def create_share_group_snapshot_member(share_group_snapshot_id, **kwargs):
"""Create a cgsnapshot member object.""" """Create a share group snapshot member object."""
member = { member = {
'share_proto': "NFS", 'share_proto': "NFS",
'size': 0, 'size': 0,
@ -63,9 +63,10 @@ def create_cgsnapshot_member(cgsnapshot_id, **kwargs):
'user_id': 'fake', 'user_id': 'fake',
'project_id': 'fake', 'project_id': 'fake',
'status': 'creating', '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): def create_share_access(**kwargs):

View File

@ -104,8 +104,8 @@ class FakeShareDriver(driver.ShareDriver):
return super(FakeShareDriver, self)._verify_share_server_handling( return super(FakeShareDriver, self)._verify_share_server_handling(
driver_handles_share_servers) driver_handles_share_servers)
def create_consistency_group(self, context, cg_id): def create_share_group(self, context, group_id, share_server=None):
pass pass
def delete_consistency_group(self, context, cg_id): def delete_share_group(self, context, group_id, share_server=None):
pass pass

View File

@ -38,7 +38,7 @@ def fake_share(**kwargs):
'snapshot_support': 'True', 'snapshot_support': 'True',
'replication_type': None, 'replication_type': None,
'is_busy': False, 'is_busy': False,
'consistency_group_id': 'fakecgid', 'share_group_id': None,
'instance': {'host': 'fakehost'}, 'instance': {'host': 'fakehost'},
} }
share.update(kwargs) share.update(kwargs)
@ -256,8 +256,8 @@ def fake_replica(id=None, as_primitive=True, for_manager=False, **kwargs):
'snapshot_id': None, 'snapshot_id': None,
'share_proto': None, 'share_proto': None,
'is_public': None, 'is_public': None,
'consistency_group_id': None, 'share_group_id': None,
'source_cgsnapshot_member_id': None, 'source_share_group_snapshot_member_id': None,
'availability_zone': 'fake_az', 'availability_zone': 'fake_az',
}) })
replica.update(kwargs) replica.update(kwargs)
@ -278,7 +278,7 @@ def fake_replica_request_spec(as_primitive=True, **kwargs):
'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f', 'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
'snapshot_id': None, 'snapshot_id': None,
'share_type': 'fake_share_type', 'share_type': 'fake_share_type',
'consistency_group': None, 'share_group': None,
'active_replica_host': 'fake_active_replica_host', 'active_replica_host': 'fake_active_replica_host',
'all_replica_hosts': all_replica_hosts, 'all_replica_hosts': all_replica_hosts,
} }

View File

@ -91,20 +91,21 @@
"scheduler_stats:pools:index": "rule:admin_api", "scheduler_stats:pools:index": "rule:admin_api",
"scheduler_stats:pools:detail": "rule:admin_api", "scheduler_stats:pools:detail": "rule:admin_api",
"consistency_group:create" : "rule:default", "share_group:create" : "rule:default",
"consistency_group:delete": "rule:default", "share_group:delete": "rule:default",
"consistency_group:update": "rule:default", "share_group:update": "rule:default",
"consistency_group:get": "rule:default", "share_group:get": "rule:default",
"consistency_group:get_all": "rule:default", "share_group:get_all": "rule:default",
"consistency_group:create_cgsnapshot" : "rule:default", "share_group:force_delete": "rule:admin_api",
"consistency_group:delete_cgsnapshot": "rule:default", "share_group:reset_status": "rule:admin_api",
"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",
"cgsnapshot:force_delete": "rule:admin_api", "share_group_snapshot:create" : "rule:default",
"cgsnapshot:reset_status": "rule:admin_api", "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:get_all": "rule:default",
"share_replica:show": "rule:default", "share_replica:show": "rule:default",
@ -114,5 +115,20 @@
"share_replica:resync": "rule:admin_api", "share_replica:resync": "rule:admin_api",
"share_replica:reset_status": "rule:admin_api", "share_replica:reset_status": "rule:admin_api",
"share_replica:force_delete": "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.assertIsNone(weighed_host)
self.assertTrue(_mock_service_get_all_by_topic.called) 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): def _setup_dedupe_fakes(self, extra_specs):
sched = fakes.FakeFilterScheduler() sched = fakes.FakeFilterScheduler()
sched.host_manager = fakes.FakeHostManager() sched.host_manager = fakes.FakeHostManager()
@ -401,41 +373,41 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase):
filter_properties['retry']['hosts'][0]) filter_properties['retry']['hosts'][0])
self.assertEqual(1024, host_state.total_capacity_gb) 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. # Ensure empty hosts/child_zones result in NoValidHosts exception.
sched = fakes.FakeFilterScheduler() sched = fakes.FakeFilterScheduler()
fake_context = context.RequestContext('user', 'project') fake_context = context.RequestContext('user', 'project')
fake_host = 'fake_host' fake_host = 'fake_host'
request_spec = {'share_types': [{'id': 'NFS'}]} 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)) mock.Mock(return_value=fake_host))
fake_updated_group = mock.Mock() 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)) 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', sched.schedule_create_share_group(fake_context, 'fake_id',
request_spec, {}) 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) 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) 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) 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. # Ensure empty hosts/child_zones result in NoValidHosts exception.
sched = fakes.FakeFilterScheduler() sched = fakes.FakeFilterScheduler()
fake_context = context.RequestContext('user', 'project') fake_context = context.RequestContext('user', 'project')
request_spec = {'share_types': [{'id': 'NFS'}]} request_spec = {'share_types': [{'id': 'NFS'}]}
self.assertRaises(exception.NoValidHost, self.assertRaises(exception.NoValidHost,
sched.schedule_create_consistency_group, sched.schedule_create_share_group,
fake_context, 'fake_id', request_spec, {}) fake_context, 'fake_id', request_spec, {})
@mock.patch('manila.db.service_get_all_by_topic') @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): self, _mock_service_get_all_by_topic):
sched = fakes.FakeFilterScheduler() sched = fakes.FakeFilterScheduler()
sched.host_manager = fakes.FakeHostManager() sched.host_manager = fakes.FakeHostManager()
@ -446,13 +418,13 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase):
SNAPSHOT_SUPPORT: 'True', SNAPSHOT_SUPPORT: 'True',
}}]} }}]}
hosts = sched._get_weighted_candidates_cg(fake_context, hosts = sched._get_weighted_candidates_share_group(
request_spec) fake_context, request_spec)
self.assertTrue(hosts) self.assertTrue(hosts)
@mock.patch('manila.db.service_get_all_by_topic') @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): self, _mock_service_get_all_by_topic):
sched = fakes.FakeFilterScheduler() sched = fakes.FakeFilterScheduler()
sched.host_manager = fakes.FakeHostManager() sched.host_manager = fakes.FakeHostManager()
@ -463,13 +435,13 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase):
SNAPSHOT_SUPPORT: 'False', SNAPSHOT_SUPPORT: 'False',
}}]} }}]}
hosts = sched._get_weighted_candidates_cg(fake_context, hosts = sched._get_weighted_candidates_share_group(
request_spec) fake_context, request_spec)
self.assertEqual([], hosts) self.assertEqual([], hosts)
@mock.patch('manila.db.service_get_all_by_topic') @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): self, _mock_service_get_all_by_topic):
sched = fakes.FakeFilterScheduler() sched = fakes.FakeFilterScheduler()
sched.host_manager = fakes.FakeHostManager() sched.host_manager = fakes.FakeHostManager()
@ -480,10 +452,10 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase):
SNAPSHOT_SUPPORT: 'True', SNAPSHOT_SUPPORT: 'True',
}}]} }}]}
hosts = sched._get_weighted_candidates_cg(fake_context, hosts = sched._get_weighted_candidates_share_group(
request_spec) fake_context, request_spec)
self.assertEqual(2, len(hosts)) self.assertEqual(6, len(hosts))
def _host_passes_filters_setup(self, mock_obj): def _host_passes_filters_setup(self, mock_obj):
sched = fakes.FakeFilterScheduler() sched = fakes.FakeFilterScheduler()

View File

@ -122,8 +122,7 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_percentage=0, reserved_percentage=0,
provisioned_capacity_gb=100, provisioned_capacity_gb=100,
max_over_subscription_ratio=20.0, max_over_subscription_ratio=20.0,
thin_provisioning=True, thin_provisioning=True)]),
consistency_group_support='pool')]),
'host4@DDD': dict(share_backend_name='DDD', 'host4@DDD': dict(share_backend_name='DDD',
timestamp=None, reserved_percentage=0, timestamp=None, reserved_percentage=0,
driver_handles_share_servers=False, driver_handles_share_servers=False,
@ -137,16 +136,14 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
reserved_percentage=0, reserved_percentage=0,
provisioned_capacity_gb=800, provisioned_capacity_gb=800,
max_over_subscription_ratio=2.0, max_over_subscription_ratio=2.0,
thin_provisioning=True, thin_provisioning=True),
consistency_group_support='host'),
dict(pool_name='pool4b', dict(pool_name='pool4b',
total_capacity_gb=542, total_capacity_gb=542,
free_capacity_gb=442, free_capacity_gb=442,
reserved_percentage=0, reserved_percentage=0,
provisioned_capacity_gb=2000, provisioned_capacity_gb=2000,
max_over_subscription_ratio=10.0, max_over_subscription_ratio=10.0,
thin_provisioning=True, thin_provisioning=True)]),
consistency_group_support='host')]),
'host5@EEE': dict(share_backend_name='EEE', 'host5@EEE': dict(share_backend_name='EEE',
timestamp=None, reserved_percentage=0, timestamp=None, reserved_percentage=0,
driver_handles_share_servers=False, driver_handles_share_servers=False,
@ -233,7 +230,6 @@ class FakeHostManager(host_manager.HostManager):
'provisioned_capacity_gb': 256, 'provisioned_capacity_gb': 256,
'max_over_subscription_ratio': 2.0, 'max_over_subscription_ratio': 2.0,
'thin_provisioning': [False], 'thin_provisioning': [False],
'consistency_group_support': 'host',
'reserved_percentage': 0, 'reserved_percentage': 0,
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
@ -262,7 +258,6 @@ class FakeHostManager(host_manager.HostManager):
'timestamp': None, 'timestamp': None,
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'consistency_group_support': 'pool',
'replication_type': None, 'replication_type': None,
}, },
'host6': {'total_capacity_gb': 'unknown', 'host6': {'total_capacity_gb': 'unknown',
@ -348,7 +343,7 @@ def fake_replica_request_spec(**kwargs):
'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f', 'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
'snapshot_id': None, 'snapshot_id': None,
'share_type': 'fake_share_type', 'share_type': 'fake_share_type',
'consistency_group': None, 'share_group': None,
} }
request_spec.update(kwargs) request_spec.update(kwargs)
return request_spec return request_spec

View File

@ -212,7 +212,6 @@ class HostManagerTestCase(test.TestCase):
'snapshot_support': False, 'snapshot_support': False,
'create_share_from_snapshot_support': False, 'create_share_from_snapshot_support': False,
'revert_to_snapshot_support': True, 'revert_to_snapshot_support': True,
'consistency_group_support': False,
'dedupe': False, 'dedupe': False,
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
@ -239,7 +238,6 @@ class HostManagerTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'consistency_group_support': False,
'dedupe': False, 'dedupe': False,
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
@ -266,7 +264,6 @@ class HostManagerTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'consistency_group_support': False,
'dedupe': False, 'dedupe': False,
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
@ -315,7 +312,6 @@ class HostManagerTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': True, 'revert_to_snapshot_support': True,
'consistency_group_support': False,
'dedupe': False, 'dedupe': False,
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
@ -343,7 +339,6 @@ class HostManagerTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'consistency_group_support': False,
'dedupe': False, 'dedupe': False,
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
@ -371,7 +366,6 @@ class HostManagerTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'consistency_group_support': 'pool',
'dedupe': False, 'dedupe': False,
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
@ -399,7 +393,6 @@ class HostManagerTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'consistency_group_support': 'host',
'dedupe': False, 'dedupe': False,
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
@ -427,7 +420,6 @@ class HostManagerTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'consistency_group_support': 'host',
'dedupe': False, 'dedupe': False,
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
@ -488,7 +480,6 @@ class HostManagerTestCase(test.TestCase):
'provisioned_capacity_gb': 312, 'provisioned_capacity_gb': 312,
'max_over_subscription_ratio': 1.0, 'max_over_subscription_ratio': 1.0,
'thin_provisioning': False, 'thin_provisioning': False,
'consistency_group_support': False,
'dedupe': False, 'dedupe': False,
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
@ -515,7 +506,6 @@ class HostManagerTestCase(test.TestCase):
'provisioned_capacity_gb': 400, 'provisioned_capacity_gb': 400,
'max_over_subscription_ratio': 2.0, 'max_over_subscription_ratio': 2.0,
'thin_provisioning': True, 'thin_provisioning': True,
'consistency_group_support': False,
'dedupe': False, 'dedupe': False,
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,
@ -570,7 +560,6 @@ class HostManagerTestCase(test.TestCase):
'thin_provisioning': True, 'thin_provisioning': True,
'vendor_name': None, 'vendor_name': None,
'storage_protocol': None, 'storage_protocol': None,
'consistency_group_support': False,
'dedupe': False, 'dedupe': False,
'compression': False, 'compression': False,
'replication_type': None, 'replication_type': None,

View File

@ -175,44 +175,45 @@ class SchedulerManagerTestCase(test.TestCase):
mock_get_pools.assert_called_once_with(self.context, 'fake_filters') mock_get_pools.assert_called_once_with(self.context, 'fake_filters')
self.assertEqual('fake_pools', result) self.assertEqual('fake_pools', result)
@mock.patch.object(db, 'consistency_group_update', mock.Mock()) @mock.patch.object(db, 'share_group_update', mock.Mock())
def test_create_cg_no_valid_host_puts_cg_in_error_state(self): def test_create_group_no_valid_host_puts_group_in_error_state(self):
"""Test that NoValidHost is raised for create_consistency_group. """Test that NoValidHost is raised for create_share_group.
Puts the share in 'error' state and eats the exception. Puts the share in 'error' state and eats the exception.
""" """
fake_cg_id = 1 fake_group_id = 1
cg_id = fake_cg_id group_id = fake_group_id
request_spec = {"consistency_group_id": cg_id} request_spec = {"share_group_id": group_id}
with mock.patch.object( 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)): mock.Mock(side_effect=self.raise_no_valid_host)):
self.manager.create_consistency_group(self.context, self.manager.create_share_group(self.context,
fake_cg_id, fake_group_id,
request_spec=request_spec, request_spec=request_spec,
filter_properties={}) filter_properties={})
db.consistency_group_update.assert_called_once_with( db.share_group_update.assert_called_once_with(
self.context, fake_cg_id, {'status': 'error'}) self.context, fake_group_id, {'status': 'error'})
(self.manager.driver.schedule_create_consistency_group. (self.manager.driver.schedule_create_share_group.
assert_called_once_with(self.context, cg_id, request_spec, {})) assert_called_once_with(self.context, group_id, request_spec,
{}))
@mock.patch.object(db, 'consistency_group_update', mock.Mock()) @mock.patch.object(db, 'share_group_update', mock.Mock())
def test_create_cg_exception_puts_cg_in_error_state(self): def test_create_group_exception_puts_group_in_error_state(self):
"""Test that exceptions for create_consistency_group. """Test that exceptions for create_share_group.
Puts the share in 'error' state and raises the exception. Puts the share in 'error' state and raises the exception.
""" """
fake_cg_id = 1 fake_group_id = 1
cg_id = fake_cg_id group_id = fake_group_id
request_spec = {"consistency_group_id": cg_id} request_spec = {"share_group_id": group_id}
with mock.patch.object(self.manager.driver, with mock.patch.object(self.manager.driver,
'schedule_create_consistency_group', 'schedule_create_share_group',
mock.Mock(side_effect=exception.NotFound)): mock.Mock(side_effect=exception.NotFound)):
self.assertRaises(exception.NotFound, self.assertRaises(exception.NotFound,
self.manager.create_consistency_group, self.manager.create_share_group,
self.context, fake_cg_id, self.context, fake_group_id,
request_spec=request_spec, request_spec=request_spec,
filter_properties={}) filter_properties={})

View File

@ -93,13 +93,13 @@ class SchedulerRpcAPITestCase(test.TestCase):
filters=None, filters=None,
version='1.1') version='1.1')
def test_create_consistency_group(self): def test_create_share_group(self):
self._test_scheduler_api('create_consistency_group', self._test_scheduler_api('create_share_group',
rpc_method='cast', rpc_method='cast',
cg_id='cg_id', share_group_id='fake_share_group_id',
request_spec='fake_request_spec', request_spec='fake_request_spec',
filter_properties='filter_properties', filter_properties='filter_properties',
version='1.3') version='1.8')
def test_migrate_share_to_host(self): def test_migrate_share_to_host(self):
self._test_scheduler_api('migrate_share_to_host', self._test_scheduler_api('migrate_share_to_host',

View File

@ -380,32 +380,30 @@ class CephFSNativeDriverTestCase(test.TestCase):
self._driver._share_path(self._share), self._driver._share_path(self._share),
"snappy1_instance1")) "snappy1_instance1"))
def test_create_consistency_group(self): def test_create_share_group(self):
self._driver.create_consistency_group(self._context, {"id": "grp1"}, self._driver.create_share_group(self._context, {"id": "grp1"}, None)
None)
self._driver._volume_client.create_group.assert_called_once_with( self._driver._volume_client.create_group.assert_called_once_with(
"grp1") "grp1")
def test_delete_consistency_group(self): def test_delete_share_group(self):
self._driver.delete_consistency_group(self._context, {"id": "grp1"}, self._driver.delete_share_group(self._context, {"id": "grp1"}, None)
None)
self._driver._volume_client.destroy_group.assert_called_once_with( self._driver._volume_client.destroy_group.assert_called_once_with(
"grp1") "grp1")
def test_create_cg_snapshot(self): def test_create_share_snapshot(self):
self._driver.create_cgsnapshot(self._context, { self._driver.create_share_group_snapshot(self._context, {
'consistency_group_id': 'cgid', 'share_group_id': 'cgid',
'id': 'snapid' 'id': 'snapid'
}) })
(self._driver._volume_client.create_snapshot_group. (self._driver._volume_client.create_snapshot_group.
assert_called_once_with("cgid", "snapid")) assert_called_once_with("cgid", "snapid"))
def test_delete_cgsnapshot(self): def test_delete_share_group_snapshot(self):
self._driver.delete_cgsnapshot(self._context, { self._driver.delete_share_group_snapshot(self._context, {
'consistency_group_id': 'cgid', 'share_group_id': 'cgid',
'id': 'snapid' 'id': 'snapid'
}) })

View File

@ -126,6 +126,7 @@ class EMCShareFrameworkTestCase(test.TestCase):
data['snapshot_support'] = True data['snapshot_support'] = True
data['create_share_from_snapshot_support'] = True data['create_share_from_snapshot_support'] = True
data['revert_to_snapshot_support'] = False data['revert_to_snapshot_support'] = False
data['share_group_snapshot_support'] = True
data['replication_domain'] = None data['replication_domain'] = None
data['filter_function'] = None data['filter_function'] = None
data['goodness_function'] = None data['goodness_function'] = None

View File

@ -258,6 +258,7 @@ class GlusterfsNativeShareDriverTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'share_group_snapshot_support': True,
'replication_domain': None, 'replication_domain': None,
'filter_function': None, 'filter_function': None,
'goodness_function': None, 'goodness_function': None,

View File

@ -735,6 +735,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'share_group_snapshot_support': True,
'storage_protocol': 'NFS_CIFS', 'storage_protocol': 'NFS_CIFS',
'thin_provisioning': True, 'thin_provisioning': True,
'total_capacity_gb': 0, 'total_capacity_gb': 0,
@ -811,6 +812,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'share_group_snapshot_support': True,
'replication_domain': None, 'replication_domain': None,
'filter_function': None, 'filter_function': None,
'goodness_function': None, 'goodness_function': None,
@ -849,6 +851,7 @@ class HPE3ParDriverTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'share_group_snapshot_support': True,
'replication_domain': None, 'replication_domain': None,
'filter_function': None, 'filter_function': None,
'goodness_function': None, 'goodness_function': None,

View File

@ -2425,6 +2425,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
"snapshot_support": snapshot_support, "snapshot_support": snapshot_support,
"create_share_from_snapshot_support": snapshot_support, "create_share_from_snapshot_support": snapshot_support,
"revert_to_snapshot_support": False, "revert_to_snapshot_support": False,
"share_group_snapshot_support": True,
"replication_domain": None, "replication_domain": None,
"filter_function": None, "filter_function": None,
"goodness_function": None, "goodness_function": None,

View File

@ -366,7 +366,6 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
'driver_version': '1.0', 'driver_version': '1.0',
'netapp_storage_family': 'ontap_cluster', 'netapp_storage_family': 'ontap_cluster',
'storage_protocol': 'NFS_CIFS', 'storage_protocol': 'NFS_CIFS',
'consistency_group_support': 'host',
'pools': fake.POOLS, 'pools': fake.POOLS,
} }
self.assertDictEqual(expected, result) self.assertDictEqual(expected, result)
@ -390,7 +389,6 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
'driver_version': '1.0', 'driver_version': '1.0',
'netapp_storage_family': 'ontap_cluster', 'netapp_storage_family': 'ontap_cluster',
'storage_protocol': 'NFS_CIFS', 'storage_protocol': 'NFS_CIFS',
'consistency_group_support': 'host',
'replication_type': 'dr', 'replication_type': 'dr',
'replication_domain': 'fake_domain', 'replication_domain': 'fake_domain',
'pools': fake.POOLS, 'pools': fake.POOLS,
@ -1825,7 +1823,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
fake_cg_snapshot = copy.deepcopy(fake.CG_SNAPSHOT) fake_cg_snapshot = copy.deepcopy(fake.CG_SNAPSHOT)
fake_cg_snapshot['cgsnapshot_members'] = [] fake_cg_snapshot['cgsnapshot_members'] = []
self.assertRaises(exception.InvalidConsistencyGroup, self.assertRaises(exception.InvalidShareGroup,
self.library._collate_cg_snapshot_info, self.library._collate_cg_snapshot_info,
fake.CONSISTENCY_GROUP_DEST, fake_cg_snapshot) fake.CONSISTENCY_GROUP_DEST, fake_cg_snapshot)

View File

@ -399,7 +399,7 @@ CG_SNAPSHOT_MEMBER_2 = {
CG_SNAPSHOT = { CG_SNAPSHOT = {
'cgsnapshot_members': [CG_SNAPSHOT_MEMBER_1, CG_SNAPSHOT_MEMBER_2], 'cgsnapshot_members': [CG_SNAPSHOT_MEMBER_1, CG_SNAPSHOT_MEMBER_2],
'consistency_group': CONSISTENCY_GROUP, 'share_group': CONSISTENCY_GROUP,
'consistency_group_id': CONSISTENCY_GROUP_ID, 'consistency_group_id': CONSISTENCY_GROUP_ID,
'id': CG_SNAPSHOT_ID, 'id': CG_SNAPSHOT_ID,
'project_id': TENANT_ID, 'project_id': TENANT_ID,

View File

@ -66,27 +66,6 @@ def get_fake_snap_dict():
'created_at': '2015-08-10 00:05:58', 'created_at': '2015-08-10 00:05:58',
'updated_at': '2015-08-10 00:05:58', 'updated_at': '2015-08-10 00:05:58',
'consistency_group_id': None, '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, 'deleted_at': None,
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a', 'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
'name': None, 'name': None,
@ -94,58 +73,6 @@ def get_fake_snap_dict():
return 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'): def get_fake_access_rule(access_to, access_level, access_type='ip'):
return { return {
'access_type': access_type, '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('LVM', self._driver._stats['share_backend_name'])
self.assertEqual('NFS_CIFS', self._driver._stats['storage_protocol']) self.assertEqual('NFS_CIFS', self._driver._stats['storage_protocol'])
self.assertEqual(50, self._driver._stats['reserved_percentage']) self.assertEqual(50, self._driver._stats['reserved_percentage'])
self.assertIsNone(self._driver._stats['consistency_group_support'])
self.assertTrue(self._driver._stats['snapshot_support']) self.assertTrue(self._driver._stats['snapshot_support'])
self.assertEqual('LVMShareDriver', self._driver._stats['driver_name']) self.assertEqual('LVMShareDriver', self._driver._stats['driver_name'])
self.assertEqual('test-pool', self._driver._stats['pools']) 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.mock_object(self.driver, '_get_pools_info')
self.assertEqual({}, self.driver._stats) self.assertEqual({}, self.driver._stats)
expected = { expected = {
'consistency_group_support': None,
'driver_handles_share_servers': False, 'driver_handles_share_servers': False,
'driver_name': 'ZFS', 'driver_name': 'ZFS',
'driver_version': '1.0', 'driver_version': '1.0',
@ -356,6 +355,7 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
'snapshot_support': True, 'snapshot_support': True,
'create_share_from_snapshot_support': True, 'create_share_from_snapshot_support': True,
'revert_to_snapshot_support': False, 'revert_to_snapshot_support': False,
'share_group_snapshot_support': True,
'storage_protocol': 'NFS', 'storage_protocol': 'NFS',
'total_capacity_gb': 'unknown', 'total_capacity_gb': 'unknown',
'vendor_name': 'Open Source', 'vendor_name': 'Open Source',

View File

@ -708,7 +708,7 @@ class ShareAPITestCase(test.TestCase):
self.context, request_spec=mock.ANY, filter_properties={}) self.context, request_spec=mock.ANY, filter_properties={})
self.assertFalse(self.api.share_rpcapi.create_share_instance.called) 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 = { fake_req_spec = {
'share_properties': 'fake_share_properties', 'share_properties': 'fake_share_properties',
'share_instance_properties': 'fake_share_instance_properties', 'share_instance_properties': 'fake_share_instance_properties',
@ -722,7 +722,7 @@ class ShareAPITestCase(test.TestCase):
fake_instance = fakes.fake_share_instance( fake_instance = fakes.fake_share_instance(
share_id=share['id'], **member_info) 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( self.mock_policy_check = self.mock_object(
policy, 'check_policy', mock.Mock(return_value=True)) policy, 'check_policy', mock.Mock(return_value=True))
mock_share_rpcapi_call = self.mock_object(self.share_rpcapi, 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', share_api.API, 'create_share_instance_and_get_request_spec',
mock.Mock(return_value=(fake_req_spec, fake_instance))) mock.Mock(return_value=(fake_req_spec, fake_instance)))
retval = self.api.create_instance(self.context, fakes.fake_share(), retval = self.api.create_instance(
cgsnapshot_member=cgsnapmember) self.context, fakes.fake_share(),
share_group_snapshot_member=sg_snap_member)
self.assertIsNone(retval) self.assertIsNone(retval)
mock_db_share_instance_update.assert_called_once_with( 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_proto': kwargs.get('share_proto', share.get('share_proto')),
'share_type_id': share_type['id'], 'share_type_id': share_type['id'],
'is_public': kwargs.get('is_public', share.get('is_public')), 'is_public': kwargs.get('is_public', share.get('is_public')),
'consistency_group_id': kwargs.get( 'share_group_id': kwargs.get(
'consistency_group_id', share.get('consistency_group_id')), 'share_group_id', share.get('share_group_id')),
'source_cgsnapshot_member_id': kwargs.get( 'source_share_group_snapshot_member_id': kwargs.get(
'source_cgsnapshot_member_id', 'source_share_group_snapshot_member_id',
share.get('source_cgsnapshot_member_id')), share.get('source_share_group_snapshot_member_id')),
'snapshot_id': kwargs.get('snapshot_id', share.get('snapshot_id')), 'snapshot_id': kwargs.get('snapshot_id', share.get('snapshot_id')),
} }
share_instance_properties = { share_instance_properties = {
@ -1406,7 +1407,7 @@ class ShareAPITestCase(test.TestCase):
@mock.patch.object(db_api, 'share_instances_get_all_by_share_server', @mock.patch.object(db_api, 'share_instances_get_all_by_share_server',
mock.Mock(return_value=[])) 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=[])) mock.Mock(return_value=[]))
def test_delete_share_server_no_dependent_shares(self): def test_delete_share_server_no_dependent_shares(self):
server = {'id': 'fake_share_server_id'} server = {'id': 'fake_share_server_id'}
@ -1418,14 +1419,14 @@ class ShareAPITestCase(test.TestCase):
self.api.delete_share_server(self.context, server) self.api.delete_share_server(self.context, server)
db_api.share_instances_get_all_by_share_server.assert_called_once_with( db_api.share_instances_get_all_by_share_server.assert_called_once_with(
self.context, server['id']) 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']) assert_called_once_with(self.context, server['id'])
self.share_rpcapi.delete_share_server.assert_called_once_with( self.share_rpcapi.delete_share_server.assert_called_once_with(
self.context, server_returned) self.context, server_returned)
@mock.patch.object(db_api, 'share_instances_get_all_by_share_server', @mock.patch.object(db_api, 'share_instances_get_all_by_share_server',
mock.Mock(return_value=['fake_share', ])) 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=[])) mock.Mock(return_value=[]))
def test_delete_share_server_dependent_share_exists(self): def test_delete_share_server_dependent_share_exists(self):
server = {'id': 'fake_share_server_id'} 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.patch.object(db_api, 'share_instances_get_all_by_share_server',
mock.Mock(return_value=[])) 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=['fake_cg', ])) mock.Mock(return_value=['fake_group', ]))
def test_delete_share_server_dependent_cg_exists(self): def test_delete_share_server_dependent_group_exists(self):
server = {'id': 'fake_share_server_id'} server = {'id': 'fake_share_server_id'}
self.assertRaises(exception.ShareServerInUse, self.assertRaises(exception.ShareServerInUse,
self.api.delete_share_server, 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( db_api.share_instances_get_all_by_share_server.assert_called_once_with(
self.context, server['id']) 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']) assert_called_once_with(self.context, server['id'])
@mock.patch.object(db_api, 'share_snapshot_instance_update', mock.Mock()) @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'], self.context, share, share_network_id=share['share_network_id'],
host=valid_host, share_type_id=share_type['id'], host=valid_host, share_type_id=share_type['id'],
availability_zone=snapshot['share']['availability_zone'], 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([ share_api.policy.check_policy.assert_has_calls([
mock.call(self.context, 'share', 'create'), mock.call(self.context, 'share', 'create'),
mock.call(self.context, 'share_snapshot', 'get_snapshot')]) mock.call(self.context, 'share_snapshot', 'get_snapshot')])
@ -1713,9 +1714,9 @@ class ShareAPITestCase(test.TestCase):
self.assertRaises(exception.Conflict, self.api.delete, self.assertRaises(exception.Conflict, self.api.delete,
self.context, share) 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)) 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_server_id = 'fake-ss-id'
share = self._setup_delete_mocks(constants.STATUS_AVAILABLE, share = self._setup_delete_mocks(constants.STATUS_AVAILABLE,
share_server_id) share_server_id)

View File

@ -676,3 +676,336 @@ class ShareDriverTestCase(test.TestCase):
share_driver.update_replicated_snapshot, share_driver.update_replicated_snapshot,
'fake_context', ['r1', 'r2'], 'r1', 'fake_context', ['r1', 'r2'], 'r1',
['s1', 's2'], 's1') ['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", "delete_share_server",
"extend_share", "extend_share",
"shrink_share", "shrink_share",
"create_consistency_group", "create_share_group",
"delete_consistency_group", "delete_share_group",
"create_cgsnapshot", "create_share_group_snapshot",
"delete_cgsnapshot", "delete_share_group_snapshot",
"create_share_replica", "create_share_replica",
"delete_share_replica", "delete_share_replica",
"promote_share_replica", "promote_share_replica",
@ -1943,7 +1943,7 @@ class ShareManagerTestCase(test.TestCase):
) )
driver_method_mock.assert_called_once_with( driver_method_mock.assert_called_once_with(
self.context, [fake_share_server], share.instance, snapshot=None, 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): def test_provide_share_server_for_share_invalid_arguments(self):
self.assertRaises(ValueError, self.assertRaises(ValueError,
@ -1994,34 +1994,36 @@ class ShareManagerTestCase(test.TestCase):
db.share_server_get.assert_called_once_with( db.share_server_get.assert_called_once_with(
self.context, fake_parent_id) 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_exception = exception.ManilaException("fake")
fake_share_server = {'id': 'fake'} fake_share_server = {'id': 'fake'}
cg = db_utils.create_consistency_group() sg = db_utils.create_share_group()
self.mock_object(db, self.mock_object(db,
'share_server_get_all_by_host_and_share_net_valid', 'share_server_get_all_by_host_and_share_net_valid',
mock.Mock(return_value=[fake_share_server])) mock.Mock(return_value=[fake_share_server]))
self.mock_object( self.mock_object(
self.share_manager.driver, self.share_manager.driver,
"choose_share_server_compatible_with_cg", "choose_share_server_compatible_with_share_group",
mock.Mock(side_effect=fake_exception) mock.Mock(side_effect=fake_exception)
) )
self.assertRaises(exception.ManilaException, self.assertRaises(
self.share_manager._provide_share_server_for_cg, exception.ManilaException,
self.context, "fake_id", cg) self.share_manager._provide_share_server_for_share_group,
self.context, "fake_id", sg)
driver_mock = self.share_manager.driver driver_mock = self.share_manager.driver
driver_method_mock = ( 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( 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): def test_provide_share_server_for_share_group_invalid_arguments(self):
self.assertRaises(exception.InvalidInput, self.assertRaises(
self.share_manager._provide_share_server_for_cg, exception.InvalidInput,
self.context, None, None) self.share_manager._provide_share_server_for_share_group,
self.context, None, None)
def test_manage_share_invalid_driver(self): def test_manage_share_invalid_driver(self):
self.mock_object(self.share_manager, 'driver', mock.Mock()) self.mock_object(self.share_manager, 'driver', mock.Mock())
@ -3144,19 +3146,19 @@ class ShareManagerTestCase(test.TestCase):
self.assertEqual(old_capabilities, self.assertEqual(old_capabilities,
self.share_manager.last_capabilities) self.share_manager.last_capabilities)
def test_create_consistency_group(self): def test_create_share_group(self):
fake_cg = {'id': 'fake_id'} fake_group = {'id': 'fake_id'}
self.mock_object(self.share_manager.db, 'consistency_group_get', self.mock_object(self.share_manager.db, 'share_group_get',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.db, 'consistency_group_update', self.mock_object(self.share_manager.db, 'share_group_update',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.driver, self.mock_object(self.share_manager.driver,
'create_consistency_group', 'create_share_group',
mock.Mock(return_value=None)) 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', assert_called_once_with(mock.ANY, 'fake_id',
{'status': constants.STATUS_AVAILABLE, {'status': constants.STATUS_AVAILABLE,
'created_at': mock.ANY}) 'created_at': mock.ANY})
@ -3166,207 +3168,226 @@ class ShareManagerTestCase(test.TestCase):
self.mock_object( self.mock_object(
self.share_manager.driver.configuration, 'safe_get', self.share_manager.driver.configuration, 'safe_get',
mock.Mock(return_value=False)) mock.Mock(return_value=False))
cg_id = 'fake_cg_id' cg_id = 'fake_group_id'
share_network_id = 'fake_sn' 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.mock_object(
self.share_manager.db, 'consistency_group_get', self.share_manager.db, 'share_group_get',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.db, 'consistency_group_update') self.mock_object(self.share_manager.db, 'share_group_update')
self.assertRaises( self.assertRaises(
exception.ManilaException, 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) 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, utils.IsAMatcher(context.RequestContext), cg_id,
{'status': constants.STATUS_ERROR}) {'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) manager.CONF.set_default('driver_handles_share_servers', True)
self.mock_object( self.mock_object(
self.share_manager.driver.configuration, 'safe_get', self.share_manager.driver.configuration, 'safe_get',
mock.Mock(return_value=True)) mock.Mock(return_value=True))
share_network_id = 'fake_sn' share_network_id = 'fake_sn'
fake_cg = {'id': 'fake_id', 'share_network_id': share_network_id, fake_group = {
'host': "fake_host"} 'id': 'fake_id',
self.mock_object(self.share_manager.db, 'consistency_group_get', 'share_network_id': share_network_id,
mock.Mock(return_value=fake_cg)) 'host': "fake_host",
self.mock_object(self.share_manager.db, 'consistency_group_update', }
mock.Mock(return_value=fake_cg)) self.mock_object(
self.mock_object(self.share_manager, '_provide_share_server_for_cg', self.share_manager.db, 'share_group_get',
mock.Mock(return_value=({}, fake_cg))) 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, 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.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',
mock.Mock(return_value={'foo': 'bar'})) 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'}) 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', assert_any_call(mock.ANY, 'fake_id',
{'status': constants.STATUS_AVAILABLE, {'status': constants.STATUS_AVAILABLE,
'created_at': mock.ANY}) 'created_at': mock.ANY})
def test_create_consistency_group_with_error(self): def test_create_share_group_with_error(self):
fake_cg = {'id': 'fake_id'} fake_group = {'id': 'fake_id'}
self.mock_object(self.share_manager.db, 'consistency_group_get', self.mock_object(self.share_manager.db, 'share_group_get',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.db, 'consistency_group_update', self.mock_object(self.share_manager.db, 'share_group_update',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.driver, self.mock_object(self.share_manager.driver,
'create_consistency_group', 'create_share_group',
mock.Mock(side_effect=exception.Error)) mock.Mock(side_effect=exception.Error))
self.assertRaises(exception.Error, self.assertRaises(exception.Error,
self.share_manager.create_consistency_group, self.share_manager.create_share_group,
self.context, "fake_id") self.context, "fake_id")
self.share_manager.db.consistency_group_update.\ self.share_manager.db.share_group_update.assert_called_once_with(
assert_called_once_with(mock.ANY, 'fake_id', mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR})
{'status': constants.STATUS_ERROR})
def test_create_consistency_group_from_cgsnapshot(self): def test_create_share_group_from_sg_snapshot(self):
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', fake_group = {
'shares': [], 'share_server_id': 'fake_ss_id'} '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_ss = {'id': 'fake_ss_id', 'share_network_id': 'fake_sn'}
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': [], fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': [],
'consistency_group': {'share_server_id': fake_ss['id']}} 'share_group': {'share_server_id': fake_ss['id']}}
self.mock_object(self.share_manager.db, 'consistency_group_get', self.mock_object(self.share_manager.db, 'share_group_get',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.db, 'cgsnapshot_get', self.mock_object(self.share_manager.db, 'share_group_snapshot_get',
mock.Mock(return_value=fake_snap)) mock.Mock(return_value=fake_snap))
self.mock_object(self.share_manager.db, 'share_server_get', self.mock_object(self.share_manager.db, 'share_server_get',
mock.Mock( mock.Mock(
return_value=fake_ss)) return_value=fake_ss))
self.mock_object(self.share_manager.db, 'consistency_group_update', self.mock_object(self.share_manager.db, 'share_group_update',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.driver, mock_create_sg_from_sg_snap = self.mock_object(
'create_consistency_group_from_cgsnapshot', self.share_manager.driver,
mock.Mock(return_value=(None, None))) '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.\ self.share_manager.db.share_group_update.assert_called_once_with(
assert_called_once_with(mock.ANY, 'fake_id', mock.ANY, 'fake_id',
{'status': constants.STATUS_AVAILABLE, {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
'created_at': mock.ANY})
self.share_manager.db.share_server_get(mock.ANY, 'fake_ss_id') self.share_manager.db.share_server_get(mock.ANY, 'fake_ss_id')
self.share_manager.driver.create_consistency_group_from_cgsnapshot.\ mock_create_sg_from_sg_snap.assert_called_once_with(
assert_called_once_with( mock.ANY, fake_group, fake_snap, share_server=fake_ss)
mock.ANY, fake_cg, fake_snap, share_server=fake_ss)
def test_create_cg_cgsnapshot_share_network_driver_not_handles_servers( def test_create_sg_snapshot_share_network_driver_not_handles_servers(self):
self):
manager.CONF.set_default('driver_handles_share_servers', False) manager.CONF.set_default('driver_handles_share_servers', False)
self.mock_object( self.mock_object(
self.share_manager.driver.configuration, 'safe_get', self.share_manager.driver.configuration, 'safe_get',
mock.Mock(return_value=False)) mock.Mock(return_value=False))
cg_id = 'fake_cg_id' sg_id = 'fake_share_group_id'
share_network_id = 'fake_sn' share_network_id = 'fake_sn'
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', fake_group = {
'shares': [], 'share_network_id': share_network_id, 'id': 'fake_id',
'host': "fake_host"} 'source_share_group_snapshot_id': 'fake_snap_id',
'shares': [],
'share_network_id': share_network_id,
'host': "fake_host",
}
self.mock_object( self.mock_object(
self.share_manager.db, 'consistency_group_get', self.share_manager.db, 'share_group_get',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []}
self.mock_object(self.share_manager.db, 'cgsnapshot_get', self.mock_object(self.share_manager.db, 'share_group_snapshot_get',
mock.Mock(return_value=fake_snap)) 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.assertRaises(exception.ManilaException,
self.share_manager.create_consistency_group, self.share_manager.create_share_group,
self.context, cg_id) self.context, sg_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) utils.IsAMatcher(context.RequestContext), sg_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, utils.IsAMatcher(context.RequestContext), sg_id,
{'status': constants.STATUS_ERROR}) {'status': constants.STATUS_ERROR})
def test_create_cg_from_cgsnapshot_share_network_driver_handles_servers( def test_create_share_group_from_sg_snapshot_share_network_dhss(self):
self):
manager.CONF.set_default('driver_handles_share_servers', True) manager.CONF.set_default('driver_handles_share_servers', True)
self.mock_object(self.share_manager.driver.configuration, 'safe_get', self.mock_object(self.share_manager.driver.configuration, 'safe_get',
mock.Mock(return_value=True)) mock.Mock(return_value=True))
share_network_id = 'fake_sn' share_network_id = 'fake_sn'
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', fake_group = {
'shares': [], 'share_network_id': share_network_id} 'id': 'fake_id',
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} 'source_share_group_snapshot_id': 'fake_snap_id',
self.mock_object(self.share_manager.db, 'consistency_group_get', 'shares': [],
mock.Mock(return_value=fake_cg)) 'share_network_id': share_network_id,
self.mock_object(self.share_manager.db, 'cgsnapshot_get', }
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)) 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',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager, '_provide_share_server_for_cg', self.mock_object(
mock.Mock(return_value=({}, fake_cg))) self.share_manager, '_provide_share_server_for_share_group',
self.mock_object(self.share_manager.driver, mock.Mock(return_value=({}, fake_group)))
'create_consistency_group_from_cgsnapshot', self.mock_object(
mock.Mock(return_value=(None, None))) 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.\ self.share_manager.db.share_group_update.assert_called_once_with(
assert_called_once_with(mock.ANY, 'fake_id', mock.ANY, 'fake_id',
{'status': constants.STATUS_AVAILABLE, {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
'created_at': mock.ANY})
def test_create_consistency_group_from_cgsnapshot_with_update(self): def test_create_share_group_from_share_group_snapshot_with_update(self):
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', fake_group = {
'shares': []} 'id': 'fake_id',
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} 'source_share_group_snapshot_id': 'fake_snap_id',
self.mock_object(self.share_manager.db, 'consistency_group_get', 'shares': [],
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': []}
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)) 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',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.driver, 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))) 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.\ self.share_manager.db.share_group_update.assert_any_call(
assert_any_call(mock.ANY, 'fake_id', {'foo': 'bar'}) mock.ANY, 'fake_id', {'foo': 'bar'})
self.share_manager.db.consistency_group_update.\ self.share_manager.db.share_group_update.assert_any_call(
assert_any_call(mock.ANY, 'fake_id', mock.ANY, 'fake_id',
{'status': constants.STATUS_AVAILABLE, {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
'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_share = {'id': 'fake_share_id'}
fake_export_locations = ['my_export_location'] fake_export_locations = ['my_export_location']
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', fake_group = {
'shares': [fake_share]} 'id': 'fake_id',
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} 'source_share_group_snapshot_id': 'fake_snap_id',
self.mock_object(self.share_manager.db, 'consistency_group_get', 'shares': [fake_share],
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': []}
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)) 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_instance_update')
self.mock_object(self.share_manager.db, self.mock_object(self.share_manager.db,
'share_export_locations_update') 'share_export_locations_update')
@ -3374,216 +3395,227 @@ class ShareManagerTestCase(test.TestCase):
'foo': 'bar', 'foo': 'bar',
'export_locations': fake_export_locations}] 'export_locations': fake_export_locations}]
self.mock_object(self.share_manager.driver, self.mock_object(self.share_manager.driver,
'create_consistency_group_from_cgsnapshot', 'create_share_group_from_share_group_snapshot',
mock.Mock( mock.Mock(
return_value=(None, fake_share_update_list))) 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.\ self.share_manager.db.share_instance_update.assert_any_call(
assert_any_call(mock.ANY, 'fake_share_id', {'foo': 'bar'}) mock.ANY, 'fake_share_id', {'foo': 'bar'})
self.share_manager.db.share_export_locations_update.\ self.share_manager.db.share_export_locations_update.assert_any_call(
assert_any_call(mock.ANY, 'fake_share_id', fake_export_locations) mock.ANY, 'fake_share_id', fake_export_locations)
self.share_manager.db.consistency_group_update.\ self.share_manager.db.share_group_update.assert_any_call(
assert_any_call(mock.ANY, 'fake_id', mock.ANY, 'fake_id',
{'status': constants.STATUS_AVAILABLE, {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
'created_at': mock.ANY})
def test_create_consistency_group_from_cgsnapshot_with_error(self): def test_create_share_group_from_sg_snapshot_with_error(self):
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', fake_group = {
'shares': []} 'id': 'fake_id',
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} 'source_share_group_snapshot_id': 'fake_snap_id',
self.mock_object(self.share_manager.db, 'consistency_group_get', 'shares': [],
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': []}
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)) mock.Mock(return_value=fake_snap))
self.mock_object(self.share_manager.db, 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=[])) mock.Mock(return_value=[]))
self.mock_object(self.share_manager.db, 'consistency_group_update', self.mock_object(self.share_manager.db, 'share_group_update',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.driver, 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)) mock.Mock(side_effect=exception.Error))
self.assertRaises(exception.Error, self.assertRaises(exception.Error,
self.share_manager.create_consistency_group, self.share_manager.create_share_group,
self.context, "fake_id") self.context, "fake_id")
self.share_manager.db.consistency_group_update.\ self.share_manager.db.share_group_update.assert_called_once_with(
assert_called_once_with(mock.ANY, 'fake_id', mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR})
{'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_share = {'id': 'fake_share_id'}
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id', fake_group = {
'shares': [fake_share]} 'id': 'fake_id',
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []} 'source_share_group_snapshot_id': 'fake_snap_id',
self.mock_object(self.share_manager.db, 'consistency_group_get', 'shares': [fake_share],
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': []}
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)) mock.Mock(return_value=fake_snap))
self.mock_object(self.share_manager.db, 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])) 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.db, 'share_instance_update')
self.mock_object(self.share_manager.driver, 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)) mock.Mock(side_effect=exception.Error))
self.assertRaises(exception.Error, self.assertRaises(exception.Error,
self.share_manager.create_consistency_group, self.share_manager.create_share_group,
self.context, "fake_id") self.context, "fake_id")
self.share_manager.db.share_instance_update.\ self.share_manager.db.share_instance_update.assert_any_call(
assert_any_call(mock.ANY, 'fake_share_id', mock.ANY, 'fake_share_id', {'status': constants.STATUS_ERROR})
{'status': constants.STATUS_ERROR}) self.share_manager.db.share_group_update.assert_called_once_with(
self.share_manager.db.consistency_group_update.\ mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR})
assert_called_once_with(mock.ANY, 'fake_id',
{'status': constants.STATUS_ERROR})
def test_delete_consistency_group(self): def test_delete_share_group(self):
fake_cg = {'id': 'fake_id'} fake_group = {'id': 'fake_id'}
self.mock_object(self.share_manager.db, 'consistency_group_get', self.mock_object(self.share_manager.db, 'share_group_get',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.db, 'consistency_group_update', self.mock_object(self.share_manager.db, 'share_group_update',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.db, 'consistency_group_destroy', self.mock_object(self.share_manager.db, 'share_group_destroy',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.driver, self.mock_object(self.share_manager.driver,
'delete_consistency_group', 'delete_share_group',
mock.Mock(return_value=None)) 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.\ self.share_manager.db.share_group_destroy.assert_called_once_with(
assert_called_once_with(mock.ANY, 'fake_id') mock.ANY, 'fake_id')
def test_delete_consistency_group_with_update(self): def test_delete_share_group_with_update(self):
fake_cg = {'id': 'fake_id'} fake_group = {'id': 'fake_id'}
self.mock_object(self.share_manager.db, 'consistency_group_get', self.mock_object(self.share_manager.db, 'share_group_get',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.db, 'consistency_group_update', self.mock_object(self.share_manager.db, 'share_group_update',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.db, 'consistency_group_destroy', self.mock_object(self.share_manager.db, 'share_group_destroy',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.driver, self.mock_object(self.share_manager.driver,
'delete_consistency_group', 'delete_share_group',
mock.Mock(return_value={'foo': 'bar'})) 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.\ self.share_manager.db.share_group_update.assert_called_once_with(
assert_called_once_with(mock.ANY, 'fake_id', {'foo': 'bar'}) mock.ANY, 'fake_id', {'foo': 'bar'})
self.share_manager.db.consistency_group_destroy.\ self.share_manager.db.share_group_destroy.assert_called_once_with(
assert_called_once_with(mock.ANY, 'fake_id') mock.ANY, 'fake_id')
def test_delete_consistency_group_with_error(self): def test_delete_share_group_with_error(self):
fake_cg = {'id': 'fake_id'} fake_group = {'id': 'fake_id'}
self.mock_object(self.share_manager.db, 'consistency_group_get', self.mock_object(self.share_manager.db, 'share_group_get',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.db, 'consistency_group_update', self.mock_object(self.share_manager.db, 'share_group_update',
mock.Mock(return_value=fake_cg)) mock.Mock(return_value=fake_group))
self.mock_object(self.share_manager.driver, self.mock_object(self.share_manager.driver,
'delete_consistency_group', 'delete_share_group',
mock.Mock(side_effect=exception.Error)) mock.Mock(side_effect=exception.Error))
self.assertRaises(exception.Error, self.assertRaises(exception.Error,
self.share_manager.delete_consistency_group, self.share_manager.delete_share_group,
self.context, "fake_id") self.context, "fake_id")
self.share_manager.db.consistency_group_update.\ self.share_manager.db.share_group_update.assert_called_once_with(
assert_called_once_with(mock.ANY, 'fake_id', mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR})
{'status': constants.STATUS_ERROR})
def test_create_cgsnapshot(self): def test_create_share_group_snapshot(self):
fake_snap = {'id': 'fake_snap_id', 'consistency_group': {}, fake_snap = {
'cgsnapshot_members': []} 'id': 'fake_snap_id',
self.mock_object(self.share_manager.db, 'cgsnapshot_get', 'share_group': {},
mock.Mock(return_value=fake_snap)) 'share_group_snapshot_members': [],
self.mock_object(self.share_manager.db, 'cgsnapshot_update', }
mock.Mock(return_value=fake_snap)) self.mock_object(
self.mock_object(self.share_manager.driver, self.share_manager.db, 'share_group_snapshot_get',
'create_cgsnapshot', mock.Mock(return_value=fake_snap))
mock.Mock(return_value=(None, None))) 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.\ mock_sg_snap_update.assert_called_once_with(
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.ANY, fake_snap['id'], mock.ANY, fake_snap['id'],
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY}) {'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
def test_create_cgsnapshot_with_member_update(self): def test_create_share_group_snapshot_with_update(self):
fake_member = { fake_snap = {'id': 'fake_snap_id', 'share_group': {},
'id': 'fake_member_id', 'share_group_snapshot_members': []}
'share_instance_id': 'blah', self.mock_object(self.share_manager.db, 'share_group_snapshot_get',
}
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',
mock.Mock(return_value=fake_snap)) 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.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',
mock.Mock(return_value=fake_snap)) mock.Mock(return_value=fake_snap))
self.mock_object(self.share_manager.driver, self.mock_object(self.share_manager.driver,
'create_cgsnapshot', 'create_share_group_snapshot',
mock.Mock(side_effect=exception.Error)) mock.Mock(return_value=({'foo': 'bar'}, None)))
self.assertRaises(exception.Error, self.share_manager.create_share_group_snapshot(
self.share_manager.create_cgsnapshot, self.context, fake_snap['id'])
self.context, fake_snap['id'])
self.share_manager.db.cgsnapshot_update.\ self.share_manager.db.share_group_snapshot_update.assert_any_call(
assert_called_once_with(mock.ANY, fake_snap['id'], mock.ANY, 'fake_snap_id', {'foo': 'bar'})
{'status': constants.STATUS_ERROR}) 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): def test_connection_get_info(self):
share_instance = {'share_server_id': 'fake_server_id'} share_instance = {'share_server_id': 'fake_server_id'}
@ -4857,9 +4889,9 @@ class ShareManagerTestCase(test.TestCase):
def test_provide_share_server(self): def test_provide_share_server(self):
instance = db_utils.create_share_instance(share_id='fake_id', 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) 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() server = db_utils.create_share_server()
# mocks # mocks
@ -4867,8 +4899,8 @@ class ShareManagerTestCase(test.TestCase):
mock.Mock(return_value=instance)) mock.Mock(return_value=instance))
self.mock_object(self.share_manager.db, 'share_snapshot_get', self.mock_object(self.share_manager.db, 'share_snapshot_get',
mock.Mock(return_value=snapshot)) mock.Mock(return_value=snapshot))
self.mock_object(self.share_manager.db, 'consistency_group_get', self.mock_object(self.share_manager.db, 'share_group_get',
mock.Mock(return_value=cg)) mock.Mock(return_value=group))
self.mock_object(self.share_manager, '_provide_share_server_for_share', self.mock_object(self.share_manager, '_provide_share_server_for_share',
mock.Mock(return_value=(server, instance))) mock.Mock(return_value=(server, instance)))
@ -4882,11 +4914,11 @@ class ShareManagerTestCase(test.TestCase):
self.context, 'ins_id', with_share_data=True) self.context, 'ins_id', with_share_data=True)
self.share_manager.db.share_snapshot_get.assert_called_once_with( self.share_manager.db.share_snapshot_get.assert_called_once_with(
self.context, 'snap_id') self.context, 'snap_id')
self.share_manager.db.consistency_group_get.assert_called_once_with( self.share_manager.db.share_group_get.assert_called_once_with(
self.context, 'cg_id') self.context, 'sg_id')
(self.share_manager._provide_share_server_for_share. (self.share_manager._provide_share_server_for_share.
assert_called_once_with(self.context, 'net_id', instance, snapshot, 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): def test_create_share_server(self):

View File

@ -46,8 +46,8 @@ class ShareRpcAPITestCase(test.TestCase):
host='fake_host', host='fake_host',
) )
share_server = db_utils.create_share_server() share_server = db_utils.create_share_server()
cg = {'id': 'fake_cg_id', 'host': 'fake_host'} share_group = {'id': 'fake_share_group_id', 'host': 'fake_host'}
cgsnapshot = {'id': 'fake_cg_id'} share_group_snapshot = {'id': 'fake_share_group_id'}
host = 'fake_host' host = 'fake_host'
self.fake_share = jsonutils.to_primitive(share) self.fake_share = jsonutils.to_primitive(share)
# mock out the getattr on the share db model object since jsonutils # 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_share_replica = jsonutils.to_primitive(share_replica)
self.fake_snapshot = jsonutils.to_primitive(snapshot) self.fake_snapshot = jsonutils.to_primitive(snapshot)
self.fake_share_server = jsonutils.to_primitive(share_server) self.fake_share_server = jsonutils.to_primitive(share_server)
self.fake_cg = jsonutils.to_primitive(cg) self.fake_share_group = jsonutils.to_primitive(share_group)
self.fake_cgsnapshot = jsonutils.to_primitive(cgsnapshot) self.fake_share_group_snapshot = jsonutils.to_primitive(
share_group_snapshot)
self.fake_host = jsonutils.to_primitive(host) self.fake_host = jsonutils.to_primitive(host)
self.ctxt = context.RequestContext('fake_user', 'fake_project') self.ctxt = context.RequestContext('fake_user', 'fake_project')
self.rpcapi = share_rpcapi.ShareAPI() self.rpcapi = share_rpcapi.ShareAPI()
@ -79,14 +80,14 @@ class ShareRpcAPITestCase(test.TestCase):
if 'share_instance' in expected_msg: if 'share_instance' in expected_msg:
share_instance = expected_msg.pop('share_instance', None) share_instance = expected_msg.pop('share_instance', None)
expected_msg['share_instance_id'] = share_instance['id'] expected_msg['share_instance_id'] = share_instance['id']
if 'cg' in expected_msg: if 'share_group' in expected_msg:
cg = expected_msg['cg'] share_group = expected_msg['share_group']
del expected_msg['cg'] del expected_msg['share_group']
expected_msg['cg_id'] = cg['id'] expected_msg['share_group_id'] = share_group['id']
if 'cgsnapshot' in expected_msg: if 'share_group_snapshot' in expected_msg:
snap = expected_msg['cgsnapshot'] snap = expected_msg['share_group_snapshot']
del expected_msg['cgsnapshot'] del expected_msg['share_group_snapshot']
expected_msg['cgsnapshot_id'] = snap['id'] expected_msg['share_group_snapshot_id'] = snap['id']
if 'host' in expected_msg: if 'host' in expected_msg:
del expected_msg['host'] del expected_msg['host']
if 'snapshot' in expected_msg: if 'snapshot' in expected_msg:
@ -113,8 +114,8 @@ class ShareRpcAPITestCase(test.TestCase):
if 'host' in kwargs: if 'host' in kwargs:
host = kwargs['host'] host = kwargs['host']
elif 'cg' in kwargs: elif 'share_group' in kwargs:
host = kwargs['cg']['host'] host = kwargs['share_group']['host']
elif 'share_instance' in kwargs: elif 'share_instance' in kwargs:
host = kwargs['share_instance']['host'] host = kwargs['share_instance']['host']
elif 'share_server' in kwargs: elif 'share_server' in kwargs:
@ -213,32 +214,34 @@ class ShareRpcAPITestCase(test.TestCase):
share=self.fake_share, share=self.fake_share,
new_size=123) new_size=123)
def test_create_consistency_group(self): def test_create_share_group(self):
self._test_share_api('create_consistency_group', self._test_share_api('create_share_group',
version='1.5', version='1.16',
rpc_method='cast', rpc_method='cast',
cg=self.fake_cg, share_group=self.fake_share_group,
host='fake_host1') host='fake_host1')
def test_delete_consistency_group(self): def test_delete_share_group(self):
self._test_share_api('delete_consistency_group', self._test_share_api('delete_share_group',
version='1.5', version='1.16',
rpc_method='cast', rpc_method='cast',
cg=self.fake_cg) share_group=self.fake_share_group)
def test_create_cgsnapshot(self): def test_create_share_group_snapshot(self):
self._test_share_api('create_cgsnapshot', self._test_share_api(
version='1.5', 'create_share_group_snapshot',
rpc_method='cast', version='1.16',
cgsnapshot=self.fake_cgsnapshot, rpc_method='cast',
host='fake_host1') share_group_snapshot=self.fake_share_group_snapshot,
host='fake_host1')
def test_delete_cgsnapshot(self): def test_delete_share_group_snapshot(self):
self._test_share_api('delete_cgsnapshot', self._test_share_api(
version='1.5', 'delete_share_group_snapshot',
rpc_method='cast', version='1.16',
cgsnapshot=self.fake_cgsnapshot, rpc_method='cast',
host='fake_host1') share_group_snapshot=self.fake_share_group_snapshot,
host='fake_host1')
def test_migration_start(self): def test_migration_start(self):
self._test_share_api('migration_start', 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 " help="The minimum api microversion is configured to be the "
"value of the minimum microversion supported by Manila."), "value of the minimum microversion supported by Manila."),
cfg.StrOpt("max_api_microversion", cfg.StrOpt("max_api_microversion",
default="2.30", default="2.31",
help="The maximum api microversion is configured to be the " help="The maximum api microversion is configured to be the "
"value of the latest microversion supported by Manila."), "value of the latest microversion supported by Manila."),
cfg.StrOpt("region", cfg.StrOpt("region",
@ -171,7 +171,7 @@ ShareGroup = [
"to snapshots or not. Enable this feature if used " "to snapshots or not. Enable this feature if used "
"driver supports it."), "driver supports it."),
cfg.BoolOpt("run_consistency_group_tests", cfg.BoolOpt("run_consistency_group_tests",
default=True, default=False,
help="Defines whether to run consistency group tests or not. " help="Defines whether to run consistency group tests or not. "
"Disable this feature if used driver doesn't support " "Disable this feature if used driver doesn't support "
"it."), "it."),

View File

@ -94,9 +94,6 @@ class SharesActionsTest(base.BaseSharesTest):
expected_keys.extend(["export_location", "export_locations"]) expected_keys.extend(["export_location", "export_locations"])
if utils.is_microversion_ge(version, '2.2'): if utils.is_microversion_ge(version, '2.2'):
expected_keys.append("snapshot_support") 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'): if utils.is_microversion_ge(version, '2.5'):
expected_keys.append("share_type_name") expected_keys.append("share_type_name")
if utils.is_microversion_ge(version, '2.10'): 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): def test_get_share_with_snapshot_support_key(self):
self._get_share('2.2') 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) @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
@utils.skip_if_microversion_not_supported('2.6') @utils.skip_if_microversion_not_supported('2.6')
def test_get_share_with_share_type_name_key(self): def test_get_share_with_share_type_name_key(self):
@ -210,9 +202,6 @@ class SharesActionsTest(base.BaseSharesTest):
keys.extend(["export_location", "export_locations"]) keys.extend(["export_location", "export_locations"])
if utils.is_microversion_ge(version, '2.2'): if utils.is_microversion_ge(version, '2.2'):
keys.append("snapshot_support") 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'): if utils.is_microversion_ge(version, '2.6'):
keys.append("share_type_name") keys.append("share_type_name")
if utils.is_microversion_ge(version, '2.10'): 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): def test_list_shares_with_detail_and_snapshot_support_key(self):
self._list_shares_with_detail('2.2') 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) @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
@utils.skip_if_microversion_not_supported('2.6') @utils.skip_if_microversion_not_supported('2.6')
def test_list_shares_with_detail_share_type_name_key(self): 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 AvailabilityZoneFilter = manila.scheduler.filters.availability_zone:AvailabilityZoneFilter
CapabilitiesFilter = manila.scheduler.filters.capabilities:CapabilitiesFilter CapabilitiesFilter = manila.scheduler.filters.capabilities:CapabilitiesFilter
CapacityFilter = manila.scheduler.filters.capacity:CapacityFilter CapacityFilter = manila.scheduler.filters.capacity:CapacityFilter
ConsistencyGroupFilter = manila.scheduler.filters.consistency_group:ConsistencyGroupFilter
DriverFilter = manila.scheduler.filters.driver:DriverFilter DriverFilter = manila.scheduler.filters.driver:DriverFilter
IgnoreAttemptedHostsFilter = manila.scheduler.filters.ignore_attempted_hosts:IgnoreAttemptedHostsFilter IgnoreAttemptedHostsFilter = manila.scheduler.filters.ignore_attempted_hosts:IgnoreAttemptedHostsFilter
JsonFilter = manila.scheduler.filters.json:JsonFilter JsonFilter = manila.scheduler.filters.json:JsonFilter