cinder/cinder/tests/unit/api/contrib/test_consistencygroups.py

1244 lines
54 KiB
Python

# Copyright (C) 2012 - 2014 EMC Corporation.
# 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.
"""
Tests for consistency group code.
"""
import ddt
import json
from xml.dom import minidom
import mock
import webob
import cinder.consistencygroup
from cinder import context
from cinder import db
from cinder import exception
from cinder.i18n import _
from cinder import objects
from cinder.objects import fields
from cinder import test
from cinder.tests.unit.api import fakes
from cinder.tests.unit.api.v2 import stubs
from cinder.tests.unit import utils
from cinder.volume import api as volume_api
@ddt.ddt
class ConsistencyGroupsAPITestCase(test.TestCase):
"""Test Case for consistency groups API."""
def setUp(self):
super(ConsistencyGroupsAPITestCase, self).setUp()
self.cg_api = cinder.consistencygroup.API()
self.ctxt = context.RequestContext('fake', 'fake', auth_token=True,
is_admin=True)
def _create_consistencygroup(
self,
ctxt=None,
name='test_consistencygroup',
description='this is a test consistency group',
volume_type_id='123456',
availability_zone='az1',
host='fakehost',
status=fields.ConsistencyGroupStatus.CREATING):
"""Create a consistency group object."""
ctxt = ctxt or self.ctxt
consistencygroup = objects.ConsistencyGroup(ctxt)
consistencygroup.user_id = 'fake'
consistencygroup.project_id = 'fake'
consistencygroup.availability_zone = availability_zone
consistencygroup.name = name
consistencygroup.description = description
consistencygroup.volume_type_id = volume_type_id
consistencygroup.host = host
consistencygroup.status = status
consistencygroup.create()
return consistencygroup
def test_show_consistencygroup(self):
consistencygroup = self._create_consistencygroup()
req = webob.Request.blank('/v2/fake/consistencygroups/%s' %
consistencygroup.id)
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
self.assertEqual('az1',
res_dict['consistencygroup']['availability_zone'])
self.assertEqual('this is a test consistency group',
res_dict['consistencygroup']['description'])
self.assertEqual('test_consistencygroup',
res_dict['consistencygroup']['name'])
self.assertEqual('creating',
res_dict['consistencygroup']['status'])
self.assertEqual(['123456'],
res_dict['consistencygroup']['volume_types'])
consistencygroup.destroy()
def test_show_consistencygroup_xml_content_type(self):
consistencygroup = self._create_consistencygroup()
req = webob.Request.blank('/v2/fake/consistencygroups/%s' %
consistencygroup.id)
req.method = 'GET'
req.headers['Content-Type'] = 'application/xml'
req.headers['Accept'] = 'application/xml'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(200, res.status_int)
dom = minidom.parseString(res.body)
consistencygroups = dom.getElementsByTagName('consistencygroup')
name = consistencygroups.item(0).getAttribute('name')
self.assertEqual("test_consistencygroup", name.strip())
consistencygroup.destroy()
def test_show_consistencygroup_with_consistencygroup_NotFound(self):
req = webob.Request.blank('/v2/fake/consistencygroups/9999')
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(404, res.status_int)
self.assertEqual(404, res_dict['itemNotFound']['code'])
self.assertEqual('ConsistencyGroup 9999 could not be found.',
res_dict['itemNotFound']['message'])
def test_show_consistencygroup_with_null_volume_type(self):
consistencygroup = self._create_consistencygroup(volume_type_id=None)
req = webob.Request.blank('/v2/fake/consistencygroups/%s' %
consistencygroup.id)
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
self.assertEqual('az1',
res_dict['consistencygroup']['availability_zone'])
self.assertEqual('this is a test consistency group',
res_dict['consistencygroup']['description'])
self.assertEqual('test_consistencygroup',
res_dict['consistencygroup']['name'])
self.assertEqual('creating',
res_dict['consistencygroup']['status'])
self.assertEqual([], res_dict['consistencygroup']['volume_types'])
consistencygroup.destroy()
def test_list_consistencygroups_json(self):
consistencygroup1 = self._create_consistencygroup()
consistencygroup2 = self._create_consistencygroup()
consistencygroup3 = self._create_consistencygroup()
req = webob.Request.blank('/v2/fake/consistencygroups')
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
self.assertEqual(consistencygroup3.id,
res_dict['consistencygroups'][0]['id'])
self.assertEqual('test_consistencygroup',
res_dict['consistencygroups'][0]['name'])
self.assertEqual(consistencygroup2.id,
res_dict['consistencygroups'][1]['id'])
self.assertEqual('test_consistencygroup',
res_dict['consistencygroups'][1]['name'])
self.assertEqual(consistencygroup1.id,
res_dict['consistencygroups'][2]['id'])
self.assertEqual('test_consistencygroup',
res_dict['consistencygroups'][2]['name'])
consistencygroup1.destroy()
consistencygroup2.destroy()
consistencygroup3.destroy()
def test_list_consistencygroups_xml(self):
consistencygroup1 = self._create_consistencygroup()
consistencygroup2 = self._create_consistencygroup()
consistencygroup3 = self._create_consistencygroup()
req = webob.Request.blank('/v2/fake/consistencygroups')
req.method = 'GET'
req.headers['Content-Type'] = 'application/xml'
req.headers['Accept'] = 'application/xml'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(200, res.status_int)
dom = minidom.parseString(res.body)
consistencygroup_list = dom.getElementsByTagName('consistencygroup')
self.assertEqual(consistencygroup3.id,
consistencygroup_list.item(0).getAttribute('id'))
self.assertEqual(consistencygroup2.id,
consistencygroup_list.item(1).getAttribute('id'))
self.assertEqual(consistencygroup1.id,
consistencygroup_list.item(2).getAttribute('id'))
consistencygroup3.destroy()
consistencygroup2.destroy()
consistencygroup1.destroy()
@ddt.data(False, True)
def test_list_consistencygroups_with_limit(self, is_detail):
consistencygroup1 = self._create_consistencygroup()
consistencygroup2 = self._create_consistencygroup()
consistencygroup3 = self._create_consistencygroup()
url = '/v2/fake/consistencygroups?limit=1'
if is_detail:
url = '/v2/fake/consistencygroups/detail?limit=1'
req = webob.Request.blank(url)
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
self.assertEqual(1, len(res_dict['consistencygroups']))
self.assertEqual(consistencygroup3.id,
res_dict['consistencygroups'][0]['id'])
next_link = ('http://localhost/v2/fake/consistencygroups?limit='
'1&marker=%s') % res_dict['consistencygroups'][0]['id']
self.assertEqual(next_link,
res_dict['consistencygroup_links'][0]['href'])
consistencygroup1.destroy()
consistencygroup2.destroy()
consistencygroup3.destroy()
@ddt.data(False, True)
def test_list_consistencygroups_with_offset(self, is_detail):
consistencygroup1 = self._create_consistencygroup()
consistencygroup2 = self._create_consistencygroup()
consistencygroup3 = self._create_consistencygroup()
url = '/v2/fake/consistencygroups?offset=1'
if is_detail:
url = '/v2/fake/consistencygroups/detail?offset=1'
req = webob.Request.blank(url)
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
self.assertEqual(2, len(res_dict['consistencygroups']))
self.assertEqual(consistencygroup2.id,
res_dict['consistencygroups'][0]['id'])
self.assertEqual(consistencygroup1.id,
res_dict['consistencygroups'][1]['id'])
consistencygroup1.destroy()
consistencygroup2.destroy()
consistencygroup3.destroy()
@ddt.data(False, True)
def test_list_consistencygroups_with_limit_and_offset(self, is_detail):
consistencygroup1 = self._create_consistencygroup()
consistencygroup2 = self._create_consistencygroup()
consistencygroup3 = self._create_consistencygroup()
url = '/v2/fake/consistencygroups?limit=2&offset=1'
if is_detail:
url = '/v2/fake/consistencygroups/detail?limit=2&offset=1'
req = webob.Request.blank(url)
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
self.assertEqual(2, len(res_dict['consistencygroups']))
self.assertEqual(consistencygroup2.id,
res_dict['consistencygroups'][0]['id'])
self.assertEqual(consistencygroup1.id,
res_dict['consistencygroups'][1]['id'])
consistencygroup1.destroy()
consistencygroup2.destroy()
consistencygroup3.destroy()
@ddt.data(False, True)
def test_list_consistencygroups_with_filter(self, is_detail):
consistencygroup1 = self._create_consistencygroup()
consistencygroup2 = self._create_consistencygroup()
common_ctxt = context.RequestContext('fake', 'fake', auth_token=True,
is_admin=False)
consistencygroup3 = self._create_consistencygroup(ctxt=common_ctxt)
url = ('/v2/fake/consistencygroups?'
'all_tenants=True&id=%s') % consistencygroup3.id
if is_detail:
url = ('/v2/fake/consistencygroups/detail?'
'all_tenants=True&id=%s') % consistencygroup3.id
req = webob.Request.blank(url)
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
res = req.get_response(fakes.wsgi_app(fake_auth_context=self.ctxt))
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
self.assertEqual(1, len(res_dict['consistencygroups']))
self.assertEqual(consistencygroup3.id,
res_dict['consistencygroups'][0]['id'])
consistencygroup1.destroy()
consistencygroup2.destroy()
consistencygroup3.destroy()
@ddt.data(False, True)
def test_list_consistencygroups_with_sort(self, is_detail):
consistencygroup1 = self._create_consistencygroup()
consistencygroup2 = self._create_consistencygroup()
consistencygroup3 = self._create_consistencygroup()
url = '/v2/fake/consistencygroups?sort=id:asc'
if is_detail:
url = '/v2/fake/consistencygroups/detail?sort=id:asc'
req = webob.Request.blank(url)
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
expect_result = [consistencygroup1.id, consistencygroup2.id,
consistencygroup3.id]
expect_result.sort()
self.assertEqual(200, res.status_int)
self.assertEqual(3, len(res_dict['consistencygroups']))
self.assertEqual(expect_result[0],
res_dict['consistencygroups'][0]['id'])
self.assertEqual(expect_result[1],
res_dict['consistencygroups'][1]['id'])
self.assertEqual(expect_result[2],
res_dict['consistencygroups'][2]['id'])
consistencygroup1.destroy()
consistencygroup2.destroy()
consistencygroup3.destroy()
def test_list_consistencygroups_detail_json(self):
consistencygroup1 = self._create_consistencygroup()
consistencygroup2 = self._create_consistencygroup()
consistencygroup3 = self._create_consistencygroup(volume_type_id=(
'uuid1,uuid2'))
req = webob.Request.blank('/v2/fake/consistencygroups/detail')
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
req.headers['Accept'] = 'application/json'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(200, res.status_int)
self.assertEqual('az1',
res_dict['consistencygroups'][0]['availability_zone'])
self.assertEqual('this is a test consistency group',
res_dict['consistencygroups'][0]['description'])
self.assertEqual('test_consistencygroup',
res_dict['consistencygroups'][0]['name'])
self.assertEqual(consistencygroup3.id,
res_dict['consistencygroups'][0]['id'])
self.assertEqual('creating',
res_dict['consistencygroups'][0]['status'])
self.assertEqual(['uuid1', 'uuid2'],
res_dict['consistencygroups'][0]['volume_types'])
self.assertEqual('az1',
res_dict['consistencygroups'][1]['availability_zone'])
self.assertEqual('this is a test consistency group',
res_dict['consistencygroups'][1]['description'])
self.assertEqual('test_consistencygroup',
res_dict['consistencygroups'][1]['name'])
self.assertEqual(consistencygroup2.id,
res_dict['consistencygroups'][1]['id'])
self.assertEqual('creating',
res_dict['consistencygroups'][1]['status'])
self.assertEqual(['123456'],
res_dict['consistencygroups'][1]['volume_types'])
self.assertEqual('az1',
res_dict['consistencygroups'][2]['availability_zone'])
self.assertEqual('this is a test consistency group',
res_dict['consistencygroups'][2]['description'])
self.assertEqual('test_consistencygroup',
res_dict['consistencygroups'][2]['name'])
self.assertEqual(consistencygroup1.id,
res_dict['consistencygroups'][2]['id'])
self.assertEqual('creating',
res_dict['consistencygroups'][2]['status'])
self.assertEqual(['123456'],
res_dict['consistencygroups'][2]['volume_types'])
consistencygroup1.destroy()
consistencygroup2.destroy()
consistencygroup3.destroy()
def test_list_consistencygroups_detail_xml(self):
consistencygroup1 = self._create_consistencygroup()
consistencygroup2 = self._create_consistencygroup()
consistencygroup3 = self._create_consistencygroup()
req = webob.Request.blank('/v2/fake/consistencygroups/detail')
req.method = 'GET'
req.headers['Content-Type'] = 'application/xml'
req.headers['Accept'] = 'application/xml'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(200, res.status_int)
dom = minidom.parseString(res.body)
consistencygroup_detail = dom.getElementsByTagName('consistencygroup')
self.assertEqual(
'az1',
consistencygroup_detail.item(0).getAttribute('availability_zone'))
self.assertEqual(
'this is a test consistency group',
consistencygroup_detail.item(0).getAttribute('description'))
self.assertEqual(
'test_consistencygroup',
consistencygroup_detail.item(0).getAttribute('name'))
self.assertEqual(
consistencygroup3.id,
consistencygroup_detail.item(0).getAttribute('id'))
self.assertEqual(
'creating',
consistencygroup_detail.item(0).getAttribute('status'))
self.assertEqual(
'az1',
consistencygroup_detail.item(1).getAttribute('availability_zone'))
self.assertEqual(
'this is a test consistency group',
consistencygroup_detail.item(1).getAttribute('description'))
self.assertEqual(
'test_consistencygroup',
consistencygroup_detail.item(1).getAttribute('name'))
self.assertEqual(
consistencygroup2.id,
consistencygroup_detail.item(1).getAttribute('id'))
self.assertEqual(
'creating',
consistencygroup_detail.item(1).getAttribute('status'))
self.assertEqual(
'az1',
consistencygroup_detail.item(2).getAttribute('availability_zone'))
self.assertEqual(
'this is a test consistency group',
consistencygroup_detail.item(2).getAttribute('description'))
self.assertEqual(
'test_consistencygroup',
consistencygroup_detail.item(2).getAttribute('name'))
self.assertEqual(
consistencygroup1.id,
consistencygroup_detail.item(2).getAttribute('id'))
self.assertEqual(
'creating',
consistencygroup_detail.item(2).getAttribute('status'))
consistencygroup3.destroy()
consistencygroup2.destroy()
consistencygroup1.destroy()
@mock.patch(
'cinder.api.openstack.wsgi.Controller.validate_name_and_description')
def test_create_consistencygroup_json(self, mock_validate):
group_id = "1"
# Create volume type
vol_type = 'test'
db.volume_type_create(context.get_admin_context(),
{'name': vol_type, 'extra_specs': {}})
body = {"consistencygroup": {"name": "cg1",
"volume_types": vol_type,
"description":
"Consistency Group 1", }}
req = webob.Request.blank('/v2/fake/consistencygroups')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(202, res.status_int)
self.assertIn('id', res_dict['consistencygroup'])
self.assertTrue(mock_validate.called)
group_id = res_dict['consistencygroup']['id']
cg = objects.ConsistencyGroup.get_by_id(context.get_admin_context(),
group_id)
cg.destroy()
def test_create_consistencygroup_with_no_body(self):
# omit body from the request
req = webob.Request.blank('/v2/fake/consistencygroups')
req.body = json.dumps(None)
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.headers['Accept'] = 'application/json'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
self.assertEqual("Missing required element 'consistencygroup' in "
"request body.",
res_dict['badRequest']['message'])
def test_delete_consistencygroup_available(self):
consistencygroup = self._create_consistencygroup(
status=fields.ConsistencyGroupStatus.AVAILABLE)
req = webob.Request.blank('/v2/fake/consistencygroups/%s/delete' %
consistencygroup.id)
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
body = {"consistencygroup": {"force": True}}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
consistencygroup = objects.ConsistencyGroup.get_by_id(
self.ctxt, consistencygroup.id)
self.assertEqual(202, res.status_int)
self.assertEqual(fields.ConsistencyGroupStatus.DELETING,
consistencygroup.status)
consistencygroup.destroy()
def test_delete_consistencygroup_with_consistencygroup_NotFound(self):
req = webob.Request.blank('/v2/fake/consistencygroups/9999/delete')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(None)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(404, res.status_int)
self.assertEqual(404, res_dict['itemNotFound']['code'])
self.assertEqual('ConsistencyGroup 9999 could not be found.',
res_dict['itemNotFound']['message'])
def test_delete_consistencygroup_with_Invalidconsistencygroup(self):
consistencygroup = self._create_consistencygroup(
status=fields.ConsistencyGroupStatus.IN_USE)
req = webob.Request.blank('/v2/fake/consistencygroups/%s/delete' %
consistencygroup.id)
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
body = {"consistencygroup": {"force": False}}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
msg = (_('Invalid ConsistencyGroup: Consistency group status must be '
'available or error, but current status is: in-use'))
self.assertEqual(msg, res_dict['badRequest']['message'])
consistencygroup.destroy()
def test_delete_consistencygroup_no_host(self):
consistencygroup = self._create_consistencygroup(
host=None,
status=fields.ConsistencyGroupStatus.ERROR)
req = webob.Request.blank('/v2/fake/consistencygroups/%s/delete' %
consistencygroup.id)
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
body = {"consistencygroup": {"force": True}}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
self.assertEqual(202, res.status_int)
cg = objects.ConsistencyGroup.get_by_id(
context.get_admin_context(read_deleted='yes'),
consistencygroup.id)
self.assertEqual(fields.ConsistencyGroupStatus.DELETED, cg.status)
self.assertIsNone(cg.host)
def test_create_delete_consistencygroup_update_quota(self):
name = 'mycg'
description = 'consistency group 1'
fake_type = {'id': '1', 'name': 'fake_type'}
self.stubs.Set(db, 'volume_types_get_by_name_or_id',
mock.Mock(return_value=[fake_type]))
self.stubs.Set(self.cg_api,
'_cast_create_consistencygroup',
mock.Mock())
self.stubs.Set(self.cg_api, 'update_quota',
mock.Mock())
cg = self.cg_api.create(self.ctxt, name, description,
fake_type['name'])
self.cg_api.update_quota.assert_called_once_with(
self.ctxt, cg, 1)
self.assertEqual(fields.ConsistencyGroupStatus.CREATING, cg.status)
self.assertIsNone(cg.host)
self.cg_api.update_quota.reset_mock()
cg.status = fields.ConsistencyGroupStatus.ERROR
self.cg_api.delete(self.ctxt, cg)
self.cg_api.update_quota.assert_called_once_with(
self.ctxt, cg, -1, self.ctxt.project_id)
cg = objects.ConsistencyGroup.get_by_id(
context.get_admin_context(read_deleted='yes'),
cg.id)
self.assertEqual(fields.ConsistencyGroupStatus.DELETED, cg.status)
def test_delete_consistencygroup_with_invalid_body(self):
consistencygroup = self._create_consistencygroup(
status=fields.ConsistencyGroupStatus.AVAILABLE)
req = webob.Request.blank('/v2/fake/consistencygroups/%s/delete' %
consistencygroup.id)
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
body = {"invalid_request_element": {"force": False}}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
self.assertEqual(400, res.status_int)
def test_delete_consistencygroup_with_invalid_force_value_in_body(self):
consistencygroup = self._create_consistencygroup(
status=fields.ConsistencyGroupStatus.AVAILABLE)
req = webob.Request.blank('/v2/fake/consistencygroups/%s/delete' %
consistencygroup.id)
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
body = {"consistencygroup": {"force": "abcd"}}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
self.assertEqual(400, res.status_int)
def test_delete_consistencygroup_with_empty_force_value_in_body(self):
consistencygroup = self._create_consistencygroup(
status=fields.ConsistencyGroupStatus.AVAILABLE)
req = webob.Request.blank('/v2/fake/consistencygroups/%s/delete' %
consistencygroup.id)
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
body = {"consistencygroup": {"force": ""}}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
self.assertEqual(400, res.status_int)
def test_create_consistencygroup_failed_no_volume_type(self):
name = 'cg1'
body = {"consistencygroup": {"name": name,
"description":
"Consistency Group 1", }}
req = webob.Request.blank('/v2/fake/consistencygroups')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
msg = (_('volume_types must be provided to create '
'consistency group %s.') % name)
self.assertEqual(msg, res_dict['badRequest']['message'])
@mock.patch(
'cinder.api.openstack.wsgi.Controller.validate_name_and_description')
def test_update_consistencygroup_success(self, mock_validate):
volume_type_id = '123456'
consistencygroup = self._create_consistencygroup(
status=fields.ConsistencyGroupStatus.AVAILABLE,
host='test_host')
remove_volume_id = utils.create_volume(
self.ctxt,
volume_type_id=volume_type_id,
consistencygroup_id=consistencygroup.id)['id']
remove_volume_id2 = utils.create_volume(
self.ctxt,
volume_type_id=volume_type_id,
consistencygroup_id=consistencygroup.id)['id']
self.assertEqual(fields.ConsistencyGroupStatus.AVAILABLE,
consistencygroup.status)
cg_volumes = db.volume_get_all_by_group(self.ctxt.elevated(),
consistencygroup.id)
cg_vol_ids = [cg_vol['id'] for cg_vol in cg_volumes]
self.assertIn(remove_volume_id, cg_vol_ids)
self.assertIn(remove_volume_id2, cg_vol_ids)
add_volume_id = utils.create_volume(
self.ctxt,
volume_type_id=volume_type_id)['id']
add_volume_id2 = utils.create_volume(
self.ctxt,
volume_type_id=volume_type_id)['id']
req = webob.Request.blank('/v2/fake/consistencygroups/%s/update' %
consistencygroup.id)
req.method = 'PUT'
req.headers['Content-Type'] = 'application/json'
name = 'newcg'
description = 'New Consistency Group Description'
add_volumes = add_volume_id + "," + add_volume_id2
remove_volumes = remove_volume_id + "," + remove_volume_id2
body = {"consistencygroup": {"name": name,
"description": description,
"add_volumes": add_volumes,
"remove_volumes": remove_volumes, }}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
consistencygroup = objects.ConsistencyGroup.get_by_id(
self.ctxt, consistencygroup.id)
self.assertEqual(202, res.status_int)
self.assertTrue(mock_validate.called)
self.assertEqual(fields.ConsistencyGroupStatus.UPDATING,
consistencygroup.status)
consistencygroup.destroy()
def test_update_consistencygroup_add_volume_not_found(self):
consistencygroup = self._create_consistencygroup(
ctxt=self.ctxt,
status=fields.ConsistencyGroupStatus.AVAILABLE)
req = webob.Request.blank('/v2/fake/consistencygroups/%s/update' %
consistencygroup.id)
req.method = 'PUT'
req.headers['Content-Type'] = 'application/json'
body = {"consistencygroup": {"name": None,
"description": None,
"add_volumes": "fake-volume-uuid",
"remove_volumes": None, }}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
msg = (_("Invalid volume: Cannot add volume fake-volume-uuid "
"to consistency group %(group_id)s because volume cannot "
"be found.") %
{'group_id': consistencygroup.id})
self.assertEqual(msg, res_dict['badRequest']['message'])
consistencygroup.destroy()
def test_update_consistencygroup_remove_volume_not_found(self):
consistencygroup = self._create_consistencygroup(
ctxt=self.ctxt,
status=fields.ConsistencyGroupStatus.AVAILABLE)
req = webob.Request.blank('/v2/fake/consistencygroups/%s/update' %
consistencygroup.id)
req.method = 'PUT'
req.headers['Content-Type'] = 'application/json'
body = {"consistencygroup": {"name": None,
"description": "new description",
"add_volumes": None,
"remove_volumes": "fake-volume-uuid", }}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
msg = (_("Invalid volume: Cannot remove volume fake-volume-uuid "
"from consistency group %(group_id)s because it is not "
"in the group.") %
{'group_id': consistencygroup.id})
self.assertEqual(msg, res_dict['badRequest']['message'])
consistencygroup.destroy()
def test_update_consistencygroup_empty_parameters(self):
consistencygroup = self._create_consistencygroup(
ctxt=self.ctxt,
status=fields.ConsistencyGroupStatus.AVAILABLE)
req = webob.Request.blank('/v2/fake/consistencygroups/%s/update' %
consistencygroup.id)
req.method = 'PUT'
req.headers['Content-Type'] = 'application/json'
body = {"consistencygroup": {"name": "",
"description": "",
"add_volumes": None,
"remove_volumes": None, }}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
consistencygroup.destroy()
def test_update_consistencygroup_add_volume_invalid_state(self):
volume_type_id = '123456'
consistencygroup = self._create_consistencygroup(
ctxt=self.ctxt,
status=fields.ConsistencyGroupStatus.AVAILABLE)
add_volume_id = utils.create_volume(
self.ctxt,
volume_type_id=volume_type_id,
status='wrong_status')['id']
req = webob.Request.blank('/v2/fake/consistencygroups/%s/update' %
consistencygroup.id)
req.method = 'PUT'
req.headers['Content-Type'] = 'application/json'
add_volumes = add_volume_id
body = {"consistencygroup": {"name": "cg1",
"description": "",
"add_volumes": add_volumes,
"remove_volumes": None, }}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
msg = (_("Invalid volume: Cannot add volume %(volume_id)s "
"to consistency group %(group_id)s because volume is in an "
"invalid state: %(status)s. Valid states are: ('available', "
"'in-use').") %
{'volume_id': add_volume_id,
'group_id': consistencygroup.id,
'status': 'wrong_status'})
self.assertEqual(msg, res_dict['badRequest']['message'])
consistencygroup.destroy()
def test_update_consistencygroup_add_volume_invalid_volume_type(self):
consistencygroup = self._create_consistencygroup(
ctxt=self.ctxt,
status=fields.ConsistencyGroupStatus.AVAILABLE)
wrong_type = 'wrong-volume-type-id'
add_volume_id = utils.create_volume(
self.ctxt,
volume_type_id=wrong_type)['id']
req = webob.Request.blank('/v2/fake/consistencygroups/%s/update' %
consistencygroup.id)
req.method = 'PUT'
req.headers['Content-Type'] = 'application/json'
add_volumes = add_volume_id
body = {"consistencygroup": {"name": "cg1",
"description": "",
"add_volumes": add_volumes,
"remove_volumes": None, }}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
msg = (_("Invalid volume: Cannot add volume %(volume_id)s "
"to consistency group %(group_id)s because volume type "
"%(volume_type)s is not supported by the group.") %
{'volume_id': add_volume_id,
'group_id': consistencygroup.id,
'volume_type': wrong_type})
self.assertEqual(msg, res_dict['badRequest']['message'])
consistencygroup.destroy()
def test_update_consistencygroup_add_volume_already_in_cg(self):
consistencygroup = self._create_consistencygroup(
ctxt=self.ctxt,
status=fields.ConsistencyGroupStatus.AVAILABLE)
add_volume_id = utils.create_volume(
self.ctxt,
consistencygroup_id='some_other_cg')['id']
req = webob.Request.blank('/v2/fake/consistencygroups/%s/update' %
consistencygroup.id)
req.method = 'PUT'
req.headers['Content-Type'] = 'application/json'
add_volumes = add_volume_id
body = {"consistencygroup": {"name": "cg1",
"description": "",
"add_volumes": add_volumes,
"remove_volumes": None, }}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
self.assertIsNotNone(res_dict['badRequest']['message'])
consistencygroup.destroy()
def test_update_consistencygroup_invalid_state(self):
consistencygroup = self._create_consistencygroup(
status=fields.ConsistencyGroupStatus.IN_USE,
ctxt=self.ctxt)
req = webob.Request.blank('/v2/fake/consistencygroups/%s/update' %
consistencygroup.id)
req.method = 'PUT'
req.headers['Content-Type'] = 'application/json'
body = {"consistencygroup": {"name": "new name",
"description": None,
"add_volumes": None,
"remove_volumes": None, }}
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
msg = _("Invalid ConsistencyGroup: Consistency group status must be "
"available, but current status is: %s.") % (
fields.ConsistencyGroupStatus.IN_USE)
self.assertEqual(msg, res_dict['badRequest']['message'])
consistencygroup.destroy()
@mock.patch(
'cinder.api.openstack.wsgi.Controller.validate_name_and_description')
def test_create_consistencygroup_from_src(self, mock_validate):
self.stubs.Set(volume_api.API, "create", stubs.stub_volume_create)
consistencygroup = utils.create_consistencygroup(self.ctxt)
volume_id = utils.create_volume(
self.ctxt,
consistencygroup_id=consistencygroup.id)['id']
cgsnapshot = utils.create_cgsnapshot(
self.ctxt, consistencygroup_id=consistencygroup.id)
snapshot = utils.create_snapshot(
self.ctxt,
volume_id,
cgsnapshot_id=cgsnapshot.id,
status='available')
test_cg_name = 'test cg'
body = {"consistencygroup-from-src": {"name": test_cg_name,
"description":
"Consistency Group 1",
"cgsnapshot_id": cgsnapshot.id}}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(202, res.status_int)
self.assertIn('id', res_dict['consistencygroup'])
self.assertEqual(test_cg_name, res_dict['consistencygroup']['name'])
self.assertTrue(mock_validate.called)
cg_ref = objects.ConsistencyGroup.get_by_id(
self.ctxt.elevated(), res_dict['consistencygroup']['id'])
cg_ref.destroy()
snapshot.destroy()
db.volume_destroy(self.ctxt.elevated(), volume_id)
consistencygroup.destroy()
cgsnapshot.destroy()
def test_create_consistencygroup_from_src_cg(self):
self.mock_object(volume_api.API, "create", stubs.stub_volume_create)
source_cg = utils.create_consistencygroup(self.ctxt)
volume_id = utils.create_volume(
self.ctxt,
consistencygroup_id=source_cg.id)['id']
test_cg_name = 'test cg'
body = {"consistencygroup-from-src": {"name": test_cg_name,
"description":
"Consistency Group 1",
"source_cgid": source_cg.id}}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(202, res.status_int)
self.assertIn('id', res_dict['consistencygroup'])
self.assertEqual(test_cg_name, res_dict['consistencygroup']['name'])
cg = objects.ConsistencyGroup.get_by_id(
self.ctxt, res_dict['consistencygroup']['id'])
cg.destroy
db.volume_destroy(self.ctxt.elevated(), volume_id)
source_cg.destroy()
def test_create_consistencygroup_from_src_both_snap_cg(self):
self.stubs.Set(volume_api.API, "create", stubs.stub_volume_create)
consistencygroup = utils.create_consistencygroup(self.ctxt)
volume_id = utils.create_volume(
self.ctxt,
consistencygroup_id=consistencygroup.id)['id']
cgsnapshot_id = utils.create_cgsnapshot(
self.ctxt,
consistencygroup_id=consistencygroup.id)['id']
snapshot = utils.create_snapshot(
self.ctxt,
volume_id,
cgsnapshot_id=cgsnapshot_id,
status='available')
test_cg_name = 'test cg'
body = {"consistencygroup-from-src": {"name": test_cg_name,
"description":
"Consistency Group 1",
"cgsnapshot_id": cgsnapshot_id,
"source_cgid":
consistencygroup.id}}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
self.assertIsNotNone(res_dict['badRequest']['message'])
snapshot.destroy()
db.cgsnapshot_destroy(self.ctxt.elevated(), cgsnapshot_id)
db.volume_destroy(self.ctxt.elevated(), volume_id)
consistencygroup.destroy()
def test_create_consistencygroup_from_src_invalid_body(self):
name = 'cg1'
body = {"invalid": {"name": name,
"description":
"Consistency Group 1", }}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
# Missing 'consistencygroup-from-src' in the body.
self.assertIsNotNone(res_dict['badRequest']['message'])
def test_create_consistencygroup_from_src_no_source_id(self):
name = 'cg1'
body = {"consistencygroup-from-src": {"name": name,
"description":
"Consistency Group 1", }}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
self.assertIsNotNone(res_dict['badRequest']['message'])
def test_create_consistencygroup_from_src_no_host(self):
consistencygroup = utils.create_consistencygroup(self.ctxt, host=None)
volume_id = utils.create_volume(
self.ctxt,
consistencygroup_id=consistencygroup.id)['id']
cgsnapshot = utils.create_cgsnapshot(
self.ctxt, consistencygroup_id=consistencygroup.id)
snapshot = utils.create_snapshot(
self.ctxt,
volume_id,
cgsnapshot_id=cgsnapshot.id,
status='available')
test_cg_name = 'test cg'
body = {"consistencygroup-from-src": {"name": test_cg_name,
"description":
"Consistency Group 1",
"cgsnapshot_id": cgsnapshot.id}}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
msg = _('Invalid ConsistencyGroup: No host to create consistency '
'group')
self.assertIn(msg, res_dict['badRequest']['message'])
snapshot.destroy()
db.volume_destroy(self.ctxt.elevated(), volume_id)
consistencygroup.destroy()
cgsnapshot.destroy()
def test_create_consistencygroup_from_src_cgsnapshot_empty(self):
consistencygroup = utils.create_consistencygroup(self.ctxt)
volume_id = utils.create_volume(
self.ctxt,
consistencygroup_id=consistencygroup.id)['id']
cgsnapshot = utils.create_cgsnapshot(
self.ctxt,
consistencygroup_id=consistencygroup.id)
test_cg_name = 'test cg'
body = {"consistencygroup-from-src": {"name": test_cg_name,
"description":
"Consistency Group 1",
"cgsnapshot_id": cgsnapshot.id}}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
self.assertIsNotNone(res_dict['badRequest']['message'])
db.volume_destroy(self.ctxt.elevated(), volume_id)
consistencygroup.destroy()
cgsnapshot.destroy()
def test_create_consistencygroup_from_src_source_cg_empty(self):
source_cg = utils.create_consistencygroup(self.ctxt)
test_cg_name = 'test cg'
body = {"consistencygroup-from-src": {"name": test_cg_name,
"description":
"Consistency Group 1",
"source_cgid": source_cg.id}}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
self.assertIsNotNone(res_dict['badRequest']['message'])
source_cg.destroy()
def test_create_consistencygroup_from_src_cgsnapshot_notfound(self):
consistencygroup = utils.create_consistencygroup(self.ctxt)
volume_id = utils.create_volume(
self.ctxt,
consistencygroup_id=consistencygroup.id)['id']
test_cg_name = 'test cg'
body = {"consistencygroup-from-src": {"name": test_cg_name,
"description":
"Consistency Group 1",
"cgsnapshot_id": "fake_cgsnap"}}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(404, res.status_int)
self.assertEqual(404, res_dict['itemNotFound']['code'])
self.assertIsNotNone(res_dict['itemNotFound']['message'])
db.volume_destroy(self.ctxt.elevated(), volume_id)
consistencygroup.destroy()
def test_create_consistencygroup_from_src_source_cg_notfound(self):
test_cg_name = 'test cg'
body = {"consistencygroup-from-src": {"name": test_cg_name,
"description":
"Consistency Group 1",
"source_cgid": "fake_source_cg"}}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(404, res.status_int)
self.assertEqual(404, res_dict['itemNotFound']['code'])
self.assertIsNotNone(res_dict['itemNotFound']['message'])
@mock.patch.object(volume_api.API, 'create',
side_effect=exception.CinderException(
'Create volume failed.'))
def test_create_consistencygroup_from_src_cgsnapshot_create_volume_failed(
self, mock_create):
consistencygroup = utils.create_consistencygroup(self.ctxt)
volume_id = utils.create_volume(
self.ctxt,
consistencygroup_id=consistencygroup.id)['id']
cgsnapshot = utils.create_cgsnapshot(
self.ctxt, consistencygroup_id=consistencygroup.id)
snapshot = utils.create_snapshot(
self.ctxt,
volume_id,
cgsnapshot_id=cgsnapshot.id,
status='available')
test_cg_name = 'test cg'
body = {"consistencygroup-from-src": {"name": test_cg_name,
"description":
"Consistency Group 1",
"cgsnapshot_id": cgsnapshot.id}}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
msg = _("Create volume failed.")
self.assertEqual(msg, res_dict['badRequest']['message'])
snapshot.destroy()
db.volume_destroy(self.ctxt.elevated(), volume_id)
consistencygroup.destroy()
cgsnapshot.destroy()
@mock.patch.object(volume_api.API, 'create',
side_effect=exception.CinderException(
'Create volume failed.'))
def test_create_consistencygroup_from_src_cg_create_volume_failed(
self, mock_create):
source_cg = utils.create_consistencygroup(self.ctxt)
volume_id = utils.create_volume(
self.ctxt,
consistencygroup_id=source_cg.id)['id']
test_cg_name = 'test cg'
body = {"consistencygroup-from-src": {"name": test_cg_name,
"description":
"Consistency Group 1",
"source_cgid": source_cg.id}}
req = webob.Request.blank('/v2/fake/consistencygroups/create_from_src')
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(400, res.status_int)
self.assertEqual(400, res_dict['badRequest']['code'])
self.assertIsNotNone(res_dict['badRequest']['message'])
db.volume_destroy(self.ctxt.elevated(), volume_id)
source_cg.destroy()