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:
parent
3e37cd2e5a
commit
d25f101ab4
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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')))):
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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())
|
|
@ -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())
|
|
@ -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())
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}
|
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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',
|
||||||
|
|
272
manila/db/api.py
272
manila/db/api.py
|
@ -322,9 +322,10 @@ def share_instances_get_all_by_share(context, share_id):
|
||||||
return IMPL.share_instances_get_all_by_share(context, share_id)
|
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)
|
||||||
|
|
|
@ -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"])
|
|
@ -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
|
@ -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():
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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')
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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'])
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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, []
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
|
@ -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',
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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'])
|
||||||
|
|
|
@ -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')
|
|
@ -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
|
@ -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,
|
||||||
|
|
|
@ -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': [
|
||||||
|
|
|
@ -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'])
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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={})
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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'])
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
||||||
|
|
|
@ -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
|
@ -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')
|
|
@ -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."),
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue