Add v2 Manila API path as base for microversions
To prevent a microversioned client from managing a non-microversioned Manila server, Manila must update its REST endpoints by adding /v2 for all microversioned APIs. This commit does the following: * Add /v2 to the URL map, connected to all the same /v1 API methods * Renumber the microversion sequence starting from 2.0 * Update the versions API to reflect v2 * Publish the new endpoint to Keystone in the DevStack plug-in * Update relevant documentation * Update Tempest tests for microversions APIImpact Co-Authored-By: Andrew Kerr <andrew.kerr@netapp.com> Closes-Bug: 1488624 Change-Id: I56a516b5f81914557dd2465746629431cfd6deac
This commit is contained in:
parent
3e0284b524
commit
dddc068879
@ -434,11 +434,19 @@ function create_manila_accounts {
|
||||
create_service_user "manila"
|
||||
|
||||
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
|
||||
# Set up Manila v1 service and endpoint
|
||||
get_or_create_service "manila" "share" "Manila Shared Filesystem Service"
|
||||
get_or_create_endpoint "share" "$REGION_NAME" \
|
||||
"$MANILA_SERVICE_PROTOCOL://$MANILA_SERVICE_HOST:$MANILA_SERVICE_PORT/v1/\$(tenant_id)s" \
|
||||
"$MANILA_SERVICE_PROTOCOL://$MANILA_SERVICE_HOST:$MANILA_SERVICE_PORT/v1/\$(tenant_id)s" \
|
||||
"$MANILA_SERVICE_PROTOCOL://$MANILA_SERVICE_HOST:$MANILA_SERVICE_PORT/v1/\$(tenant_id)s"
|
||||
|
||||
# Set up Manila v2 service and endpoint
|
||||
get_or_create_service "manilav2" "sharev2" "Manila Shared Filesystem Service V2"
|
||||
get_or_create_endpoint "sharev2" "$REGION_NAME" \
|
||||
"$MANILA_SERVICE_PROTOCOL://$MANILA_SERVICE_HOST:$MANILA_SERVICE_PORT/v2/\$(tenant_id)s" \
|
||||
"$MANILA_SERVICE_PROTOCOL://$MANILA_SERVICE_HOST:$MANILA_SERVICE_PORT/v2/\$(tenant_id)s" \
|
||||
"$MANILA_SERVICE_PROTOCOL://$MANILA_SERVICE_HOST:$MANILA_SERVICE_PORT/v2/\$(tenant_id)s"
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -132,6 +132,11 @@ Here are the registration steps, similar to those of Cinder:
|
||||
--type share \
|
||||
--description "OpenStack Shared Filesystems"
|
||||
|
||||
$ keystone service-create \
|
||||
--name manilav2 \
|
||||
--type sharev2 \
|
||||
--description "OpenStack Shared Filesystems V2"
|
||||
|
||||
|
||||
Result::
|
||||
|
||||
@ -145,6 +150,16 @@ Result::
|
||||
| type | share |
|
||||
+-------------+----------------------------------+
|
||||
|
||||
+-------------+----------------------------------+
|
||||
| Property | Value |
|
||||
+-------------+----------------------------------+
|
||||
| description | OpenStack Shared Filesystems V2 |
|
||||
| enabled | True |
|
||||
| id | 2840d1e7b033437f8776a7bd5045b28d |
|
||||
| name | manilav2 |
|
||||
| type | sharev2 |
|
||||
+-------------+----------------------------------+
|
||||
|
||||
|
||||
4) Create the Share Filesystems service API endpoints::
|
||||
|
||||
@ -155,6 +170,13 @@ Result::
|
||||
--adminurl http://controller:8786/v1/%\(tenant_id\)s \
|
||||
--region regionOne
|
||||
|
||||
$ keystone endpoint-create \
|
||||
--service-id $(keystone service-list | awk '/ sharev2 / {print $2}') \
|
||||
--publicurl http://controller:8786/v2/%\(tenant_id\)s \
|
||||
--internalurl http://controller:8786/v2/%\(tenant_id\)s \
|
||||
--adminurl http://controller:8786/v2/%\(tenant_id\)s \
|
||||
--region regionOne
|
||||
|
||||
Result::
|
||||
|
||||
+-------------+-----------------------------------------+
|
||||
@ -168,6 +190,17 @@ Result::
|
||||
| service_id | 4c13e9ff7ec04f4e95a26f72ecdf9919 |
|
||||
+-------------+-----------------------------------------+
|
||||
|
||||
+-------------+-----------------------------------------+
|
||||
| Property | Value |
|
||||
+-------------+-----------------------------------------+
|
||||
| adminurl | http://controller:8786/v2/%(tenant_id)s |
|
||||
| id | 63ddffd27e8c4c62b4ffb228083325e6 |
|
||||
| internalurl | http://controller:8786/v2/%(tenant_id)s |
|
||||
| publicurl | http://controller:8786/v2/%(tenant_id)s |
|
||||
| region | regionOne |
|
||||
| service_id | 2840d1e7b033437f8776a7bd5045b28d |
|
||||
+-------------+-----------------------------------------+
|
||||
|
||||
.. note::
|
||||
Port ‘8786’ is the default port for Manila. It may be changed to any
|
||||
other port, but this change should also be made in the Manila configuration
|
||||
|
@ -5,13 +5,14 @@
|
||||
[composite:osapi_share]
|
||||
use = call:manila.api:root_app_factory
|
||||
/: apiversions
|
||||
/v1: openstack_share_api_v1
|
||||
/v1: openstack_share_api
|
||||
/v2: openstack_share_api
|
||||
|
||||
[composite:openstack_share_api_v1]
|
||||
[composite:openstack_share_api]
|
||||
use = call:manila.api.middleware.auth:pipeline_factory
|
||||
noauth = faultwrap ssl sizelimit noauth apiv1
|
||||
keystone = faultwrap ssl sizelimit authtoken keystonecontext apiv1
|
||||
keystone_nolimit = faultwrap ssl sizelimit authtoken keystonecontext apiv1
|
||||
noauth = faultwrap ssl sizelimit noauth api
|
||||
keystone = faultwrap ssl sizelimit authtoken keystonecontext api
|
||||
keystone_nolimit = faultwrap ssl sizelimit authtoken keystonecontext api
|
||||
|
||||
[filter:faultwrap]
|
||||
paste.filter_factory = manila.api.middleware.fault:FaultWrapper.factory
|
||||
@ -25,7 +26,7 @@ paste.filter_factory = oslo_middleware.sizelimit:RequestBodySizeLimiter.factory
|
||||
[filter:ssl]
|
||||
paste.filter_factory = oslo_middleware.ssl:SSLMiddleware.factory
|
||||
|
||||
[app:apiv1]
|
||||
[app:api]
|
||||
paste.app_factory = manila.api.v1.router:APIRouter.factory
|
||||
|
||||
[pipeline:apiversions]
|
||||
|
@ -167,15 +167,15 @@ class CGAdminController(AdminController):
|
||||
def _delete(self, context, resource, force=True):
|
||||
db.consistency_group_destroy(context.elevated(), resource['id'])
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.action('os-reset_status')
|
||||
@wsgi.response(202)
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
def cg_reset_status(self, req, id, body):
|
||||
super(CGAdminController, self)._reset_status(req, id, body)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.action('os-force_delete')
|
||||
@wsgi.response(202)
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
def cg_force_delete(self, req, id, body):
|
||||
super(CGAdminController, self)._force_delete(req, id, body)
|
||||
|
||||
@ -198,15 +198,15 @@ class CGSnapshotAdminController(AdminController):
|
||||
def _delete(self, context, resource, force=True):
|
||||
db.cgsnapshot_destroy(context.elevated(), resource['id'])
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.action('os-reset_status')
|
||||
@wsgi.response(202)
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
def cgsnapshot_reset_status(self, req, id, body):
|
||||
super(CGSnapshotAdminController, self)._reset_status(req, id, body)
|
||||
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.action('os-force_delete')
|
||||
@wsgi.response(202)
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
def cgsnapshot_force_delete(self, req, id, body):
|
||||
super(CGSnapshotAdminController, self)._force_delete(req, id, body)
|
||||
|
||||
|
@ -45,20 +45,20 @@ REST_API_VERSION_HISTORY = """
|
||||
REST API Version History:
|
||||
|
||||
* 1.0 - Initial version. Includes all V1 APIs and extensions in Kilo.
|
||||
* 1.1 - Versions API updated to reflect beginning of microversions epoch.
|
||||
* 1.2 - Share create() doesn't ignore availability_zone field of share.
|
||||
* 1.3 - Snapshots become optional feature.
|
||||
* 1.4 - Share instances admin API
|
||||
* 1.5 - Consistency Group support
|
||||
* 1.6 - Share Migration admin API
|
||||
* 2.0 - Versions API updated to reflect beginning of microversions epoch.
|
||||
* 2.1 - Share create() doesn't ignore availability_zone field of share.
|
||||
* 2.2 - Snapshots become optional feature.
|
||||
* 2.3 - Share instances admin API
|
||||
* 2.4 - Consistency Group support
|
||||
* 2.5 - Share Migration admin API
|
||||
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
# The default api version request is defined to be the
|
||||
# the minimum version of the API supported.
|
||||
_MIN_API_VERSION = "1.0"
|
||||
_MAX_API_VERSION = "1.6"
|
||||
_MIN_API_VERSION = "2.0"
|
||||
_MAX_API_VERSION = "2.5"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
|
||||
|
@ -8,15 +8,15 @@ user documentation.
|
||||
|
||||
1.0
|
||||
---
|
||||
|
||||
The 1.0 Manila API includes all v1 core APIs existing prior to
|
||||
the introduction of microversions.
|
||||
the introduction of microversions. The /v1 URL is used to call
|
||||
1.0 APIs, and microversions headers sent to this endpoint are
|
||||
ignored.
|
||||
|
||||
1.1
|
||||
2.0
|
||||
---
|
||||
|
||||
This is the initial version of the Manila API which supports
|
||||
microversions.
|
||||
microversions. The /v2 URL is used to call 2.x APIs.
|
||||
|
||||
A user can specify a header in the API request::
|
||||
|
||||
@ -24,33 +24,35 @@ user documentation.
|
||||
|
||||
where ``<version>`` is any valid api version for this API.
|
||||
|
||||
If no version is specified then the API will behave as version 1.0
|
||||
If no version is specified then the API will behave as if version 2.0
|
||||
was requested.
|
||||
|
||||
The only API change in version 1.1 is versions, i.e.
|
||||
GET http://localhost:8786/, which now returns the minimum and
|
||||
current microversion values.
|
||||
The only API change in version 2.0 is versions, i.e.
|
||||
GET http://localhost:8786/, which now returns information about
|
||||
both 1.0 and 2.x versions and their respective /v1 and /v2 endpoints.
|
||||
|
||||
1.2
|
||||
All other 2.0 APIs are functionally identical to version 1.0.
|
||||
|
||||
2.1
|
||||
---
|
||||
Share create() method doesn't ignore availability_zone field of provided
|
||||
share.
|
||||
|
||||
1.3
|
||||
2.2
|
||||
---
|
||||
Snapshots become optional and share payload now has
|
||||
boolean attr 'snapshot_support'.
|
||||
|
||||
1.4
|
||||
2.3
|
||||
---
|
||||
Share instances admin API and update of Admin Actions extension.
|
||||
|
||||
1.5
|
||||
2.4
|
||||
---
|
||||
Consistency groups support. /consistency-groups and /cgsnapshots are
|
||||
implemented. AdminActions 'os-force_delete and' 'os-reset_status' have been
|
||||
updated for both new resources.
|
||||
|
||||
1.6
|
||||
2.5
|
||||
---
|
||||
Share Migration admin API.
|
||||
|
@ -51,6 +51,8 @@ VER_METHOD_ATTR = 'versioned_methods'
|
||||
API_VERSION_REQUEST_HEADER = 'X-OpenStack-Manila-API-Version'
|
||||
EXPERIMENTAL_API_REQUEST_HEADER = 'X-OpenStack-Manila-API-Experimental'
|
||||
|
||||
V1_SCRIPT_NAME = '/v1'
|
||||
|
||||
|
||||
class Request(webob.Request):
|
||||
"""Add some OpenStack API-specific logic to the base webob.Request."""
|
||||
@ -207,30 +209,40 @@ class Request(webob.Request):
|
||||
return content_type
|
||||
|
||||
def set_api_version_request(self):
|
||||
"""Set API version request based on the request header information."""
|
||||
if API_VERSION_REQUEST_HEADER in self.headers:
|
||||
hdr_string = self.headers[API_VERSION_REQUEST_HEADER]
|
||||
# 'latest' is a special keyword which is equivalent to requesting
|
||||
# the maximum version of the API supported
|
||||
if hdr_string == 'latest':
|
||||
self.api_version_request = api_version.max_api_version()
|
||||
"""Set API version request based on the request header information.
|
||||
|
||||
Microversions starts with /v2, so if a client sends a /v1 URL, then
|
||||
ignore the headers and request 1.0 APIs.
|
||||
"""
|
||||
|
||||
if not self.script_name:
|
||||
self.api_version_request = api_version.APIVersionRequest()
|
||||
elif self.script_name == V1_SCRIPT_NAME:
|
||||
self.api_version_request = api_version.APIVersionRequest('1.0')
|
||||
else:
|
||||
if API_VERSION_REQUEST_HEADER in self.headers:
|
||||
hdr_string = self.headers[API_VERSION_REQUEST_HEADER]
|
||||
# 'latest' is a special keyword which is equivalent to
|
||||
# requesting the maximum version of the API supported
|
||||
if hdr_string == 'latest':
|
||||
self.api_version_request = api_version.max_api_version()
|
||||
else:
|
||||
self.api_version_request = api_version.APIVersionRequest(
|
||||
hdr_string)
|
||||
|
||||
# Check that the version requested is within the global
|
||||
# minimum/maximum of supported API versions
|
||||
if not self.api_version_request.matches(
|
||||
api_version.min_api_version(),
|
||||
api_version.max_api_version()):
|
||||
raise exception.InvalidGlobalAPIVersion(
|
||||
req_ver=self.api_version_request.get_string(),
|
||||
min_ver=api_version.min_api_version().get_string(),
|
||||
max_ver=api_version.max_api_version().get_string())
|
||||
|
||||
else:
|
||||
self.api_version_request = api_version.APIVersionRequest(
|
||||
hdr_string)
|
||||
|
||||
# Check that the version requested is within the global
|
||||
# minimum/maximum of supported API versions
|
||||
if not self.api_version_request.matches(
|
||||
api_version.min_api_version(),
|
||||
api_version.max_api_version()):
|
||||
raise exception.InvalidGlobalAPIVersion(
|
||||
req_ver=self.api_version_request.get_string(),
|
||||
min_ver=api_version.min_api_version().get_string(),
|
||||
max_ver=api_version.max_api_version().get_string())
|
||||
|
||||
else:
|
||||
self.api_version_request = api_version.APIVersionRequest(
|
||||
api_version.DEFAULT_API_VERSION)
|
||||
api_version.DEFAULT_API_VERSION)
|
||||
|
||||
# Check if experimental API was requested
|
||||
if EXPERIMENTAL_API_REQUEST_HEADER in self.headers:
|
||||
|
@ -41,7 +41,7 @@ class CGSnapshotController(wsgi.Controller):
|
||||
super(CGSnapshotController, self).__init__()
|
||||
self.cg_api = cg_api.API()
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def show(self, req, id):
|
||||
"""Return data about the given cgsnapshot."""
|
||||
context = req.environ['manila.context']
|
||||
@ -54,7 +54,7 @@ class CGSnapshotController(wsgi.Controller):
|
||||
|
||||
return self._view_builder.detail(req, cg)
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def delete(self, req, id):
|
||||
"""Delete a cgsnapshot."""
|
||||
context = req.environ['manila.context']
|
||||
@ -75,12 +75,12 @@ class CGSnapshotController(wsgi.Controller):
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def index(self, req):
|
||||
"""Returns a summary list of cgsnapshots."""
|
||||
return self._get_cgs(req, is_detail=False)
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def detail(self, req):
|
||||
"""Returns a detailed list of cgsnapshots."""
|
||||
return self._get_cgs(req, is_detail=True)
|
||||
@ -107,7 +107,7 @@ class CGSnapshotController(wsgi.Controller):
|
||||
snaps = self._view_builder.summary_list(req, limited_list)
|
||||
return snaps
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def update(self, req, id, body):
|
||||
"""Update a cgsnapshot."""
|
||||
context = req.environ['manila.context']
|
||||
@ -135,7 +135,7 @@ class CGSnapshotController(wsgi.Controller):
|
||||
cg = self.cg_api.update_cgsnapshot(context, cg, cg_data)
|
||||
return self._view_builder.detail(req, cg)
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.response(202)
|
||||
def create(self, req, body):
|
||||
"""Creates a new cgsnapshot."""
|
||||
@ -174,7 +174,7 @@ class CGSnapshotController(wsgi.Controller):
|
||||
return self._view_builder.detail(req, dict(six.iteritems(
|
||||
new_snapshot)))
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def members(self, req, id):
|
||||
"""Returns a list of cgsnapshot members."""
|
||||
context = req.environ['manila.context']
|
||||
|
@ -42,7 +42,7 @@ class CGController(wsgi.Controller):
|
||||
super(CGController, self).__init__()
|
||||
self.cg_api = cg_api.API()
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def show(self, req, id):
|
||||
"""Return data about the given CG."""
|
||||
context = req.environ['manila.context']
|
||||
@ -55,7 +55,7 @@ class CGController(wsgi.Controller):
|
||||
|
||||
return self._view_builder.detail(req, cg)
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def delete(self, req, id):
|
||||
"""Delete a CG."""
|
||||
context = req.environ['manila.context']
|
||||
@ -76,12 +76,12 @@ class CGController(wsgi.Controller):
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def index(self, req):
|
||||
"""Returns a summary list of shares."""
|
||||
return self._get_cgs(req, is_detail=False)
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def detail(self, req):
|
||||
"""Returns a detailed list of shares."""
|
||||
return self._get_cgs(req, is_detail=True)
|
||||
@ -108,7 +108,7 @@ class CGController(wsgi.Controller):
|
||||
cgs = self._view_builder.summary_list(req, limited_list)
|
||||
return cgs
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
def update(self, req, id, body):
|
||||
"""Update a share."""
|
||||
context = req.environ['manila.context']
|
||||
@ -136,7 +136,7 @@ class CGController(wsgi.Controller):
|
||||
cg = self.cg_api.update(context, cg, cg_data)
|
||||
return self._view_builder.detail(req, cg)
|
||||
|
||||
@wsgi.Controller.api_version('1.5', experimental=True)
|
||||
@wsgi.Controller.api_version('2.4', experimental=True)
|
||||
@wsgi.response(202)
|
||||
def create(self, req, body):
|
||||
"""Creates a new share."""
|
||||
|
@ -39,7 +39,7 @@ class ShareInstancesController(wsgi.Controller):
|
||||
except exception.PolicyNotAuthorized:
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
@wsgi.Controller.api_version("1.4")
|
||||
@wsgi.Controller.api_version("2.3")
|
||||
def index(self, req):
|
||||
context = req.environ['manila.context']
|
||||
self._authorize(context, 'index')
|
||||
@ -47,7 +47,7 @@ class ShareInstancesController(wsgi.Controller):
|
||||
instances = db.share_instances_get_all(context)
|
||||
return self._view_builder.detail_list(req, instances)
|
||||
|
||||
@wsgi.Controller.api_version("1.4")
|
||||
@wsgi.Controller.api_version("2.3")
|
||||
def show(self, req, id):
|
||||
context = req.environ['manila.context']
|
||||
self._authorize(context, 'show')
|
||||
@ -59,7 +59,7 @@ class ShareInstancesController(wsgi.Controller):
|
||||
|
||||
return self._view_builder.detail(req, instance)
|
||||
|
||||
@wsgi.Controller.api_version("1.4")
|
||||
@wsgi.Controller.api_version("2.3")
|
||||
def get_share_instances(self, req, share_id):
|
||||
context = req.environ['manila.context']
|
||||
self._authorize(context, 'index')
|
||||
|
@ -89,7 +89,7 @@ class ShareController(wsgi.Controller):
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@wsgi.Controller.api_version("1.6", None, True)
|
||||
@wsgi.Controller.api_version("2.5", None, True)
|
||||
@wsgi.action("os-migrate_share")
|
||||
def migrate_share(self, req, id, body):
|
||||
"""Migrate a share to the specified host."""
|
||||
@ -205,11 +205,11 @@ class ShareController(wsgi.Controller):
|
||||
share.update(update_dict)
|
||||
return self._view_builder.detail(req, share)
|
||||
|
||||
@wsgi.Controller.api_version("1.5")
|
||||
@wsgi.Controller.api_version("2.4")
|
||||
def create(self, req, body):
|
||||
return self._create(req, body)
|
||||
|
||||
@wsgi.Controller.api_version("1.0", "1.4") # noqa
|
||||
@wsgi.Controller.api_version("1.0", "2.3") # noqa
|
||||
def create(self, req, body): # pylint: disable=E0102
|
||||
# Remove consistency group attributes
|
||||
share = body.get('share', {})
|
||||
|
@ -26,27 +26,35 @@ from manila.api.views import versions as views_versions
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
_LINKS = [{
|
||||
'rel': 'describedby',
|
||||
'type': 'text/html',
|
||||
'href': 'http://docs.openstack.org/',
|
||||
}]
|
||||
|
||||
_MEDIA_TYPES = [{
|
||||
'base': 'application/json',
|
||||
'type': 'application/vnd.openstack.share+json;version=1',
|
||||
}]
|
||||
|
||||
_KNOWN_VERSIONS = {
|
||||
'v1.0': {
|
||||
'id': 'v1.0',
|
||||
'status': 'SUPPORTED',
|
||||
'version': '',
|
||||
'min_version': '',
|
||||
'updated': '2015-08-27T11:33:21Z',
|
||||
'links': _LINKS,
|
||||
'media-types': _MEDIA_TYPES,
|
||||
},
|
||||
'v2.0': {
|
||||
'id': 'v2.0',
|
||||
'status': 'CURRENT',
|
||||
'version': api_version_request._MAX_API_VERSION,
|
||||
'min_version': api_version_request._MIN_API_VERSION,
|
||||
'updated': '2015-07-30T11:33:21Z',
|
||||
'links': [
|
||||
{
|
||||
'rel': 'describedby',
|
||||
'type': 'text/html',
|
||||
'href': 'http://docs.openstack.org/',
|
||||
},
|
||||
],
|
||||
'media-types': [
|
||||
{
|
||||
'base': 'application/json',
|
||||
'type': 'application/vnd.openstack.share+json;version=1',
|
||||
}
|
||||
],
|
||||
'updated': '2015-08-27T11:33:21Z',
|
||||
'links': _LINKS,
|
||||
'media-types': _MEDIA_TYPES,
|
||||
},
|
||||
}
|
||||
|
||||
@ -60,7 +68,7 @@ class VersionsRouter(openstack.APIRouter):
|
||||
self.resources['versions'] = create_resource()
|
||||
mapper.connect('versions', '/',
|
||||
controller=self.resources['versions'],
|
||||
action='index')
|
||||
action='all')
|
||||
mapper.redirect('', '/')
|
||||
|
||||
|
||||
@ -71,16 +79,27 @@ class VersionsController(wsgi.Controller):
|
||||
|
||||
@wsgi.Controller.api_version('1.0', '1.0')
|
||||
def index(self, req):
|
||||
"""Return all versions."""
|
||||
"""Return versions supported prior to the microversions epoch."""
|
||||
builder = views_versions.get_view_builder(req)
|
||||
known_versions = copy.deepcopy(_KNOWN_VERSIONS)
|
||||
known_versions['v1.0'].pop('min_version')
|
||||
known_versions['v1.0'].pop('version')
|
||||
known_versions.pop('v2.0')
|
||||
return builder.build_versions(known_versions)
|
||||
|
||||
@wsgi.Controller.api_version('1.1') # noqa
|
||||
@wsgi.Controller.api_version('2.0') # noqa
|
||||
def index(self, req): # pylint: disable=E0102
|
||||
"""Return all versions."""
|
||||
"""Return versions supported after the start of microversions."""
|
||||
builder = views_versions.get_view_builder(req)
|
||||
known_versions = copy.deepcopy(_KNOWN_VERSIONS)
|
||||
known_versions.pop('v1.0')
|
||||
return builder.build_versions(known_versions)
|
||||
|
||||
# NOTE (cknight): Calling the versions API without
|
||||
# /v1 or /v2 in the URL will lead to this unversioned
|
||||
# method, which should always return info about all
|
||||
# available versions.
|
||||
@wsgi.response(300)
|
||||
def all(self, req):
|
||||
"""Return all known versions."""
|
||||
builder = views_versions.get_view_builder(req)
|
||||
known_versions = copy.deepcopy(_KNOWN_VERSIONS)
|
||||
return builder.build_versions(known_versions)
|
||||
|
@ -85,7 +85,7 @@ class ViewBuilder(common.ViewBuilder):
|
||||
share_dict['share_server_id'] = share.get('share_server_id')
|
||||
return {'share': share_dict}
|
||||
|
||||
@common.ViewBuilder.versioned_method("1.5")
|
||||
@common.ViewBuilder.versioned_method("2.4")
|
||||
def add_consistency_group_fields(self, share_dict, share):
|
||||
share_dict['consistency_group_id'] = share.get(
|
||||
'consistency_group_id')
|
||||
|
@ -24,6 +24,9 @@ def get_view_builder(req):
|
||||
return ViewBuilder(req.application_url)
|
||||
|
||||
|
||||
_URL_SUFFIX = {'v1.0': 'v1', 'v2.0': 'v2'}
|
||||
|
||||
|
||||
class ViewBuilder(object):
|
||||
def __init__(self, base_url):
|
||||
"""Initialize ViewBuilder.
|
||||
@ -45,7 +48,9 @@ class ViewBuilder(object):
|
||||
def _build_links(self, version_data):
|
||||
"""Generate a container of links that refer to the provided version."""
|
||||
links = copy.deepcopy(version_data.get('links', {}))
|
||||
links.append({'rel': 'self', 'href': self._generate_href()})
|
||||
version = _URL_SUFFIX.get(version_data['id'])
|
||||
links.append({'rel': 'self',
|
||||
'href': self._generate_href(version=version)})
|
||||
return links
|
||||
|
||||
def _generate_href(self, version='v1', path=None):
|
||||
|
@ -36,7 +36,7 @@ def app():
|
||||
# no auth, just let environ['manila.context'] pass through
|
||||
api = fakes.router.APIRouter()
|
||||
mapper = fakes.urlmap.URLMap()
|
||||
mapper['/v1'] = api
|
||||
mapper['/v2'] = api
|
||||
return mapper
|
||||
|
||||
|
||||
@ -76,7 +76,7 @@ class AdminActionsTest(test.TestCase):
|
||||
share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
|
||||
size='1',
|
||||
override_defaults=True)
|
||||
req = webob.Request.blank('/v1/fake/shares/%s/action' % share['id'])
|
||||
req = webob.Request.blank('/v2/fake/shares/%s/action' % share['id'])
|
||||
return share, req
|
||||
|
||||
def _setup_snapshot_data(self, snapshot=None):
|
||||
@ -84,7 +84,7 @@ class AdminActionsTest(test.TestCase):
|
||||
share = db_utils.create_share()
|
||||
snapshot = db_utils.create_snapshot(
|
||||
status=constants.STATUS_AVAILABLE, share_id=share['id'])
|
||||
req = webob.Request.blank('/v1/fake/snapshots/%s/action' %
|
||||
req = webob.Request.blank('/v2/fake/snapshots/%s/action' %
|
||||
snapshot['id'])
|
||||
return snapshot, req
|
||||
|
||||
@ -93,16 +93,16 @@ class AdminActionsTest(test.TestCase):
|
||||
instance = db_utils.create_share(status=constants.STATUS_AVAILABLE,
|
||||
size='1').instance
|
||||
req = webob.Request.blank(
|
||||
'/v1/fake/share_instances/%s/action' % instance['id'])
|
||||
'/v2/fake/share_instances/%s/action' % instance['id'])
|
||||
return instance, req
|
||||
|
||||
def _setup_cg_data(self, cg=None):
|
||||
if cg is None:
|
||||
cg = db_utils.create_consistency_group(
|
||||
status=constants.STATUS_AVAILABLE)
|
||||
req = webob.Request.blank('/v1/fake/consistency-groups/%s/action' %
|
||||
req = webob.Request.blank('/v2/fake/consistency-groups/%s/action' %
|
||||
cg['id'])
|
||||
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = '1.5'
|
||||
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = '2.4'
|
||||
req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True'
|
||||
|
||||
return cg, req
|
||||
@ -111,9 +111,9 @@ class AdminActionsTest(test.TestCase):
|
||||
if cgsnapshot is None:
|
||||
cgsnapshot = db_utils.create_cgsnapshot(
|
||||
'fake_id', status=constants.STATUS_AVAILABLE)
|
||||
req = webob.Request.blank('/v1/fake/cgsnapshots/%s/action' %
|
||||
req = webob.Request.blank('/v2/fake/cgsnapshots/%s/action' %
|
||||
cgsnapshot['id'])
|
||||
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = '1.5'
|
||||
req.headers[wsgi.API_VERSION_REQUEST_HEADER] = '2.4'
|
||||
req.headers[wsgi.EXPERIMENTAL_API_REQUEST_HEADER] = 'True'
|
||||
return cgsnapshot, req
|
||||
|
||||
|
@ -36,94 +36,81 @@ class VersionsControllerTestCase(test.TestCase):
|
||||
super(VersionsControllerTestCase, self).setUp()
|
||||
self.wsgi_apps = (versions.VersionsRouter(), router.APIRouter())
|
||||
|
||||
@ddt.data(('', 302), ('/', 200))
|
||||
@ddt.unpack
|
||||
def test_versions_return_codes(self, request_path, return_code):
|
||||
req = fakes.HTTPRequest.blank(request_path)
|
||||
@ddt.data('1.0', '1.1', '2.0', '3.0')
|
||||
def test_versions_root(self, version):
|
||||
req = fakes.HTTPRequest.blank('/', base_url='http://localhost')
|
||||
req.method = 'GET'
|
||||
req.content_type = 'application/json'
|
||||
req.headers = {version_header_name: version}
|
||||
|
||||
for app in self.wsgi_apps:
|
||||
response = req.get_response(app)
|
||||
self.assertEqual(return_code, response.status_int)
|
||||
response = req.get_response(versions.VersionsRouter())
|
||||
self.assertEqual(300, response.status_int)
|
||||
body = jsonutils.loads(response.body)
|
||||
version_list = body['versions']
|
||||
|
||||
@ddt.data(
|
||||
('http://localhost/', True),
|
||||
(None, True),
|
||||
('http://localhost/', False),
|
||||
(None, False),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_versions_index_v10(self, base_url, include_header):
|
||||
req = fakes.HTTPRequest.blank('/', base_url=base_url)
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v1.0', 'v2.0'}, set(ids))
|
||||
self.assertNotIn(version_header_name, response.headers)
|
||||
self.assertNotIn('Vary', response.headers)
|
||||
|
||||
v1 = [v for v in version_list if v['id'] == 'v1.0'][0]
|
||||
self.assertEqual('', v1.get('min_version'))
|
||||
self.assertEqual('', v1.get('version'))
|
||||
|
||||
v2 = [v for v in version_list if v['id'] == 'v2.0'][0]
|
||||
self.assertEqual(api_version_request._MIN_API_VERSION,
|
||||
v2.get('min_version'))
|
||||
self.assertEqual(api_version_request._MAX_API_VERSION,
|
||||
v2.get('version'))
|
||||
|
||||
@ddt.data('1.0',
|
||||
'1.1',
|
||||
api_version_request._MIN_API_VERSION,
|
||||
api_version_request._MAX_API_VERSION)
|
||||
def test_versions_v1(self, version):
|
||||
req = fakes.HTTPRequest.blank('/', base_url='http://localhost/v1')
|
||||
req.method = 'GET'
|
||||
req.content_type = 'application/json'
|
||||
if include_header:
|
||||
req.headers = {version_header_name: '1.0'}
|
||||
req.headers = {version_header_name: version}
|
||||
|
||||
for app in self.wsgi_apps:
|
||||
response = req.get_response(app)
|
||||
body = jsonutils.loads(response.body)
|
||||
version_list = body['versions']
|
||||
response = req.get_response(router.APIRouter())
|
||||
self.assertEqual(200, response.status_int)
|
||||
body = jsonutils.loads(response.body)
|
||||
version_list = body['versions']
|
||||
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v1.0'}, set(ids))
|
||||
self.assertEqual('1.0', response.headers[version_header_name])
|
||||
self.assertEqual(version_header_name, response.headers['Vary'])
|
||||
self.assertIsNone(version_list[0].get('min_version'))
|
||||
self.assertIsNone(version_list[0].get('version'))
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v1.0'}, set(ids))
|
||||
self.assertEqual('1.0', response.headers[version_header_name])
|
||||
self.assertEqual(version_header_name, response.headers['Vary'])
|
||||
self.assertEqual('', version_list[0].get('min_version'))
|
||||
self.assertEqual('', version_list[0].get('version'))
|
||||
|
||||
@ddt.data(
|
||||
('http://localhost/', '1.1'),
|
||||
(None, '1.1'),
|
||||
('http://localhost/', 'latest'),
|
||||
(None, 'latest')
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_versions_index_v11(self, base_url, req_version):
|
||||
req = fakes.HTTPRequest.blank('/', base_url=base_url)
|
||||
@ddt.data(api_version_request._MIN_API_VERSION,
|
||||
api_version_request._MAX_API_VERSION)
|
||||
def test_versions_v2(self, version):
|
||||
req = fakes.HTTPRequest.blank('/', base_url='http://localhost/v2')
|
||||
req.method = 'GET'
|
||||
req.content_type = 'application/json'
|
||||
req.headers = {version_header_name: req_version}
|
||||
req.headers = {version_header_name: version}
|
||||
|
||||
for app in self.wsgi_apps:
|
||||
response = req.get_response(app)
|
||||
body = jsonutils.loads(response.body)
|
||||
version_list = body['versions']
|
||||
response = req.get_response(router.APIRouter())
|
||||
self.assertEqual(200, response.status_int)
|
||||
body = jsonutils.loads(response.body)
|
||||
version_list = body['versions']
|
||||
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v1.0'}, set(ids))
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v2.0'}, set(ids))
|
||||
self.assertEqual(version, response.headers[version_header_name])
|
||||
self.assertEqual(version_header_name, response.headers['Vary'])
|
||||
|
||||
if req_version == 'latest':
|
||||
self.assertEqual(api_version_request._MAX_API_VERSION,
|
||||
response.headers[version_header_name])
|
||||
else:
|
||||
self.assertEqual(req_version,
|
||||
response.headers[version_header_name])
|
||||
v2 = [v for v in version_list if v['id'] == 'v2.0'][0]
|
||||
self.assertEqual(api_version_request._MIN_API_VERSION,
|
||||
v2.get('min_version'))
|
||||
self.assertEqual(api_version_request._MAX_API_VERSION,
|
||||
v2.get('version'))
|
||||
|
||||
self.assertEqual(version_header_name, response.headers['Vary'])
|
||||
self.assertEqual(api_version_request._MIN_API_VERSION,
|
||||
version_list[0].get('min_version'))
|
||||
self.assertEqual(api_version_request._MAX_API_VERSION,
|
||||
version_list[0].get('version'))
|
||||
|
||||
@ddt.data('http://localhost/', None)
|
||||
def test_versions_index_v2(self, base_url):
|
||||
req = fakes.HTTPRequest.blank('/', base_url=base_url)
|
||||
req.method = 'GET'
|
||||
req.content_type = 'application/json'
|
||||
req.headers = {version_header_name: '2.0'}
|
||||
|
||||
for app in self.wsgi_apps:
|
||||
response = req.get_response(app)
|
||||
|
||||
self.assertEqual(406, response.status_int)
|
||||
self.assertEqual('2.0', response.headers[version_header_name])
|
||||
self.assertEqual(version_header_name, response.headers['Vary'])
|
||||
|
||||
@ddt.data('http://localhost/', None)
|
||||
def test_versions_index_invalid_version_request(self, base_url):
|
||||
req = fakes.HTTPRequest.blank('/', base_url=base_url)
|
||||
def test_versions_version_invalid(self):
|
||||
req = fakes.HTTPRequest.blank('/', base_url='http://localhost/v2')
|
||||
req.method = 'GET'
|
||||
req.content_type = 'application/json'
|
||||
req.headers = {version_header_name: '2.0.1'}
|
||||
@ -132,8 +119,6 @@ class VersionsControllerTestCase(test.TestCase):
|
||||
response = req.get_response(app)
|
||||
|
||||
self.assertEqual(400, response.status_int)
|
||||
self.assertEqual('1.0', response.headers[version_header_name])
|
||||
self.assertEqual(version_header_name, response.headers['Vary'])
|
||||
|
||||
def test_versions_version_not_found(self):
|
||||
api_version_request_3_0 = api_version_request.APIVersionRequest('3.0')
|
||||
@ -142,45 +127,59 @@ class VersionsControllerTestCase(test.TestCase):
|
||||
mock.Mock(return_value=api_version_request_3_0))
|
||||
|
||||
class Controller(wsgi.Controller):
|
||||
@wsgi.Controller.api_version('1.0', '1.0')
|
||||
@wsgi.Controller.api_version('2.0', '2.0')
|
||||
def index(self, req):
|
||||
return 'off'
|
||||
|
||||
req = fakes.HTTPRequest.blank('/tests')
|
||||
req.headers = {version_header_name: '2.0'}
|
||||
req = fakes.HTTPRequest.blank('/tests', base_url='http://localhost/v2')
|
||||
req.headers = {version_header_name: '2.5'}
|
||||
app = fakes.TestRouter(Controller())
|
||||
|
||||
response = req.get_response(app)
|
||||
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
def test_versions_version_not_acceptable(self):
|
||||
req = fakes.HTTPRequest.blank('/', base_url='http://localhost/v2')
|
||||
req.method = 'GET'
|
||||
req.content_type = 'application/json'
|
||||
req.headers = {version_header_name: '3.0'}
|
||||
|
||||
response = req.get_response(router.APIRouter())
|
||||
|
||||
self.assertEqual(406, response.status_int)
|
||||
self.assertEqual('3.0', response.headers[version_header_name])
|
||||
self.assertEqual(version_header_name, response.headers['Vary'])
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ExperimentalAPITestCase(test.TestCase):
|
||||
|
||||
class Controller(wsgi.Controller):
|
||||
@wsgi.Controller.api_version('1.0', '1.0')
|
||||
@wsgi.Controller.api_version('2.0', '2.0')
|
||||
def index(self, req):
|
||||
return {'fake_key': 'fake_value'}
|
||||
|
||||
@wsgi.Controller.api_version('1.1', '1.1', experimental=True) # noqa
|
||||
@wsgi.Controller.api_version('2.1', '2.1', experimental=True) # noqa
|
||||
def index(self, req): # pylint: disable=E0102
|
||||
return {'fake_key': 'fake_value'}
|
||||
|
||||
def setUp(self):
|
||||
super(ExperimentalAPITestCase, self).setUp()
|
||||
self.app = fakes.TestRouter(ExperimentalAPITestCase.Controller())
|
||||
self.req = fakes.HTTPRequest.blank('/tests',
|
||||
base_url='http://localhost/v2')
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_stable_api_always_called(self, experimental):
|
||||
|
||||
req = fakes.HTTPRequest.blank('/tests')
|
||||
req.headers = {version_header_name: '1.0'}
|
||||
self.req.headers = {version_header_name: '2.0'}
|
||||
if experimental:
|
||||
req.headers[experimental_header_name] = experimental
|
||||
response = req.get_response(self.app)
|
||||
self.req.headers[experimental_header_name] = experimental
|
||||
response = self.req.get_response(self.app)
|
||||
|
||||
self.assertEqual(200, response.status_int)
|
||||
self.assertEqual('1.0', response.headers[version_header_name])
|
||||
self.assertEqual('2.0', response.headers[version_header_name])
|
||||
|
||||
if experimental:
|
||||
self.assertEqual(experimental,
|
||||
@ -190,22 +189,20 @@ class ExperimentalAPITestCase(test.TestCase):
|
||||
|
||||
def test_experimental_api_called_when_requested(self):
|
||||
|
||||
req = fakes.HTTPRequest.blank('/tests')
|
||||
req.headers = {
|
||||
version_header_name: '1.1',
|
||||
self.req.headers = {
|
||||
version_header_name: '2.1',
|
||||
experimental_header_name: 'True',
|
||||
}
|
||||
response = req.get_response(self.app)
|
||||
response = self.req.get_response(self.app)
|
||||
|
||||
self.assertEqual(200, response.status_int)
|
||||
self.assertEqual('1.1', response.headers[version_header_name])
|
||||
self.assertEqual('2.1', response.headers[version_header_name])
|
||||
self.assertTrue(response.headers.get(experimental_header_name))
|
||||
|
||||
def test_experimental_api_not_called_when_not_requested(self):
|
||||
|
||||
req = fakes.HTTPRequest.blank('/tests')
|
||||
req.headers = {version_header_name: '1.1'}
|
||||
response = req.get_response(self.app)
|
||||
self.req.headers = {version_header_name: '2.1'}
|
||||
response = self.req.get_response(self.app)
|
||||
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertFalse(experimental_header_name in response.headers)
|
||||
@ -217,12 +214,11 @@ class ExperimentalAPITestCase(test.TestCase):
|
||||
'max_api_version',
|
||||
mock.Mock(return_value=api_version_request_3_0))
|
||||
|
||||
req = fakes.HTTPRequest.blank('/tests')
|
||||
req.headers = {
|
||||
version_header_name: '1.2',
|
||||
self.req.headers = {
|
||||
version_header_name: '2.2',
|
||||
experimental_header_name: 'True',
|
||||
}
|
||||
response = req.get_response(self.app)
|
||||
response = self.req.get_response(self.app)
|
||||
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertTrue(response.headers.get(experimental_header_name))
|
@ -36,7 +36,7 @@ class CGSnapshotApiTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(CGSnapshotApiTest, self).setUp()
|
||||
self.controller = cgs.CGSnapshotController()
|
||||
self.api_version = '1.5'
|
||||
self.api_version = '2.4'
|
||||
self.request = fakes.HTTPRequest.blank('/consistency-groups',
|
||||
version=self.api_version,
|
||||
experimental=True)
|
||||
|
@ -40,7 +40,7 @@ class CGApiTest(test.TestCase):
|
||||
super(CGApiTest, self).setUp()
|
||||
self.controller = cgs.CGController()
|
||||
self.fake_share_type = {'id': six.text_type(uuid.uuid4())}
|
||||
self.api_version = '1.5'
|
||||
self.api_version = '2.4'
|
||||
self.request = fakes.HTTPRequest.blank('/consistency-groups',
|
||||
version=self.api_version,
|
||||
experimental=True)
|
||||
|
@ -34,7 +34,7 @@ class ShareInstancesApiTest(test.TestCase):
|
||||
def _get_request(self, uri, context=None):
|
||||
if context is None:
|
||||
context = self.context
|
||||
req = fakes.HTTPRequest.blank('/shares', version="1.4")
|
||||
req = fakes.HTTPRequest.blank('/shares', version="2.3")
|
||||
req.environ['manila.context'] = context
|
||||
return req
|
||||
|
||||
|
@ -142,7 +142,7 @@ class ShareApiTest(test.TestCase):
|
||||
self.mock_object(share_api.API, 'create', self.create_mock)
|
||||
|
||||
body = {"share": copy.deepcopy(self.share)}
|
||||
req = fakes.HTTPRequest.blank('/shares', version="1.5")
|
||||
req = fakes.HTTPRequest.blank('/shares', version="2.4")
|
||||
res_dict = self.controller.create(req, body)
|
||||
|
||||
expected = self._get_expected_share_detailed_response(self.share)
|
||||
@ -213,7 +213,7 @@ class ShareApiTest(test.TestCase):
|
||||
use_admin_context=True)
|
||||
req.method = 'POST'
|
||||
req.headers['content-type'] = 'application/json'
|
||||
req.api_version_request = api_version.APIVersionRequest('1.6')
|
||||
req.api_version_request = api_version.APIVersionRequest('2.5')
|
||||
req.api_version_request.experimental = True
|
||||
body = {'os-migrate_share': {'host': 'fake_host'}}
|
||||
self.mock_object(share_api.API, 'migrate_share')
|
||||
@ -224,7 +224,7 @@ class ShareApiTest(test.TestCase):
|
||||
use_admin_context=True)
|
||||
req.method = 'POST'
|
||||
req.headers['content-type'] = 'application/json'
|
||||
req.api_version_request = api_version.APIVersionRequest('1.6')
|
||||
req.api_version_request = api_version.APIVersionRequest('2.5')
|
||||
req.api_version_request.experimental = True
|
||||
body = {'os-migrate_share': {'host': 'fake_host'}}
|
||||
self.mock_object(share_api.API, 'migrate_share')
|
||||
@ -240,7 +240,7 @@ class ShareApiTest(test.TestCase):
|
||||
use_admin_context=True)
|
||||
req.method = 'POST'
|
||||
req.headers['content-type'] = 'application/json'
|
||||
req.api_version_request = api_version.APIVersionRequest('1.6')
|
||||
req.api_version_request = api_version.APIVersionRequest('2.5')
|
||||
req.api_version_request.experimental = True
|
||||
body = {'os-migrate_share': {}}
|
||||
self.mock_object(share_api.API, 'migrate_share')
|
||||
@ -254,7 +254,7 @@ class ShareApiTest(test.TestCase):
|
||||
use_admin_context=True)
|
||||
req.method = 'POST'
|
||||
req.headers['content-type'] = 'application/json'
|
||||
req.api_version_request = api_version.APIVersionRequest('1.6')
|
||||
req.api_version_request = api_version.APIVersionRequest('2.5')
|
||||
req.api_version_request.experimental = True
|
||||
body = {'os-migrate_share': {'host': 'fake_host',
|
||||
'force_host_copy': 'fake'}}
|
||||
@ -418,7 +418,7 @@ class ShareApiTest(test.TestCase):
|
||||
self.assertEqual(expected, res_dict)
|
||||
|
||||
def test_share_show_with_consistency_group(self):
|
||||
req = fakes.HTTPRequest.blank('/shares/1', version='1.5')
|
||||
req = fakes.HTTPRequest.blank('/shares/1', version='2.4')
|
||||
res_dict = self.controller.show(req, '1')
|
||||
expected = self._get_expected_share_detailed_response()
|
||||
expected['share']['consistency_group_id'] = None
|
||||
@ -489,7 +489,7 @@ class ShareApiTest(test.TestCase):
|
||||
shr = self.share
|
||||
body = {"share": shr}
|
||||
|
||||
req = fakes.HTTPRequest.blank('/share/1', version="1.5")
|
||||
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"])
|
||||
@ -732,7 +732,7 @@ class ShareApiTest(test.TestCase):
|
||||
stubs.stub_share_get_all_by_project)
|
||||
env = {'QUERY_STRING': 'name=Share+Test+Name'}
|
||||
req = fakes.HTTPRequest.blank('/shares/detail', environ=env,
|
||||
version="1.5")
|
||||
version="2.4")
|
||||
res_dict = self.controller.detail(req)
|
||||
expected = {
|
||||
'shares': [
|
||||
|
@ -17,12 +17,16 @@ from tempest import clients
|
||||
from tempest.common import cred_provider
|
||||
|
||||
from manila_tempest_tests.services.share.json import shares_client
|
||||
from manila_tempest_tests.services.share.v2.json import shares_client \
|
||||
as shares_v2_client
|
||||
|
||||
|
||||
class Manager(clients.Manager):
|
||||
def __init__(self, credentials=None, service=None):
|
||||
super(Manager, self).__init__(credentials, service)
|
||||
self.shares_client = shares_client.SharesClient(self.auth_provider)
|
||||
self.shares_v2_client = shares_v2_client.SharesV2Client(
|
||||
self.auth_provider)
|
||||
|
||||
|
||||
class AltManager(Manager):
|
||||
|
@ -32,11 +32,11 @@ share_group = cfg.OptGroup(name="share", title="Share Service Options")
|
||||
|
||||
ShareGroup = [
|
||||
cfg.StrOpt("min_api_microversion",
|
||||
default="1.0",
|
||||
default="2.0",
|
||||
help="The minimum api microversion is configured to be the "
|
||||
"value of the minimum microversion supported by Manila."),
|
||||
cfg.StrOpt("max_api_microversion",
|
||||
default="1.6",
|
||||
default="2.5",
|
||||
help="The maximum api microversion is configured to be the "
|
||||
"value of the latest microversion supported by Manila."),
|
||||
cfg.StrOpt("region",
|
||||
|
@ -26,13 +26,6 @@ from tempest_lib import exceptions
|
||||
from manila_tempest_tests import share_exceptions
|
||||
|
||||
CONF = config.CONF
|
||||
LATEST_MICRO_API = {
|
||||
'X-OpenStack-Manila-API-Version': CONF.share.max_api_microversion,
|
||||
}
|
||||
EXPERIMENTAL = {
|
||||
'X-OpenStack-Manila-API-Experimental': 'True',
|
||||
'X-OpenStack-Manila-API-Version': CONF.share.max_api_microversion,
|
||||
}
|
||||
|
||||
|
||||
class SharesClient(rest_client.RestClient):
|
||||
@ -53,48 +46,11 @@ class SharesClient(rest_client.RestClient):
|
||||
self.share_network_id = CONF.share.share_network_id
|
||||
self.build_interval = CONF.share.build_interval
|
||||
self.build_timeout = CONF.share.build_timeout
|
||||
self.API_MICROVERSIONS_HEADER = 'x-openstack-manila-api-version'
|
||||
self.API_MICROVERSIONS_EXPERIMENTAL_HEADER = (
|
||||
'x-openstack-manila-api-experimental')
|
||||
|
||||
def _get_version_dict(self, version):
|
||||
return {self.API_MICROVERSIONS_HEADER: version}
|
||||
|
||||
def migrate_share(self, share_id, host):
|
||||
post_body = {
|
||||
'os-migrate_share': {
|
||||
'host': host,
|
||||
}
|
||||
}
|
||||
body = json.dumps(post_body)
|
||||
headers = self._get_version_dict('1.6')
|
||||
headers[self.API_MICROVERSIONS_EXPERIMENTAL_HEADER] = 'true'
|
||||
return self.post('shares/%s/action' % share_id, body,
|
||||
headers=headers, extra_headers=True)
|
||||
|
||||
def send_microversion_request(self, version=None):
|
||||
"""Prepare and send the HTTP GET Request to the base URL.
|
||||
|
||||
Extracts the base URL from the shares_client endpoint and makes a GET
|
||||
request with the microversions request header.
|
||||
"""
|
||||
|
||||
headers = self.get_headers()
|
||||
url, headers, body = self.auth_provider.auth_request(
|
||||
'GET', 'shares', headers, None, self.filters)
|
||||
url = '/'.join(url.split('/')[:3]) + '/'
|
||||
if version:
|
||||
headers[self.API_MICROVERSIONS_HEADER] = version
|
||||
resp, resp_body = self.raw_request(url, 'GET', headers=headers)
|
||||
self.response_checker('GET', resp, resp_body)
|
||||
resp_body = json.loads(resp_body)
|
||||
return resp, resp_body
|
||||
|
||||
def create_share(self, share_protocol=None, size=1,
|
||||
name=None, snapshot_id=None, description=None,
|
||||
metadata=None, share_network_id=None,
|
||||
share_type_id=None, is_public=False,
|
||||
consistency_group_id=None):
|
||||
share_type_id=None, is_public=False):
|
||||
metadata = metadata or {}
|
||||
if name is None:
|
||||
name = data_utils.rand_name("tempest-created-share")
|
||||
@ -119,18 +75,13 @@ class SharesClient(rest_client.RestClient):
|
||||
post_body["share"]["share_network_id"] = share_network_id
|
||||
if share_type_id:
|
||||
post_body["share"]["share_type"] = share_type_id
|
||||
if consistency_group_id:
|
||||
post_body["share"]["consistency_group_id"] = consistency_group_id
|
||||
body = json.dumps(post_body)
|
||||
resp, body = self.post("shares", body, headers=LATEST_MICRO_API,
|
||||
extra_headers=True)
|
||||
resp, body = self.post("shares", body)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def delete_share(self, share_id, params=None):
|
||||
uri = "shares/%s" % share_id
|
||||
uri += '?%s' % (urllib.urlencode(params) if params else '')
|
||||
resp, body = self.delete(uri)
|
||||
def delete_share(self, share_id):
|
||||
resp, body = self.delete("shares/%s" % share_id)
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
@ -161,8 +112,7 @@ class SharesClient(rest_client.RestClient):
|
||||
"""Get list of shares w/o filters."""
|
||||
uri = 'shares/detail' if detailed else 'shares'
|
||||
uri += '?%s' % urllib.urlencode(params) if params else ''
|
||||
resp, body = self.get(uri, headers=LATEST_MICRO_API,
|
||||
extra_headers=True)
|
||||
resp, body = self.get(uri)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
@ -171,29 +121,7 @@ class SharesClient(rest_client.RestClient):
|
||||
return self.list_shares(detailed=True, params=params)
|
||||
|
||||
def get_share(self, share_id):
|
||||
resp, body = self.get("shares/%s" % share_id, headers=LATEST_MICRO_API,
|
||||
extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def get_instances_of_share(self, share_id):
|
||||
resp, body = self.get("shares/%s/instances" % share_id,
|
||||
headers=self._get_version_dict('1.4'),
|
||||
extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def list_share_instances(self):
|
||||
resp, body = self.get("share_instances",
|
||||
headers=self._get_version_dict('1.4'),
|
||||
extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def get_share_instance(self, instance_id):
|
||||
resp, body = self.get("share_instances/%s" % instance_id,
|
||||
headers=self._get_version_dict('1.4'),
|
||||
extra_headers=True)
|
||||
resp, body = self.get("shares/%s" % share_id)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
@ -292,30 +220,6 @@ class SharesClient(rest_client.RestClient):
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
def wait_for_migration_completed(self, share_id, dest_host):
|
||||
"""Waits for a share to migrate to a certain host."""
|
||||
share = self.get_share(share_id)
|
||||
migration_timeout = CONF.share.migration_timeout
|
||||
start = int(time.time())
|
||||
while share['task_state'] != 'migration_success':
|
||||
time.sleep(self.build_interval)
|
||||
share = self.get_share(share_id)
|
||||
if share['task_state'] == 'migration_success':
|
||||
return share
|
||||
elif share['task_state'] == 'migration_error':
|
||||
raise share_exceptions.ShareMigrationException(
|
||||
share_id=share['id'], src=share['host'], dest=dest_host)
|
||||
elif int(time.time()) - start >= migration_timeout:
|
||||
message = ('Share %(share_id)s failed to migrate from '
|
||||
'host %(src)s to host %(dest)s within the required '
|
||||
'time %(timeout)s.' % {
|
||||
'src': share['host'],
|
||||
'dest': dest_host,
|
||||
'share_id': share['id'],
|
||||
'timeout': self.build_timeout
|
||||
})
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
def wait_for_share_status(self, share_id, status):
|
||||
"""Waits for a share to reach a given status."""
|
||||
body = self.get_share(share_id)
|
||||
@ -339,28 +243,6 @@ class SharesClient(rest_client.RestClient):
|
||||
(share_name, status, self.build_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
def wait_for_share_instance_status(self, instance_id, status):
|
||||
"""Waits for a share to reach a given status."""
|
||||
body = self.get_share_instance(instance_id)
|
||||
instance_status = body['status']
|
||||
start = int(time.time())
|
||||
|
||||
while instance_status != status:
|
||||
time.sleep(self.build_interval)
|
||||
body = self.get_share(instance_id)
|
||||
instance_status = body['status']
|
||||
if instance_status == status:
|
||||
return
|
||||
elif 'error' in instance_status.lower():
|
||||
raise share_exceptions.\
|
||||
ShareInstanceBuildErrorException(id=instance_id)
|
||||
|
||||
if int(time.time()) - start >= self.build_timeout:
|
||||
message = ('Share instance %s failed to reach %s status within'
|
||||
' the required time (%s s).' %
|
||||
(instance_id, status, self.build_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
def wait_for_snapshot_status(self, snapshot_id, status):
|
||||
"""Waits for a snapshot to reach a given status."""
|
||||
body = self.get_snapshot(snapshot_id)
|
||||
@ -382,49 +264,6 @@ class SharesClient(rest_client.RestClient):
|
||||
(snapshot_name, status, self.build_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
def wait_for_consistency_group_status(self, consistency_group_id, status):
|
||||
"""Waits for a consistency group to reach a given status."""
|
||||
body = self.get_consistency_group(consistency_group_id)
|
||||
consistency_group_name = body['name']
|
||||
consistency_group_status = body['status']
|
||||
start = int(time.time())
|
||||
|
||||
while consistency_group_status != status:
|
||||
time.sleep(self.build_interval)
|
||||
body = self.get_consistency_group(consistency_group_id)
|
||||
consistency_group_status = body['status']
|
||||
if 'error' in consistency_group_status and status != 'error':
|
||||
raise share_exceptions.ConsistencyGroupBuildErrorException(
|
||||
consistency_group_id=consistency_group_id)
|
||||
|
||||
if int(time.time()) - start >= self.build_timeout:
|
||||
message = ('Consistency Group %s failed to reach %s status '
|
||||
'within the required time (%s s).' %
|
||||
(consistency_group_name, status,
|
||||
self.build_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
def wait_for_cgsnapshot_status(self, cgsnapshot_id, status):
|
||||
"""Waits for a cgsnapshot to reach a given status."""
|
||||
body = self.get_cgsnapshot(cgsnapshot_id)
|
||||
cgsnapshot_name = body['name']
|
||||
cgsnapshot_status = body['status']
|
||||
start = int(time.time())
|
||||
|
||||
while cgsnapshot_status != status:
|
||||
time.sleep(self.build_interval)
|
||||
body = self.get_cgsnapshot(cgsnapshot_id)
|
||||
cgsnapshot_status = body['status']
|
||||
if 'error' in cgsnapshot_status and status != 'error':
|
||||
raise share_exceptions.CGSnapshotBuildErrorException(
|
||||
cgsnapshot_id=cgsnapshot_id)
|
||||
|
||||
if int(time.time()) - start >= self.build_timeout:
|
||||
message = ('CGSnapshot %s failed to reach %s status '
|
||||
'within the required time (%s s).' %
|
||||
(cgsnapshot_name, status, self.build_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
def wait_for_access_rule_status(self, share_id, rule_id, status):
|
||||
"""Waits for an access rule to reach a given status."""
|
||||
rule_status = "new"
|
||||
@ -502,8 +341,7 @@ class SharesClient(rest_client.RestClient):
|
||||
"""Verifies whether provided resource deleted or not.
|
||||
|
||||
:param kwargs: dict with expected keys 'share_id', 'snapshot_id',
|
||||
:param kwargs: 'sn_id', 'ss_id', 'vt_id', 'server_id', 'cg_id',
|
||||
:param kwargs: and 'cgsnapshot_id'
|
||||
:param kwargs: 'sn_id', 'ss_id', 'vt_id' and 'server_id'
|
||||
:raises share_exceptions.InvalidResource
|
||||
"""
|
||||
if "share_id" in kwargs:
|
||||
@ -518,9 +356,6 @@ class SharesClient(rest_client.RestClient):
|
||||
else:
|
||||
return self._is_resource_deleted(
|
||||
self.get_share, kwargs.get("share_id"))
|
||||
elif "share_instance_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.get_share_instance, kwargs.get("share_instance_id"))
|
||||
elif "snapshot_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.get_snapshot, kwargs.get("snapshot_id"))
|
||||
@ -539,12 +374,6 @@ class SharesClient(rest_client.RestClient):
|
||||
elif "server_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.show_share_server, kwargs.get("server_id"))
|
||||
elif "cg_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.get_consistency_group, kwargs.get("cg_id"))
|
||||
elif "cgsnapshot_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.get_cgsnapshot, kwargs.get("cgsnapshot_id"))
|
||||
else:
|
||||
raise share_exceptions.InvalidResource(
|
||||
message=six.text_type(kwargs))
|
||||
@ -598,29 +427,26 @@ class SharesClient(rest_client.RestClient):
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def reset_state(self, s_id, status="error", s_type="shares",
|
||||
headers=None):
|
||||
"""Resets the state of a share, snapshot, cg, or a cgsnapshot.
|
||||
def reset_state(self, s_id, status="error", s_type="shares"):
|
||||
"""Resets the state of a share or a snapshot.
|
||||
|
||||
status: available, error, creating, deleting, error_deleting
|
||||
s_type: shares, snapshots, consistency-groups, cgsnapshots
|
||||
s_type: shares, snapshots
|
||||
"""
|
||||
body = {"os-reset_status": {"status": status}}
|
||||
body = json.dumps(body)
|
||||
resp, body = self.post("%s/%s/action" % (s_type, s_id), body,
|
||||
headers=headers, extra_headers=True)
|
||||
resp, body = self.post("%s/%s/action" % (s_type, s_id), body)
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
def force_delete(self, s_id, s_type="shares", headers=None):
|
||||
def force_delete(self, s_id, s_type="shares"):
|
||||
"""Force delete share or snapshot.
|
||||
|
||||
s_type: shares, snapshots
|
||||
"""
|
||||
body = {"os-force_delete": None}
|
||||
body = json.dumps(body)
|
||||
resp, body = self.post("%s/%s/action" % (s_type, s_id), body,
|
||||
headers=headers, extra_headers=True)
|
||||
resp, body = self.post("%s/%s/action" % (s_type, s_id), body)
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
@ -925,143 +751,3 @@ class SharesClient(rest_client.RestClient):
|
||||
resp, body = self.get(uri)
|
||||
self.expected_success(200, resp.status)
|
||||
return json.loads(body)
|
||||
|
||||
###############
|
||||
|
||||
def create_consistency_group(self, name=None, description=None,
|
||||
share_type_ids=(), share_network_id=None,
|
||||
source_cgsnapshot_id=None):
|
||||
"""Create a new consistency group."""
|
||||
uri = 'consistency-groups'
|
||||
post_body = {}
|
||||
if name:
|
||||
post_body['name'] = name
|
||||
if description:
|
||||
post_body['description'] = description
|
||||
if share_type_ids:
|
||||
post_body['share_types'] = share_type_ids
|
||||
if source_cgsnapshot_id:
|
||||
post_body['source_cgsnapshot_id'] = source_cgsnapshot_id
|
||||
if share_network_id:
|
||||
post_body['share_network_id'] = share_network_id
|
||||
body = json.dumps({'consistency_group': post_body})
|
||||
resp, body = self.post(uri, body, headers=EXPERIMENTAL,
|
||||
extra_headers=True)
|
||||
self.expected_success(202, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def delete_consistency_group(self, consistency_group_id):
|
||||
"""Delete a consistency group."""
|
||||
uri = 'consistency-groups/%s' % consistency_group_id
|
||||
resp, body = self.delete(uri, headers=EXPERIMENTAL,
|
||||
extra_headers=True)
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
def list_consistency_groups(self, detailed=False, params=None):
|
||||
"""Get list of consistency groups w/o filters."""
|
||||
uri = 'consistency-groups%s' % ('/detail' if detailed else '')
|
||||
uri += '?%s' % (urllib.urlencode(params) if params else '')
|
||||
resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def get_consistency_group(self, consistency_group_id):
|
||||
"""Get consistency group info."""
|
||||
uri = 'consistency-groups/%s' % consistency_group_id
|
||||
resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def update_consistency_group(self, consistency_group_id, name=None,
|
||||
description=None, **kwargs):
|
||||
"""Update an existing consistency group."""
|
||||
uri = 'consistency-groups/%s' % consistency_group_id
|
||||
post_body = {}
|
||||
if name:
|
||||
post_body['name'] = name
|
||||
if description:
|
||||
post_body['description'] = description
|
||||
if kwargs:
|
||||
post_body.update(kwargs)
|
||||
body = json.dumps({'consistency_group': post_body})
|
||||
resp, body = self.put(uri, body, headers=EXPERIMENTAL,
|
||||
extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def consistency_group_reset_state(self, id, status):
|
||||
self.reset_state(id, status=status,
|
||||
s_type='consistency-groups', headers=EXPERIMENTAL)
|
||||
|
||||
def consistency_group_force_delete(self, id, status):
|
||||
self.force_delete(id, status=status,
|
||||
s_type='consistency-groups', headers=EXPERIMENTAL)
|
||||
|
||||
###############
|
||||
|
||||
def create_cgsnapshot(self, consistency_group_id,
|
||||
name=None, description=None):
|
||||
"""Create a new cgsnapshot of an existing consistency group."""
|
||||
uri = 'cgsnapshots'
|
||||
post_body = {'consistency_group_id': consistency_group_id}
|
||||
if name:
|
||||
post_body['name'] = name
|
||||
if description:
|
||||
post_body['description'] = description
|
||||
body = json.dumps({'cgsnapshot': post_body})
|
||||
resp, body = self.post(uri, body, headers=EXPERIMENTAL,
|
||||
extra_headers=True)
|
||||
self.expected_success(202, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def delete_cgsnapshot(self, cgsnapshot_id):
|
||||
"""Delete an existing cgsnapshot."""
|
||||
uri = 'cgsnapshots/%s' % cgsnapshot_id
|
||||
resp, body = self.delete(uri, headers=EXPERIMENTAL, extra_headers=True)
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
def list_cgsnapshots(self, detailed=False, params=None):
|
||||
"""Get list of cgsnapshots w/o filters."""
|
||||
uri = 'cgsnapshots/detail' if detailed else 'cgsnapshots'
|
||||
uri += '?%s' % (urllib.urlencode(params) if params else '')
|
||||
resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def list_cgsnapshot_members(self, cgsnapshot_id):
|
||||
"""Get list of members of a cgsnapshots."""
|
||||
uri = 'cgsnapshots/%s/members' % cgsnapshot_id
|
||||
resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def get_cgsnapshot(self, cgsnapshot_id):
|
||||
"""Get cgsnapshot info."""
|
||||
uri = 'cgsnapshots/%s' % cgsnapshot_id
|
||||
resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def update_cgsnapshot(self, cgsnapshot_id, name=None, description=None):
|
||||
"""Update an existing cgsnapshot."""
|
||||
uri = 'cgsnapshots/%s' % cgsnapshot_id
|
||||
post_body = {}
|
||||
if name:
|
||||
post_body['name'] = name
|
||||
if description:
|
||||
post_body['description'] = description
|
||||
body = json.dumps({'cgsnapshot': post_body})
|
||||
resp, body = self.put(uri, body, headers=EXPERIMENTAL,
|
||||
extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def cgsnapshot_reset_state(self, id, status):
|
||||
self.reset_state(id, status=status,
|
||||
s_type='cgsnapshots', headers=EXPERIMENTAL)
|
||||
|
||||
def cgsnapshot_force_delete(self, id, status):
|
||||
self.force_delete(id, status=status,
|
||||
s_type='cgsnapshots', headers=EXPERIMENTAL)
|
||||
|
0
manila_tempest_tests/services/share/v2/__init__.py
Normal file
0
manila_tempest_tests/services/share/v2/__init__.py
Normal file
516
manila_tempest_tests/services/share/v2/json/shares_client.py
Normal file
516
manila_tempest_tests/services/share/v2/json/shares_client.py
Normal file
@ -0,0 +1,516 @@
|
||||
# Copyright 2015 Andrew Kerr
|
||||
# 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.
|
||||
|
||||
import json
|
||||
import time
|
||||
import urllib
|
||||
|
||||
from tempest_lib.common.utils import data_utils
|
||||
from tempest_lib import exceptions
|
||||
|
||||
from manila_tempest_tests.services.share.json import shares_client # noqa
|
||||
from manila_tempest_tests import share_exceptions
|
||||
from tempest import config # noqa
|
||||
|
||||
CONF = config.CONF
|
||||
LATEST_MICROVERSION = CONF.share.max_api_microversion
|
||||
EXPERIMENTAL = {'X-OpenStack-Manila-API-Experimental': 'True'}
|
||||
|
||||
|
||||
class SharesV2Client(shares_client.SharesClient):
|
||||
"""Tempest REST client for Manila.
|
||||
|
||||
It handles shares and access to it in OpenStack.
|
||||
"""
|
||||
api_version = 'v2'
|
||||
|
||||
def __init__(self, auth_provider):
|
||||
super(SharesV2Client, self).__init__(auth_provider)
|
||||
self.API_MICROVERSIONS_HEADER = 'x-openstack-manila-api-version'
|
||||
|
||||
def inject_microversion_header(self, headers, version,
|
||||
extra_headers=False):
|
||||
"""Inject the required manila microversion header."""
|
||||
new_headers = self.get_headers()
|
||||
new_headers[self.API_MICROVERSIONS_HEADER] = version
|
||||
if extra_headers and headers:
|
||||
new_headers.update(headers)
|
||||
elif headers:
|
||||
new_headers = headers
|
||||
return new_headers
|
||||
|
||||
# Overwrite all http verb calls to inject the micro version header
|
||||
def post(self, url, body, headers=None, extra_headers=False,
|
||||
version=LATEST_MICROVERSION):
|
||||
headers = self.inject_microversion_header(headers, version,
|
||||
extra_headers=extra_headers)
|
||||
return super(SharesV2Client, self).post(url, body, headers=headers)
|
||||
|
||||
def get(self, url, headers=None, extra_headers=False,
|
||||
version=LATEST_MICROVERSION):
|
||||
headers = self.inject_microversion_header(headers, version,
|
||||
extra_headers=extra_headers)
|
||||
return super(SharesV2Client, self).get(url, headers=headers)
|
||||
|
||||
def delete(self, url, headers=None, body=None, extra_headers=False,
|
||||
version=LATEST_MICROVERSION):
|
||||
headers = self.inject_microversion_header(headers, version,
|
||||
extra_headers=extra_headers)
|
||||
return super(SharesV2Client, self).delete(url, headers=headers,
|
||||
body=body)
|
||||
|
||||
def patch(self, url, body, headers=None, extra_headers=False,
|
||||
version=LATEST_MICROVERSION):
|
||||
headers = self.inject_microversion_header(headers, version,
|
||||
extra_headers=extra_headers)
|
||||
return super(SharesV2Client, self).patch(url, body, headers=headers)
|
||||
|
||||
def put(self, url, body, headers=None, extra_headers=False,
|
||||
version=LATEST_MICROVERSION):
|
||||
headers = self.inject_microversion_header(headers, version,
|
||||
extra_headers=extra_headers)
|
||||
return super(SharesV2Client, self).put(url, body, headers=headers)
|
||||
|
||||
def head(self, url, headers=None, extra_headers=False,
|
||||
version=LATEST_MICROVERSION):
|
||||
headers = self.inject_microversion_header(headers, version,
|
||||
extra_headers=extra_headers)
|
||||
return super(SharesV2Client, self).head(url, headers=headers)
|
||||
|
||||
def copy(self, url, headers=None, extra_headers=False,
|
||||
version=LATEST_MICROVERSION):
|
||||
headers = self.inject_microversion_header(headers, version,
|
||||
extra_headers=extra_headers)
|
||||
return super(SharesV2Client, self).copy(url, headers=headers)
|
||||
|
||||
def reset_state(self, s_id, status="error", s_type="shares",
|
||||
headers=None, version=LATEST_MICROVERSION):
|
||||
"""Resets the state of a share, snapshot, cg, or a cgsnapshot.
|
||||
|
||||
status: available, error, creating, deleting, error_deleting
|
||||
s_type: shares, snapshots, consistency-groups, cgsnapshots
|
||||
"""
|
||||
body = {"os-reset_status": {"status": status}}
|
||||
body = json.dumps(body)
|
||||
resp, body = self.post("%s/%s/action" % (s_type, s_id), body,
|
||||
headers=headers, extra_headers=True,
|
||||
version=version)
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
def force_delete(self, s_id, s_type="shares", headers=None,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Force delete share or snapshot.
|
||||
|
||||
s_type: shares, snapshots
|
||||
"""
|
||||
body = {"os-force_delete": None}
|
||||
body = json.dumps(body)
|
||||
resp, body = self.post("%s/%s/action" % (s_type, s_id), body,
|
||||
headers=headers, extra_headers=True,
|
||||
version=version)
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
def send_microversion_request(self, version=None, script_name=None):
|
||||
"""Prepare and send the HTTP GET Request to the base URL.
|
||||
|
||||
Extracts the base URL from the shares_client endpoint and makes a GET
|
||||
request with the microversions request header.
|
||||
:param version: The string to send for the value of the microversion
|
||||
header, or None to omit the header.
|
||||
:param script_name: The first part of the URL (v1 or v2), or None to
|
||||
omit it.
|
||||
"""
|
||||
|
||||
headers = self.get_headers()
|
||||
url, headers, body = self.auth_provider.auth_request(
|
||||
'GET', 'shares', headers, None, self.filters)
|
||||
url = '/'.join(url.split('/')[:3]) + '/'
|
||||
if script_name:
|
||||
url += script_name + '/'
|
||||
if version:
|
||||
headers[self.API_MICROVERSIONS_HEADER] = version
|
||||
resp, resp_body = self.raw_request(url, 'GET', headers=headers)
|
||||
self.response_checker('GET', resp, resp_body)
|
||||
resp_body = json.loads(resp_body)
|
||||
return resp, resp_body
|
||||
|
||||
def is_resource_deleted(self, *args, **kwargs):
|
||||
"""Verifies whether provided resource deleted or not.
|
||||
|
||||
:param kwargs: dict with expected keys 'share_id', 'snapshot_id',
|
||||
:param kwargs: 'sn_id', 'ss_id', 'vt_id' and 'server_id'
|
||||
:raises share_exceptions.InvalidResource
|
||||
"""
|
||||
if "share_instance_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.get_share_instance, kwargs.get("share_instance_id"))
|
||||
elif "cg_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.get_consistency_group, kwargs.get("cg_id"))
|
||||
elif "cgsnapshot_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.get_cgsnapshot, kwargs.get("cgsnapshot_id"))
|
||||
else:
|
||||
return super(SharesV2Client, self).is_resource_deleted(
|
||||
*args, **kwargs)
|
||||
|
||||
###############
|
||||
|
||||
def create_share(self, share_protocol=None, size=1,
|
||||
name=None, snapshot_id=None, description=None,
|
||||
metadata=None, share_network_id=None,
|
||||
share_type_id=None, is_public=False,
|
||||
consistency_group_id=None, version=LATEST_MICROVERSION):
|
||||
metadata = metadata or {}
|
||||
if name is None:
|
||||
name = data_utils.rand_name("tempest-created-share")
|
||||
if description is None:
|
||||
description = data_utils.rand_name("tempest-created-share-desc")
|
||||
if share_protocol is None:
|
||||
share_protocol = self.share_protocol
|
||||
if share_protocol is None:
|
||||
raise share_exceptions.ShareProtocolNotSpecified()
|
||||
post_body = {
|
||||
"share": {
|
||||
"share_proto": share_protocol,
|
||||
"description": description,
|
||||
"snapshot_id": snapshot_id,
|
||||
"name": name,
|
||||
"size": size,
|
||||
"metadata": metadata,
|
||||
"is_public": is_public,
|
||||
}
|
||||
}
|
||||
if share_network_id:
|
||||
post_body["share"]["share_network_id"] = share_network_id
|
||||
if share_type_id:
|
||||
post_body["share"]["share_type"] = share_type_id
|
||||
if consistency_group_id:
|
||||
post_body["share"]["consistency_group_id"] = consistency_group_id
|
||||
body = json.dumps(post_body)
|
||||
resp, body = self.post("shares", body, version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def list_shares(self, detailed=False, params=None,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Get list of shares w/o filters."""
|
||||
uri = 'shares/detail' if detailed else 'shares'
|
||||
uri += '?%s' % urllib.urlencode(params) if params else ''
|
||||
resp, body = self.get(uri, version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def list_shares_with_detail(self, params=None,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Get detailed list of shares w/o filters."""
|
||||
return self.list_shares(detailed=True, params=params, version=version)
|
||||
|
||||
def get_share(self, share_id, version=LATEST_MICROVERSION):
|
||||
resp, body = self.get("shares/%s" % share_id, version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def delete_share(self, share_id, params=None,
|
||||
version=LATEST_MICROVERSION):
|
||||
uri = "shares/%s" % share_id
|
||||
uri += '?%s' % (urllib.urlencode(params) if params else '')
|
||||
resp, body = self.delete(uri, version=version)
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
###############
|
||||
|
||||
def get_instances_of_share(self, share_id, version=LATEST_MICROVERSION):
|
||||
resp, body = self.get("shares/%s/instances" % share_id,
|
||||
version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def list_share_instances(self, version=LATEST_MICROVERSION):
|
||||
resp, body = self.get("share_instances", version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def get_share_instance(self, instance_id, version=LATEST_MICROVERSION):
|
||||
resp, body = self.get("share_instances/%s" % instance_id,
|
||||
version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def wait_for_share_instance_status(self, instance_id, status,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Waits for a share to reach a given status."""
|
||||
body = self.get_share_instance(instance_id, version=version)
|
||||
instance_status = body['status']
|
||||
start = int(time.time())
|
||||
|
||||
while instance_status != status:
|
||||
time.sleep(self.build_interval)
|
||||
body = self.get_share(instance_id)
|
||||
instance_status = body['status']
|
||||
if instance_status == status:
|
||||
return
|
||||
elif 'error' in instance_status.lower():
|
||||
raise share_exceptions. \
|
||||
ShareInstanceBuildErrorException(id=instance_id)
|
||||
|
||||
if int(time.time()) - start >= self.build_timeout:
|
||||
message = ('Share instance %s failed to reach %s status within'
|
||||
' the required time (%s s).' %
|
||||
(instance_id, status, self.build_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
###############
|
||||
|
||||
def create_consistency_group(self, name=None, description=None,
|
||||
share_type_ids=(), share_network_id=None,
|
||||
source_cgsnapshot_id=None,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Create a new consistency group."""
|
||||
uri = 'consistency-groups'
|
||||
post_body = {}
|
||||
if name:
|
||||
post_body['name'] = name
|
||||
if description:
|
||||
post_body['description'] = description
|
||||
if share_type_ids:
|
||||
post_body['share_types'] = share_type_ids
|
||||
if source_cgsnapshot_id:
|
||||
post_body['source_cgsnapshot_id'] = source_cgsnapshot_id
|
||||
if share_network_id:
|
||||
post_body['share_network_id'] = share_network_id
|
||||
body = json.dumps({'consistency_group': post_body})
|
||||
resp, body = self.post(uri, body, headers=EXPERIMENTAL,
|
||||
extra_headers=True, version=version)
|
||||
self.expected_success(202, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def delete_consistency_group(self, consistency_group_id,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Delete a consistency group."""
|
||||
uri = 'consistency-groups/%s' % consistency_group_id
|
||||
resp, body = self.delete(uri, headers=EXPERIMENTAL,
|
||||
extra_headers=True, version=version)
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
def list_consistency_groups(self, detailed=False, params=None,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Get list of consistency groups w/o filters."""
|
||||
uri = 'consistency-groups%s' % ('/detail' if detailed else '')
|
||||
uri += '?%s' % (urllib.urlencode(params) if params else '')
|
||||
resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
|
||||
version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def get_consistency_group(self, consistency_group_id,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Get consistency group info."""
|
||||
uri = 'consistency-groups/%s' % consistency_group_id
|
||||
resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
|
||||
version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def update_consistency_group(self, consistency_group_id, name=None,
|
||||
description=None,
|
||||
version=LATEST_MICROVERSION, **kwargs):
|
||||
"""Update an existing consistency group."""
|
||||
uri = 'consistency-groups/%s' % consistency_group_id
|
||||
post_body = {}
|
||||
if name:
|
||||
post_body['name'] = name
|
||||
if description:
|
||||
post_body['description'] = description
|
||||
if kwargs:
|
||||
post_body.update(kwargs)
|
||||
body = json.dumps({'consistency_group': post_body})
|
||||
resp, body = self.put(uri, body, headers=EXPERIMENTAL,
|
||||
extra_headers=True, version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def consistency_group_reset_state(self, id, status,
|
||||
version=LATEST_MICROVERSION):
|
||||
self.reset_state(id, status=status,
|
||||
s_type='consistency-groups', headers=EXPERIMENTAL,
|
||||
version=version)
|
||||
|
||||
def consistency_group_force_delete(self, id, version=LATEST_MICROVERSION):
|
||||
self.force_delete(id, s_type='consistency-groups',
|
||||
headers=EXPERIMENTAL, version=version)
|
||||
|
||||
def wait_for_consistency_group_status(self, consistency_group_id, status):
|
||||
"""Waits for a consistency group to reach a given status."""
|
||||
body = self.get_consistency_group(consistency_group_id)
|
||||
consistency_group_name = body['name']
|
||||
consistency_group_status = body['status']
|
||||
start = int(time.time())
|
||||
|
||||
while consistency_group_status != status:
|
||||
time.sleep(self.build_interval)
|
||||
body = self.get_consistency_group(consistency_group_id)
|
||||
consistency_group_status = body['status']
|
||||
if 'error' in consistency_group_status and status != 'error':
|
||||
raise share_exceptions.ConsistencyGroupBuildErrorException(
|
||||
consistency_group_id=consistency_group_id)
|
||||
|
||||
if int(time.time()) - start >= self.build_timeout:
|
||||
message = ('Consistency Group %s failed to reach %s status '
|
||||
'within the required time (%s s).' %
|
||||
(consistency_group_name, status,
|
||||
self.build_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
###############
|
||||
|
||||
def create_cgsnapshot(self, consistency_group_id,
|
||||
name=None, description=None,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Create a new cgsnapshot of an existing consistency group."""
|
||||
uri = 'cgsnapshots'
|
||||
post_body = {'consistency_group_id': consistency_group_id}
|
||||
if name:
|
||||
post_body['name'] = name
|
||||
if description:
|
||||
post_body['description'] = description
|
||||
body = json.dumps({'cgsnapshot': post_body})
|
||||
resp, body = self.post(uri, body, headers=EXPERIMENTAL,
|
||||
extra_headers=True, version=version)
|
||||
self.expected_success(202, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def delete_cgsnapshot(self, cgsnapshot_id,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Delete an existing cgsnapshot."""
|
||||
uri = 'cgsnapshots/%s' % cgsnapshot_id
|
||||
resp, body = self.delete(uri, headers=EXPERIMENTAL,
|
||||
extra_headers=True, version=version)
|
||||
self.expected_success(202, resp.status)
|
||||
return body
|
||||
|
||||
def list_cgsnapshots(self, detailed=False, params=None,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Get list of cgsnapshots w/o filters."""
|
||||
uri = 'cgsnapshots/detail' if detailed else 'cgsnapshots'
|
||||
uri += '?%s' % (urllib.urlencode(params) if params else '')
|
||||
resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
|
||||
version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def list_cgsnapshot_members(self, cgsnapshot_id,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Get list of members of a cgsnapshots."""
|
||||
uri = 'cgsnapshots/%s/members' % cgsnapshot_id
|
||||
resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
|
||||
version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def get_cgsnapshot(self, cgsnapshot_id, version=LATEST_MICROVERSION):
|
||||
"""Get cgsnapshot info."""
|
||||
uri = 'cgsnapshots/%s' % cgsnapshot_id
|
||||
resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
|
||||
version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def update_cgsnapshot(self, cgsnapshot_id, name=None, description=None,
|
||||
version=LATEST_MICROVERSION):
|
||||
"""Update an existing cgsnapshot."""
|
||||
uri = 'cgsnapshots/%s' % cgsnapshot_id
|
||||
post_body = {}
|
||||
if name:
|
||||
post_body['name'] = name
|
||||
if description:
|
||||
post_body['description'] = description
|
||||
body = json.dumps({'cgsnapshot': post_body})
|
||||
resp, body = self.put(uri, body, headers=EXPERIMENTAL,
|
||||
extra_headers=True, version=version)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def cgsnapshot_reset_state(self, id, status,
|
||||
version=LATEST_MICROVERSION):
|
||||
self.reset_state(id, status=status,
|
||||
s_type='cgsnapshots', headers=EXPERIMENTAL,
|
||||
version=version)
|
||||
|
||||
def cgsnapshot_force_delete(self, id, version=LATEST_MICROVERSION):
|
||||
self.force_delete(id, s_type='cgsnapshots', headers=EXPERIMENTAL,
|
||||
version=version)
|
||||
|
||||
def wait_for_cgsnapshot_status(self, cgsnapshot_id, status):
|
||||
"""Waits for a cgsnapshot to reach a given status."""
|
||||
body = self.get_cgsnapshot(cgsnapshot_id)
|
||||
cgsnapshot_name = body['name']
|
||||
cgsnapshot_status = body['status']
|
||||
start = int(time.time())
|
||||
|
||||
while cgsnapshot_status != status:
|
||||
time.sleep(self.build_interval)
|
||||
body = self.get_cgsnapshot(cgsnapshot_id)
|
||||
cgsnapshot_status = body['status']
|
||||
if 'error' in cgsnapshot_status and status != 'error':
|
||||
raise share_exceptions.CGSnapshotBuildErrorException(
|
||||
cgsnapshot_id=cgsnapshot_id)
|
||||
|
||||
if int(time.time()) - start >= self.build_timeout:
|
||||
message = ('CGSnapshot %s failed to reach %s status '
|
||||
'within the required time (%s s).' %
|
||||
(cgsnapshot_name, status, self.build_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
###############
|
||||
|
||||
def migrate_share(self, share_id, host, version=LATEST_MICROVERSION):
|
||||
post_body = {
|
||||
'os-migrate_share': {
|
||||
'host': host,
|
||||
}
|
||||
}
|
||||
body = json.dumps(post_body)
|
||||
return self.post('shares/%s/action' % share_id, body,
|
||||
headers=EXPERIMENTAL, extra_headers=True,
|
||||
version=version)
|
||||
|
||||
def wait_for_migration_completed(self, share_id, dest_host):
|
||||
"""Waits for a share to migrate to a certain host."""
|
||||
share = self.get_share(share_id)
|
||||
migration_timeout = CONF.share.migration_timeout
|
||||
start = int(time.time())
|
||||
while share['task_state'] != 'migration_success':
|
||||
time.sleep(self.build_interval)
|
||||
share = self.get_share(share_id)
|
||||
if share['task_state'] == 'migration_success':
|
||||
return share
|
||||
elif share['task_state'] == 'migration_error':
|
||||
raise share_exceptions.ShareMigrationException(
|
||||
share_id=share['id'], src=share['host'], dest=dest_host)
|
||||
elif int(time.time()) - start >= migration_timeout:
|
||||
message = ('Share %(share_id)s failed to migrate from '
|
||||
'host %(src)s to host %(dest)s within the required '
|
||||
'time %(timeout)s.' % {
|
||||
'src': share['host'],
|
||||
'dest': dest_host,
|
||||
'share_id': share['id'],
|
||||
'timeout': self.build_timeout
|
||||
})
|
||||
raise exceptions.TimeoutException(message)
|
@ -32,7 +32,7 @@ class AdminActionsTest(base.BaseSharesAdminTest):
|
||||
cls.bad_status = "error_deleting"
|
||||
cls.sh = cls.create_share()
|
||||
cls.sh_instance = (
|
||||
cls.shares_client.get_instances_of_share(cls.sh["id"])[0]
|
||||
cls.shares_v2_client.get_instances_of_share(cls.sh["id"])[0]
|
||||
)
|
||||
if CONF.share.run_snapshot_tests:
|
||||
cls.sn = cls.create_snapshot_wait_for_active(cls.sh["id"])
|
||||
@ -49,7 +49,7 @@ class AdminActionsTest(base.BaseSharesAdminTest):
|
||||
for status in self.states:
|
||||
self.shares_client.reset_state(
|
||||
id, s_type="share_instances", status=status)
|
||||
self.shares_client.wait_for_share_instance_status(id, status)
|
||||
self.shares_v2_client.wait_for_share_instance_status(id, status)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
@testtools.skipUnless(CONF.share.run_snapshot_tests,
|
||||
@ -78,7 +78,7 @@ class AdminActionsTest(base.BaseSharesAdminTest):
|
||||
@test.attr(type=["gate", ])
|
||||
def test_force_delete_share_instance(self):
|
||||
share = self.create_share(cleanup_in_class=False)
|
||||
instances = self.shares_client.get_instances_of_share(share["id"])
|
||||
instances = self.shares_v2_client.get_instances_of_share(share["id"])
|
||||
# Check that instance was created
|
||||
self.assertEqual(1, len(instances))
|
||||
|
||||
@ -89,13 +89,13 @@ class AdminActionsTest(base.BaseSharesAdminTest):
|
||||
instance["id"], s_type="share_instances", status=self.bad_status)
|
||||
|
||||
# Check that status was changed
|
||||
check_status = self.shares_client.get_share_instance(instance["id"])
|
||||
check_status = self.shares_v2_client.get_share_instance(instance["id"])
|
||||
self.assertEqual(self.bad_status, check_status["status"])
|
||||
|
||||
# Share with status 'error_deleting' should be deleted
|
||||
self.shares_client.force_delete(
|
||||
instance["id"], s_type="share_instances")
|
||||
self.shares_client.wait_for_resource_deletion(
|
||||
self.shares_v2_client.wait_for_resource_deletion(
|
||||
share_instance_id=instance["id"])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
|
@ -31,11 +31,12 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
|
||||
super(AdminActionsNegativeTest, cls).resource_setup()
|
||||
cls.sh = cls.create_share()
|
||||
cls.sh_instance = (
|
||||
cls.shares_client.get_instances_of_share(cls.sh["id"])[0]
|
||||
cls.shares_v2_client.get_instances_of_share(cls.sh["id"])[0]
|
||||
)
|
||||
if CONF.share.run_snapshot_tests:
|
||||
cls.sn = cls.create_snapshot_wait_for_active(cls.sh["id"])
|
||||
cls.member_shares_client = clients.Manager().shares_client
|
||||
cls.member_shares_v2_client = clients.Manager().shares_v2_client
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
def test_reset_nonexistent_share_state(self):
|
||||
@ -149,19 +150,19 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
|
||||
def test_try_get_share_instance_with_member(self):
|
||||
# If a non-admin tries to get instance, it should be unauthorized
|
||||
self.assertRaises(lib_exc.Forbidden,
|
||||
self.member_shares_client.get_share_instance,
|
||||
self.member_shares_v2_client.get_share_instance,
|
||||
self.sh_instance["id"])
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
def test_try_list_share_instance_with_member(self):
|
||||
# If a non-admin tries to list instances, it should be unauthorized
|
||||
self.assertRaises(lib_exc.Forbidden,
|
||||
self.member_shares_client.list_share_instances)
|
||||
self.member_shares_v2_client.list_share_instances)
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
def test_try_get_instances_of_share_with_member(self):
|
||||
# If a non-admin tries to list instances of given share, it should be
|
||||
# unauthorized
|
||||
self.assertRaises(lib_exc.Forbidden,
|
||||
self.member_shares_client.get_instances_of_share,
|
||||
self.member_shares_v2_client.get_instances_of_share,
|
||||
self.sh['id'])
|
||||
|
@ -45,13 +45,19 @@ class ConsistencyGroupActionsTest(base.BaseSharesAdminTest):
|
||||
share_type_ids=[cls.share_type['id'], cls.share_type2['id']])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_create_cg_from_cgsnapshot_with_multiple_share_types(self):
|
||||
def test_create_cg_from_cgsnapshot_with_multiple_share_types_v2_4(self):
|
||||
# Create cgsnapshot
|
||||
cgsnapshot = self.create_cgsnapshot_wait_for_active(
|
||||
self.consistency_group["id"], cleanup_in_class=False)
|
||||
self.consistency_group["id"],
|
||||
cleanup_in_class=False,
|
||||
version='2.4',
|
||||
)
|
||||
|
||||
new_consistency_group = self.create_consistency_group(
|
||||
cleanup_in_class=False, source_cgsnapshot_id=cgsnapshot['id'])
|
||||
cleanup_in_class=False,
|
||||
source_cgsnapshot_id=cgsnapshot['id'],
|
||||
version='2.4',
|
||||
)
|
||||
|
||||
# Verify share_types are the same
|
||||
expected_types = sorted(self.consistency_group['share_types'])
|
||||
@ -61,7 +67,7 @@ class ConsistencyGroupActionsTest(base.BaseSharesAdminTest):
|
||||
expected_types, actual_types))
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_create_cg_from_multi_typed_populated_cgsnapshot(self):
|
||||
def test_create_cg_from_multi_typed_populated_cgsnapshot_v2_4(self):
|
||||
share_name = data_utils.rand_name("tempest-share-name")
|
||||
share_desc = data_utils.rand_name("tempest-share-description")
|
||||
share_size = 1
|
||||
@ -71,7 +77,9 @@ class ConsistencyGroupActionsTest(base.BaseSharesAdminTest):
|
||||
description=share_desc,
|
||||
size=share_size,
|
||||
consistency_group_id=self.consistency_group['id'],
|
||||
share_type_id=self.share_type['id']
|
||||
share_type_id=self.share_type['id'],
|
||||
client=self.shares_v2_client,
|
||||
version='2.4',
|
||||
)
|
||||
|
||||
share_name2 = data_utils.rand_name("tempest-share-name")
|
||||
@ -83,11 +91,16 @@ class ConsistencyGroupActionsTest(base.BaseSharesAdminTest):
|
||||
description=share_desc2,
|
||||
size=share_size2,
|
||||
consistency_group_id=self.consistency_group['id'],
|
||||
share_type_id=self.share_type2['id']
|
||||
share_type_id=self.share_type2['id'],
|
||||
client=self.shares_v2_client,
|
||||
version='2.4',
|
||||
)
|
||||
|
||||
cg_shares = self.shares_client.list_shares(detailed=True, params={
|
||||
'consistency_group_id': self.consistency_group['id']})
|
||||
cg_shares = self.shares_v2_client.list_shares(
|
||||
detailed=True,
|
||||
params={'consistency_group_id': self.consistency_group['id']},
|
||||
version='2.4',
|
||||
)
|
||||
|
||||
cg_share_ids = [s['id'] for s in cg_shares]
|
||||
for share_id in [share['id'], share2['id']]:
|
||||
@ -101,10 +114,13 @@ class ConsistencyGroupActionsTest(base.BaseSharesAdminTest):
|
||||
self.consistency_group["id"],
|
||||
name=cgsnap_name,
|
||||
description=cgsnap_desc,
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4',
|
||||
)
|
||||
|
||||
self.create_consistency_group(
|
||||
cleanup_in_class=False, source_cgsnapshot_id=cgsnapshot['id'])
|
||||
self.create_consistency_group(cleanup_in_class=False,
|
||||
source_cgsnapshot_id=cgsnapshot['id'],
|
||||
version='2.4')
|
||||
|
||||
# TODO(akerr): Skip until bug 1483886 is resolved
|
||||
# Verify that the new shares correspond to correct share types
|
||||
|
@ -43,11 +43,13 @@ class ConsistencyGroupsTest(base.BaseSharesAdminTest):
|
||||
cls.share_type2 = share_type['share_type']
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_create_cg_with_multiple_share_types(self):
|
||||
def test_create_cg_with_multiple_share_types_v2_4(self):
|
||||
# Create a consistency group
|
||||
consistency_group = self.create_consistency_group(
|
||||
cleanup_in_class=False, share_type_ids=[self.share_type['id'],
|
||||
self.share_type2['id']])
|
||||
cleanup_in_class=False,
|
||||
share_type_ids=[self.share_type['id'], self.share_type2['id']],
|
||||
version='2.4',
|
||||
)
|
||||
|
||||
self.assertTrue(CG_REQUIRED_ELEMENTS.issubset(
|
||||
consistency_group.keys()),
|
||||
|
@ -51,6 +51,7 @@ class ConsistencyGroupsNegativeTest(base.BaseSharesAdminTest):
|
||||
size=cls.share_size,
|
||||
consistency_group_id=cls.consistency_group['id'],
|
||||
share_type_id=cls.share_type['id'],
|
||||
client=cls.shares_v2_client,
|
||||
)
|
||||
|
||||
# Create a cgsnapshot of the consistency group
|
||||
@ -69,91 +70,105 @@ class ConsistencyGroupsNegativeTest(base.BaseSharesAdminTest):
|
||||
self.share_type['id'])
|
||||
|
||||
@test.attr(type=["negative", "gate", ])
|
||||
def test_create_share_of_unsupported_type_in_cg(self):
|
||||
def test_create_share_of_unsupported_type_in_cg_v2_4(self):
|
||||
# Attempt to create share of default type in the cg
|
||||
self.assertRaises(exceptions.BadRequest,
|
||||
self.shares_client.create_share, size=1,
|
||||
consistency_group_id=self.consistency_group['id'])
|
||||
self.create_share,
|
||||
size=1,
|
||||
consistency_group_id=self.consistency_group['id'],
|
||||
client=self.shares_v2_client,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "gate", ])
|
||||
def test_create_share_in_cg_that_is_not_available(self):
|
||||
def test_create_share_in_cg_that_is_not_available_v2_4(self):
|
||||
consistency_group = self.create_consistency_group(
|
||||
cleanup_in_class=False)
|
||||
self.addCleanup(self.shares_client.consistency_group_reset_state,
|
||||
cleanup_in_class=False, version='2.4')
|
||||
self.addCleanup(self.shares_v2_client.consistency_group_reset_state,
|
||||
consistency_group['id'],
|
||||
status='available')
|
||||
status='available',
|
||||
version='2.4')
|
||||
# creating
|
||||
self.shares_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='creating')
|
||||
self.shares_client.wait_for_consistency_group_status(
|
||||
self.shares_v2_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='creating', version='2.4')
|
||||
self.shares_v2_client.wait_for_consistency_group_status(
|
||||
consistency_group['id'], 'creating')
|
||||
self.assertRaises(exceptions.BadRequest, self.create_share,
|
||||
name=self.share_name,
|
||||
description=self.share_desc,
|
||||
size=self.share_size,
|
||||
consistency_group_id=consistency_group['id'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
client=self.shares_v2_client,
|
||||
version='2.4')
|
||||
# deleting
|
||||
self.shares_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='deleting')
|
||||
self.shares_client.wait_for_consistency_group_status(
|
||||
self.shares_v2_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='deleting', version='2.4')
|
||||
self.shares_v2_client.wait_for_consistency_group_status(
|
||||
consistency_group['id'], 'deleting')
|
||||
self.assertRaises(exceptions.BadRequest, self.create_share,
|
||||
name=self.share_name,
|
||||
description=self.share_desc,
|
||||
size=self.share_size,
|
||||
consistency_group_id=consistency_group['id'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
client=self.shares_v2_client,
|
||||
version='2.4')
|
||||
# error
|
||||
self.shares_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='error')
|
||||
self.shares_client.wait_for_consistency_group_status(
|
||||
self.shares_v2_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='error', version='2.4')
|
||||
self.shares_v2_client.wait_for_consistency_group_status(
|
||||
consistency_group['id'], 'error')
|
||||
self.assertRaises(exceptions.BadRequest, self.create_share,
|
||||
name=self.share_name,
|
||||
description=self.share_desc,
|
||||
size=self.share_size,
|
||||
consistency_group_id=consistency_group['id'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
client=self.shares_v2_client,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "gate", ])
|
||||
def test_create_cgsnapshot_of_cg_that_is_not_available(self):
|
||||
def test_create_cgsnapshot_of_cg_that_is_not_available_v2_4(self):
|
||||
consistency_group = self.create_consistency_group(
|
||||
cleanup_in_class=False)
|
||||
self.addCleanup(self.shares_client.consistency_group_reset_state,
|
||||
cleanup_in_class=False, version='2.4')
|
||||
self.addCleanup(self.shares_v2_client.consistency_group_reset_state,
|
||||
consistency_group['id'],
|
||||
status='available')
|
||||
status='available',
|
||||
version='2.4')
|
||||
# creating
|
||||
self.shares_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='creating')
|
||||
self.shares_client.wait_for_consistency_group_status(
|
||||
self.shares_v2_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='creating', version='2.4')
|
||||
self.shares_v2_client.wait_for_consistency_group_status(
|
||||
consistency_group['id'], 'creating')
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.create_cgsnapshot_wait_for_active,
|
||||
consistency_group['id'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
# deleting
|
||||
self.shares_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='deleting')
|
||||
self.shares_client.wait_for_consistency_group_status(
|
||||
self.shares_v2_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='deleting', version='2.4')
|
||||
self.shares_v2_client.wait_for_consistency_group_status(
|
||||
consistency_group['id'], 'deleting')
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.create_cgsnapshot_wait_for_active,
|
||||
consistency_group['id'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
# error
|
||||
self.shares_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='error')
|
||||
self.shares_client.wait_for_consistency_group_status(
|
||||
self.shares_v2_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='error', version='2.4')
|
||||
self.shares_v2_client.wait_for_consistency_group_status(
|
||||
consistency_group['id'], 'error')
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.create_cgsnapshot_wait_for_active,
|
||||
consistency_group['id'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "gate", ])
|
||||
def test_create_cgsnapshot_of_cg_with_share_in_error_state(self):
|
||||
consistency_group = self.create_consistency_group()
|
||||
def test_create_cgsnapshot_of_cg_with_share_in_error_state_v2_4(self):
|
||||
consistency_group = self.create_consistency_group(version='2.4')
|
||||
share_name = data_utils.rand_name("tempest-share-name")
|
||||
share_desc = data_utils.rand_name("tempest-share-description")
|
||||
share_size = 1
|
||||
@ -163,65 +178,79 @@ class ConsistencyGroupsNegativeTest(base.BaseSharesAdminTest):
|
||||
size=share_size,
|
||||
consistency_group_id=consistency_group['id'],
|
||||
cleanup_in_class=False,
|
||||
client=self.shares_v2_client,
|
||||
version='2.4',
|
||||
)
|
||||
self.shares_client.reset_state(s_id=share['id'])
|
||||
self.shares_client.wait_for_share_status(share['id'], 'error')
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.create_cgsnapshot_wait_for_active,
|
||||
consistency_group['id'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "gate", ])
|
||||
def test_delete_cgsnapshot_not_in_available_or_error(self):
|
||||
def test_delete_cgsnapshot_not_in_available_or_error_v2_4(self):
|
||||
cgsnapshot = self.create_cgsnapshot_wait_for_active(
|
||||
self.consistency_group['id'], cleanup_in_class=False)
|
||||
self.addCleanup(self.shares_client.cgsnapshot_reset_state,
|
||||
self.consistency_group['id'],
|
||||
cleanup_in_class=False,
|
||||
version='2.4',
|
||||
)
|
||||
self.addCleanup(self.shares_v2_client.cgsnapshot_reset_state,
|
||||
cgsnapshot['id'],
|
||||
status='available')
|
||||
status='available',
|
||||
version='2.4')
|
||||
|
||||
# creating
|
||||
self.shares_client.cgsnapshot_reset_state(cgsnapshot['id'],
|
||||
status='creating')
|
||||
self.shares_client.wait_for_cgsnapshot_status(cgsnapshot['id'],
|
||||
'creating')
|
||||
self.shares_v2_client.cgsnapshot_reset_state(cgsnapshot['id'],
|
||||
status='creating',
|
||||
version='2.4')
|
||||
self.shares_v2_client.wait_for_cgsnapshot_status(cgsnapshot['id'],
|
||||
'creating')
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.shares_client.delete_cgsnapshot,
|
||||
cgsnapshot['id'])
|
||||
self.shares_v2_client.delete_cgsnapshot,
|
||||
cgsnapshot['id'],
|
||||
version='2.4')
|
||||
# deleting
|
||||
self.shares_client.cgsnapshot_reset_state(cgsnapshot['id'],
|
||||
status='deleting')
|
||||
self.shares_client.wait_for_cgsnapshot_status(cgsnapshot['id'],
|
||||
'deleting')
|
||||
self.shares_v2_client.cgsnapshot_reset_state(cgsnapshot['id'],
|
||||
status='deleting',
|
||||
version='2.4')
|
||||
self.shares_v2_client.wait_for_cgsnapshot_status(cgsnapshot['id'],
|
||||
'deleting')
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.shares_client.delete_cgsnapshot,
|
||||
cgsnapshot['id'])
|
||||
self.shares_v2_client.delete_cgsnapshot,
|
||||
cgsnapshot['id'],
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "gate", ])
|
||||
def test_delete_cg_not_in_available_or_error(self):
|
||||
def test_delete_cg_not_in_available_or_error_v2_4(self):
|
||||
consistency_group = self.create_consistency_group(
|
||||
cleanup_in_class=False)
|
||||
self.addCleanup(self.shares_client.consistency_group_reset_state,
|
||||
cleanup_in_class=False, version='2.4')
|
||||
self.addCleanup(self.shares_v2_client.consistency_group_reset_state,
|
||||
consistency_group['id'],
|
||||
status='available')
|
||||
status='available',
|
||||
version='2.4')
|
||||
# creating
|
||||
self.shares_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='creating')
|
||||
self.shares_client.wait_for_consistency_group_status(
|
||||
self.shares_v2_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='creating', version='2.4')
|
||||
self.shares_v2_client.wait_for_consistency_group_status(
|
||||
consistency_group['id'], 'creating')
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.shares_client.delete_consistency_group,
|
||||
consistency_group['id'])
|
||||
self.shares_v2_client.delete_consistency_group,
|
||||
consistency_group['id'],
|
||||
version='2.4')
|
||||
# deleting
|
||||
self.shares_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='deleting')
|
||||
self.shares_client.wait_for_consistency_group_status(
|
||||
self.shares_v2_client.consistency_group_reset_state(
|
||||
consistency_group['id'], status='deleting', version='2.4')
|
||||
self.shares_v2_client.wait_for_consistency_group_status(
|
||||
consistency_group['id'], 'deleting')
|
||||
self.assertRaises(exceptions.Conflict,
|
||||
self.shares_client.delete_consistency_group,
|
||||
consistency_group['id'])
|
||||
self.shares_v2_client.delete_consistency_group,
|
||||
consistency_group['id'],
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "gate", ])
|
||||
def test_create_cg_with_conflicting_share_types(self):
|
||||
def test_create_cg_with_conflicting_share_types_v2_4(self):
|
||||
# Create conflicting share types
|
||||
name = data_utils.rand_name("tempest-manila")
|
||||
extra_specs = {"driver_handles_share_servers": False}
|
||||
@ -237,10 +266,12 @@ class ConsistencyGroupsNegativeTest(base.BaseSharesAdminTest):
|
||||
self.create_consistency_group,
|
||||
share_type_ids=[single_tenant_share_type['id'],
|
||||
multi_tenant_share_type['id']],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "gate", ])
|
||||
def test_create_cg_with_multi_tenant_share_type_and_no_share_network(self):
|
||||
def test_create_cg_with_multi_tenant_share_type_and_no_share_network_v2_4(
|
||||
self):
|
||||
# Create multi tenant share type
|
||||
name = data_utils.rand_name("tempest-manila")
|
||||
extra_specs = {"driver_handles_share_servers": True}
|
||||
@ -248,12 +279,15 @@ class ConsistencyGroupsNegativeTest(base.BaseSharesAdminTest):
|
||||
multi_tenant_share_type = share_type['share_type']
|
||||
|
||||
def create_cg():
|
||||
cg = self.shares_client.create_consistency_group(
|
||||
share_type_ids=[multi_tenant_share_type['id']])
|
||||
cg = self.shares_v2_client.create_consistency_group(
|
||||
share_type_ids=[multi_tenant_share_type['id']],
|
||||
version='2.4'
|
||||
)
|
||||
resource = {
|
||||
"type": "consistency_group",
|
||||
"id": cg["id"],
|
||||
"client": self.shares_client}
|
||||
"client": self.shares_client
|
||||
}
|
||||
self.method_resources.insert(0, resource)
|
||||
return cg
|
||||
|
||||
@ -262,9 +296,10 @@ class ConsistencyGroupsNegativeTest(base.BaseSharesAdminTest):
|
||||
@test.attr(type=["negative", "gate", ])
|
||||
def test_update_cg_share_types(self):
|
||||
consistency_group = self.create_consistency_group(
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False, version='2.4')
|
||||
|
||||
self.assertRaises(exceptions.BadRequest,
|
||||
self.shares_client.update_consistency_group,
|
||||
self.shares_v2_client.update_consistency_group,
|
||||
consistency_group['id'],
|
||||
share_types=[self.share_type['id']])
|
||||
share_types=[self.share_type['id']],
|
||||
version='2.4')
|
||||
|
@ -37,7 +37,7 @@ class MigrationTest(base.BaseSharesAdminTest):
|
||||
raise cls.skipException(message)
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_migration_empty(self):
|
||||
def test_migration_empty_v2_5(self):
|
||||
|
||||
if not CONF.share.migration_enabled:
|
||||
raise self.skipException("Migration tests disabled. Skipping.")
|
||||
@ -60,7 +60,7 @@ class MigrationTest(base.BaseSharesAdminTest):
|
||||
|
||||
old_export_location = share['export_locations'][0]
|
||||
|
||||
share = self.migrate_share(share['id'], dest_pool)
|
||||
share = self.migrate_share(share['id'], dest_pool, version='2.5')
|
||||
|
||||
self.assertEqual(dest_pool, share['host'])
|
||||
self.assertNotEqual(old_export_location, share['export_locations'][0])
|
||||
|
78
manila_tempest_tests/tests/api/admin/test_share_instances.py
Normal file
78
manila_tempest_tests/tests/api/admin/test_share_instances.py
Normal file
@ -0,0 +1,78 @@
|
||||
# Copyright 2015 Andrew Kerr
|
||||
# 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 tempest import config
|
||||
from tempest import test
|
||||
|
||||
from manila_tempest_tests.tests.api import base
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class ShareInstancesTest(base.BaseSharesAdminTest):
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(ShareInstancesTest, cls).resource_setup()
|
||||
cls.share = cls.create_share()
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_get_instances_of_share_v2_3(self):
|
||||
"""Test that we get only the 1 share instance back for the share."""
|
||||
share_instances = self.shares_v2_client.get_instances_of_share(
|
||||
self.share['id'], version='2.3'
|
||||
)
|
||||
|
||||
self.assertEqual(1, len(share_instances),
|
||||
'Too many share instances found; expected 1, '
|
||||
'found %s' % len(share_instances))
|
||||
|
||||
si = share_instances[0]
|
||||
self.assertEqual(self.share['id'], si['share_id'],
|
||||
'Share instance %s has incorrect share id value; '
|
||||
'expected %s, got %s.' % (si['id'],
|
||||
self.share['id'],
|
||||
si['share_id']))
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_share_instances_v2_3(self):
|
||||
"""Test that we get at least the share instance back for the share."""
|
||||
share_instances = self.shares_v2_client.get_instances_of_share(
|
||||
self.share['id'], version='2.3'
|
||||
)
|
||||
|
||||
share_ids = [si['share_id'] for si in share_instances]
|
||||
|
||||
msg = 'Share instance for share %s was not found.' % self.share['id']
|
||||
self.assertIn(self.share['id'], share_ids, msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_get_share_instance_v2_3(self):
|
||||
"""Test that we get the proper keys back for the instance."""
|
||||
share_instances = self.shares_v2_client.get_instances_of_share(
|
||||
self.share['id'], version='2.3'
|
||||
)
|
||||
si = self.shares_v2_client.get_share_instance(share_instances[0]['id'],
|
||||
version='2.3')
|
||||
|
||||
expected_keys = ['host', 'share_id', 'id', 'share_network_id',
|
||||
'status', 'availability_zone', 'share_server_id',
|
||||
'export_locations', 'export_location', 'created_at']
|
||||
actual_keys = si.keys()
|
||||
self.assertEqual(sorted(expected_keys), sorted(actual_keys),
|
||||
'Share instance %s returned incorrect keys; '
|
||||
'expected %s, got %s.' % (si['id'],
|
||||
sorted(expected_keys),
|
||||
sorted(actual_keys)))
|
@ -98,7 +98,8 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
def get_client_with_isolated_creds(cls,
|
||||
name=None,
|
||||
type_of_creds="admin",
|
||||
cleanup_in_class=False):
|
||||
cleanup_in_class=False,
|
||||
client_version='1'):
|
||||
"""Creates isolated creds.
|
||||
|
||||
:param name: name, will be used for naming ic and related stuff
|
||||
@ -126,7 +127,10 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
|
||||
# create client with isolated creds
|
||||
os = clients.Manager(credentials=creds)
|
||||
client = os.shares_client
|
||||
if client_version == '1':
|
||||
client = os.shares_client
|
||||
elif client_version == '2':
|
||||
client = os.shares_v2_client
|
||||
|
||||
# Set place where will be deleted isolated creds
|
||||
ic_res = {
|
||||
@ -183,7 +187,9 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
nc = cls.os.network_client
|
||||
share_network_id = cls.provide_share_network(sc, nc)
|
||||
cls.os.shares_client.share_network_id = share_network_id
|
||||
cls.os.shares_v2_client.share_network_id = share_network_id
|
||||
cls.shares_client = cls.os.shares_client
|
||||
cls.shares_v2_client = cls.os.shares_v2_client
|
||||
|
||||
def setUp(self):
|
||||
super(BaseSharesTest, self).setUp()
|
||||
@ -281,12 +287,12 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
snapshot_id=None, description=None, metadata=None,
|
||||
share_network_id=None, share_type_id=None,
|
||||
consistency_group_id=None, client=None,
|
||||
cleanup_in_class=True, is_public=False):
|
||||
cleanup_in_class=True, is_public=False, **kwargs):
|
||||
client = client or cls.shares_client
|
||||
description = description or "Tempest's share"
|
||||
share_network_id = share_network_id or client.share_network_id or None
|
||||
metadata = metadata or {}
|
||||
kwargs = {
|
||||
kwargs.update({
|
||||
'share_protocol': share_protocol,
|
||||
'size': size,
|
||||
'name': name,
|
||||
@ -296,7 +302,7 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
'share_network_id': share_network_id,
|
||||
'share_type_id': share_type_id,
|
||||
'is_public': is_public,
|
||||
}
|
||||
})
|
||||
if consistency_group_id:
|
||||
kwargs['consistency_group_id'] = consistency_group_id
|
||||
|
||||
@ -309,9 +315,9 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
return share
|
||||
|
||||
@classmethod
|
||||
def migrate_share(cls, share_id, dest_host, client=None):
|
||||
client = client or cls.shares_client
|
||||
client.migrate_share(share_id, dest_host)
|
||||
def migrate_share(cls, share_id, dest_host, client=None, **kwargs):
|
||||
client = client or cls.shares_v2_client
|
||||
client.migrate_share(share_id, dest_host, **kwargs)
|
||||
share = client.wait_for_migration_completed(share_id, dest_host)
|
||||
return share
|
||||
|
||||
@ -383,7 +389,7 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
@classmethod
|
||||
def create_consistency_group(cls, client=None, cleanup_in_class=True,
|
||||
share_network_id=None, **kwargs):
|
||||
client = client or cls.shares_client
|
||||
client = client or cls.shares_v2_client
|
||||
kwargs['share_network_id'] = (share_network_id or
|
||||
client.share_network_id or None)
|
||||
consistency_group = client.create_consistency_group(**kwargs)
|
||||
@ -440,12 +446,15 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
@classmethod
|
||||
def create_cgsnapshot_wait_for_active(cls, consistency_group_id,
|
||||
name=None, description=None,
|
||||
client=None, cleanup_in_class=True):
|
||||
client = client or cls.shares_client
|
||||
client=None, cleanup_in_class=True,
|
||||
**kwargs):
|
||||
client = client or cls.shares_v2_client
|
||||
if description is None:
|
||||
description = "Tempest's cgsnapshot"
|
||||
cgsnapshot = client.create_cgsnapshot(consistency_group_id, name=name,
|
||||
description=description)
|
||||
cgsnapshot = client.create_cgsnapshot(consistency_group_id,
|
||||
name=name,
|
||||
description=description,
|
||||
**kwargs)
|
||||
resource = {
|
||||
"type": "cgsnapshot",
|
||||
"id": cgsnapshot["id"],
|
||||
@ -556,11 +565,12 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
client = res["client"]
|
||||
with handle_cleanup_exceptions():
|
||||
if res["type"] is "share":
|
||||
params = None
|
||||
cg_id = res.get('consistency_group_id')
|
||||
if cg_id:
|
||||
params = {'consistency_group_id': cg_id}
|
||||
client.delete_share(res_id, params=params)
|
||||
client.delete_share(res_id, params=params)
|
||||
else:
|
||||
client.delete_share(res_id)
|
||||
client.wait_for_resource_deletion(share_id=res_id)
|
||||
elif res["type"] is "snapshot":
|
||||
client.delete_snapshot(res_id)
|
||||
@ -679,6 +689,7 @@ class BaseSharesAltTest(BaseSharesTest):
|
||||
cls.os = clients.AltManager()
|
||||
alt_share_network_id = CONF.share.alt_share_network_id
|
||||
cls.os.shares_client.share_network_id = alt_share_network_id
|
||||
cls.os.shares_v2_client.share_network_id = alt_share_network_id
|
||||
super(BaseSharesAltTest, cls).resource_setup()
|
||||
|
||||
|
||||
@ -694,4 +705,5 @@ class BaseSharesAdminTest(BaseSharesTest):
|
||||
cls.os = clients.AdminManager()
|
||||
admin_share_network_id = CONF.share.admin_share_network_id
|
||||
cls.os.shares_client.share_network_id = admin_share_network_id
|
||||
cls.os.shares_v2_client.share_network_id = admin_share_network_id
|
||||
super(BaseSharesAdminTest, cls).resource_setup()
|
||||
|
@ -57,6 +57,7 @@ class ConsistencyGroupActionsTest(base.BaseSharesTest):
|
||||
size=cls.share_size,
|
||||
consistency_group_id=cls.consistency_group['id'],
|
||||
metadata={'key': 'value'},
|
||||
client=cls.shares_v2_client,
|
||||
)
|
||||
|
||||
cls.share_name2 = data_utils.rand_name("tempest-share-name")
|
||||
@ -67,6 +68,7 @@ class ConsistencyGroupActionsTest(base.BaseSharesTest):
|
||||
description=cls.share_desc2,
|
||||
size=cls.share_size2,
|
||||
consistency_group_id=cls.consistency_group['id'],
|
||||
client=cls.shares_v2_client,
|
||||
)
|
||||
|
||||
cls.cgsnap_name = data_utils.rand_name("tempest-cgsnap-name")
|
||||
@ -93,6 +95,7 @@ class ConsistencyGroupActionsTest(base.BaseSharesTest):
|
||||
description=cls.share_desc3,
|
||||
size=cls.share_size,
|
||||
consistency_group_id=cls.consistency_group2['id'],
|
||||
client=cls.shares_v2_client,
|
||||
)
|
||||
|
||||
cls.cgsnap_name2 = data_utils.rand_name("tempest-cgsnap-name")
|
||||
@ -103,11 +106,11 @@ class ConsistencyGroupActionsTest(base.BaseSharesTest):
|
||||
description=cls.cgsnap_desc2)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_get_consistency_group(self):
|
||||
def test_get_consistency_group_v2_4(self):
|
||||
|
||||
# Get consistency group
|
||||
consistency_group = self.shares_client.get_consistency_group(
|
||||
self.consistency_group['id'])
|
||||
consistency_group = self.shares_v2_client.get_consistency_group(
|
||||
self.consistency_group['id'], version='2.4')
|
||||
|
||||
# Verify keys
|
||||
actual_keys = set(consistency_group.keys())
|
||||
@ -129,10 +132,11 @@ class ConsistencyGroupActionsTest(base.BaseSharesTest):
|
||||
msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_get_share(self):
|
||||
def test_get_share_v2_4(self):
|
||||
|
||||
# Get share
|
||||
share = self.shares_client.get_share(self.share['id'])
|
||||
share = self.shares_v2_client.get_share(self.share['id'],
|
||||
version='2.4')
|
||||
|
||||
# Verify keys
|
||||
expected_keys = {"status", "description", "links", "availability_zone",
|
||||
@ -165,10 +169,11 @@ class ConsistencyGroupActionsTest(base.BaseSharesTest):
|
||||
self.consistency_group["id"], share["consistency_group_id"], msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_consistency_groups(self):
|
||||
def test_list_consistency_groups_v2_4(self):
|
||||
|
||||
# List consistency groups
|
||||
consistency_groups = self.shares_client.list_consistency_groups()
|
||||
consistency_groups = self.shares_v2_client.list_consistency_groups(
|
||||
version='2.4')
|
||||
|
||||
# Verify keys
|
||||
[self.assertEqual(CG_SIMPLE_KEYS, set(cg.keys())) for cg in
|
||||
@ -184,11 +189,11 @@ class ConsistencyGroupActionsTest(base.BaseSharesTest):
|
||||
self.assertEqual(1, len(gen), msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_list_consistency_groups_with_detail(self):
|
||||
def test_list_consistency_groups_with_detail_v2_4(self):
|
||||
|
||||
# List consistency groups
|
||||
consistency_groups = self.shares_client.list_consistency_groups(
|
||||
detailed=True)
|
||||
consistency_groups = self.shares_v2_client.list_consistency_groups(
|
||||
detailed=True, version='2.4')
|
||||
|
||||
# Verify keys
|
||||
[self.assertTrue(CG_DETAIL_REQUIRED_KEYS.issubset(set(cg.keys())))
|
||||
@ -204,10 +209,13 @@ class ConsistencyGroupActionsTest(base.BaseSharesTest):
|
||||
self.assertEqual(1, len(gen), msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_filter_shares_by_consistency_group_id(self):
|
||||
def test_filter_shares_by_consistency_group_id_v2_4(self):
|
||||
|
||||
shares = self.shares_client.list_shares(detailed=True, params={
|
||||
'consistency_group_id': self.consistency_group['id']})
|
||||
shares = self.shares_v2_client.list_shares(
|
||||
detailed=True,
|
||||
params={'consistency_group_id': self.consistency_group['id']},
|
||||
version='2.4'
|
||||
)
|
||||
|
||||
share_ids = [share['id'] for share in shares]
|
||||
|
||||
@ -222,10 +230,10 @@ class ConsistencyGroupActionsTest(base.BaseSharesTest):
|
||||
% (self.share['id'], share_ids))
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_get_cgsnapshot(self):
|
||||
def test_get_cgsnapshot_v2_4(self):
|
||||
# Get consistency group
|
||||
consistency_group = self.shares_client.get_consistency_group(
|
||||
self.consistency_group['id'])
|
||||
consistency_group = self.shares_v2_client.get_consistency_group(
|
||||
self.consistency_group['id'], version='2.4')
|
||||
|
||||
# Verify keys
|
||||
actual_keys = set(consistency_group.keys())
|
||||
@ -247,10 +255,10 @@ class ConsistencyGroupActionsTest(base.BaseSharesTest):
|
||||
msg)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_get_cgsnapshot_members(self):
|
||||
def test_get_cgsnapshot_members_v2_4(self):
|
||||
|
||||
cgsnapshot_members = self.shares_client.list_cgsnapshot_members(
|
||||
self.cgsnapshot['id'])
|
||||
cgsnapshot_members = self.shares_v2_client.list_cgsnapshot_members(
|
||||
self.cgsnapshot['id'], version='2.4')
|
||||
member_share_ids = [member['share_id'] for member in
|
||||
cgsnapshot_members]
|
||||
self.assertEqual(2, len(cgsnapshot_members),
|
||||
@ -272,17 +280,22 @@ class ConsistencyGroupActionsTest(base.BaseSharesTest):
|
||||
# member['share_type_id'])
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_create_consistency_group_from_populated_cgsnapshot(self):
|
||||
def test_create_consistency_group_from_populated_cgsnapshot_v2_4(self):
|
||||
|
||||
cgsnapshot_members = self.shares_client.list_cgsnapshot_members(
|
||||
self.cgsnapshot['id'])
|
||||
cgsnapshot_members = self.shares_v2_client.list_cgsnapshot_members(
|
||||
self.cgsnapshot['id'], version='2.4')
|
||||
|
||||
new_consistency_group = self.create_consistency_group(
|
||||
cleanup_in_class=False, source_cgsnapshot_id=self.cgsnapshot['id'])
|
||||
cleanup_in_class=False,
|
||||
source_cgsnapshot_id=self.cgsnapshot['id'],
|
||||
version='2.4'
|
||||
)
|
||||
|
||||
new_shares = self.shares_client.list_shares(
|
||||
new_shares = self.shares_v2_client.list_shares(
|
||||
params={'consistency_group_id': new_consistency_group['id']},
|
||||
detailed=True)
|
||||
detailed=True,
|
||||
version='2.4'
|
||||
)
|
||||
|
||||
# Verify each new share is available
|
||||
for share in new_shares:
|
||||
@ -326,46 +339,58 @@ class ConsistencyGroupRenameTest(base.BaseSharesTest):
|
||||
)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_update_consistency_group(self):
|
||||
def test_update_consistency_group_v2_4(self):
|
||||
|
||||
# Get consistency_group
|
||||
consistency_group = self.shares_client.get_consistency_group(
|
||||
self.consistency_group['id'])
|
||||
consistency_group = self.shares_v2_client.get_consistency_group(
|
||||
self.consistency_group['id'], version='2.4')
|
||||
self.assertEqual(self.cg_name, consistency_group["name"])
|
||||
self.assertEqual(self.cg_desc, consistency_group["description"])
|
||||
|
||||
# Update consistency_group
|
||||
new_name = data_utils.rand_name("tempest-new-name")
|
||||
new_desc = data_utils.rand_name("tempest-new-description")
|
||||
updated = self.shares_client.update_consistency_group(
|
||||
consistency_group["id"], name=new_name, description=new_desc)
|
||||
updated = self.shares_v2_client.update_consistency_group(
|
||||
consistency_group["id"],
|
||||
name=new_name,
|
||||
description=new_desc,
|
||||
version='2.4'
|
||||
)
|
||||
self.assertEqual(new_name, updated["name"])
|
||||
self.assertEqual(new_desc, updated["description"])
|
||||
|
||||
# Get consistency_group
|
||||
consistency_group = self.shares_client.get_consistency_group(
|
||||
self.consistency_group['id'])
|
||||
consistency_group = self.shares_v2_client.get_consistency_group(
|
||||
self.consistency_group['id'], version='2.4')
|
||||
self.assertEqual(new_name, consistency_group["name"])
|
||||
self.assertEqual(new_desc, consistency_group["description"])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_create_update_read_consistency_group_with_unicode(self):
|
||||
def test_create_update_read_consistency_group_with_unicode_v2_4(self):
|
||||
value1 = u'ಠ_ಠ'
|
||||
value2 = u'ಠ_ರೃ'
|
||||
# Create consistency_group
|
||||
consistency_group = self.create_consistency_group(
|
||||
cleanup_in_class=False, name=value1, description=value1)
|
||||
cleanup_in_class=False,
|
||||
name=value1,
|
||||
description=value1,
|
||||
version='2.4'
|
||||
)
|
||||
self.assertEqual(value1, consistency_group["name"])
|
||||
self.assertEqual(value1, consistency_group["description"])
|
||||
|
||||
# Update consistency_group
|
||||
updated = self.shares_client.update_consistency_group(
|
||||
consistency_group["id"], name=value2, description=value2)
|
||||
updated = self.shares_v2_client.update_consistency_group(
|
||||
consistency_group["id"],
|
||||
name=value2,
|
||||
description=value2,
|
||||
version='2.4'
|
||||
)
|
||||
self.assertEqual(value2, updated["name"])
|
||||
self.assertEqual(value2, updated["description"])
|
||||
|
||||
# Get consistency_group
|
||||
consistency_group = self.shares_client.get_consistency_group(
|
||||
consistency_group['id'])
|
||||
consistency_group = self.shares_v2_client.get_consistency_group(
|
||||
consistency_group['id'], version='2.4')
|
||||
self.assertEqual(value2, consistency_group["name"])
|
||||
self.assertEqual(value2, consistency_group["description"])
|
||||
|
@ -33,10 +33,10 @@ class ConsistencyGroupsTest(base.BaseSharesTest):
|
||||
"""Covers consistency group functionality."""
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_create_populate_delete_consistency_group(self):
|
||||
def test_create_populate_delete_consistency_group_v2_4(self):
|
||||
# Create a consistency group
|
||||
consistency_group = self.create_consistency_group(
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False, version='2.4')
|
||||
self.assertTrue(CG_REQUIRED_ELEMENTS.issubset(
|
||||
consistency_group.keys()),
|
||||
'At least one expected element missing from consistency group '
|
||||
@ -45,31 +45,35 @@ class ConsistencyGroupsTest(base.BaseSharesTest):
|
||||
"actual": consistency_group.keys()})
|
||||
# Populate
|
||||
share = self.create_share(consistency_group_id=consistency_group['id'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
client=self.shares_v2_client,
|
||||
version='2.4')
|
||||
# Delete
|
||||
params = {"consistency_group_id": consistency_group['id']}
|
||||
self.shares_client.delete_share(share['id'], params=params)
|
||||
self.shares_v2_client.delete_share(share['id'], params=params,
|
||||
version='2.4')
|
||||
self.shares_client.wait_for_resource_deletion(share_id=share['id'])
|
||||
self.shares_client.delete_consistency_group(consistency_group['id'])
|
||||
self.shares_client.wait_for_resource_deletion(
|
||||
self.shares_v2_client.delete_consistency_group(consistency_group['id'],
|
||||
version='2.4')
|
||||
self.shares_v2_client.wait_for_resource_deletion(
|
||||
cg_id=consistency_group['id'])
|
||||
|
||||
# Verify
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.shares_client.get_consistency_group,
|
||||
self.shares_v2_client.get_consistency_group,
|
||||
consistency_group['id'])
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.shares_client.get_share,
|
||||
share['id'])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_create_delete_empty_cgsnapshot(self):
|
||||
def test_create_delete_empty_cgsnapshot_v2_4(self):
|
||||
# Create base consistency group
|
||||
consistency_group = self.create_consistency_group(
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False, version='2.4')
|
||||
# Create cgsnapshot
|
||||
cgsnapshot = self.create_cgsnapshot_wait_for_active(
|
||||
consistency_group["id"], cleanup_in_class=False)
|
||||
consistency_group["id"], cleanup_in_class=False, version='2.4')
|
||||
|
||||
self.assertTrue(CGSNAPSHOT_REQUIRED_ELEMENTS.issubset(
|
||||
cgsnapshot.keys()),
|
||||
@ -78,19 +82,22 @@ class ConsistencyGroupsTest(base.BaseSharesTest):
|
||||
"expected": CGSNAPSHOT_REQUIRED_ELEMENTS,
|
||||
"actual": cgsnapshot.keys()})
|
||||
|
||||
cgsnapshot_members = self.shares_client.list_cgsnapshot_members(
|
||||
cgsnapshot['id'])
|
||||
cgsnapshot_members = self.shares_v2_client.list_cgsnapshot_members(
|
||||
cgsnapshot['id'], version='2.4')
|
||||
|
||||
self.assertEmpty(cgsnapshot_members,
|
||||
'Expected 0 cgsnapshot members, got %s' % len(
|
||||
cgsnapshot_members))
|
||||
|
||||
# delete snapshot
|
||||
self.shares_client.delete_cgsnapshot(cgsnapshot["id"])
|
||||
self.shares_client.wait_for_resource_deletion(
|
||||
self.shares_v2_client.delete_cgsnapshot(cgsnapshot["id"],
|
||||
version='2.4')
|
||||
self.shares_v2_client.wait_for_resource_deletion(
|
||||
cgsnapshot_id=cgsnapshot["id"])
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.shares_client.get_cgsnapshot, cgsnapshot['id'])
|
||||
self.shares_v2_client.get_cgsnapshot,
|
||||
cgsnapshot['id'],
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_create_consistency_group_from_empty_cgsnapshot(self):
|
||||
@ -102,7 +109,7 @@ class ConsistencyGroupsTest(base.BaseSharesTest):
|
||||
cgsnapshot = self.create_cgsnapshot_wait_for_active(
|
||||
consistency_group["id"], cleanup_in_class=False)
|
||||
|
||||
cgsnapshot_members = self.shares_client.list_cgsnapshot_members(
|
||||
cgsnapshot_members = self.shares_v2_client.list_cgsnapshot_members(
|
||||
cgsnapshot['id'])
|
||||
|
||||
self.assertEmpty(cgsnapshot_members,
|
||||
@ -123,8 +130,8 @@ class ConsistencyGroupsTest(base.BaseSharesTest):
|
||||
self.assertEqual(new_consistency_group['source_cgsnapshot_id'],
|
||||
cgsnapshot['id'], msg)
|
||||
|
||||
msg = 'Unexpected share_types on new consistency group. Expected %s, ' \
|
||||
'got %s.' % (consistency_group['share_types'],
|
||||
new_consistency_group['share_types'])
|
||||
msg = ('Unexpected share_types on new consistency group. Expected '
|
||||
'%s, got %s.' % (consistency_group['share_types'],
|
||||
new_consistency_group['share_types']))
|
||||
self.assertEqual(sorted(consistency_group['share_types']),
|
||||
sorted(new_consistency_group['share_types']), msg)
|
||||
|
@ -46,7 +46,8 @@ class ConsistencyGroupsNegativeTest(base.BaseSharesTest):
|
||||
name=cls.share_name,
|
||||
description=cls.share_desc,
|
||||
size=cls.share_size,
|
||||
consistency_group_id=cls.consistency_group['id']
|
||||
consistency_group_id=cls.consistency_group['id'],
|
||||
client=cls.shares_v2_client
|
||||
)
|
||||
# Create a cgsnapshot of the consistency group
|
||||
cls.cgsnap_name = data_utils.rand_name("tempest-cgsnap-name")
|
||||
@ -57,149 +58,174 @@ class ConsistencyGroupsNegativeTest(base.BaseSharesTest):
|
||||
description=cls.cgsnap_desc)
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_create_cg_with_invalid_source_cgsnapshot_id_value(
|
||||
def test_create_cg_with_invalid_source_cgsnapshot_id_value_v2_4(
|
||||
self):
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.create_consistency_group,
|
||||
source_cgsnapshot_id='foobar',
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_create_cg_with_nonexistent_source_cgsnapshot_id_value(self):
|
||||
def test_create_cg_with_nonexistent_source_cgsnapshot_id_value_v2_4(self):
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.create_consistency_group,
|
||||
source_cgsnapshot_id=self.share['id'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_create_cg_with_invalid_share_network_id_value(
|
||||
self):
|
||||
def test_create_cg_with_invalid_share_network_id_value_v2_4(self):
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.create_consistency_group,
|
||||
share_network_id='foobar',
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_create_cg_with_nonexistent_share_network_id_value(self):
|
||||
def test_create_cg_with_nonexistent_share_network_id_value_v2_4(self):
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.create_consistency_group,
|
||||
share_network_id=self.share['id'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_create_cg_with_invalid_share_type_id_value(
|
||||
self):
|
||||
def test_create_cg_with_invalid_share_type_id_value_v2_4(self):
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.create_consistency_group,
|
||||
share_type_ids=['foobar'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_create_cg_with_nonexistent_share_type_id_value(self):
|
||||
def test_create_cg_with_nonexistent_share_type_id_value_v2_4(self):
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.create_consistency_group,
|
||||
share_type_ids=[self.share['id']],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_create_cgsnapshot_with_invalid_cg_id_value(
|
||||
self):
|
||||
def test_create_cgsnapshot_with_invalid_cg_id_value_v2_4(self):
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.create_cgsnapshot_wait_for_active,
|
||||
'foobar',
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_create_cgsnapshot_with_nonexistent_cg_id_value(self):
|
||||
def test_create_cgsnapshot_with_nonexistent_cg_id_value_v2_4(self):
|
||||
self.assertRaises(lib_exc.BadRequest,
|
||||
self.create_cgsnapshot_wait_for_active,
|
||||
self.share['id'],
|
||||
cleanup_in_class=False)
|
||||
cleanup_in_class=False,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_get_cg_with_wrong_id(self):
|
||||
def test_get_cg_with_wrong_id_v2_4(self):
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.shares_client.get_consistency_group,
|
||||
"wrong_consistency_group_id")
|
||||
self.shares_v2_client.get_consistency_group,
|
||||
"wrong_consistency_group_id",
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_get_cg_without_passing_cg_id(self):
|
||||
def test_get_cg_without_passing_cg_id_v2_4(self):
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.shares_client.get_consistency_group, '')
|
||||
self.shares_v2_client.get_consistency_group,
|
||||
'',
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_update_cg_with_wrong_id(self):
|
||||
def test_update_cg_with_wrong_id_v2_4(self):
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.shares_client.update_consistency_group,
|
||||
self.shares_v2_client.update_consistency_group,
|
||||
'wrong_consistency_group_id',
|
||||
name='new_name',
|
||||
description='new_description')
|
||||
description='new_description',
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_delete_cg_with_wrong_id(self):
|
||||
def test_delete_cg_with_wrong_id_v2_4(self):
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.shares_client.delete_consistency_group,
|
||||
"wrong_consistency_group_id")
|
||||
self.shares_v2_client.delete_consistency_group,
|
||||
"wrong_consistency_group_id",
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_delete_cg_without_passing_cg_id(self):
|
||||
def test_delete_cg_without_passing_cg_id_v2_4(self):
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.shares_client.delete_consistency_group, '')
|
||||
self.shares_v2_client.delete_consistency_group,
|
||||
'',
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "gate", ])
|
||||
def test_delete_cg_in_use_by_cgsnapshot(self):
|
||||
def test_delete_cg_in_use_by_cgsnapshot_v2_4(self):
|
||||
# Attempt delete of share type
|
||||
self.assertRaises(lib_exc.Conflict,
|
||||
self.shares_client.delete_consistency_group,
|
||||
self.consistency_group['id'])
|
||||
self.shares_v2_client.delete_consistency_group,
|
||||
self.consistency_group['id'],
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "gate", ])
|
||||
def test_delete_share_in_use_by_cgsnapshot(self):
|
||||
def test_delete_share_in_use_by_cgsnapshot_v2_4(self):
|
||||
# Attempt delete of share type
|
||||
params = {'consistency_group_id': self.share['consistency_group_id']}
|
||||
self.assertRaises(lib_exc.Forbidden,
|
||||
self.shares_client.delete_share,
|
||||
self.shares_v2_client.delete_share,
|
||||
self.share['id'],
|
||||
params=params)
|
||||
params=params,
|
||||
version='2.4')
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_delete_cg_containing_a_share(self):
|
||||
def test_delete_cg_containing_a_share_v2_4(self):
|
||||
self.assertRaises(lib_exc.Conflict,
|
||||
self.shares_client.delete_consistency_group,
|
||||
self.consistency_group['id'])
|
||||
self.shares_v2_client.delete_consistency_group,
|
||||
self.consistency_group['id'],
|
||||
version='2.4')
|
||||
# Verify consistency group is not put into error state from conflict
|
||||
cg = self.shares_client.get_consistency_group(
|
||||
self.consistency_group['id'])
|
||||
cg = self.shares_v2_client.get_consistency_group(
|
||||
self.consistency_group['id'], version='2.4')
|
||||
self.assertEqual('available', cg['status'])
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_filter_shares_on_invalid_cg_id(self):
|
||||
shares = self.shares_client.list_shares(detailed=True, params={
|
||||
'consistency_group_id': 'foobar'})
|
||||
def test_filter_shares_on_invalid_cg_id_v2_4(self):
|
||||
shares = self.shares_v2_client.list_shares(
|
||||
detailed=True,
|
||||
params={'consistency_group_id': 'foobar'},
|
||||
version='2.4'
|
||||
)
|
||||
|
||||
self.assertEqual(0, len(shares), 'Incorrect number of shares '
|
||||
'returned. Expected 0, got %s.' %
|
||||
len(shares))
|
||||
self.assertEqual(0, len(shares),
|
||||
'Incorrect number of shares returned. Expected 0, '
|
||||
'got %s.' % len(shares))
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_filter_shares_on_nonexistent_cg_id(self):
|
||||
shares = self.shares_client.list_shares(detailed=True, params={
|
||||
'consistency_group_id': self.share['id']})
|
||||
def test_filter_shares_on_nonexistent_cg_id_v2_4(self):
|
||||
shares = self.shares_v2_client.list_shares(
|
||||
detailed=True,
|
||||
params={'consistency_group_id': self.share['id']},
|
||||
version='2.4'
|
||||
)
|
||||
|
||||
self.assertEqual(0, len(shares), 'Incorrect number of shares '
|
||||
'returned. Expected 0, got %s.' %
|
||||
len(shares))
|
||||
self.assertEqual(0, len(shares),
|
||||
'Incorrect number of shares returned. Expected 0, '
|
||||
'got %s.' % len(shares))
|
||||
|
||||
@test.attr(type=["negative", "smoke", "gate", ])
|
||||
def test_filter_shares_on_empty_cg_id(self):
|
||||
def test_filter_shares_on_empty_cg_id_v2_4(self):
|
||||
consistency_group = self.create_consistency_group(
|
||||
name='tempest_cg',
|
||||
description='tempest_cg_desc',
|
||||
cleanup_in_class=False,
|
||||
version='2.4',
|
||||
)
|
||||
shares = self.shares_v2_client.list_shares(
|
||||
detailed=True,
|
||||
params={'consistency_group_id': consistency_group['id']},
|
||||
version='2.4',
|
||||
)
|
||||
shares = self.shares_client.list_shares(detailed=True, params={
|
||||
'consistency_group_id': consistency_group['id']})
|
||||
|
||||
self.assertEqual(0, len(shares), 'Incorrect number of shares '
|
||||
'returned. Expected 0, got %s.' %
|
||||
len(shares))
|
||||
self.assertEqual(0, len(shares),
|
||||
'Incorrect number of shares returned. Expected 0, '
|
||||
'got %s.' % len(shares))
|
||||
|
@ -1,4 +1,5 @@
|
||||
# Copyright 2015 Goutham Pacha Ravi
|
||||
# Copyright 2015 Clinton Knight
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -20,90 +21,152 @@ from manila_tempest_tests.tests.api import base
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
API_MICROVERSIONS_HEADER_LOWER = 'x-openstack-manila-api-version'
|
||||
API_MICROVERSIONS_HEADER = 'X-OpenStack-Manila-API-Version'
|
||||
_MIN_API_VERSION = CONF.share.min_api_microversion
|
||||
_MAX_API_VERSION = CONF.share.max_api_microversion
|
||||
|
||||
|
||||
class MicroversionsTest(base.BaseSharesTest):
|
||||
"""Request and validate REST API Microversions.
|
||||
|
||||
Sends a HTTP GET request with the base endpoint to request a Microversion.
|
||||
Sends HTTP GET requests to the version API to validate microversions.
|
||||
"""
|
||||
|
||||
_MIN_API_VERSION = CONF.share.min_api_microversion
|
||||
_MAX_API_VERSION = CONF.share.max_api_microversion
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_microversions_root_version(self):
|
||||
|
||||
resp, resp_body = self.shares_v2_client.send_microversion_request()
|
||||
|
||||
self.assertEqual(300, resp.status)
|
||||
|
||||
version_list = resp_body['versions']
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v1.0', 'v2.0'}, set(ids))
|
||||
|
||||
self.assertNotIn(API_MICROVERSIONS_HEADER_LOWER, resp)
|
||||
self.assertNotIn('vary', resp)
|
||||
|
||||
v1 = [v for v in version_list if v['id'] == 'v1.0'][0]
|
||||
self.assertEqual('', v1.get('min_version'))
|
||||
self.assertEqual('', v1.get('version'))
|
||||
|
||||
v2 = [v for v in version_list if v['id'] == 'v2.0'][0]
|
||||
self.assertEqual(_MIN_API_VERSION, v2.get('min_version'))
|
||||
self.assertEqual(_MAX_API_VERSION, v2.get('version'))
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_microversions_no_version(self):
|
||||
resp, resp_body = self.shares_client.send_microversion_request()
|
||||
def test_microversions_v1_no_version(self):
|
||||
|
||||
self.assertEqual(self._MIN_API_VERSION,
|
||||
resp[self.shares_client.API_MICROVERSIONS_HEADER])
|
||||
self.assertTrue(len(resp_body['versions']) > 0)
|
||||
self.assertNotIn('min_version', resp_body['versions'][0])
|
||||
self.assertNotIn('version', resp_body['versions'][0])
|
||||
resp, resp_body = self.shares_v2_client.send_microversion_request(
|
||||
script_name='v1')
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
|
||||
version_list = resp_body['versions']
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v1.0'}, set(ids))
|
||||
|
||||
self.assertEqual('1.0', resp.get(API_MICROVERSIONS_HEADER_LOWER))
|
||||
self.assertEqual(API_MICROVERSIONS_HEADER, resp.get('vary'))
|
||||
self.assertEqual('', version_list[0].get('min_version'))
|
||||
self.assertEqual('', version_list[0].get('version'))
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_microversions_version_min_version(self):
|
||||
"""Requests base version 1.0."""
|
||||
def test_microversions_v1_with_version(self):
|
||||
|
||||
resp, resp_body = self.shares_client.send_microversion_request(
|
||||
self._MIN_API_VERSION)
|
||||
resp, resp_body = self.shares_v2_client.send_microversion_request(
|
||||
script_name='v1', version='5.0')
|
||||
|
||||
self.assertEqual(self._MIN_API_VERSION,
|
||||
resp[self.shares_client.API_MICROVERSIONS_HEADER])
|
||||
self.assertTrue(len(resp_body['versions']) > 0)
|
||||
self.assertNotIn('min_version', resp_body['versions'][0])
|
||||
self.assertNotIn('version', resp_body['versions'][0])
|
||||
self.assertEqual(200, resp.status)
|
||||
|
||||
version_list = resp_body['versions']
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v1.0'}, set(ids))
|
||||
|
||||
self.assertEqual('1.0', resp.get(API_MICROVERSIONS_HEADER_LOWER))
|
||||
self.assertEqual(API_MICROVERSIONS_HEADER, resp.get('vary'))
|
||||
self.assertEqual('', version_list[0].get('min_version'))
|
||||
self.assertEqual('', version_list[0].get('version'))
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_microversions_version_max_configured_version(self):
|
||||
"""Requests maximum API microversion.
|
||||
def test_microversions_v2_no_version(self):
|
||||
|
||||
Requests the current maximum API microversion from the Manila API
|
||||
service, and confirms that version is the same as what Tempest is
|
||||
configured to request in other versioned API calls.
|
||||
"""
|
||||
resp, resp_body = self.shares_v2_client.send_microversion_request(
|
||||
script_name='v2')
|
||||
|
||||
resp, resp_body = self.shares_client.send_microversion_request(
|
||||
self._MAX_API_VERSION)
|
||||
self.assertEqual(200, resp.status)
|
||||
|
||||
self.assertEqual(self._MAX_API_VERSION,
|
||||
resp[self.shares_client.API_MICROVERSIONS_HEADER])
|
||||
self.assertTrue(len(resp_body['versions']) > 0)
|
||||
self.assertEqual(self._MAX_API_VERSION,
|
||||
resp_body['versions'][0]['version'])
|
||||
version_list = resp_body['versions']
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v2.0'}, set(ids))
|
||||
|
||||
self.assertEqual(_MIN_API_VERSION,
|
||||
resp.get(API_MICROVERSIONS_HEADER_LOWER))
|
||||
self.assertEqual(API_MICROVERSIONS_HEADER, resp.get('vary'))
|
||||
self.assertEqual(_MIN_API_VERSION, version_list[0].get('min_version'))
|
||||
self.assertEqual(_MAX_API_VERSION, version_list[0].get('version'))
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_microversions_version_1_1(self):
|
||||
"""Requests version 1.1, the first Manila microversion."""
|
||||
def test_microversions_v2_min_version(self):
|
||||
|
||||
resp, resp_body = self.shares_client.send_microversion_request('1.1')
|
||||
resp, resp_body = self.shares_v2_client.send_microversion_request(
|
||||
script_name='v2', version=_MIN_API_VERSION)
|
||||
|
||||
self.assertEqual('1.1',
|
||||
resp[self.shares_client.API_MICROVERSIONS_HEADER])
|
||||
self.assertTrue(len(resp_body['versions']) > 0)
|
||||
self.assertEqual(self._MIN_API_VERSION,
|
||||
resp_body['versions'][0]['min_version'])
|
||||
self.assertEqual(200, resp.status)
|
||||
|
||||
version_list = resp_body['versions']
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v2.0'}, set(ids))
|
||||
|
||||
self.assertEqual(_MIN_API_VERSION,
|
||||
resp.get(API_MICROVERSIONS_HEADER_LOWER))
|
||||
self.assertEqual(API_MICROVERSIONS_HEADER, resp.get('vary'))
|
||||
self.assertEqual(_MIN_API_VERSION, version_list[0].get('min_version'))
|
||||
self.assertEqual(_MAX_API_VERSION, version_list[0].get('version'))
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_microversions_unavailable_versions(self):
|
||||
"""Requests a version greater than the latest available version."""
|
||||
def test_microversions_v2_max_version(self):
|
||||
|
||||
resp, resp_body = self.shares_client.send_microversion_request('1.1')
|
||||
self.assertTrue(len(resp_body['versions']) > 0)
|
||||
major_ver, minor_ver = [int(ver) for ver in
|
||||
resp_body['versions'][0]['version'].split(".")]
|
||||
req_version = ('%s.%s' % (major_ver + 1, minor_ver + 1))
|
||||
resp, _ = self.shares_client.send_microversion_request(req_version)
|
||||
resp, resp_body = self.shares_v2_client.send_microversion_request(
|
||||
script_name='v2', version=_MAX_API_VERSION)
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
|
||||
version_list = resp_body['versions']
|
||||
ids = [v['id'] for v in version_list]
|
||||
self.assertEqual({'v2.0'}, set(ids))
|
||||
|
||||
self.assertEqual(_MAX_API_VERSION,
|
||||
resp.get(API_MICROVERSIONS_HEADER_LOWER))
|
||||
self.assertEqual(API_MICROVERSIONS_HEADER, resp.get('vary'))
|
||||
self.assertEqual(_MIN_API_VERSION, version_list[0].get('min_version'))
|
||||
self.assertEqual(_MAX_API_VERSION, version_list[0].get('version'))
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_microversions_v2_invalid_version(self):
|
||||
|
||||
resp, _ = self.shares_v2_client.send_microversion_request(
|
||||
script_name='v2', version='1.2.1')
|
||||
|
||||
self.assertEqual(400, resp.status)
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_microversions_v2_unacceptable_version(self):
|
||||
|
||||
# First get max version from the server
|
||||
resp, resp_body = self.shares_v2_client.send_microversion_request(
|
||||
script_name='v2')
|
||||
|
||||
self.assertEqual(200, resp.status)
|
||||
|
||||
version_list = resp_body['versions']
|
||||
latest_version = version_list[0].get('version')
|
||||
major, minor = [int(ver) for ver in latest_version.split(".")]
|
||||
next_version = ('%s.%s' % (major + 1, minor + 1))
|
||||
|
||||
# Request a version that is too high
|
||||
resp, _ = self.shares_v2_client.send_microversion_request(
|
||||
script_name='v2', version=next_version)
|
||||
|
||||
self.assertEqual(406, resp.status)
|
||||
|
||||
@test.attr(type=["gate", "smoke", ])
|
||||
def test_microversions_invalid_versions(self):
|
||||
"""Requests invalid versions."""
|
||||
|
||||
resp, resp_body = self.shares_client.send_microversion_request('1.2.1')
|
||||
|
||||
self.assertEqual(400, resp.status)
|
||||
|
||||
resp, _ = self.shares_client.send_microversion_request('None')
|
||||
|
||||
self.assertEqual(400, resp.status)
|
||||
|
Loading…
Reference in New Issue
Block a user