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_SNAPSHOT_TESTS=${RUN_MANILA_SNAPSHOT_TESTS:-True}
|
||||
RUN_MANILA_REVERT_TO_SNAPSHOT_TESTS=${RUN_MANILA_REVERT_TO_SNAPSHOT_TESTS:-False}
|
||||
RUN_MANILA_CG_TESTS=${RUN_MANILA_CG_TESTS:-True}
|
||||
RUN_MANILA_CG_TESTS=${RUN_MANILA_CG_TESTS:-False}
|
||||
RUN_MANILA_MANAGE_TESTS=${RUN_MANILA_MANAGE_TESTS:-True}
|
||||
RUN_MANILA_MANAGE_SNAPSHOT_TESTS=${RUN_MANILA_MANAGE_SNAPSHOT_TESTS:-False}
|
||||
RUN_MANILA_REPLICATION_TESTS=${RUN_MANILA_REPLICATION_TESTS:-False}
|
||||
@ -207,7 +207,7 @@ elif [[ "$DRIVER" == "zfsonlinux" ]]; then
|
||||
iniset $TEMPEST_CONFIG share capability_snapshot_support True
|
||||
elif [[ "$DRIVER" == "dummy" ]]; then
|
||||
MANILA_TEMPEST_CONCURRENCY=24
|
||||
RUN_MANILA_CG_TESTS=True
|
||||
RUN_MANILA_CG_TESTS=False
|
||||
RUN_MANILA_MANAGE_TESTS=False
|
||||
RUN_MANILA_DRIVER_ASSISTED_MIGRATION_TESTS=True
|
||||
RUN_MANILA_REVERT_TO_SNAPSHOT_TESTS=True
|
||||
|
@ -108,21 +108,21 @@
|
||||
"scheduler_stats:pools:index": "rule:admin_api",
|
||||
"scheduler_stats:pools:detail": "rule:admin_api",
|
||||
|
||||
"consistency_group:create" : "rule:default",
|
||||
"consistency_group:delete": "rule:default",
|
||||
"consistency_group:update": "rule:default",
|
||||
"consistency_group:get": "rule:default",
|
||||
"consistency_group:get_all": "rule:default",
|
||||
"consistency_group:force_delete": "rule:admin_api",
|
||||
"consistency_group:reset_status": "rule:admin_api",
|
||||
"share_group:create" : "rule:default",
|
||||
"share_group:delete": "rule:default",
|
||||
"share_group:update": "rule:default",
|
||||
"share_group:get": "rule:default",
|
||||
"share_group:get_all": "rule:default",
|
||||
"share_group:force_delete": "rule:admin_api",
|
||||
"share_group:reset_status": "rule:admin_api",
|
||||
|
||||
"cgsnapshot:force_delete": "rule:admin_api",
|
||||
"cgsnapshot:reset_status": "rule:admin_api",
|
||||
"cgsnapshot:create" : "rule:default",
|
||||
"cgsnapshot:update" : "rule:default",
|
||||
"cgsnapshot:delete": "rule:default",
|
||||
"cgsnapshot:get_cgsnapshot": "rule:default",
|
||||
"cgsnapshot:get_all": "rule:default",
|
||||
"share_group_snapshot:create" : "rule:default",
|
||||
"share_group_snapshot:delete": "rule:default",
|
||||
"share_group_snapshot:update" : "rule:default",
|
||||
"share_group_snapshot:get": "rule:default",
|
||||
"share_group_snapshot:get_all": "rule:default",
|
||||
"share_group_snapshot:force_delete": "rule:admin_api",
|
||||
"share_group_snapshot:reset_status": "rule:admin_api",
|
||||
|
||||
"share_replica:get_all": "rule:default",
|
||||
"share_replica:show": "rule:default",
|
||||
@ -132,5 +132,20 @@
|
||||
"share_replica:resync": "rule:admin_api",
|
||||
"share_replica:reset_status": "rule:admin_api",
|
||||
"share_replica:force_delete": "rule:admin_api",
|
||||
"share_replica:reset_replica_state": "rule:admin_api"
|
||||
"share_replica:reset_replica_state": "rule:admin_api",
|
||||
|
||||
"share_group_type:index": "rule:default",
|
||||
"share_group_type:show": "rule:default",
|
||||
"share_group_type:default": "rule:default",
|
||||
"share_group_type:create": "rule:admin_api",
|
||||
"share_group_type:delete": "rule:admin_api",
|
||||
"share_group_type:add_project_access": "rule:admin_api",
|
||||
"share_group_type:list_project_access": "rule:admin_api",
|
||||
"share_group_type:remove_project_access": "rule:admin_api",
|
||||
|
||||
"share_group_types_spec:create": "rule:admin_api",
|
||||
"share_group_types_spec:update": "rule:admin_api",
|
||||
"share_group_types_spec:show": "rule:admin_api",
|
||||
"share_group_types_spec:index": "rule:admin_api",
|
||||
"share_group_types_spec:delete": "rule:admin_api"
|
||||
}
|
||||
|
@ -96,13 +96,14 @@ REST_API_VERSION_HISTORY = """
|
||||
migration_start APIs prior to this microversion are now
|
||||
unsupported.
|
||||
* 2.30 - Added cast_rules_to_readonly field to share_instances.
|
||||
* 2.31 - Convert consistency groups to share groups.
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
# The default api version request is defined to be the
|
||||
# minimum version of the API supported.
|
||||
_MIN_API_VERSION = "2.0"
|
||||
_MAX_API_VERSION = "2.30"
|
||||
_MAX_API_VERSION = "2.31"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
|
||||
@ -142,8 +143,13 @@ class APIVersionRequest(utils.ComparableMixin):
|
||||
|
||||
def __str__(self):
|
||||
"""Debug/Logging representation of object."""
|
||||
return ("API Version Request Major: %(major)s, Minor: %(minor)s"
|
||||
% {'major': self._ver_major, 'minor': self._ver_minor})
|
||||
params = {
|
||||
'major': self._ver_major,
|
||||
'minor': self._ver_minor,
|
||||
'experimental': self._experimental,
|
||||
}
|
||||
return ("API Version Request Major: %(major)s, Minor: %(minor)s, "
|
||||
"Experimental: %(experimental)s" % params)
|
||||
|
||||
def is_null(self):
|
||||
return self._ver_major is None and self._ver_minor is None
|
||||
|
@ -183,3 +183,7 @@ user documentation.
|
||||
2.30
|
||||
----
|
||||
Added cast_rules_to_readonly field to share_instances.
|
||||
|
||||
2.31
|
||||
----
|
||||
Convert consistency groups to share groups.
|
||||
|
@ -75,19 +75,19 @@ class ShareMixin(object):
|
||||
try:
|
||||
share = self.share_api.get(context, id)
|
||||
|
||||
# NOTE(ameade): If the share is in a consistency group, we require
|
||||
# it's id be specified as a param.
|
||||
if share.get('consistency_group_id'):
|
||||
consistency_group_id = req.params.get('consistency_group_id')
|
||||
if (share.get('consistency_group_id') and
|
||||
not consistency_group_id):
|
||||
msg = _("Must provide 'consistency_group_id' as a request "
|
||||
"parameter when deleting a share in a consistency "
|
||||
"group.")
|
||||
# NOTE(ameade): If the share is in a share group, we require its
|
||||
# id be specified as a param.
|
||||
sg_id_key = 'share_group_id'
|
||||
if share.get(sg_id_key):
|
||||
share_group_id = req.params.get(sg_id_key)
|
||||
if not share_group_id:
|
||||
msg = _("Must provide '%s' as a request "
|
||||
"parameter when deleting a share in a share "
|
||||
"group.") % sg_id_key
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
elif consistency_group_id != share.get('consistency_group_id'):
|
||||
msg = _("The specified 'consistency_group_id' does not "
|
||||
"match the consistency group id of the share.")
|
||||
elif share_group_id != share.get(sg_id_key):
|
||||
msg = _("The specified '%s' does not match "
|
||||
"the share group id of the share.") % sg_id_key
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
self.share_api.delete(context, share)
|
||||
@ -161,7 +161,7 @@ class ShareMixin(object):
|
||||
'display_name', 'status', 'share_server_id', 'volume_type_id',
|
||||
'share_type_id', 'snapshot_id', 'host', 'share_network_id',
|
||||
'is_public', 'metadata', 'extra_specs', 'sort_key', 'sort_dir',
|
||||
'consistency_group_id', 'cgsnapshot_id'
|
||||
'share_group_id', 'share_group_snapshot_id'
|
||||
)
|
||||
|
||||
def update(self, req, id, body):
|
||||
@ -192,8 +192,8 @@ class ShareMixin(object):
|
||||
return self._view_builder.detail(req, share)
|
||||
|
||||
def create(self, req, body):
|
||||
# Remove consistency group attributes
|
||||
body.get('share', {}).pop('consistency_group_id', None)
|
||||
# Remove share group attributes
|
||||
body.get('share', {}).pop('share_group_id', None)
|
||||
share = self._create(req, body)
|
||||
return share
|
||||
|
||||
@ -237,7 +237,7 @@ class ShareMixin(object):
|
||||
'availability_zone': availability_zone,
|
||||
'metadata': share.get('metadata'),
|
||||
'is_public': share.get('is_public', False),
|
||||
'consistency_group_id': share.get('consistency_group_id')
|
||||
'share_group_id': share.get('share_group_id')
|
||||
}
|
||||
|
||||
snapshot_id = share.get('snapshot_id')
|
||||
@ -310,10 +310,10 @@ class ShareMixin(object):
|
||||
share_type = def_share_type
|
||||
|
||||
# Only use in create share feature. Create share from snapshot
|
||||
# and create share with consistency group features not
|
||||
# and create share with share group features not
|
||||
# need this check.
|
||||
if (not share_network_id and not snapshot
|
||||
and not share.get('consistency_group_id')
|
||||
and not share.get('share_group_id')
|
||||
and share_type and share_type.get('extra_specs')
|
||||
and (strutils.bool_from_string(share_type.get('extra_specs').
|
||||
get('driver_handles_share_servers')))):
|
||||
|
@ -36,6 +36,8 @@ from manila.api.v2 import quota_sets
|
||||
from manila.api.v2 import services
|
||||
from manila.api.v2 import share_export_locations
|
||||
from manila.api.v2 import share_group_snapshots
|
||||
from manila.api.v2 import share_group_type_specs
|
||||
from manila.api.v2 import share_group_types
|
||||
from manila.api.v2 import share_groups
|
||||
from manila.api.v2 import share_instance_export_locations
|
||||
from manila.api.v2 import share_instances
|
||||
@ -280,21 +282,83 @@ class APIRouter(manila.api.openstack.APIRouter):
|
||||
action="pools_detail",
|
||||
conditions={"method": ["GET"]})
|
||||
|
||||
self.resources["consistency-groups"] = share_groups.create_resource()
|
||||
mapper.resource("consistency-group", "consistency-groups",
|
||||
controller=self.resources["consistency-groups"],
|
||||
self.resources["share-groups"] = share_groups.create_resource()
|
||||
mapper.resource(
|
||||
"share-group",
|
||||
"share-groups",
|
||||
controller=self.resources["share-groups"],
|
||||
collection={"detail": "GET"})
|
||||
mapper.connect("consistency-groups",
|
||||
"/{project_id}/consistency-groups/{id}/action",
|
||||
controller=self.resources["consistency-groups"],
|
||||
mapper.connect(
|
||||
"share-groups",
|
||||
"/{project_id}/share-groups/{id}/action",
|
||||
controller=self.resources["share-groups"],
|
||||
action="action",
|
||||
conditions={"action": ["POST"]})
|
||||
conditions={"method": ["POST"]})
|
||||
|
||||
self.resources["cgsnapshots"] = share_group_snapshots.create_resource()
|
||||
mapper.resource("cgsnapshot", "cgsnapshots",
|
||||
controller=self.resources["cgsnapshots"],
|
||||
self.resources["share-group-types"] = (
|
||||
share_group_types.create_resource())
|
||||
mapper.resource(
|
||||
"share-group-type",
|
||||
"share-group-types",
|
||||
controller=self.resources["share-group-types"],
|
||||
collection={"detail": "GET", "default": "GET"},
|
||||
member={"action": "POST"})
|
||||
mapper.connect(
|
||||
"share-group-types",
|
||||
"/{project_id}/share-group-types/{id}/access",
|
||||
controller=self.resources["share-group-types"],
|
||||
action="share_group_type_access",
|
||||
conditions={"method": ["GET"]})
|
||||
|
||||
# NOTE(ameade): These routes can be simplified when the following
|
||||
# issue is fixed: https://github.com/bbangert/routes/issues/68
|
||||
self.resources["group-specs"] = (
|
||||
share_group_type_specs.create_resource())
|
||||
mapper.connect(
|
||||
"share-group-types",
|
||||
"/{project_id}/share-group-types/{id}/group-specs",
|
||||
controller=self.resources["group-specs"],
|
||||
action="index",
|
||||
conditions={"method": ["GET"]})
|
||||
mapper.connect(
|
||||
"share-group-types",
|
||||
"/{project_id}/share-group-types/{id}/group-specs",
|
||||
controller=self.resources["group-specs"],
|
||||
action="create",
|
||||
conditions={"method": ["POST"]})
|
||||
mapper.connect(
|
||||
"share-group-types",
|
||||
"/{project_id}/share-group-types/{id}/group-specs/{key}",
|
||||
controller=self.resources["group-specs"],
|
||||
action="show",
|
||||
conditions={"method": ["GET"]})
|
||||
mapper.connect(
|
||||
"share-group-types",
|
||||
"/{project_id}/share-group-types/{id}/group-specs/{key}",
|
||||
controller=self.resources["group-specs"],
|
||||
action="delete",
|
||||
conditions={"method": ["DELETE"]})
|
||||
mapper.connect(
|
||||
"share-group-types",
|
||||
"/{project_id}/share-group-types/{id}/group-specs/{key}",
|
||||
controller=self.resources["group-specs"],
|
||||
action="update",
|
||||
conditions={"method": ["PUT"]})
|
||||
|
||||
self.resources["share-group-snapshots"] = (
|
||||
share_group_snapshots.create_resource())
|
||||
mapper.resource(
|
||||
"share-group-snapshot",
|
||||
"share-group-snapshots",
|
||||
controller=self.resources["share-group-snapshots"],
|
||||
collection={"detail": "GET"},
|
||||
member={"members": "GET", "action": "POST"})
|
||||
mapper.connect(
|
||||
"share-group-snapshots",
|
||||
"/{project_id}/share-group-snapshots/{id}/action",
|
||||
controller=self.resources["share-group-snapshots"],
|
||||
action="action",
|
||||
conditions={"method": ["POST"]})
|
||||
|
||||
self.resources['share-replicas'] = share_replicas.create_resource()
|
||||
mapper.resource("share-replica", "share-replicas",
|
||||
|
@ -13,8 +13,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""The consistency groups snapshot API."""
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
@ -23,86 +21,85 @@ from webob import exc
|
||||
|
||||
from manila.api import common
|
||||
from manila.api.openstack import wsgi
|
||||
import manila.api.views.share_group_snapshots as sgs_views
|
||||
import manila.api.views.share_group_snapshots as share_group_snapshots_views
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila.i18n import _, _LI
|
||||
import manila.share_group.api as sg_api
|
||||
import manila.share_group.api as share_group_api
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class CGSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
|
||||
"""The Consistency Group Snapshots API controller for the OpenStack API."""
|
||||
class ShareGroupSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
|
||||
"""The share group snapshots API controller for the OpenStack API."""
|
||||
|
||||
resource_name = 'cgsnapshot'
|
||||
_view_builder_class = sgs_views.CGSnapshotViewBuilder
|
||||
resource_name = 'share_group_snapshot'
|
||||
_view_builder_class = (
|
||||
share_group_snapshots_views.ShareGroupSnapshotViewBuilder)
|
||||
|
||||
def __init__(self):
|
||||
super(CGSnapshotController, self).__init__()
|
||||
self.cg_api = sg_api.API()
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.authorize('get_cgsnapshot')
|
||||
def show(self, req, id):
|
||||
"""Return data about the given cgsnapshot."""
|
||||
context = req.environ['manila.context']
|
||||
super(ShareGroupSnapshotController, self).__init__()
|
||||
self.share_group_api = share_group_api.API()
|
||||
|
||||
def _get_share_group_snapshot(self, context, sg_snapshot_id):
|
||||
try:
|
||||
cg = self.cg_api.get_cgsnapshot(context, id)
|
||||
return self.share_group_api.get_share_group_snapshot(
|
||||
context, sg_snapshot_id)
|
||||
except exception.NotFound:
|
||||
msg = _("Consistency group snapshot %s not found.") % id
|
||||
msg = _("Share group snapshot %s not found.") % sg_snapshot_id
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
return self._view_builder.detail(req, cg)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize('get')
|
||||
def show(self, req, id):
|
||||
"""Return data about the given share group snapshot."""
|
||||
context = req.environ['manila.context']
|
||||
sg_snapshot = self._get_share_group_snapshot(context, id)
|
||||
return self._view_builder.detail(req, sg_snapshot)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def delete(self, req, id):
|
||||
"""Delete a cgsnapshot."""
|
||||
"""Delete a share group snapshot."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
LOG.info(_LI("Delete consistency group snapshot with id: %s"), id,
|
||||
context=context)
|
||||
|
||||
LOG.info(_LI("Delete share group snapshot with id: %s"),
|
||||
id, context=context)
|
||||
sg_snapshot = self._get_share_group_snapshot(context, id)
|
||||
try:
|
||||
snap = self.cg_api.get_cgsnapshot(context, id)
|
||||
except exception.NotFound:
|
||||
msg = _("Consistency group snapshot %s not found.") % id
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
try:
|
||||
self.cg_api.delete_cgsnapshot(context, snap)
|
||||
except exception.InvalidCGSnapshot as e:
|
||||
self.share_group_api.delete_share_group_snapshot(
|
||||
context, sg_snapshot)
|
||||
except exception.InvalidShareGroupSnapshot as e:
|
||||
raise exc.HTTPConflict(explanation=six.text_type(e))
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize('get_all')
|
||||
def index(self, req):
|
||||
"""Returns a summary list of cgsnapshots."""
|
||||
return self._get_cgs(req, is_detail=False)
|
||||
"""Returns a summary list of share group snapshots."""
|
||||
return self._get_share_group_snaps(req, is_detail=False)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize('get_all')
|
||||
def detail(self, req):
|
||||
"""Returns a detailed list of cgsnapshots."""
|
||||
return self._get_cgs(req, is_detail=True)
|
||||
"""Returns a detailed list of share group snapshots."""
|
||||
return self._get_share_group_snaps(req, is_detail=True)
|
||||
|
||||
def _get_cgs(self, req, is_detail):
|
||||
"""Returns a list of cgsnapshots."""
|
||||
def _get_share_group_snaps(self, req, is_detail):
|
||||
"""Returns a list of share group snapshots."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
search_opts = {}
|
||||
search_opts.update(req.GET)
|
||||
|
||||
# Remove keys that are not related to cg attrs
|
||||
# Remove keys that are not related to group attrs
|
||||
search_opts.pop('limit', None)
|
||||
search_opts.pop('offset', None)
|
||||
sort_key = search_opts.pop('sort_key', 'created_at')
|
||||
sort_dir = search_opts.pop('sort_dir', 'desc')
|
||||
|
||||
snaps = self.cg_api.get_all_cgsnapshots(
|
||||
context, detailed=is_detail, search_opts=search_opts)
|
||||
snaps = self.share_group_api.get_all_share_group_snapshots(
|
||||
context, detailed=is_detail, search_opts=search_opts,
|
||||
sort_dir=sort_dir, sort_key=sort_key)
|
||||
|
||||
limited_list = common.limited(snaps, req)
|
||||
|
||||
@ -112,88 +109,76 @@ class CGSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
|
||||
snaps = self._view_builder.summary_list(req, limited_list)
|
||||
return snaps
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def update(self, req, id, body):
|
||||
"""Update a cgsnapshot."""
|
||||
"""Update a share group snapshot."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
if not self.is_valid_body(body, 'cgsnapshot'):
|
||||
msg = _("'cgsnapshot' is missing from the request body")
|
||||
key = 'share_group_snapshot'
|
||||
if not self.is_valid_body(body, key):
|
||||
msg = _("'%s' is missing from the request body.") % key
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
cg_data = body['cgsnapshot']
|
||||
sg_snapshot_data = body[key]
|
||||
valid_update_keys = {
|
||||
'name',
|
||||
'description',
|
||||
}
|
||||
invalid_fields = set(cg_data.keys()) - valid_update_keys
|
||||
invalid_fields = set(sg_snapshot_data.keys()) - valid_update_keys
|
||||
if invalid_fields:
|
||||
msg = _("The fields %s are invalid or not allowed to be updated.")
|
||||
raise exc.HTTPBadRequest(explanation=msg % invalid_fields)
|
||||
|
||||
try:
|
||||
cg = self.cg_api.get_cgsnapshot(context, id)
|
||||
except exception.NotFound:
|
||||
msg = _("Consistency group snapshot %s not found.") % id
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
sg_snapshot = self._get_share_group_snapshot(context, id)
|
||||
sg_snapshot = self.share_group_api.update_share_group_snapshot(
|
||||
context, sg_snapshot, sg_snapshot_data)
|
||||
return self._view_builder.detail(req, sg_snapshot)
|
||||
|
||||
cg = self.cg_api.update_cgsnapshot(context, cg, cg_data)
|
||||
return self._view_builder.detail(req, cg)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.response(202)
|
||||
@wsgi.Controller.authorize
|
||||
def create(self, req, body):
|
||||
"""Creates a new cgsnapshot."""
|
||||
"""Creates a new share group snapshot."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
if not self.is_valid_body(body, 'cgsnapshot'):
|
||||
msg = _("'cgsnapshot' is missing from the request body")
|
||||
if not self.is_valid_body(body, 'share_group_snapshot'):
|
||||
msg = _("'share_group_snapshot' is missing from the request body.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
cgsnapshot = body.get('cgsnapshot')
|
||||
share_group_snapshot = body.get('share_group_snapshot', {})
|
||||
|
||||
if not cgsnapshot.get('consistency_group_id'):
|
||||
msg = _("Must supply 'consistency_group_id' attribute.")
|
||||
share_group_id = share_group_snapshot.get('share_group_id')
|
||||
if not share_group_id:
|
||||
msg = _("Must supply 'share_group_id' attribute.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
consistency_group_id = cgsnapshot.get('consistency_group_id')
|
||||
if (consistency_group_id and
|
||||
not uuidutils.is_uuid_like(consistency_group_id)):
|
||||
msg = _("The 'consistency_group_id' attribute must be a uuid.")
|
||||
if not uuidutils.is_uuid_like(share_group_id):
|
||||
msg = _("The 'share_group_id' attribute must be a uuid.")
|
||||
raise exc.HTTPBadRequest(explanation=six.text_type(msg))
|
||||
|
||||
kwargs = {"consistency_group_id": consistency_group_id}
|
||||
|
||||
if 'name' in cgsnapshot:
|
||||
kwargs['name'] = cgsnapshot.get('name')
|
||||
if 'description' in cgsnapshot:
|
||||
kwargs['description'] = cgsnapshot.get('description')
|
||||
kwargs = {"share_group_id": share_group_id}
|
||||
if 'name' in share_group_snapshot:
|
||||
kwargs['name'] = share_group_snapshot.get('name')
|
||||
if 'description' in share_group_snapshot:
|
||||
kwargs['description'] = share_group_snapshot.get('description')
|
||||
|
||||
try:
|
||||
new_snapshot = self.cg_api.create_cgsnapshot(context, **kwargs)
|
||||
except exception.ConsistencyGroupNotFound as e:
|
||||
new_snapshot = self.share_group_api.create_share_group_snapshot(
|
||||
context, **kwargs)
|
||||
except exception.ShareGroupNotFound as e:
|
||||
raise exc.HTTPBadRequest(explanation=six.text_type(e))
|
||||
except exception.InvalidConsistencyGroup as e:
|
||||
except exception.InvalidShareGroup as e:
|
||||
raise exc.HTTPConflict(explanation=six.text_type(e))
|
||||
|
||||
return self._view_builder.detail(req, dict(new_snapshot.items()))
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.authorize('get_cgsnapshot')
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize('get')
|
||||
def members(self, req, id):
|
||||
"""Returns a list of cgsnapshot members."""
|
||||
"""Returns a list of share group snapshot members."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
search_opts = {}
|
||||
search_opts.update(req.GET)
|
||||
|
||||
# Remove keys that are not related to cg attrs
|
||||
search_opts.pop('limit', None)
|
||||
search_opts.pop('offset', None)
|
||||
|
||||
snaps = self.cg_api.get_all_cgsnapshot_members(context, id)
|
||||
snaps = self.share_group_api.get_all_share_group_snapshot_members(
|
||||
context, id)
|
||||
|
||||
limited_list = common.limited(snaps, req)
|
||||
|
||||
@ -201,34 +186,24 @@ class CGSnapshotController(wsgi.Controller, wsgi.AdminActionsMixin):
|
||||
return snaps
|
||||
|
||||
def _update(self, *args, **kwargs):
|
||||
db.cgsnapshot_update(*args, **kwargs)
|
||||
db.share_group_snapshot_update(*args, **kwargs)
|
||||
|
||||
def _get(self, *args, **kwargs):
|
||||
return self.cg_api.get_cgsnapshot(*args, **kwargs)
|
||||
return self.share_group_api.get_share_group_snapshot(*args, **kwargs)
|
||||
|
||||
def _delete(self, context, resource, force=True):
|
||||
db.cgsnapshot_destroy(context.elevated(), resource['id'])
|
||||
db.share_group_snapshot_destroy(context.elevated(), resource['id'])
|
||||
|
||||
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
|
||||
@wsgi.action('os-reset_status')
|
||||
def cgsnapshot_reset_status_legacy(self, req, id, body):
|
||||
return self._reset_status(req, id, body)
|
||||
|
||||
@wsgi.Controller.api_version('2.7', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.action('reset_status')
|
||||
def cgsnapshot_reset_status(self, req, id, body):
|
||||
def share_group_snapshot_reset_status(self, req, id, body):
|
||||
return self._reset_status(req, id, body)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
|
||||
@wsgi.action('os-force_delete')
|
||||
def cgsnapshot_force_delete_legacy(self, req, id, body):
|
||||
return self._force_delete(req, id, body)
|
||||
|
||||
@wsgi.Controller.api_version('2.7', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.action('force_delete')
|
||||
def cgsnapshot_force_delete(self, req, id, body):
|
||||
def share_group_snapshot_force_delete(self, req, id, body):
|
||||
return self._force_delete(req, id, body)
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(CGSnapshotController())
|
||||
return wsgi.Resource(ShareGroupSnapshotController())
|
||||
|
133
manila/api/v2/share_group_type_specs.py
Normal file
133
manila/api/v2/share_group_type_specs.py
Normal file
@ -0,0 +1,133 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import six
|
||||
import webob
|
||||
|
||||
from manila.api import common
|
||||
from manila.api.openstack import wsgi
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share_group import share_group_types
|
||||
|
||||
|
||||
class ShareGroupTypeSpecsController(wsgi.Controller):
|
||||
"""The share group type specs API controller for the OpenStack API."""
|
||||
|
||||
resource_name = 'share_group_types_spec'
|
||||
|
||||
def _get_group_specs(self, context, type_id):
|
||||
specs = db.share_group_type_specs_get(context, type_id)
|
||||
return {"group_specs": copy.deepcopy(specs)}
|
||||
|
||||
def _assert_share_group_type_exists(self, context, type_id):
|
||||
try:
|
||||
share_group_types.get(context, type_id)
|
||||
except exception.NotFound as ex:
|
||||
raise webob.exc.HTTPNotFound(explanation=ex.msg)
|
||||
|
||||
def _verify_group_specs(self, group_specs):
|
||||
|
||||
def is_valid_string(v):
|
||||
return isinstance(v, six.string_types) and len(v) in range(1, 256)
|
||||
|
||||
def is_valid_spec(k, v):
|
||||
valid_spec_key = is_valid_string(k)
|
||||
valid_type = is_valid_string(v) or isinstance(v, bool)
|
||||
return valid_spec_key and valid_type
|
||||
|
||||
for k, v in group_specs.items():
|
||||
if is_valid_string(k) and isinstance(v, dict):
|
||||
self._verify_group_specs(v)
|
||||
elif not is_valid_spec(k, v):
|
||||
expl = _('Invalid extra_spec: %(key)s: %(value)s') % {
|
||||
'key': k, 'value': v
|
||||
}
|
||||
raise webob.exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def index(self, req, id):
|
||||
"""Returns the list of group specs for a given share group type."""
|
||||
|
||||
context = req.environ['manila.context']
|
||||
self._assert_share_group_type_exists(context, id)
|
||||
return self._get_group_specs(context, id)
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def create(self, req, id, body=None):
|
||||
context = req.environ['manila.context']
|
||||
if not self.is_valid_body(body, 'group_specs'):
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
|
||||
self._assert_share_group_type_exists(context, id)
|
||||
specs = body['group_specs']
|
||||
self._verify_group_specs(specs)
|
||||
self._check_key_names(specs.keys())
|
||||
db.share_group_type_specs_update_or_create(context, id, specs)
|
||||
return body
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def update(self, req, id, key, body=None):
|
||||
context = req.environ['manila.context']
|
||||
if not body:
|
||||
expl = _('Request body empty.')
|
||||
raise webob.exc.HTTPBadRequest(explanation=expl)
|
||||
self._assert_share_group_type_exists(context, id)
|
||||
if key not in body:
|
||||
expl = _('Request body and URI mismatch.')
|
||||
raise webob.exc.HTTPBadRequest(explanation=expl)
|
||||
if len(body) > 1:
|
||||
expl = _('Request body contains too many items.')
|
||||
raise webob.exc.HTTPBadRequest(explanation=expl)
|
||||
self._verify_group_specs(body)
|
||||
db.share_group_type_specs_update_or_create(context, id, body)
|
||||
return body
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def show(self, req, id, key):
|
||||
"""Return a single group spec item."""
|
||||
context = req.environ['manila.context']
|
||||
self._assert_share_group_type_exists(context, id)
|
||||
specs = self._get_group_specs(context, id)
|
||||
if key in specs['group_specs']:
|
||||
return {key: specs['group_specs'][key]}
|
||||
else:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def delete(self, req, id, key):
|
||||
"""Deletes an existing group spec."""
|
||||
context = req.environ['manila.context']
|
||||
self._assert_share_group_type_exists(context, id)
|
||||
try:
|
||||
db.share_group_type_specs_delete(context, id, key)
|
||||
except exception.ShareGroupTypeSpecsNotFound as error:
|
||||
raise webob.exc.HTTPNotFound(explanation=error.msg)
|
||||
return webob.Response(status_int=204)
|
||||
|
||||
def _check_key_names(self, keys):
|
||||
if not common.validate_key_names(keys):
|
||||
expl = _('Key names can only contain alphanumeric characters, '
|
||||
'underscores, periods, colons and hyphens.')
|
||||
|
||||
raise webob.exc.HTTPBadRequest(explanation=expl)
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(ShareGroupTypeSpecsController())
|
227
manila/api/v2/share_group_types.py
Normal file
227
manila/api/v2/share_group_types.py
Normal file
@ -0,0 +1,227 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""The group type API controller module."""
|
||||
|
||||
from oslo_utils import strutils
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
import webob
|
||||
from webob import exc
|
||||
|
||||
from manila.api.openstack import wsgi
|
||||
from manila.api.views import share_group_types as views
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share_group import share_group_types
|
||||
|
||||
|
||||
class ShareGroupTypesController(wsgi.Controller):
|
||||
"""The share group types API controller for the OpenStack API."""
|
||||
|
||||
resource_name = 'share_group_type'
|
||||
_view_builder_class = views.ShareGroupTypeViewBuilder
|
||||
|
||||
def _check_body(self, body, action_name):
|
||||
if not self.is_valid_body(body, action_name):
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
access = body[action_name]
|
||||
project = access.get('project')
|
||||
if not uuidutils.is_uuid_like(project):
|
||||
msg = _("Project value (%s) must be in uuid format.") % project
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def index(self, req):
|
||||
"""Returns the list of share group types."""
|
||||
limited_types = self._get_share_group_types(req)
|
||||
return self._view_builder.index(req, limited_types)
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def show(self, req, id):
|
||||
"""Return a single share group type item."""
|
||||
context = req.environ['manila.context']
|
||||
try:
|
||||
share_group_type = share_group_types.get(context, id)
|
||||
except exception.NotFound:
|
||||
msg = _("Share group type with id %s not found.")
|
||||
raise exc.HTTPNotFound(explanation=msg % id)
|
||||
|
||||
share_group_type['id'] = six.text_type(share_group_type['id'])
|
||||
return self._view_builder.show(req, share_group_type)
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def default(self, req):
|
||||
"""Return default share group type."""
|
||||
context = req.environ['manila.context']
|
||||
share_group_type = share_group_types.get_default(context)
|
||||
if not share_group_type:
|
||||
msg = _("Default share group type not found.")
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
share_group_type['id'] = six.text_type(share_group_type['id'])
|
||||
return self._view_builder.show(req, share_group_type)
|
||||
|
||||
def _get_share_group_types(self, req):
|
||||
"""Helper function that returns a list of share group type dicts."""
|
||||
filters = {}
|
||||
context = req.environ['manila.context']
|
||||
if context.is_admin:
|
||||
# Only admin has query access to all group types
|
||||
filters['is_public'] = self._parse_is_public(
|
||||
req.params.get('is_public'))
|
||||
else:
|
||||
filters['is_public'] = True
|
||||
limited_types = share_group_types.get_all(
|
||||
context, search_opts=filters).values()
|
||||
return list(limited_types)
|
||||
|
||||
@staticmethod
|
||||
def _parse_is_public(is_public):
|
||||
"""Parse is_public into something usable.
|
||||
|
||||
:returns:
|
||||
- True: API should list public share group types only
|
||||
- False: API should list private share group types only
|
||||
- None: API should list both public and private share group types
|
||||
"""
|
||||
if is_public is None:
|
||||
# preserve default value of showing only public types
|
||||
return True
|
||||
elif six.text_type(is_public).lower() == "all":
|
||||
return None
|
||||
else:
|
||||
try:
|
||||
return strutils.bool_from_string(is_public, strict=True)
|
||||
except ValueError:
|
||||
msg = _('Invalid is_public filter [%s]') % is_public
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.action("create")
|
||||
@wsgi.Controller.authorize('create')
|
||||
def _create(self, req, body):
|
||||
"""Creates a new share group type."""
|
||||
context = req.environ['manila.context']
|
||||
if not self.is_valid_body(body, 'share_group_type'):
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
|
||||
share_group_type = body['share_group_type']
|
||||
name = share_group_type.get('name')
|
||||
specs = share_group_type.get('group_specs', {})
|
||||
is_public = share_group_type.get('is_public', True)
|
||||
|
||||
if not share_group_type.get('share_types'):
|
||||
msg = _("Supported share types must be provided.")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
share_types = share_group_type.get('share_types')
|
||||
|
||||
if name is None or name == "" or len(name) > 255:
|
||||
msg = _("Share group type name is not valid.")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
try:
|
||||
share_group_types.create(
|
||||
context, name, share_types, specs, is_public)
|
||||
share_group_type = share_group_types.get_by_name(
|
||||
context, name)
|
||||
except exception.ShareGroupTypeExists as err:
|
||||
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
|
||||
except exception.NotFound:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
return self._view_builder.show(req, share_group_type)
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.action("delete")
|
||||
@wsgi.Controller.authorize('delete')
|
||||
def _delete(self, req, id):
|
||||
"""Deletes an existing group type."""
|
||||
context = req.environ['manila.context']
|
||||
try:
|
||||
share_group_type = share_group_types.get(context, id)
|
||||
share_group_types.destroy(context, share_group_type['id'])
|
||||
except exception.ShareGroupTypeInUse:
|
||||
msg = _('Target share group type with id %s is still in use.')
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg % id)
|
||||
except exception.NotFound:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
return webob.Response(status_int=204)
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize('list_project_access')
|
||||
def share_group_type_access(self, req, id):
|
||||
context = req.environ['manila.context']
|
||||
try:
|
||||
share_group_type = share_group_types.get(
|
||||
context, id, expected_fields=['projects'])
|
||||
except exception.ShareGroupTypeNotFound:
|
||||
explanation = _("Share group type %s not found.") % id
|
||||
raise webob.exc.HTTPNotFound(explanation=explanation)
|
||||
|
||||
if share_group_type['is_public']:
|
||||
expl = _("Access list not available for public share group types.")
|
||||
raise webob.exc.HTTPNotFound(explanation=expl)
|
||||
|
||||
projects = []
|
||||
for project_id in share_group_type['projects']:
|
||||
projects.append(
|
||||
{'share_group_type_id': share_group_type['id'],
|
||||
'project_id': project_id}
|
||||
)
|
||||
return {'share_group_type_access': projects}
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.action('addProjectAccess')
|
||||
@wsgi.Controller.authorize('add_project_access')
|
||||
def _add_project_access(self, req, id, body):
|
||||
context = req.environ['manila.context']
|
||||
self._check_body(body, 'addProjectAccess')
|
||||
project = body['addProjectAccess']['project']
|
||||
self._assert_non_public_share_group_type(context, id)
|
||||
try:
|
||||
share_group_types.add_share_group_type_access(
|
||||
context, id, project)
|
||||
except exception.ShareGroupTypeAccessExists as err:
|
||||
raise webob.exc.HTTPConflict(explanation=six.text_type(err))
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.action('removeProjectAccess')
|
||||
@wsgi.Controller.authorize('remove_project_access')
|
||||
def _remove_project_access(self, req, id, body):
|
||||
context = req.environ['manila.context']
|
||||
self._check_body(body, 'removeProjectAccess')
|
||||
project = body['removeProjectAccess']['project']
|
||||
self._assert_non_public_share_group_type(context, id)
|
||||
try:
|
||||
share_group_types.remove_share_group_type_access(
|
||||
context, id, project)
|
||||
except exception.ShareGroupTypeAccessNotFound as err:
|
||||
raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
def _assert_non_public_share_group_type(self, context, type_id):
|
||||
try:
|
||||
share_group_type = share_group_types.get(
|
||||
context, type_id)
|
||||
if share_group_type['is_public']:
|
||||
msg = _("Type access modification is not applicable to "
|
||||
"public share group type.")
|
||||
raise webob.exc.HTTPConflict(explanation=msg)
|
||||
except exception.ShareGroupTypeNotFound as err:
|
||||
raise webob.exc.HTTPNotFound(explanation=six.text_type(err))
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(ShareGroupTypesController())
|
@ -13,8 +13,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""The consistency groups API."""
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
@ -23,155 +21,156 @@ from webob import exc
|
||||
|
||||
from manila.api import common
|
||||
from manila.api.openstack import wsgi
|
||||
import manila.api.views.share_groups as share_group_views
|
||||
from manila.api.views import share_groups as share_group_views
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila.i18n import _, _LI
|
||||
from manila.share import share_types
|
||||
import manila.share_group.api as sg_api
|
||||
from manila.share_group import api as share_group_api
|
||||
from manila.share_group import share_group_types
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class CGController(wsgi.Controller, wsgi.AdminActionsMixin):
|
||||
"""The Consistency Groups API controller for the OpenStack API."""
|
||||
class ShareGroupController(wsgi.Controller, wsgi.AdminActionsMixin):
|
||||
"""The Share Groups API controller for the OpenStack API."""
|
||||
|
||||
resource_name = 'consistency_group'
|
||||
_view_builder_class = share_group_views.CGViewBuilder
|
||||
resource_name = 'consistency_group'
|
||||
resource_name = 'share_group'
|
||||
_view_builder_class = share_group_views.ShareGroupViewBuilder
|
||||
|
||||
def __init__(self):
|
||||
super(CGController, self).__init__()
|
||||
self.cg_api = sg_api.API()
|
||||
super(ShareGroupController, self).__init__()
|
||||
self.share_group_api = share_group_api.API()
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def _get_share_group(self, context, share_group_id):
|
||||
try:
|
||||
return self.share_group_api.get(context, share_group_id)
|
||||
except exception.NotFound:
|
||||
msg = _("Share group %s not found.") % share_group_id
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize('get')
|
||||
def show(self, req, id):
|
||||
"""Return data about the given CG."""
|
||||
"""Return data about the given share group."""
|
||||
context = req.environ['manila.context']
|
||||
share_group = self._get_share_group(context, id)
|
||||
return self._view_builder.detail(req, share_group)
|
||||
|
||||
try:
|
||||
cg = self.cg_api.get(context, id)
|
||||
except exception.NotFound:
|
||||
msg = _("Consistency group %s not found.") % id
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
return self._view_builder.detail(req, cg)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def delete(self, req, id):
|
||||
"""Delete a CG."""
|
||||
"""Delete a share group."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
LOG.info(_LI("Delete consistency group with id: %s"), id,
|
||||
context=context)
|
||||
|
||||
LOG.info(_LI("Delete share group with id: %s"), id, context=context)
|
||||
share_group = self._get_share_group(context, id)
|
||||
try:
|
||||
cg = self.cg_api.get(context, id)
|
||||
except exception.NotFound:
|
||||
msg = _("Consistency group %s not found.") % id
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
try:
|
||||
self.cg_api.delete(context, cg)
|
||||
except exception.InvalidConsistencyGroup as e:
|
||||
self.share_group_api.delete(context, share_group)
|
||||
except exception.InvalidShareGroup as e:
|
||||
raise exc.HTTPConflict(explanation=six.text_type(e))
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize('get_all')
|
||||
def index(self, req):
|
||||
"""Returns a summary list of shares."""
|
||||
return self._get_cgs(req, is_detail=False)
|
||||
"""Returns a summary list of share groups."""
|
||||
return self._get_share_groups(req, is_detail=False)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize('get_all')
|
||||
def detail(self, req):
|
||||
"""Returns a detailed list of shares."""
|
||||
return self._get_cgs(req, is_detail=True)
|
||||
"""Returns a detailed list of share groups."""
|
||||
return self._get_share_groups(req, is_detail=True)
|
||||
|
||||
def _get_cgs(self, req, is_detail):
|
||||
"""Returns a list of shares, transformed through view builder."""
|
||||
def _get_share_groups(self, req, is_detail):
|
||||
"""Returns a list of share groups, transformed through view builder."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
search_opts = {}
|
||||
search_opts.update(req.GET)
|
||||
|
||||
# Remove keys that are not related to cg attrs
|
||||
# Remove keys that are not related to share group attrs
|
||||
search_opts.pop('limit', None)
|
||||
search_opts.pop('offset', None)
|
||||
sort_key = search_opts.pop('sort_key', 'created_at')
|
||||
sort_dir = search_opts.pop('sort_dir', 'desc')
|
||||
if 'group_type_id' in search_opts:
|
||||
search_opts['share_group_type_id'] = search_opts.pop(
|
||||
'group_type_id')
|
||||
|
||||
cgs = self.cg_api.get_all(
|
||||
context, detailed=is_detail, search_opts=search_opts)
|
||||
share_groups = self.share_group_api.get_all(
|
||||
context, detailed=is_detail, search_opts=search_opts,
|
||||
sort_dir=sort_dir, sort_key=sort_key,
|
||||
)
|
||||
|
||||
limited_list = common.limited(cgs, req)
|
||||
limited_list = common.limited(share_groups, req)
|
||||
|
||||
if is_detail:
|
||||
cgs = self._view_builder.detail_list(req, limited_list)
|
||||
share_groups = self._view_builder.detail_list(req, limited_list)
|
||||
else:
|
||||
cgs = self._view_builder.summary_list(req, limited_list)
|
||||
return cgs
|
||||
share_groups = self._view_builder.summary_list(req, limited_list)
|
||||
return share_groups
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.Controller.authorize
|
||||
def update(self, req, id, body):
|
||||
"""Update a share."""
|
||||
"""Update a share group."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
if not self.is_valid_body(body, 'consistency_group'):
|
||||
msg = _("'consistency_group' is missing from the request body.")
|
||||
if not self.is_valid_body(body, 'share_group'):
|
||||
msg = _("'share_group' is missing from the request body.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
cg_data = body['consistency_group']
|
||||
valid_update_keys = {
|
||||
'name',
|
||||
'description',
|
||||
}
|
||||
invalid_fields = set(cg_data.keys()) - valid_update_keys
|
||||
share_group_data = body['share_group']
|
||||
valid_update_keys = {'name', 'description'}
|
||||
invalid_fields = set(share_group_data.keys()) - valid_update_keys
|
||||
if invalid_fields:
|
||||
msg = _("The fields %s are invalid or not allowed to be updated.")
|
||||
raise exc.HTTPBadRequest(explanation=msg % invalid_fields)
|
||||
|
||||
try:
|
||||
cg = self.cg_api.get(context, id)
|
||||
except exception.NotFound:
|
||||
msg = _("Consistency group %s not found.") % id
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
share_group = self._get_share_group(context, id)
|
||||
share_group = self.share_group_api.update(
|
||||
context, share_group, share_group_data)
|
||||
return self._view_builder.detail(req, share_group)
|
||||
|
||||
cg = self.cg_api.update(context, cg, cg_data)
|
||||
return self._view_builder.detail(req, cg)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.response(202)
|
||||
@wsgi.Controller.authorize
|
||||
def create(self, req, body):
|
||||
"""Creates a new share."""
|
||||
"""Creates a new share group."""
|
||||
context = req.environ['manila.context']
|
||||
|
||||
if not self.is_valid_body(body, 'consistency_group'):
|
||||
msg = _("'consistency_group' is missing from the request body.")
|
||||
if not self.is_valid_body(body, 'share_group'):
|
||||
msg = _("'share_group' is missing from the request body.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
cg = body['consistency_group']
|
||||
|
||||
valid_fields = {'name', 'description', 'share_types',
|
||||
'source_cgsnapshot_id', 'share_network_id'}
|
||||
invalid_fields = set(cg.keys()) - valid_fields
|
||||
share_group = body['share_group']
|
||||
valid_fields = {
|
||||
'name',
|
||||
'description',
|
||||
'share_types',
|
||||
'share_group_type_id',
|
||||
'source_share_group_snapshot_id',
|
||||
'share_network_id',
|
||||
}
|
||||
invalid_fields = set(share_group.keys()) - valid_fields
|
||||
if invalid_fields:
|
||||
msg = _("The fields %s are invalid.") % invalid_fields
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
if 'share_types' in cg and 'source_cgsnapshot_id' in cg:
|
||||
if ('share_types' in share_group and
|
||||
'source_share_group_snapshot_id' in share_group):
|
||||
msg = _("Cannot supply both 'share_types' and "
|
||||
"'source_cgsnapshot_id' attributes.")
|
||||
"'source_share_group_snapshot_id' attributes.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
if not cg.get('share_types') and 'source_cgsnapshot_id' not in cg:
|
||||
if not (share_group.get('share_types') or
|
||||
'source_share_group_snapshot_id' in share_group):
|
||||
default_share_type = share_types.get_default_share_type()
|
||||
if default_share_type:
|
||||
cg['share_types'] = [default_share_type['id']]
|
||||
share_group['share_types'] = [default_share_type['id']]
|
||||
else:
|
||||
msg = _("Must specify at least one share type as a default "
|
||||
"share type has not been configured.")
|
||||
@ -179,76 +178,94 @@ class CGController(wsgi.Controller, wsgi.AdminActionsMixin):
|
||||
|
||||
kwargs = {}
|
||||
|
||||
if 'name' in cg:
|
||||
kwargs['name'] = cg.get('name')
|
||||
if 'description' in cg:
|
||||
kwargs['description'] = cg.get('description')
|
||||
if 'name' in share_group:
|
||||
kwargs['name'] = share_group.get('name')
|
||||
if 'description' in share_group:
|
||||
kwargs['description'] = share_group.get('description')
|
||||
|
||||
_share_types = cg.get('share_types')
|
||||
_share_types = share_group.get('share_types')
|
||||
if _share_types:
|
||||
if not all([uuidutils.is_uuid_like(st) for st in _share_types]):
|
||||
msg = _("The 'share_types' attribute must be a list of uuids")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
kwargs['share_type_ids'] = _share_types
|
||||
|
||||
if 'share_network_id' in cg and 'source_cgsnapshot_id' in cg:
|
||||
if ('share_network_id' in share_group and
|
||||
'source_share_group_snapshot_id' in share_group):
|
||||
msg = _("Cannot supply both 'share_network_id' and "
|
||||
"'source_cgsnapshot_id' attributes as the share network "
|
||||
"is inherited from the source.")
|
||||
"'source_share_group_snapshot_id' attributes as the share "
|
||||
"network is inherited from the source.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
if 'source_cgsnapshot_id' in cg:
|
||||
source_cgsnapshot_id = cg.get('source_cgsnapshot_id')
|
||||
if not uuidutils.is_uuid_like(source_cgsnapshot_id):
|
||||
msg = _("The 'source_cgsnapshot_id' attribute must be a uuid.")
|
||||
if 'source_share_group_snapshot_id' in share_group:
|
||||
source_share_group_snapshot_id = share_group.get(
|
||||
'source_share_group_snapshot_id')
|
||||
if not uuidutils.is_uuid_like(source_share_group_snapshot_id):
|
||||
msg = _("The 'source_share_group_snapshot_id' attribute "
|
||||
"must be a uuid.")
|
||||
raise exc.HTTPBadRequest(explanation=six.text_type(msg))
|
||||
kwargs['source_cgsnapshot_id'] = source_cgsnapshot_id
|
||||
|
||||
elif 'share_network_id' in cg:
|
||||
share_network_id = cg.get('share_network_id')
|
||||
kwargs['source_share_group_snapshot_id'] = (
|
||||
source_share_group_snapshot_id)
|
||||
elif 'share_network_id' in share_group:
|
||||
share_network_id = share_group.get('share_network_id')
|
||||
if not uuidutils.is_uuid_like(share_network_id):
|
||||
msg = _("The 'share_network_id' attribute must be a uuid.")
|
||||
raise exc.HTTPBadRequest(explanation=six.text_type(msg))
|
||||
kwargs['share_network_id'] = share_network_id
|
||||
|
||||
if 'share_group_type_id' in share_group:
|
||||
share_group_type_id = share_group.get('share_group_type_id')
|
||||
if not uuidutils.is_uuid_like(share_group_type_id):
|
||||
msg = _("The 'share_group_type_id' attribute must be a uuid.")
|
||||
raise exc.HTTPBadRequest(explanation=six.text_type(msg))
|
||||
kwargs['share_group_type_id'] = share_group_type_id
|
||||
else: # get default
|
||||
def_share_group_type = share_group_types.get_default()
|
||||
if def_share_group_type:
|
||||
kwargs['share_group_type_id'] = def_share_group_type['id']
|
||||
else:
|
||||
msg = _("Must specify a share group type as a default "
|
||||
"share group type has not been configured.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
try:
|
||||
new_cg = self.cg_api.create(context, **kwargs)
|
||||
except exception.InvalidCGSnapshot as e:
|
||||
new_share_group = self.share_group_api.create(context, **kwargs)
|
||||
except exception.InvalidShareGroupSnapshot as e:
|
||||
raise exc.HTTPConflict(explanation=six.text_type(e))
|
||||
except (exception.CGSnapshotNotFound, exception.InvalidInput) as e:
|
||||
except (exception.ShareGroupSnapshotNotFound,
|
||||
exception.InvalidInput) as e:
|
||||
raise exc.HTTPBadRequest(explanation=six.text_type(e))
|
||||
|
||||
return self._view_builder.detail(req, dict(new_cg.items()))
|
||||
return self._view_builder.detail(
|
||||
req, {k: v for k, v in new_share_group.items()})
|
||||
|
||||
def _update(self, *args, **kwargs):
|
||||
db.consistency_group_update(*args, **kwargs)
|
||||
db.share_group_update(*args, **kwargs)
|
||||
|
||||
def _get(self, *args, **kwargs):
|
||||
return self.cg_api.get(*args, **kwargs)
|
||||
return self.share_group_api.get(*args, **kwargs)
|
||||
|
||||
def _delete(self, context, resource, force=True):
|
||||
db.consistency_group_destroy(context.elevated(), resource['id'])
|
||||
# Delete all share group snapshots
|
||||
for snap in resource['snapshots']:
|
||||
db.share_group_snapshot_destroy(context, snap['id'])
|
||||
|
||||
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
|
||||
@wsgi.action('os-reset_status')
|
||||
def cg_reset_status_legacy(self, req, id, body):
|
||||
return self._reset_status(req, id, body)
|
||||
# Delete all shares in share group
|
||||
for share in db.get_all_shares_by_share_group(context, resource['id']):
|
||||
db.share_delete(context, share['id'])
|
||||
|
||||
@wsgi.Controller.api_version('2.7', experimental=True)
|
||||
db.share_group_destroy(context.elevated(), resource['id'])
|
||||
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.action('reset_status')
|
||||
def cg_reset_status(self, req, id, body):
|
||||
def share_group_reset_status(self, req, id, body):
|
||||
return self._reset_status(req, id, body)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', '2.6', experimental=True)
|
||||
@wsgi.action('os-force_delete')
|
||||
def cg_force_delete_legacy(self, req, id, body):
|
||||
return self._force_delete(req, id, body)
|
||||
|
||||
@wsgi.Controller.api_version('2.7', experimental=True)
|
||||
@wsgi.Controller.api_version('2.31', experimental=True)
|
||||
@wsgi.action('force_delete')
|
||||
def cg_force_delete(self, req, id, body):
|
||||
def share_group_force_delete(self, req, id, body):
|
||||
return self._force_delete(req, id, body)
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(CGController())
|
||||
return wsgi.Resource(ShareGroupController())
|
||||
|
@ -79,12 +79,12 @@ class ShareNetworkController(wsgi.Controller):
|
||||
LOG.error(msg)
|
||||
raise exc.HTTPConflict(explanation=msg)
|
||||
|
||||
# NOTE(ameade): Do not allow deletion of share network used by CG
|
||||
cg_count = db_api.count_consistency_groups_in_share_network(context,
|
||||
id)
|
||||
if cg_count:
|
||||
# NOTE(ameade): Do not allow deletion of share network used by share
|
||||
# group
|
||||
sg_count = db_api.count_share_groups_in_share_network(context, id)
|
||||
if sg_count:
|
||||
msg = _("Can not delete share network %(id)s, it has %(len)s "
|
||||
"consistency group(s).") % {'id': id, 'len': cg_count}
|
||||
"share group(s).") % {'id': id, 'len': sg_count}
|
||||
LOG.error(msg)
|
||||
raise exc.HTTPConflict(explanation=msg)
|
||||
|
||||
|
@ -163,22 +163,22 @@ class ShareController(shares.ShareMixin,
|
||||
|
||||
return data
|
||||
|
||||
@wsgi.Controller.api_version("2.0", "2.3")
|
||||
@wsgi.Controller.api_version("2.31")
|
||||
def create(self, req, body):
|
||||
# Remove consistency group attributes
|
||||
body.get('share', {}).pop('consistency_group_id', None)
|
||||
share = self._create(req, body)
|
||||
return share
|
||||
return self._create(
|
||||
req, body, check_create_share_from_snapshot_support=True)
|
||||
|
||||
@wsgi.Controller.api_version("2.4", "2.23") # noqa
|
||||
def create(self, req, body): # pylint: disable=E0102
|
||||
return self._create(req, body)
|
||||
|
||||
@wsgi.Controller.api_version("2.24") # noqa
|
||||
@wsgi.Controller.api_version("2.24", "2.30") # noqa
|
||||
def create(self, req, body): # pylint: disable=E0102
|
||||
body.get('share', {}).pop('share_group_id', None)
|
||||
return self._create(req, body,
|
||||
check_create_share_from_snapshot_support=True)
|
||||
|
||||
@wsgi.Controller.api_version("2.0", "2.23") # noqa
|
||||
def create(self, req, body): # pylint: disable=E0102
|
||||
body.get('share', {}).pop('share_group_id', None)
|
||||
return self._create(req, body)
|
||||
|
||||
@wsgi.Controller.api_version('2.0', '2.6')
|
||||
@wsgi.action('os-reset_status')
|
||||
def share_reset_status_legacy(self, req, id, body):
|
||||
|
@ -13,23 +13,21 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""The consistency groups snapshot API."""
|
||||
|
||||
from manila.api import common
|
||||
|
||||
|
||||
class CGSnapshotViewBuilder(common.ViewBuilder):
|
||||
"""Model a cgsnapshot API response as a python dictionary."""
|
||||
class ShareGroupSnapshotViewBuilder(common.ViewBuilder):
|
||||
"""Model a share group snapshot API response as a python dictionary."""
|
||||
|
||||
_collection_name = 'cgsnapshot'
|
||||
_collection_name = "share_group_snapshot"
|
||||
|
||||
def summary_list(self, request, cgs):
|
||||
"""Show a list of cgsnapshots without many details."""
|
||||
return self._list_view(self.summary, request, cgs)
|
||||
def summary_list(self, request, group_snaps):
|
||||
"""Show a list of share_group_snapshots without many details."""
|
||||
return self._list_view(self.summary, request, group_snaps)
|
||||
|
||||
def detail_list(self, request, cgs):
|
||||
"""Detailed view of a list of cgsnapshots."""
|
||||
return self._list_view(self.detail, request, cgs)
|
||||
def detail_list(self, request, group_snaps):
|
||||
"""Detailed view of a list of share_group_snapshots."""
|
||||
return self._list_view(self.detail, request, group_snaps)
|
||||
|
||||
def member_list(self, request, members):
|
||||
members_list = []
|
||||
@ -40,56 +38,72 @@ class CGSnapshotViewBuilder(common.ViewBuilder):
|
||||
'size': member.get('size'),
|
||||
'share_protocol': member.get('share_proto'),
|
||||
'project_id': member.get('project_id'),
|
||||
'share_type_id': member.get('share_type_id'),
|
||||
'cgsnapshot_id': member.get('cgsnapshot_id'),
|
||||
'share_group_snapshot_id': member.get(
|
||||
'share_group_snapshot_id'),
|
||||
'share_id': member.get('share_id'),
|
||||
}
|
||||
members_list.append(member_dict)
|
||||
|
||||
members_links = self._get_collection_links(request,
|
||||
members,
|
||||
'cgsnapshot_id')
|
||||
members_dict = dict(cgsnapshot_members=members_list)
|
||||
members_links = self._get_collection_links(
|
||||
request, members, "share_group_snapshot_id")
|
||||
members_dict = {"share_group_snapshot_members": members_list}
|
||||
|
||||
if members_links:
|
||||
members_dict['cgsnapshot_members_links'] = members_links
|
||||
members_dict["share_group_snapshot_members_links"] = members_links
|
||||
|
||||
return members_dict
|
||||
|
||||
def summary(self, request, cg):
|
||||
"""Generic, non-detailed view of a cgsnapshot."""
|
||||
def summary(self, request, share_group_snap):
|
||||
"""Generic, non-detailed view of a share group snapshot."""
|
||||
return {
|
||||
'cgsnapshot': {
|
||||
'id': cg.get('id'),
|
||||
'name': cg.get('name'),
|
||||
'links': self._get_links(request, cg['id'])
|
||||
'share_group_snapshot': {
|
||||
'id': share_group_snap.get('id'),
|
||||
'name': share_group_snap.get('name'),
|
||||
'links': self._get_links(request, share_group_snap['id']),
|
||||
}
|
||||
}
|
||||
|
||||
def detail(self, request, cg):
|
||||
"""Detailed view of a single cgsnapshot."""
|
||||
cg_dict = {
|
||||
'id': cg.get('id'),
|
||||
'name': cg.get('name'),
|
||||
'created_at': cg.get('created_at'),
|
||||
'status': cg.get('status'),
|
||||
'description': cg.get('description'),
|
||||
'project_id': cg.get('project_id'),
|
||||
'consistency_group_id': cg.get('consistency_group_id'),
|
||||
'links': self._get_links(request, cg['id']),
|
||||
def detail(self, request, share_group_snap):
|
||||
"""Detailed view of a single share group snapshot."""
|
||||
|
||||
members = self._format_member_list(
|
||||
share_group_snap.get('share_group_snapshot_members', []))
|
||||
|
||||
share_group_snap_dict = {
|
||||
'id': share_group_snap.get('id'),
|
||||
'name': share_group_snap.get('name'),
|
||||
'created_at': share_group_snap.get('created_at'),
|
||||
'status': share_group_snap.get('status'),
|
||||
'description': share_group_snap.get('description'),
|
||||
'project_id': share_group_snap.get('project_id'),
|
||||
'share_group_id': share_group_snap.get('share_group_id'),
|
||||
'members': members,
|
||||
'links': self._get_links(request, share_group_snap['id']),
|
||||
}
|
||||
return {'cgsnapshot': cg_dict}
|
||||
return {'share_group_snapshot': share_group_snap_dict}
|
||||
|
||||
def _format_member_list(self, members):
|
||||
members_list = []
|
||||
for member in members:
|
||||
member_dict = {
|
||||
'id': member.get('id'),
|
||||
'size': member.get('size'),
|
||||
'share_id': member.get('share_id'),
|
||||
}
|
||||
members_list.append(member_dict)
|
||||
|
||||
return members_list
|
||||
|
||||
def _list_view(self, func, request, snaps):
|
||||
"""Provide a view for a list of cgsnapshots."""
|
||||
snap_list = [func(request, snap)['cgsnapshot']
|
||||
"""Provide a view for a list of share group snapshots."""
|
||||
snap_list = [func(request, snap)["share_group_snapshot"]
|
||||
for snap in snaps]
|
||||
snaps_links = self._get_collection_links(request,
|
||||
snaps,
|
||||
self._collection_name)
|
||||
snaps_dict = dict(cgsnapshots=snap_list)
|
||||
snaps_dict = {"share_group_snapshots": snap_list}
|
||||
|
||||
if snaps_links:
|
||||
snaps_dict['cgsnapshot_links'] = snaps_links
|
||||
snaps_dict["share_group_snapshot_links"] = snaps_links
|
||||
|
||||
return snaps_dict
|
||||
|
39
manila/api/views/share_group_types.py
Normal file
39
manila/api/views/share_group_types.py
Normal file
@ -0,0 +1,39 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from manila.api import common
|
||||
|
||||
|
||||
class ShareGroupTypeViewBuilder(common.ViewBuilder):
|
||||
_collection_name = 'share_group_types'
|
||||
|
||||
def show(self, request, share_group_type, brief=False):
|
||||
"""Trim away extraneous share group type attributes."""
|
||||
group_specs = share_group_type.get('group_specs', {})
|
||||
trimmed = {
|
||||
'id': share_group_type.get('id'),
|
||||
'name': share_group_type.get('name'),
|
||||
'is_public': share_group_type.get('is_public'),
|
||||
'group_specs': group_specs,
|
||||
'share_types': [
|
||||
st['share_type_id'] for st in share_group_type['share_types']],
|
||||
}
|
||||
self.update_versioned_resource_dict(request, trimmed, share_group_type)
|
||||
return trimmed if brief else {"share_group_type": trimmed}
|
||||
|
||||
def index(self, request, share_group_types):
|
||||
"""Index over trimmed share group types."""
|
||||
share_group_types_list = [
|
||||
self.show(request, share_group_type, True)
|
||||
for share_group_type in share_group_types
|
||||
]
|
||||
return {"share_group_types": share_group_types_list}
|
@ -13,65 +13,67 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""The consistency groups API."""
|
||||
|
||||
from manila.api import common
|
||||
|
||||
|
||||
class CGViewBuilder(common.ViewBuilder):
|
||||
"""Model a consistency group API response as a python dictionary."""
|
||||
class ShareGroupViewBuilder(common.ViewBuilder):
|
||||
"""Model a share group API response as a python dictionary."""
|
||||
|
||||
_collection_name = 'consistency_groups'
|
||||
_collection_name = 'share_groups'
|
||||
|
||||
def summary_list(self, request, cgs):
|
||||
"""Show a list of consistency groups without many details."""
|
||||
return self._list_view(self.summary, request, cgs)
|
||||
def summary_list(self, request, share_groups):
|
||||
"""Show a list of share groups without many details."""
|
||||
return self._list_view(self.summary, request, share_groups)
|
||||
|
||||
def detail_list(self, request, cgs):
|
||||
"""Detailed view of a list of consistency groups."""
|
||||
return self._list_view(self.detail, request, cgs)
|
||||
def detail_list(self, request, share_groups):
|
||||
"""Detailed view of a list of share groups."""
|
||||
return self._list_view(self.detail, request, share_groups)
|
||||
|
||||
def summary(self, request, cg):
|
||||
"""Generic, non-detailed view of a consistency group."""
|
||||
def summary(self, request, share_group):
|
||||
"""Generic, non-detailed view of a share group."""
|
||||
return {
|
||||
'consistency_group': {
|
||||
'id': cg.get('id'),
|
||||
'name': cg.get('name'),
|
||||
'links': self._get_links(request, cg['id'])
|
||||
'share_group': {
|
||||
'id': share_group.get('id'),
|
||||
'name': share_group.get('name'),
|
||||
'links': self._get_links(request, share_group['id'])
|
||||
}
|
||||
}
|
||||
|
||||
def detail(self, request, cg):
|
||||
"""Detailed view of a single consistency group."""
|
||||
def detail(self, request, share_group):
|
||||
"""Detailed view of a single share group."""
|
||||
context = request.environ['manila.context']
|
||||
cg_dict = {
|
||||
'id': cg.get('id'),
|
||||
'name': cg.get('name'),
|
||||
'created_at': cg.get('created_at'),
|
||||
'status': cg.get('status'),
|
||||
'description': cg.get('description'),
|
||||
'project_id': cg.get('project_id'),
|
||||
'host': cg.get('host'),
|
||||
'source_cgsnapshot_id': cg.get('source_cgsnapshot_id'),
|
||||
'share_network_id': cg.get('share_network_id'),
|
||||
'share_types': [st['share_type_id'] for st in cg.get(
|
||||
share_group_dict = {
|
||||
'id': share_group.get('id'),
|
||||
'name': share_group.get('name'),
|
||||
'created_at': share_group.get('created_at'),
|
||||
'status': share_group.get('status'),
|
||||
'description': share_group.get('description'),
|
||||
'project_id': share_group.get('project_id'),
|
||||
'host': share_group.get('host'),
|
||||
'share_group_type_id': share_group.get('share_group_type_id'),
|
||||
'source_share_group_snapshot_id': share_group.get(
|
||||
'source_share_group_snapshot_id'),
|
||||
'share_network_id': share_group.get('share_network_id'),
|
||||
'share_types': [st['share_type_id'] for st in share_group.get(
|
||||
'share_types')],
|
||||
'links': self._get_links(request, cg['id']),
|
||||
'links': self._get_links(request, share_group['id']),
|
||||
}
|
||||
if context.is_admin:
|
||||
cg_dict['share_server_id'] = cg.get('share_server_id')
|
||||
return {'consistency_group': cg_dict}
|
||||
share_group_dict['share_server_id'] = share_group.get(
|
||||
'share_server_id')
|
||||
return {'share_group': share_group_dict}
|
||||
|
||||
def _list_view(self, func, request, shares):
|
||||
"""Provide a view for a list of consistency groups."""
|
||||
cg_list = [func(request, share)['consistency_group']
|
||||
for share in shares]
|
||||
cgs_links = self._get_collection_links(request,
|
||||
shares,
|
||||
self._collection_name)
|
||||
cgs_dict = dict(consistency_groups=cg_list)
|
||||
"""Provide a view for a list of share groups."""
|
||||
share_group_list = [
|
||||
func(request, share)['share_group']
|
||||
for share in shares
|
||||
]
|
||||
share_groups_links = self._get_collection_links(
|
||||
request, shares, self._collection_name)
|
||||
share_groups_dict = {"share_groups": share_group_list}
|
||||
|
||||
if cgs_links:
|
||||
cgs_dict['consistency_groups_links'] = cgs_links
|
||||
if share_groups_links:
|
||||
share_groups_dict['share_groups_links'] = share_groups_links
|
||||
|
||||
return cgs_dict
|
||||
return share_groups_dict
|
||||
|
@ -23,7 +23,6 @@ class ViewBuilder(common.ViewBuilder):
|
||||
_collection_name = 'shares'
|
||||
_detail_version_modifiers = [
|
||||
"add_snapshot_support_field",
|
||||
"add_consistency_group_fields",
|
||||
"add_task_state_field",
|
||||
"modify_share_type_field",
|
||||
"remove_export_locations",
|
||||
@ -33,6 +32,7 @@ class ViewBuilder(common.ViewBuilder):
|
||||
"add_create_share_from_snapshot_support_field",
|
||||
"add_revert_to_snapshot_support_field",
|
||||
"translate_access_rules_status",
|
||||
"add_share_group_fields",
|
||||
]
|
||||
|
||||
def summary_list(self, request, shares):
|
||||
@ -103,13 +103,6 @@ class ViewBuilder(common.ViewBuilder):
|
||||
def add_snapshot_support_field(self, context, share_dict, share):
|
||||
share_dict['snapshot_support'] = share.get('snapshot_support')
|
||||
|
||||
@common.ViewBuilder.versioned_method("2.4")
|
||||
def add_consistency_group_fields(self, context, share_dict, share):
|
||||
share_dict['consistency_group_id'] = share.get(
|
||||
'consistency_group_id')
|
||||
share_dict['source_cgsnapshot_member_id'] = share.get(
|
||||
'source_cgsnapshot_member_id')
|
||||
|
||||
@common.ViewBuilder.versioned_method("2.5")
|
||||
def add_task_state_field(self, context, share_dict, share):
|
||||
share_dict['task_state'] = share.get('task_state')
|
||||
@ -162,6 +155,13 @@ class ViewBuilder(common.ViewBuilder):
|
||||
constants.SHARE_INSTANCE_RULES_SYNCING):
|
||||
share_dict['access_rules_status'] = constants.STATUS_OUT_OF_SYNC
|
||||
|
||||
@common.ViewBuilder.versioned_method("2.31")
|
||||
def add_share_group_fields(self, context, share_dict, share):
|
||||
share_dict['share_group_id'] = share.get(
|
||||
'share_group_id')
|
||||
share_dict['source_share_group_snapshot_member_id'] = share.get(
|
||||
'source_share_group_snapshot_member_id')
|
||||
|
||||
def _list_view(self, func, request, shares):
|
||||
"""Provide a view for a list of shares."""
|
||||
shares_list = [func(request, share)['share'] for share in shares]
|
||||
|
@ -118,6 +118,8 @@ global_opts = [
|
||||
help='Availability zone of this node.'),
|
||||
cfg.StrOpt('default_share_type',
|
||||
help='Default share type to use.'),
|
||||
cfg.StrOpt('default_share_group_type',
|
||||
help='Default share group type to use.'),
|
||||
cfg.ListOpt('memcached_servers',
|
||||
help='Memcached servers or None for in process cache.'),
|
||||
cfg.StrOpt('share_usage_audit_period',
|
||||
|
266
manila/db/api.py
266
manila/db/api.py
@ -322,9 +322,10 @@ def share_instances_get_all_by_share(context, share_id):
|
||||
return IMPL.share_instances_get_all_by_share(context, share_id)
|
||||
|
||||
|
||||
def share_instances_get_all_by_consistency_group_id(context, cg_id):
|
||||
"""Returns list of share instances that belong to given cg."""
|
||||
return IMPL.share_instances_get_all_by_consistency_group_id(context, cg_id)
|
||||
def share_instances_get_all_by_share_group_id(context, share_group_id):
|
||||
"""Returns list of share instances that belong to given share group."""
|
||||
return IMPL.share_instances_get_all_by_share_group_id(
|
||||
context, share_group_id)
|
||||
|
||||
###################
|
||||
|
||||
@ -361,12 +362,12 @@ def share_get_all_by_project(context, project_id, filters=None,
|
||||
)
|
||||
|
||||
|
||||
def share_get_all_by_consistency_group_id(context, cg_id,
|
||||
def share_get_all_by_share_group_id(context, share_group_id,
|
||||
filters=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
"""Returns all shares with given project ID and CG id."""
|
||||
return IMPL.share_get_all_by_consistency_group_id(
|
||||
context, cg_id, filters=filters,
|
||||
"""Returns all shares with given project ID and share group id."""
|
||||
return IMPL.share_get_all_by_share_group_id(
|
||||
context, share_group_id, filters=filters,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
|
||||
|
||||
@ -894,120 +895,148 @@ def availability_zone_get_all(context):
|
||||
|
||||
####################
|
||||
|
||||
def consistency_group_get(context, consistency_group_id):
|
||||
"""Get a consistency group or raise if it does not exist."""
|
||||
return IMPL.consistency_group_get(context, consistency_group_id)
|
||||
def share_group_get(context, share_group_id):
|
||||
"""Get a share group or raise if it does not exist."""
|
||||
return IMPL.share_group_get(context, share_group_id)
|
||||
|
||||
|
||||
def consistency_group_get_all(context, detailed=True):
|
||||
"""Get all consistency groups."""
|
||||
return IMPL.consistency_group_get_all(context, detailed=detailed)
|
||||
def share_group_get_all(context, detailed=True, filters=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
"""Get all share groups."""
|
||||
return IMPL.share_group_get_all(
|
||||
context, detailed=detailed, filters=filters, sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
|
||||
def consistency_group_create(context, values):
|
||||
"""Create a consistency group from the values dictionary."""
|
||||
return IMPL.consistency_group_create(context, values)
|
||||
def share_group_get_all_by_host(context, host, detailed=True, filters=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Get all share groups belonging to a host."""
|
||||
return IMPL.share_group_get_all_by_host(
|
||||
context, host, detailed=detailed, filters=filters, sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
|
||||
def consistency_group_get_all_by_share_server(context, share_server_id):
|
||||
"""Get all consistency groups associated with a share server."""
|
||||
return IMPL.consistency_group_get_all_by_share_server(context,
|
||||
share_server_id)
|
||||
def share_group_create(context, values):
|
||||
"""Create a share group from the values dictionary."""
|
||||
return IMPL.share_group_create(context, values)
|
||||
|
||||
|
||||
def consistency_group_get_all_by_project(context, project_id, detailed=True):
|
||||
"""Get all consistency groups belonging to a project."""
|
||||
return IMPL.consistency_group_get_all_by_project(context, project_id,
|
||||
detailed=detailed)
|
||||
def share_group_get_all_by_share_server(context, share_server_id,
|
||||
filters=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
"""Get all share groups associated with a share server."""
|
||||
return IMPL.share_group_get_all_by_share_server(
|
||||
context, share_server_id, filters=filters, sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
|
||||
def consistency_group_update(context, consistency_group_id, values):
|
||||
"""Set the given properties on a consistency group and update it.
|
||||
def share_group_get_all_by_project(context, project_id, detailed=True,
|
||||
filters=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
"""Get all share groups belonging to a project."""
|
||||
return IMPL.share_group_get_all_by_project(
|
||||
context, project_id, detailed=detailed, filters=filters,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
|
||||
Raises NotFound if consistency group does not exist.
|
||||
|
||||
def share_group_update(context, share_group_id, values):
|
||||
"""Set the given properties on a share group and update it.
|
||||
|
||||
Raises NotFound if share group does not exist.
|
||||
"""
|
||||
return IMPL.consistency_group_update(context, consistency_group_id, values)
|
||||
return IMPL.share_group_update(context, share_group_id, values)
|
||||
|
||||
|
||||
def consistency_group_destroy(context, consistency_group_id):
|
||||
"""Destroy the consistency group or raise if it does not exist."""
|
||||
return IMPL.consistency_group_destroy(context, consistency_group_id)
|
||||
def share_group_destroy(context, share_group_id):
|
||||
"""Destroy the share group or raise if it does not exist."""
|
||||
return IMPL.share_group_destroy(context, share_group_id)
|
||||
|
||||
|
||||
def count_shares_in_consistency_group(context, consistency_group_id):
|
||||
"""Returns the number of undeleted shares with the specified cg."""
|
||||
return IMPL.count_shares_in_consistency_group(context,
|
||||
consistency_group_id)
|
||||
def count_shares_in_share_group(context, share_group_id):
|
||||
"""Returns the number of undeleted shares with the specified group."""
|
||||
return IMPL.count_shares_in_share_group(context, share_group_id)
|
||||
|
||||
|
||||
def count_cgsnapshots_in_consistency_group(context, consistency_group_id):
|
||||
"""Returns the number of undeleted cgsnapshots with the specified cg."""
|
||||
return IMPL.count_cgsnapshots_in_consistency_group(context,
|
||||
consistency_group_id)
|
||||
def get_all_shares_by_share_group(context, share_group_id):
|
||||
return IMPL.get_all_shares_by_share_group(context, share_group_id)
|
||||
|
||||
|
||||
def count_consistency_groups_in_share_network(context, share_network_id,
|
||||
def count_share_group_snapshots_in_share_group(context, share_group_id):
|
||||
"""Returns the number of sg snapshots with the specified share group."""
|
||||
return IMPL.count_share_group_snapshots_in_share_group(
|
||||
context, share_group_id)
|
||||
|
||||
|
||||
def count_share_groups_in_share_network(context, share_network_id,
|
||||
session=None):
|
||||
"""Returns the number of undeleted cgs with the specified share network."""
|
||||
return IMPL.count_consistency_groups_in_share_network(context,
|
||||
share_network_id)
|
||||
"""Return the number of groups with the specified share network."""
|
||||
return IMPL.count_share_groups_in_share_network(context, share_network_id)
|
||||
|
||||
|
||||
def count_cgsnapshot_members_in_share(context, share_id, session=None):
|
||||
"""Returns the number of cgsnapshot members linked to the share."""
|
||||
return IMPL.count_cgsnapshot_members_in_share(context, share_id)
|
||||
def count_share_group_snapshot_members_in_share(context, share_id,
|
||||
session=None):
|
||||
"""Returns the number of group snapshot members linked to the share."""
|
||||
return IMPL.count_share_group_snapshot_members_in_share(context, share_id)
|
||||
|
||||
|
||||
def cgsnapshot_get(context, cgsnapshot_id):
|
||||
"""Get a cgsnapshot."""
|
||||
return IMPL.cgsnapshot_get(context, cgsnapshot_id)
|
||||
def share_group_snapshot_get(context, share_group_snapshot_id):
|
||||
"""Get a share group snapshot."""
|
||||
return IMPL.share_group_snapshot_get(context, share_group_snapshot_id)
|
||||
|
||||
|
||||
def cgsnapshot_get_all(context, detailed=True):
|
||||
"""Get all cgsnapshots."""
|
||||
return IMPL.cgsnapshot_get_all(context, detailed=detailed)
|
||||
def share_group_snapshot_get_all(context, detailed=True, filters=None,
|
||||
sort_key=None, sort_dir=None):
|
||||
"""Get all share group snapshots."""
|
||||
return IMPL.share_group_snapshot_get_all(
|
||||
context, detailed=detailed, filters=filters, sort_key=sort_key,
|
||||
sort_dir=sort_dir)
|
||||
|
||||
|
||||
def cgsnapshot_get_all_by_project(context, project_id, detailed=True):
|
||||
"""Get all cgsnapshots belonging to a project."""
|
||||
return IMPL.cgsnapshot_get_all_by_project(context, project_id,
|
||||
detailed=detailed)
|
||||
def share_group_snapshot_get_all_by_project(context, project_id, detailed=True,
|
||||
filters=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
"""Get all share group snapshots belonging to a project."""
|
||||
return IMPL.share_group_snapshot_get_all_by_project(
|
||||
context, project_id, detailed=detailed, filters=filters,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
|
||||
|
||||
def cgsnapshot_create(context, values):
|
||||
"""Create a cgsnapshot from the values dictionary."""
|
||||
return IMPL.cgsnapshot_create(context, values)
|
||||
def share_group_snapshot_create(context, values):
|
||||
"""Create a share group snapshot from the values dictionary."""
|
||||
return IMPL.share_group_snapshot_create(context, values)
|
||||
|
||||
|
||||
def cgsnapshot_update(context, cgsnapshot_id, values):
|
||||
"""Set the given properties on a cgsnapshot and update it.
|
||||
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 cgsnapshot does not exist.
|
||||
Raises NotFound if share group snapshot does not exist.
|
||||
"""
|
||||
return IMPL.cgsnapshot_update(context, cgsnapshot_id, values)
|
||||
return IMPL.share_group_snapshot_update(
|
||||
context, share_group_snapshot_id, values)
|
||||
|
||||
|
||||
def cgsnapshot_destroy(context, cgsnapshot_id):
|
||||
"""Destroy the cgsnapshot or raise if it does not exist."""
|
||||
return IMPL.cgsnapshot_destroy(context, cgsnapshot_id)
|
||||
def share_group_snapshot_destroy(context, share_group_snapshot_id):
|
||||
"""Destroy the share_group_snapshot or raise if it does not exist."""
|
||||
return IMPL.share_group_snapshot_destroy(context, share_group_snapshot_id)
|
||||
|
||||
|
||||
def cgsnapshot_members_get_all(context, cgsnapshot_id):
|
||||
"""Return the members of a cgsnapshot."""
|
||||
return IMPL.cgsnapshot_members_get_all(context, cgsnapshot_id)
|
||||
def share_group_snapshot_members_get_all(context, share_group_snapshot_id):
|
||||
"""Return the members of a share group snapshot."""
|
||||
return IMPL.share_group_snapshot_members_get_all(
|
||||
context, share_group_snapshot_id)
|
||||
|
||||
|
||||
def cgsnapshot_member_create(context, values):
|
||||
"""Create a cgsnapshot member from the values dictionary."""
|
||||
return IMPL.cgsnapshot_member_create(context, values)
|
||||
def share_group_snapshot_member_create(context, values):
|
||||
"""Create a share group snapshot member from the values dictionary."""
|
||||
return IMPL.share_group_snapshot_member_create(context, values)
|
||||
|
||||
|
||||
def cgsnapshot_member_update(context, member_id, values):
|
||||
"""Set the given properties on a cgsnapshot member and update it.
|
||||
def share_group_snapshot_member_update(context, member_id, values):
|
||||
"""Set the given properties on a share group snapshot member and update it.
|
||||
|
||||
Raises NotFound if cgsnapshot member does not exist.
|
||||
Raises NotFound if share_group_snapshot member does not exist.
|
||||
"""
|
||||
return IMPL.cgsnapshot_member_update(context, member_id, values)
|
||||
return IMPL.share_group_snapshot_member_update(context, member_id, values)
|
||||
|
||||
|
||||
####################
|
||||
@ -1063,3 +1092,88 @@ def purge_deleted_records(context, age_in_days):
|
||||
:raises: InvalidParameterValue if age_in_days is incorrect.
|
||||
"""
|
||||
return IMPL.purge_deleted_records(context, age_in_days=age_in_days)
|
||||
|
||||
|
||||
####################
|
||||
|
||||
|
||||
def share_group_type_create(context, values, projects=None):
|
||||
"""Create a new share group type."""
|
||||
return IMPL.share_group_type_create(context, values, projects)
|
||||
|
||||
|
||||
def share_group_type_get_all(context, inactive=False, filters=None):
|
||||
"""Get all share group types.
|
||||
|
||||
:param context: context to query under
|
||||
:param inactive: Include inactive share group types to the result set
|
||||
:param filters: Filters for the query in the form of key/value.
|
||||
:is_public: Filter share group types based on visibility:
|
||||
|
||||
* **True**: List public group types only
|
||||
* **False**: List private group types only
|
||||
* **None**: List both public and private group types
|
||||
|
||||
:returns: list of matching share group types
|
||||
"""
|
||||
return IMPL.share_group_type_get_all(context, inactive, filters)
|
||||
|
||||
|
||||
def share_group_type_get(context, type_id, inactive=False,
|
||||
expected_fields=None):
|
||||
"""Get share_group type by id.
|
||||
|
||||
:param context: context to query under
|
||||
:param type_id: group type id to get.
|
||||
:param inactive: Consider inactive group types when searching
|
||||
:param expected_fields: Return those additional fields.
|
||||
Supported fields are: projects.
|
||||
:returns: share group type
|
||||
"""
|
||||
return IMPL.share_group_type_get(
|
||||
context, type_id, inactive, expected_fields)
|
||||
|
||||
|
||||
def share_group_type_get_by_name(context, name):
|
||||
"""Get share group type by name."""
|
||||
return IMPL.share_group_type_get_by_name(context, name)
|
||||
|
||||
|
||||
def share_group_type_access_get_all(context, type_id):
|
||||
"""Get all share group type access of a share group type."""
|
||||
return IMPL.share_group_type_access_get_all(context, type_id)
|
||||
|
||||
|
||||
def share_group_type_access_add(context, type_id, project_id):
|
||||
"""Add share group type access for project."""
|
||||
return IMPL.share_group_type_access_add(context, type_id, project_id)
|
||||
|
||||
|
||||
def share_group_type_access_remove(context, type_id, project_id):
|
||||
"""Remove share group type access for project."""
|
||||
return IMPL.share_group_type_access_remove(context, type_id, project_id)
|
||||
|
||||
|
||||
def share_group_type_destroy(context, type_id):
|
||||
"""Delete a share group type."""
|
||||
return IMPL.share_group_type_destroy(context, type_id)
|
||||
|
||||
|
||||
def share_group_type_specs_get(context, type_id):
|
||||
"""Get all group specs for a share group type."""
|
||||
return IMPL.share_group_type_specs_get(context, type_id)
|
||||
|
||||
|
||||
def share_group_type_specs_delete(context, type_id, key):
|
||||
"""Delete the given group specs item."""
|
||||
return IMPL.share_group_type_specs_delete(context, type_id, key)
|
||||
|
||||
|
||||
def share_group_type_specs_update_or_create(context, type_id, group_specs):
|
||||
"""Create or update share group type specs.
|
||||
|
||||
This adds or modifies the key/value pairs specified in the group
|
||||
specs dict argument.
|
||||
"""
|
||||
return IMPL.share_group_type_specs_update_or_create(
|
||||
context, type_id, group_specs)
|
||||
|
@ -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)
|
||||
share_proto = Column(String(255))
|
||||
is_public = Column(Boolean, default=False)
|
||||
consistency_group_id = Column(String(36),
|
||||
ForeignKey('consistency_groups.id'),
|
||||
share_group_id = Column(String(36),
|
||||
ForeignKey('share_groups.id'),
|
||||
nullable=True)
|
||||
|
||||
source_cgsnapshot_member_id = Column(String(36), nullable=True)
|
||||
source_share_group_snapshot_member_id = Column(String(36), nullable=True)
|
||||
task_state = Column(String(255))
|
||||
instances = orm.relationship(
|
||||
"ShareInstance",
|
||||
@ -335,8 +335,8 @@ class ShareInstance(BASE, ManilaBase):
|
||||
_proxified_properties = ('user_id', 'project_id', 'size',
|
||||
'display_name', 'display_description',
|
||||
'snapshot_id', 'share_proto', 'is_public',
|
||||
'consistency_group_id', 'replication_type',
|
||||
'source_cgsnapshot_member_id')
|
||||
'share_group_id', 'replication_type',
|
||||
'source_share_group_snapshot_member_id')
|
||||
|
||||
def set_share_data(self, share):
|
||||
for share_property in self._proxified_properties:
|
||||
@ -832,10 +832,10 @@ class ShareServer(BASE, ManilaBase):
|
||||
'ShareServer.id == ShareInstance.share_server_id,'
|
||||
'ShareInstance.deleted == "False")')
|
||||
|
||||
consistency_groups = orm.relationship(
|
||||
"ConsistencyGroup", backref='share_server', primaryjoin='and_('
|
||||
'ShareServer.id == ConsistencyGroup.share_server_id,'
|
||||
'ConsistencyGroup.deleted == "False")')
|
||||
share_groups = orm.relationship(
|
||||
"ShareGroup", backref='share_server', primaryjoin='and_('
|
||||
'ShareServer.id == ShareGroup.share_server_id,'
|
||||
'ShareGroup.deleted == "False")')
|
||||
|
||||
_backend_details = orm.relationship(
|
||||
"ShareServerBackendDetails",
|
||||
@ -913,95 +913,168 @@ class AvailabilityZone(BASE, ManilaBase):
|
||||
name = Column(String(255), nullable=False)
|
||||
|
||||
|
||||
class ConsistencyGroup(BASE, ManilaBase):
|
||||
"""Represents a consistency group."""
|
||||
__tablename__ = 'consistency_groups'
|
||||
class ShareGroupTypes(BASE, ManilaBase):
|
||||
"""Represent possible share group types of shares offered."""
|
||||
__tablename__ = "share_group_types"
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint(
|
||||
"name", "deleted", name="uniq_share_group_type_name"),
|
||||
)
|
||||
id = Column(String(36), primary_key=True)
|
||||
deleted = Column(String(36), default='False')
|
||||
name = Column(String(255))
|
||||
is_public = Column(Boolean, default=True)
|
||||
|
||||
|
||||
class ShareGroup(BASE, ManilaBase):
|
||||
"""Represents a share group."""
|
||||
__tablename__ = 'share_groups'
|
||||
id = Column(String(36), primary_key=True)
|
||||
user_id = Column(String(255), nullable=False)
|
||||
project_id = Column(String(255), nullable=False)
|
||||
deleted = Column(String(36), default='False')
|
||||
|
||||
host = Column(String(255))
|
||||
name = Column(String(255))
|
||||
description = Column(String(255))
|
||||
status = Column(String(255))
|
||||
source_cgsnapshot_id = Column(String(36))
|
||||
share_network_id = Column(String(36), ForeignKey('share_networks.id'),
|
||||
nullable=True)
|
||||
share_server_id = Column(String(36), ForeignKey('share_servers.id'),
|
||||
nullable=True)
|
||||
source_share_group_snapshot_id = Column(String(36))
|
||||
share_network_id = Column(
|
||||
String(36), ForeignKey('share_networks.id'), nullable=True)
|
||||
share_server_id = Column(
|
||||
String(36), ForeignKey('share_servers.id'), nullable=True)
|
||||
share_group_type_id = Column(
|
||||
String(36), ForeignKey('share_group_types.id'), nullable=True)
|
||||
share_group_type = orm.relationship(
|
||||
ShareGroupTypes,
|
||||
backref="share_groups",
|
||||
foreign_keys=share_group_type_id,
|
||||
primaryjoin='and_('
|
||||
'ShareGroup.share_group_type_id == '
|
||||
'ShareGroupTypes.id,'
|
||||
'ShareGroup.deleted == 0)')
|
||||
|
||||
|
||||
class CGSnapshot(BASE, ManilaBase):
|
||||
"""Represents a cgsnapshot."""
|
||||
__tablename__ = 'cgsnapshots'
|
||||
class ShareGroupTypeProjects(BASE, ManilaBase):
|
||||
"""Represent projects associated share group types."""
|
||||
__tablename__ = "share_group_type_projects"
|
||||
__table_args__ = (schema.UniqueConstraint(
|
||||
"share_group_type_id", "project_id", "deleted",
|
||||
name=("uniq_share_group_type_projects0share_group_type_id"
|
||||
"0project_id0deleted")),
|
||||
)
|
||||
id = Column(Integer, primary_key=True)
|
||||
share_group_type_id = Column(
|
||||
String, ForeignKey('share_group_types.id'), nullable=False)
|
||||
project_id = Column(String(255))
|
||||
share_group_type = orm.relationship(
|
||||
ShareGroupTypes,
|
||||
backref="projects",
|
||||
foreign_keys=share_group_type_id,
|
||||
primaryjoin='and_('
|
||||
'ShareGroupTypeProjects.share_group_type_id == '
|
||||
'ShareGroupTypes.id,'
|
||||
'ShareGroupTypeProjects.deleted == 0)')
|
||||
|
||||
|
||||
class ShareGroupTypeSpecs(BASE, ManilaBase):
|
||||
"""Represents additional specs for a share group type."""
|
||||
__tablename__ = 'share_group_type_specs'
|
||||
id = Column(Integer, primary_key=True)
|
||||
key = Column("spec_key", String(255))
|
||||
value = Column("spec_value", String(255))
|
||||
share_group_type_id = Column(
|
||||
String(36), ForeignKey('share_group_types.id'), nullable=False)
|
||||
share_group_type = orm.relationship(
|
||||
ShareGroupTypes,
|
||||
backref="group_specs",
|
||||
foreign_keys=share_group_type_id,
|
||||
primaryjoin='and_('
|
||||
'ShareGroupTypeSpecs.share_group_type_id == ShareGroupTypes.id,'
|
||||
'ShareGroupTypeSpecs.deleted == 0)'
|
||||
)
|
||||
|
||||
|
||||
class ShareGroupSnapshot(BASE, ManilaBase):
|
||||
"""Represents a share group snapshot."""
|
||||
__tablename__ = 'share_group_snapshots'
|
||||
id = Column(String(36), primary_key=True)
|
||||
|
||||
consistency_group_id = Column(String(36),
|
||||
ForeignKey('consistency_groups.id'))
|
||||
share_group_id = Column(String(36), ForeignKey('share_groups.id'))
|
||||
user_id = Column(String(255), nullable=False)
|
||||
project_id = Column(String(255), nullable=False)
|
||||
deleted = Column(String(36), default='False')
|
||||
|
||||
name = Column(String(255))
|
||||
description = Column(String(255))
|
||||
status = Column(String(255))
|
||||
|
||||
consistency_group = orm.relationship(
|
||||
ConsistencyGroup,
|
||||
backref="cgsnapshots",
|
||||
foreign_keys=consistency_group_id,
|
||||
share_group = orm.relationship(
|
||||
ShareGroup,
|
||||
backref="snapshots",
|
||||
foreign_keys=share_group_id,
|
||||
primaryjoin=('and_('
|
||||
'CGSnapshot.consistency_group_id == ConsistencyGroup.id,'
|
||||
'CGSnapshot.deleted == "False")')
|
||||
'ShareGroupSnapshot.share_group_id == ShareGroup.id,'
|
||||
'ShareGroupSnapshot.deleted == "False")')
|
||||
)
|
||||
|
||||
|
||||
class ConsistencyGroupShareTypeMapping(BASE, ManilaBase):
|
||||
"""Represents the share types in a consistency group."""
|
||||
__tablename__ = 'consistency_group_share_type_mappings'
|
||||
class ShareGroupTypeShareTypeMapping(BASE, ManilaBase):
|
||||
"""Represents the share types supported by a share group type."""
|
||||
__tablename__ = 'share_group_type_share_type_mappings'
|
||||
id = Column(String(36), primary_key=True)
|
||||
deleted = Column(String(36), default='False')
|
||||
consistency_group_id = Column(String(36),
|
||||
ForeignKey('consistency_groups.id'),
|
||||
nullable=False)
|
||||
share_type_id = Column(String(36),
|
||||
ForeignKey('share_types.id'),
|
||||
nullable=False)
|
||||
|
||||
consistency_group = orm.relationship(
|
||||
ConsistencyGroup,
|
||||
share_group_type_id = Column(
|
||||
String(36), ForeignKey('share_group_types.id'), nullable=False)
|
||||
share_type_id = Column(
|
||||
String(36), ForeignKey('share_types.id'), nullable=False)
|
||||
share_group_type = orm.relationship(
|
||||
ShareGroupTypes,
|
||||
backref="share_types",
|
||||
foreign_keys=consistency_group_id,
|
||||
foreign_keys=share_group_type_id,
|
||||
primaryjoin=('and_('
|
||||
'ConsistencyGroupShareTypeMapping.consistency_group_id '
|
||||
'== ConsistencyGroup.id,'
|
||||
'ConsistencyGroupShareTypeMapping.deleted == "False")')
|
||||
'ShareGroupTypeShareTypeMapping.share_group_type_id '
|
||||
'== ShareGroupTypes.id,'
|
||||
'ShareGroupTypeShareTypeMapping.deleted == "False")')
|
||||
)
|
||||
|
||||
|
||||
class CGSnapshotMember(BASE, ManilaBase):
|
||||
"""Represents the share snapshots in a consistency group snapshot."""
|
||||
__tablename__ = 'cgsnapshot_members'
|
||||
class ShareGroupShareTypeMapping(BASE, ManilaBase):
|
||||
"""Represents the share types in a share group."""
|
||||
__tablename__ = 'share_group_share_type_mappings'
|
||||
id = Column(String(36), primary_key=True)
|
||||
cgsnapshot_id = Column(String(36), ForeignKey('cgsnapshots.id'))
|
||||
deleted = Column(String(36), default='False')
|
||||
share_group_id = Column(
|
||||
String(36), ForeignKey('share_groups.id'), nullable=False)
|
||||
share_type_id = Column(
|
||||
String(36), ForeignKey('share_types.id'), nullable=False)
|
||||
share_group = orm.relationship(
|
||||
ShareGroup,
|
||||
backref="share_types",
|
||||
foreign_keys=share_group_id,
|
||||
primaryjoin=('and_('
|
||||
'ShareGroupShareTypeMapping.share_group_id '
|
||||
'== ShareGroup.id,'
|
||||
'ShareGroupShareTypeMapping.deleted == "False")')
|
||||
)
|
||||
|
||||
|
||||
class ShareGroupSnapshotMember(BASE, ManilaBase):
|
||||
"""Represents the share snapshots in a share group snapshot."""
|
||||
__tablename__ = 'share_group_snapshot_members'
|
||||
id = Column(String(36), primary_key=True)
|
||||
share_group_snapshot_id = Column(
|
||||
String(36), ForeignKey('share_group_snapshots.id'))
|
||||
share_id = Column(String(36), ForeignKey('shares.id'))
|
||||
share_instance_id = Column(String(36), ForeignKey('share_instances.id'))
|
||||
size = Column(Integer)
|
||||
status = Column(String(255))
|
||||
share_proto = Column(String(255))
|
||||
share_type_id = Column(String(36), ForeignKey('share_types.id'),
|
||||
nullable=True)
|
||||
user_id = Column(String(255))
|
||||
project_id = Column(String(255))
|
||||
deleted = Column(String(36), default='False')
|
||||
|
||||
cgsnapshot = orm.relationship(
|
||||
CGSnapshot,
|
||||
backref="cgsnapshot_members",
|
||||
foreign_keys=cgsnapshot_id,
|
||||
primaryjoin='CGSnapshot.id == CGSnapshotMember.cgsnapshot_id')
|
||||
share_group_snapshot = orm.relationship(
|
||||
ShareGroupSnapshot,
|
||||
backref="share_group_snapshot_members",
|
||||
foreign_keys=share_group_snapshot_id,
|
||||
primaryjoin='ShareGroupSnapshot.id == '
|
||||
'ShareGroupSnapshotMember.share_group_snapshot_id')
|
||||
|
||||
|
||||
def register_models():
|
||||
|
@ -478,6 +478,10 @@ class ShareSnapshotNotSupported(ManilaException):
|
||||
message = _("Share %(share_name)s does not support snapshots.")
|
||||
|
||||
|
||||
class ShareGroupSnapshotNotSupported(ManilaException):
|
||||
message = _("Share group %(share_group)s does not support snapshots.")
|
||||
|
||||
|
||||
class ShareSnapshotIsBusy(ManilaException):
|
||||
message = _("Deleting snapshot %(snapshot_name)s that has "
|
||||
"dependent shares.")
|
||||
@ -531,6 +535,10 @@ class InvalidShareType(Invalid):
|
||||
message = _("Invalid share type: %(reason)s.")
|
||||
|
||||
|
||||
class InvalidShareGroupType(Invalid):
|
||||
message = _("Invalid share group type: %(reason)s.")
|
||||
|
||||
|
||||
class InvalidExtraSpec(Invalid):
|
||||
message = _("Invalid extra_spec: %(reason)s.")
|
||||
|
||||
@ -547,21 +555,40 @@ class ShareTypeNotFound(NotFound):
|
||||
message = _("Share type %(share_type_id)s could not be found.")
|
||||
|
||||
|
||||
class ShareGroupTypeNotFound(NotFound):
|
||||
message = _("Share group type %(type_id)s could not be found.")
|
||||
|
||||
|
||||
class ShareTypeAccessNotFound(NotFound):
|
||||
message = _("Share type access not found for %(share_type_id)s / "
|
||||
"%(project_id)s combination.")
|
||||
|
||||
|
||||
class ShareGroupTypeAccessNotFound(NotFound):
|
||||
message = _("Share group type access not found for %(type_id)s / "
|
||||
"%(project_id)s combination.")
|
||||
|
||||
|
||||
class ShareTypeNotFoundByName(ShareTypeNotFound):
|
||||
message = _("Share type with name %(share_type_name)s "
|
||||
"could not be found.")
|
||||
|
||||
|
||||
class ShareGroupTypeNotFoundByName(ShareTypeNotFound):
|
||||
message = _("Share group type with name %(type_name)s "
|
||||
"could not be found.")
|
||||
|
||||
|
||||
class ShareTypeExtraSpecsNotFound(NotFound):
|
||||
message = _("Share Type %(share_type_id)s has no extra specs with "
|
||||
"key %(extra_specs_key)s.")
|
||||
|
||||
|
||||
class ShareGroupTypeSpecsNotFound(NotFound):
|
||||
message = _("Share group type %(type_id)s has no group specs with "
|
||||
"key %(specs_key)s.")
|
||||
|
||||
|
||||
class ShareTypeInUse(ManilaException):
|
||||
message = _("Share Type %(share_type_id)s deletion is not allowed with "
|
||||
"shares present with the type.")
|
||||
@ -571,20 +598,39 @@ class IPAddressInUse(InUse):
|
||||
message = _("IP address %(ip)s is already used.")
|
||||
|
||||
|
||||
class ShareGroupTypeInUse(ManilaException):
|
||||
message = _("Share group Type %(type_id)s deletion is not allowed "
|
||||
"with groups present with the type.")
|
||||
|
||||
|
||||
class ShareTypeExists(ManilaException):
|
||||
message = _("Share Type %(id)s already exists.")
|
||||
|
||||
|
||||
class ShareGroupTypeExists(ManilaException):
|
||||
message = _("Share group type %(type_id)s already exists.")
|
||||
|
||||
|
||||
class ShareTypeAccessExists(ManilaException):
|
||||
message = _("Share type access for %(share_type_id)s / "
|
||||
"%(project_id)s combination already exists.")
|
||||
|
||||
|
||||
class ShareGroupTypeAccessExists(ManilaException):
|
||||
message = _("Share group type access for %(type_id)s / "
|
||||
"%(project_id)s combination already exists.")
|
||||
|
||||
|
||||
class ShareTypeCreateFailed(ManilaException):
|
||||
message = _("Cannot create share_type with "
|
||||
"name %(name)s and specs %(extra_specs)s.")
|
||||
|
||||
|
||||
class ShareGroupTypeCreateFailed(ManilaException):
|
||||
message = _("Cannot create share group type with "
|
||||
"name %(name)s and specs %(group_specs)s.")
|
||||
|
||||
|
||||
class ManageExistingShareTypeMismatch(ManilaException):
|
||||
message = _("Manage existing share failed due to share type mismatch: "
|
||||
"%(reason)s")
|
||||
@ -746,27 +792,27 @@ class HNASNothingToCloneException(ManilaException):
|
||||
message = _("HNAS Nothing To Clone Exception: %(msg)s")
|
||||
|
||||
|
||||
# ConsistencyGroup
|
||||
class ConsistencyGroupNotFound(NotFound):
|
||||
message = _("ConsistencyGroup %(consistency_group_id)s could not be "
|
||||
# ShareGroup
|
||||
class ShareGroupNotFound(NotFound):
|
||||
message = _("Share group %(share_group_id)s could not be found.")
|
||||
|
||||
|
||||
class ShareGroupSnapshotNotFound(NotFound):
|
||||
message = _(
|
||||
"Share group snapshot %(share_group_snapshot_id)s could not be found.")
|
||||
|
||||
|
||||
class ShareGroupSnapshotMemberNotFound(NotFound):
|
||||
message = _("Share group snapshot member %(member_id)s could not be "
|
||||
"found.")
|
||||
|
||||
|
||||
class CGSnapshotNotFound(NotFound):
|
||||
message = _("Consistency group snapshot %(cgsnapshot_id)s could not be "
|
||||
"found.")
|
||||
class InvalidShareGroup(Invalid):
|
||||
message = _("Invalid share group: %(reason)s")
|
||||
|
||||
|
||||
class CGSnapshotMemberNotFound(NotFound):
|
||||
message = _("CG snapshot %(member_id)s could not be found.")
|
||||
|
||||
|
||||
class InvalidConsistencyGroup(Invalid):
|
||||
message = _("Invalid ConsistencyGroup: %(reason)s")
|
||||
|
||||
|
||||
class InvalidCGSnapshot(Invalid):
|
||||
message = _("Invalid CGSnapshot: %(reason)s")
|
||||
class InvalidShareGroupSnapshot(Invalid):
|
||||
message = _("Invalid share group snapshot: %(reason)s")
|
||||
|
||||
|
||||
class DriverNotInitialized(ManilaException):
|
||||
|
@ -61,14 +61,14 @@ def share_replica_update_db(context, share_replica_id, host):
|
||||
return db.share_replica_update(context, share_replica_id, values)
|
||||
|
||||
|
||||
def cg_update_db(context, cg_id, host):
|
||||
'''Set the host and set the updated_at field of a consistency group.
|
||||
def share_group_update_db(context, share_group_id, host):
|
||||
'''Set the host and set the updated_at field of a share group.
|
||||
|
||||
:returns: A CG with the updated fields set properly.
|
||||
:returns: A share group with the updated fields set properly.
|
||||
'''
|
||||
now = timeutils.utcnow()
|
||||
values = {'host': host, 'updated_at': now}
|
||||
return db.consistency_group_update(context, cg_id, values)
|
||||
return db.share_group_update(context, share_group_id, values)
|
||||
|
||||
|
||||
class Scheduler(object):
|
||||
@ -109,12 +109,12 @@ class Scheduler(object):
|
||||
"""Must override schedule method for scheduler to work."""
|
||||
raise NotImplementedError(_("Must implement schedule_create_share"))
|
||||
|
||||
def schedule_create_consistency_group(self, context, group_id,
|
||||
def schedule_create_share_group(self, context, share_group_id,
|
||||
request_spec,
|
||||
filter_properties):
|
||||
"""Must override schedule method for scheduler to work."""
|
||||
raise NotImplementedError(_(
|
||||
"Must implement schedule_create_consistency_group"))
|
||||
"Must implement schedule_create_share_group"))
|
||||
|
||||
def get_pools(self, context, filters):
|
||||
"""Must override schedule method for scheduler to work."""
|
||||
|
@ -169,17 +169,7 @@ class FilterScheduler(base.Scheduler):
|
||||
|
||||
config_options = self._get_configuration_options()
|
||||
|
||||
# NOTE(ameade): If a consistency group is specified, pass the
|
||||
# consistency group support level to the ConsistencyGroupFilter
|
||||
# (host, pool, or False)
|
||||
cg_support = None
|
||||
cg = request_spec.get('consistency_group')
|
||||
if cg:
|
||||
temp_hosts = self.host_manager.get_all_host_states_share(elevated)
|
||||
cg_host = next((host for host in temp_hosts
|
||||
if host.host == cg.get('host')), None)
|
||||
if cg_host:
|
||||
cg_support = cg_host.consistency_group_support
|
||||
share_group = request_spec.get('share_group')
|
||||
|
||||
# NOTE(gouthamr): If 'active_replica_host' is present in the request
|
||||
# spec, pass that host's 'replication_domain' to the
|
||||
@ -202,8 +192,7 @@ class FilterScheduler(base.Scheduler):
|
||||
'config_options': config_options,
|
||||
'share_type': share_type,
|
||||
'resource_type': resource_type,
|
||||
'cg_support': cg_support,
|
||||
'consistency_group': cg,
|
||||
'share_group': share_group,
|
||||
'replication_domain': replication_domain,
|
||||
})
|
||||
|
||||
@ -318,27 +307,24 @@ class FilterScheduler(base.Scheduler):
|
||||
filter_properties['user_id'] = shr.get('user_id')
|
||||
filter_properties['metadata'] = shr.get('metadata')
|
||||
|
||||
def schedule_create_consistency_group(self, context, group_id,
|
||||
request_spec,
|
||||
filter_properties):
|
||||
def schedule_create_share_group(self, context, share_group_id,
|
||||
request_spec, filter_properties):
|
||||
|
||||
LOG.info(_LI("Scheduling consistency group %s") % group_id)
|
||||
|
||||
host = self._get_best_host_for_consistency_group(
|
||||
context,
|
||||
request_spec)
|
||||
LOG.info(_LI("Scheduling share group %s.") % share_group_id)
|
||||
host = self._get_best_host_for_share_group(context, request_spec)
|
||||
|
||||
if not host:
|
||||
msg = _("No hosts available for consistency group %s") % group_id
|
||||
msg = _("No hosts available for share group %s.") % share_group_id
|
||||
raise exception.NoValidHost(reason=msg)
|
||||
|
||||
msg = _LI("Chose host %(host)s for create_consistency_group %(cg_id)s")
|
||||
LOG.info(msg % {'host': host, 'cg_id': group_id})
|
||||
msg = _LI("Chose host %(host)s for create_share_group %(group)s.")
|
||||
LOG.info(msg % {'host': host, 'group': share_group_id})
|
||||
|
||||
updated_group = base.cg_update_db(context, group_id, host)
|
||||
updated_share_group = base.share_group_update_db(
|
||||
context, share_group_id, host)
|
||||
|
||||
self.share_rpcapi.create_consistency_group(context,
|
||||
updated_group, host)
|
||||
self.share_rpcapi.create_share_group(
|
||||
context, updated_share_group, host)
|
||||
|
||||
def _get_weighted_hosts_for_share_type(self, context, request_spec,
|
||||
share_type):
|
||||
@ -363,9 +349,6 @@ class FilterScheduler(base.Scheduler):
|
||||
if extra_spec is not None:
|
||||
share_type['extra_specs'][spec_name] = (
|
||||
"<is> %s" % extra_spec)
|
||||
# Only allow pools that support consistency groups
|
||||
share_type['extra_specs']['consistency_group_support'] = (
|
||||
"<or> host <or> pool")
|
||||
|
||||
filter_properties = {
|
||||
'context': context,
|
||||
@ -393,8 +376,40 @@ class FilterScheduler(base.Scheduler):
|
||||
|
||||
return weighed_hosts
|
||||
|
||||
def _get_weighted_candidates_cg(self, context, request_spec):
|
||||
"""Finds hosts that support the consistency group.
|
||||
def _get_weighted_hosts_for_share_group_type(self, context, request_spec,
|
||||
share_group_type):
|
||||
config_options = self._get_configuration_options()
|
||||
all_hosts = self.host_manager.get_all_host_states_share(context)
|
||||
|
||||
if not all_hosts:
|
||||
return []
|
||||
|
||||
filter_properties = {
|
||||
'context': context,
|
||||
'request_spec': request_spec,
|
||||
'config_options': config_options,
|
||||
'share_group_type': share_group_type,
|
||||
'resource_type': share_group_type,
|
||||
}
|
||||
|
||||
hosts = self.host_manager.get_filtered_hosts(
|
||||
all_hosts, filter_properties)
|
||||
|
||||
if not hosts:
|
||||
return []
|
||||
|
||||
LOG.debug("Filtered %s" % hosts)
|
||||
|
||||
weighed_hosts = self.host_manager.get_weighed_hosts(
|
||||
hosts,
|
||||
filter_properties)
|
||||
if not weighed_hosts:
|
||||
return []
|
||||
|
||||
return weighed_hosts
|
||||
|
||||
def _get_weighted_candidates_share_group(self, context, request_spec):
|
||||
"""Finds hosts that support the share group.
|
||||
|
||||
Returns a list of hosts that meet the required specs,
|
||||
ordered by their fitness.
|
||||
@ -410,7 +425,7 @@ class FilterScheduler(base.Scheduler):
|
||||
elevated, request_spec, share_type)
|
||||
|
||||
# NOTE(ameade): Take the intersection of hosts so we have one that
|
||||
# can support all share types of the CG
|
||||
# can support all share types of the share group
|
||||
if iteration_count == 0:
|
||||
weighed_hosts = temp_weighed_hosts
|
||||
else:
|
||||
@ -423,10 +438,22 @@ class FilterScheduler(base.Scheduler):
|
||||
if not weighed_hosts:
|
||||
return []
|
||||
|
||||
# NOTE(ameade): Ensure the hosts support the share group type
|
||||
share_group_type = request_spec.get("resource_type", {})
|
||||
temp_weighed_group_hosts = (
|
||||
self._get_weighted_hosts_for_share_group_type(
|
||||
elevated, request_spec, share_group_type))
|
||||
new_weighed_hosts = []
|
||||
for host1 in weighed_hosts:
|
||||
for host2 in temp_weighed_group_hosts:
|
||||
if host1.obj.host == host2.obj.host:
|
||||
new_weighed_hosts.append(host1)
|
||||
weighed_hosts = new_weighed_hosts
|
||||
|
||||
return weighed_hosts
|
||||
|
||||
def _get_best_host_for_consistency_group(self, context, request_spec):
|
||||
weighed_hosts = self._get_weighted_candidates_cg(
|
||||
def _get_best_host_for_share_group(self, context, request_spec):
|
||||
weighed_hosts = self._get_weighted_candidates_share_group(
|
||||
context,
|
||||
request_spec)
|
||||
|
||||
|
@ -33,7 +33,7 @@ class CapacityFilter(base_host.BaseHostFilter):
|
||||
|
||||
def host_passes(self, host_state, filter_properties):
|
||||
"""Return True if host has sufficient capacity."""
|
||||
share_size = filter_properties.get('size')
|
||||
share_size = filter_properties.get('size', 0)
|
||||
|
||||
if host_state.free_capacity_gb is None:
|
||||
# Fail Safe
|
||||
|
@ -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',
|
||||
'CapacityFilter',
|
||||
'CapabilitiesFilter',
|
||||
'ConsistencyGroupFilter',
|
||||
'DriverFilter',
|
||||
'ShareReplicationFilter',
|
||||
],
|
||||
@ -132,7 +131,6 @@ class HostState(object):
|
||||
self.snapshot_support = True
|
||||
self.create_share_from_snapshot_support = True
|
||||
self.revert_to_snapshot_support = False
|
||||
self.consistency_group_support = False
|
||||
self.dedupe = False
|
||||
self.compression = False
|
||||
self.replication_type = None
|
||||
@ -304,10 +302,6 @@ class HostState(object):
|
||||
pool_cap['revert_to_snapshot_support'] = (
|
||||
self.revert_to_snapshot_support)
|
||||
|
||||
if not pool_cap.get('consistency_group_support'):
|
||||
pool_cap['consistency_group_support'] = \
|
||||
self.consistency_group_support
|
||||
|
||||
if 'dedupe' not in pool_cap:
|
||||
pool_cap['dedupe'] = self.dedupe
|
||||
|
||||
@ -332,8 +326,6 @@ class HostState(object):
|
||||
'create_share_from_snapshot_support')
|
||||
self.revert_to_snapshot_support = capability.get(
|
||||
'revert_to_snapshot_support', False)
|
||||
self.consistency_group_support = capability.get(
|
||||
'consistency_group_support', False)
|
||||
self.updated = capability['timestamp']
|
||||
self.replication_type = capability.get('replication_type')
|
||||
self.replication_domain = capability.get('replication_domain')
|
||||
|
@ -58,7 +58,7 @@ MAPPING = {
|
||||
class SchedulerManager(manager.Manager):
|
||||
"""Chooses a host to create shares."""
|
||||
|
||||
RPC_API_VERSION = '1.7'
|
||||
RPC_API_VERSION = '1.8'
|
||||
|
||||
def __init__(self, scheduler_driver=None, service_name=None,
|
||||
*args, **kwargs):
|
||||
@ -212,34 +212,29 @@ class SchedulerManager(manager.Manager):
|
||||
def request_service_capabilities(self, context):
|
||||
share_rpcapi.ShareAPI().publish_service_capabilities(context)
|
||||
|
||||
def _set_cg_error_state(self, method, context, ex, request_spec):
|
||||
def _set_share_group_error_state(self, method, context, ex, request_spec):
|
||||
LOG.warning(_LW("Failed to schedule_%(method)s: %(ex)s"),
|
||||
{"method": method, "ex": ex})
|
||||
|
||||
cg_state = {'status': constants.STATUS_ERROR}
|
||||
share_group_state = {'status': constants.STATUS_ERROR}
|
||||
|
||||
consistency_group_id = request_spec.get('consistency_group_id')
|
||||
share_group_id = request_spec.get('share_group_id')
|
||||
|
||||
if consistency_group_id:
|
||||
db.consistency_group_update(context,
|
||||
consistency_group_id,
|
||||
cg_state)
|
||||
if share_group_id:
|
||||
db.share_group_update(context, share_group_id, share_group_state)
|
||||
|
||||
# TODO(ameade): add notifications
|
||||
|
||||
def create_consistency_group(self, context, cg_id, request_spec=None,
|
||||
def create_share_group(self, context, share_group_id, request_spec=None,
|
||||
filter_properties=None):
|
||||
try:
|
||||
self.driver.schedule_create_consistency_group(context, cg_id,
|
||||
request_spec,
|
||||
filter_properties)
|
||||
self.driver.schedule_create_share_group(
|
||||
context, share_group_id, request_spec, filter_properties)
|
||||
except exception.NoValidHost as ex:
|
||||
self._set_cg_error_state('create_consistency_group',
|
||||
context, ex, request_spec)
|
||||
self._set_share_group_error_state(
|
||||
'create_share_group', context, ex, request_spec)
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self._set_cg_error_state('create_consistency_group',
|
||||
context, ex, request_spec)
|
||||
self._set_share_group_error_state(
|
||||
'create_share_group', context, ex, request_spec)
|
||||
|
||||
def _set_share_replica_error_state(self, context, method, exc,
|
||||
request_spec):
|
||||
|
@ -34,20 +34,21 @@ class SchedulerAPI(object):
|
||||
1.1 - Add get_pools method
|
||||
1.2 - Introduce Share Instances. Replace ``create_share()`` with
|
||||
``create_share_instance()``
|
||||
1.3 - Add create_consistency_group method
|
||||
1.3 - Add create_consistency_group method (renamed in 1.7)
|
||||
1.4 - Add migrate_share_to_host method
|
||||
1.5 - Add create_share_replica
|
||||
1.6 - Add manage_share
|
||||
1.7 - Updated migrate_share_to_host method with new parameters
|
||||
1.8 - Rename create_consistency_group -> create_share_group method
|
||||
"""
|
||||
|
||||
RPC_API_VERSION = '1.7'
|
||||
RPC_API_VERSION = '1.8'
|
||||
|
||||
def __init__(self):
|
||||
super(SchedulerAPI, self).__init__()
|
||||
target = messaging.Target(topic=CONF.scheduler_topic,
|
||||
version=self.RPC_API_VERSION)
|
||||
self.client = rpc.get_client(target, version_cap='1.7')
|
||||
self.client = rpc.get_client(target, version_cap=self.RPC_API_VERSION)
|
||||
|
||||
def create_share_instance(self, context, request_spec=None,
|
||||
filter_properties=None):
|
||||
@ -72,13 +73,23 @@ class SchedulerAPI(object):
|
||||
call_context = self.client.prepare(version='1.1')
|
||||
return call_context.call(context, 'get_pools', filters=filters)
|
||||
|
||||
def create_consistency_group(self, context, cg_id, request_spec=None,
|
||||
def create_share_group(self, context, share_group_id, request_spec=None,
|
||||
filter_properties=None):
|
||||
"""Casts an rpc to the scheduler to create a share group.
|
||||
|
||||
Example of 'request_spec' argument value::
|
||||
{
|
||||
'share_group_type_id': ,
|
||||
'share_group_id': 'some_fake_uuid',
|
||||
'share_types': [models.ShareType],
|
||||
'resource_type': models.ShareGroup,
|
||||
}
|
||||
"""
|
||||
request_spec_p = jsonutils.to_primitive(request_spec)
|
||||
call_context = self.client.prepare(version='1.3')
|
||||
call_context = self.client.prepare(version='1.8')
|
||||
return call_context.cast(context,
|
||||
'create_consistency_group',
|
||||
cg_id=cg_id,
|
||||
'create_share_group',
|
||||
share_group_id=share_group_id,
|
||||
request_spec=request_spec_p,
|
||||
filter_properties=filter_properties)
|
||||
|
||||
|
@ -40,7 +40,6 @@ def generate_stats(host_state, properties):
|
||||
host_state.driver_handles_share_servers,
|
||||
'thin_provisioning': host_state.thin_provisioning,
|
||||
'updated': host_state.updated,
|
||||
'consistency_group_support': host_state.consistency_group_support,
|
||||
'dedupe': host_state.dedupe,
|
||||
'compression': host_state.compression,
|
||||
'snapshot_support': host_state.snapshot_support,
|
||||
|
@ -70,7 +70,7 @@ class API(base.Base):
|
||||
def create(self, context, share_proto, size, name, description,
|
||||
snapshot_id=None, availability_zone=None, metadata=None,
|
||||
share_network_id=None, share_type=None, is_public=False,
|
||||
consistency_group_id=None, cgsnapshot_member=None):
|
||||
share_group_id=None, share_group_snapshot_member=None):
|
||||
"""Create new share."""
|
||||
policy.check_policy(context, 'share', 'create')
|
||||
|
||||
@ -171,47 +171,45 @@ class API(base.Base):
|
||||
except ValueError as e:
|
||||
raise exception.InvalidParameterValue(six.text_type(e))
|
||||
|
||||
consistency_group = None
|
||||
if consistency_group_id:
|
||||
share_group = None
|
||||
if share_group_id:
|
||||
try:
|
||||
consistency_group = self.db.consistency_group_get(
|
||||
context, consistency_group_id)
|
||||
share_group = self.db.share_group_get(context, share_group_id)
|
||||
except exception.NotFound as e:
|
||||
raise exception.InvalidParameterValue(six.text_type(e))
|
||||
|
||||
if (not cgsnapshot_member and
|
||||
not (consistency_group['status'] ==
|
||||
constants.STATUS_AVAILABLE)):
|
||||
if (not share_group_snapshot_member and
|
||||
not (share_group['status'] == constants.STATUS_AVAILABLE)):
|
||||
params = {
|
||||
'avail': constants.STATUS_AVAILABLE,
|
||||
'cg_status': consistency_group['status'],
|
||||
'status': share_group['status'],
|
||||
}
|
||||
msg = _("Consistency group status must be %(avail)s, got"
|
||||
"%(cg_status)s.") % params
|
||||
raise exception.InvalidConsistencyGroup(message=msg)
|
||||
msg = _("Share group status must be %(avail)s, got"
|
||||
"%(status)s.") % params
|
||||
raise exception.InvalidShareGroup(message=msg)
|
||||
|
||||
if share_type_id:
|
||||
cg_st_ids = [st['share_type_id'] for st in
|
||||
consistency_group.get('share_types', [])]
|
||||
if share_type_id not in cg_st_ids:
|
||||
share_group_st_ids = [
|
||||
st['share_type_id']
|
||||
for st in share_group.get('share_types', [])]
|
||||
if share_type_id not in share_group_st_ids:
|
||||
params = {
|
||||
'type': share_type_id,
|
||||
'cg': consistency_group_id
|
||||
'group': share_group_id,
|
||||
}
|
||||
msg = _("The specified share type (%(type)s) is not "
|
||||
"supported by the specified consistency group "
|
||||
"(%(cg)s).") % params
|
||||
"supported by the specified share group "
|
||||
"(%(group)s).") % params
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
|
||||
if (not consistency_group.get('share_network_id')
|
||||
== share_network_id):
|
||||
if not share_group.get('share_network_id') == share_network_id:
|
||||
params = {
|
||||
'net': share_network_id,
|
||||
'cg': consistency_group_id
|
||||
'group': share_group_id
|
||||
}
|
||||
msg = _("The specified share network (%(net)s) is not "
|
||||
"supported by the specified consistency group "
|
||||
"(%(cg)s).") % params
|
||||
"supported by the specified share group "
|
||||
"(%(group)s).") % params
|
||||
raise exception.InvalidParameterValue(msg)
|
||||
|
||||
options = {
|
||||
@ -224,12 +222,13 @@ class API(base.Base):
|
||||
'display_description': description,
|
||||
'share_proto': share_proto,
|
||||
'is_public': is_public,
|
||||
'consistency_group_id': consistency_group_id,
|
||||
'share_group_id': share_group_id,
|
||||
}
|
||||
options.update(self.get_share_attributes_from_share_type(share_type))
|
||||
|
||||
if cgsnapshot_member:
|
||||
options['source_cgsnapshot_member_id'] = cgsnapshot_member['id']
|
||||
if share_group_snapshot_member:
|
||||
options['source_share_group_snapshot_member_id'] = (
|
||||
share_group_snapshot_member['id'])
|
||||
|
||||
try:
|
||||
share = self.db.share_create(context, options,
|
||||
@ -248,10 +247,13 @@ class API(base.Base):
|
||||
# It is common situation for different types of backends.
|
||||
host = snapshot['share']['instance']['host']
|
||||
|
||||
self.create_instance(context, share, share_network_id=share_network_id,
|
||||
host=host, availability_zone=availability_zone,
|
||||
consistency_group=consistency_group,
|
||||
cgsnapshot_member=cgsnapshot_member,
|
||||
elif share_group:
|
||||
host = share_group['host']
|
||||
|
||||
self.create_instance(
|
||||
context, share, share_network_id=share_network_id, host=host,
|
||||
availability_zone=availability_zone, share_group=share_group,
|
||||
share_group_snapshot_member=share_group_snapshot_member,
|
||||
share_type_id=share_type_id)
|
||||
|
||||
# Retrieve the share with instance details
|
||||
@ -316,20 +318,21 @@ class API(base.Base):
|
||||
|
||||
def create_instance(self, context, share, share_network_id=None,
|
||||
host=None, availability_zone=None,
|
||||
consistency_group=None, cgsnapshot_member=None,
|
||||
share_group=None, share_group_snapshot_member=None,
|
||||
share_type_id=None):
|
||||
policy.check_policy(context, 'share', 'create')
|
||||
|
||||
request_spec, share_instance = (
|
||||
self.create_share_instance_and_get_request_spec(
|
||||
context, share, availability_zone=availability_zone,
|
||||
consistency_group=consistency_group, host=host,
|
||||
share_group=share_group, host=host,
|
||||
share_network_id=share_network_id,
|
||||
share_type_id=share_type_id))
|
||||
|
||||
if cgsnapshot_member:
|
||||
# Inherit properties from the cgsnapshot_member
|
||||
member_share_instance = cgsnapshot_member['share_instance']
|
||||
if share_group_snapshot_member:
|
||||
# Inherit properties from the share_group_snapshot_member
|
||||
member_share_instance = share_group_snapshot_member[
|
||||
'share_instance']
|
||||
updates = {
|
||||
'host': member_share_instance['host'],
|
||||
'share_network_id': member_share_instance['share_network_id'],
|
||||
@ -338,7 +341,8 @@ class API(base.Base):
|
||||
share = self.db.share_instance_update(context,
|
||||
share_instance['id'],
|
||||
updates)
|
||||
# NOTE(ameade): Do not cast to driver if creating from cgsnapshot
|
||||
# NOTE(ameade): Do not cast to driver if creating from share group
|
||||
# snapshot
|
||||
return
|
||||
|
||||
if host:
|
||||
@ -360,7 +364,7 @@ class API(base.Base):
|
||||
|
||||
def create_share_instance_and_get_request_spec(
|
||||
self, context, share, availability_zone=None,
|
||||
consistency_group=None, host=None, share_network_id=None,
|
||||
share_group=None, host=None, share_network_id=None,
|
||||
share_type_id=None, cast_rules_to_readonly=False):
|
||||
|
||||
availability_zone_id = None
|
||||
@ -398,9 +402,9 @@ class API(base.Base):
|
||||
'share_proto': share['share_proto'],
|
||||
'share_type_id': share_type_id,
|
||||
'is_public': share['is_public'],
|
||||
'consistency_group_id': share['consistency_group_id'],
|
||||
'source_cgsnapshot_member_id': share[
|
||||
'source_cgsnapshot_member_id'],
|
||||
'share_group_id': share['share_group_id'],
|
||||
'source_share_group_snapshot_member_id': share[
|
||||
'source_share_group_snapshot_member_id'],
|
||||
'snapshot_id': share['snapshot_id'],
|
||||
'replication_type': share['replication_type'],
|
||||
}
|
||||
@ -428,7 +432,7 @@ class API(base.Base):
|
||||
'share_id': share['id'],
|
||||
'snapshot_id': share['snapshot_id'],
|
||||
'share_type': share_type,
|
||||
'consistency_group': consistency_group,
|
||||
'share_group': share_group,
|
||||
'availability_zone_id': availability_zone_id,
|
||||
}
|
||||
return request_spec, share_instance
|
||||
@ -440,6 +444,10 @@ class API(base.Base):
|
||||
msg = _("Replication not supported for share %s.")
|
||||
raise exception.InvalidShare(message=msg % share['id'])
|
||||
|
||||
if share.get('share_group_id'):
|
||||
msg = _("Replication not supported for shares in a group.")
|
||||
raise exception.InvalidShare(message=msg)
|
||||
|
||||
self._check_is_share_busy(share)
|
||||
|
||||
active_replica = self.db.share_replicas_get_available_active_replica(
|
||||
@ -642,11 +650,11 @@ class API(base.Base):
|
||||
'share_proto': kwargs.get('share_proto', share.get('share_proto')),
|
||||
'share_type_id': share_type['id'],
|
||||
'is_public': kwargs.get('is_public', share.get('is_public')),
|
||||
'consistency_group_id': kwargs.get(
|
||||
'consistency_group_id', share.get('consistency_group_id')),
|
||||
'source_cgsnapshot_member_id': kwargs.get(
|
||||
'source_cgsnapshot_member_id',
|
||||
share.get('source_cgsnapshot_member_id')),
|
||||
'share_group_id': kwargs.get(
|
||||
'share_group_id', share.get('share_group_id')),
|
||||
'source_share_group_snapshot_member_id': kwargs.get(
|
||||
'source_share_group_snapshot_member_id',
|
||||
share.get('source_share_group_snapshot_member_id')),
|
||||
'snapshot_id': kwargs.get('snapshot_id', share.get('snapshot_id')),
|
||||
}
|
||||
share_instance_properties = {
|
||||
@ -878,14 +886,16 @@ class API(base.Base):
|
||||
|
||||
snapshots = self.db.share_snapshot_get_all_for_share(context, share_id)
|
||||
if len(snapshots):
|
||||
msg = _("Share still has %d dependent snapshots") % len(snapshots)
|
||||
msg = _("Share still has %d dependent snapshots.") % len(snapshots)
|
||||
raise exception.InvalidShare(reason=msg)
|
||||
|
||||
cgsnapshot_members_count = self.db.count_cgsnapshot_members_in_share(
|
||||
context, share_id)
|
||||
if cgsnapshot_members_count:
|
||||
msg = (_("Share still has %d dependent cgsnapshot members") %
|
||||
cgsnapshot_members_count)
|
||||
share_group_snapshot_members_count = (
|
||||
self.db.count_share_group_snapshot_members_in_share(
|
||||
context, share_id))
|
||||
if share_group_snapshot_members_count:
|
||||
msg = (
|
||||
_("Share still has %d dependent share group snapshot "
|
||||
"members.") % share_group_snapshot_members_count)
|
||||
raise exception.InvalidShare(reason=msg)
|
||||
|
||||
self._check_is_share_busy(share)
|
||||
@ -955,10 +965,10 @@ class API(base.Base):
|
||||
if shares:
|
||||
raise exception.ShareServerInUse(share_server_id=server['id'])
|
||||
|
||||
cgs = self.db.consistency_group_get_all_by_share_server(context,
|
||||
server['id'])
|
||||
if cgs:
|
||||
LOG.error(_LE("share server '%(ssid)s' in use by CGs"),
|
||||
share_groups = self.db.share_group_get_all_by_share_server(
|
||||
context, server['id'])
|
||||
if share_groups:
|
||||
LOG.error(_LE("share server '%(ssid)s' in use by share groups."),
|
||||
{'ssid': server['id']})
|
||||
raise exception.ShareServerInUse(share_server_id=server['id'])
|
||||
|
||||
|
@ -25,7 +25,7 @@ from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
||||
from manila import exception
|
||||
from manila.i18n import _, _LE
|
||||
from manila.i18n import _, _LE, _LW
|
||||
from manila import network
|
||||
from manila import utils
|
||||
|
||||
@ -825,7 +825,7 @@ class ShareDriver(object):
|
||||
|
||||
def choose_share_server_compatible_with_share(self, context, share_servers,
|
||||
share, snapshot=None,
|
||||
consistency_group=None):
|
||||
share_group=None):
|
||||
"""Method that allows driver to choose share server for provided share.
|
||||
|
||||
If compatible share-server is not found, method should return None.
|
||||
@ -834,21 +834,22 @@ class ShareDriver(object):
|
||||
:param share_servers: list with share-server models
|
||||
:param share: share model
|
||||
:param snapshot: snapshot model
|
||||
:param consistency_group: ConsistencyGroup model with shares
|
||||
:param share_group: ConsistencyGroup model with shares
|
||||
:returns: share-server or None
|
||||
"""
|
||||
# If creating in a consistency group, use its share server
|
||||
if consistency_group:
|
||||
# If creating in a share group, use its share server
|
||||
if share_group:
|
||||
for share_server in share_servers:
|
||||
if (consistency_group.get('share_server_id') ==
|
||||
if (share_group.get('share_server_id') ==
|
||||
share_server['id']):
|
||||
return share_server
|
||||
return None
|
||||
|
||||
return share_servers[0] if share_servers else None
|
||||
|
||||
def choose_share_server_compatible_with_cg(self, context, share_servers,
|
||||
cg_ref, cgsnapshot=None):
|
||||
def choose_share_server_compatible_with_share_group(
|
||||
self, context, share_servers, share_group_ref,
|
||||
share_group_snapshot=None):
|
||||
|
||||
return share_servers[0] if share_servers else None
|
||||
|
||||
@ -1074,6 +1075,7 @@ class ShareDriver(object):
|
||||
create_share_from_snapshot_support=(
|
||||
self.creating_shares_from_snapshots_is_supported),
|
||||
revert_to_snapshot_support=False,
|
||||
share_group_snapshot_support=self.snapshots_are_supported,
|
||||
replication_domain=self.replication_domain,
|
||||
filter_function=self.get_filter_function(),
|
||||
goodness_function=self.get_goodness_function(),
|
||||
@ -1089,11 +1091,11 @@ class ShareDriver(object):
|
||||
"""
|
||||
return []
|
||||
|
||||
def create_consistency_group(self, context, cg_dict, share_server=None):
|
||||
"""Create a consistency group.
|
||||
def create_share_group(self, context, share_group_dict, share_server=None):
|
||||
"""Create a share group.
|
||||
|
||||
:param context:
|
||||
:param cg_dict: The consistency group details
|
||||
:param share_group_dict: The share group details
|
||||
EXAMPLE:
|
||||
{
|
||||
'status': 'creating',
|
||||
@ -1103,27 +1105,30 @@ class ShareDriver(object):
|
||||
'deleted': 'False',
|
||||
'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6),
|
||||
'updated_at': None,
|
||||
'source_cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'host': 'openstack2@cmodeSSVMNFS',
|
||||
'source_share_group_snapshot_id': 'some_fake_uuid',
|
||||
'share_group_type_id': 'some_fake_uuid',
|
||||
'host': 'hostname@backend_name',
|
||||
'share_network_id': None,
|
||||
'share_server_id': None,
|
||||
'deleted_at': None,
|
||||
'share_types': [<models.ConsistencyGroupShareTypeMapping>],
|
||||
'id': 'eda52174-0442-476d-9694-a58327466c14',
|
||||
'share_types': [<models.ShareGroupShareTypeMapping>],
|
||||
'id': 'some_fake_uuid',
|
||||
'name': None
|
||||
}
|
||||
:returns: (cg_model_update, share_update_list)
|
||||
cg_model_update - a dict containing any values to be updated
|
||||
for the CG in the database. This value may be None.
|
||||
:returns: (share_group_model_update, share_update_list)
|
||||
share_group_model_update - a dict containing any values to be
|
||||
updated for the SG in the database. This value may be None.
|
||||
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
LOG.debug('Created a Share Group with ID: %s.', share_group_dict['id'])
|
||||
|
||||
def create_consistency_group_from_cgsnapshot(self, context, cg_dict,
|
||||
cgsnapshot_dict,
|
||||
def create_share_group_from_share_group_snapshot(
|
||||
self, context, share_group_dict, share_group_snapshot_dict,
|
||||
share_server=None):
|
||||
"""Create a consistency group from a cgsnapshot.
|
||||
"""Create a share group from a share group snapshot.
|
||||
|
||||
:param context:
|
||||
:param cg_dict: The consistency group details
|
||||
:param share_group_dict: The share group details
|
||||
EXAMPLE:
|
||||
.. code::
|
||||
|
||||
@ -1135,15 +1140,16 @@ class ShareDriver(object):
|
||||
'deleted': 'False',
|
||||
'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6),
|
||||
'updated_at': None,
|
||||
'source_cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'host': 'openstack2@cmodeSSVMNFS',
|
||||
'source_share_group_snapshot_id':
|
||||
'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'host': 'hostname@backend_name',
|
||||
'deleted_at': None,
|
||||
'shares': [<models.Share>], # The new shares being created
|
||||
'share_types': [<models.ConsistencyGroupShareTypeMapping>],
|
||||
'id': 'eda52174-0442-476d-9694-a58327466c14',
|
||||
'share_types': [<models.ShareGroupShareTypeMapping>],
|
||||
'id': 'some_fake_uuid',
|
||||
'name': None
|
||||
}
|
||||
:param cgsnapshot_dict: The cgsnapshot details
|
||||
:param share_group_snapshot_dict: The share group snapshot details
|
||||
EXAMPLE:
|
||||
.. code::
|
||||
|
||||
@ -1155,11 +1161,10 @@ class ShareDriver(object):
|
||||
'deleted': '0',
|
||||
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
'consistency_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67',
|
||||
'cgsnapshot_members': [
|
||||
'share_group_id': 'some_fake_uuid',
|
||||
'share_share_group_snapshot_members': [
|
||||
{
|
||||
'status': 'available',
|
||||
'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f',
|
||||
'user_id': 'a0314a441ca842019b0952224aa39192',
|
||||
'deleted': 'False',
|
||||
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
@ -1167,9 +1172,9 @@ class ShareDriver(object):
|
||||
'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
'share_proto': 'NFS',
|
||||
'project_id': '13c0be6290934bd98596cfa004650049',
|
||||
'cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'share_group_snapshot_id': 'some_fake_uuid',
|
||||
'deleted_at': None,
|
||||
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e',
|
||||
'id': 'some_fake_uuid',
|
||||
'size': 1
|
||||
}
|
||||
],
|
||||
@ -1177,27 +1182,48 @@ class ShareDriver(object):
|
||||
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'name': None
|
||||
}
|
||||
:return: (cg_model_update, share_update_list)
|
||||
cg_model_update - a dict containing any values to be updated
|
||||
for the CG in the database. This value may be None.
|
||||
:return: (share_group_model_update, share_update_list)
|
||||
share_group_model_update - a dict containing any values to be
|
||||
updated for the share group in the database. This value may be None
|
||||
|
||||
share_update_list - a list of dictionaries containing dicts for
|
||||
every share created in the CG. Any share dicts should at a minimum
|
||||
contain the 'id' key and 'export_locations'. Export locations
|
||||
should be in the same format as returned by a share_create. This
|
||||
list may be empty or None.
|
||||
EXAMPLE:
|
||||
every share created in the share group. Any share dicts should at a
|
||||
minimum contain the 'id' key and 'export_locations'.
|
||||
Export locations should be in the same format as returned by
|
||||
a share_create. This list may be empty or None. EXAMPLE:
|
||||
.. code::
|
||||
|
||||
[{'id': 'uuid', 'export_locations': ['export_path']}]
|
||||
[{'id': 'uuid', 'export_locations': [{...}, {...}]}]
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
# Ensure that the share group snapshot has members
|
||||
if not share_group_snapshot_dict['share_group_snapshot_members']:
|
||||
return None, None
|
||||
|
||||
def delete_consistency_group(self, context, cg_dict, share_server=None):
|
||||
"""Delete a consistency group
|
||||
clone_list = self._collate_share_group_snapshot_info(
|
||||
share_group_dict, share_group_snapshot_dict)
|
||||
share_update_list = []
|
||||
|
||||
LOG.debug('Creating share group from group snapshot %s.',
|
||||
share_group_snapshot_dict['id'])
|
||||
|
||||
for clone in clone_list:
|
||||
kwargs = {}
|
||||
if self.driver_handles_share_servers:
|
||||
kwargs['share_server'] = share_server
|
||||
export_locations = (
|
||||
self.create_share_from_snapshot(
|
||||
context, clone['share'], clone['snapshot'], **kwargs))
|
||||
share_update_list.append({
|
||||
'id': clone['share']['id'],
|
||||
'export_locations': export_locations,
|
||||
})
|
||||
return None, share_update_list
|
||||
|
||||
def delete_share_group(self, context, share_group_dict, share_server=None):
|
||||
"""Delete a share group
|
||||
|
||||
:param context: The request context
|
||||
:param cg_dict: The consistency group details
|
||||
:param share_group_dict: The share group details
|
||||
EXAMPLE:
|
||||
.. code::
|
||||
|
||||
@ -1209,25 +1235,42 @@ class ShareDriver(object):
|
||||
'deleted': 'False',
|
||||
'created_at': datetime.datetime(2015, 8, 10, 15, 14, 6),
|
||||
'updated_at': None,
|
||||
'source_cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'host': 'openstack2@cmodeSSVMNFS',
|
||||
'source_share_group_snapshot_id': 'some_fake_uuid',
|
||||
'share_share_group_type_id': 'some_fake_uuid',
|
||||
'host': 'hostname@backend_name',
|
||||
'deleted_at': None,
|
||||
'shares': [<models.Share>], # The new shares being created
|
||||
'share_types': [<models.ConsistencyGroupShareTypeMapping>],
|
||||
'id': 'eda52174-0442-476d-9694-a58327466c14',
|
||||
'share_types': [<models.ShareGroupShareTypeMapping>],
|
||||
'id': 'some_fake_uuid',
|
||||
'name': None
|
||||
}
|
||||
:return: cg_model_update
|
||||
cg_model_update - a dict containing any values to be updated
|
||||
for the CG in the database. This value may be None.
|
||||
:return: share_group_model_update
|
||||
share_group_model_update - a dict containing any values to be
|
||||
updated for the group in the database. This value may be None.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def create_cgsnapshot(self, context, snap_dict, share_server=None):
|
||||
"""Create a consistency group snapshot.
|
||||
def _cleanup_group_share_snapshot(self, context, share_snapshot,
|
||||
share_server):
|
||||
"""Deletes the snapshot of a share belonging to a group."""
|
||||
|
||||
try:
|
||||
self.delete_snapshot(
|
||||
context, share_snapshot, share_server=share_server)
|
||||
except exception.ManilaException:
|
||||
msg = _LE('Could not delete share group snapshot member %(snap)s '
|
||||
'for share %(share)s.')
|
||||
LOG.error(msg % {
|
||||
'snap': share_snapshot['id'],
|
||||
'share': share_snapshot['share_id'],
|
||||
})
|
||||
raise
|
||||
|
||||
def create_share_group_snapshot(self, context, snap_dict,
|
||||
share_server=None):
|
||||
"""Create a share group snapshot.
|
||||
|
||||
:param context:
|
||||
:param snap_dict: The cgsnapshot details
|
||||
:param snap_dict: The share group snapshot details
|
||||
EXAMPLE:
|
||||
.. code::
|
||||
|
||||
@ -1239,11 +1282,11 @@ class ShareDriver(object):
|
||||
'deleted': '0',
|
||||
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
'consistency_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67',
|
||||
'cgsnapshot_members': [
|
||||
'share_group_id': 'some_fake_uuid',
|
||||
'share_group_snapshot_members': [
|
||||
{
|
||||
'status': 'available',
|
||||
'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f',
|
||||
'share_type_id': 'some_fake_uuid',
|
||||
'user_id': 'a0314a441ca842019b0952224aa39192',
|
||||
'deleted': 'False',
|
||||
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
@ -1251,32 +1294,75 @@ class ShareDriver(object):
|
||||
'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
'share_proto': 'NFS',
|
||||
'project_id': '13c0be6290934bd98596cfa004650049',
|
||||
'cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'share_group_snapshot_id': 'some_fake_uuid',
|
||||
'deleted_at': None,
|
||||
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e',
|
||||
'share_id': 'some_fake_uuid',
|
||||
'id': 'some_fake_uuid',
|
||||
'size': 1
|
||||
}
|
||||
],
|
||||
'deleted_at': None,
|
||||
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'id': 'some_fake_uuid',
|
||||
'name': None
|
||||
}
|
||||
:return: (cgsnapshot_update, member_update_list)
|
||||
cgsnapshot_update - a dict containing any values to be updated
|
||||
for the CGSnapshot in the database. This value may be None.
|
||||
:return: (share_group_snapshot_update, member_update_list)
|
||||
share_group_snapshot_update - a dict containing any values to be
|
||||
updated for the CGSnapshot in the database. This value may be None.
|
||||
|
||||
member_update_list - a list of dictionaries containing for every
|
||||
member of the cgsnapshot. Each dict should contains values to be
|
||||
updated for the CGSnapshotMember in the database. This list may be
|
||||
empty or None.
|
||||
member of the share group snapshot. Each dict should contains
|
||||
values to be updated for the ShareGroupSnapshotMember in
|
||||
the database. This list may be empty or None.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
LOG.debug('Attempting to create a share group snapshot %s.',
|
||||
snap_dict['id'])
|
||||
|
||||
def delete_cgsnapshot(self, context, snap_dict, share_server=None):
|
||||
"""Delete a consistency group snapshot
|
||||
snapshot_members = snap_dict.get('share_group_snapshot_members', [])
|
||||
if not self._stats.get('share_group_snapshot_support'):
|
||||
raise exception.ShareGroupSnapshotNotSupported(
|
||||
share_group=snap_dict['share_group_id'])
|
||||
elif not snapshot_members:
|
||||
LOG.warning(_LW('No shares in share group to create snapshot.'))
|
||||
else:
|
||||
share_snapshots = []
|
||||
for member in snapshot_members:
|
||||
share_snapshot = {
|
||||
'snapshot_id': member['share_group_snapshot_id'],
|
||||
'share_id': member['share_id'],
|
||||
'id': member['id'],
|
||||
}
|
||||
try:
|
||||
self.create_snapshot(context, share_snapshot,
|
||||
share_server=share_server)
|
||||
share_snapshots.append(share_snapshot)
|
||||
except exception.ManilaException as e:
|
||||
msg = _LE('Could not create share group snapshot. Failed '
|
||||
'to create share snapshot %(snap)s for '
|
||||
'share %(share)s.')
|
||||
LOG.exception(msg % {
|
||||
'snap': share_snapshot['id'],
|
||||
'share': share_snapshot['share_id']
|
||||
})
|
||||
|
||||
# clean up any share snapshots previously created
|
||||
LOG.debug(
|
||||
'Attempting to clean up snapshots due to failure.')
|
||||
for share_snapshot in share_snapshots:
|
||||
self._cleanup_group_share_snapshot(
|
||||
context, share_snapshot, share_server)
|
||||
raise e
|
||||
|
||||
LOG.debug('Successfully created share group snapshot %s.',
|
||||
snap_dict['id'])
|
||||
|
||||
return None, None
|
||||
|
||||
def delete_share_group_snapshot(self, context, snap_dict,
|
||||
share_server=None):
|
||||
"""Delete a share group snapshot
|
||||
|
||||
:param context:
|
||||
:param snap_dict: The cgsnapshot details
|
||||
:param snap_dict: The share group snapshot details
|
||||
EXAMPLE:
|
||||
.. code::
|
||||
|
||||
@ -1288,12 +1374,12 @@ class ShareDriver(object):
|
||||
'deleted': '0',
|
||||
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
'consistency_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67',
|
||||
'cgsnapshot_members': [
|
||||
'share_group_id': 'some_fake_uuid',
|
||||
'share_group_snapshot_members': [
|
||||
{
|
||||
'status': 'available',
|
||||
'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f',
|
||||
'share_id': 'e14b5174-e534-4f35-bc4f-fe81c1575d6f',
|
||||
'share_type_id': 'some_fake_uuid',
|
||||
'share_id': 'some_fake_uuid',
|
||||
'user_id': 'a0314a441ca842019b0952224aa39192',
|
||||
'deleted': 'False',
|
||||
'created_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
@ -1301,9 +1387,9 @@ class ShareDriver(object):
|
||||
'updated_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
|
||||
'share_proto': 'NFS',
|
||||
'project_id': '13c0be6290934bd98596cfa004650049',
|
||||
'cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'share_group_snapshot_id': 'some_fake_uuid',
|
||||
'deleted_at': None,
|
||||
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e',
|
||||
'id': 'some_fake_uuid',
|
||||
'size': 1
|
||||
}
|
||||
],
|
||||
@ -1311,11 +1397,54 @@ class ShareDriver(object):
|
||||
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'name': None
|
||||
}
|
||||
:return: (cgsnapshot_update, member_update_list)
|
||||
cgsnapshot_update - a dict containing any values to be updated
|
||||
for the CGSnapshot in the database. This value may be None.
|
||||
:return: (share_group_snapshot_update, member_update_list)
|
||||
share_group_snapshot_update - a dict containing any values
|
||||
to be updated for the ShareGroupSnapshot in the database.
|
||||
This value may be None.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
snapshot_members = snap_dict.get('share_group_snapshot_members', [])
|
||||
LOG.debug('Deleting share group snapshot %s.' % snap_dict['id'])
|
||||
for member in snapshot_members:
|
||||
share_snapshot = {
|
||||
'share_id': member['share_id'],
|
||||
'id': member['id'],
|
||||
}
|
||||
self.delete_snapshot(
|
||||
context, share_snapshot, share_server=share_server)
|
||||
|
||||
LOG.debug('Deleted share group snapshot %s.' % snap_dict['id'])
|
||||
return None, None
|
||||
|
||||
def _collate_share_group_snapshot_info(self, share_group_dict,
|
||||
share_group_snapshot_dict):
|
||||
"""Collate the data for a clone of the SG snapshot.
|
||||
|
||||
Given two data structures, a share group snapshot (
|
||||
share_group_snapshot_dict) and a new share to be cloned from
|
||||
the snapshot (share_group_dict), match up both structures into a list
|
||||
of dicts (share & snapshot) suitable for use by existing method
|
||||
that clones individual share snapshots.
|
||||
"""
|
||||
clone_list = []
|
||||
for share in share_group_dict['shares']:
|
||||
clone_info = {'share': share}
|
||||
for share_group_snapshot_member in share_group_snapshot_dict[
|
||||
'share_group_snapshot_members']:
|
||||
if (share['source_share_group_snapshot_member_id'] ==
|
||||
share_group_snapshot_member['id']):
|
||||
clone_info['snapshot'] = share_group_snapshot_member
|
||||
break
|
||||
|
||||
if len(clone_info) != 2:
|
||||
msg = _(
|
||||
"Invalid data supplied for creating share group from "
|
||||
"share group snapshot "
|
||||
"%s.") % share_group_snapshot_dict['id']
|
||||
raise exception.InvalidShareGroup(reason=msg)
|
||||
|
||||
clone_list.append(clone_info)
|
||||
|
||||
return clone_list
|
||||
|
||||
def get_periodic_hook_data(self, context, share_instances):
|
||||
"""Dedicated for update/extend of data for existing share instances.
|
||||
|
@ -94,7 +94,6 @@ class CephFSNativeDriver(driver.ShareDriver,):
|
||||
free_capacity_gb = stats['kb_avail'] * units.Mi
|
||||
|
||||
data = {
|
||||
'consistency_group_support': 'pool',
|
||||
'vendor_name': 'Ceph',
|
||||
'driver_version': '1.0',
|
||||
'share_backend_name': self.backend_name,
|
||||
@ -166,7 +165,7 @@ class CephFSNativeDriver(driver.ShareDriver,):
|
||||
def _share_path(self, share):
|
||||
"""Get VolumePath from Share."""
|
||||
return ceph_volume_client.VolumePath(
|
||||
share['consistency_group_id'], share['id'])
|
||||
share['share_group_id'], share['id'])
|
||||
|
||||
def create_share(self, context, share, share_server=None):
|
||||
"""Create a CephFS volume.
|
||||
@ -178,9 +177,11 @@ class CephFSNativeDriver(driver.ShareDriver,):
|
||||
"""
|
||||
|
||||
# `share` is a Share
|
||||
LOG.debug("create_share {be} name={id} size={size} cg_id={cg}".format(
|
||||
msg = _("create_share {be} name={id} size={size}"
|
||||
" share_group_id={group}")
|
||||
LOG.debug(msg.format(
|
||||
be=self.backend_name, id=share['id'], size=share['size'],
|
||||
cg=share['consistency_group_id']))
|
||||
group=share['share_group_id']))
|
||||
|
||||
extra_specs = share_types.get_extra_specs_from_share(share)
|
||||
data_isolated = extra_specs.get("cephfs:data_isolated", False)
|
||||
@ -340,22 +341,24 @@ class CephFSNativeDriver(driver.ShareDriver,):
|
||||
self._share_path(snapshot['share']),
|
||||
'_'.join([snapshot['snapshot_id'], snapshot['id']]))
|
||||
|
||||
def create_consistency_group(self, context, cg_dict, share_server=None):
|
||||
def create_share_group(self, context, cg_dict, share_server=None):
|
||||
self.volume_client.create_group(cg_dict['id'])
|
||||
|
||||
def delete_consistency_group(self, context, cg_dict, share_server=None):
|
||||
def delete_share_group(self, context, cg_dict, share_server=None):
|
||||
self.volume_client.destroy_group(cg_dict['id'])
|
||||
|
||||
def delete_cgsnapshot(self, context, snap_dict, share_server=None):
|
||||
def delete_share_group_snapshot(self, context, snap_dict,
|
||||
share_server=None):
|
||||
self.volume_client.destroy_snapshot_group(
|
||||
snap_dict['consistency_group_id'],
|
||||
snap_dict['share_group_id'],
|
||||
snap_dict['id'])
|
||||
|
||||
return None, []
|
||||
|
||||
def create_cgsnapshot(self, context, snap_dict, share_server=None):
|
||||
def create_share_group_snapshot(self, context, snap_dict,
|
||||
share_server=None):
|
||||
self.volume_client.create_snapshot_group(
|
||||
snap_dict['consistency_group_id'],
|
||||
snap_dict['share_group_id'],
|
||||
snap_dict['id'])
|
||||
|
||||
return None, []
|
||||
|
@ -616,7 +616,6 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver):
|
||||
share_backend_name=self.backend_name,
|
||||
storage_protocol='NFS_CIFS',
|
||||
reserved_percentage=self.configuration.reserved_share_percentage,
|
||||
consistency_group_support=None,
|
||||
)
|
||||
super(GenericShareDriver, self)._update_share_stats(data)
|
||||
|
||||
|
@ -188,7 +188,6 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
|
||||
'storage_protocol': 'NFS_CIFS',
|
||||
'reserved_percentage':
|
||||
self.configuration.reserved_share_percentage,
|
||||
'consistency_group_support': None,
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': True,
|
||||
|
@ -242,7 +242,6 @@ class NetAppCmodeFileStorageLibrary(object):
|
||||
'driver_version': '1.0',
|
||||
'netapp_storage_family': 'ontap_cluster',
|
||||
'storage_protocol': 'NFS_CIFS',
|
||||
'consistency_group_support': 'host',
|
||||
'pools': self._get_pools(filter_function=filter_function,
|
||||
goodness_function=goodness_function),
|
||||
}
|
||||
@ -1070,7 +1069,7 @@ class NetAppCmodeFileStorageLibrary(object):
|
||||
else:
|
||||
msg = _("Invalid data supplied for creating consistency group "
|
||||
"from CG snapshot %s.") % cgsnapshot_dict['id']
|
||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||
raise exception.InvalidShareGroup(reason=msg)
|
||||
|
||||
clone_list.append(clone_info)
|
||||
|
||||
|
@ -375,7 +375,6 @@ class ZFSonLinuxShareDriver(zfs_utils.ExecuteMixin, driver.ShareDriver):
|
||||
'storage_protocol': 'NFS',
|
||||
'reserved_percentage':
|
||||
self.configuration.reserved_share_percentage,
|
||||
'consistency_group_support': None,
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'driver_name': 'ZFS',
|
||||
|
@ -189,7 +189,7 @@ def add_hooks(f):
|
||||
class ShareManager(manager.SchedulerDependentManager):
|
||||
"""Manages NAS storages."""
|
||||
|
||||
RPC_API_VERSION = '1.15'
|
||||
RPC_API_VERSION = '1.16'
|
||||
|
||||
def __init__(self, share_driver=None, service_name=None, *args, **kwargs):
|
||||
"""Load the driver from args, or from flags."""
|
||||
@ -351,7 +351,7 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
|
||||
def _provide_share_server_for_share(self, context, share_network_id,
|
||||
share_instance, snapshot=None,
|
||||
consistency_group=None,
|
||||
share_group=None,
|
||||
create_on_backend=True):
|
||||
"""Gets or creates share_server and updates share with its id.
|
||||
|
||||
@ -442,7 +442,7 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
self.driver.choose_share_server_compatible_with_share(
|
||||
context, available_share_servers, share_instance,
|
||||
snapshot=snapshot.instance if snapshot else None,
|
||||
consistency_group=consistency_group
|
||||
share_group=share_group
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
@ -538,19 +538,20 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
if snapshot_id:
|
||||
snapshot_ref = self.db.share_snapshot_get(context, snapshot_id)
|
||||
|
||||
consistency_group_ref = None
|
||||
if share_instance.get('consistency_group_id'):
|
||||
consistency_group_ref = self.db.consistency_group_get(
|
||||
context, share_instance['consistency_group_id'])
|
||||
share_group_ref = None
|
||||
if share_instance.get('share_group_id'):
|
||||
share_group_ref = self.db.share_group_get(
|
||||
context, share_instance['share_group_id'])
|
||||
|
||||
share_server, share_instance = self._provide_share_server_for_share(
|
||||
context, share_network_id, share_instance, snapshot_ref,
|
||||
consistency_group_ref, create_on_backend=False)
|
||||
share_group_ref, create_on_backend=False)
|
||||
|
||||
return share_server['id']
|
||||
|
||||
def _provide_share_server_for_cg(self, context, share_network_id,
|
||||
cg_ref, cgsnapshot=None):
|
||||
def _provide_share_server_for_share_group(self, context, share_network_id,
|
||||
share_group_ref,
|
||||
share_group_snapshot=None):
|
||||
"""Gets or creates share_server and updates share with its id.
|
||||
|
||||
Active share_server can be deleted if there are no dependent shares
|
||||
@ -567,27 +568,28 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
should be found or created. If
|
||||
share_network_id is None method use
|
||||
share_network_id from provided snapshot.
|
||||
:param cg_ref: Consistency Group model
|
||||
:param cgsnapshot: Optional -- CGSnapshot model
|
||||
:param share_group_ref: Share Group model
|
||||
:param share_group_snapshot: Optional -- ShareGroupSnapshot model
|
||||
|
||||
:returns: dict, dict -- first value is share_server, that
|
||||
has been chosen for consistency group schedule.
|
||||
Second value is consistency group updated with
|
||||
has been chosen for share group schedule.
|
||||
Second value is share group updated with
|
||||
share_server_id.
|
||||
"""
|
||||
if not (share_network_id or cgsnapshot):
|
||||
if not (share_network_id or share_group_snapshot):
|
||||
msg = _("'share_network_id' parameter or 'snapshot'"
|
||||
" should be provided. ")
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
def error(msg, *args):
|
||||
LOG.error(msg, *args)
|
||||
self.db.consistency_group_update(
|
||||
context, cg_ref['id'], {'status': constants.STATUS_ERROR})
|
||||
self.db.share_group_update(
|
||||
context, share_group_ref['id'],
|
||||
{'status': constants.STATUS_ERROR})
|
||||
|
||||
@utils.synchronized("share_manager_%s" % share_network_id,
|
||||
external=True)
|
||||
def _provide_share_server_for_cg():
|
||||
def _provide_share_server_for_share_group():
|
||||
try:
|
||||
available_share_servers = (
|
||||
self.db.share_server_get_all_by_host_and_share_net_valid(
|
||||
@ -596,14 +598,14 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
available_share_servers = None
|
||||
|
||||
compatible_share_server = None
|
||||
choose_share_server = (
|
||||
self.driver.choose_share_server_compatible_with_share_group)
|
||||
|
||||
if available_share_servers:
|
||||
try:
|
||||
compatible_share_server = (
|
||||
self.driver.choose_share_server_compatible_with_cg(
|
||||
context, available_share_servers, cg_ref,
|
||||
cgsnapshot=cgsnapshot
|
||||
)
|
||||
compatible_share_server = choose_share_server(
|
||||
context, available_share_servers, share_group_ref,
|
||||
share_group_snapshot=share_group_snapshot,
|
||||
)
|
||||
except Exception as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
@ -620,16 +622,16 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
}
|
||||
)
|
||||
|
||||
msg = ("Using share_server %(share_server)s for consistency "
|
||||
"group %(cg_id)s")
|
||||
msg = ("Using share_server %(share_server)s for share "
|
||||
"group %(group_id)s")
|
||||
LOG.debug(msg, {
|
||||
'share_server': compatible_share_server['id'],
|
||||
'cg_id': cg_ref['id']
|
||||
'group_id': share_group_ref['id']
|
||||
})
|
||||
|
||||
updated_cg = self.db.consistency_group_update(
|
||||
updated_share_group = self.db.share_group_update(
|
||||
context,
|
||||
cg_ref['id'],
|
||||
share_group_ref['id'],
|
||||
{'share_server_id': compatible_share_server['id']},
|
||||
)
|
||||
|
||||
@ -642,9 +644,9 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
LOG.info(_LI("Used preexisting share server "
|
||||
"'%(share_server_id)s'"),
|
||||
{'share_server_id': compatible_share_server['id']})
|
||||
return compatible_share_server, updated_cg
|
||||
return compatible_share_server, updated_share_group
|
||||
|
||||
return _provide_share_server_for_cg()
|
||||
return _provide_share_server_for_share_group()
|
||||
|
||||
def _get_share_server(self, context, share_instance):
|
||||
if share_instance['share_server_id']:
|
||||
@ -1526,10 +1528,10 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
snapshot_ref = None
|
||||
parent_share_server_id = None
|
||||
|
||||
consistency_group_ref = None
|
||||
if share_instance.get('consistency_group_id'):
|
||||
consistency_group_ref = self.db.consistency_group_get(
|
||||
context, share_instance['consistency_group_id'])
|
||||
share_group_ref = None
|
||||
if share_instance.get('share_group_id'):
|
||||
share_group_ref = self.db.share_group_get(
|
||||
context, share_instance['share_group_id'])
|
||||
|
||||
if share_network_id or parent_share_server_id:
|
||||
try:
|
||||
@ -1537,7 +1539,7 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
self._provide_share_server_for_share(
|
||||
context, share_network_id, share_instance,
|
||||
snapshot=snapshot_ref,
|
||||
consistency_group=consistency_group_ref
|
||||
share_group=share_group_ref,
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
@ -3313,30 +3315,32 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
LOG.info(_LI("Shrink share completed successfully."), resource=share)
|
||||
|
||||
@utils.require_driver_initialized
|
||||
def create_consistency_group(self, context, cg_id):
|
||||
def create_share_group(self, context, share_group_id):
|
||||
context = context.elevated()
|
||||
group_ref = self.db.consistency_group_get(context, cg_id)
|
||||
group_ref['host'] = self.host
|
||||
shares = self.db.share_instances_get_all_by_consistency_group_id(
|
||||
context, cg_id)
|
||||
share_group_ref = self.db.share_group_get(context, share_group_id)
|
||||
share_group_ref['host'] = self.host
|
||||
shares = self.db.share_instances_get_all_by_share_group_id(
|
||||
context, share_group_id)
|
||||
|
||||
source_cgsnapshot_id = group_ref.get("source_cgsnapshot_id")
|
||||
source_share_group_snapshot_id = share_group_ref.get(
|
||||
"source_share_group_snapshot_id")
|
||||
snap_ref = None
|
||||
parent_share_server_id = None
|
||||
if source_cgsnapshot_id:
|
||||
snap_ref = self.db.cgsnapshot_get(context, source_cgsnapshot_id)
|
||||
for member in snap_ref['cgsnapshot_members']:
|
||||
if source_share_group_snapshot_id:
|
||||
snap_ref = self.db.share_group_snapshot_get(
|
||||
context, source_share_group_snapshot_id)
|
||||
for member in snap_ref['share_group_snapshot_members']:
|
||||
member['share'] = self.db.share_instance_get(
|
||||
context, member['share_instance_id'], with_share_data=True)
|
||||
member['share_id'] = member['share_instance_id']
|
||||
if 'consistency_group' in snap_ref:
|
||||
parent_share_server_id = snap_ref['consistency_group'][
|
||||
if 'share_group' in snap_ref:
|
||||
parent_share_server_id = snap_ref['share_group'][
|
||||
'share_server_id']
|
||||
|
||||
status = constants.STATUS_AVAILABLE
|
||||
model_update = False
|
||||
|
||||
share_network_id = group_ref.get('share_network_id', None)
|
||||
share_network_id = share_group_ref.get('share_network_id')
|
||||
share_server = None
|
||||
|
||||
if parent_share_server_id and self.driver.driver_handles_share_servers:
|
||||
@ -3345,44 +3349,47 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
share_network_id = share_server['share_network_id']
|
||||
|
||||
if share_network_id and not self.driver.driver_handles_share_servers:
|
||||
self.db.consistency_group_update(
|
||||
context, cg_id, {'status': constants.STATUS_ERROR})
|
||||
self.db.share_group_update(
|
||||
context, share_group_id, {'status': constants.STATUS_ERROR})
|
||||
msg = _("Driver does not expect share-network to be provided "
|
||||
"with current configuration.")
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
if not share_server and share_network_id:
|
||||
try:
|
||||
share_server, group_ref = self._provide_share_server_for_cg(
|
||||
context, share_network_id, group_ref, cgsnapshot=snap_ref
|
||||
share_server, share_group_ref = (
|
||||
self._provide_share_server_for_share_group(
|
||||
context, share_network_id, share_group_ref,
|
||||
share_group_snapshot=snap_ref,
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE("Failed to get share server"
|
||||
" for consistency group creation."))
|
||||
self.db.consistency_group_update(
|
||||
context, cg_id, {'status': constants.STATUS_ERROR})
|
||||
" for share group creation."))
|
||||
self.db.share_group_update(
|
||||
context, share_group_id,
|
||||
{'status': constants.STATUS_ERROR})
|
||||
|
||||
try:
|
||||
# TODO(ameade): Add notification for create.start
|
||||
LOG.info(_LI("Consistency group %s: creating"), cg_id)
|
||||
LOG.info(_LI("Share group %s: creating"), share_group_id)
|
||||
|
||||
model_update, share_update_list = None, None
|
||||
|
||||
group_ref['shares'] = shares
|
||||
share_group_ref['shares'] = shares
|
||||
if snap_ref:
|
||||
model_update, share_update_list = (
|
||||
self.driver.create_consistency_group_from_cgsnapshot(
|
||||
context, group_ref, snap_ref,
|
||||
self.driver.create_share_group_from_share_group_snapshot(
|
||||
context, share_group_ref, snap_ref,
|
||||
share_server=share_server))
|
||||
else:
|
||||
model_update = self.driver.create_consistency_group(
|
||||
context, group_ref, share_server=share_server)
|
||||
model_update = self.driver.create_share_group(
|
||||
context, share_group_ref, share_server=share_server)
|
||||
|
||||
if model_update:
|
||||
group_ref = self.db.consistency_group_update(context,
|
||||
group_ref['id'],
|
||||
model_update)
|
||||
share_group_ref = self.db.share_group_update(
|
||||
context, share_group_ref['id'], model_update)
|
||||
|
||||
if share_update_list:
|
||||
for share in share_update_list:
|
||||
@ -3396,134 +3403,133 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.db.consistency_group_update(
|
||||
self.db.share_group_update(
|
||||
context,
|
||||
group_ref['id'],
|
||||
share_group_ref['id'],
|
||||
{'status': constants.STATUS_ERROR})
|
||||
for share in shares:
|
||||
self.db.share_instance_update(
|
||||
context, share['id'],
|
||||
{'status': constants.STATUS_ERROR})
|
||||
LOG.error(_LE("Consistency group %s: create failed"), cg_id)
|
||||
LOG.error(_LE("Share group %s: create failed"), share_group_id)
|
||||
|
||||
now = timeutils.utcnow()
|
||||
for share in shares:
|
||||
self.db.share_instance_update(
|
||||
context, share['id'], {'status': constants.STATUS_AVAILABLE})
|
||||
self.db.consistency_group_update(context,
|
||||
group_ref['id'],
|
||||
self.db.share_group_update(context,
|
||||
share_group_ref['id'],
|
||||
{'status': status,
|
||||
'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
|
||||
|
||||
return group_ref['id']
|
||||
return share_group_ref['id']
|
||||
|
||||
@utils.require_driver_initialized
|
||||
def delete_consistency_group(self, context, cg_id):
|
||||
def delete_share_group(self, context, share_group_id):
|
||||
context = context.elevated()
|
||||
group_ref = self.db.consistency_group_get(context, cg_id)
|
||||
group_ref['host'] = self.host
|
||||
group_ref['shares'] = (
|
||||
self.db.share_instances_get_all_by_consistency_group_id(
|
||||
context, cg_id))
|
||||
share_group_ref = self.db.share_group_get(context, share_group_id)
|
||||
share_group_ref['host'] = self.host
|
||||
share_group_ref['shares'] = (
|
||||
self.db.share_instances_get_all_by_share_group_id(
|
||||
context, share_group_id))
|
||||
|
||||
model_update = False
|
||||
|
||||
# TODO(ameade): Add notification for delete.start
|
||||
|
||||
try:
|
||||
LOG.info(_LI("Consistency group %s: deleting"), cg_id)
|
||||
LOG.info(_LI("Share group %s: deleting"), share_group_id)
|
||||
share_server = None
|
||||
if group_ref.get('share_server_id'):
|
||||
if share_group_ref.get('share_server_id'):
|
||||
share_server = self.db.share_server_get(
|
||||
context, group_ref['share_server_id'])
|
||||
model_update = self.driver.delete_consistency_group(
|
||||
context, group_ref, share_server=share_server)
|
||||
context, share_group_ref['share_server_id'])
|
||||
model_update = self.driver.delete_share_group(
|
||||
context, share_group_ref, share_server=share_server)
|
||||
|
||||
if model_update:
|
||||
group_ref = self.db.consistency_group_update(
|
||||
context, group_ref['id'], model_update)
|
||||
share_group_ref = self.db.share_group_update(
|
||||
context, share_group_ref['id'], model_update)
|
||||
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.db.consistency_group_update(
|
||||
self.db.share_group_update(
|
||||
context,
|
||||
group_ref['id'],
|
||||
share_group_ref['id'],
|
||||
{'status': constants.STATUS_ERROR})
|
||||
LOG.error(_LE("Consistency group %s: delete failed"),
|
||||
group_ref['id'])
|
||||
LOG.error(_LE("Share group %s: delete failed"),
|
||||
share_group_ref['id'])
|
||||
|
||||
self.db.consistency_group_destroy(context,
|
||||
cg_id)
|
||||
LOG.info(_LI("Consistency group %s: deleted successfully"),
|
||||
cg_id)
|
||||
self.db.share_group_destroy(context, share_group_id)
|
||||
LOG.info(_LI("Share group %s: deleted successfully"), share_group_id)
|
||||
|
||||
# TODO(ameade): Add notification for delete.end
|
||||
|
||||
@utils.require_driver_initialized
|
||||
def create_cgsnapshot(self, context, cgsnapshot_id):
|
||||
def create_share_group_snapshot(self, context, share_group_snapshot_id):
|
||||
context = context.elevated()
|
||||
snap_ref = self.db.cgsnapshot_get(context, cgsnapshot_id)
|
||||
for member in snap_ref['cgsnapshot_members']:
|
||||
snap_ref = self.db.share_group_snapshot_get(
|
||||
context, share_group_snapshot_id)
|
||||
for member in snap_ref['share_group_snapshot_members']:
|
||||
member['share'] = self.db.share_instance_get(
|
||||
context, member['share_instance_id'], with_share_data=True)
|
||||
member['share_id'] = member['share_instance_id']
|
||||
|
||||
status = constants.STATUS_AVAILABLE
|
||||
snapshot_update = False
|
||||
|
||||
try:
|
||||
LOG.info(_LI("Consistency group snapshot %s: creating"),
|
||||
cgsnapshot_id)
|
||||
LOG.info(_LI("Share group snapshot %s: creating"),
|
||||
share_group_snapshot_id)
|
||||
share_server = None
|
||||
if snap_ref['consistency_group'].get('share_server_id'):
|
||||
if snap_ref['share_group'].get('share_server_id'):
|
||||
share_server = self.db.share_server_get(
|
||||
context, snap_ref['consistency_group']['share_server_id'])
|
||||
context, snap_ref['share_group']['share_server_id'])
|
||||
snapshot_update, member_update_list = (
|
||||
self.driver.create_cgsnapshot(context, snap_ref,
|
||||
share_server=share_server))
|
||||
self.driver.create_share_group_snapshot(
|
||||
context, snap_ref, share_server=share_server))
|
||||
|
||||
if member_update_list:
|
||||
snapshot_update = snapshot_update or {}
|
||||
snapshot_update['cgsnapshot_members'] = []
|
||||
snapshot_update['share_group_snapshot_members'] = []
|
||||
for update in (member_update_list or []):
|
||||
snapshot_update['cgsnapshot_members'].append(update)
|
||||
snapshot_update['share_group_snapshot_members'].append(
|
||||
update)
|
||||
|
||||
if snapshot_update:
|
||||
snap_ref = self.db.cgsnapshot_update(
|
||||
snap_ref = self.db.share_group_snapshot_update(
|
||||
context, snap_ref['id'], snapshot_update)
|
||||
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.db.cgsnapshot_update(
|
||||
self.db.share_group_snapshot_update(
|
||||
context,
|
||||
snap_ref['id'],
|
||||
{'status': constants.STATUS_ERROR})
|
||||
LOG.error(_LE("Consistency group snapshot %s: create failed"),
|
||||
cgsnapshot_id)
|
||||
LOG.error(_LE("Share group snapshot %s: create failed"),
|
||||
share_group_snapshot_id)
|
||||
|
||||
now = timeutils.utcnow()
|
||||
for member in (snap_ref.get('cgsnapshot_members') or []):
|
||||
for member in (snap_ref.get('share_group_snapshot_members') or []):
|
||||
update = {'status': status, 'created_at': now}
|
||||
self.db.cgsnapshot_member_update(context, member['id'],
|
||||
update)
|
||||
self.db.share_group_snapshot_member_update(
|
||||
context, member['id'], update)
|
||||
|
||||
self.db.cgsnapshot_update(context,
|
||||
snap_ref['id'],
|
||||
{'status': status,
|
||||
'created_at': now})
|
||||
LOG.info(_LI("Consistency group snapshot %s: created successfully"),
|
||||
cgsnapshot_id)
|
||||
self.db.share_group_snapshot_update(
|
||||
context, snap_ref['id'],
|
||||
{'status': status, 'created_at': now})
|
||||
LOG.info(_LI("Share group snapshot %s: created successfully"),
|
||||
share_group_snapshot_id)
|
||||
|
||||
return snap_ref['id']
|
||||
|
||||
@utils.require_driver_initialized
|
||||
def delete_cgsnapshot(self, context, cgsnapshot_id):
|
||||
def delete_share_group_snapshot(self, context, share_group_snapshot_id):
|
||||
context = context.elevated()
|
||||
snap_ref = self.db.cgsnapshot_get(context, cgsnapshot_id)
|
||||
for member in snap_ref['cgsnapshot_members']:
|
||||
snap_ref = self.db.share_group_snapshot_get(
|
||||
context, share_group_snapshot_id)
|
||||
for member in snap_ref['share_group_snapshot_members']:
|
||||
member['share'] = self.db.share_instance_get(
|
||||
context, member['share_instance_id'], with_share_data=True)
|
||||
member['share_id'] = member['share_instance_id']
|
||||
@ -3531,41 +3537,41 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
snapshot_update = False
|
||||
|
||||
try:
|
||||
LOG.info(_LI("Consistency group snapshot %s: deleting"),
|
||||
cgsnapshot_id)
|
||||
LOG.info(_LI("Share group snapshot %s: deleting"),
|
||||
share_group_snapshot_id)
|
||||
|
||||
share_server = None
|
||||
if snap_ref['consistency_group'].get('share_server_id'):
|
||||
if snap_ref['share_group'].get('share_server_id'):
|
||||
share_server = self.db.share_server_get(
|
||||
context, snap_ref['consistency_group']['share_server_id'])
|
||||
context, snap_ref['share_group']['share_server_id'])
|
||||
|
||||
snapshot_update, member_update_list = (
|
||||
self.driver.delete_cgsnapshot(context, snap_ref,
|
||||
share_server=share_server))
|
||||
self.driver.delete_share_group_snapshot(
|
||||
context, snap_ref, share_server=share_server))
|
||||
|
||||
if member_update_list:
|
||||
snapshot_update = snapshot_update or {}
|
||||
snapshot_update['cgsnapshot_members'] = []
|
||||
snapshot_update['share_group_snapshot_members'] = []
|
||||
for update in (member_update_list or []):
|
||||
snapshot_update['cgsnapshot_members'].append(update)
|
||||
snapshot_update['share_group_snapshot_members'].append(update)
|
||||
|
||||
if snapshot_update:
|
||||
snap_ref = self.db.cgsnapshot_update(
|
||||
snap_ref = self.db.share_group_snapshot_update(
|
||||
context, snap_ref['id'], snapshot_update)
|
||||
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.db.cgsnapshot_update(
|
||||
self.db.share_group_snapshot_update(
|
||||
context,
|
||||
snap_ref['id'],
|
||||
{'status': constants.STATUS_ERROR})
|
||||
LOG.error(_LE("Consistency group snapshot %s: delete failed"),
|
||||
LOG.error(_LE("Share group snapshot %s: delete failed"),
|
||||
snap_ref['name'])
|
||||
|
||||
self.db.cgsnapshot_destroy(context, cgsnapshot_id)
|
||||
self.db.share_group_snapshot_destroy(context, share_group_snapshot_id)
|
||||
|
||||
LOG.info(_LI("Consistency group snapshot %s: deleted successfully"),
|
||||
cgsnapshot_id)
|
||||
LOG.info(_LI("Share group snapshot %s: deleted successfully"),
|
||||
share_group_snapshot_id)
|
||||
|
||||
def _get_share_replica_dict(self, context, share_replica):
|
||||
# TODO(gouthamr): remove method when the db layer returns primitives
|
||||
@ -3596,9 +3602,9 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
'share_proto': share_replica.get('share_proto'),
|
||||
'share_type_id': share_replica.get('share_type_id'),
|
||||
'is_public': share_replica.get('is_public'),
|
||||
'consistency_group_id': share_replica.get('consistency_group_id'),
|
||||
'source_cgsnapshot_member_id': share_replica.get(
|
||||
'source_cgsnapshot_member_id'),
|
||||
'share_group_id': share_replica.get('share_group_id'),
|
||||
'source_share_group_snapshot_member_id': share_replica.get(
|
||||
'source_share_group_snapshot_member_id'),
|
||||
}
|
||||
|
||||
return share_replica_ref
|
||||
|
@ -69,6 +69,10 @@ class ShareAPI(object):
|
||||
1.14 - Add update_access() and remove allow_access() and deny_access().
|
||||
1.15 - Updated migration_start() method with new parameter
|
||||
"preserve_snapshots"
|
||||
1.16 - Convert create_consistency_group, delete_consistency_group
|
||||
create_cgsnapshot, and delete_cgsnapshot methods to
|
||||
create_share_group, delete_share_group
|
||||
create_share_group_snapshot, and delete_share_group_snapshot
|
||||
"""
|
||||
|
||||
BASE_RPC_API_VERSION = '1.0'
|
||||
@ -77,7 +81,7 @@ class ShareAPI(object):
|
||||
super(ShareAPI, self).__init__()
|
||||
target = messaging.Target(topic=CONF.share_topic,
|
||||
version=self.BASE_RPC_API_VERSION)
|
||||
self.client = rpc.get_client(target, version_cap='1.15')
|
||||
self.client = rpc.get_client(target, version_cap='1.16')
|
||||
|
||||
def create_share_instance(self, context, share_instance, host,
|
||||
request_spec, filter_properties,
|
||||
@ -232,33 +236,31 @@ class ShareAPI(object):
|
||||
share_id=share['id'],
|
||||
new_size=new_size)
|
||||
|
||||
def create_consistency_group(self, context, cg, host):
|
||||
def create_share_group(self, context, share_group, host):
|
||||
new_host = utils.extract_host(host)
|
||||
call_context = self.client.prepare(server=new_host, version='1.5')
|
||||
call_context.cast(context,
|
||||
'create_consistency_group',
|
||||
cg_id=cg['id'])
|
||||
call_context = self.client.prepare(server=new_host, version='1.16')
|
||||
call_context.cast(
|
||||
context, 'create_share_group', share_group_id=share_group['id'])
|
||||
|
||||
def delete_consistency_group(self, context, cg):
|
||||
new_host = utils.extract_host(cg['host'])
|
||||
call_context = self.client.prepare(server=new_host, version='1.5')
|
||||
call_context.cast(context,
|
||||
'delete_consistency_group',
|
||||
cg_id=cg['id'])
|
||||
def delete_share_group(self, context, share_group):
|
||||
new_host = utils.extract_host(share_group['host'])
|
||||
call_context = self.client.prepare(server=new_host, version='1.16')
|
||||
call_context.cast(
|
||||
context, 'delete_share_group', share_group_id=share_group['id'])
|
||||
|
||||
def create_cgsnapshot(self, context, cgsnapshot, host):
|
||||
def create_share_group_snapshot(self, context, share_group_snapshot, host):
|
||||
new_host = utils.extract_host(host)
|
||||
call_context = self.client.prepare(server=new_host, version='1.5')
|
||||
call_context.cast(context,
|
||||
'create_cgsnapshot',
|
||||
cgsnapshot_id=cgsnapshot['id'])
|
||||
call_context = self.client.prepare(server=new_host, version='1.16')
|
||||
call_context.cast(
|
||||
context, 'create_share_group_snapshot',
|
||||
share_group_snapshot_id=share_group_snapshot['id'])
|
||||
|
||||
def delete_cgsnapshot(self, context, cgsnapshot, host):
|
||||
def delete_share_group_snapshot(self, context, share_group_snapshot, host):
|
||||
new_host = utils.extract_host(host)
|
||||
call_context = self.client.prepare(server=new_host, version='1.5')
|
||||
call_context.cast(context,
|
||||
'delete_cgsnapshot',
|
||||
cgsnapshot_id=cgsnapshot['id'])
|
||||
call_context = self.client.prepare(server=new_host, version='1.16')
|
||||
call_context.cast(
|
||||
context, 'delete_share_group_snapshot',
|
||||
share_group_snapshot_id=share_group_snapshot['id'])
|
||||
|
||||
def create_share_replica(self, context, share_replica, host,
|
||||
request_spec, filter_properties):
|
||||
|
@ -14,7 +14,7 @@
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Handles all requests relating to consistency groups.
|
||||
Handles all requests relating to share groups.
|
||||
"""
|
||||
|
||||
from oslo_config import cfg
|
||||
@ -48,28 +48,31 @@ class API(base.Base):
|
||||
super(API, self).__init__(db_driver)
|
||||
|
||||
def create(self, context, name=None, description=None,
|
||||
share_type_ids=None, source_cgsnapshot_id=None,
|
||||
share_network_id=None):
|
||||
"""Create new consistency group."""
|
||||
share_type_ids=None, source_share_group_snapshot_id=None,
|
||||
share_network_id=None, share_group_type_id=None):
|
||||
"""Create new share group."""
|
||||
|
||||
cgsnapshot = None
|
||||
original_cg = None
|
||||
# NOTE(gouthamr): share_server_id is inherited from the parent CG if a
|
||||
# CG snapshot is specified, else, it will be set in the share manager.
|
||||
share_group_snapshot = None
|
||||
original_share_group = None
|
||||
# NOTE(gouthamr): share_server_id is inherited from the
|
||||
# parent share group if a share group snapshot is specified,
|
||||
# else, it will be set in the share manager.
|
||||
share_server_id = None
|
||||
if source_cgsnapshot_id:
|
||||
cgsnapshot = self.db.cgsnapshot_get(context, source_cgsnapshot_id)
|
||||
if cgsnapshot['status'] != constants.STATUS_AVAILABLE:
|
||||
msg = (_("Consistency group snapshot status must be %s")
|
||||
if source_share_group_snapshot_id:
|
||||
share_group_snapshot = self.db.share_group_snapshot_get(
|
||||
context, source_share_group_snapshot_id)
|
||||
if share_group_snapshot['status'] != constants.STATUS_AVAILABLE:
|
||||
msg = (_("Share group snapshot status must be %s.")
|
||||
% constants.STATUS_AVAILABLE)
|
||||
raise exception.InvalidCGSnapshot(reason=msg)
|
||||
raise exception.InvalidShareGroupSnapshot(reason=msg)
|
||||
|
||||
original_cg = self.db.consistency_group_get(context, cgsnapshot[
|
||||
'consistency_group_id'])
|
||||
share_type_ids = [s['share_type_id'] for s in original_cg[
|
||||
'share_types']]
|
||||
share_network_id = original_cg['share_network_id']
|
||||
share_server_id = original_cg['share_server_id']
|
||||
original_share_group = self.db.share_group_get(
|
||||
context, share_group_snapshot['share_group_id'])
|
||||
share_type_ids = [
|
||||
s['share_type_id']
|
||||
for s in original_share_group['share_types']]
|
||||
share_network_id = original_share_group['share_network_id']
|
||||
share_server_id = original_share_group['share_server_id']
|
||||
|
||||
# Get share_type_objects
|
||||
share_type_objects = []
|
||||
@ -79,7 +82,7 @@ class API(base.Base):
|
||||
share_type_object = share_types.get_share_type(
|
||||
context, share_type_id)
|
||||
except exception.ShareTypeNotFound:
|
||||
msg = _("Share type with id %s could not be found")
|
||||
msg = _("Share type with id %s could not be found.")
|
||||
raise exception.InvalidInput(msg % share_type_id)
|
||||
share_type_objects.append(share_type_object)
|
||||
|
||||
@ -112,14 +115,30 @@ class API(base.Base):
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
if (driver_handles_share_servers and
|
||||
not (source_cgsnapshot_id or share_network_id)):
|
||||
not (source_share_group_snapshot_id or share_network_id)):
|
||||
msg = _("When using a share type with the "
|
||||
"driver_handles_share_servers extra spec as "
|
||||
"True, a share_network_id must be provided.")
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
try:
|
||||
share_group_type = self.db.share_group_type_get(
|
||||
context, share_group_type_id)
|
||||
except exception.ShareGroupTypeNotFound:
|
||||
msg = _("The specified share group type %s does not exist.")
|
||||
raise exception.InvalidInput(reason=msg % share_group_type_id)
|
||||
|
||||
supported_share_types = set(
|
||||
[x['share_type_id'] for x in share_group_type['share_types']])
|
||||
|
||||
if not set(share_type_ids or []) <= supported_share_types:
|
||||
msg = _("The specified share types must be a subset of the share "
|
||||
"types supported by the share group type.")
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
options = {
|
||||
'source_cgsnapshot_id': source_cgsnapshot_id,
|
||||
'share_group_type_id': share_group_type_id,
|
||||
'source_share_group_snapshot_id': source_share_group_snapshot_id,
|
||||
'share_network_id': share_network_id,
|
||||
'share_server_id': share_server_id,
|
||||
'name': name,
|
||||
@ -127,210 +146,216 @@ class API(base.Base):
|
||||
'user_id': context.user_id,
|
||||
'project_id': context.project_id,
|
||||
'status': constants.STATUS_CREATING,
|
||||
'share_types': share_type_ids
|
||||
'share_types': share_type_ids or supported_share_types
|
||||
}
|
||||
if original_cg:
|
||||
options['host'] = original_cg['host']
|
||||
if original_share_group:
|
||||
options['host'] = original_share_group['host']
|
||||
|
||||
cg = self.db.consistency_group_create(context, options)
|
||||
share_group = self.db.share_group_create(context, options)
|
||||
|
||||
try:
|
||||
if cgsnapshot:
|
||||
members = self.db.cgsnapshot_members_get_all(
|
||||
context, source_cgsnapshot_id)
|
||||
if share_group_snapshot:
|
||||
members = self.db.share_group_snapshot_members_get_all(
|
||||
context, source_share_group_snapshot_id)
|
||||
for member in members:
|
||||
share = self.db.share_get(context, member['share_id'])
|
||||
share_type = share_types.get_share_type(
|
||||
context, member['share_type_id'])
|
||||
context, share['share_type_id'])
|
||||
member['share_instance'] = self.db.share_instance_get(
|
||||
context, member['share_instance_id'],
|
||||
with_share_data=True)
|
||||
self.share_api.create(context, member['share_proto'],
|
||||
member['size'], None, None,
|
||||
consistency_group_id=cg['id'],
|
||||
cgsnapshot_member=member,
|
||||
share_group_id=share_group['id'],
|
||||
share_group_snapshot_member=member,
|
||||
share_type=share_type,
|
||||
share_network_id=share_network_id)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.db.consistency_group_destroy(context.elevated(), cg['id'])
|
||||
self.db.share_group_destroy(
|
||||
context.elevated(), share_group['id'])
|
||||
|
||||
request_spec = {'consistency_group_id': cg['id']}
|
||||
request_spec = {'share_group_id': share_group['id']}
|
||||
request_spec.update(options)
|
||||
request_spec['share_types'] = share_type_objects
|
||||
request_spec['resource_type'] = share_group_type
|
||||
|
||||
if cgsnapshot and original_cg:
|
||||
self.share_rpcapi.create_consistency_group(
|
||||
context, cg, original_cg['host'])
|
||||
if share_group_snapshot and original_share_group:
|
||||
self.share_rpcapi.create_share_group(
|
||||
context, share_group, original_share_group['host'])
|
||||
else:
|
||||
self.scheduler_rpcapi.create_consistency_group(
|
||||
context, cg_id=cg['id'], request_spec=request_spec,
|
||||
filter_properties={})
|
||||
self.scheduler_rpcapi.create_share_group(
|
||||
context, share_group_id=share_group['id'],
|
||||
request_spec=request_spec, filter_properties={})
|
||||
|
||||
return cg
|
||||
return share_group
|
||||
|
||||
def delete(self, context, cg):
|
||||
"""Delete consistency group."""
|
||||
def delete(self, context, share_group):
|
||||
"""Delete share group."""
|
||||
|
||||
cg_id = cg['id']
|
||||
if not cg['host']:
|
||||
self.db.consistency_group_destroy(context.elevated(), cg_id)
|
||||
share_group_id = share_group['id']
|
||||
if not share_group['host']:
|
||||
self.db.share_group_destroy(context.elevated(), share_group_id)
|
||||
return
|
||||
|
||||
statuses = (constants.STATUS_AVAILABLE, constants.STATUS_ERROR)
|
||||
if not cg['status'] in statuses:
|
||||
msg = (_("Consistency group status must be one of %(statuses)s")
|
||||
if not share_group['status'] in statuses:
|
||||
msg = (_("Share group status must be one of %(statuses)s")
|
||||
% {"statuses": statuses})
|
||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||
raise exception.InvalidShareGroup(reason=msg)
|
||||
|
||||
# NOTE(ameade): check for cgsnapshots in the CG
|
||||
if self.db.count_cgsnapshots_in_consistency_group(context, cg_id):
|
||||
msg = (_("Cannot delete a consistency group with cgsnapshots"))
|
||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||
# NOTE(ameade): check for group_snapshots in the group
|
||||
if self.db.count_share_group_snapshots_in_share_group(
|
||||
context, share_group_id):
|
||||
msg = (_("Cannot delete a share group with snapshots"))
|
||||
raise exception.InvalidShareGroup(reason=msg)
|
||||
|
||||
# NOTE(ameade): check for shares in the CG
|
||||
if self.db.count_shares_in_consistency_group(context, cg_id):
|
||||
msg = (_("Cannot delete a consistency group with shares"))
|
||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||
# NOTE(ameade): check for shares in the share group
|
||||
if self.db.count_shares_in_share_group(context, share_group_id):
|
||||
msg = (_("Cannot delete a share group with shares"))
|
||||
raise exception.InvalidShareGroup(reason=msg)
|
||||
|
||||
cg = self.db.consistency_group_update(
|
||||
context, cg_id, {'status': constants.STATUS_DELETING})
|
||||
share_group = self.db.share_group_update(
|
||||
context, share_group_id, {'status': constants.STATUS_DELETING})
|
||||
|
||||
self.share_rpcapi.delete_consistency_group(context, cg)
|
||||
self.share_rpcapi.delete_share_group(context, share_group)
|
||||
|
||||
def update(self, context, cg, fields):
|
||||
return self.db.consistency_group_update(context, cg['id'], fields)
|
||||
def update(self, context, group, fields):
|
||||
return self.db.share_group_update(context, group['id'], fields)
|
||||
|
||||
def get(self, context, cg_id):
|
||||
return self.db.consistency_group_get(context, cg_id)
|
||||
def get(self, context, share_group_id):
|
||||
return self.db.share_group_get(context, share_group_id)
|
||||
|
||||
def get_all(self, context, detailed=True, search_opts=None):
|
||||
def get_all(self, context, detailed=True, search_opts=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
|
||||
if search_opts is None:
|
||||
search_opts = {}
|
||||
|
||||
LOG.debug("Searching for consistency_groups by: %s",
|
||||
LOG.debug("Searching for share_groups by: %s",
|
||||
six.text_type(search_opts))
|
||||
|
||||
# Get filtered list of consistency_groups
|
||||
if context.is_admin and search_opts.get('all_tenants'):
|
||||
consistency_groups = self.db.consistency_group_get_all(
|
||||
context, detailed=detailed)
|
||||
# Get filtered list of share_groups
|
||||
if search_opts.pop('all_tenants', 0) and context.is_admin:
|
||||
share_groups = self.db.share_group_get_all(
|
||||
context, detailed=detailed, filters=search_opts,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
else:
|
||||
consistency_groups = self.db.consistency_group_get_all_by_project(
|
||||
context, context.project_id, detailed=detailed)
|
||||
share_groups = self.db.share_group_get_all_by_project(
|
||||
context, context.project_id, detailed=detailed,
|
||||
filters=search_opts, sort_key=sort_key, sort_dir=sort_dir)
|
||||
|
||||
return consistency_groups
|
||||
|
||||
def create_cgsnapshot(self, context, name=None, description=None,
|
||||
consistency_group_id=None):
|
||||
"""Create new cgsnapshot."""
|
||||
return share_groups
|
||||
|
||||
def create_share_group_snapshot(self, context, name=None, description=None,
|
||||
share_group_id=None):
|
||||
"""Create new share group snapshot."""
|
||||
options = {
|
||||
'consistency_group_id': consistency_group_id,
|
||||
'share_group_id': share_group_id,
|
||||
'name': name,
|
||||
'description': description,
|
||||
'user_id': context.user_id,
|
||||
'project_id': context.project_id,
|
||||
'status': constants.STATUS_CREATING,
|
||||
}
|
||||
|
||||
cg = self.db.consistency_group_get(context, consistency_group_id)
|
||||
# Check status of CG, must be active
|
||||
if not cg['status'] == constants.STATUS_AVAILABLE:
|
||||
msg = (_("Consistency group status must be %s")
|
||||
share_group = self.db.share_group_get(context, share_group_id)
|
||||
# Check status of group, must be active
|
||||
if not share_group['status'] == constants.STATUS_AVAILABLE:
|
||||
msg = (_("Share group status must be %s")
|
||||
% constants.STATUS_AVAILABLE)
|
||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||
raise exception.InvalidShareGroup(reason=msg)
|
||||
|
||||
# Create members for every share in the CG
|
||||
shares = self.db.share_get_all_by_consistency_group_id(
|
||||
context, consistency_group_id)
|
||||
# Create members for every share in the group
|
||||
shares = self.db.share_get_all_by_share_group_id(
|
||||
context, share_group_id)
|
||||
|
||||
# Check status of all shares, they must be active in order to snap
|
||||
# the CG
|
||||
# the group
|
||||
for s in shares:
|
||||
if not s['status'] == constants.STATUS_AVAILABLE:
|
||||
msg = (_("Share %(s)s in consistency group must have status "
|
||||
"of %(status)s in order to create a CG snapshot")
|
||||
msg = (_("Share %(s)s in share group must have status "
|
||||
"of %(status)s in order to create a group snapshot")
|
||||
% {"s": s['id'],
|
||||
"status": constants.STATUS_AVAILABLE})
|
||||
raise exception.InvalidConsistencyGroup(reason=msg)
|
||||
|
||||
snap = self.db.cgsnapshot_create(context, options)
|
||||
raise exception.InvalidShareGroup(reason=msg)
|
||||
|
||||
snap = self.db.share_group_snapshot_create(context, options)
|
||||
try:
|
||||
members = []
|
||||
for s in shares:
|
||||
member_options = {
|
||||
'cgsnapshot_id': snap['id'],
|
||||
'share_group_snapshot_id': snap['id'],
|
||||
'user_id': context.user_id,
|
||||
'project_id': context.project_id,
|
||||
'status': constants.STATUS_CREATING,
|
||||
'size': s['size'],
|
||||
'share_proto': s['share_proto'],
|
||||
'share_type_id': s['share_type_id'],
|
||||
'share_id': s['id'],
|
||||
'share_instance_id': s.instance['id']
|
||||
}
|
||||
member = self.db.cgsnapshot_member_create(context,
|
||||
member_options)
|
||||
member = self.db.share_group_snapshot_member_create(
|
||||
context, member_options)
|
||||
members.append(member)
|
||||
|
||||
# Cast to share manager
|
||||
self.share_rpcapi.create_cgsnapshot(context, snap, cg['host'])
|
||||
self.share_rpcapi.create_share_group_snapshot(
|
||||
context, snap, share_group['host'])
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
# This will delete the snapshot and all of it's members
|
||||
self.db.cgsnapshot_destroy(context, snap['id'])
|
||||
self.db.share_group_snapshot_destroy(context, snap['id'])
|
||||
|
||||
return snap
|
||||
|
||||
def delete_cgsnapshot(self, context, snap):
|
||||
"""Delete consistency group snapshot."""
|
||||
|
||||
def delete_share_group_snapshot(self, context, snap):
|
||||
"""Delete share group snapshot."""
|
||||
snap_id = snap['id']
|
||||
|
||||
cg = self.db.consistency_group_get(context,
|
||||
snap['consistency_group_id'])
|
||||
|
||||
statuses = (constants.STATUS_AVAILABLE, constants.STATUS_ERROR)
|
||||
share_group = self.db.share_group_get(context, snap['share_group_id'])
|
||||
if not snap['status'] in statuses:
|
||||
msg = (_("Consistency group snapshot status must be one of"
|
||||
" %(statuses)s")
|
||||
% {"statuses": statuses})
|
||||
raise exception.InvalidCGSnapshot(reason=msg)
|
||||
msg = (_("Share group snapshot status must be one of"
|
||||
" %(statuses)s") % {"statuses": statuses})
|
||||
raise exception.InvalidShareGroupSnapshot(reason=msg)
|
||||
|
||||
self.db.cgsnapshot_update(context, snap_id,
|
||||
{'status': constants.STATUS_DELETING})
|
||||
self.db.share_group_snapshot_update(
|
||||
context, snap_id, {'status': constants.STATUS_DELETING})
|
||||
|
||||
# Cast to share manager
|
||||
self.share_rpcapi.delete_cgsnapshot(context, snap, cg['host'])
|
||||
self.share_rpcapi.delete_share_group_snapshot(
|
||||
context, snap, share_group['host'])
|
||||
|
||||
def update_cgsnapshot(self, context, cg, fields):
|
||||
return self.db.cgsnapshot_update(context, cg['id'], fields)
|
||||
def update_share_group_snapshot(self, context, share_group_snapshot,
|
||||
fields):
|
||||
return self.db.share_group_snapshot_update(
|
||||
context, share_group_snapshot['id'], fields)
|
||||
|
||||
def get_cgsnapshot(self, context, snapshot_id):
|
||||
return self.db.cgsnapshot_get(context, snapshot_id)
|
||||
|
||||
def get_all_cgsnapshots(self, context, detailed=True, search_opts=None):
|
||||
def get_share_group_snapshot(self, context, snapshot_id):
|
||||
return self.db.share_group_snapshot_get(context, snapshot_id)
|
||||
|
||||
def get_all_share_group_snapshots(self, context, detailed=True,
|
||||
search_opts=None, sort_key=None,
|
||||
sort_dir=None):
|
||||
if search_opts is None:
|
||||
search_opts = {}
|
||||
|
||||
LOG.debug("Searching for consistency group snapshots by: %s",
|
||||
LOG.debug("Searching for share group snapshots by: %s",
|
||||
six.text_type(search_opts))
|
||||
|
||||
# Get filtered list of consistency_groups
|
||||
if context.is_admin and search_opts.get('all_tenants'):
|
||||
cgsnapshots = self.db.cgsnapshot_get_all(
|
||||
context, detailed=detailed)
|
||||
# Get filtered list of share group snapshots
|
||||
if search_opts.pop('all_tenants', 0) and context.is_admin:
|
||||
share_group_snapshots = self.db.share_group_snapshot_get_all(
|
||||
context, detailed=detailed, filters=search_opts,
|
||||
sort_key=sort_key, sort_dir=sort_dir)
|
||||
else:
|
||||
cgsnapshots = self.db.cgsnapshot_get_all_by_project(
|
||||
context, context.project_id, detailed=detailed)
|
||||
|
||||
return cgsnapshots
|
||||
|
||||
def get_all_cgsnapshot_members(self, context, cgsnapshot_id):
|
||||
members = self.db.cgsnapshot_members_get_all(context,
|
||||
cgsnapshot_id)
|
||||
share_group_snapshots = (
|
||||
self.db.share_group_snapshot_get_all_by_project(
|
||||
context, context.project_id, detailed=detailed,
|
||||
filters=search_opts, sort_key=sort_key, sort_dir=sort_dir,
|
||||
)
|
||||
)
|
||||
return share_group_snapshots
|
||||
|
||||
def get_all_share_group_snapshot_members(self, context,
|
||||
share_group_snapshot_id):
|
||||
members = self.db.share_group_snapshot_members_get_all(
|
||||
context, share_group_snapshot_id)
|
||||
return members
|
||||
|
174
manila/share_group/share_group_types.py
Normal file
174
manila/share_group/share_group_types.py
Normal file
@ -0,0 +1,174 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_db import exception as db_exception
|
||||
from oslo_log import log
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from manila.common import constants
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.i18n import _LE
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def create(context, name, share_types, group_specs=None, is_public=True,
|
||||
projects=None):
|
||||
"""Creates share group types."""
|
||||
group_specs = group_specs or {}
|
||||
projects = projects or []
|
||||
try:
|
||||
type_ref = db.share_group_type_create(
|
||||
context,
|
||||
{"name": name, "group_specs": group_specs, "is_public": is_public,
|
||||
"share_types": share_types},
|
||||
projects=projects)
|
||||
except db_exception.DBError:
|
||||
LOG.exception(_LE('DB error'))
|
||||
raise exception.ShareGroupTypeCreateFailed(
|
||||
name=name, group_specs=group_specs)
|
||||
return type_ref
|
||||
|
||||
|
||||
def destroy(context, type_id):
|
||||
"""Marks share group types as deleted."""
|
||||
if id is None:
|
||||
msg = _("Share group type ID cannot be None.")
|
||||
raise exception.InvalidShareGroupType(reason=msg)
|
||||
else:
|
||||
db.share_group_type_destroy(context, type_id)
|
||||
|
||||
|
||||
def get_all(context, inactive=0, search_opts=None):
|
||||
"""Get all non-deleted share group types."""
|
||||
# TODO(ameade): Fix docstring
|
||||
search_opts = search_opts or {}
|
||||
filters = {}
|
||||
|
||||
if 'is_public' in search_opts:
|
||||
filters['is_public'] = search_opts.pop('is_public')
|
||||
|
||||
share_group_types = db.share_group_type_get_all(
|
||||
context, inactive, filters=filters)
|
||||
|
||||
if search_opts:
|
||||
LOG.debug("Searching by: %s", search_opts)
|
||||
|
||||
def _check_group_specs_match(share_group_type, searchdict):
|
||||
for k, v in searchdict.items():
|
||||
if (k not in share_group_type['group_specs'].keys()
|
||||
or share_group_type['group_specs'][k] != v):
|
||||
return False
|
||||
return True
|
||||
|
||||
# search_option to filter_name mapping.
|
||||
filter_mapping = {'group_specs': _check_group_specs_match}
|
||||
|
||||
result = {}
|
||||
for type_name, type_args in share_group_types.items():
|
||||
# go over all filters in the list
|
||||
for opt, values in search_opts.items():
|
||||
try:
|
||||
filter_func = filter_mapping[opt]
|
||||
except KeyError:
|
||||
# no such filter - ignore it, go to next filter
|
||||
continue
|
||||
else:
|
||||
if filter_func(type_args, values):
|
||||
result[type_name] = type_args
|
||||
break
|
||||
share_group_types = result
|
||||
return share_group_types
|
||||
|
||||
|
||||
def get(ctxt, type_id, expected_fields=None):
|
||||
"""Retrieves single share group type by id."""
|
||||
if type_id is None:
|
||||
msg = _("Share type ID cannot be None.")
|
||||
raise exception.InvalidShareGroupType(reason=msg)
|
||||
|
||||
if ctxt is None:
|
||||
ctxt = context.get_admin_context()
|
||||
|
||||
return db.share_group_type_get(
|
||||
ctxt, type_id, expected_fields=expected_fields)
|
||||
|
||||
|
||||
def get_by_name(context, name):
|
||||
"""Retrieves single share group type by name."""
|
||||
if name is None:
|
||||
msg = _("name cannot be None.")
|
||||
raise exception.InvalidShareGroupType(reason=msg)
|
||||
|
||||
return db.share_group_type_get_by_name(context, name)
|
||||
|
||||
|
||||
def get_by_name_or_id(context, share_group_type=None):
|
||||
if not share_group_type:
|
||||
share_group_type_ref = get_default(context)
|
||||
if not share_group_type_ref:
|
||||
msg = _("Default share group type not found.")
|
||||
raise exception.ShareGroupTypeNotFound(reason=msg)
|
||||
return share_group_type_ref
|
||||
|
||||
if uuidutils.is_uuid_like(share_group_type):
|
||||
return get(context, share_group_type)
|
||||
else:
|
||||
return get_by_name(context, share_group_type)
|
||||
|
||||
|
||||
def get_default(ctxt=None):
|
||||
"""Get the default share group type."""
|
||||
name = CONF.default_share_group_type
|
||||
if name is None:
|
||||
return {}
|
||||
if ctxt is None:
|
||||
ctxt = context.get_admin_context()
|
||||
try:
|
||||
return get_by_name(ctxt, name)
|
||||
except exception.ShareGroupTypeNotFoundByName:
|
||||
LOG.exception(
|
||||
_LE("Default share group type '%s' is not found, "
|
||||
"please check 'default_share_group_type' config."),
|
||||
name,
|
||||
)
|
||||
|
||||
|
||||
def get_tenant_visible_group_specs():
|
||||
return constants.ExtraSpecs.TENANT_VISIBLE
|
||||
|
||||
|
||||
def get_boolean_group_specs():
|
||||
return constants.ExtraSpecs.BOOLEAN
|
||||
|
||||
|
||||
def add_share_group_type_access(context, share_group_type_id, project_id):
|
||||
"""Add access to share group type for project_id."""
|
||||
if share_group_type_id is None:
|
||||
msg = _("share_group_type_id cannot be None.")
|
||||
raise exception.InvalidShareGroupType(reason=msg)
|
||||
return db.share_group_type_access_add(
|
||||
context, share_group_type_id, project_id)
|
||||
|
||||
|
||||
def remove_share_group_type_access(context, share_group_type_id, project_id):
|
||||
"""Remove access to share group type for project_id."""
|
||||
if share_group_type_id is None:
|
||||
msg = _("share_group_type_id cannot be None.")
|
||||
raise exception.InvalidShareGroupType(reason=msg)
|
||||
return db.share_group_type_access_remove(
|
||||
context, share_group_type_id, project_id)
|
@ -178,7 +178,7 @@ def stub_snapshot_get_all_by_project(self, context, search_opts=None,
|
||||
return [stub_snapshot_get(self, context, 2)]
|
||||
|
||||
|
||||
def stub_cgsnapshot_member(id, **kwargs):
|
||||
def stub_share_group_snapshot_member(id, **kwargs):
|
||||
member = {
|
||||
'id': id,
|
||||
'share_id': 'fakeshareid',
|
||||
|
@ -186,13 +186,14 @@ class APIVersionRequestTests(test.TestCase):
|
||||
self.assertRaises(ValueError,
|
||||
api_version_request.APIVersionRequest().get_string)
|
||||
|
||||
@ddt.data(('1', '0'), ('1', '1'))
|
||||
@ddt.data(('1', '0', False), ('1', '1', False), ('1', '0', True))
|
||||
@ddt.unpack
|
||||
def test_str(self, major, minor):
|
||||
def test_str(self, major, minor, experimental):
|
||||
request_input = '%s.%s' % (major, minor)
|
||||
request = api_version_request.APIVersionRequest(request_input)
|
||||
request = api_version_request.APIVersionRequest(
|
||||
request_input, experimental=experimental)
|
||||
request_string = six.text_type(request)
|
||||
|
||||
self.assertEqual('API Version Request '
|
||||
'Major: %s, Minor: %s' % (major, minor),
|
||||
request_string)
|
||||
'Major: %s, Minor: %s, Experimental: %s' %
|
||||
(major, minor, experimental), request_string)
|
||||
|
@ -24,7 +24,6 @@ import six
|
||||
import webob
|
||||
|
||||
from manila.api import common
|
||||
from manila.api.openstack import api_version_request as api_version
|
||||
from manila.api.v1 import shares
|
||||
from manila.common import constants
|
||||
from manila import context
|
||||
@ -149,22 +148,6 @@ class ShareAPITest(test.TestCase):
|
||||
expected = self._get_expected_share_detailed_response(self.share)
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
@ddt.data("2.4", "2.5")
|
||||
def test_share_create_with_consistency_group(self, microversion):
|
||||
self.mock_object(share_api.API, 'create', self.create_mock)
|
||||
body = {"share": copy.deepcopy(self.share)}
|
||||
req = fakes.HTTPRequest.blank('/shares', version=microversion)
|
||||
|
||||
res_dict = self.controller.create(req, body)
|
||||
|
||||
expected = self._get_expected_share_detailed_response(self.share)
|
||||
expected['share']['consistency_group_id'] = None
|
||||
expected['share']['source_cgsnapshot_member_id'] = None
|
||||
if (api_version.APIVersionRequest(microversion) >=
|
||||
api_version.APIVersionRequest('2.5')):
|
||||
expected['share']['task_state'] = None
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_share_create_with_valid_default_share_type(self):
|
||||
self.mock_object(share_types, 'get_share_type_by_name',
|
||||
mock.Mock(return_value=self.vt))
|
||||
@ -455,22 +438,10 @@ class ShareAPITest(test.TestCase):
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_share_show_with_consistency_group(self):
|
||||
req = fakes.HTTPRequest.blank('/shares/1', version='2.4')
|
||||
expected = self._get_expected_share_detailed_response()
|
||||
expected['share']['consistency_group_id'] = None
|
||||
expected['share']['source_cgsnapshot_member_id'] = None
|
||||
|
||||
res_dict = self.controller.show(req, '1')
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_share_show_with_share_type_name(self):
|
||||
req = fakes.HTTPRequest.blank('/shares/1', version='2.6')
|
||||
res_dict = self.controller.show(req, '1')
|
||||
expected = self._get_expected_share_detailed_response()
|
||||
expected['share']['consistency_group_id'] = None
|
||||
expected['share']['source_cgsnapshot_member_id'] = None
|
||||
expected['share']['share_type_name'] = None
|
||||
expected['share']['task_state'] = None
|
||||
self.assertEqual(expected, res_dict)
|
||||
@ -497,35 +468,6 @@ class ShareAPITest(test.TestCase):
|
||||
resp = self.controller.delete(req, 1)
|
||||
self.assertEqual(202, resp.status_int)
|
||||
|
||||
def test_share_delete_in_consistency_group_param_not_provided(self):
|
||||
fake_share = stubs.stub_share('fake_share',
|
||||
consistency_group_id='fake_cg_id')
|
||||
self.mock_object(share_api.API, 'get',
|
||||
mock.Mock(return_value=fake_share))
|
||||
req = fakes.HTTPRequest.blank('/shares/1')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.delete, req, 1)
|
||||
|
||||
def test_share_delete_in_consistency_group(self):
|
||||
fake_share = stubs.stub_share('fake_share',
|
||||
consistency_group_id='fake_cg_id')
|
||||
self.mock_object(share_api.API, 'get',
|
||||
mock.Mock(return_value=fake_share))
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/shares/1?consistency_group_id=fake_cg_id')
|
||||
resp = self.controller.delete(req, 1)
|
||||
self.assertEqual(202, resp.status_int)
|
||||
|
||||
def test_share_delete_in_consistency_group_wrong_id(self):
|
||||
fake_share = stubs.stub_share('fake_share',
|
||||
consistency_group_id='fake_cg_id')
|
||||
self.mock_object(share_api.API, 'get',
|
||||
mock.Mock(return_value=fake_share))
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/shares/1?consistency_group_id=not_fake_cg_id')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.delete, req, 1)
|
||||
|
||||
def test_share_update(self):
|
||||
shr = self.share
|
||||
body = {"share": shr}
|
||||
@ -538,15 +480,6 @@ class ShareAPITest(test.TestCase):
|
||||
self.assertEqual(shr['is_public'],
|
||||
res_dict['share']['is_public'])
|
||||
|
||||
def test_share_update_with_consistency_group(self):
|
||||
shr = self.share
|
||||
body = {"share": shr}
|
||||
|
||||
req = fakes.HTTPRequest.blank('/share/1', version="2.4")
|
||||
res_dict = self.controller.update(req, 1, body)
|
||||
self.assertIsNone(res_dict['share']["consistency_group_id"])
|
||||
self.assertIsNone(res_dict['share']["source_cgsnapshot_member_id"])
|
||||
|
||||
def test_share_not_updates_size(self):
|
||||
req = fakes.HTTPRequest.blank('/share/1')
|
||||
res_dict = self.controller.update(req, 1, {"share": self.share})
|
||||
@ -787,22 +720,11 @@ class ShareAPITest(test.TestCase):
|
||||
expected['shares'][0].pop('snapshot_support')
|
||||
self._list_detail_test_common(req, expected)
|
||||
|
||||
def test_share_list_detail_with_consistency_group(self):
|
||||
env = {'QUERY_STRING': 'name=Share+Test+Name'}
|
||||
req = fakes.HTTPRequest.blank('/shares/detail', environ=env,
|
||||
version="2.4")
|
||||
expected = self._list_detail_common_expected()
|
||||
expected['shares'][0]['consistency_group_id'] = None
|
||||
expected['shares'][0]['source_cgsnapshot_member_id'] = None
|
||||
self._list_detail_test_common(req, expected)
|
||||
|
||||
def test_share_list_detail_with_task_state(self):
|
||||
env = {'QUERY_STRING': 'name=Share+Test+Name'}
|
||||
req = fakes.HTTPRequest.blank('/shares/detail', environ=env,
|
||||
version="2.5")
|
||||
expected = self._list_detail_common_expected()
|
||||
expected['shares'][0]['consistency_group_id'] = None
|
||||
expected['shares'][0]['source_cgsnapshot_member_id'] = None
|
||||
expected['shares'][0]['task_state'] = None
|
||||
self._list_detail_test_common(req, expected)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright 2015 Alex Meade
|
||||
# Copyright 2016 Alex Meade
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -39,24 +39,23 @@ CONF = cfg.CONF
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class CGSnapshotApiTest(test.TestCase):
|
||||
class ShareGroupSnapshotAPITest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(self.__class__, self).setUp()
|
||||
self.controller = share_group_snapshots.CGSnapshotController()
|
||||
self.controller = share_group_snapshots.ShareGroupSnapshotController()
|
||||
self.resource_name = self.controller.resource_name
|
||||
self.api_version = '2.4'
|
||||
self.api_version = '2.31'
|
||||
self.mock_policy_check = self.mock_object(
|
||||
policy, 'check_policy', mock.Mock(return_value=True))
|
||||
self.request = fakes.HTTPRequest.blank('/consistency-groups',
|
||||
version=self.api_version,
|
||||
experimental=True)
|
||||
self.request = fakes.HTTPRequest.blank(
|
||||
'/share-groups', version=self.api_version, experimental=True)
|
||||
self.context = self.request.environ['manila.context']
|
||||
self.admin_context = context.RequestContext('admin', 'fake', True)
|
||||
self.member_context = context.RequestContext('fake', 'fake')
|
||||
self.flags(rpc_backend='manila.openstack.common.rpc.impl_fake')
|
||||
|
||||
def _get_fake_cgsnapshot(self, **values):
|
||||
def _get_fake_share_group_snapshot(self, **values):
|
||||
snap = {
|
||||
'id': 'fake_id',
|
||||
'user_id': 'fakeuser',
|
||||
@ -64,18 +63,18 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
'status': constants.STATUS_CREATING,
|
||||
'name': None,
|
||||
'description': None,
|
||||
'consistency_group_id': None,
|
||||
'share_group_id': None,
|
||||
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
'members': [],
|
||||
}
|
||||
|
||||
snap.update(**values)
|
||||
|
||||
expected_snap = copy.deepcopy(snap)
|
||||
del expected_snap['user_id']
|
||||
expected_snap['links'] = mock.ANY
|
||||
return snap, expected_snap
|
||||
|
||||
def _get_fake_simple_cgsnapshot(self, **values):
|
||||
def _get_fake_simple_share_group_snapshot(self, **values):
|
||||
snap = {
|
||||
'id': 'fake_id',
|
||||
'name': None,
|
||||
@ -83,18 +82,16 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
|
||||
snap.update(**values)
|
||||
expected_snap = copy.deepcopy(snap)
|
||||
expected_snap['links'] = mock.ANY
|
||||
return snap, expected_snap
|
||||
|
||||
def _get_fake_cgsnapshot_member(self, **values):
|
||||
def _get_fake_share_group_snapshot_member(self, **values):
|
||||
member = {
|
||||
'id': 'fake_id',
|
||||
'user_id': 'fakeuser',
|
||||
'project_id': 'fakeproject',
|
||||
'status': constants.STATUS_CREATING,
|
||||
'cgsnapshot_id': None,
|
||||
'share_group_snapshot_id': None,
|
||||
'share_proto': None,
|
||||
'share_type_id': None,
|
||||
'share_id': None,
|
||||
'size': None,
|
||||
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
|
||||
@ -110,110 +107,133 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
return member, expected_member
|
||||
|
||||
def test_create_invalid_body(self):
|
||||
body = {"not_cg_snapshot": {}}
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||
self.request, body)
|
||||
body = {"not_group_snapshot": {}}
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPBadRequest,
|
||||
self.controller.create, self.request, body)
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'create')
|
||||
|
||||
def test_create_no_consistency_group_id(self):
|
||||
body = {"cgnapshot": {}}
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||
self.request, body)
|
||||
def test_create_no_share_group_id(self):
|
||||
body = {"share_group_snapshot": {}}
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPBadRequest,
|
||||
self.controller.create, self.request, body)
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'create')
|
||||
|
||||
def test_create(self):
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot()
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
|
||||
fake_id = six.text_type(uuidutils.generate_uuid())
|
||||
self.mock_object(self.controller.cg_api, 'create_cgsnapshot',
|
||||
body = {"share_group_snapshot": {"share_group_id": fake_id}}
|
||||
mock_create = self.mock_object(
|
||||
self.controller.share_group_api, 'create_share_group_snapshot',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
|
||||
body = {"cgsnapshot": {"consistency_group_id": fake_id}}
|
||||
|
||||
res_dict = self.controller.create(self.request, body)
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'create')
|
||||
self.controller.cg_api.create_cgsnapshot.assert_called_once_with(
|
||||
self.context, consistency_group_id=fake_id)
|
||||
self.assertEqual(expected_snap, res_dict['cgsnapshot'])
|
||||
mock_create.assert_called_once_with(
|
||||
self.context, share_group_id=fake_id)
|
||||
res_dict['share_group_snapshot'].pop('links')
|
||||
|
||||
def test_create_cg_does_not_exist(self):
|
||||
self.assertEqual(expected_snap, res_dict['share_group_snapshot'])
|
||||
|
||||
def test_create_group_does_not_exist(self):
|
||||
fake_id = six.text_type(uuidutils.generate_uuid())
|
||||
self.mock_object(self.controller.cg_api, 'create_cgsnapshot',
|
||||
mock.Mock(
|
||||
side_effect=exception.ConsistencyGroupNotFound(
|
||||
consistency_group_id=six.text_type(
|
||||
uuidutils.generate_uuid())
|
||||
)))
|
||||
body = {"share_group_snapshot": {"share_group_id": fake_id}}
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'create_share_group_snapshot',
|
||||
mock.Mock(side_effect=exception.ShareGroupNotFound(
|
||||
share_group_id=six.text_type(uuidutils.generate_uuid()))))
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPBadRequest,
|
||||
self.controller.create, self.request, body)
|
||||
|
||||
body = {"cgsnapshot": {"consistency_group_id": fake_id}}
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||
self.request, body)
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'create')
|
||||
|
||||
def test_create_cg_does_not_a_uuid(self):
|
||||
self.mock_object(self.controller.cg_api, 'create_cgsnapshot',
|
||||
mock.Mock(
|
||||
side_effect=exception.ConsistencyGroupNotFound(
|
||||
consistency_group_id='not_a_uuid'
|
||||
def test_create_group_does_not_a_uuid(self):
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'create_share_group_snapshot',
|
||||
mock.Mock(side_effect=exception.ShareGroupNotFound(
|
||||
share_group_id='not_a_uuid',
|
||||
)))
|
||||
body = {"share_group_snapshot": {"share_group_id": "not_a_uuid"}}
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPBadRequest,
|
||||
self.controller.create, self.request, body)
|
||||
|
||||
body = {"cgsnapshot": {"consistency_group_id": "not_a_uuid"}}
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
|
||||
self.request, body)
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'create')
|
||||
|
||||
def test_create_invalid_cg(self):
|
||||
def test_create_invalid_share_group(self):
|
||||
fake_id = six.text_type(uuidutils.generate_uuid())
|
||||
self.mock_object(self.controller.cg_api, 'create_cgsnapshot',
|
||||
mock.Mock(
|
||||
side_effect=exception.InvalidConsistencyGroup(
|
||||
reason='bad_status'
|
||||
)))
|
||||
body = {"share_group_snapshot": {"share_group_id": fake_id}}
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'create_share_group_snapshot',
|
||||
mock.Mock(side_effect=exception.InvalidShareGroup(
|
||||
reason='bad_status')))
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPConflict, self.controller.create, self.request, body)
|
||||
|
||||
body = {"cgsnapshot": {"consistency_group_id": fake_id}}
|
||||
self.assertRaises(webob.exc.HTTPConflict, self.controller.create,
|
||||
self.request, body)
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'create')
|
||||
|
||||
def test_create_with_name(self):
|
||||
fake_name = 'fake_name'
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot(name=fake_name)
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot(
|
||||
name=fake_name)
|
||||
fake_id = six.text_type(uuidutils.generate_uuid())
|
||||
self.mock_object(self.controller.cg_api, 'create_cgsnapshot',
|
||||
mock_create = self.mock_object(
|
||||
self.controller.share_group_api, 'create_share_group_snapshot',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
|
||||
body = {"cgsnapshot": {"consistency_group_id": fake_id,
|
||||
"name": fake_name}}
|
||||
body = {
|
||||
"share_group_snapshot": {
|
||||
"share_group_id": fake_id,
|
||||
"name": fake_name,
|
||||
}
|
||||
}
|
||||
res_dict = self.controller.create(self.request, body)
|
||||
|
||||
self.controller.cg_api.create_cgsnapshot.assert_called_once_with(
|
||||
self.context, consistency_group_id=fake_id, name=fake_name)
|
||||
self.assertEqual(expected_snap, res_dict['cgsnapshot'])
|
||||
res_dict['share_group_snapshot'].pop('links')
|
||||
|
||||
mock_create.assert_called_once_with(
|
||||
self.context, share_group_id=fake_id, name=fake_name)
|
||||
self.assertEqual(expected_snap, res_dict['share_group_snapshot'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'create')
|
||||
|
||||
def test_create_with_description(self):
|
||||
fake_description = 'fake_description'
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot(
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot(
|
||||
description=fake_description)
|
||||
fake_id = six.text_type(uuidutils.generate_uuid())
|
||||
self.mock_object(self.controller.cg_api, 'create_cgsnapshot',
|
||||
mock_create = self.mock_object(
|
||||
self.controller.share_group_api, 'create_share_group_snapshot',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
|
||||
body = {"cgsnapshot": {"consistency_group_id": fake_id,
|
||||
"description": fake_description}}
|
||||
body = {
|
||||
"share_group_snapshot": {
|
||||
"share_group_id": fake_id,
|
||||
"description": fake_description,
|
||||
}
|
||||
}
|
||||
res_dict = self.controller.create(self.request, body)
|
||||
|
||||
self.controller.cg_api.create_cgsnapshot.assert_called_once_with(
|
||||
self.context, consistency_group_id=fake_id,
|
||||
description=fake_description)
|
||||
self.assertEqual(expected_snap, res_dict['cgsnapshot'])
|
||||
res_dict['share_group_snapshot'].pop('links')
|
||||
|
||||
mock_create.assert_called_once_with(
|
||||
self.context, share_group_id=fake_id, description=fake_description)
|
||||
self.assertEqual(expected_snap, res_dict['share_group_snapshot'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'create')
|
||||
|
||||
@ -221,20 +241,27 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
fake_name = 'fake_name'
|
||||
fake_description = 'fake_description'
|
||||
fake_id = six.text_type(uuidutils.generate_uuid())
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot(
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot(
|
||||
description=fake_description, name=fake_name)
|
||||
self.mock_object(self.controller.cg_api, 'create_cgsnapshot',
|
||||
mock_create = self.mock_object(
|
||||
self.controller.share_group_api, 'create_share_group_snapshot',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
|
||||
body = {"cgsnapshot": {"consistency_group_id": fake_id,
|
||||
body = {
|
||||
"share_group_snapshot": {
|
||||
"share_group_id": fake_id,
|
||||
"description": fake_description,
|
||||
"name": fake_name}}
|
||||
"name": fake_name,
|
||||
}
|
||||
}
|
||||
res_dict = self.controller.create(self.request, body)
|
||||
|
||||
self.controller.cg_api.create_cgsnapshot.assert_called_once_with(
|
||||
self.context, consistency_group_id=fake_id, name=fake_name,
|
||||
res_dict['share_group_snapshot'].pop('links')
|
||||
|
||||
mock_create.assert_called_once_with(
|
||||
self.context, share_group_id=fake_id, name=fake_name,
|
||||
description=fake_description)
|
||||
self.assertEqual(expected_snap, res_dict['cgsnapshot'])
|
||||
self.assertEqual(expected_snap, res_dict['share_group_snapshot'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'create')
|
||||
|
||||
@ -242,36 +269,47 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
fake_name = 'fake_name'
|
||||
fake_description = 'fake_description'
|
||||
fake_id = six.text_type(uuidutils.generate_uuid())
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot(
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot(
|
||||
description=fake_description, name=fake_name)
|
||||
self.mock_object(self.controller.cg_api, 'get_cgsnapshot',
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_share_group_snapshot',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.controller.cg_api, 'update_cgsnapshot',
|
||||
mock_update = self.mock_object(
|
||||
self.controller.share_group_api, 'update_share_group_snapshot',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
|
||||
body = {"cgsnapshot": {"description": fake_description,
|
||||
"name": fake_name}}
|
||||
body = {
|
||||
"share_group_snapshot": {
|
||||
"description": fake_description,
|
||||
"name": fake_name,
|
||||
}
|
||||
}
|
||||
res_dict = self.controller.update(self.request, fake_id, body)
|
||||
|
||||
self.controller.cg_api.update_cgsnapshot.assert_called_once_with(
|
||||
res_dict['share_group_snapshot'].pop('links')
|
||||
|
||||
mock_update.assert_called_once_with(
|
||||
self.context, fake_snap,
|
||||
dict(name=fake_name, description=fake_description))
|
||||
self.assertEqual(expected_snap, res_dict['cgsnapshot'])
|
||||
{"name": fake_name, "description": fake_description})
|
||||
self.assertEqual(expected_snap, res_dict['share_group_snapshot'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'update')
|
||||
|
||||
def test_update_snapshot_not_found(self):
|
||||
body = {"cgsnapshot": {}}
|
||||
self.mock_object(self.controller.cg_api, 'get_cgsnapshot',
|
||||
body = {"share_group_snapshot": {}}
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_share_group_snapshot',
|
||||
mock.Mock(side_effect=exception.NotFound))
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller.update,
|
||||
self.request, 'fake_id', body)
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPNotFound,
|
||||
self.controller.update, self.request, 'fake_id', body)
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'update')
|
||||
|
||||
def test_update_invalid_body(self):
|
||||
body = {"not_cgsnapshot": {}}
|
||||
body = {"not_group_snapshot": {}}
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update,
|
||||
self.request, 'fake_id', body)
|
||||
@ -279,7 +317,7 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
self.context, self.resource_name, 'update')
|
||||
|
||||
def test_update_invalid_body_invalid_field(self):
|
||||
body = {"cgsnapshot": {"unknown_field": ""}}
|
||||
body = {"share_group_snapshot": {"unknown_field": ""}}
|
||||
exc = self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update,
|
||||
self.request, 'fake_id', body)
|
||||
@ -288,7 +326,7 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
self.context, self.resource_name, 'update')
|
||||
|
||||
def test_update_invalid_body_readonly_field(self):
|
||||
body = {"cgsnapshot": {"created_at": []}}
|
||||
body = {"share_group_snapshot": {"created_at": []}}
|
||||
exc = self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update,
|
||||
self.request, 'fake_id', body)
|
||||
@ -297,116 +335,147 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
self.context, self.resource_name, 'update')
|
||||
|
||||
def test_list_index(self):
|
||||
fake_snap, expected_snap = self._get_fake_simple_cgsnapshot()
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots',
|
||||
fake_snap, expected_snap = self._get_fake_simple_share_group_snapshot()
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_all_share_group_snapshots',
|
||||
mock.Mock(return_value=[fake_snap]))
|
||||
|
||||
res_dict = self.controller.index(self.request)
|
||||
self.assertEqual([expected_snap], res_dict['cgsnapshots'])
|
||||
|
||||
res_dict['share_group_snapshots'][0].pop('links')
|
||||
|
||||
self.assertEqual([expected_snap], res_dict['share_group_snapshots'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'get_all')
|
||||
|
||||
def test_list_index_no_cgs(self):
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots',
|
||||
def test_list_index_no_share_groups(self):
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_all_share_group_snapshots',
|
||||
mock.Mock(return_value=[]))
|
||||
|
||||
res_dict = self.controller.index(self.request)
|
||||
self.assertEqual([], res_dict['cgsnapshots'])
|
||||
|
||||
self.assertEqual([], res_dict['share_group_snapshots'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'get_all')
|
||||
|
||||
def test_list_index_with_limit(self):
|
||||
fake_snap, expected_snap = self._get_fake_simple_cgsnapshot()
|
||||
fake_snap2, expected_snap2 = self._get_fake_simple_cgsnapshot(
|
||||
id="fake_id2")
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots',
|
||||
fake_snap, expected_snap = self._get_fake_simple_share_group_snapshot()
|
||||
fake_snap2, expected_snap2 = (
|
||||
self._get_fake_simple_share_group_snapshot(
|
||||
id="fake_id2"))
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_all_share_group_snapshots',
|
||||
mock.Mock(return_value=[fake_snap, fake_snap2]))
|
||||
req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1',
|
||||
req = fakes.HTTPRequest.blank('/share-group-snapshots?limit=1',
|
||||
version=self.api_version,
|
||||
experimental=True)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.index(req)
|
||||
|
||||
self.assertEqual(1, len(res_dict['cgsnapshots']))
|
||||
self.assertEqual([expected_snap], res_dict['cgsnapshots'])
|
||||
res_dict['share_group_snapshots'][0].pop('links')
|
||||
|
||||
self.assertEqual(1, len(res_dict['share_group_snapshots']))
|
||||
self.assertEqual([expected_snap], res_dict['share_group_snapshots'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'get_all')
|
||||
|
||||
def test_list_index_with_limit_and_offset(self):
|
||||
fake_snap, expected_snap = self._get_fake_simple_cgsnapshot()
|
||||
fake_snap2, expected_snap2 = self._get_fake_simple_cgsnapshot(
|
||||
id="fake_id2")
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots',
|
||||
fake_snap, expected_snap = self._get_fake_simple_share_group_snapshot()
|
||||
fake_snap2, expected_snap2 = (
|
||||
self._get_fake_simple_share_group_snapshot(id="fake_id2"))
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_all_share_group_snapshots',
|
||||
mock.Mock(return_value=[fake_snap, fake_snap2]))
|
||||
req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1&offset=1',
|
||||
version=self.api_version,
|
||||
experimental=True)
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/share-group-snapshots?limit=1&offset=1',
|
||||
version=self.api_version, experimental=True)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.index(req)
|
||||
|
||||
self.assertEqual(1, len(res_dict['cgsnapshots']))
|
||||
self.assertEqual([expected_snap2], res_dict['cgsnapshots'])
|
||||
res_dict['share_group_snapshots'][0].pop('links')
|
||||
|
||||
self.assertEqual(1, len(res_dict['share_group_snapshots']))
|
||||
self.assertEqual([expected_snap2], res_dict['share_group_snapshots'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'get_all')
|
||||
|
||||
def test_list_detail(self):
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot()
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots',
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_all_share_group_snapshots',
|
||||
mock.Mock(return_value=[fake_snap]))
|
||||
|
||||
res_dict = self.controller.detail(self.request)
|
||||
self.assertEqual([expected_snap], res_dict['cgsnapshots'])
|
||||
|
||||
res_dict['share_group_snapshots'][0].pop('links')
|
||||
|
||||
self.assertEqual(1, len(res_dict['share_group_snapshots']))
|
||||
self.assertEqual(expected_snap, res_dict['share_group_snapshots'][0])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'get_all')
|
||||
|
||||
def test_list_detail_no_cgs(self):
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots',
|
||||
def test_list_detail_no_share_groups(self):
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_all_share_group_snapshots',
|
||||
mock.Mock(return_value=[]))
|
||||
res_dict = self.controller.detail(self.request)
|
||||
self.assertEqual([], res_dict['cgsnapshots'])
|
||||
self.assertEqual([], res_dict['share_group_snapshots'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'get_all')
|
||||
|
||||
def test_list_detail_with_limit(self):
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot()
|
||||
fake_snap2, expected_snap2 = self._get_fake_cgsnapshot(
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
|
||||
fake_snap2, expected_snap2 = self._get_fake_share_group_snapshot(
|
||||
id="fake_id2")
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots',
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_all_share_group_snapshots',
|
||||
mock.Mock(return_value=[fake_snap, fake_snap2]))
|
||||
req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1',
|
||||
req = fakes.HTTPRequest.blank('/share-group-snapshots?limit=1',
|
||||
version=self.api_version,
|
||||
experimental=True)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.detail(req)
|
||||
|
||||
self.assertEqual(1, len(res_dict['cgsnapshots']))
|
||||
self.assertEqual([expected_snap], res_dict['cgsnapshots'])
|
||||
res_dict['share_group_snapshots'][0].pop('links')
|
||||
|
||||
self.assertEqual(1, len(res_dict['share_group_snapshots']))
|
||||
self.assertEqual([expected_snap], res_dict['share_group_snapshots'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'get_all')
|
||||
|
||||
def test_list_detail_with_limit_and_offset(self):
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot()
|
||||
fake_snap2, expected_snap2 = self._get_fake_cgsnapshot(
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
|
||||
fake_snap2, expected_snap2 = self._get_fake_share_group_snapshot(
|
||||
id="fake_id2")
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshots',
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_all_share_group_snapshots',
|
||||
mock.Mock(return_value=[fake_snap, fake_snap2]))
|
||||
req = fakes.HTTPRequest.blank('/cgsnapshots?limit=1&offset=1',
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/share-group-snapshots?limit=1&offset=1',
|
||||
version=self.api_version,
|
||||
experimental=True)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.detail(req)
|
||||
|
||||
self.assertEqual(1, len(res_dict['cgsnapshots']))
|
||||
self.assertEqual([expected_snap2], res_dict['cgsnapshots'])
|
||||
res_dict['share_group_snapshots'][0].pop('links')
|
||||
|
||||
self.assertEqual(1, len(res_dict['share_group_snapshots']))
|
||||
self.assertEqual([expected_snap2], res_dict['share_group_snapshots'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'get_all')
|
||||
|
||||
def test_delete(self):
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot()
|
||||
self.mock_object(self.controller.cg_api, 'get_cgsnapshot',
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_share_group_snapshot',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.controller.cg_api, 'delete_cgsnapshot')
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'delete_share_group_snapshot')
|
||||
|
||||
res = self.controller.delete(self.request, fake_snap['id'])
|
||||
|
||||
@ -415,134 +484,89 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
self.context, self.resource_name, 'delete')
|
||||
|
||||
def test_delete_not_found(self):
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot()
|
||||
self.mock_object(self.controller.cg_api, 'get_cgsnapshot',
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_share_group_snapshot',
|
||||
mock.Mock(side_effect=exception.NotFound))
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete,
|
||||
self.request, fake_snap['id'])
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPNotFound,
|
||||
self.controller.delete, self.request, fake_snap['id'])
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'delete')
|
||||
|
||||
def test_delete_in_conflicting_status(self):
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot()
|
||||
self.mock_object(self.controller.cg_api, 'get_cgsnapshot',
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_share_group_snapshot',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.controller.cg_api, 'delete_cgsnapshot',
|
||||
mock.Mock(
|
||||
side_effect=exception.InvalidCGSnapshot(
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'delete_share_group_snapshot',
|
||||
mock.Mock(side_effect=exception.InvalidShareGroupSnapshot(
|
||||
reason='blah')))
|
||||
|
||||
self.assertRaises(webob.exc.HTTPConflict, self.controller.delete,
|
||||
self.request, fake_snap['id'])
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPConflict,
|
||||
self.controller.delete, self.request, fake_snap['id'])
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'delete')
|
||||
|
||||
def test_show(self):
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot()
|
||||
self.mock_object(self.controller.cg_api, 'get_cgsnapshot',
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_share_group_snapshot',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
|
||||
res_dict = self.controller.show(self.request, fake_snap['id'])
|
||||
|
||||
self.assertEqual(expected_snap, res_dict['cgsnapshot'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'get_cgsnapshot')
|
||||
res_dict['share_group_snapshot'].pop('links')
|
||||
|
||||
def test_show_cg_not_found(self):
|
||||
fake_snap, expected_snap = self._get_fake_cgsnapshot()
|
||||
self.mock_object(self.controller.cg_api, 'get_cgsnapshot',
|
||||
self.assertEqual(expected_snap, res_dict['share_group_snapshot'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'get')
|
||||
|
||||
def test_show_share_group_not_found(self):
|
||||
fake_snap, expected_snap = self._get_fake_share_group_snapshot()
|
||||
self.mock_object(
|
||||
self.controller.share_group_api, 'get_share_group_snapshot',
|
||||
mock.Mock(side_effect=exception.NotFound))
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
|
||||
self.request, fake_snap['id'])
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPNotFound,
|
||||
self.controller.show, self.request, fake_snap['id'])
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'get_cgsnapshot')
|
||||
|
||||
def test_members_empty(self):
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshot_members',
|
||||
mock.Mock(return_value=[]))
|
||||
|
||||
res_dict = self.controller.members(self.request, 'fake_cg_id')
|
||||
|
||||
self.assertEqual([], res_dict['cgsnapshot_members'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'get_cgsnapshot')
|
||||
|
||||
def test_members(self):
|
||||
fake_member, expected_member = self._get_fake_cgsnapshot_member()
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshot_members',
|
||||
mock.Mock(return_value=[fake_member]))
|
||||
|
||||
res_dict = self.controller.members(self.request, 'fake_cg_id')
|
||||
|
||||
self.assertEqual([expected_member], res_dict['cgsnapshot_members'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
self.context, self.resource_name, 'get_cgsnapshot')
|
||||
|
||||
def test_members_with_limit(self):
|
||||
fake_member, expected_member = self._get_fake_cgsnapshot_member()
|
||||
fake_member2, expected_member2 = self._get_fake_cgsnapshot_member(
|
||||
id="fake_id2")
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshot_members',
|
||||
mock.Mock(return_value=[fake_member, fake_member2]))
|
||||
req = fakes.HTTPRequest.blank('/members?limit=1',
|
||||
version=self.api_version,
|
||||
experimental=True)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.members(req, 'fake_cg_id')
|
||||
|
||||
self.assertEqual(1, len(res_dict['cgsnapshot_members']))
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'get_cgsnapshot')
|
||||
|
||||
def test_members_with_limit_and_offset(self):
|
||||
fake_member, expected_member = self._get_fake_cgsnapshot_member()
|
||||
fake_member2, expected_member2 = self._get_fake_cgsnapshot_member(
|
||||
id="fake_id2")
|
||||
self.mock_object(self.controller.cg_api, 'get_all_cgsnapshot_members',
|
||||
mock.Mock(return_value=[fake_member, fake_member2]))
|
||||
req = fakes.HTTPRequest.blank('/members?limit=1&offset=1',
|
||||
version=self.api_version,
|
||||
experimental=True)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.members(req, 'fake_cg_id')
|
||||
|
||||
self.assertEqual(1, len(res_dict['cgsnapshot_members']))
|
||||
self.assertEqual([expected_member2], res_dict['cgsnapshot_members'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'get_cgsnapshot')
|
||||
self.context, self.resource_name, 'get')
|
||||
|
||||
def _get_context(self, role):
|
||||
return getattr(self, '%s_context' % role)
|
||||
|
||||
def _setup_cgsnapshot_data(self, cgsnapshot=None, version='2.7'):
|
||||
if cgsnapshot is None:
|
||||
cgsnapshot = db_utils.create_cgsnapshot(
|
||||
def _setup_share_group_snapshot_data(self, share_group_snapshot=None,
|
||||
version='2.31'):
|
||||
if share_group_snapshot is None:
|
||||
share_group_snapshot = db_utils.create_share_group_snapshot(
|
||||
'fake_id', status=constants.STATUS_AVAILABLE)
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/cgsnapshots/%s/action' %
|
||||
cgsnapshot['id'], version=version)
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/v2/fake/share-group-snapshots/%s/action' %
|
||||
share_group_snapshot['id'], version=version)
|
||||
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = version
|
||||
req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True'
|
||||
return cgsnapshot, req
|
||||
return share_group_snapshot, req
|
||||
|
||||
@ddt.data(*fakes.fixture_force_delete_with_different_roles)
|
||||
@ddt.unpack
|
||||
def test_cgsnapshot_force_delete_with_different_roles(self, role,
|
||||
resp_code, version):
|
||||
cgsnap, req = self._setup_cgsnapshot_data()
|
||||
def test_share_group_snapshot_force_delete_with_different_roles(
|
||||
self, role, resp_code, version):
|
||||
group_snap, req = self._setup_share_group_snapshot_data()
|
||||
ctxt = self._get_context(role)
|
||||
req.method = 'POST'
|
||||
req.headers['content-type'] = 'application/json'
|
||||
if float(version) > 2.6:
|
||||
action_name = 'force_delete'
|
||||
else:
|
||||
action_name = 'os-force_delete'
|
||||
body = {action_name: {'status': constants.STATUS_ERROR}}
|
||||
req.body = six.b(jsonutils.dumps(body))
|
||||
req.headers['X-Openstack-Manila-Api-Version'] = version
|
||||
req.headers['X-Openstack-Manila-Api-Version'] = self.api_version
|
||||
req.environ['manila.context'] = ctxt
|
||||
|
||||
with mock.patch.object(
|
||||
@ -554,19 +578,16 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
|
||||
@ddt.data(*fakes.fixture_reset_status_with_different_roles)
|
||||
@ddt.unpack
|
||||
def test_cgsnapshot_reset_status_with_different_roles(
|
||||
def test_share_group_snapshot_reset_status_with_different_roles(
|
||||
self, role, valid_code, valid_status, version):
|
||||
ctxt = self._get_context(role)
|
||||
cgsnap, req = self._setup_cgsnapshot_data(version=version)
|
||||
if float(version) > 2.6:
|
||||
group_snap, req = self._setup_share_group_snapshot_data()
|
||||
action_name = 'reset_status'
|
||||
else:
|
||||
action_name = 'os-reset_status'
|
||||
body = {action_name: {'status': constants.STATUS_ERROR}}
|
||||
req.method = 'POST'
|
||||
req.headers['content-type'] = 'application/json'
|
||||
req.body = six.b(jsonutils.dumps(body))
|
||||
req.headers['X-Openstack-Manila-Api-Version'] = version
|
||||
req.headers['X-Openstack-Manila-Api-Version'] = self.api_version
|
||||
req.environ['manila.context'] = ctxt
|
||||
|
||||
with mock.patch.object(
|
||||
@ -578,9 +599,9 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
|
||||
if valid_code == 404:
|
||||
self.assertRaises(exception.NotFound,
|
||||
db.cgsnapshot_get,
|
||||
db.share_group_snapshot_get,
|
||||
ctxt,
|
||||
cgsnap['id'])
|
||||
group_snap['id'])
|
||||
else:
|
||||
actual_model = db.cgsnapshot_get(ctxt, cgsnap['id'])
|
||||
actual_model = db.share_group_snapshot_get(ctxt, group_snap['id'])
|
||||
self.assertEqual(valid_status, actual_model['status'])
|
||||
|
354
manila/tests/api/v2/test_share_group_type_specs.py
Normal file
354
manila/tests/api/v2/test_share_group_type_specs.py
Normal file
@ -0,0 +1,354 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from oslo_utils import strutils
|
||||
import webob
|
||||
|
||||
from manila.api.v2 import share_group_type_specs
|
||||
from manila import exception
|
||||
from manila import policy
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
import manila.wsgi
|
||||
|
||||
CONSISTENT_SNAPSHOTS = 'consistent_snapshots'
|
||||
|
||||
|
||||
def return_create_share_group_type_specs(context, share_group_type_id,
|
||||
group_specs):
|
||||
return stub_share_group_type_specs()
|
||||
|
||||
|
||||
def return_share_group_type_specs(context, share_group_type_id):
|
||||
return stub_share_group_type_specs()
|
||||
|
||||
|
||||
def return_empty_share_group_type_specs(context, share_group_type_id):
|
||||
return {}
|
||||
|
||||
|
||||
def delete_share_group_type_specs(context, share_group_type_id, key):
|
||||
pass
|
||||
|
||||
|
||||
def delete_share_group_type_specs_not_found(context, share_group_type_id, key):
|
||||
raise exception.ShareGroupTypeSpecsNotFound("Not Found")
|
||||
|
||||
|
||||
def stub_share_group_type_specs():
|
||||
return {"key%d" % i: "value%d" % i for i in (1, 2, 3, 4, 5)}
|
||||
|
||||
|
||||
def get_large_string():
|
||||
return "s" * 256
|
||||
|
||||
|
||||
def get_group_specs_dict(group_specs, include_required=True):
|
||||
|
||||
if not group_specs:
|
||||
group_specs = {}
|
||||
|
||||
return {'group_specs': group_specs}
|
||||
|
||||
|
||||
def fake_request(url, admin=False, experimental=True, version='2.31',
|
||||
**kwargs):
|
||||
return fakes.HTTPRequest.blank(
|
||||
url, use_admin_context=admin, experimental=experimental,
|
||||
version=version, **kwargs)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ShareGroupTypesSpecsTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ShareGroupTypesSpecsTest, self).setUp()
|
||||
self.flags(host='fake')
|
||||
self.mock_object(manila.db, 'share_group_type_get')
|
||||
self.api_path = '/v2/fake/share-group-types/1/group_specs'
|
||||
self.controller = (
|
||||
share_group_type_specs.ShareGroupTypeSpecsController())
|
||||
self.resource_name = self.controller.resource_name
|
||||
self.mock_policy_check = self.mock_object(policy, 'check_policy')
|
||||
|
||||
def test_index(self):
|
||||
self.mock_object(
|
||||
manila.db, 'share_group_type_specs_get',
|
||||
return_share_group_type_specs)
|
||||
req = fake_request(self.api_path)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.index(req, 1)
|
||||
|
||||
self.assertEqual('value1', res_dict['group_specs']['key1'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'index')
|
||||
|
||||
def test_index_no_data(self):
|
||||
self.mock_object(manila.db, 'share_group_type_specs_get',
|
||||
return_empty_share_group_type_specs)
|
||||
req = fake_request(self.api_path)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.index(req, 1)
|
||||
|
||||
self.assertEqual(0, len(res_dict['group_specs']))
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'index')
|
||||
|
||||
def test_show(self):
|
||||
self.mock_object(manila.db, 'share_group_type_specs_get',
|
||||
return_share_group_type_specs)
|
||||
req = fake_request(self.api_path + '/key5')
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.show(req, 1, 'key5')
|
||||
|
||||
self.assertEqual('value5', res_dict['key5'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'show')
|
||||
|
||||
def test_show_spec_not_found(self):
|
||||
self.mock_object(manila.db, 'share_group_type_specs_get',
|
||||
return_empty_share_group_type_specs)
|
||||
req = fake_request(self.api_path + '/key6')
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
|
||||
req, 1, 'key6')
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'show')
|
||||
|
||||
def test_delete(self):
|
||||
self.mock_object(manila.db, 'share_group_type_specs_delete',
|
||||
delete_share_group_type_specs)
|
||||
req = fake_request(self.api_path + '/key5')
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
self.controller.delete(req, 1, 'key5')
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'delete')
|
||||
|
||||
def test_delete_not_found(self):
|
||||
self.mock_object(manila.db, 'share_group_type_specs_delete',
|
||||
delete_share_group_type_specs_not_found)
|
||||
req = fake_request(self.api_path + '/key6')
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.delete,
|
||||
req, 1, 'key6')
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'delete')
|
||||
|
||||
@ddt.data(
|
||||
get_group_specs_dict({}),
|
||||
{'foo': 'bar'},
|
||||
{CONSISTENT_SNAPSHOTS + 'foo': True},
|
||||
{'foo' + CONSISTENT_SNAPSHOTS: False},
|
||||
*[{CONSISTENT_SNAPSHOTS: v}
|
||||
for v in strutils.TRUE_STRINGS + strutils.FALSE_STRINGS]
|
||||
)
|
||||
def test_create(self, data):
|
||||
body = {'group_specs': data}
|
||||
mock_spec_update_or_create = self.mock_object(
|
||||
manila.db, 'share_group_type_specs_update_or_create',
|
||||
mock.Mock(return_value=return_create_share_group_type_specs))
|
||||
req = fake_request(self.api_path)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.create(req, 1, body)
|
||||
|
||||
for k, v in data.items():
|
||||
self.assertIn(k, res_dict['group_specs'])
|
||||
self.assertEqual(v, res_dict['group_specs'][k])
|
||||
mock_spec_update_or_create.assert_called_once_with(
|
||||
req.environ['manila.context'], 1, body['group_specs'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'create')
|
||||
|
||||
def test_create_with_too_small_key(self):
|
||||
self.mock_object(
|
||||
manila.db, 'share_group_type_specs_update_or_create',
|
||||
mock.Mock(return_value=return_create_share_group_type_specs))
|
||||
too_small_key = ""
|
||||
body = {"group_specs": {too_small_key: "value"}}
|
||||
req = fake_request(self.api_path)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, 1, body)
|
||||
|
||||
self.assertFalse(
|
||||
manila.db.share_group_type_specs_update_or_create.called)
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'create')
|
||||
|
||||
def test_create_with_too_big_key(self):
|
||||
self.mock_object(
|
||||
manila.db, 'share_group_type_specs_update_or_create',
|
||||
mock.Mock(return_value=return_create_share_group_type_specs))
|
||||
too_big_key = "k" * 256
|
||||
body = {"group_specs": {too_big_key: "value"}}
|
||||
req = fake_request(self.api_path)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, 1, body)
|
||||
|
||||
self.assertFalse(
|
||||
manila.db.share_group_type_specs_update_or_create.called)
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'create')
|
||||
|
||||
def test_create_with_too_small_value(self):
|
||||
self.mock_object(
|
||||
manila.db, 'share_group_type_specs_update_or_create',
|
||||
mock.Mock(return_value=return_create_share_group_type_specs))
|
||||
too_small_value = ""
|
||||
body = {"group_specs": {"key": too_small_value}}
|
||||
req = fake_request(self.api_path)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, 1, body)
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'create')
|
||||
self.assertFalse(
|
||||
manila.db.share_group_type_specs_update_or_create.called)
|
||||
|
||||
def test_create_with_too_big_value(self):
|
||||
self.mock_object(
|
||||
manila.db, 'share_group_type_specs_update_or_create',
|
||||
mock.Mock(return_value=return_create_share_group_type_specs))
|
||||
too_big_value = "v" * 256
|
||||
body = {"extra_specs": {"key": too_big_value}}
|
||||
req = fake_request(self.api_path)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, 1, body)
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'create')
|
||||
self.assertFalse(
|
||||
manila.db.share_group_type_specs_update_or_create.called)
|
||||
|
||||
def test_create_key_allowed_chars(self):
|
||||
mock_return_value = stub_share_group_type_specs()
|
||||
mock_spec_update_or_create = self.mock_object(
|
||||
manila.db, 'share_group_type_specs_update_or_create',
|
||||
mock.Mock(return_value=mock_return_value))
|
||||
body = get_group_specs_dict({"other_alphanum.-_:": "value1"})
|
||||
req = fake_request(self.api_path)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.create(req, 1, body)
|
||||
|
||||
self.assertEqual(mock_return_value['key1'],
|
||||
res_dict['group_specs']['other_alphanum.-_:'])
|
||||
mock_spec_update_or_create.assert_called_once_with(
|
||||
req.environ['manila.context'], 1, body['group_specs'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'create')
|
||||
|
||||
def test_create_too_many_keys_allowed_chars(self):
|
||||
mock_return_value = stub_share_group_type_specs()
|
||||
mock_spec_update_or_create = self.mock_object(
|
||||
manila.db, 'share_group_type_specs_update_or_create',
|
||||
mock.Mock(return_value=mock_return_value))
|
||||
body = get_group_specs_dict({
|
||||
"other_alphanum.-_:": "value1",
|
||||
"other2_alphanum.-_:": "value2",
|
||||
"other3_alphanum.-_:": "value3",
|
||||
})
|
||||
req = fake_request(self.api_path)
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
res_dict = self.controller.create(req, 1, body)
|
||||
|
||||
self.assertEqual(mock_return_value['key1'],
|
||||
res_dict['group_specs']['other_alphanum.-_:'])
|
||||
self.assertEqual(mock_return_value['key2'],
|
||||
res_dict['group_specs']['other2_alphanum.-_:'])
|
||||
self.assertEqual(mock_return_value['key3'],
|
||||
res_dict['group_specs']['other3_alphanum.-_:'])
|
||||
mock_spec_update_or_create.assert_called_once_with(
|
||||
req_context, 1, body['group_specs'])
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'create')
|
||||
|
||||
def test_update_item_too_many_keys(self):
|
||||
self.mock_object(manila.db, 'share_group_type_specs_update_or_create')
|
||||
body = {"key1": "value1", "key2": "value2"}
|
||||
req = fake_request(self.api_path + '/key1')
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
|
||||
req, 1, 'key1', body)
|
||||
|
||||
self.assertFalse(
|
||||
manila.db.share_group_type_specs_update_or_create.called)
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'update')
|
||||
|
||||
def test_update_item_body_uri_mismatch(self):
|
||||
self.mock_object(manila.db, 'share_group_type_specs_update_or_create')
|
||||
body = {"key1": "value1"}
|
||||
req = fake_request(self.api_path + '/bad')
|
||||
req_context = req.environ['manila.context']
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.update,
|
||||
req, 1, 'bad', body)
|
||||
|
||||
self.assertFalse(
|
||||
manila.db.share_group_type_specs_update_or_create.called)
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'update')
|
||||
|
||||
@ddt.data(None, {}, {"group_specs": {CONSISTENT_SNAPSHOTS: ""}})
|
||||
def test_update_invalid_body(self, body):
|
||||
req = fake_request('/v2/fake/share-group-types/1/group_specs')
|
||||
req_context = req.environ['manila.context']
|
||||
req.method = 'POST'
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.update, req, '1', body)
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'update')
|
||||
|
||||
@ddt.data(
|
||||
None, {}, {'foo': {'a': 'b'}}, {'group_specs': 'string'},
|
||||
{"group_specs": {"ke/y1": "value1"}},
|
||||
{"key1": "value1", "ke/y2": "value2", "key3": "value3"},
|
||||
{"group_specs": {CONSISTENT_SNAPSHOTS: ""}},
|
||||
{"group_specs": {"": "value"}},
|
||||
{"group_specs": {"t": get_large_string()}},
|
||||
{"group_specs": {get_large_string(): get_large_string()}},
|
||||
{"group_specs": {get_large_string(): "v"}},
|
||||
{"group_specs": {"k": ""}})
|
||||
def test_create_invalid_body(self, body):
|
||||
req = fake_request('/v2/fake/share-group-types/1/group_specs')
|
||||
req_context = req.environ['manila.context']
|
||||
req.method = 'POST'
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.create, req, '1', body)
|
||||
|
||||
self.mock_policy_check.assert_called_once_with(
|
||||
req_context, self.resource_name, 'create')
|
592
manila/tests/api/v2/test_share_group_types.py
Normal file
592
manila/tests/api/v2/test_share_group_types.py
Normal file
@ -0,0 +1,592 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import datetime
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
import webob
|
||||
|
||||
from manila.api.v2 import share_group_types as types
|
||||
from manila import exception
|
||||
from manila import policy
|
||||
from manila.share_group import share_group_types
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
|
||||
PROJ1_UUID = '11111111-1111-1111-1111-111111111111'
|
||||
PROJ2_UUID = '22222222-2222-2222-2222-222222222222'
|
||||
PROJ3_UUID = '33333333-3333-3333-3333-333333333333'
|
||||
|
||||
SHARE_TYPE_ID = '4b1e460f-8bc5-4a97-989b-739a2eceaec6'
|
||||
GROUP_TYPE_1 = {
|
||||
'id': 'c8d7bf70-0db9-4b3e-8498-055dd0306461',
|
||||
'name': u'group type 1',
|
||||
'deleted': False,
|
||||
'created_at': datetime.datetime(2012, 1, 1, 1, 1, 1, 1),
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'is_public': True,
|
||||
'group_specs': {},
|
||||
'share_types': [],
|
||||
}
|
||||
|
||||
GROUP_TYPE_2 = {
|
||||
'id': 'f93f7a1f-62d7-4e7e-b9e6-72eec95a47f5',
|
||||
'name': u'group type 2',
|
||||
'deleted': False,
|
||||
'created_at': datetime.datetime(2012, 1, 1, 1, 1, 1, 1),
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'is_public': False,
|
||||
'group_specs': {'consistent_snapshots': 'true'},
|
||||
'share_types': [{'share_type_id': SHARE_TYPE_ID}],
|
||||
}
|
||||
|
||||
|
||||
def fake_request(url, admin=False, experimental=True, version='2.31',
|
||||
**kwargs):
|
||||
|
||||
return fakes.HTTPRequest.blank(
|
||||
url,
|
||||
use_admin_context=admin,
|
||||
experimental=experimental,
|
||||
version=version,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ShareGroupTypesAPITest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(self.__class__, self).setUp()
|
||||
self.flags(host='fake')
|
||||
self.controller = types.ShareGroupTypesController()
|
||||
self.resource_name = self.controller.resource_name
|
||||
self.mock_object(policy, 'check_policy', mock.Mock(return_value=True))
|
||||
|
||||
def test_share_group_types_index(self):
|
||||
fake_types = {GROUP_TYPE_1['name']: GROUP_TYPE_1}
|
||||
mock_get_all = self.mock_object(
|
||||
share_group_types, 'get_all', mock.Mock(return_value=fake_types))
|
||||
req = fake_request('/v2/fake/share-group-types', admin=False)
|
||||
expected_list = [{
|
||||
'id': GROUP_TYPE_1['id'],
|
||||
'name': GROUP_TYPE_1['name'],
|
||||
'is_public': True,
|
||||
'group_specs': {},
|
||||
'share_types': [],
|
||||
}]
|
||||
|
||||
res_dict = self.controller.index(req)
|
||||
|
||||
mock_get_all.assert_called_once_with(
|
||||
mock.ANY, search_opts={"is_public": True})
|
||||
self.assertEqual(1, len(res_dict['share_group_types']))
|
||||
self.assertEqual(expected_list, res_dict['share_group_types'])
|
||||
|
||||
def test_share_group_types_index_as_admin(self):
|
||||
fake_types = {
|
||||
GROUP_TYPE_1['name']: GROUP_TYPE_1,
|
||||
GROUP_TYPE_2['name']: GROUP_TYPE_2,
|
||||
}
|
||||
mock_get_all = self.mock_object(
|
||||
share_group_types, 'get_all',
|
||||
mock.Mock(return_value=fake_types))
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types?is_public=all', admin=True)
|
||||
expected_type_1 = {
|
||||
'id': GROUP_TYPE_1['id'],
|
||||
'name': GROUP_TYPE_1['name'],
|
||||
'is_public': True,
|
||||
'group_specs': {},
|
||||
'share_types': [],
|
||||
}
|
||||
expected_type_2 = {
|
||||
'id': GROUP_TYPE_2['id'],
|
||||
'name': GROUP_TYPE_2['name'],
|
||||
'is_public': False,
|
||||
'group_specs': {'consistent_snapshots': 'true'},
|
||||
'share_types': [SHARE_TYPE_ID],
|
||||
}
|
||||
|
||||
res_dict = self.controller.index(req)
|
||||
|
||||
mock_get_all.assert_called_once_with(
|
||||
mock.ANY, search_opts={'is_public': None})
|
||||
self.assertEqual(2, len(res_dict['share_group_types']))
|
||||
self.assertIn(expected_type_1, res_dict['share_group_types'])
|
||||
self.assertIn(expected_type_2, res_dict['share_group_types'])
|
||||
|
||||
def test_share_group_types_index_as_admin_default_public_only(self):
|
||||
fake_types = {}
|
||||
mock_get_all = self.mock_object(
|
||||
share_group_types, 'get_all',
|
||||
mock.Mock(return_value=fake_types))
|
||||
req = fake_request('/v2/fake/share-group-types', admin=True)
|
||||
|
||||
self.controller.index(req)
|
||||
|
||||
mock_get_all.assert_called_once_with(
|
||||
mock.ANY, search_opts={'is_public': True})
|
||||
|
||||
def test_share_group_types_index_not_experimental(self):
|
||||
self.mock_object(
|
||||
share_group_types, 'get_all', mock.Mock(return_value={}))
|
||||
req = fake_request('/v2/fake/share-group-types', experimental=False)
|
||||
|
||||
self.assertRaises(
|
||||
exception.VersionNotFoundForAPIMethod, self.controller.index, req)
|
||||
self.assertFalse(share_group_types.get_all.called)
|
||||
|
||||
def test_share_group_types_index_older_api_version(self):
|
||||
self.mock_object(
|
||||
share_group_types, 'get_all', mock.Mock(return_value={}))
|
||||
req = fake_request('/v2/fake/share-group-types', version='2.1')
|
||||
|
||||
self.assertRaises(
|
||||
exception.VersionNotFoundForAPIMethod, self.controller.index, req)
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_share_group_types_index_no_data(self, admin):
|
||||
self.mock_object(
|
||||
share_group_types, 'get_all', mock.Mock(return_value={}))
|
||||
req = fake_request('/v2/fake/share-group-types', admin=admin)
|
||||
|
||||
res_dict = self.controller.index(req)
|
||||
|
||||
self.assertEqual(0, len(res_dict['share_group_types']))
|
||||
|
||||
def test_share_group_types_show(self):
|
||||
mock_get = self.mock_object(
|
||||
share_group_types, 'get',
|
||||
mock.Mock(return_value=GROUP_TYPE_1))
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'])
|
||||
expected_type = {
|
||||
'id': GROUP_TYPE_1['id'],
|
||||
'name': GROUP_TYPE_1['name'],
|
||||
'is_public': True,
|
||||
'group_specs': {},
|
||||
'share_types': [],
|
||||
}
|
||||
|
||||
res_dict = self.controller.show(req, GROUP_TYPE_1['id'])
|
||||
|
||||
mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_1['id'])
|
||||
self.assertEqual(expected_type, res_dict['share_group_type'])
|
||||
|
||||
def test_share_group_types_show_with_share_types(self):
|
||||
mock_get = self.mock_object(
|
||||
share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_2))
|
||||
req = fake_request('/v2/fake/group-types/%s' % GROUP_TYPE_2['id'])
|
||||
expected_type = {
|
||||
'id': GROUP_TYPE_2['id'],
|
||||
'name': GROUP_TYPE_2['name'],
|
||||
'is_public': False,
|
||||
'group_specs': {'consistent_snapshots': 'true'},
|
||||
'share_types': [SHARE_TYPE_ID],
|
||||
}
|
||||
|
||||
res_dict = self.controller.show(req, GROUP_TYPE_2['id'])
|
||||
|
||||
mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_2['id'])
|
||||
self.assertEqual(expected_type, res_dict['share_group_type'])
|
||||
|
||||
def test_share_group_types_show_not_found(self):
|
||||
mock_get = self.mock_object(
|
||||
share_group_types, 'get',
|
||||
mock.Mock(side_effect=exception.ShareGroupTypeNotFound(
|
||||
type_id=GROUP_TYPE_2['id'])))
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'])
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPNotFound,
|
||||
self.controller.show, req, GROUP_TYPE_2['id'])
|
||||
|
||||
mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_2['id'])
|
||||
|
||||
def test_share_group_types_default(self):
|
||||
mock_get = self.mock_object(
|
||||
share_group_types, 'get_default',
|
||||
mock.Mock(return_value=GROUP_TYPE_2))
|
||||
req = fake_request('/v2/fake/share-group-types/default')
|
||||
expected_type = {
|
||||
'id': GROUP_TYPE_2['id'],
|
||||
'name': GROUP_TYPE_2['name'],
|
||||
'is_public': False,
|
||||
'group_specs': {'consistent_snapshots': 'true'},
|
||||
'share_types': [SHARE_TYPE_ID],
|
||||
}
|
||||
|
||||
res_dict = self.controller.default(req)
|
||||
|
||||
mock_get.assert_called_once_with(mock.ANY)
|
||||
self.assertEqual(expected_type, res_dict['share_group_type'])
|
||||
|
||||
def test_share_group_types_default_not_found(self):
|
||||
mock_get = self.mock_object(
|
||||
share_group_types, 'get_default', mock.Mock(return_value=None))
|
||||
req = fake_request('/v2/fake/share-group-types/default')
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller.default, req)
|
||||
|
||||
mock_get.assert_called_once_with(mock.ANY)
|
||||
|
||||
def test_share_group_types_delete(self):
|
||||
mock_get = self.mock_object(
|
||||
share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_1))
|
||||
mock_destroy = self.mock_object(share_group_types, 'destroy')
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'])
|
||||
|
||||
self.controller._delete(req, GROUP_TYPE_1['id'])
|
||||
|
||||
mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_1['id'])
|
||||
mock_destroy.assert_called_once_with(mock.ANY, GROUP_TYPE_1['id'])
|
||||
|
||||
def test_share_group_types_delete_not_found(self):
|
||||
mock_get = self.mock_object(
|
||||
share_group_types, 'get',
|
||||
mock.Mock(side_effect=exception.ShareGroupTypeNotFound(
|
||||
type_id=GROUP_TYPE_2['id'])))
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'])
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound, self.controller._delete,
|
||||
req, GROUP_TYPE_2['id'])
|
||||
|
||||
mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_2['id'])
|
||||
|
||||
def test_create_minimal(self):
|
||||
fake_type = copy.deepcopy(GROUP_TYPE_1)
|
||||
fake_type['share_types'] = [{'share_type_id': SHARE_TYPE_ID}]
|
||||
mock_create = self.mock_object(share_group_types, 'create')
|
||||
mock_get = self.mock_object(
|
||||
share_group_types, 'get_by_name',
|
||||
mock.Mock(return_value=fake_type))
|
||||
req = fake_request('/v2/fake/share-group-types')
|
||||
fake_body = {'share_group_type': {
|
||||
'name': GROUP_TYPE_1['name'],
|
||||
'share_types': [SHARE_TYPE_ID],
|
||||
}}
|
||||
expected_type = {
|
||||
'id': GROUP_TYPE_1['id'],
|
||||
'name': GROUP_TYPE_1['name'],
|
||||
'is_public': True,
|
||||
'group_specs': {},
|
||||
'share_types': [SHARE_TYPE_ID],
|
||||
}
|
||||
|
||||
res_dict = self.controller._create(req, fake_body)
|
||||
|
||||
mock_create.assert_called_once_with(
|
||||
mock.ANY, GROUP_TYPE_1['name'],
|
||||
[SHARE_TYPE_ID], {}, True)
|
||||
mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_1['name'])
|
||||
self.assertEqual(expected_type, res_dict['share_group_type'])
|
||||
|
||||
def test_create_with_group_specs(self):
|
||||
fake_group_specs = {'my_fake_group_spec': 'false'}
|
||||
fake_type = copy.deepcopy(GROUP_TYPE_1)
|
||||
fake_type['share_types'] = [{'share_type_id': SHARE_TYPE_ID}]
|
||||
fake_type['group_specs'] = fake_group_specs
|
||||
mock_create = self.mock_object(share_group_types, 'create')
|
||||
mock_get = self.mock_object(
|
||||
share_group_types, 'get_by_name',
|
||||
mock.Mock(return_value=fake_type))
|
||||
req = fake_request('/v2/fake/share-group-types')
|
||||
fake_body = {'share_group_type': {
|
||||
'name': GROUP_TYPE_1['name'],
|
||||
'share_types': [SHARE_TYPE_ID],
|
||||
'group_specs': fake_group_specs,
|
||||
}}
|
||||
expected_type = {
|
||||
'id': GROUP_TYPE_1['id'],
|
||||
'name': GROUP_TYPE_1['name'],
|
||||
'is_public': True,
|
||||
'group_specs': fake_group_specs,
|
||||
'share_types': [SHARE_TYPE_ID],
|
||||
}
|
||||
|
||||
res_dict = self.controller._create(req, fake_body)
|
||||
|
||||
mock_create.assert_called_once_with(
|
||||
mock.ANY, GROUP_TYPE_1['name'], [SHARE_TYPE_ID], fake_group_specs,
|
||||
True)
|
||||
mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_1['name'])
|
||||
self.assertEqual(expected_type, res_dict['share_group_type'])
|
||||
|
||||
def test_create_private_share_group_type(self):
|
||||
fake_type = copy.deepcopy(GROUP_TYPE_1)
|
||||
fake_type['share_types'] = [{'share_type_id': SHARE_TYPE_ID}]
|
||||
fake_type['is_public'] = False
|
||||
mock_create = self.mock_object(share_group_types, 'create')
|
||||
mock_get = self.mock_object(
|
||||
share_group_types, 'get_by_name',
|
||||
mock.Mock(return_value=fake_type))
|
||||
req = fake_request('/v2/fake/share-group-types')
|
||||
fake_body = {'share_group_type': {
|
||||
'name': GROUP_TYPE_1['name'],
|
||||
'share_types': [SHARE_TYPE_ID],
|
||||
'is_public': False
|
||||
}}
|
||||
expected_type = {
|
||||
'id': GROUP_TYPE_1['id'],
|
||||
'name': GROUP_TYPE_1['name'],
|
||||
'is_public': False,
|
||||
'group_specs': {},
|
||||
'share_types': [SHARE_TYPE_ID],
|
||||
}
|
||||
|
||||
res_dict = self.controller._create(req, fake_body)
|
||||
|
||||
mock_create.assert_called_once_with(
|
||||
mock.ANY, GROUP_TYPE_1['name'], [SHARE_TYPE_ID], {}, False)
|
||||
mock_get.assert_called_once_with(mock.ANY, GROUP_TYPE_1['name'])
|
||||
self.assertEqual(expected_type, res_dict['share_group_type'])
|
||||
|
||||
def test_create_invalid_request_duplicate_name(self):
|
||||
mock_create = self.mock_object(
|
||||
share_group_types, 'create',
|
||||
mock.Mock(side_effect=exception.ShareGroupTypeExists(
|
||||
type_id=GROUP_TYPE_1['name'])))
|
||||
req = fake_request('/v2/fake/sahre-group-types')
|
||||
fake_body = {'share_group_type': {
|
||||
'name': GROUP_TYPE_1['name'],
|
||||
'share_types': [SHARE_TYPE_ID],
|
||||
}}
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPConflict, self.controller._create, req, fake_body)
|
||||
|
||||
mock_create.assert_called_once_with(
|
||||
mock.ANY, GROUP_TYPE_1['name'], [SHARE_TYPE_ID], {}, True)
|
||||
|
||||
def test_create_invalid_request_missing_name(self):
|
||||
req = fake_request('/v2/fake/share-group-types')
|
||||
fake_body = {'share_group_type': {'share_types': [SHARE_TYPE_ID]}}
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPBadRequest, self.controller._create, req, fake_body)
|
||||
|
||||
def test_create_invalid_request_missing_share_types(self):
|
||||
req = fake_request('/v2/fake/share-group-types')
|
||||
fake_body = {'share_group_type': {'name': GROUP_TYPE_1['name']}}
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPBadRequest,
|
||||
self.controller._create, req, fake_body)
|
||||
|
||||
|
||||
class ShareGroupTypeAccessTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(self.__class__, self).setUp()
|
||||
self.controller = types.ShareGroupTypesController()
|
||||
|
||||
def test_list_type_access_public(self):
|
||||
self.mock_object(
|
||||
share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_1))
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'], admin=True)
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPNotFound,
|
||||
self.controller.share_group_type_access, req, GROUP_TYPE_1['id'])
|
||||
|
||||
def test_list_type_access_private(self):
|
||||
fake_type = copy.deepcopy(GROUP_TYPE_2)
|
||||
fake_type['projects'] = [PROJ2_UUID, PROJ3_UUID]
|
||||
mock_get = self.mock_object(
|
||||
share_group_types, 'get', mock.Mock(return_value=fake_type))
|
||||
expected = {'share_group_type_access': [
|
||||
{'share_group_type_id': fake_type['id'], 'project_id': PROJ2_UUID},
|
||||
{'share_group_type_id': fake_type['id'], 'project_id': PROJ3_UUID},
|
||||
]}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % fake_type['id'], admin=True)
|
||||
|
||||
actual = self.controller.share_group_type_access(req, fake_type['id'])
|
||||
|
||||
mock_get.assert_called_once_with(
|
||||
mock.ANY, fake_type['id'], expected_fields=['projects'])
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_list_type_access_type_not_found(self):
|
||||
self.mock_object(
|
||||
share_group_types, 'get',
|
||||
mock.Mock(side_effect=exception.ShareGroupTypeNotFound(
|
||||
type_id=GROUP_TYPE_2['id'])))
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPNotFound,
|
||||
self.controller.share_group_type_access, req, GROUP_TYPE_2['id'])
|
||||
|
||||
def test_add_project_access(self):
|
||||
self.mock_object(share_group_types, 'get',
|
||||
mock.Mock(return_value=GROUP_TYPE_2))
|
||||
mock_add_access = self.mock_object(
|
||||
share_group_types, 'add_share_group_type_access')
|
||||
body = {'addProjectAccess': {'project': PROJ1_UUID}}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
|
||||
|
||||
response = self.controller._add_project_access(
|
||||
req, GROUP_TYPE_2['id'], body)
|
||||
|
||||
mock_add_access.assert_called_once_with(
|
||||
mock.ANY, GROUP_TYPE_2['id'], PROJ1_UUID)
|
||||
self.assertEqual(202, response.status_code)
|
||||
|
||||
def test_add_project_access_non_existent_type(self):
|
||||
self.mock_object(
|
||||
share_group_types, 'get',
|
||||
mock.Mock(side_effect=exception.ShareGroupTypeNotFound(
|
||||
type_id=GROUP_TYPE_2['id'])))
|
||||
body = {'addProjectAccess': {'project': PROJ1_UUID}}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPNotFound,
|
||||
self.controller._add_project_access, req, GROUP_TYPE_2['id'], body)
|
||||
|
||||
def test_add_project_access_missing_project_in_body(self):
|
||||
body = {'addProjectAccess': {}}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPBadRequest,
|
||||
self.controller._add_project_access, req, GROUP_TYPE_2['id'], body)
|
||||
|
||||
def test_add_project_access_missing_add_project_access_in_body(self):
|
||||
body = {}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPBadRequest,
|
||||
self.controller._add_project_access, req, GROUP_TYPE_2['id'], body)
|
||||
|
||||
def test_add_project_access_with_already_added_access(self):
|
||||
self.mock_object(
|
||||
share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_2))
|
||||
mock_add_access = self.mock_object(
|
||||
share_group_types, 'add_share_group_type_access',
|
||||
mock.Mock(side_effect=exception.ShareGroupTypeAccessExists(
|
||||
type_id=GROUP_TYPE_2['id'], project_id=PROJ1_UUID))
|
||||
)
|
||||
body = {'addProjectAccess': {'project': PROJ1_UUID}}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPConflict,
|
||||
self.controller._add_project_access, req, GROUP_TYPE_2['id'], body)
|
||||
|
||||
mock_add_access.assert_called_once_with(
|
||||
mock.ANY, GROUP_TYPE_2['id'], PROJ1_UUID)
|
||||
|
||||
def test_add_project_access_to_public_share_type(self):
|
||||
self.mock_object(
|
||||
share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_1))
|
||||
body = {'addProjectAccess': {'project': PROJ1_UUID}}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'], admin=True)
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPConflict,
|
||||
self.controller._add_project_access, req, GROUP_TYPE_1['id'], body)
|
||||
|
||||
def test_remove_project_access(self):
|
||||
self.mock_object(
|
||||
share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_2))
|
||||
mock_remove_access = self.mock_object(
|
||||
share_group_types, 'remove_share_group_type_access')
|
||||
body = {'removeProjectAccess': {'project': PROJ1_UUID}}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
|
||||
|
||||
response = self.controller._remove_project_access(
|
||||
req, GROUP_TYPE_2['id'], body)
|
||||
|
||||
mock_remove_access.assert_called_once_with(
|
||||
mock.ANY, GROUP_TYPE_2['id'], PROJ1_UUID)
|
||||
self.assertEqual(202, response.status_code)
|
||||
|
||||
def test_remove_project_access_nonexistent_rule(self):
|
||||
self.mock_object(
|
||||
share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_2))
|
||||
mock_remove_access = self.mock_object(
|
||||
share_group_types, 'remove_share_group_type_access', mock.Mock(
|
||||
side_effect=exception.ShareGroupTypeAccessNotFound(
|
||||
type_id=GROUP_TYPE_2['id'], project_id=PROJ1_UUID)))
|
||||
body = {'removeProjectAccess': {'project': PROJ1_UUID}}
|
||||
req = fake_request('/v2/fake/group-types/%s' % GROUP_TYPE_2['id'],
|
||||
admin=True)
|
||||
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPNotFound,
|
||||
self.controller._remove_project_access,
|
||||
req, GROUP_TYPE_2['id'], body)
|
||||
|
||||
mock_remove_access.assert_called_once_with(
|
||||
mock.ANY, GROUP_TYPE_2['id'], PROJ1_UUID)
|
||||
|
||||
def test_remove_project_access_from_public_share_type(self):
|
||||
self.mock_object(
|
||||
share_group_types, 'get', mock.Mock(return_value=GROUP_TYPE_1))
|
||||
body = {'removeProjectAccess': {'project': PROJ1_UUID}}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_1['id'], admin=True)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPConflict,
|
||||
self.controller._remove_project_access, req,
|
||||
GROUP_TYPE_1['id'], body)
|
||||
|
||||
def test_remove_project_access_non_existent_type(self):
|
||||
self.mock_object(
|
||||
share_group_types, 'get',
|
||||
mock.Mock(side_effect=exception.ShareGroupTypeNotFound(
|
||||
type_id=GROUP_TYPE_2['id'])))
|
||||
body = {'removeProjectAccess': {'project': PROJ1_UUID}}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.controller._remove_project_access, req,
|
||||
GROUP_TYPE_2['id'], body)
|
||||
|
||||
def test_remove_project_access_missing_project_in_body(self):
|
||||
body = {'removeProjectAccess': {}}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._remove_project_access, req,
|
||||
GROUP_TYPE_2['id'], body)
|
||||
|
||||
def test_remove_project_access_missing_remove_project_access_in_body(self):
|
||||
body = {}
|
||||
req = fake_request(
|
||||
'/v2/fake/share-group-types/%s' % GROUP_TYPE_2['id'], admin=True)
|
||||
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller._remove_project_access, req,
|
||||
GROUP_TYPE_2['id'], body)
|
File diff suppressed because it is too large
Load Diff
@ -292,11 +292,11 @@ class ShareNetworkAPITest(test.TestCase):
|
||||
assert_called_once_with(self.req.environ['manila.context'],
|
||||
share_nw['id'])
|
||||
|
||||
def test_delete_in_use_by_consistency_group(self):
|
||||
def test_delete_in_use_by_share_group(self):
|
||||
share_nw = fake_share_network.copy()
|
||||
self.mock_object(db_api, 'share_network_get',
|
||||
mock.Mock(return_value=share_nw))
|
||||
self.mock_object(db_api, 'count_consistency_groups_in_share_network',
|
||||
self.mock_object(db_api, 'count_share_groups_in_share_network',
|
||||
mock.Mock(return_value=2))
|
||||
|
||||
self.assertRaises(webob_exc.HTTPConflict,
|
||||
|
@ -117,8 +117,6 @@ class ShareAPITest(test.TestCase):
|
||||
'volume_type': '1',
|
||||
'snapshot_support': True,
|
||||
'is_public': False,
|
||||
'consistency_group_id': None,
|
||||
'source_cgsnapshot_member_id': None,
|
||||
'task_state': None,
|
||||
'share_type_name': None,
|
||||
'links': [
|
||||
@ -406,8 +404,6 @@ class ShareAPITest(test.TestCase):
|
||||
expected['share'].pop('snapshot_support')
|
||||
expected['share'].pop('share_type_name')
|
||||
expected['share'].pop('task_state')
|
||||
expected['share'].pop('consistency_group_id')
|
||||
expected['share'].pop('source_cgsnapshot_member_id')
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
@ddt.data("2.2", "2.3")
|
||||
@ -421,23 +417,28 @@ class ShareAPITest(test.TestCase):
|
||||
expected = self._get_expected_share_detailed_response(self.share)
|
||||
expected['share'].pop('share_type_name')
|
||||
expected['share'].pop('task_state')
|
||||
expected['share'].pop('consistency_group_id')
|
||||
expected['share'].pop('source_cgsnapshot_member_id')
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
@ddt.data("2.4", "2.5")
|
||||
def test_share_create_with_consistency_group(self, microversion):
|
||||
@ddt.data("2.31")
|
||||
def test_share_create_with_share_group(self, microversion):
|
||||
self.mock_object(share_api.API, 'create', self.create_mock)
|
||||
body = {"share": copy.deepcopy(self.share)}
|
||||
req = fakes.HTTPRequest.blank('/shares', version=microversion)
|
||||
req = fakes.HTTPRequest.blank('/shares', version=microversion,
|
||||
experimental=True)
|
||||
|
||||
res_dict = self.controller.create(req, body)
|
||||
|
||||
expected = self._get_expected_share_detailed_response(self.share)
|
||||
expected['share'].pop('share_type_name')
|
||||
if (api_version.APIVersionRequest(microversion) ==
|
||||
api_version.APIVersionRequest('2.4')):
|
||||
expected['share'].pop('task_state')
|
||||
expected['share'].pop('export_location')
|
||||
expected['share'].pop('export_locations')
|
||||
expected['share']['access_rules_status'] = 'active'
|
||||
expected['share']['replication_type'] = None
|
||||
expected['share']['has_replicas'] = False
|
||||
expected['share']['user_id'] = 'fakeuser'
|
||||
expected['share']['create_share_from_snapshot_support'] = True
|
||||
expected['share']['revert_to_snapshot_support'] = False
|
||||
expected['share']['share_group_id'] = None
|
||||
expected['share']['source_share_group_snapshot_member_id'] = None
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_share_create_with_valid_default_share_type(self):
|
||||
@ -479,8 +480,6 @@ class ShareAPITest(test.TestCase):
|
||||
expected = self._get_expected_share_detailed_response(self.share)
|
||||
|
||||
expected['share']['task_state'] = None
|
||||
expected['share']['consistency_group_id'] = None
|
||||
expected['share']['source_cgsnapshot_member_id'] = None
|
||||
expected['share']['replication_type'] = None
|
||||
expected['share']['share_type_name'] = None
|
||||
expected['share']['has_replicas'] = False
|
||||
@ -515,7 +514,7 @@ class ShareAPITest(test.TestCase):
|
||||
res_dict = self.controller.create(req, body)
|
||||
|
||||
expected = self._get_expected_share_detailed_response(shr)
|
||||
self.assertEqual(expected, res_dict)
|
||||
self.assertDictMatch(expected, res_dict)
|
||||
self.assertEqual("fakenetid",
|
||||
create_mock.call_args[1]['share_network_id'])
|
||||
|
||||
@ -534,8 +533,6 @@ class ShareAPITest(test.TestCase):
|
||||
else:
|
||||
self.assertNotIn('user_id', expected['share'])
|
||||
expected['share']['task_state'] = None
|
||||
expected['share']['consistency_group_id'] = None
|
||||
expected['share']['source_cgsnapshot_member_id'] = None
|
||||
expected['share']['replication_type'] = None
|
||||
expected['share']['share_type_name'] = None
|
||||
expected['share']['has_replicas'] = False
|
||||
@ -1084,7 +1081,7 @@ class ShareAPITest(test.TestCase):
|
||||
req = fakes.HTTPRequest.blank('/shares', version='2.7')
|
||||
res_dict = self.controller.create(req, body)
|
||||
expected = self._get_expected_share_detailed_response(shr)
|
||||
self.assertEqual(expected, res_dict)
|
||||
self.assertDictMatch(expected, res_dict)
|
||||
self.assertEqual(parent_share_net,
|
||||
create_mock.call_args[1]['share_network_id'])
|
||||
|
||||
@ -1165,29 +1162,49 @@ class ShareAPITest(test.TestCase):
|
||||
expected['share'].pop('snapshot_support')
|
||||
expected['share'].pop('share_type_name')
|
||||
expected['share'].pop('task_state')
|
||||
expected['share'].pop('consistency_group_id')
|
||||
expected['share'].pop('source_cgsnapshot_member_id')
|
||||
|
||||
res_dict = self.controller.show(req, '1')
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_share_show_with_consistency_group(self):
|
||||
req = fakes.HTTPRequest.blank('/shares/1', version='2.4')
|
||||
def test_share_show_with_share_group(self):
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/shares/1', version='2.31', experimental=True)
|
||||
expected = self._get_expected_share_detailed_response()
|
||||
expected['share'].pop('share_type_name')
|
||||
expected['share'].pop('task_state')
|
||||
expected['share'].pop('export_location')
|
||||
expected['share'].pop('export_locations')
|
||||
expected['share']['create_share_from_snapshot_support'] = True
|
||||
expected['share']['revert_to_snapshot_support'] = False
|
||||
expected['share']['share_group_id'] = None
|
||||
expected['share']['source_share_group_snapshot_member_id'] = None
|
||||
expected['share']['access_rules_status'] = 'active'
|
||||
expected['share']['replication_type'] = None
|
||||
expected['share']['has_replicas'] = False
|
||||
expected['share']['user_id'] = 'fakeuser'
|
||||
|
||||
res_dict = self.controller.show(req, '1')
|
||||
|
||||
self.assertEqual(expected, res_dict)
|
||||
self.assertDictMatch(expected, res_dict)
|
||||
|
||||
def test_share_show_with_share_group_earlier_version(self):
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/shares/1', version='2.23', experimental=True)
|
||||
expected = self._get_expected_share_detailed_response()
|
||||
expected['share'].pop('export_location')
|
||||
expected['share'].pop('export_locations')
|
||||
expected['share']['access_rules_status'] = 'active'
|
||||
expected['share']['replication_type'] = None
|
||||
expected['share']['has_replicas'] = False
|
||||
expected['share']['user_id'] = 'fakeuser'
|
||||
|
||||
res_dict = self.controller.show(req, '1')
|
||||
|
||||
self.assertDictMatch(expected, res_dict)
|
||||
|
||||
def test_share_show_with_share_type_name(self):
|
||||
req = fakes.HTTPRequest.blank('/shares/1', version='2.6')
|
||||
res_dict = self.controller.show(req, '1')
|
||||
expected = self._get_expected_share_detailed_response()
|
||||
expected['share']['consistency_group_id'] = None
|
||||
expected['share']['source_cgsnapshot_member_id'] = None
|
||||
expected['share']['share_type_name'] = None
|
||||
expected['share']['task_state'] = None
|
||||
self.assertEqual(expected, res_dict)
|
||||
@ -1204,8 +1221,6 @@ class ShareAPITest(test.TestCase):
|
||||
expected['share']['user_id'] = 'fakeuser'
|
||||
else:
|
||||
self.assertNotIn('user_id', expected['share'])
|
||||
expected['share']['consistency_group_id'] = None
|
||||
expected['share']['source_cgsnapshot_member_id'] = None
|
||||
expected['share']['share_type_name'] = None
|
||||
expected['share']['task_state'] = None
|
||||
expected['share']['access_rules_status'] = 'active'
|
||||
@ -1222,8 +1237,6 @@ class ShareAPITest(test.TestCase):
|
||||
expected['share'].pop('snapshot_support')
|
||||
expected['share'].pop('share_type_name')
|
||||
expected['share'].pop('task_state')
|
||||
expected['share'].pop('consistency_group_id')
|
||||
expected['share'].pop('source_cgsnapshot_member_id')
|
||||
|
||||
res_dict = self.controller.show(req, '1')
|
||||
|
||||
@ -1245,8 +1258,6 @@ class ShareAPITest(test.TestCase):
|
||||
expected = self._get_expected_share_detailed_response()
|
||||
|
||||
expected['share']['task_state'] = None
|
||||
expected['share']['consistency_group_id'] = None
|
||||
expected['share']['source_cgsnapshot_member_id'] = None
|
||||
expected['share']['access_rules_status'] = 'active'
|
||||
expected['share']['share_type_name'] = None
|
||||
expected['share']['replication_type'] = None
|
||||
@ -1289,32 +1300,32 @@ class ShareAPITest(test.TestCase):
|
||||
self.assertRaises(
|
||||
webob.exc.HTTPConflict, self.controller.delete, req, 1)
|
||||
|
||||
def test_share_delete_in_consistency_group_param_not_provided(self):
|
||||
def test_share_delete_in_share_group_param_not_provided(self):
|
||||
fake_share = stubs.stub_share('fake_share',
|
||||
consistency_group_id='fake_cg_id')
|
||||
share_group_id='fake_group_id')
|
||||
self.mock_object(share_api.API, 'get',
|
||||
mock.Mock(return_value=fake_share))
|
||||
req = fakes.HTTPRequest.blank('/shares/1')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.delete, req, 1)
|
||||
|
||||
def test_share_delete_in_consistency_group(self):
|
||||
def test_share_delete_in_share_group(self):
|
||||
fake_share = stubs.stub_share('fake_share',
|
||||
consistency_group_id='fake_cg_id')
|
||||
share_group_id='fake_group_id')
|
||||
self.mock_object(share_api.API, 'get',
|
||||
mock.Mock(return_value=fake_share))
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/shares/1?consistency_group_id=fake_cg_id')
|
||||
'/shares/1?share_group_id=fake_group_id')
|
||||
resp = self.controller.delete(req, 1)
|
||||
self.assertEqual(202, resp.status_int)
|
||||
|
||||
def test_share_delete_in_consistency_group_wrong_id(self):
|
||||
def test_share_delete_in_share_group_wrong_id(self):
|
||||
fake_share = stubs.stub_share('fake_share',
|
||||
consistency_group_id='fake_cg_id')
|
||||
share_group_id='fake_group_id')
|
||||
self.mock_object(share_api.API, 'get',
|
||||
mock.Mock(return_value=fake_share))
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/shares/1?consistency_group_id=not_fake_cg_id')
|
||||
'/shares/1?share_group_id=not_fake_group_id')
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.delete, req, 1)
|
||||
|
||||
@ -1330,14 +1341,18 @@ class ShareAPITest(test.TestCase):
|
||||
self.assertEqual(shr['is_public'],
|
||||
res_dict['share']['is_public'])
|
||||
|
||||
def test_share_update_with_consistency_group(self):
|
||||
def test_share_update_with_share_group(self):
|
||||
shr = self.share
|
||||
body = {"share": shr}
|
||||
|
||||
req = fakes.HTTPRequest.blank('/share/1', version="2.4")
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/share/1', version="2.31", experimental=True)
|
||||
|
||||
res_dict = self.controller.update(req, 1, body)
|
||||
self.assertIsNone(res_dict['share']["consistency_group_id"])
|
||||
self.assertIsNone(res_dict['share']["source_cgsnapshot_member_id"])
|
||||
|
||||
self.assertIsNone(res_dict['share']["share_group_id"])
|
||||
self.assertIsNone(
|
||||
res_dict['share']["source_share_group_snapshot_member_id"])
|
||||
|
||||
def test_share_not_updates_size(self):
|
||||
req = fakes.HTTPRequest.blank('/share/1')
|
||||
@ -1570,7 +1585,7 @@ class ShareAPITest(test.TestCase):
|
||||
self.mock_object(share_api.API, 'get_all',
|
||||
stubs.stub_share_get_all_by_project)
|
||||
res_dict = self.controller.detail(req)
|
||||
self.assertEqual(expected, res_dict)
|
||||
self.assertDictListMatch(expected['shares'], res_dict['shares'])
|
||||
self.assertEqual(res_dict['shares'][0]['volume_type'],
|
||||
res_dict['shares'][0]['share_type'])
|
||||
|
||||
@ -1581,13 +1596,23 @@ class ShareAPITest(test.TestCase):
|
||||
expected['shares'][0].pop('snapshot_support')
|
||||
self._list_detail_test_common(req, expected)
|
||||
|
||||
def test_share_list_detail_with_consistency_group(self):
|
||||
def test_share_list_detail_with_share_group(self):
|
||||
env = {'QUERY_STRING': 'name=Share+Test+Name'}
|
||||
req = fakes.HTTPRequest.blank('/shares/detail', environ=env,
|
||||
version="2.4")
|
||||
req = fakes.HTTPRequest.blank(
|
||||
'/shares/detail', environ=env, version="2.31", experimental=True)
|
||||
expected = self._list_detail_common_expected()
|
||||
expected['shares'][0]['consistency_group_id'] = None
|
||||
expected['shares'][0]['source_cgsnapshot_member_id'] = None
|
||||
expected['shares'][0]['task_state'] = None
|
||||
expected['shares'][0]['share_type_name'] = None
|
||||
expected['shares'][0].pop('export_location')
|
||||
expected['shares'][0].pop('export_locations')
|
||||
expected['shares'][0]['access_rules_status'] = 'active'
|
||||
expected['shares'][0]['replication_type'] = None
|
||||
expected['shares'][0]['has_replicas'] = False
|
||||
expected['shares'][0]['user_id'] = 'fakeuser'
|
||||
expected['shares'][0]['create_share_from_snapshot_support'] = True
|
||||
expected['shares'][0]['revert_to_snapshot_support'] = False
|
||||
expected['shares'][0]['share_group_id'] = None
|
||||
expected['shares'][0]['source_share_group_snapshot_member_id'] = None
|
||||
self._list_detail_test_common(req, expected)
|
||||
|
||||
def test_share_list_detail_with_task_state(self):
|
||||
@ -1595,8 +1620,6 @@ class ShareAPITest(test.TestCase):
|
||||
req = fakes.HTTPRequest.blank('/shares/detail', environ=env,
|
||||
version="2.5")
|
||||
expected = self._list_detail_common_expected()
|
||||
expected['shares'][0]['consistency_group_id'] = None
|
||||
expected['shares'][0]['source_cgsnapshot_member_id'] = None
|
||||
expected['shares'][0]['task_state'] = None
|
||||
self._list_detail_test_common(req, expected)
|
||||
|
||||
@ -1605,8 +1628,6 @@ class ShareAPITest(test.TestCase):
|
||||
req = fakes.HTTPRequest.blank('/shares/detail', environ=env,
|
||||
version="2.9")
|
||||
expected = self._list_detail_common_expected()
|
||||
expected['shares'][0]['consistency_group_id'] = None
|
||||
expected['shares'][0]['source_cgsnapshot_member_id'] = None
|
||||
expected['shares'][0]['task_state'] = None
|
||||
expected['shares'][0]['share_type_name'] = None
|
||||
expected['shares'][0].pop('export_location')
|
||||
@ -1642,8 +1663,6 @@ class ShareAPITest(test.TestCase):
|
||||
'share_type': '1',
|
||||
'volume_type': '1',
|
||||
'is_public': False,
|
||||
'consistency_group_id': None,
|
||||
'source_cgsnapshot_member_id': None,
|
||||
'snapshot_support': True,
|
||||
'has_replicas': False,
|
||||
'replication_type': None,
|
||||
@ -2463,8 +2482,6 @@ class ShareManageTest(test.TestCase):
|
||||
'share_type': '1',
|
||||
'volume_type': '1',
|
||||
'is_public': False,
|
||||
'consistency_group_id': None,
|
||||
'source_cgsnapshot_member_id': None,
|
||||
'snapshot_support': True,
|
||||
'task_state': None,
|
||||
'links': [
|
||||
|
@ -1838,3 +1838,181 @@ class AddCastRulesToReadonlyToInstances(BaseMigrationChecks):
|
||||
|
||||
for instance in engine.execute(share_instances_table.select()):
|
||||
self.test_case.assertNotIn('cast_rules_to_readonly', instance)
|
||||
|
||||
|
||||
@map_to_migration('03da71c0e321')
|
||||
class ShareGroupMigrationChecks(BaseMigrationChecks):
|
||||
|
||||
def setup_upgrade_data(self, engine):
|
||||
# Create share type
|
||||
self.share_type_id = uuidutils.generate_uuid()
|
||||
st_fixture = {
|
||||
'deleted': "False",
|
||||
'id': self.share_type_id,
|
||||
}
|
||||
st_table = utils.load_table('share_types', engine)
|
||||
engine.execute(st_table.insert(st_fixture))
|
||||
|
||||
# Create CG
|
||||
self.cg_id = uuidutils.generate_uuid()
|
||||
cg_fixture = {
|
||||
'deleted': "False",
|
||||
'id': self.cg_id,
|
||||
'user_id': 'fake_user',
|
||||
'project_id': 'fake_project_id',
|
||||
}
|
||||
cg_table = utils.load_table('consistency_groups', engine)
|
||||
engine.execute(cg_table.insert(cg_fixture))
|
||||
|
||||
# Create share_type group mapping
|
||||
self.mapping_id = uuidutils.generate_uuid()
|
||||
mapping_fixture = {
|
||||
'deleted': "False",
|
||||
'id': self.mapping_id,
|
||||
'consistency_group_id': self.cg_id,
|
||||
'share_type_id': self.share_type_id,
|
||||
}
|
||||
mapping_table = utils.load_table(
|
||||
'consistency_group_share_type_mappings', engine)
|
||||
engine.execute(mapping_table.insert(mapping_fixture))
|
||||
|
||||
# Create share
|
||||
self.share_id = uuidutils.generate_uuid()
|
||||
share_fixture = {
|
||||
'deleted': "False",
|
||||
'id': self.share_id,
|
||||
'consistency_group_id': self.cg_id,
|
||||
'user_id': 'fake_user',
|
||||
'project_id': 'fake_project_id',
|
||||
}
|
||||
share_table = utils.load_table('shares', engine)
|
||||
engine.execute(share_table.insert(share_fixture))
|
||||
|
||||
# Create share instance
|
||||
self.share_instance_id = uuidutils.generate_uuid()
|
||||
share_instance_fixture = {
|
||||
'deleted': "False",
|
||||
'share_type_id': self.share_type_id,
|
||||
'id': self.share_instance_id,
|
||||
'share_id': self.share_id,
|
||||
'cast_rules_to_readonly': False,
|
||||
}
|
||||
share_instance_table = utils.load_table('share_instances', engine)
|
||||
engine.execute(share_instance_table.insert(share_instance_fixture))
|
||||
|
||||
# Create cgsnapshot
|
||||
self.cgsnapshot_id = uuidutils.generate_uuid()
|
||||
cg_snap_fixture = {
|
||||
'deleted': "False",
|
||||
'id': self.cgsnapshot_id,
|
||||
'consistency_group_id': self.cg_id,
|
||||
'user_id': 'fake_user',
|
||||
'project_id': 'fake_project_id',
|
||||
}
|
||||
cgsnapshots_table = utils.load_table('cgsnapshots', engine)
|
||||
engine.execute(cgsnapshots_table.insert(cg_snap_fixture))
|
||||
|
||||
# Create cgsnapshot member
|
||||
self.cgsnapshot_member_id = uuidutils.generate_uuid()
|
||||
cg_snap_member_fixture = {
|
||||
'deleted': "False",
|
||||
'id': self.cgsnapshot_member_id,
|
||||
'cgsnapshot_id': self.cgsnapshot_id,
|
||||
'share_type_id': self.share_type_id,
|
||||
'share_instance_id': self.share_instance_id,
|
||||
'share_id': self.share_id,
|
||||
'user_id': 'fake_user',
|
||||
'project_id': 'fake_project_id',
|
||||
}
|
||||
cgsnapshot_members_table = utils.load_table(
|
||||
'cgsnapshot_members', engine)
|
||||
engine.execute(cgsnapshot_members_table.insert(cg_snap_member_fixture))
|
||||
|
||||
def check_upgrade(self, engine, data):
|
||||
sg_table = utils.load_table("share_groups", engine)
|
||||
db_result = engine.execute(sg_table.select().where(
|
||||
sg_table.c.id == self.cg_id))
|
||||
self.test_case.assertEqual(1, db_result.rowcount)
|
||||
sg = db_result.first()
|
||||
self.test_case.assertIsNone(sg['source_share_group_snapshot_id'])
|
||||
|
||||
share_table = utils.load_table("shares", engine)
|
||||
share_result = engine.execute(share_table.select().where(
|
||||
share_table.c.id == self.share_id))
|
||||
self.test_case.assertEqual(1, share_result.rowcount)
|
||||
share = share_result.first()
|
||||
self.test_case.assertEqual(self.cg_id, share['share_group_id'])
|
||||
self.test_case.assertIsNone(
|
||||
share['source_share_group_snapshot_member_id'])
|
||||
|
||||
mapping_table = utils.load_table(
|
||||
"share_group_share_type_mappings", engine)
|
||||
mapping_result = engine.execute(mapping_table.select().where(
|
||||
mapping_table.c.id == self.mapping_id))
|
||||
self.test_case.assertEqual(1, mapping_result.rowcount)
|
||||
mapping_record = mapping_result.first()
|
||||
self.test_case.assertEqual(
|
||||
self.cg_id, mapping_record['share_group_id'])
|
||||
self.test_case.assertEqual(
|
||||
self.share_type_id, mapping_record['share_type_id'])
|
||||
|
||||
sgs_table = utils.load_table("share_group_snapshots", engine)
|
||||
db_result = engine.execute(sgs_table.select().where(
|
||||
sgs_table.c.id == self.cgsnapshot_id))
|
||||
self.test_case.assertEqual(1, db_result.rowcount)
|
||||
sgs = db_result.first()
|
||||
self.test_case.assertEqual(self.cg_id, sgs['share_group_id'])
|
||||
|
||||
sgsm_table = utils.load_table("share_group_snapshot_members", engine)
|
||||
db_result = engine.execute(sgsm_table.select().where(
|
||||
sgsm_table.c.id == self.cgsnapshot_member_id))
|
||||
self.test_case.assertEqual(1, db_result.rowcount)
|
||||
sgsm = db_result.first()
|
||||
self.test_case.assertEqual(
|
||||
self.cgsnapshot_id, sgsm['share_group_snapshot_id'])
|
||||
self.test_case.assertNotIn('share_type_id', sgsm)
|
||||
|
||||
def check_downgrade(self, engine):
|
||||
cg_table = utils.load_table("consistency_groups", engine)
|
||||
db_result = engine.execute(cg_table.select().where(
|
||||
cg_table.c.id == self.cg_id))
|
||||
self.test_case.assertEqual(1, db_result.rowcount)
|
||||
cg = db_result.first()
|
||||
self.test_case.assertIsNone(cg['source_cgsnapshot_id'])
|
||||
|
||||
share_table = utils.load_table("shares", engine)
|
||||
share_result = engine.execute(share_table.select().where(
|
||||
share_table.c.id == self.share_id))
|
||||
self.test_case.assertEqual(1, share_result.rowcount)
|
||||
share = share_result.first()
|
||||
self.test_case.assertEqual(self.cg_id, share['consistency_group_id'])
|
||||
self.test_case.assertIsNone(
|
||||
share['source_cgsnapshot_member_id'])
|
||||
|
||||
mapping_table = utils.load_table(
|
||||
"consistency_group_share_type_mappings", engine)
|
||||
mapping_result = engine.execute(mapping_table.select().where(
|
||||
mapping_table.c.id == self.mapping_id))
|
||||
self.test_case.assertEqual(1, mapping_result.rowcount)
|
||||
cg_st_mapping = mapping_result.first()
|
||||
self.test_case.assertEqual(
|
||||
self.cg_id, cg_st_mapping['consistency_group_id'])
|
||||
self.test_case.assertEqual(
|
||||
self.share_type_id, cg_st_mapping['share_type_id'])
|
||||
|
||||
cg_snapshots_table = utils.load_table("cgsnapshots", engine)
|
||||
db_result = engine.execute(cg_snapshots_table.select().where(
|
||||
cg_snapshots_table.c.id == self.cgsnapshot_id))
|
||||
self.test_case.assertEqual(1, db_result.rowcount)
|
||||
cgsnap = db_result.first()
|
||||
self.test_case.assertEqual(self.cg_id, cgsnap['consistency_group_id'])
|
||||
|
||||
cg_snap_member_table = utils.load_table("cgsnapshot_members", engine)
|
||||
db_result = engine.execute(cg_snap_member_table.select().where(
|
||||
cg_snap_member_table.c.id == self.cgsnapshot_member_id))
|
||||
self.test_case.assertEqual(1, db_result.rowcount)
|
||||
member = db_result.first()
|
||||
self.test_case.assertEqual(
|
||||
self.cgsnapshot_id, member['cgsnapshot_id'])
|
||||
self.test_case.assertIn('share_type_id', member)
|
||||
self.test_case.assertEqual(self.share_type_id, member['share_type_id'])
|
||||
|
@ -266,12 +266,12 @@ class ShareDatabaseAPITestCase(test.TestCase):
|
||||
self.assertEqual(1, len(actual_result))
|
||||
self.assertEqual(share['id'], actual_result[0].id)
|
||||
|
||||
def test_share_filter_all_by_consistency_group(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
share = db_utils.create_share(consistency_group_id=cg['id'])
|
||||
def test_share_filter_all_by_share_group(self):
|
||||
group = db_utils.create_share_group()
|
||||
share = db_utils.create_share(share_group_id=group['id'])
|
||||
|
||||
actual_result = db_api.share_get_all_by_consistency_group_id(
|
||||
self.ctxt, cg['id'])
|
||||
actual_result = db_api.share_get_all_by_share_group_id(
|
||||
self.ctxt, group['id'])
|
||||
|
||||
self.assertEqual(1, len(actual_result))
|
||||
self.assertEqual(share['id'], actual_result[0].id)
|
||||
@ -322,20 +322,20 @@ class ShareDatabaseAPITestCase(test.TestCase):
|
||||
|
||||
self.assertEqual(0, len(instances))
|
||||
|
||||
def test_share_instance_get_all_by_consistency_group(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
db_utils.create_share(consistency_group_id=cg['id'])
|
||||
def test_share_instance_get_all_by_share_group(self):
|
||||
group = db_utils.create_share_group()
|
||||
db_utils.create_share(share_group_id=group['id'])
|
||||
db_utils.create_share()
|
||||
|
||||
instances = db_api.share_instances_get_all_by_consistency_group_id(
|
||||
self.ctxt, cg['id'])
|
||||
instances = db_api.share_instances_get_all_by_share_group_id(
|
||||
self.ctxt, group['id'])
|
||||
|
||||
self.assertEqual(1, len(instances))
|
||||
instance = instances[0]
|
||||
|
||||
self.assertEqual('share-%s' % instance['id'], instance['name'])
|
||||
|
||||
@ddt.data('host', 'consistency_group_id')
|
||||
@ddt.data('host', 'share_group_id')
|
||||
def test_share_get_all_sort_by_share_instance_fields(self, sort_key):
|
||||
shares = [db_utils.create_share(**{sort_key: n, 'size': 1})
|
||||
for n in ('test1', 'test2')]
|
||||
@ -384,7 +384,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
|
||||
expected_share_keys = {
|
||||
'project_id', 'share_type_id', 'display_name',
|
||||
'name', 'share_proto', 'is_public',
|
||||
'source_cgsnapshot_member_id',
|
||||
'source_share_group_snapshot_member_id',
|
||||
}
|
||||
session = db_api.get_session()
|
||||
|
||||
@ -432,7 +432,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
|
||||
expected_share_keys = {
|
||||
'project_id', 'share_type_id', 'display_name',
|
||||
'name', 'share_proto', 'is_public',
|
||||
'source_cgsnapshot_member_id',
|
||||
'source_share_group_snapshot_member_id',
|
||||
}
|
||||
session = db_api.get_session()
|
||||
|
||||
@ -497,7 +497,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
|
||||
expected_share_keys = {
|
||||
'project_id', 'share_type_id', 'display_name',
|
||||
'name', 'share_proto', 'is_public',
|
||||
'source_cgsnapshot_member_id',
|
||||
'source_share_group_snapshot_member_id',
|
||||
}
|
||||
|
||||
with session.begin():
|
||||
@ -542,7 +542,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
|
||||
expected_extra_keys = {
|
||||
'project_id', 'share_type_id', 'display_name',
|
||||
'name', 'share_proto', 'is_public',
|
||||
'source_cgsnapshot_member_id',
|
||||
'source_share_group_snapshot_member_id',
|
||||
}
|
||||
|
||||
share_replica = db_api.share_replica_get(self.ctxt, replica['id'])
|
||||
@ -559,7 +559,7 @@ class ShareDatabaseAPITestCase(test.TestCase):
|
||||
expected_extra_keys = {
|
||||
'project_id', 'share_type_id', 'display_name',
|
||||
'name', 'share_proto', 'is_public',
|
||||
'source_cgsnapshot_member_id',
|
||||
'source_share_group_snapshot_member_id',
|
||||
}
|
||||
|
||||
share_replica = db_api.share_replica_get(
|
||||
@ -639,273 +639,305 @@ class ShareDatabaseAPITestCase(test.TestCase):
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ConsistencyGroupDatabaseAPITestCase(test.TestCase):
|
||||
|
||||
class ShareGroupDatabaseAPITestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
"""Run before each test."""
|
||||
super(ConsistencyGroupDatabaseAPITestCase, self).setUp()
|
||||
super(ShareGroupDatabaseAPITestCase, self).setUp()
|
||||
self.ctxt = context.get_admin_context()
|
||||
|
||||
def test_consistency_group_create_with_share_type(self):
|
||||
def test_share_group_create_with_share_type(self):
|
||||
fake_share_types = ["fake_share_type"]
|
||||
cg = db_utils.create_consistency_group(share_types=fake_share_types)
|
||||
cg = db_api.consistency_group_get(self.ctxt, cg['id'])
|
||||
share_group = db_utils.create_share_group(share_types=fake_share_types)
|
||||
share_group = db_api.share_group_get(self.ctxt, share_group['id'])
|
||||
|
||||
self.assertEqual(1, len(cg['share_types']))
|
||||
self.assertEqual(1, len(share_group['share_types']))
|
||||
|
||||
def test_consistency_group_get(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
def test_share_group_get(self):
|
||||
share_group = db_utils.create_share_group()
|
||||
|
||||
self.assertDictMatch(dict(cg),
|
||||
dict(db_api.consistency_group_get(self.ctxt,
|
||||
cg['id'])))
|
||||
self.assertDictMatch(
|
||||
dict(share_group),
|
||||
dict(db_api.share_group_get(self.ctxt, share_group['id'])))
|
||||
|
||||
def test_count_consistency_groups_in_share_network(self):
|
||||
def test_count_share_groups_in_share_network(self):
|
||||
share_network = db_utils.create_share_network()
|
||||
db_utils.create_consistency_group()
|
||||
db_utils.create_consistency_group(share_network_id=share_network['id'])
|
||||
db_utils.create_share_group()
|
||||
db_utils.create_share_group(share_network_id=share_network['id'])
|
||||
|
||||
count = db_api.count_consistency_groups_in_share_network(
|
||||
count = db_api.count_share_groups_in_share_network(
|
||||
self.ctxt, share_network_id=share_network['id'])
|
||||
|
||||
self.assertEqual(1, count)
|
||||
|
||||
def test_consistency_group_get_all(self):
|
||||
expected_cg = db_utils.create_consistency_group()
|
||||
def test_share_group_get_all(self):
|
||||
expected_share_group = db_utils.create_share_group()
|
||||
|
||||
cgs = db_api.consistency_group_get_all(self.ctxt, detailed=False)
|
||||
share_groups = db_api.share_group_get_all(self.ctxt, detailed=False)
|
||||
|
||||
self.assertEqual(1, len(cgs))
|
||||
cg = cgs[0]
|
||||
self.assertEqual(2, len(dict(cg).keys()))
|
||||
self.assertEqual(expected_cg['id'], cg['id'])
|
||||
self.assertEqual(expected_cg['name'], cg['name'])
|
||||
self.assertEqual(1, len(share_groups))
|
||||
share_group = share_groups[0]
|
||||
self.assertEqual(2, len(dict(share_group).keys()))
|
||||
self.assertEqual(expected_share_group['id'], share_group['id'])
|
||||
self.assertEqual(expected_share_group['name'], share_group['name'])
|
||||
|
||||
def test_consistency_group_get_all_with_detail(self):
|
||||
expected_cg = db_utils.create_consistency_group()
|
||||
def test_share_group_get_all_with_detail(self):
|
||||
expected_share_group = db_utils.create_share_group()
|
||||
|
||||
cgs = db_api.consistency_group_get_all(self.ctxt, detailed=True)
|
||||
share_groups = db_api.share_group_get_all(self.ctxt, detailed=True)
|
||||
|
||||
self.assertEqual(1, len(cgs))
|
||||
cg = cgs[0]
|
||||
self.assertDictMatch(dict(expected_cg), dict(cg))
|
||||
self.assertEqual(1, len(share_groups))
|
||||
self.assertDictMatch(dict(expected_share_group), dict(share_groups[0]))
|
||||
|
||||
def test_consistency_group_get_all_by_project(self):
|
||||
def test_share_group_get_all_by_host(self):
|
||||
fake_host = 'my_fake_host'
|
||||
expected_share_group = db_utils.create_share_group(host=fake_host)
|
||||
db_utils.create_share_group()
|
||||
|
||||
share_groups = db_api.share_group_get_all_by_host(
|
||||
self.ctxt, fake_host, detailed=False)
|
||||
|
||||
self.assertEqual(1, len(share_groups))
|
||||
share_group = share_groups[0]
|
||||
self.assertEqual(2, len(dict(share_group).keys()))
|
||||
self.assertEqual(expected_share_group['id'], share_group['id'])
|
||||
self.assertEqual(expected_share_group['name'], share_group['name'])
|
||||
|
||||
def test_share_group_get_all_by_host_with_details(self):
|
||||
fake_host = 'my_fake_host'
|
||||
expected_share_group = db_utils.create_share_group(host=fake_host)
|
||||
db_utils.create_share_group()
|
||||
|
||||
share_groups = db_api.share_group_get_all_by_host(
|
||||
self.ctxt, fake_host, detailed=True)
|
||||
|
||||
self.assertEqual(1, len(share_groups))
|
||||
share_group = share_groups[0]
|
||||
self.assertDictMatch(dict(expected_share_group), dict(share_group))
|
||||
self.assertEqual(fake_host, share_group['host'])
|
||||
|
||||
def test_share_group_get_all_by_project(self):
|
||||
fake_project = 'fake_project'
|
||||
expected_cg = db_utils.create_consistency_group(
|
||||
expected_group = db_utils.create_share_group(
|
||||
project_id=fake_project)
|
||||
db_utils.create_consistency_group()
|
||||
db_utils.create_share_group()
|
||||
|
||||
cgs = db_api.consistency_group_get_all_by_project(self.ctxt,
|
||||
groups = db_api.share_group_get_all_by_project(self.ctxt,
|
||||
fake_project,
|
||||
detailed=False)
|
||||
|
||||
self.assertEqual(1, len(cgs))
|
||||
cg = cgs[0]
|
||||
self.assertEqual(2, len(dict(cg).keys()))
|
||||
self.assertEqual(expected_cg['id'], cg['id'])
|
||||
self.assertEqual(expected_cg['name'], cg['name'])
|
||||
self.assertEqual(1, len(groups))
|
||||
group = groups[0]
|
||||
self.assertEqual(2, len(dict(group).keys()))
|
||||
self.assertEqual(expected_group['id'], group['id'])
|
||||
self.assertEqual(expected_group['name'], group['name'])
|
||||
|
||||
def test_consistency_group_get_all_by_share_server(self):
|
||||
def test_share_group_get_all_by_share_server(self):
|
||||
fake_server = 123
|
||||
expected_cg = db_utils.create_consistency_group(
|
||||
expected_group = db_utils.create_share_group(
|
||||
share_server_id=fake_server)
|
||||
db_utils.create_consistency_group()
|
||||
db_utils.create_share_group()
|
||||
|
||||
cgs = db_api.consistency_group_get_all_by_share_server(self.ctxt,
|
||||
groups = db_api.share_group_get_all_by_share_server(self.ctxt,
|
||||
fake_server)
|
||||
|
||||
self.assertEqual(1, len(cgs))
|
||||
cg = cgs[0]
|
||||
self.assertEqual(expected_cg['id'], cg['id'])
|
||||
self.assertEqual(expected_cg['name'], cg['name'])
|
||||
self.assertEqual(1, len(groups))
|
||||
group = groups[0]
|
||||
self.assertEqual(expected_group['id'], group['id'])
|
||||
self.assertEqual(expected_group['name'], group['name'])
|
||||
|
||||
def test_consistency_group_get_all_by_project_with_details(self):
|
||||
def test_share_group_get_all_by_project_with_details(self):
|
||||
fake_project = 'fake_project'
|
||||
expected_cg = db_utils.create_consistency_group(
|
||||
expected_group = db_utils.create_share_group(
|
||||
project_id=fake_project)
|
||||
db_utils.create_consistency_group()
|
||||
db_utils.create_share_group()
|
||||
|
||||
cgs = db_api.consistency_group_get_all_by_project(self.ctxt,
|
||||
groups = db_api.share_group_get_all_by_project(self.ctxt,
|
||||
fake_project,
|
||||
detailed=True)
|
||||
|
||||
self.assertEqual(1, len(cgs))
|
||||
cg = cgs[0]
|
||||
self.assertDictMatch(dict(expected_cg), dict(cg))
|
||||
self.assertEqual(fake_project, cg['project_id'])
|
||||
self.assertEqual(1, len(groups))
|
||||
group = groups[0]
|
||||
self.assertDictMatch(dict(expected_group), dict(group))
|
||||
self.assertEqual(fake_project, group['project_id'])
|
||||
|
||||
def test_consistency_group_update(self):
|
||||
def test_share_group_update(self):
|
||||
fake_name = "my_fake_name"
|
||||
expected_cg = db_utils.create_consistency_group()
|
||||
expected_cg['name'] = fake_name
|
||||
expected_group = db_utils.create_share_group()
|
||||
expected_group['name'] = fake_name
|
||||
|
||||
db_api.consistency_group_update(self.ctxt,
|
||||
expected_cg['id'],
|
||||
db_api.share_group_update(self.ctxt,
|
||||
expected_group['id'],
|
||||
{'name': fake_name})
|
||||
|
||||
cg = db_api.consistency_group_get(self.ctxt, expected_cg['id'])
|
||||
self.assertEqual(fake_name, cg['name'])
|
||||
group = db_api.share_group_get(self.ctxt, expected_group['id'])
|
||||
self.assertEqual(fake_name, group['name'])
|
||||
|
||||
def test_consistency_group_destroy(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
db_api.consistency_group_get(self.ctxt, cg['id'])
|
||||
def test_share_group_destroy(self):
|
||||
group = db_utils.create_share_group()
|
||||
db_api.share_group_get(self.ctxt, group['id'])
|
||||
|
||||
db_api.consistency_group_destroy(self.ctxt, cg['id'])
|
||||
db_api.share_group_destroy(self.ctxt, group['id'])
|
||||
|
||||
self.assertRaises(exception.NotFound, db_api.consistency_group_get,
|
||||
self.ctxt, cg['id'])
|
||||
self.assertRaises(exception.NotFound, db_api.share_group_get,
|
||||
self.ctxt, group['id'])
|
||||
|
||||
def test_count_shares_in_consistency_group(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
db_utils.create_share(consistency_group_id=cg['id'])
|
||||
def test_count_shares_in_share_group(self):
|
||||
sg = db_utils.create_share_group()
|
||||
db_utils.create_share(share_group_id=sg['id'])
|
||||
db_utils.create_share()
|
||||
|
||||
count = db_api.count_shares_in_consistency_group(self.ctxt, cg['id'])
|
||||
count = db_api.count_shares_in_share_group(self.ctxt, sg['id'])
|
||||
|
||||
self.assertEqual(1, count)
|
||||
|
||||
def test_count_cgsnapshots_in_consistency_group(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
db_utils.create_cgsnapshot(cg['id'])
|
||||
db_utils.create_cgsnapshot(cg['id'])
|
||||
def test_count_sg_snapshots_in_share_group(self):
|
||||
sg = db_utils.create_share_group()
|
||||
db_utils.create_share_group_snapshot(sg['id'])
|
||||
db_utils.create_share_group_snapshot(sg['id'])
|
||||
|
||||
count = db_api.count_cgsnapshots_in_consistency_group(self.ctxt,
|
||||
cg['id'])
|
||||
count = db_api.count_share_group_snapshots_in_share_group(
|
||||
self.ctxt, sg['id'])
|
||||
|
||||
self.assertEqual(2, count)
|
||||
|
||||
def test_cgsnapshot_get(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
cgsnap = db_utils.create_cgsnapshot(cg['id'])
|
||||
def test_share_group_snapshot_get(self):
|
||||
sg = db_utils.create_share_group()
|
||||
sg_snap = db_utils.create_share_group_snapshot(sg['id'])
|
||||
|
||||
self.assertDictMatch(dict(cgsnap),
|
||||
dict(db_api.cgsnapshot_get(self.ctxt,
|
||||
cgsnap['id'])))
|
||||
self.assertDictMatch(
|
||||
dict(sg_snap),
|
||||
dict(db_api.share_group_snapshot_get(self.ctxt, sg_snap['id'])))
|
||||
|
||||
def test_cgsnapshot_get_all(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'])
|
||||
def test_share_group_snapshot_get_all(self):
|
||||
sg = db_utils.create_share_group()
|
||||
expected_sg_snap = db_utils.create_share_group_snapshot(sg['id'])
|
||||
|
||||
snaps = db_api.cgsnapshot_get_all(self.ctxt, detailed=False)
|
||||
snaps = db_api.share_group_snapshot_get_all(self.ctxt, detailed=False)
|
||||
|
||||
self.assertEqual(1, len(snaps))
|
||||
snap = snaps[0]
|
||||
self.assertEqual(2, len(dict(snap).keys()))
|
||||
self.assertEqual(expected_cgsnap['id'], snap['id'])
|
||||
self.assertEqual(expected_cgsnap['name'], snap['name'])
|
||||
self.assertEqual(expected_sg_snap['id'], snap['id'])
|
||||
self.assertEqual(expected_sg_snap['name'], snap['name'])
|
||||
|
||||
def test_cgsnapshot_get_all_with_detail(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'])
|
||||
def test_share_group_snapshot_get_all_with_detail(self):
|
||||
sg = db_utils.create_share_group()
|
||||
expected_sg_snap = db_utils.create_share_group_snapshot(sg['id'])
|
||||
|
||||
snaps = db_api.cgsnapshot_get_all(self.ctxt, detailed=True)
|
||||
snaps = db_api.share_group_snapshot_get_all(self.ctxt, detailed=True)
|
||||
|
||||
self.assertEqual(1, len(snaps))
|
||||
snap = snaps[0]
|
||||
self.assertDictMatch(dict(expected_cgsnap), dict(snap))
|
||||
self.assertDictMatch(dict(expected_sg_snap), dict(snap))
|
||||
|
||||
def test_cgsnapshot_get_all_by_project(self):
|
||||
fake_project = 'fake_project'
|
||||
cg = db_utils.create_consistency_group()
|
||||
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'],
|
||||
project_id=fake_project)
|
||||
def test_share_group_snapshot_get_all_by_project(self):
|
||||
fake_project = uuidutils.generate_uuid()
|
||||
sg = db_utils.create_share_group()
|
||||
expected_sg_snap = db_utils.create_share_group_snapshot(
|
||||
sg['id'], project_id=fake_project)
|
||||
|
||||
snaps = db_api.cgsnapshot_get_all_by_project(self.ctxt,
|
||||
fake_project,
|
||||
detailed=False)
|
||||
snaps = db_api.share_group_snapshot_get_all_by_project(
|
||||
self.ctxt, fake_project, detailed=False)
|
||||
|
||||
self.assertEqual(1, len(snaps))
|
||||
snap = snaps[0]
|
||||
self.assertEqual(2, len(dict(snap).keys()))
|
||||
self.assertEqual(expected_cgsnap['id'], snap['id'])
|
||||
self.assertEqual(expected_cgsnap['name'], snap['name'])
|
||||
self.assertEqual(expected_sg_snap['id'], snap['id'])
|
||||
self.assertEqual(expected_sg_snap['name'], snap['name'])
|
||||
|
||||
def test_cgsnapshot_get_all_by_project_with_details(self):
|
||||
fake_project = 'fake_project'
|
||||
cg = db_utils.create_consistency_group()
|
||||
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'],
|
||||
project_id=fake_project)
|
||||
def test_share_group_snapshot_get_all_by_project_with_details(self):
|
||||
fake_project = uuidutils.generate_uuid()
|
||||
sg = db_utils.create_share_group()
|
||||
expected_sg_snap = db_utils.create_share_group_snapshot(
|
||||
sg['id'], project_id=fake_project)
|
||||
|
||||
snaps = db_api.cgsnapshot_get_all_by_project(self.ctxt,
|
||||
fake_project,
|
||||
detailed=True)
|
||||
snaps = db_api.share_group_snapshot_get_all_by_project(
|
||||
self.ctxt, fake_project, detailed=True)
|
||||
|
||||
self.assertEqual(1, len(snaps))
|
||||
snap = snaps[0]
|
||||
self.assertDictMatch(dict(expected_cgsnap), dict(snap))
|
||||
self.assertDictMatch(dict(expected_sg_snap), dict(snap))
|
||||
self.assertEqual(fake_project, snap['project_id'])
|
||||
|
||||
def test_cgsnapshot_update(self):
|
||||
def test_share_group_snapshot_update(self):
|
||||
fake_name = "my_fake_name"
|
||||
cg = db_utils.create_consistency_group()
|
||||
expected_cgsnap = db_utils.create_cgsnapshot(cg['id'])
|
||||
expected_cgsnap['name'] = fake_name
|
||||
sg = db_utils.create_share_group()
|
||||
expected_sg_snap = db_utils.create_share_group_snapshot(sg['id'])
|
||||
expected_sg_snap['name'] = fake_name
|
||||
|
||||
db_api.cgsnapshot_update(self.ctxt, expected_cgsnap['id'],
|
||||
{'name': fake_name})
|
||||
db_api.share_group_snapshot_update(
|
||||
self.ctxt, expected_sg_snap['id'], {'name': fake_name})
|
||||
|
||||
cgsnap = db_api.cgsnapshot_get(self.ctxt, expected_cgsnap['id'])
|
||||
self.assertEqual(fake_name, cgsnap['name'])
|
||||
sg_snap = db_api.share_group_snapshot_get(
|
||||
self.ctxt, expected_sg_snap['id'])
|
||||
self.assertEqual(fake_name, sg_snap['name'])
|
||||
|
||||
def test_cgsnapshot_destroy(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
cgsnap = db_utils.create_cgsnapshot(cg['id'])
|
||||
db_api.cgsnapshot_get(self.ctxt, cgsnap['id'])
|
||||
def test_share_group_snapshot_destroy(self):
|
||||
sg = db_utils.create_share_group()
|
||||
sg_snap = db_utils.create_share_group_snapshot(sg['id'])
|
||||
db_api.share_group_snapshot_get(self.ctxt, sg_snap['id'])
|
||||
|
||||
db_api.cgsnapshot_destroy(self.ctxt, cgsnap['id'])
|
||||
db_api.share_group_snapshot_destroy(self.ctxt, sg_snap['id'])
|
||||
|
||||
self.assertRaises(exception.NotFound, db_api.cgsnapshot_get,
|
||||
self.ctxt, cgsnap['id'])
|
||||
self.assertRaises(
|
||||
exception.NotFound,
|
||||
db_api.share_group_snapshot_get, self.ctxt, sg_snap['id'])
|
||||
|
||||
def test_cgsnapshot_members_get_all(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
cgsnap = db_utils.create_cgsnapshot(cg['id'])
|
||||
expected_member = db_utils.create_cgsnapshot_member(cgsnap['id'])
|
||||
def test_share_group_snapshot_members_get_all(self):
|
||||
sg = db_utils.create_share_group()
|
||||
sg_snap = db_utils.create_share_group_snapshot(sg['id'])
|
||||
expected_member = db_utils.create_share_group_snapshot_member(
|
||||
sg_snap['id'])
|
||||
|
||||
members = db_api.cgsnapshot_members_get_all(self.ctxt, cgsnap['id'])
|
||||
members = db_api.share_group_snapshot_members_get_all(
|
||||
self.ctxt, sg_snap['id'])
|
||||
|
||||
self.assertEqual(1, len(members))
|
||||
member = members[0]
|
||||
self.assertDictMatch(dict(expected_member), dict(member))
|
||||
self.assertDictMatch(dict(expected_member), dict(members[0]))
|
||||
|
||||
def test_count_cgsnapshot_members_in_share(self):
|
||||
def test_count_share_group_snapshot_members_in_share(self):
|
||||
share = db_utils.create_share()
|
||||
share2 = db_utils.create_share()
|
||||
cg = db_utils.create_consistency_group()
|
||||
cgsnap = db_utils.create_cgsnapshot(cg['id'])
|
||||
db_utils.create_cgsnapshot_member(cgsnap['id'], share_id=share['id'])
|
||||
db_utils.create_cgsnapshot_member(cgsnap['id'], share_id=share2['id'])
|
||||
sg = db_utils.create_share_group()
|
||||
sg_snap = db_utils.create_share_group_snapshot(sg['id'])
|
||||
db_utils.create_share_group_snapshot_member(
|
||||
sg_snap['id'], share_id=share['id'])
|
||||
db_utils.create_share_group_snapshot_member(
|
||||
sg_snap['id'], share_id=share2['id'])
|
||||
|
||||
count = db_api.count_cgsnapshot_members_in_share(
|
||||
count = db_api.count_share_group_snapshot_members_in_share(
|
||||
self.ctxt, share['id'])
|
||||
|
||||
self.assertEqual(1, count)
|
||||
|
||||
def test_cgsnapshot_members_get(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
cgsnap = db_utils.create_cgsnapshot(cg['id'])
|
||||
expected_member = db_utils.create_cgsnapshot_member(cgsnap['id'])
|
||||
def test_share_group_snapshot_members_get(self):
|
||||
sg = db_utils.create_share_group()
|
||||
sg_snap = db_utils.create_share_group_snapshot(sg['id'])
|
||||
expected_member = db_utils.create_share_group_snapshot_member(
|
||||
sg_snap['id'])
|
||||
|
||||
member = db_api.cgsnapshot_member_get(self.ctxt,
|
||||
expected_member['id'])
|
||||
member = db_api.share_group_snapshot_member_get(
|
||||
self.ctxt, expected_member['id'])
|
||||
|
||||
self.assertDictMatch(dict(expected_member), dict(member))
|
||||
|
||||
def test_cgsnapshot_members_get_not_found(self):
|
||||
self.assertRaises(exception.CGSnapshotMemberNotFound,
|
||||
db_api.cgsnapshot_member_get, self.ctxt, 'fake_id')
|
||||
def test_share_group_snapshot_members_get_not_found(self):
|
||||
self.assertRaises(
|
||||
exception.ShareGroupSnapshotMemberNotFound,
|
||||
db_api.share_group_snapshot_member_get, self.ctxt, 'fake_id')
|
||||
|
||||
def test_cgsnapshot_member_update(self):
|
||||
cg = db_utils.create_consistency_group()
|
||||
cgsnap = db_utils.create_cgsnapshot(cg['id'])
|
||||
expected_member = db_utils.create_cgsnapshot_member(cgsnap['id'])
|
||||
def test_share_group_snapshot_member_update(self):
|
||||
sg = db_utils.create_share_group()
|
||||
sg_snap = db_utils.create_share_group_snapshot(sg['id'])
|
||||
expected_member = db_utils.create_share_group_snapshot_member(
|
||||
sg_snap['id'])
|
||||
|
||||
db_api.cgsnapshot_member_update(
|
||||
db_api.share_group_snapshot_member_update(
|
||||
self.ctxt, expected_member['id'],
|
||||
{'status': constants.STATUS_AVAILABLE})
|
||||
|
||||
member = db_api.cgsnapshot_member_get(self.ctxt, expected_member['id'])
|
||||
member = db_api.share_group_snapshot_member_get(
|
||||
self.ctxt, expected_member['id'])
|
||||
self.assertEqual(constants.STATUS_AVAILABLE, member['status'])
|
||||
|
||||
|
||||
@ -2360,19 +2392,6 @@ class PurgeDeletedTest(test.TestCase):
|
||||
'deleted_at': self._days_ago(start,
|
||||
end)},
|
||||
create_snapshot_instance=False)
|
||||
# create consistency group
|
||||
cg = db_utils.create_consistency_group(
|
||||
deleted_at=self._days_ago(start, end))
|
||||
# create cg snapshot
|
||||
db_utils.create_cgsnapshot(
|
||||
cg.id, deleted_at=self._days_ago(start, end))
|
||||
# create cgsnapshot member
|
||||
db_api.cgsnapshot_member_create(
|
||||
self.context,
|
||||
{'id': uuidutils.generate_uuid(),
|
||||
'share_id': share.id,
|
||||
'share_instance_id': s_instance.id,
|
||||
'deleted_at': self._days_ago(start, end)})
|
||||
# update share instance
|
||||
db_api.share_instance_update(
|
||||
self.context,
|
||||
@ -2384,9 +2403,7 @@ class PurgeDeletedTest(test.TestCase):
|
||||
for model in [models.ShareTypes, models.Share,
|
||||
models.ShareNetwork, models.ShareAccessMapping,
|
||||
models.ShareInstance, models.ShareServer,
|
||||
models.ShareSnapshot, models.ConsistencyGroup,
|
||||
models.CGSnapshot, models.SecurityService,
|
||||
models.CGSnapshotMember]:
|
||||
models.ShareSnapshot, models.SecurityService]:
|
||||
rows = db_api.model_query(self.context, model).count()
|
||||
self.assertEqual(num_left, rows)
|
||||
|
||||
|
@ -29,9 +29,9 @@ def _create_db_row(method, default_values, custom_values):
|
||||
return method(context.get_admin_context(), default_values)
|
||||
|
||||
|
||||
def create_consistency_group(**kwargs):
|
||||
"""Create a consistency group object."""
|
||||
cg = {
|
||||
def create_share_group(**kwargs):
|
||||
"""Create a share group object."""
|
||||
share_group = {
|
||||
'share_network_id': None,
|
||||
'share_server_id': None,
|
||||
'user_id': 'fake',
|
||||
@ -39,22 +39,22 @@ def create_consistency_group(**kwargs):
|
||||
'status': constants.STATUS_CREATING,
|
||||
'host': 'fake_host'
|
||||
}
|
||||
return _create_db_row(db.consistency_group_create, cg, kwargs)
|
||||
return _create_db_row(db.share_group_create, share_group, kwargs)
|
||||
|
||||
|
||||
def create_cgsnapshot(cg_id, **kwargs):
|
||||
"""Create a cgsnapshot object."""
|
||||
def create_share_group_snapshot(share_group_id, **kwargs):
|
||||
"""Create a share group snapshot object."""
|
||||
snapshot = {
|
||||
'consistency_group_id': cg_id,
|
||||
'share_group_id': share_group_id,
|
||||
'user_id': 'fake',
|
||||
'project_id': 'fake',
|
||||
'status': constants.STATUS_CREATING,
|
||||
}
|
||||
return _create_db_row(db.cgsnapshot_create, snapshot, kwargs)
|
||||
return _create_db_row(db.share_group_snapshot_create, snapshot, kwargs)
|
||||
|
||||
|
||||
def create_cgsnapshot_member(cgsnapshot_id, **kwargs):
|
||||
"""Create a cgsnapshot member object."""
|
||||
def create_share_group_snapshot_member(share_group_snapshot_id, **kwargs):
|
||||
"""Create a share group snapshot member object."""
|
||||
member = {
|
||||
'share_proto': "NFS",
|
||||
'size': 0,
|
||||
@ -63,9 +63,10 @@ def create_cgsnapshot_member(cgsnapshot_id, **kwargs):
|
||||
'user_id': 'fake',
|
||||
'project_id': 'fake',
|
||||
'status': 'creating',
|
||||
'cgsnapshot_id': cgsnapshot_id,
|
||||
'share_group_snapshot_id': share_group_snapshot_id,
|
||||
}
|
||||
return _create_db_row(db.cgsnapshot_member_create, member, kwargs)
|
||||
return _create_db_row(
|
||||
db.share_group_snapshot_member_create, member, kwargs)
|
||||
|
||||
|
||||
def create_share_access(**kwargs):
|
||||
|
@ -104,8 +104,8 @@ class FakeShareDriver(driver.ShareDriver):
|
||||
return super(FakeShareDriver, self)._verify_share_server_handling(
|
||||
driver_handles_share_servers)
|
||||
|
||||
def create_consistency_group(self, context, cg_id):
|
||||
def create_share_group(self, context, group_id, share_server=None):
|
||||
pass
|
||||
|
||||
def delete_consistency_group(self, context, cg_id):
|
||||
def delete_share_group(self, context, group_id, share_server=None):
|
||||
pass
|
||||
|
@ -38,7 +38,7 @@ def fake_share(**kwargs):
|
||||
'snapshot_support': 'True',
|
||||
'replication_type': None,
|
||||
'is_busy': False,
|
||||
'consistency_group_id': 'fakecgid',
|
||||
'share_group_id': None,
|
||||
'instance': {'host': 'fakehost'},
|
||||
}
|
||||
share.update(kwargs)
|
||||
@ -256,8 +256,8 @@ def fake_replica(id=None, as_primitive=True, for_manager=False, **kwargs):
|
||||
'snapshot_id': None,
|
||||
'share_proto': None,
|
||||
'is_public': None,
|
||||
'consistency_group_id': None,
|
||||
'source_cgsnapshot_member_id': None,
|
||||
'share_group_id': None,
|
||||
'source_share_group_snapshot_member_id': None,
|
||||
'availability_zone': 'fake_az',
|
||||
})
|
||||
replica.update(kwargs)
|
||||
@ -278,7 +278,7 @@ def fake_replica_request_spec(as_primitive=True, **kwargs):
|
||||
'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
|
||||
'snapshot_id': None,
|
||||
'share_type': 'fake_share_type',
|
||||
'consistency_group': None,
|
||||
'share_group': None,
|
||||
'active_replica_host': 'fake_active_replica_host',
|
||||
'all_replica_hosts': all_replica_hosts,
|
||||
}
|
||||
|
@ -91,20 +91,21 @@
|
||||
"scheduler_stats:pools:index": "rule:admin_api",
|
||||
"scheduler_stats:pools:detail": "rule:admin_api",
|
||||
|
||||
"consistency_group:create" : "rule:default",
|
||||
"consistency_group:delete": "rule:default",
|
||||
"consistency_group:update": "rule:default",
|
||||
"consistency_group:get": "rule:default",
|
||||
"consistency_group:get_all": "rule:default",
|
||||
"consistency_group:create_cgsnapshot" : "rule:default",
|
||||
"consistency_group:delete_cgsnapshot": "rule:default",
|
||||
"consistency_group:force_delete": "rule:admin_api",
|
||||
"consistency_group:reset_status": "rule:admin_api",
|
||||
"consistency_group:get_cgsnapshot": "rule:default",
|
||||
"consistency_group:get_all_cgsnapshots": "rule:default",
|
||||
"share_group:create" : "rule:default",
|
||||
"share_group:delete": "rule:default",
|
||||
"share_group:update": "rule:default",
|
||||
"share_group:get": "rule:default",
|
||||
"share_group:get_all": "rule:default",
|
||||
"share_group:force_delete": "rule:admin_api",
|
||||
"share_group:reset_status": "rule:admin_api",
|
||||
|
||||
"cgsnapshot:force_delete": "rule:admin_api",
|
||||
"cgsnapshot:reset_status": "rule:admin_api",
|
||||
"share_group_snapshot:create" : "rule:default",
|
||||
"share_group_snapshot:delete": "rule:default",
|
||||
"share_group_snapshot:update" : "rule:default",
|
||||
"share_group_snapshot:get": "rule:default",
|
||||
"share_group_snapshot:get_all": "rule:default",
|
||||
"share_group_snapshot:force_delete": "rule:admin_api",
|
||||
"share_group_snapshot:reset_status": "rule:admin_api",
|
||||
|
||||
"share_replica:get_all": "rule:default",
|
||||
"share_replica:show": "rule:default",
|
||||
@ -114,5 +115,20 @@
|
||||
"share_replica:resync": "rule:admin_api",
|
||||
"share_replica:reset_status": "rule:admin_api",
|
||||
"share_replica:force_delete": "rule:admin_api",
|
||||
"share_replica:reset_replica_state": "rule:admin_api"
|
||||
"share_replica:reset_replica_state": "rule:admin_api",
|
||||
|
||||
"share_group_type:index": "rule:default",
|
||||
"share_group_type:show": "rule:default",
|
||||
"share_group_type:default": "rule:default",
|
||||
"share_group_type:create": "rule:admin_api",
|
||||
"share_group_type:delete": "rule:admin_api",
|
||||
"share_group_type:add_project_access": "rule:admin_api",
|
||||
"share_group_type:list_project_access": "rule:admin_api",
|
||||
"share_group_type:remove_project_access": "rule:admin_api",
|
||||
|
||||
"share_group_types_spec:create": "rule:admin_api",
|
||||
"share_group_types_spec:update": "rule:admin_api",
|
||||
"share_group_types_spec:show": "rule:admin_api",
|
||||
"share_group_types_spec:index": "rule:admin_api",
|
||||
"share_group_types_spec:delete": "rule:admin_api"
|
||||
}
|
||||
|
@ -208,34 +208,6 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase):
|
||||
self.assertIsNone(weighed_host)
|
||||
self.assertTrue(_mock_service_get_all_by_topic.called)
|
||||
|
||||
@mock.patch('manila.db.service_get_all_by_topic')
|
||||
def test_schedule_share_with_cg_pool_support(
|
||||
self, _mock_service_get_all_by_topic):
|
||||
sched = fakes.FakeFilterScheduler()
|
||||
sched.host_manager = fakes.FakeHostManager()
|
||||
fake_context = context.RequestContext('user', 'project',
|
||||
is_admin=True)
|
||||
fakes.mock_host_manager_db_calls(_mock_service_get_all_by_topic)
|
||||
request_spec = {
|
||||
'share_type': {
|
||||
'name': 'NFS',
|
||||
'extra_specs': {'consistency_group_support': 'pool'}
|
||||
},
|
||||
'share_properties': {'project_id': 1, 'size': 1},
|
||||
'share_instance_properties': {'project_id': 1, 'size': 1},
|
||||
'consistency_group': {
|
||||
'id': 'fake-cg-id',
|
||||
'host': 'host5#_pool0',
|
||||
}
|
||||
}
|
||||
|
||||
weighed_host = sched._schedule_share(fake_context, request_spec, {})
|
||||
|
||||
self.assertIsNotNone(weighed_host)
|
||||
self.assertIsNotNone(weighed_host.obj)
|
||||
self.assertEqual('host5#_pool0', weighed_host.obj.host)
|
||||
self.assertTrue(_mock_service_get_all_by_topic.called)
|
||||
|
||||
def _setup_dedupe_fakes(self, extra_specs):
|
||||
sched = fakes.FakeFilterScheduler()
|
||||
sched.host_manager = fakes.FakeHostManager()
|
||||
@ -401,41 +373,41 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase):
|
||||
filter_properties['retry']['hosts'][0])
|
||||
self.assertEqual(1024, host_state.total_capacity_gb)
|
||||
|
||||
def test_schedule_create_consistency_group(self):
|
||||
def test_schedule_create_share_group(self):
|
||||
# Ensure empty hosts/child_zones result in NoValidHosts exception.
|
||||
sched = fakes.FakeFilterScheduler()
|
||||
fake_context = context.RequestContext('user', 'project')
|
||||
fake_host = 'fake_host'
|
||||
request_spec = {'share_types': [{'id': 'NFS'}]}
|
||||
self.mock_object(sched, "_get_best_host_for_consistency_group",
|
||||
self.mock_object(sched, "_get_best_host_for_share_group",
|
||||
mock.Mock(return_value=fake_host))
|
||||
fake_updated_group = mock.Mock()
|
||||
self.mock_object(base, "cg_update_db", mock.Mock(
|
||||
self.mock_object(base, "share_group_update_db", mock.Mock(
|
||||
return_value=fake_updated_group))
|
||||
self.mock_object(sched.share_rpcapi, "create_consistency_group")
|
||||
self.mock_object(sched.share_rpcapi, "create_share_group")
|
||||
|
||||
sched.schedule_create_consistency_group(fake_context, 'fake_id',
|
||||
sched.schedule_create_share_group(fake_context, 'fake_id',
|
||||
request_spec, {})
|
||||
|
||||
sched._get_best_host_for_consistency_group.assert_called_once_with(
|
||||
sched._get_best_host_for_share_group.assert_called_once_with(
|
||||
fake_context, request_spec)
|
||||
base.cg_update_db.assert_called_once_with(
|
||||
base.share_group_update_db.assert_called_once_with(
|
||||
fake_context, 'fake_id', fake_host)
|
||||
sched.share_rpcapi.create_consistency_group.assert_called_once_with(
|
||||
sched.share_rpcapi.create_share_group.assert_called_once_with(
|
||||
fake_context, fake_updated_group, fake_host)
|
||||
|
||||
def test_create_cg_no_hosts(self):
|
||||
def test_create_group_no_hosts(self):
|
||||
# Ensure empty hosts/child_zones result in NoValidHosts exception.
|
||||
sched = fakes.FakeFilterScheduler()
|
||||
fake_context = context.RequestContext('user', 'project')
|
||||
request_spec = {'share_types': [{'id': 'NFS'}]}
|
||||
|
||||
self.assertRaises(exception.NoValidHost,
|
||||
sched.schedule_create_consistency_group,
|
||||
sched.schedule_create_share_group,
|
||||
fake_context, 'fake_id', request_spec, {})
|
||||
|
||||
@mock.patch('manila.db.service_get_all_by_topic')
|
||||
def test_get_weighted_candidates_for_consistency_group(
|
||||
def test_get_weighted_candidates_for_share_group(
|
||||
self, _mock_service_get_all_by_topic):
|
||||
sched = fakes.FakeFilterScheduler()
|
||||
sched.host_manager = fakes.FakeHostManager()
|
||||
@ -446,13 +418,13 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase):
|
||||
SNAPSHOT_SUPPORT: 'True',
|
||||
}}]}
|
||||
|
||||
hosts = sched._get_weighted_candidates_cg(fake_context,
|
||||
request_spec)
|
||||
hosts = sched._get_weighted_candidates_share_group(
|
||||
fake_context, request_spec)
|
||||
|
||||
self.assertTrue(hosts)
|
||||
|
||||
@mock.patch('manila.db.service_get_all_by_topic')
|
||||
def test_get_weighted_candidates_for_consistency_group_no_hosts(
|
||||
def test_get_weighted_candidates_for_share_group_no_hosts(
|
||||
self, _mock_service_get_all_by_topic):
|
||||
sched = fakes.FakeFilterScheduler()
|
||||
sched.host_manager = fakes.FakeHostManager()
|
||||
@ -463,13 +435,13 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase):
|
||||
SNAPSHOT_SUPPORT: 'False',
|
||||
}}]}
|
||||
|
||||
hosts = sched._get_weighted_candidates_cg(fake_context,
|
||||
request_spec)
|
||||
hosts = sched._get_weighted_candidates_share_group(
|
||||
fake_context, request_spec)
|
||||
|
||||
self.assertEqual([], hosts)
|
||||
|
||||
@mock.patch('manila.db.service_get_all_by_topic')
|
||||
def test_get_weighted_candidates_for_consistency_group_many_hosts(
|
||||
def test_get_weighted_candidates_for_share_group_many_hosts(
|
||||
self, _mock_service_get_all_by_topic):
|
||||
sched = fakes.FakeFilterScheduler()
|
||||
sched.host_manager = fakes.FakeHostManager()
|
||||
@ -480,10 +452,10 @@ class FilterSchedulerTestCase(test_base.SchedulerTestCase):
|
||||
SNAPSHOT_SUPPORT: 'True',
|
||||
}}]}
|
||||
|
||||
hosts = sched._get_weighted_candidates_cg(fake_context,
|
||||
request_spec)
|
||||
hosts = sched._get_weighted_candidates_share_group(
|
||||
fake_context, request_spec)
|
||||
|
||||
self.assertEqual(2, len(hosts))
|
||||
self.assertEqual(6, len(hosts))
|
||||
|
||||
def _host_passes_filters_setup(self, mock_obj):
|
||||
sched = fakes.FakeFilterScheduler()
|
||||
|
@ -122,8 +122,7 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
|
||||
reserved_percentage=0,
|
||||
provisioned_capacity_gb=100,
|
||||
max_over_subscription_ratio=20.0,
|
||||
thin_provisioning=True,
|
||||
consistency_group_support='pool')]),
|
||||
thin_provisioning=True)]),
|
||||
'host4@DDD': dict(share_backend_name='DDD',
|
||||
timestamp=None, reserved_percentage=0,
|
||||
driver_handles_share_servers=False,
|
||||
@ -137,16 +136,14 @@ SHARE_SERVICE_STATES_WITH_POOLS = {
|
||||
reserved_percentage=0,
|
||||
provisioned_capacity_gb=800,
|
||||
max_over_subscription_ratio=2.0,
|
||||
thin_provisioning=True,
|
||||
consistency_group_support='host'),
|
||||
thin_provisioning=True),
|
||||
dict(pool_name='pool4b',
|
||||
total_capacity_gb=542,
|
||||
free_capacity_gb=442,
|
||||
reserved_percentage=0,
|
||||
provisioned_capacity_gb=2000,
|
||||
max_over_subscription_ratio=10.0,
|
||||
thin_provisioning=True,
|
||||
consistency_group_support='host')]),
|
||||
thin_provisioning=True)]),
|
||||
'host5@EEE': dict(share_backend_name='EEE',
|
||||
timestamp=None, reserved_percentage=0,
|
||||
driver_handles_share_servers=False,
|
||||
@ -233,7 +230,6 @@ class FakeHostManager(host_manager.HostManager):
|
||||
'provisioned_capacity_gb': 256,
|
||||
'max_over_subscription_ratio': 2.0,
|
||||
'thin_provisioning': [False],
|
||||
'consistency_group_support': 'host',
|
||||
'reserved_percentage': 0,
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
@ -262,7 +258,6 @@ class FakeHostManager(host_manager.HostManager):
|
||||
'timestamp': None,
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'consistency_group_support': 'pool',
|
||||
'replication_type': None,
|
||||
},
|
||||
'host6': {'total_capacity_gb': 'unknown',
|
||||
@ -348,7 +343,7 @@ def fake_replica_request_spec(**kwargs):
|
||||
'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
|
||||
'snapshot_id': None,
|
||||
'share_type': 'fake_share_type',
|
||||
'consistency_group': None,
|
||||
'share_group': None,
|
||||
}
|
||||
request_spec.update(kwargs)
|
||||
return request_spec
|
||||
|
@ -212,7 +212,6 @@ class HostManagerTestCase(test.TestCase):
|
||||
'snapshot_support': False,
|
||||
'create_share_from_snapshot_support': False,
|
||||
'revert_to_snapshot_support': True,
|
||||
'consistency_group_support': False,
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'replication_type': None,
|
||||
@ -239,7 +238,6 @@ class HostManagerTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
'consistency_group_support': False,
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'replication_type': None,
|
||||
@ -266,7 +264,6 @@ class HostManagerTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
'consistency_group_support': False,
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'replication_type': None,
|
||||
@ -315,7 +312,6 @@ class HostManagerTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': True,
|
||||
'consistency_group_support': False,
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'replication_type': None,
|
||||
@ -343,7 +339,6 @@ class HostManagerTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
'consistency_group_support': False,
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'replication_type': None,
|
||||
@ -371,7 +366,6 @@ class HostManagerTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
'consistency_group_support': 'pool',
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'replication_type': None,
|
||||
@ -399,7 +393,6 @@ class HostManagerTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
'consistency_group_support': 'host',
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'replication_type': None,
|
||||
@ -427,7 +420,6 @@ class HostManagerTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
'consistency_group_support': 'host',
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'replication_type': None,
|
||||
@ -488,7 +480,6 @@ class HostManagerTestCase(test.TestCase):
|
||||
'provisioned_capacity_gb': 312,
|
||||
'max_over_subscription_ratio': 1.0,
|
||||
'thin_provisioning': False,
|
||||
'consistency_group_support': False,
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'replication_type': None,
|
||||
@ -515,7 +506,6 @@ class HostManagerTestCase(test.TestCase):
|
||||
'provisioned_capacity_gb': 400,
|
||||
'max_over_subscription_ratio': 2.0,
|
||||
'thin_provisioning': True,
|
||||
'consistency_group_support': False,
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'replication_type': None,
|
||||
@ -570,7 +560,6 @@ class HostManagerTestCase(test.TestCase):
|
||||
'thin_provisioning': True,
|
||||
'vendor_name': None,
|
||||
'storage_protocol': None,
|
||||
'consistency_group_support': False,
|
||||
'dedupe': False,
|
||||
'compression': False,
|
||||
'replication_type': None,
|
||||
|
@ -175,44 +175,45 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
mock_get_pools.assert_called_once_with(self.context, 'fake_filters')
|
||||
self.assertEqual('fake_pools', result)
|
||||
|
||||
@mock.patch.object(db, 'consistency_group_update', mock.Mock())
|
||||
def test_create_cg_no_valid_host_puts_cg_in_error_state(self):
|
||||
"""Test that NoValidHost is raised for create_consistency_group.
|
||||
@mock.patch.object(db, 'share_group_update', mock.Mock())
|
||||
def test_create_group_no_valid_host_puts_group_in_error_state(self):
|
||||
"""Test that NoValidHost is raised for create_share_group.
|
||||
|
||||
Puts the share in 'error' state and eats the exception.
|
||||
"""
|
||||
|
||||
fake_cg_id = 1
|
||||
cg_id = fake_cg_id
|
||||
request_spec = {"consistency_group_id": cg_id}
|
||||
fake_group_id = 1
|
||||
group_id = fake_group_id
|
||||
request_spec = {"share_group_id": group_id}
|
||||
with mock.patch.object(
|
||||
self.manager.driver, 'schedule_create_consistency_group',
|
||||
self.manager.driver, 'schedule_create_share_group',
|
||||
mock.Mock(side_effect=self.raise_no_valid_host)):
|
||||
self.manager.create_consistency_group(self.context,
|
||||
fake_cg_id,
|
||||
self.manager.create_share_group(self.context,
|
||||
fake_group_id,
|
||||
request_spec=request_spec,
|
||||
filter_properties={})
|
||||
db.consistency_group_update.assert_called_once_with(
|
||||
self.context, fake_cg_id, {'status': 'error'})
|
||||
(self.manager.driver.schedule_create_consistency_group.
|
||||
assert_called_once_with(self.context, cg_id, request_spec, {}))
|
||||
db.share_group_update.assert_called_once_with(
|
||||
self.context, fake_group_id, {'status': 'error'})
|
||||
(self.manager.driver.schedule_create_share_group.
|
||||
assert_called_once_with(self.context, group_id, request_spec,
|
||||
{}))
|
||||
|
||||
@mock.patch.object(db, 'consistency_group_update', mock.Mock())
|
||||
def test_create_cg_exception_puts_cg_in_error_state(self):
|
||||
"""Test that exceptions for create_consistency_group.
|
||||
@mock.patch.object(db, 'share_group_update', mock.Mock())
|
||||
def test_create_group_exception_puts_group_in_error_state(self):
|
||||
"""Test that exceptions for create_share_group.
|
||||
|
||||
Puts the share in 'error' state and raises the exception.
|
||||
"""
|
||||
|
||||
fake_cg_id = 1
|
||||
cg_id = fake_cg_id
|
||||
request_spec = {"consistency_group_id": cg_id}
|
||||
fake_group_id = 1
|
||||
group_id = fake_group_id
|
||||
request_spec = {"share_group_id": group_id}
|
||||
with mock.patch.object(self.manager.driver,
|
||||
'schedule_create_consistency_group',
|
||||
'schedule_create_share_group',
|
||||
mock.Mock(side_effect=exception.NotFound)):
|
||||
self.assertRaises(exception.NotFound,
|
||||
self.manager.create_consistency_group,
|
||||
self.context, fake_cg_id,
|
||||
self.manager.create_share_group,
|
||||
self.context, fake_group_id,
|
||||
request_spec=request_spec,
|
||||
filter_properties={})
|
||||
|
||||
|
@ -93,13 +93,13 @@ class SchedulerRpcAPITestCase(test.TestCase):
|
||||
filters=None,
|
||||
version='1.1')
|
||||
|
||||
def test_create_consistency_group(self):
|
||||
self._test_scheduler_api('create_consistency_group',
|
||||
def test_create_share_group(self):
|
||||
self._test_scheduler_api('create_share_group',
|
||||
rpc_method='cast',
|
||||
cg_id='cg_id',
|
||||
share_group_id='fake_share_group_id',
|
||||
request_spec='fake_request_spec',
|
||||
filter_properties='filter_properties',
|
||||
version='1.3')
|
||||
version='1.8')
|
||||
|
||||
def test_migrate_share_to_host(self):
|
||||
self._test_scheduler_api('migrate_share_to_host',
|
||||
|
@ -380,32 +380,30 @@ class CephFSNativeDriverTestCase(test.TestCase):
|
||||
self._driver._share_path(self._share),
|
||||
"snappy1_instance1"))
|
||||
|
||||
def test_create_consistency_group(self):
|
||||
self._driver.create_consistency_group(self._context, {"id": "grp1"},
|
||||
None)
|
||||
def test_create_share_group(self):
|
||||
self._driver.create_share_group(self._context, {"id": "grp1"}, None)
|
||||
|
||||
self._driver._volume_client.create_group.assert_called_once_with(
|
||||
"grp1")
|
||||
|
||||
def test_delete_consistency_group(self):
|
||||
self._driver.delete_consistency_group(self._context, {"id": "grp1"},
|
||||
None)
|
||||
def test_delete_share_group(self):
|
||||
self._driver.delete_share_group(self._context, {"id": "grp1"}, None)
|
||||
|
||||
self._driver._volume_client.destroy_group.assert_called_once_with(
|
||||
"grp1")
|
||||
|
||||
def test_create_cg_snapshot(self):
|
||||
self._driver.create_cgsnapshot(self._context, {
|
||||
'consistency_group_id': 'cgid',
|
||||
def test_create_share_snapshot(self):
|
||||
self._driver.create_share_group_snapshot(self._context, {
|
||||
'share_group_id': 'cgid',
|
||||
'id': 'snapid'
|
||||
})
|
||||
|
||||
(self._driver._volume_client.create_snapshot_group.
|
||||
assert_called_once_with("cgid", "snapid"))
|
||||
|
||||
def test_delete_cgsnapshot(self):
|
||||
self._driver.delete_cgsnapshot(self._context, {
|
||||
'consistency_group_id': 'cgid',
|
||||
def test_delete_share_group_snapshot(self):
|
||||
self._driver.delete_share_group_snapshot(self._context, {
|
||||
'share_group_id': 'cgid',
|
||||
'id': 'snapid'
|
||||
})
|
||||
|
||||
|
@ -126,6 +126,7 @@ class EMCShareFrameworkTestCase(test.TestCase):
|
||||
data['snapshot_support'] = True
|
||||
data['create_share_from_snapshot_support'] = True
|
||||
data['revert_to_snapshot_support'] = False
|
||||
data['share_group_snapshot_support'] = True
|
||||
data['replication_domain'] = None
|
||||
data['filter_function'] = None
|
||||
data['goodness_function'] = None
|
||||
|
@ -258,6 +258,7 @@ class GlusterfsNativeShareDriverTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
'share_group_snapshot_support': True,
|
||||
'replication_domain': None,
|
||||
'filter_function': None,
|
||||
'goodness_function': None,
|
||||
|
@ -735,6 +735,7 @@ class HPE3ParDriverTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
'share_group_snapshot_support': True,
|
||||
'storage_protocol': 'NFS_CIFS',
|
||||
'thin_provisioning': True,
|
||||
'total_capacity_gb': 0,
|
||||
@ -811,6 +812,7 @@ class HPE3ParDriverTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
'share_group_snapshot_support': True,
|
||||
'replication_domain': None,
|
||||
'filter_function': None,
|
||||
'goodness_function': None,
|
||||
@ -849,6 +851,7 @@ class HPE3ParDriverTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
'share_group_snapshot_support': True,
|
||||
'replication_domain': None,
|
||||
'filter_function': None,
|
||||
'goodness_function': None,
|
||||
|
@ -2425,6 +2425,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
|
||||
"snapshot_support": snapshot_support,
|
||||
"create_share_from_snapshot_support": snapshot_support,
|
||||
"revert_to_snapshot_support": False,
|
||||
"share_group_snapshot_support": True,
|
||||
"replication_domain": None,
|
||||
"filter_function": None,
|
||||
"goodness_function": None,
|
||||
|
@ -366,7 +366,6 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
'driver_version': '1.0',
|
||||
'netapp_storage_family': 'ontap_cluster',
|
||||
'storage_protocol': 'NFS_CIFS',
|
||||
'consistency_group_support': 'host',
|
||||
'pools': fake.POOLS,
|
||||
}
|
||||
self.assertDictEqual(expected, result)
|
||||
@ -390,7 +389,6 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
'driver_version': '1.0',
|
||||
'netapp_storage_family': 'ontap_cluster',
|
||||
'storage_protocol': 'NFS_CIFS',
|
||||
'consistency_group_support': 'host',
|
||||
'replication_type': 'dr',
|
||||
'replication_domain': 'fake_domain',
|
||||
'pools': fake.POOLS,
|
||||
@ -1825,7 +1823,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
fake_cg_snapshot = copy.deepcopy(fake.CG_SNAPSHOT)
|
||||
fake_cg_snapshot['cgsnapshot_members'] = []
|
||||
|
||||
self.assertRaises(exception.InvalidConsistencyGroup,
|
||||
self.assertRaises(exception.InvalidShareGroup,
|
||||
self.library._collate_cg_snapshot_info,
|
||||
fake.CONSISTENCY_GROUP_DEST, fake_cg_snapshot)
|
||||
|
||||
|
@ -399,7 +399,7 @@ CG_SNAPSHOT_MEMBER_2 = {
|
||||
|
||||
CG_SNAPSHOT = {
|
||||
'cgsnapshot_members': [CG_SNAPSHOT_MEMBER_1, CG_SNAPSHOT_MEMBER_2],
|
||||
'consistency_group': CONSISTENCY_GROUP,
|
||||
'share_group': CONSISTENCY_GROUP,
|
||||
'consistency_group_id': CONSISTENCY_GROUP_ID,
|
||||
'id': CG_SNAPSHOT_ID,
|
||||
'project_id': TENANT_ID,
|
||||
|
@ -66,27 +66,6 @@ def get_fake_snap_dict():
|
||||
'created_at': '2015-08-10 00:05:58',
|
||||
'updated_at': '2015-08-10 00:05:58',
|
||||
'consistency_group_id': None,
|
||||
'cgsnapshot_members': [
|
||||
{
|
||||
'status': 'available',
|
||||
'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f',
|
||||
'share_id': 'e14b5174-e534-4f35-bc4f-fe81c1575d6f',
|
||||
'user_id': 'a0314a441ca842019b0952224aa39192',
|
||||
'deleted': 'False',
|
||||
'created_at': '2015-08-10 00:05:58',
|
||||
'share': {
|
||||
'id': '03e2f06e-14f2-45a5-9631-0949d1937bd8',
|
||||
'deleted': False,
|
||||
},
|
||||
'updated_at': '2015-08-10 00:05:58',
|
||||
'share_proto': 'NFS',
|
||||
'project_id': '13c0be6290934bd98596cfa004650049',
|
||||
'cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'deleted_at': None,
|
||||
'id': '03e2f06e-14f2-45a5-9631-0949d1937bd8',
|
||||
'size': 1,
|
||||
},
|
||||
],
|
||||
'deleted_at': None,
|
||||
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'name': None,
|
||||
@ -94,58 +73,6 @@ def get_fake_snap_dict():
|
||||
return snap_dict
|
||||
|
||||
|
||||
def get_fake_cg_dict():
|
||||
cg_dict = {
|
||||
'status': 'creating',
|
||||
'project_id': '13c0be6290934bd98596cfa004650049',
|
||||
'user_id': 'a0314a441ca842019b0952224aa39192',
|
||||
'description': None,
|
||||
'deleted': 'False',
|
||||
'created_at': '2015-08-10 00:07:58',
|
||||
'updated_at': None,
|
||||
'source_cgsnapshot_id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'host': 'openstack2@cmodeSSVMNFS',
|
||||
'deleted_at': None,
|
||||
'shares': [
|
||||
{
|
||||
'id': '02a32f06e-14f2-45a5-9631-7483f1937bd8',
|
||||
'deleted': False,
|
||||
'source_cgsnapshot_member_id':
|
||||
'03e2f06e-14f2-45a5-9631-0949d1937bd8',
|
||||
},
|
||||
|
||||
],
|
||||
'share_types': [
|
||||
{
|
||||
'id': 'f6aa3b56-45a5-9631-02a32f06e1937b',
|
||||
'deleted': False,
|
||||
'consistency_group_id': None,
|
||||
'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f',
|
||||
},
|
||||
],
|
||||
'id': 'eda52174-0442-476d-9694-a58327466c14',
|
||||
'name': None
|
||||
}
|
||||
return cg_dict
|
||||
|
||||
|
||||
def get_fake_collated_cg_snap_info():
|
||||
fake_collated_cg_snap_info = [
|
||||
{
|
||||
'share': {
|
||||
'id': '02a32f06e-14f2-45a5-9631-7483f1937bd8',
|
||||
'deleted': False,
|
||||
'source_cgsnapshot_member_id':
|
||||
'03e2f06e-14f2-45a5-9631-0949d1937bd8',
|
||||
},
|
||||
'snapshot': {
|
||||
'id': '03e2f06e-14f2-45a5-9631-0949d1937bd8',
|
||||
},
|
||||
},
|
||||
]
|
||||
return fake_collated_cg_snap_info
|
||||
|
||||
|
||||
def get_fake_access_rule(access_to, access_level, access_type='ip'):
|
||||
return {
|
||||
'access_type': access_type,
|
||||
|
@ -520,7 +520,6 @@ class LVMShareDriverTestCase(test.TestCase):
|
||||
self.assertEqual('LVM', self._driver._stats['share_backend_name'])
|
||||
self.assertEqual('NFS_CIFS', self._driver._stats['storage_protocol'])
|
||||
self.assertEqual(50, self._driver._stats['reserved_percentage'])
|
||||
self.assertIsNone(self._driver._stats['consistency_group_support'])
|
||||
self.assertTrue(self._driver._stats['snapshot_support'])
|
||||
self.assertEqual('LVMShareDriver', self._driver._stats['driver_name'])
|
||||
self.assertEqual('test-pool', self._driver._stats['pools'])
|
||||
|
@ -343,7 +343,6 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
|
||||
self.mock_object(self.driver, '_get_pools_info')
|
||||
self.assertEqual({}, self.driver._stats)
|
||||
expected = {
|
||||
'consistency_group_support': None,
|
||||
'driver_handles_share_servers': False,
|
||||
'driver_name': 'ZFS',
|
||||
'driver_version': '1.0',
|
||||
@ -356,6 +355,7 @@ class ZFSonLinuxShareDriverTestCase(test.TestCase):
|
||||
'snapshot_support': True,
|
||||
'create_share_from_snapshot_support': True,
|
||||
'revert_to_snapshot_support': False,
|
||||
'share_group_snapshot_support': True,
|
||||
'storage_protocol': 'NFS',
|
||||
'total_capacity_gb': 'unknown',
|
||||
'vendor_name': 'Open Source',
|
||||
|
@ -708,7 +708,7 @@ class ShareAPITestCase(test.TestCase):
|
||||
self.context, request_spec=mock.ANY, filter_properties={})
|
||||
self.assertFalse(self.api.share_rpcapi.create_share_instance.called)
|
||||
|
||||
def test_create_instance_cgsnapshot_member(self):
|
||||
def test_create_instance_share_group_snapshot_member(self):
|
||||
fake_req_spec = {
|
||||
'share_properties': 'fake_share_properties',
|
||||
'share_instance_properties': 'fake_share_instance_properties',
|
||||
@ -722,7 +722,7 @@ class ShareAPITestCase(test.TestCase):
|
||||
|
||||
fake_instance = fakes.fake_share_instance(
|
||||
share_id=share['id'], **member_info)
|
||||
cgsnapmember = {'share_instance': fake_instance}
|
||||
sg_snap_member = {'share_instance': fake_instance}
|
||||
self.mock_policy_check = self.mock_object(
|
||||
policy, 'check_policy', mock.Mock(return_value=True))
|
||||
mock_share_rpcapi_call = self.mock_object(self.share_rpcapi,
|
||||
@ -735,8 +735,9 @@ class ShareAPITestCase(test.TestCase):
|
||||
share_api.API, 'create_share_instance_and_get_request_spec',
|
||||
mock.Mock(return_value=(fake_req_spec, fake_instance)))
|
||||
|
||||
retval = self.api.create_instance(self.context, fakes.fake_share(),
|
||||
cgsnapshot_member=cgsnapmember)
|
||||
retval = self.api.create_instance(
|
||||
self.context, fakes.fake_share(),
|
||||
share_group_snapshot_member=sg_snap_member)
|
||||
|
||||
self.assertIsNone(retval)
|
||||
mock_db_share_instance_update.assert_called_once_with(
|
||||
@ -895,11 +896,11 @@ class ShareAPITestCase(test.TestCase):
|
||||
'share_proto': kwargs.get('share_proto', share.get('share_proto')),
|
||||
'share_type_id': share_type['id'],
|
||||
'is_public': kwargs.get('is_public', share.get('is_public')),
|
||||
'consistency_group_id': kwargs.get(
|
||||
'consistency_group_id', share.get('consistency_group_id')),
|
||||
'source_cgsnapshot_member_id': kwargs.get(
|
||||
'source_cgsnapshot_member_id',
|
||||
share.get('source_cgsnapshot_member_id')),
|
||||
'share_group_id': kwargs.get(
|
||||
'share_group_id', share.get('share_group_id')),
|
||||
'source_share_group_snapshot_member_id': kwargs.get(
|
||||
'source_share_group_snapshot_member_id',
|
||||
share.get('source_share_group_snapshot_member_id')),
|
||||
'snapshot_id': kwargs.get('snapshot_id', share.get('snapshot_id')),
|
||||
}
|
||||
share_instance_properties = {
|
||||
@ -1406,7 +1407,7 @@ class ShareAPITestCase(test.TestCase):
|
||||
|
||||
@mock.patch.object(db_api, 'share_instances_get_all_by_share_server',
|
||||
mock.Mock(return_value=[]))
|
||||
@mock.patch.object(db_api, 'consistency_group_get_all_by_share_server',
|
||||
@mock.patch.object(db_api, 'share_group_get_all_by_share_server',
|
||||
mock.Mock(return_value=[]))
|
||||
def test_delete_share_server_no_dependent_shares(self):
|
||||
server = {'id': 'fake_share_server_id'}
|
||||
@ -1418,14 +1419,14 @@ class ShareAPITestCase(test.TestCase):
|
||||
self.api.delete_share_server(self.context, server)
|
||||
db_api.share_instances_get_all_by_share_server.assert_called_once_with(
|
||||
self.context, server['id'])
|
||||
db_api.consistency_group_get_all_by_share_server.\
|
||||
db_api.share_group_get_all_by_share_server.\
|
||||
assert_called_once_with(self.context, server['id'])
|
||||
self.share_rpcapi.delete_share_server.assert_called_once_with(
|
||||
self.context, server_returned)
|
||||
|
||||
@mock.patch.object(db_api, 'share_instances_get_all_by_share_server',
|
||||
mock.Mock(return_value=['fake_share', ]))
|
||||
@mock.patch.object(db_api, 'consistency_group_get_all_by_share_server',
|
||||
@mock.patch.object(db_api, 'share_group_get_all_by_share_server',
|
||||
mock.Mock(return_value=[]))
|
||||
def test_delete_share_server_dependent_share_exists(self):
|
||||
server = {'id': 'fake_share_server_id'}
|
||||
@ -1438,9 +1439,9 @@ class ShareAPITestCase(test.TestCase):
|
||||
|
||||
@mock.patch.object(db_api, 'share_instances_get_all_by_share_server',
|
||||
mock.Mock(return_value=[]))
|
||||
@mock.patch.object(db_api, 'consistency_group_get_all_by_share_server',
|
||||
mock.Mock(return_value=['fake_cg', ]))
|
||||
def test_delete_share_server_dependent_cg_exists(self):
|
||||
@mock.patch.object(db_api, 'share_group_get_all_by_share_server',
|
||||
mock.Mock(return_value=['fake_group', ]))
|
||||
def test_delete_share_server_dependent_group_exists(self):
|
||||
server = {'id': 'fake_share_server_id'}
|
||||
self.assertRaises(exception.ShareServerInUse,
|
||||
self.api.delete_share_server,
|
||||
@ -1449,7 +1450,7 @@ class ShareAPITestCase(test.TestCase):
|
||||
|
||||
db_api.share_instances_get_all_by_share_server.assert_called_once_with(
|
||||
self.context, server['id'])
|
||||
db_api.consistency_group_get_all_by_share_server.\
|
||||
db_api.share_group_get_all_by_share_server.\
|
||||
assert_called_once_with(self.context, server['id'])
|
||||
|
||||
@mock.patch.object(db_api, 'share_snapshot_instance_update', mock.Mock())
|
||||
@ -1597,7 +1598,7 @@ class ShareAPITestCase(test.TestCase):
|
||||
self.context, share, share_network_id=share['share_network_id'],
|
||||
host=valid_host, share_type_id=share_type['id'],
|
||||
availability_zone=snapshot['share']['availability_zone'],
|
||||
consistency_group=None, cgsnapshot_member=None)
|
||||
share_group=None, share_group_snapshot_member=None)
|
||||
share_api.policy.check_policy.assert_has_calls([
|
||||
mock.call(self.context, 'share', 'create'),
|
||||
mock.call(self.context, 'share_snapshot', 'get_snapshot')])
|
||||
@ -1713,9 +1714,9 @@ class ShareAPITestCase(test.TestCase):
|
||||
self.assertRaises(exception.Conflict, self.api.delete,
|
||||
self.context, share)
|
||||
|
||||
@mock.patch.object(db_api, 'count_cgsnapshot_members_in_share',
|
||||
@mock.patch.object(db_api, 'count_share_group_snapshot_members_in_share',
|
||||
mock.Mock(return_value=2))
|
||||
def test_delete_dependent_cgsnapshot_members(self):
|
||||
def test_delete_dependent_share_group_snapshot_members(self):
|
||||
share_server_id = 'fake-ss-id'
|
||||
share = self._setup_delete_mocks(constants.STATUS_AVAILABLE,
|
||||
share_server_id)
|
||||
|
@ -676,3 +676,336 @@ class ShareDriverTestCase(test.TestCase):
|
||||
share_driver.update_replicated_snapshot,
|
||||
'fake_context', ['r1', 'r2'], 'r1',
|
||||
['s1', 's2'], 's1')
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_share_group_snapshot_support_exists_and_equals_snapshot_support(
|
||||
self, snapshots_are_supported):
|
||||
driver.CONF.set_default('driver_handles_share_servers', True)
|
||||
child_class_instance = driver.ShareDriver(True)
|
||||
child_class_instance._snapshots_are_supported = snapshots_are_supported
|
||||
self.mock_object(child_class_instance, "configuration")
|
||||
|
||||
child_class_instance._update_share_stats()
|
||||
|
||||
self.assertEqual(
|
||||
snapshots_are_supported,
|
||||
child_class_instance._stats["snapshot_support"])
|
||||
self.assertEqual(
|
||||
snapshots_are_supported,
|
||||
child_class_instance._stats["share_group_snapshot_support"])
|
||||
self.assertTrue(child_class_instance.configuration.safe_get.called)
|
||||
|
||||
def test_create_share_group_from_share_group_snapshot(self):
|
||||
share_driver = self._instantiate_share_driver(None, False)
|
||||
fake_shares = [
|
||||
{'id': 'fake_share_%d' % i,
|
||||
'source_share_group_snapshot_member_id': 'fake_member_%d' % i}
|
||||
for i in (1, 2)]
|
||||
fake_share_group_dict = {
|
||||
'source_share_group_snapshot_id': 'some_fake_uuid_abc',
|
||||
'shares': fake_shares,
|
||||
'id': 'some_fake_uuid_def',
|
||||
}
|
||||
fake_share_group_snapshot_dict = {
|
||||
'share_group_snapshot_members': [
|
||||
{'id': 'fake_member_1'}, {'id': 'fake_member_2'}],
|
||||
'id': 'fake_share_group_snapshot_id',
|
||||
}
|
||||
mock_create = self.mock_object(
|
||||
share_driver, 'create_share_from_snapshot',
|
||||
mock.Mock(side_effect=['fake_export1', 'fake_export2']))
|
||||
expected_share_updates = [
|
||||
{
|
||||
'id': 'fake_share_1',
|
||||
'export_locations': 'fake_export1',
|
||||
},
|
||||
{
|
||||
'id': 'fake_share_2',
|
||||
'export_locations': 'fake_export2',
|
||||
},
|
||||
]
|
||||
|
||||
share_group_update, share_update = (
|
||||
share_driver.create_share_group_from_share_group_snapshot(
|
||||
'fake_context', fake_share_group_dict,
|
||||
fake_share_group_snapshot_dict))
|
||||
|
||||
mock_create.assert_has_calls([
|
||||
mock.call(
|
||||
'fake_context',
|
||||
{'id': 'fake_share_1',
|
||||
'source_share_group_snapshot_member_id': 'fake_member_1'},
|
||||
{'id': 'fake_member_1'}),
|
||||
mock.call(
|
||||
'fake_context',
|
||||
{'id': 'fake_share_2',
|
||||
'source_share_group_snapshot_member_id': 'fake_member_2'},
|
||||
{'id': 'fake_member_2'})
|
||||
])
|
||||
self.assertIsNone(share_group_update)
|
||||
self.assertEqual(expected_share_updates, share_update)
|
||||
|
||||
def test_create_share_group_from_share_group_snapshot_dhss(self):
|
||||
share_driver = self._instantiate_share_driver(None, True)
|
||||
mock_share_server = mock.Mock()
|
||||
fake_shares = [
|
||||
{'id': 'fake_share_1',
|
||||
'source_share_group_snapshot_member_id': 'foo_member_1'},
|
||||
{'id': 'fake_share_2',
|
||||
'source_share_group_snapshot_member_id': 'foo_member_2'}]
|
||||
fake_share_group_dict = {
|
||||
'source_share_group_snapshot_id': 'some_fake_uuid',
|
||||
'shares': fake_shares,
|
||||
'id': 'eda52174-0442-476d-9694-a58327466c14',
|
||||
}
|
||||
fake_share_group_snapshot_dict = {
|
||||
'share_group_snapshot_members': [
|
||||
{'id': 'foo_member_1'}, {'id': 'foo_member_2'}],
|
||||
'id': 'fake_share_group_snapshot_id'
|
||||
}
|
||||
mock_create = self.mock_object(
|
||||
share_driver, 'create_share_from_snapshot',
|
||||
mock.Mock(side_effect=['fake_export1', 'fake_export2']))
|
||||
expected_share_updates = [
|
||||
{'id': 'fake_share_1', 'export_locations': 'fake_export1'},
|
||||
{'id': 'fake_share_2', 'export_locations': 'fake_export2'},
|
||||
]
|
||||
|
||||
share_group_update, share_update = (
|
||||
share_driver.create_share_group_from_share_group_snapshot(
|
||||
'fake_context',
|
||||
fake_share_group_dict,
|
||||
fake_share_group_snapshot_dict, share_server=mock_share_server,
|
||||
)
|
||||
)
|
||||
|
||||
mock_create.assert_has_calls([
|
||||
mock.call(
|
||||
'fake_context',
|
||||
{'id': 'fake_share_%d' % i,
|
||||
'source_share_group_snapshot_member_id': 'foo_member_%d' % i},
|
||||
{'id': 'foo_member_%d' % i},
|
||||
share_server=mock_share_server)
|
||||
for i in (1, 2)
|
||||
])
|
||||
self.assertIsNone(share_group_update)
|
||||
self.assertEqual(expected_share_updates, share_update)
|
||||
|
||||
def test_create_share_group_from_sg_snapshot_with_no_members(self):
|
||||
share_driver = self._instantiate_share_driver(None, False)
|
||||
fake_share_group_dict = {}
|
||||
fake_share_group_snapshot_dict = {'share_group_snapshot_members': []}
|
||||
|
||||
share_group_update, share_update = (
|
||||
share_driver.create_share_group_from_share_group_snapshot(
|
||||
'fake_context', fake_share_group_dict,
|
||||
fake_share_group_snapshot_dict))
|
||||
|
||||
self.assertIsNone(share_group_update)
|
||||
self.assertIsNone(share_update)
|
||||
|
||||
def test_create_share_group_snapshot(self):
|
||||
fake_snap_member_1 = {
|
||||
'share_group_snapshot_id': 'fake_sg_snap_id',
|
||||
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e',
|
||||
'share_id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2',
|
||||
'status': 'bar',
|
||||
}
|
||||
fake_snap_member_2 = {
|
||||
'share_group_snapshot_id': 'fake_sg_snap_id',
|
||||
'id': '1e010dfe-545b-432d-ab95-4ef03cd82f89',
|
||||
'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296',
|
||||
'status': 'foo',
|
||||
}
|
||||
fake_snap_dict = {
|
||||
'status': 'available',
|
||||
'project_id': '13c0be6290934bd98596cfa004650049',
|
||||
'user_id': 'a0314a441ca842019b0952224aa39192',
|
||||
'description': None,
|
||||
'deleted': '0',
|
||||
'share_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67',
|
||||
'share_group_snapshot_members': [
|
||||
fake_snap_member_1, fake_snap_member_2],
|
||||
'deleted_at': None,
|
||||
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'name': None
|
||||
}
|
||||
share_driver = self._instantiate_share_driver(None, False)
|
||||
share_driver._stats['share_group_snapshot_support'] = True
|
||||
mock_create_snap = self.mock_object(share_driver, 'create_snapshot')
|
||||
|
||||
share_group_snapshot_update, member_update_list = (
|
||||
share_driver.create_share_group_snapshot(
|
||||
'fake_context', fake_snap_dict))
|
||||
|
||||
mock_create_snap.assert_has_calls([
|
||||
mock.call(
|
||||
'fake_context',
|
||||
{'id': fake_snap_member_1['id'],
|
||||
'share_id': fake_snap_member_1['share_id'],
|
||||
'snapshot_id': fake_snap_member_1['share_group_snapshot_id']},
|
||||
share_server=None),
|
||||
mock.call(
|
||||
'fake_context',
|
||||
{'id': fake_snap_member_2['id'],
|
||||
'share_id': fake_snap_member_2['share_id'],
|
||||
'snapshot_id': fake_snap_member_2['share_group_snapshot_id']},
|
||||
share_server=None),
|
||||
])
|
||||
self.assertIsNone(share_group_snapshot_update)
|
||||
self.assertIsNone(member_update_list)
|
||||
|
||||
def test_create_share_group_snapshot_failed_snapshot(self):
|
||||
fake_snap_member_1 = {
|
||||
'share_group_snapshot_id': 'fake_sg_snap_id',
|
||||
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e',
|
||||
'share_id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2',
|
||||
'status': 'bar',
|
||||
}
|
||||
fake_snap_member_2 = {
|
||||
'share_group_snapshot_id': 'fake_sg_snap_id',
|
||||
'id': '1e010dfe-545b-432d-ab95-4ef03cd82f89',
|
||||
'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296',
|
||||
'status': 'foo',
|
||||
}
|
||||
fake_snap_dict = {
|
||||
'status': 'available',
|
||||
'project_id': '13c0be6290934bd98596cfa004650049',
|
||||
'user_id': 'a0314a441ca842019b0952224aa39192',
|
||||
'description': None,
|
||||
'deleted': '0',
|
||||
'share_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67',
|
||||
'share_group_snapshot_members': [
|
||||
fake_snap_member_1, fake_snap_member_2],
|
||||
'deleted_at': None,
|
||||
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'name': None
|
||||
}
|
||||
expected_exception = exception.ManilaException
|
||||
|
||||
share_driver = self._instantiate_share_driver(None, False)
|
||||
share_driver._stats['share_group_snapshot_support'] = True
|
||||
mock_create_snap = self.mock_object(
|
||||
share_driver, 'create_snapshot',
|
||||
mock.Mock(side_effect=[None, expected_exception]))
|
||||
mock_delete_snap = self.mock_object(share_driver, 'delete_snapshot')
|
||||
|
||||
self.assertRaises(
|
||||
expected_exception,
|
||||
share_driver.create_share_group_snapshot,
|
||||
'fake_context', fake_snap_dict)
|
||||
|
||||
fake_snap_member_1_expected = {
|
||||
'id': fake_snap_member_1['id'],
|
||||
'share_id': fake_snap_member_1['share_id'],
|
||||
'snapshot_id': fake_snap_member_1['share_group_snapshot_id'],
|
||||
}
|
||||
mock_create_snap.assert_has_calls([
|
||||
mock.call(
|
||||
'fake_context', fake_snap_member_1_expected, share_server=None,
|
||||
),
|
||||
mock.call(
|
||||
'fake_context',
|
||||
{'id': fake_snap_member_2['id'],
|
||||
'share_id': fake_snap_member_2['share_id'],
|
||||
'snapshot_id': fake_snap_member_2['share_group_snapshot_id']},
|
||||
share_server=None),
|
||||
])
|
||||
mock_delete_snap.assert_called_with(
|
||||
'fake_context', fake_snap_member_1_expected, share_server=None)
|
||||
|
||||
def test_create_share_group_snapshot_no_support(self):
|
||||
fake_snap_dict = {
|
||||
'status': 'available',
|
||||
'project_id': '13c0be6290934bd98596cfa004650049',
|
||||
'user_id': 'a0314a441ca842019b0952224aa39192',
|
||||
'description': None,
|
||||
'deleted': '0',
|
||||
'share_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67',
|
||||
'share_group_snapshot_members': [
|
||||
{
|
||||
'status': 'available',
|
||||
'share_type_id': '1a9ed31e-ee70-483d-93ba-89690e028d7f',
|
||||
'user_id': 'a0314a441ca842019b0952224aa39192',
|
||||
'deleted': 'False',
|
||||
'share_proto': 'NFS',
|
||||
'project_id': '13c0be6290934bd98596cfa004650049',
|
||||
'share_group_snapshot_id':
|
||||
'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'deleted_at': None,
|
||||
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e',
|
||||
'size': 1
|
||||
},
|
||||
],
|
||||
'deleted_at': None,
|
||||
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'name': None
|
||||
}
|
||||
share_driver = self._instantiate_share_driver(None, False)
|
||||
share_driver._stats['share_group_snapshot_support'] = False
|
||||
|
||||
self.assertRaises(
|
||||
exception.ShareGroupSnapshotNotSupported,
|
||||
share_driver.create_share_group_snapshot,
|
||||
'fake_context', fake_snap_dict)
|
||||
|
||||
def test_create_share_group_snapshot_no_members(self):
|
||||
fake_snap_dict = {
|
||||
'status': 'available',
|
||||
'project_id': '13c0be6290934bd98596cfa004650049',
|
||||
'user_id': 'a0314a441ca842019b0952224aa39192',
|
||||
'description': None,
|
||||
'deleted': '0',
|
||||
'share_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67',
|
||||
'share_group_snapshot_members': [],
|
||||
'deleted_at': None,
|
||||
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'name': None
|
||||
}
|
||||
share_driver = self._instantiate_share_driver(None, False)
|
||||
share_driver._stats['share_group_snapshot_support'] = True
|
||||
|
||||
share_group_snapshot_update, member_update_list = (
|
||||
share_driver.create_share_group_snapshot(
|
||||
'fake_context', fake_snap_dict))
|
||||
|
||||
self.assertIsNone(share_group_snapshot_update)
|
||||
self.assertIsNone(member_update_list)
|
||||
|
||||
def test_delete_share_group_snapshot(self):
|
||||
fake_snap_member_1 = {
|
||||
'id': '6813e06b-a8f5-4784-b17d-f3e91afa370e',
|
||||
'share_id': '420f978b-dbf6-4b3c-92fe-f5b17a0bb5e2'
|
||||
}
|
||||
fake_snap_member_2 = {
|
||||
'id': '1e010dfe-545b-432d-ab95-4ef03cd82f89',
|
||||
'share_id': 'a3ebdba5-b4e1-46c8-a0ea-a9ac8daf5296'
|
||||
}
|
||||
fake_snap_dict = {
|
||||
'status': 'available',
|
||||
'project_id': '13c0be6290934bd98596cfa004650049',
|
||||
'user_id': 'a0314a441ca842019b0952224aa39192',
|
||||
'description': None,
|
||||
'deleted': '0',
|
||||
'share_group_id': '4b04fdc3-00b9-4909-ba1a-06e9b3f88b67',
|
||||
'share_group_snapshot_members': [
|
||||
fake_snap_member_1, fake_snap_member_2],
|
||||
'deleted_at': None,
|
||||
'id': 'f6aa3b59-57eb-421e-965c-4e182538e36a',
|
||||
'name': None
|
||||
}
|
||||
|
||||
share_driver = self._instantiate_share_driver(None, False)
|
||||
share_driver._stats['share_group_snapshot_support'] = True
|
||||
mock_delete_snap = self.mock_object(share_driver, 'delete_snapshot')
|
||||
|
||||
share_group_snapshot_update, member_update_list = (
|
||||
share_driver.delete_share_group_snapshot(
|
||||
'fake_context', fake_snap_dict))
|
||||
|
||||
mock_delete_snap.assert_has_calls([
|
||||
mock.call('fake_context', fake_snap_member_1, share_server=None),
|
||||
mock.call('fake_context', fake_snap_member_2, share_server=None),
|
||||
])
|
||||
self.assertIsNone(share_group_snapshot_update)
|
||||
self.assertIsNone(member_update_list)
|
||||
|
@ -200,10 +200,10 @@ class ShareManagerTestCase(test.TestCase):
|
||||
"delete_share_server",
|
||||
"extend_share",
|
||||
"shrink_share",
|
||||
"create_consistency_group",
|
||||
"delete_consistency_group",
|
||||
"create_cgsnapshot",
|
||||
"delete_cgsnapshot",
|
||||
"create_share_group",
|
||||
"delete_share_group",
|
||||
"create_share_group_snapshot",
|
||||
"delete_share_group_snapshot",
|
||||
"create_share_replica",
|
||||
"delete_share_replica",
|
||||
"promote_share_replica",
|
||||
@ -1943,7 +1943,7 @@ class ShareManagerTestCase(test.TestCase):
|
||||
)
|
||||
driver_method_mock.assert_called_once_with(
|
||||
self.context, [fake_share_server], share.instance, snapshot=None,
|
||||
consistency_group=None)
|
||||
share_group=None)
|
||||
|
||||
def test_provide_share_server_for_share_invalid_arguments(self):
|
||||
self.assertRaises(ValueError,
|
||||
@ -1994,33 +1994,35 @@ class ShareManagerTestCase(test.TestCase):
|
||||
db.share_server_get.assert_called_once_with(
|
||||
self.context, fake_parent_id)
|
||||
|
||||
def test_provide_share_server_for_cg_incompatible_servers(self):
|
||||
def test_provide_share_server_for_share_group_incompatible_servers(self):
|
||||
fake_exception = exception.ManilaException("fake")
|
||||
fake_share_server = {'id': 'fake'}
|
||||
cg = db_utils.create_consistency_group()
|
||||
sg = db_utils.create_share_group()
|
||||
|
||||
self.mock_object(db,
|
||||
'share_server_get_all_by_host_and_share_net_valid',
|
||||
mock.Mock(return_value=[fake_share_server]))
|
||||
self.mock_object(
|
||||
self.share_manager.driver,
|
||||
"choose_share_server_compatible_with_cg",
|
||||
"choose_share_server_compatible_with_share_group",
|
||||
mock.Mock(side_effect=fake_exception)
|
||||
)
|
||||
|
||||
self.assertRaises(exception.ManilaException,
|
||||
self.share_manager._provide_share_server_for_cg,
|
||||
self.context, "fake_id", cg)
|
||||
self.assertRaises(
|
||||
exception.ManilaException,
|
||||
self.share_manager._provide_share_server_for_share_group,
|
||||
self.context, "fake_id", sg)
|
||||
|
||||
driver_mock = self.share_manager.driver
|
||||
driver_method_mock = (
|
||||
driver_mock.choose_share_server_compatible_with_cg
|
||||
)
|
||||
driver_mock.choose_share_server_compatible_with_share_group)
|
||||
driver_method_mock.assert_called_once_with(
|
||||
self.context, [fake_share_server], cg, cgsnapshot=None)
|
||||
self.context, [fake_share_server], sg, share_group_snapshot=None)
|
||||
|
||||
def test_provide_share_server_for_cg_invalid_arguments(self):
|
||||
self.assertRaises(exception.InvalidInput,
|
||||
self.share_manager._provide_share_server_for_cg,
|
||||
def test_provide_share_server_for_share_group_invalid_arguments(self):
|
||||
self.assertRaises(
|
||||
exception.InvalidInput,
|
||||
self.share_manager._provide_share_server_for_share_group,
|
||||
self.context, None, None)
|
||||
|
||||
def test_manage_share_invalid_driver(self):
|
||||
@ -3144,19 +3146,19 @@ class ShareManagerTestCase(test.TestCase):
|
||||
self.assertEqual(old_capabilities,
|
||||
self.share_manager.last_capabilities)
|
||||
|
||||
def test_create_consistency_group(self):
|
||||
fake_cg = {'id': 'fake_id'}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
def test_create_share_group(self):
|
||||
fake_group = {'id': 'fake_id'}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_update',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_consistency_group',
|
||||
'create_share_group',
|
||||
mock.Mock(return_value=None))
|
||||
|
||||
self.share_manager.create_consistency_group(self.context, "fake_id")
|
||||
self.share_manager.create_share_group(self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
self.share_manager.db.share_group_update.\
|
||||
assert_called_once_with(mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE,
|
||||
'created_at': mock.ANY})
|
||||
@ -3166,207 +3168,226 @@ class ShareManagerTestCase(test.TestCase):
|
||||
self.mock_object(
|
||||
self.share_manager.driver.configuration, 'safe_get',
|
||||
mock.Mock(return_value=False))
|
||||
cg_id = 'fake_cg_id'
|
||||
cg_id = 'fake_group_id'
|
||||
share_network_id = 'fake_sn'
|
||||
fake_cg = {'id': 'fake_id', 'share_network_id': share_network_id}
|
||||
fake_group = {'id': 'fake_id', 'share_network_id': share_network_id}
|
||||
self.mock_object(
|
||||
self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update')
|
||||
self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_update')
|
||||
|
||||
self.assertRaises(
|
||||
exception.ManilaException,
|
||||
self.share_manager.create_consistency_group, self.context, cg_id)
|
||||
self.share_manager.create_share_group, self.context, cg_id)
|
||||
|
||||
self.share_manager.db.consistency_group_get.assert_called_once_with(
|
||||
self.share_manager.db.share_group_get.assert_called_once_with(
|
||||
utils.IsAMatcher(context.RequestContext), cg_id)
|
||||
self.share_manager.db.consistency_group_update.assert_called_once_with(
|
||||
self.share_manager.db.share_group_update.assert_called_once_with(
|
||||
utils.IsAMatcher(context.RequestContext), cg_id,
|
||||
{'status': constants.STATUS_ERROR})
|
||||
|
||||
def test_create_cg_with_share_network_driver_handles_servers(self):
|
||||
def test_create_sg_with_share_network_driver_handles_servers(self):
|
||||
manager.CONF.set_default('driver_handles_share_servers', True)
|
||||
self.mock_object(
|
||||
self.share_manager.driver.configuration, 'safe_get',
|
||||
mock.Mock(return_value=True))
|
||||
share_network_id = 'fake_sn'
|
||||
fake_cg = {'id': 'fake_id', 'share_network_id': share_network_id,
|
||||
'host': "fake_host"}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager, '_provide_share_server_for_cg',
|
||||
mock.Mock(return_value=({}, fake_cg)))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_consistency_group',
|
||||
fake_group = {
|
||||
'id': 'fake_id',
|
||||
'share_network_id': share_network_id,
|
||||
'host': "fake_host",
|
||||
}
|
||||
self.mock_object(
|
||||
self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(
|
||||
self.share_manager.db, 'share_group_update',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(
|
||||
self.share_manager, '_provide_share_server_for_share_group',
|
||||
mock.Mock(return_value=({}, fake_group)))
|
||||
self.mock_object(
|
||||
self.share_manager.driver, 'create_share_group',
|
||||
mock.Mock(return_value=None))
|
||||
|
||||
self.share_manager.create_consistency_group(self.context, "fake_id")
|
||||
self.share_manager.create_share_group(self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
assert_called_once_with(mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE,
|
||||
'created_at': mock.ANY})
|
||||
self.share_manager.db.share_group_update.assert_called_once_with(
|
||||
mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
|
||||
|
||||
def test_create_consistency_group_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))
|
||||
def test_create_share_group_with_update(self):
|
||||
fake_group = {'id': 'fake_id'}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_update',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_consistency_group',
|
||||
'create_share_group',
|
||||
mock.Mock(return_value={'foo': 'bar'}))
|
||||
|
||||
self.share_manager.create_consistency_group(self.context, "fake_id")
|
||||
self.share_manager.create_share_group(self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
self.share_manager.db.share_group_update.\
|
||||
assert_any_call(mock.ANY, 'fake_id', {'foo': 'bar'})
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
self.share_manager.db.share_group_update.\
|
||||
assert_any_call(mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE,
|
||||
'created_at': mock.ANY})
|
||||
|
||||
def test_create_consistency_group_with_error(self):
|
||||
fake_cg = {'id': 'fake_id'}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
def test_create_share_group_with_error(self):
|
||||
fake_group = {'id': 'fake_id'}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_update',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_consistency_group',
|
||||
'create_share_group',
|
||||
mock.Mock(side_effect=exception.Error))
|
||||
|
||||
self.assertRaises(exception.Error,
|
||||
self.share_manager.create_consistency_group,
|
||||
self.share_manager.create_share_group,
|
||||
self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
assert_called_once_with(mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_ERROR})
|
||||
self.share_manager.db.share_group_update.assert_called_once_with(
|
||||
mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR})
|
||||
|
||||
def test_create_consistency_group_from_cgsnapshot(self):
|
||||
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id',
|
||||
'shares': [], 'share_server_id': 'fake_ss_id'}
|
||||
def test_create_share_group_from_sg_snapshot(self):
|
||||
fake_group = {
|
||||
'id': 'fake_id',
|
||||
'source_share_group_snapshot_id': 'fake_snap_id',
|
||||
'shares': [],
|
||||
'share_server_id': 'fake_ss_id',
|
||||
}
|
||||
fake_ss = {'id': 'fake_ss_id', 'share_network_id': 'fake_sn'}
|
||||
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': [],
|
||||
'consistency_group': {'share_server_id': fake_ss['id']}}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_get',
|
||||
fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': [],
|
||||
'share_group': {'share_server_id': fake_ss['id']}}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_snapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db, 'share_server_get',
|
||||
mock.Mock(
|
||||
return_value=fake_ss))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_consistency_group_from_cgsnapshot',
|
||||
self.mock_object(self.share_manager.db, 'share_group_update',
|
||||
mock.Mock(return_value=fake_group))
|
||||
mock_create_sg_from_sg_snap = self.mock_object(
|
||||
self.share_manager.driver,
|
||||
'create_share_group_from_share_group_snapshot',
|
||||
mock.Mock(return_value=(None, None)))
|
||||
|
||||
self.share_manager.create_consistency_group(self.context, "fake_id")
|
||||
self.share_manager.create_share_group(self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
assert_called_once_with(mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE,
|
||||
'created_at': mock.ANY})
|
||||
self.share_manager.db.share_group_update.assert_called_once_with(
|
||||
mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
|
||||
self.share_manager.db.share_server_get(mock.ANY, 'fake_ss_id')
|
||||
self.share_manager.driver.create_consistency_group_from_cgsnapshot.\
|
||||
assert_called_once_with(
|
||||
mock.ANY, fake_cg, fake_snap, share_server=fake_ss)
|
||||
mock_create_sg_from_sg_snap.assert_called_once_with(
|
||||
mock.ANY, fake_group, fake_snap, share_server=fake_ss)
|
||||
|
||||
def test_create_cg_cgsnapshot_share_network_driver_not_handles_servers(
|
||||
self):
|
||||
def test_create_sg_snapshot_share_network_driver_not_handles_servers(self):
|
||||
manager.CONF.set_default('driver_handles_share_servers', False)
|
||||
self.mock_object(
|
||||
self.share_manager.driver.configuration, 'safe_get',
|
||||
mock.Mock(return_value=False))
|
||||
cg_id = 'fake_cg_id'
|
||||
sg_id = 'fake_share_group_id'
|
||||
share_network_id = 'fake_sn'
|
||||
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id',
|
||||
'shares': [], 'share_network_id': share_network_id,
|
||||
'host': "fake_host"}
|
||||
fake_group = {
|
||||
'id': 'fake_id',
|
||||
'source_share_group_snapshot_id': 'fake_snap_id',
|
||||
'shares': [],
|
||||
'share_network_id': share_network_id,
|
||||
'host': "fake_host",
|
||||
}
|
||||
self.mock_object(
|
||||
self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_get',
|
||||
self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'share_group_snapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update')
|
||||
self.mock_object(self.share_manager.db, 'share_group_update')
|
||||
|
||||
self.assertRaises(exception.ManilaException,
|
||||
self.share_manager.create_consistency_group,
|
||||
self.context, cg_id)
|
||||
self.share_manager.create_share_group,
|
||||
self.context, sg_id)
|
||||
|
||||
self.share_manager.db.consistency_group_get.assert_called_once_with(
|
||||
utils.IsAMatcher(context.RequestContext), cg_id)
|
||||
self.share_manager.db.consistency_group_update.assert_called_once_with(
|
||||
utils.IsAMatcher(context.RequestContext), cg_id,
|
||||
self.share_manager.db.share_group_get.assert_called_once_with(
|
||||
utils.IsAMatcher(context.RequestContext), sg_id)
|
||||
self.share_manager.db.share_group_update.assert_called_once_with(
|
||||
utils.IsAMatcher(context.RequestContext), sg_id,
|
||||
{'status': constants.STATUS_ERROR})
|
||||
|
||||
def test_create_cg_from_cgsnapshot_share_network_driver_handles_servers(
|
||||
self):
|
||||
def test_create_share_group_from_sg_snapshot_share_network_dhss(self):
|
||||
manager.CONF.set_default('driver_handles_share_servers', True)
|
||||
self.mock_object(self.share_manager.driver.configuration, 'safe_get',
|
||||
mock.Mock(return_value=True))
|
||||
share_network_id = 'fake_sn'
|
||||
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id',
|
||||
'shares': [], 'share_network_id': share_network_id}
|
||||
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_get',
|
||||
fake_group = {
|
||||
'id': 'fake_id',
|
||||
'source_share_group_snapshot_id': 'fake_snap_id',
|
||||
'shares': [],
|
||||
'share_network_id': share_network_id,
|
||||
}
|
||||
fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_snapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager, '_provide_share_server_for_cg',
|
||||
mock.Mock(return_value=({}, fake_cg)))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_consistency_group_from_cgsnapshot',
|
||||
self.mock_object(self.share_manager.db, 'share_group_update',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(
|
||||
self.share_manager, '_provide_share_server_for_share_group',
|
||||
mock.Mock(return_value=({}, fake_group)))
|
||||
self.mock_object(
|
||||
self.share_manager.driver,
|
||||
'create_share_group_from_share_group_snapshot',
|
||||
mock.Mock(return_value=(None, None)))
|
||||
|
||||
self.share_manager.create_consistency_group(self.context, "fake_id")
|
||||
self.share_manager.create_share_group(self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
assert_called_once_with(mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE,
|
||||
'created_at': mock.ANY})
|
||||
self.share_manager.db.share_group_update.assert_called_once_with(
|
||||
mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
|
||||
|
||||
def test_create_consistency_group_from_cgsnapshot_with_update(self):
|
||||
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id',
|
||||
'shares': []}
|
||||
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_get',
|
||||
def test_create_share_group_from_share_group_snapshot_with_update(self):
|
||||
fake_group = {
|
||||
'id': 'fake_id',
|
||||
'source_share_group_snapshot_id': 'fake_snap_id',
|
||||
'shares': [],
|
||||
}
|
||||
fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_snapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'share_group_update',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_consistency_group_from_cgsnapshot',
|
||||
'create_share_group_from_share_group_snapshot',
|
||||
mock.Mock(return_value=({'foo': 'bar'}, None)))
|
||||
|
||||
self.share_manager.create_consistency_group(self.context, "fake_id")
|
||||
self.share_manager.create_share_group(self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
assert_any_call(mock.ANY, 'fake_id', {'foo': 'bar'})
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
assert_any_call(mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE,
|
||||
'created_at': mock.ANY})
|
||||
self.share_manager.db.share_group_update.assert_any_call(
|
||||
mock.ANY, 'fake_id', {'foo': 'bar'})
|
||||
self.share_manager.db.share_group_update.assert_any_call(
|
||||
mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
|
||||
|
||||
def test_create_consistency_group_from_cgsnapshot_with_share_update(self):
|
||||
def test_create_share_group_from_sg_snapshot_with_share_update(self):
|
||||
fake_share = {'id': 'fake_share_id'}
|
||||
fake_export_locations = ['my_export_location']
|
||||
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id',
|
||||
'shares': [fake_share]}
|
||||
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_get',
|
||||
fake_group = {
|
||||
'id': 'fake_id',
|
||||
'source_share_group_snapshot_id': 'fake_snap_id',
|
||||
'shares': [fake_share],
|
||||
}
|
||||
fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_snapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update')
|
||||
self.mock_object(self.share_manager.db, 'share_group_update')
|
||||
self.mock_object(self.share_manager.db, 'share_instance_update')
|
||||
self.mock_object(self.share_manager.db,
|
||||
'share_export_locations_update')
|
||||
@ -3374,216 +3395,227 @@ class ShareManagerTestCase(test.TestCase):
|
||||
'foo': 'bar',
|
||||
'export_locations': fake_export_locations}]
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_consistency_group_from_cgsnapshot',
|
||||
'create_share_group_from_share_group_snapshot',
|
||||
mock.Mock(
|
||||
return_value=(None, fake_share_update_list)))
|
||||
|
||||
self.share_manager.create_consistency_group(self.context, "fake_id")
|
||||
self.share_manager.create_share_group(self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.share_instance_update.\
|
||||
assert_any_call(mock.ANY, 'fake_share_id', {'foo': 'bar'})
|
||||
self.share_manager.db.share_export_locations_update.\
|
||||
assert_any_call(mock.ANY, 'fake_share_id', fake_export_locations)
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
assert_any_call(mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE,
|
||||
'created_at': mock.ANY})
|
||||
self.share_manager.db.share_instance_update.assert_any_call(
|
||||
mock.ANY, 'fake_share_id', {'foo': 'bar'})
|
||||
self.share_manager.db.share_export_locations_update.assert_any_call(
|
||||
mock.ANY, 'fake_share_id', fake_export_locations)
|
||||
self.share_manager.db.share_group_update.assert_any_call(
|
||||
mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
|
||||
|
||||
def test_create_consistency_group_from_cgsnapshot_with_error(self):
|
||||
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id',
|
||||
'shares': []}
|
||||
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_get',
|
||||
def test_create_share_group_from_sg_snapshot_with_error(self):
|
||||
fake_group = {
|
||||
'id': 'fake_id',
|
||||
'source_share_group_snapshot_id': 'fake_snap_id',
|
||||
'shares': [],
|
||||
}
|
||||
fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_snapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db,
|
||||
'share_instances_get_all_by_consistency_group_id',
|
||||
'share_instances_get_all_by_share_group_id',
|
||||
mock.Mock(return_value=[]))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'share_group_update',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_consistency_group_from_cgsnapshot',
|
||||
'create_share_group_from_share_group_snapshot',
|
||||
mock.Mock(side_effect=exception.Error))
|
||||
|
||||
self.assertRaises(exception.Error,
|
||||
self.share_manager.create_consistency_group,
|
||||
self.share_manager.create_share_group,
|
||||
self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
assert_called_once_with(mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_ERROR})
|
||||
self.share_manager.db.share_group_update.assert_called_once_with(
|
||||
mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR})
|
||||
|
||||
def test_create_consistency_group_from_cgsnapshot_with_share_error(self):
|
||||
def test_create_share_group_from_sg_snapshot_with_share_error(self):
|
||||
fake_share = {'id': 'fake_share_id'}
|
||||
fake_cg = {'id': 'fake_id', 'source_cgsnapshot_id': 'fake_snap_id',
|
||||
'shares': [fake_share]}
|
||||
fake_snap = {'id': 'fake_snap_id', 'cgsnapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_get',
|
||||
fake_group = {
|
||||
'id': 'fake_id',
|
||||
'source_share_group_snapshot_id': 'fake_snap_id',
|
||||
'shares': [fake_share],
|
||||
}
|
||||
fake_snap = {'id': 'fake_snap_id', 'share_group_snapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_snapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db,
|
||||
'share_instances_get_all_by_consistency_group_id',
|
||||
'share_instances_get_all_by_share_group_id',
|
||||
mock.Mock(return_value=[fake_share]))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update')
|
||||
self.mock_object(self.share_manager.db, 'share_group_update')
|
||||
self.mock_object(self.share_manager.db, 'share_instance_update')
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_consistency_group_from_cgsnapshot',
|
||||
'create_share_group_from_share_group_snapshot',
|
||||
mock.Mock(side_effect=exception.Error))
|
||||
|
||||
self.assertRaises(exception.Error,
|
||||
self.share_manager.create_consistency_group,
|
||||
self.share_manager.create_share_group,
|
||||
self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.share_instance_update.\
|
||||
assert_any_call(mock.ANY, 'fake_share_id',
|
||||
{'status': constants.STATUS_ERROR})
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
assert_called_once_with(mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_ERROR})
|
||||
self.share_manager.db.share_instance_update.assert_any_call(
|
||||
mock.ANY, 'fake_share_id', {'status': constants.STATUS_ERROR})
|
||||
self.share_manager.db.share_group_update.assert_called_once_with(
|
||||
mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR})
|
||||
|
||||
def test_delete_consistency_group(self):
|
||||
fake_cg = {'id': 'fake_id'}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_destroy',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
def test_delete_share_group(self):
|
||||
fake_group = {'id': 'fake_id'}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_update',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_destroy',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'delete_consistency_group',
|
||||
'delete_share_group',
|
||||
mock.Mock(return_value=None))
|
||||
|
||||
self.share_manager.delete_consistency_group(self.context, "fake_id")
|
||||
self.share_manager.delete_share_group(self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.consistency_group_destroy.\
|
||||
assert_called_once_with(mock.ANY, 'fake_id')
|
||||
self.share_manager.db.share_group_destroy.assert_called_once_with(
|
||||
mock.ANY, 'fake_id')
|
||||
|
||||
def test_delete_consistency_group_with_update(self):
|
||||
fake_cg = {'id': 'fake_id'}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_destroy',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
def test_delete_share_group_with_update(self):
|
||||
fake_group = {'id': 'fake_id'}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_update',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_destroy',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'delete_consistency_group',
|
||||
'delete_share_group',
|
||||
mock.Mock(return_value={'foo': 'bar'}))
|
||||
|
||||
self.share_manager.delete_consistency_group(self.context, "fake_id")
|
||||
self.share_manager.delete_share_group(self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
assert_called_once_with(mock.ANY, 'fake_id', {'foo': 'bar'})
|
||||
self.share_manager.db.consistency_group_destroy.\
|
||||
assert_called_once_with(mock.ANY, 'fake_id')
|
||||
self.share_manager.db.share_group_update.assert_called_once_with(
|
||||
mock.ANY, 'fake_id', {'foo': 'bar'})
|
||||
self.share_manager.db.share_group_destroy.assert_called_once_with(
|
||||
mock.ANY, 'fake_id')
|
||||
|
||||
def test_delete_consistency_group_with_error(self):
|
||||
fake_cg = {'id': 'fake_id'}
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_update',
|
||||
mock.Mock(return_value=fake_cg))
|
||||
def test_delete_share_group_with_error(self):
|
||||
fake_group = {'id': 'fake_id'}
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.db, 'share_group_update',
|
||||
mock.Mock(return_value=fake_group))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'delete_consistency_group',
|
||||
'delete_share_group',
|
||||
mock.Mock(side_effect=exception.Error))
|
||||
|
||||
self.assertRaises(exception.Error,
|
||||
self.share_manager.delete_consistency_group,
|
||||
self.share_manager.delete_share_group,
|
||||
self.context, "fake_id")
|
||||
|
||||
self.share_manager.db.consistency_group_update.\
|
||||
assert_called_once_with(mock.ANY, 'fake_id',
|
||||
{'status': constants.STATUS_ERROR})
|
||||
self.share_manager.db.share_group_update.assert_called_once_with(
|
||||
mock.ANY, 'fake_id', {'status': constants.STATUS_ERROR})
|
||||
|
||||
def test_create_cgsnapshot(self):
|
||||
fake_snap = {'id': 'fake_snap_id', 'consistency_group': {},
|
||||
'cgsnapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_get',
|
||||
def test_create_share_group_snapshot(self):
|
||||
fake_snap = {
|
||||
'id': 'fake_snap_id',
|
||||
'share_group': {},
|
||||
'share_group_snapshot_members': [],
|
||||
}
|
||||
self.mock_object(
|
||||
self.share_manager.db, 'share_group_snapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_update',
|
||||
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_cgsnapshot',
|
||||
self.mock_object(
|
||||
self.share_manager.driver,
|
||||
'create_share_group_snapshot',
|
||||
mock.Mock(return_value=(None, None)))
|
||||
|
||||
self.share_manager.create_cgsnapshot(self.context, fake_snap['id'])
|
||||
self.share_manager.create_share_group_snapshot(
|
||||
self.context, fake_snap['id'])
|
||||
|
||||
self.share_manager.db.cgsnapshot_update.\
|
||||
assert_called_once_with(mock.ANY, fake_snap['id'],
|
||||
{'status': constants.STATUS_AVAILABLE,
|
||||
'created_at': mock.ANY})
|
||||
|
||||
def test_create_cgsnapshot_with_update(self):
|
||||
fake_snap = {'id': 'fake_snap_id', 'consistency_group': {},
|
||||
'cgsnapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_update',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_cgsnapshot',
|
||||
mock.Mock(return_value=({'foo': 'bar'}, None)))
|
||||
|
||||
self.share_manager.create_cgsnapshot(self.context, fake_snap['id'])
|
||||
|
||||
self.share_manager.db.cgsnapshot_update.\
|
||||
assert_any_call(mock.ANY, 'fake_snap_id', {'foo': 'bar'})
|
||||
self.share_manager.db.cgsnapshot_update.assert_any_call(
|
||||
mock_sg_snap_update.assert_called_once_with(
|
||||
mock.ANY, fake_snap['id'],
|
||||
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
|
||||
|
||||
def test_create_cgsnapshot_with_member_update(self):
|
||||
fake_member = {
|
||||
'id': 'fake_member_id',
|
||||
'share_instance_id': 'blah',
|
||||
}
|
||||
fake_member_update = {
|
||||
'id': 'fake_member_id',
|
||||
'foo': 'bar'
|
||||
}
|
||||
fake_snap = {'id': 'fake_snap_id', 'consistency_group': {},
|
||||
'cgsnapshot_members': [fake_member]}
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_get',
|
||||
def test_create_share_group_snapshot_with_update(self):
|
||||
fake_snap = {'id': 'fake_snap_id', 'share_group': {},
|
||||
'share_group_snapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'share_group_snapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_update',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_member_update')
|
||||
self.mock_object(self.share_manager.db, 'share_instance_get',
|
||||
mock.Mock(return_value={'id': 'blah'}))
|
||||
self.mock_object(self.share_manager.driver, 'create_cgsnapshot',
|
||||
mock.Mock(return_value=(None, [fake_member_update])))
|
||||
|
||||
self.share_manager.create_cgsnapshot(self.context, fake_snap['id'])
|
||||
|
||||
self.share_manager.db.cgsnapshot_update.assert_any_call(
|
||||
mock.ANY, fake_snap['id'],
|
||||
{'cgsnapshot_members': [fake_member_update]})
|
||||
self.share_manager.db.cgsnapshot_update.\
|
||||
assert_any_call(mock.ANY, fake_snap['id'],
|
||||
{'status': constants.STATUS_AVAILABLE,
|
||||
'created_at': mock.ANY})
|
||||
self.assertTrue(self.share_manager.db.cgsnapshot_member_update.called)
|
||||
|
||||
def test_create_cgsnapshot_with_error(self):
|
||||
fake_snap = {'id': 'fake_snap_id', 'consistency_group': {},
|
||||
'cgsnapshot_members': []}
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.db, 'cgsnapshot_update',
|
||||
self.mock_object(self.share_manager.db, 'share_group_snapshot_update',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(self.share_manager.driver,
|
||||
'create_cgsnapshot',
|
||||
mock.Mock(side_effect=exception.Error))
|
||||
'create_share_group_snapshot',
|
||||
mock.Mock(return_value=({'foo': 'bar'}, None)))
|
||||
|
||||
self.assertRaises(exception.Error,
|
||||
self.share_manager.create_cgsnapshot,
|
||||
self.share_manager.create_share_group_snapshot(
|
||||
self.context, fake_snap['id'])
|
||||
|
||||
self.share_manager.db.cgsnapshot_update.\
|
||||
assert_called_once_with(mock.ANY, fake_snap['id'],
|
||||
{'status': constants.STATUS_ERROR})
|
||||
self.share_manager.db.share_group_snapshot_update.assert_any_call(
|
||||
mock.ANY, 'fake_snap_id', {'foo': 'bar'})
|
||||
self.share_manager.db.share_group_snapshot_update.assert_any_call(
|
||||
mock.ANY, fake_snap['id'],
|
||||
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
|
||||
|
||||
def test_create_share_group_snapshot_with_member_update(self):
|
||||
fake_member = {'id': 'fake_member_id', 'share_instance_id': 'blah'}
|
||||
fake_member_update = {'id': 'fake_member_id', 'foo': 'bar'}
|
||||
fake_snap = {'id': 'fake_snap_id', 'share_group': {},
|
||||
'share_group_snapshot_members': [fake_member]}
|
||||
self.mock_object(
|
||||
self.share_manager.db, 'share_group_snapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(
|
||||
self.share_manager.db, 'share_group_snapshot_update',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(
|
||||
self.share_manager.db, 'share_group_snapshot_member_update')
|
||||
self.mock_object(
|
||||
self.share_manager.db, 'share_instance_get',
|
||||
mock.Mock(return_value={'id': 'blah'}))
|
||||
self.mock_object(
|
||||
self.share_manager.driver, 'create_share_group_snapshot',
|
||||
mock.Mock(return_value=(None, [fake_member_update])))
|
||||
|
||||
self.share_manager.create_share_group_snapshot(
|
||||
self.context, fake_snap['id'])
|
||||
|
||||
self.share_manager.db.share_group_snapshot_update.assert_any_call(
|
||||
mock.ANY, fake_snap['id'],
|
||||
{'share_group_snapshot_members': [fake_member_update]})
|
||||
self.share_manager.db.share_group_snapshot_update.assert_any_call(
|
||||
mock.ANY, fake_snap['id'],
|
||||
{'status': constants.STATUS_AVAILABLE, 'created_at': mock.ANY})
|
||||
self.assertTrue(
|
||||
self.share_manager.db.share_group_snapshot_member_update.called)
|
||||
|
||||
def test_create_group_snapshot_with_error(self):
|
||||
fake_snap = {'id': 'fake_snap_id', 'share_group': {},
|
||||
'share_group_snapshot_members': []}
|
||||
self.mock_object(
|
||||
self.share_manager.db, 'share_group_snapshot_get',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
mock_sg_snap_update = self.mock_object(
|
||||
self.share_manager.db, 'share_group_snapshot_update',
|
||||
mock.Mock(return_value=fake_snap))
|
||||
self.mock_object(
|
||||
self.share_manager.driver,
|
||||
'create_share_group_snapshot',
|
||||
mock.Mock(side_effect=exception.Error))
|
||||
|
||||
self.assertRaises(
|
||||
exception.Error,
|
||||
self.share_manager.create_share_group_snapshot,
|
||||
self.context, fake_snap['id'])
|
||||
|
||||
mock_sg_snap_update.assert_called_once_with(
|
||||
mock.ANY, fake_snap['id'], {'status': constants.STATUS_ERROR})
|
||||
|
||||
def test_connection_get_info(self):
|
||||
share_instance = {'share_server_id': 'fake_server_id'}
|
||||
@ -4857,9 +4889,9 @@ class ShareManagerTestCase(test.TestCase):
|
||||
def test_provide_share_server(self):
|
||||
|
||||
instance = db_utils.create_share_instance(share_id='fake_id',
|
||||
consistency_group_id='cg_id')
|
||||
share_group_id='sg_id')
|
||||
snapshot = db_utils.create_snapshot(with_share=True)
|
||||
cg = db_utils.create_consistency_group()
|
||||
group = db_utils.create_share_group()
|
||||
server = db_utils.create_share_server()
|
||||
|
||||
# mocks
|
||||
@ -4867,8 +4899,8 @@ class ShareManagerTestCase(test.TestCase):
|
||||
mock.Mock(return_value=instance))
|
||||
self.mock_object(self.share_manager.db, 'share_snapshot_get',
|
||||
mock.Mock(return_value=snapshot))
|
||||
self.mock_object(self.share_manager.db, 'consistency_group_get',
|
||||
mock.Mock(return_value=cg))
|
||||
self.mock_object(self.share_manager.db, 'share_group_get',
|
||||
mock.Mock(return_value=group))
|
||||
self.mock_object(self.share_manager, '_provide_share_server_for_share',
|
||||
mock.Mock(return_value=(server, instance)))
|
||||
|
||||
@ -4882,11 +4914,11 @@ class ShareManagerTestCase(test.TestCase):
|
||||
self.context, 'ins_id', with_share_data=True)
|
||||
self.share_manager.db.share_snapshot_get.assert_called_once_with(
|
||||
self.context, 'snap_id')
|
||||
self.share_manager.db.consistency_group_get.assert_called_once_with(
|
||||
self.context, 'cg_id')
|
||||
self.share_manager.db.share_group_get.assert_called_once_with(
|
||||
self.context, 'sg_id')
|
||||
(self.share_manager._provide_share_server_for_share.
|
||||
assert_called_once_with(self.context, 'net_id', instance, snapshot,
|
||||
cg, create_on_backend=False))
|
||||
group, create_on_backend=False))
|
||||
|
||||
def test_create_share_server(self):
|
||||
|
||||
|
@ -46,8 +46,8 @@ class ShareRpcAPITestCase(test.TestCase):
|
||||
host='fake_host',
|
||||
)
|
||||
share_server = db_utils.create_share_server()
|
||||
cg = {'id': 'fake_cg_id', 'host': 'fake_host'}
|
||||
cgsnapshot = {'id': 'fake_cg_id'}
|
||||
share_group = {'id': 'fake_share_group_id', 'host': 'fake_host'}
|
||||
share_group_snapshot = {'id': 'fake_share_group_id'}
|
||||
host = 'fake_host'
|
||||
self.fake_share = jsonutils.to_primitive(share)
|
||||
# mock out the getattr on the share db model object since jsonutils
|
||||
@ -56,8 +56,9 @@ class ShareRpcAPITestCase(test.TestCase):
|
||||
self.fake_share_replica = jsonutils.to_primitive(share_replica)
|
||||
self.fake_snapshot = jsonutils.to_primitive(snapshot)
|
||||
self.fake_share_server = jsonutils.to_primitive(share_server)
|
||||
self.fake_cg = jsonutils.to_primitive(cg)
|
||||
self.fake_cgsnapshot = jsonutils.to_primitive(cgsnapshot)
|
||||
self.fake_share_group = jsonutils.to_primitive(share_group)
|
||||
self.fake_share_group_snapshot = jsonutils.to_primitive(
|
||||
share_group_snapshot)
|
||||
self.fake_host = jsonutils.to_primitive(host)
|
||||
self.ctxt = context.RequestContext('fake_user', 'fake_project')
|
||||
self.rpcapi = share_rpcapi.ShareAPI()
|
||||
@ -79,14 +80,14 @@ class ShareRpcAPITestCase(test.TestCase):
|
||||
if 'share_instance' in expected_msg:
|
||||
share_instance = expected_msg.pop('share_instance', None)
|
||||
expected_msg['share_instance_id'] = share_instance['id']
|
||||
if 'cg' in expected_msg:
|
||||
cg = expected_msg['cg']
|
||||
del expected_msg['cg']
|
||||
expected_msg['cg_id'] = cg['id']
|
||||
if 'cgsnapshot' in expected_msg:
|
||||
snap = expected_msg['cgsnapshot']
|
||||
del expected_msg['cgsnapshot']
|
||||
expected_msg['cgsnapshot_id'] = snap['id']
|
||||
if 'share_group' in expected_msg:
|
||||
share_group = expected_msg['share_group']
|
||||
del expected_msg['share_group']
|
||||
expected_msg['share_group_id'] = share_group['id']
|
||||
if 'share_group_snapshot' in expected_msg:
|
||||
snap = expected_msg['share_group_snapshot']
|
||||
del expected_msg['share_group_snapshot']
|
||||
expected_msg['share_group_snapshot_id'] = snap['id']
|
||||
if 'host' in expected_msg:
|
||||
del expected_msg['host']
|
||||
if 'snapshot' in expected_msg:
|
||||
@ -113,8 +114,8 @@ class ShareRpcAPITestCase(test.TestCase):
|
||||
|
||||
if 'host' in kwargs:
|
||||
host = kwargs['host']
|
||||
elif 'cg' in kwargs:
|
||||
host = kwargs['cg']['host']
|
||||
elif 'share_group' in kwargs:
|
||||
host = kwargs['share_group']['host']
|
||||
elif 'share_instance' in kwargs:
|
||||
host = kwargs['share_instance']['host']
|
||||
elif 'share_server' in kwargs:
|
||||
@ -213,31 +214,33 @@ class ShareRpcAPITestCase(test.TestCase):
|
||||
share=self.fake_share,
|
||||
new_size=123)
|
||||
|
||||
def test_create_consistency_group(self):
|
||||
self._test_share_api('create_consistency_group',
|
||||
version='1.5',
|
||||
def test_create_share_group(self):
|
||||
self._test_share_api('create_share_group',
|
||||
version='1.16',
|
||||
rpc_method='cast',
|
||||
cg=self.fake_cg,
|
||||
share_group=self.fake_share_group,
|
||||
host='fake_host1')
|
||||
|
||||
def test_delete_consistency_group(self):
|
||||
self._test_share_api('delete_consistency_group',
|
||||
version='1.5',
|
||||
def test_delete_share_group(self):
|
||||
self._test_share_api('delete_share_group',
|
||||
version='1.16',
|
||||
rpc_method='cast',
|
||||
cg=self.fake_cg)
|
||||
share_group=self.fake_share_group)
|
||||
|
||||
def test_create_cgsnapshot(self):
|
||||
self._test_share_api('create_cgsnapshot',
|
||||
version='1.5',
|
||||
def test_create_share_group_snapshot(self):
|
||||
self._test_share_api(
|
||||
'create_share_group_snapshot',
|
||||
version='1.16',
|
||||
rpc_method='cast',
|
||||
cgsnapshot=self.fake_cgsnapshot,
|
||||
share_group_snapshot=self.fake_share_group_snapshot,
|
||||
host='fake_host1')
|
||||
|
||||
def test_delete_cgsnapshot(self):
|
||||
self._test_share_api('delete_cgsnapshot',
|
||||
version='1.5',
|
||||
def test_delete_share_group_snapshot(self):
|
||||
self._test_share_api(
|
||||
'delete_share_group_snapshot',
|
||||
version='1.16',
|
||||
rpc_method='cast',
|
||||
cgsnapshot=self.fake_cgsnapshot,
|
||||
share_group_snapshot=self.fake_share_group_snapshot,
|
||||
host='fake_host1')
|
||||
|
||||
def test_migration_start(self):
|
||||
|
File diff suppressed because it is too large
Load Diff
156
manila/tests/share_group/test_share_group_types.py
Normal file
156
manila/tests/share_group/test_share_group_types.py
Normal file
@ -0,0 +1,156 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
"""Test of Share Type methods for Manila."""
|
||||
import copy
|
||||
import datetime
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
|
||||
from manila.common import constants
|
||||
from manila import context
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila.share_group import share_group_types
|
||||
from manila import test
|
||||
|
||||
|
||||
def create_share_group_type_dict(group_specs=None):
|
||||
return {
|
||||
'fake_type': {
|
||||
'name': 'fake1',
|
||||
'group_specs': group_specs
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ShareGroupTypesTestCase(test.TestCase):
|
||||
|
||||
fake_type = {
|
||||
'test': {
|
||||
'created_at': datetime.datetime(2015, 1, 22, 11, 43, 24),
|
||||
'deleted': '0',
|
||||
'deleted_at': None,
|
||||
'group_specs': {},
|
||||
'id': u'fooid-1',
|
||||
'name': u'test',
|
||||
'updated_at': None
|
||||
}
|
||||
}
|
||||
fake_group_specs = {u'gold': u'True'}
|
||||
fake_share_group_type_id = u'fooid-2'
|
||||
fake_type_w_extra = {
|
||||
'test_with_extra': {
|
||||
'created_at': datetime.datetime(2015, 1, 22, 11, 45, 31),
|
||||
'deleted': '0',
|
||||
'deleted_at': None,
|
||||
'group_specs': fake_group_specs,
|
||||
'id': fake_share_group_type_id,
|
||||
'name': u'test_with_extra',
|
||||
'updated_at': None
|
||||
}
|
||||
}
|
||||
|
||||
fake_type_w_valid_extra = {
|
||||
'test_with_extra': {
|
||||
'created_at': datetime.datetime(2015, 1, 22, 11, 45, 31),
|
||||
'deleted': '0',
|
||||
'deleted_at': None,
|
||||
'group_specs': {
|
||||
constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS: 'true'
|
||||
},
|
||||
'id': u'fooid-2',
|
||||
'name': u'test_with_extra',
|
||||
'updated_at': None
|
||||
}
|
||||
}
|
||||
|
||||
fake_types = fake_type.copy()
|
||||
fake_types.update(fake_type_w_extra)
|
||||
fake_types.update(fake_type_w_valid_extra)
|
||||
|
||||
fake_share_group = {
|
||||
'id': u'fooid-1',
|
||||
'share_group_type_id': fake_share_group_type_id,
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(ShareGroupTypesTestCase, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
@ddt.data({}, fake_type, fake_type_w_extra, fake_types)
|
||||
def test_get_all_types(self, share_group_type):
|
||||
self.mock_object(
|
||||
db, 'share_group_type_get_all',
|
||||
mock.Mock(return_value=copy.deepcopy(share_group_type)))
|
||||
|
||||
returned_type = share_group_types.get_all(self.context)
|
||||
|
||||
self.assertItemsEqual(share_group_type, returned_type)
|
||||
|
||||
def test_get_all_types_search(self):
|
||||
share_group_type = self.fake_type_w_extra
|
||||
search_filter = {"group_specs": {"gold": "True"}, 'is_public': True}
|
||||
self.mock_object(
|
||||
db, 'share_group_type_get_all',
|
||||
mock.Mock(return_value=share_group_type))
|
||||
|
||||
returned_type = share_group_types.get_all(
|
||||
self.context, search_opts=search_filter)
|
||||
|
||||
db.share_group_type_get_all.assert_called_once_with(
|
||||
mock.ANY, 0, filters={'is_public': True})
|
||||
self.assertItemsEqual(share_group_type, returned_type)
|
||||
search_filter = {"group_specs": {"gold": "False"}}
|
||||
returned_type = share_group_types.get_all(
|
||||
self.context, search_opts=search_filter)
|
||||
self.assertEqual({}, returned_type)
|
||||
|
||||
def test_add_access(self):
|
||||
project_id = '456'
|
||||
share_group_type = share_group_types.create(self.context, 'type2', [])
|
||||
share_group_type_id = share_group_type.get('id')
|
||||
|
||||
share_group_types.add_share_group_type_access(
|
||||
self.context, share_group_type_id, project_id)
|
||||
stype_access = db.share_group_type_access_get_all(
|
||||
self.context, share_group_type_id)
|
||||
|
||||
self.assertIn(project_id, [a.project_id for a in stype_access])
|
||||
|
||||
def test_add_access_invalid(self):
|
||||
self.assertRaises(
|
||||
exception.InvalidShareGroupType,
|
||||
share_group_types.add_share_group_type_access,
|
||||
'fake', None, 'fake')
|
||||
|
||||
def test_remove_access(self):
|
||||
project_id = '456'
|
||||
share_group_type = share_group_types.create(
|
||||
self.context, 'type3', [], projects=['456'])
|
||||
share_group_type_id = share_group_type.get('id')
|
||||
|
||||
share_group_types.remove_share_group_type_access(
|
||||
self.context, share_group_type_id, project_id)
|
||||
stype_access = db.share_group_type_access_get_all(
|
||||
self.context, share_group_type_id)
|
||||
|
||||
self.assertNotIn(project_id, stype_access)
|
||||
|
||||
def test_remove_access_invalid(self):
|
||||
self.assertRaises(
|
||||
exception.InvalidShareGroupType,
|
||||
share_group_types.remove_share_group_type_access,
|
||||
'fake', None, 'fake')
|
@ -30,7 +30,7 @@ ShareGroup = [
|
||||
help="The minimum api microversion is configured to be the "
|
||||
"value of the minimum microversion supported by Manila."),
|
||||
cfg.StrOpt("max_api_microversion",
|
||||
default="2.30",
|
||||
default="2.31",
|
||||
help="The maximum api microversion is configured to be the "
|
||||
"value of the latest microversion supported by Manila."),
|
||||
cfg.StrOpt("region",
|
||||
@ -171,7 +171,7 @@ ShareGroup = [
|
||||
"to snapshots or not. Enable this feature if used "
|
||||
"driver supports it."),
|
||||
cfg.BoolOpt("run_consistency_group_tests",
|
||||
default=True,
|
||||
default=False,
|
||||
help="Defines whether to run consistency group tests or not. "
|
||||
"Disable this feature if used driver doesn't support "
|
||||
"it."),
|
||||
|
@ -94,9 +94,6 @@ class SharesActionsTest(base.BaseSharesTest):
|
||||
expected_keys.extend(["export_location", "export_locations"])
|
||||
if utils.is_microversion_ge(version, '2.2'):
|
||||
expected_keys.append("snapshot_support")
|
||||
if utils.is_microversion_ge(version, '2.4'):
|
||||
expected_keys.extend(["consistency_group_id",
|
||||
"source_cgsnapshot_member_id"])
|
||||
if utils.is_microversion_ge(version, '2.5'):
|
||||
expected_keys.append("share_type_name")
|
||||
if utils.is_microversion_ge(version, '2.10'):
|
||||
@ -136,11 +133,6 @@ class SharesActionsTest(base.BaseSharesTest):
|
||||
def test_get_share_with_snapshot_support_key(self):
|
||||
self._get_share('2.2')
|
||||
|
||||
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
|
||||
@utils.skip_if_microversion_not_supported('2.4')
|
||||
def test_get_share_with_consistency_groups_keys(self):
|
||||
self._get_share('2.4')
|
||||
|
||||
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
|
||||
@utils.skip_if_microversion_not_supported('2.6')
|
||||
def test_get_share_with_share_type_name_key(self):
|
||||
@ -210,9 +202,6 @@ class SharesActionsTest(base.BaseSharesTest):
|
||||
keys.extend(["export_location", "export_locations"])
|
||||
if utils.is_microversion_ge(version, '2.2'):
|
||||
keys.append("snapshot_support")
|
||||
if utils.is_microversion_ge(version, '2.4'):
|
||||
keys.extend(["consistency_group_id",
|
||||
"source_cgsnapshot_member_id"])
|
||||
if utils.is_microversion_ge(version, '2.6'):
|
||||
keys.append("share_type_name")
|
||||
if utils.is_microversion_ge(version, '2.10'):
|
||||
@ -242,11 +231,6 @@ class SharesActionsTest(base.BaseSharesTest):
|
||||
def test_list_shares_with_detail_and_snapshot_support_key(self):
|
||||
self._list_shares_with_detail('2.2')
|
||||
|
||||
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
|
||||
@utils.skip_if_microversion_not_supported('2.4')
|
||||
def test_list_shares_with_detail_consistency_groups_keys(self):
|
||||
self._list_shares_with_detail('2.4')
|
||||
|
||||
@tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
|
||||
@utils.skip_if_microversion_not_supported('2.6')
|
||||
def test_list_shares_with_detail_share_type_name_key(self):
|
||||
|
@ -37,7 +37,6 @@ manila.scheduler.filters =
|
||||
AvailabilityZoneFilter = manila.scheduler.filters.availability_zone:AvailabilityZoneFilter
|
||||
CapabilitiesFilter = manila.scheduler.filters.capabilities:CapabilitiesFilter
|
||||
CapacityFilter = manila.scheduler.filters.capacity:CapacityFilter
|
||||
ConsistencyGroupFilter = manila.scheduler.filters.consistency_group:ConsistencyGroupFilter
|
||||
DriverFilter = manila.scheduler.filters.driver:DriverFilter
|
||||
IgnoreAttemptedHostsFilter = manila.scheduler.filters.ignore_attempted_hosts:IgnoreAttemptedHostsFilter
|
||||
JsonFilter = manila.scheduler.filters.json:JsonFilter
|
||||
|
Loading…
Reference in New Issue
Block a user