cinder/cinder/tests/unit/api/v3/test_groups.py

1473 lines
65 KiB
Python

# Copyright (C) 2016 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 group code."""
from unittest import mock
import ddt
from six.moves import http_client
import webob
from cinder.api import microversions as mv
from cinder.api.v3 import groups as v3_groups
from cinder import context
from cinder import db
from cinder import exception
import cinder.group
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.v3 import fakes as v3_fakes
from cinder.tests.unit import fake_constants as fake
from cinder.tests.unit import utils
from cinder.volume import api as volume_api
INVALID_GROUP_REPLICATION = mv.get_prior_version(mv.GROUP_REPLICATION)
@ddt.ddt
class GroupsAPITestCase(test.TestCase):
"""Test Case for groups API."""
def setUp(self):
super(GroupsAPITestCase, self).setUp()
self.controller = v3_groups.GroupsController()
self.group_api = cinder.group.API()
self.ctxt = context.RequestContext(fake.USER_ID, fake.PROJECT_ID,
auth_token=True,
is_admin=True)
self.user_ctxt = context.RequestContext(
fake.USER_ID, fake.PROJECT_ID, auth_token=True)
self.volume_type1 = self._create_volume_type(id=fake.VOLUME_TYPE_ID)
self.group1 = self._create_group()
self.group2 = self._create_group()
self.group3 = self._create_group(ctxt=self.user_ctxt)
self.addCleanup(self._cleanup)
def _cleanup(self):
self.group1.destroy()
self.group2.destroy()
self.group3.destroy()
db.volume_type_destroy(self.ctxt, self.volume_type1.id)
def _create_group(
self,
ctxt=None,
name='test_group',
description='this is a test group',
group_type_id=fake.GROUP_TYPE_ID,
volume_type_ids=[fake.VOLUME_TYPE_ID],
availability_zone='az1',
host='fakehost',
status=fields.GroupStatus.CREATING,
replication_status=fields.ReplicationStatus.DISABLED,
**kwargs):
"""Create a group object."""
ctxt = ctxt or self.ctxt
group = objects.Group(ctxt)
group.user_id = fake.USER_ID
group.project_id = fake.PROJECT_ID
group.availability_zone = availability_zone
group.name = name
group.description = description
group.group_type_id = group_type_id
group.volume_type_ids = volume_type_ids
group.host = host
group.status = status
group.replication_status = replication_status
group.update(kwargs)
group.create()
return group
def _create_volume_type(
self,
ctxt=None,
id=fake.VOLUME_TYPE_ID,
name='test_volume_type',
description='this is a test volume type',
extra_specs={"test_key": "test_val"},
testcase_instance=None,
**kwargs):
"""Create a volume type."""
ctxt = ctxt or self.ctxt
vol_type = utils.create_volume_type(
ctxt,
testcase_instance=testcase_instance,
id=id,
name=name,
description=description,
extra_specs=extra_specs,
**kwargs)
return vol_type
@mock.patch('cinder.objects.volume_type.VolumeTypeList.get_all_by_group')
@mock.patch('cinder.objects.volume.VolumeList.get_all_by_generic_group')
def test_show_group(self, mock_vol_get_all_by_group,
mock_vol_type_get_all_by_group):
volume_objs = [objects.Volume(context=self.ctxt, id=i)
for i in [fake.VOLUME_ID]]
volumes = objects.VolumeList(context=self.ctxt, objects=volume_objs)
mock_vol_get_all_by_group.return_value = volumes
vol_type_objs = [objects.VolumeType(context=self.ctxt, id=i)
for i in [fake.VOLUME_TYPE_ID]]
vol_types = objects.VolumeTypeList(context=self.ctxt,
objects=vol_type_objs)
mock_vol_type_get_all_by_group.return_value = vol_types
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict))
self.assertEqual('az1',
res_dict['group']['availability_zone'])
self.assertEqual('this is a test group',
res_dict['group']['description'])
self.assertEqual('test_group',
res_dict['group']['name'])
self.assertEqual('creating',
res_dict['group']['status'])
self.assertEqual([fake.VOLUME_TYPE_ID],
res_dict['group']['volume_types'])
@ddt.data((mv.get_prior_version(mv.GROUP_VOLUME_LIST), False),
(mv.get_prior_version(mv.GROUP_VOLUME_LIST), True),
(mv.GROUP_VOLUME_LIST, False),
(mv.GROUP_VOLUME_LIST, True))
@ddt.unpack
@mock.patch('cinder.objects.volume_type.VolumeTypeList.get_all_by_group')
@mock.patch('cinder.objects.volume.VolumeList.get_all_by_generic_group')
def test_list_group_with_list_volume(self, version, has_list_volume,
mock_vol_get_all_by_group,
mock_vol_type_get_all_by_group):
volume_objs = [objects.Volume(context=self.ctxt, id=i)
for i in [fake.VOLUME_ID]]
volumes = objects.VolumeList(context=self.ctxt, objects=volume_objs)
mock_vol_get_all_by_group.return_value = volumes
vol_type_objs = [objects.VolumeType(context=self.ctxt, id=i)
for i in [fake.VOLUME_TYPE_ID]]
vol_types = objects.VolumeTypeList(context=self.ctxt,
objects=vol_type_objs)
mock_vol_type_get_all_by_group.return_value = vol_types
if has_list_volume:
req = fakes.HTTPRequest.blank(
'/v3/%s/groups/detail?list_volume=True' % fake.PROJECT_ID,
version=version)
else:
req = fakes.HTTPRequest.blank('/v3/%s/groups/detail' %
fake.PROJECT_ID,
version=version)
res_dict = self.controller.detail(req)
# If the microversion >= 3.25 and "list_volume=True", "volumes" should
# be contained in the response body. Else, "volumes" should not be
# contained in the response body.
self.assertEqual(3, len(res_dict['groups']))
if (version, has_list_volume) == (mv.GROUP_VOLUME_LIST, True):
self.assertEqual([fake.VOLUME_ID],
res_dict['groups'][0]['volumes'])
else:
self.assertNotIn('volumes', res_dict['groups'][0])
# "volumes" should not be contained in the response body when list
# groups without detail.
res_dict = self.controller.index(req)
self.assertNotIn('volumes', res_dict['groups'][0])
@mock.patch('cinder.objects.volume_type.VolumeTypeList.get_all_by_group')
@mock.patch('cinder.objects.volume.VolumeList.get_all_by_generic_group')
def test_show_group_with_list_volume(self, mock_vol_get_all_by_group,
mock_vol_type_get_all_by_group):
volume_objs = [objects.Volume(context=self.ctxt, id=i)
for i in [fake.VOLUME_ID]]
volumes = objects.VolumeList(context=self.ctxt, objects=volume_objs)
mock_vol_get_all_by_group.return_value = volumes
vol_type_objs = [objects.VolumeType(context=self.ctxt, id=i)
for i in [fake.VOLUME_TYPE_ID]]
vol_types = objects.VolumeTypeList(context=self.ctxt,
objects=vol_type_objs)
mock_vol_type_get_all_by_group.return_value = vol_types
# If the microversion >= 3.25 and "list_volume=True", "volumes" should
# be contained in the response body.
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s?list_volume=True' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME_LIST)
res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict))
self.assertEqual([fake.VOLUME_ID],
res_dict['group']['volumes'])
# If the microversion >= 3.25 but "list_volume" is missing, "volumes"
# should not be contained in the response body.
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME_LIST)
res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict))
self.assertNotIn('volumes', res_dict['group'])
# If the microversion < 3.25, "volumes" should not be contained in the
# response body.
req = fakes.HTTPRequest.blank(
'/v3/%s/groups/%s?list_volume=True' %
(fake.PROJECT_ID, self.group1.id),
version=mv.get_prior_version(mv.GROUP_VOLUME_LIST))
res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict))
self.assertNotIn('volumes', res_dict['group'])
def test_show_group_with_group_NotFound(self):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s' %
(fake.PROJECT_ID,
fake.WILL_NOT_BE_FOUND_ID),
version=mv.GROUP_VOLUME)
self.assertRaises(exception.GroupNotFound, self.controller.show,
req, fake.WILL_NOT_BE_FOUND_ID)
@ddt.data(mv.get_prior_version(mv.RESOURCE_FILTER),
mv.RESOURCE_FILTER, mv.LIKE_FILTER)
@mock.patch('cinder.api.common.reject_invalid_filters')
def test_group_list_with_general_filter(self, version, mock_update):
url = '/v3/%s/groups' % fake.PROJECT_ID
req = fakes.HTTPRequest.blank(url,
version=version,
use_admin_context=False)
self.controller.index(req)
if version != mv.get_prior_version(mv.RESOURCE_FILTER):
support_like = True if version == mv.LIKE_FILTER else False
mock_update.assert_called_once_with(req.environ['cinder.context'],
mock.ANY, 'group',
support_like)
def test_list_groups_json(self):
self.group2.group_type_id = fake.GROUP_TYPE2_ID
# TODO(geguileo): One `volume_type_ids` gets sorted out make proper
# changes here
# self.group2.volume_type_ids = [fake.VOLUME_TYPE2_ID]
self.group2.save()
self.group3.group_type_id = fake.GROUP_TYPE3_ID
# TODO(geguileo): One `volume_type_ids` gets sorted out make proper
# changes here
# self.group3.volume_type_ids = [fake.VOLUME_TYPE3_ID]
self.group3.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=mv.GROUP_VOLUME)
res_dict = self.controller.index(req)
self.assertEqual(1, len(res_dict))
self.assertEqual(self.group3.id,
res_dict['groups'][0]['id'])
self.assertEqual('test_group',
res_dict['groups'][0]['name'])
self.assertEqual(self.group2.id,
res_dict['groups'][1]['id'])
self.assertEqual('test_group',
res_dict['groups'][1]['name'])
self.assertEqual(self.group1.id,
res_dict['groups'][2]['id'])
self.assertEqual('test_group',
res_dict['groups'][2]['name'])
@ddt.data(False, True)
def test_list_groups_with_limit(self, is_detail):
url = '/v3/%s/groups?limit=1' % fake.PROJECT_ID
if is_detail:
url = '/v3/%s/groups/detail?limit=1' % fake.PROJECT_ID
req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME)
if is_detail:
res_dict = self.controller.detail(req)
else:
res_dict = self.controller.index(req)
self.assertEqual(2, len(res_dict))
self.assertEqual(1, len(res_dict['groups']))
self.assertEqual(self.group3.id,
res_dict['groups'][0]['id'])
next_link = (
'http://localhost/v3/%s/groups?limit='
'1&marker=%s' %
(fake.PROJECT_ID, res_dict['groups'][0]['id']))
self.assertEqual(next_link,
res_dict['group_links'][0]['href'])
if is_detail:
self.assertIn('description', res_dict['groups'][0].keys())
@ddt.data(False, True)
def test_list_groups_with_offset(self, is_detail):
url = '/v3/%s/groups?offset=1' % fake.PROJECT_ID
if is_detail:
url = '/v3/%s/groups/detail?offset=1' % fake.PROJECT_ID
req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME)
res_dict = self.controller.index(req)
self.assertEqual(1, len(res_dict))
self.assertEqual(2, len(res_dict['groups']))
self.assertEqual(self.group2.id,
res_dict['groups'][0]['id'])
self.assertEqual(self.group1.id,
res_dict['groups'][1]['id'])
@ddt.data(False, True)
def test_list_groups_with_offset_out_of_range(self, is_detail):
url = ('/v3/%s/groups?offset=234523423455454' %
fake.PROJECT_ID)
if is_detail:
url = ('/v3/%s/groups/detail?offset=234523423455454' %
fake.PROJECT_ID)
req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME)
if is_detail:
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.detail,
req)
else:
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index,
req)
@ddt.data(False, True)
def test_list_groups_with_limit_and_offset(self, is_detail):
url = '/v3/%s/groups?limit=2&offset=1' % fake.PROJECT_ID
if is_detail:
url = ('/v3/%s/groups/detail?limit=2&offset=1' %
fake.PROJECT_ID)
req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME)
if is_detail:
res_dict = self.controller.detail(req)
else:
res_dict = self.controller.index(req)
self.assertEqual(2, len(res_dict))
self.assertEqual(2, len(res_dict['groups']))
self.assertEqual(self.group2.id,
res_dict['groups'][0]['id'])
self.assertEqual(self.group1.id,
res_dict['groups'][1]['id'])
if is_detail:
self.assertIn('description', res_dict['groups'][0].keys())
@ddt.data(False, True)
def test_list_groups_with_filter(self, is_detail):
# Create a group with user context
url = ('/v3/%s/groups?'
'all_tenants=True&id=%s') % (fake.PROJECT_ID,
self.group3.id)
if is_detail:
url = ('/v3/%s/groups/detail?'
'all_tenants=True&id=%s') % (fake.PROJECT_ID,
self.group3.id)
req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME,
use_admin_context=True)
if is_detail:
res_dict = self.controller.detail(req)
else:
res_dict = self.controller.index(req)
self.assertEqual(1, len(res_dict))
self.assertEqual(1, len(res_dict['groups']))
self.assertEqual(self.group3.id,
res_dict['groups'][0]['id'])
if is_detail:
self.assertIn('description', res_dict['groups'][0].keys())
@ddt.data(False, True)
def test_list_groups_with_sort(self, is_detail):
url = '/v3/%s/groups?sort=id:asc' % fake.PROJECT_ID
if is_detail:
url = ('/v3/%s/groups/detail?sort=id:asc' %
fake.PROJECT_ID)
req = fakes.HTTPRequest.blank(url, version=mv.GROUP_VOLUME)
expect_result = [self.group1.id, self.group2.id,
self.group3.id]
expect_result.sort()
if is_detail:
res_dict = self.controller.detail(req)
else:
res_dict = self.controller.index(req)
self.assertEqual(1, len(res_dict))
self.assertEqual(3, len(res_dict['groups']))
self.assertEqual(expect_result[0],
res_dict['groups'][0]['id'])
self.assertEqual(expect_result[1],
res_dict['groups'][1]['id'])
self.assertEqual(expect_result[2],
res_dict['groups'][2]['id'])
if is_detail:
self.assertIn('description', res_dict['groups'][0].keys())
@mock.patch('cinder.objects.volume_type.VolumeTypeList.get_all_by_group')
def test_list_groups_detail_json(self, mock_vol_type_get_all_by_group):
volume_type_ids = [fake.VOLUME_TYPE_ID, fake.VOLUME_TYPE2_ID]
vol_type_objs = [objects.VolumeType(context=self.ctxt, id=i)
for i in volume_type_ids]
vol_types = objects.VolumeTypeList(context=self.ctxt,
objects=vol_type_objs)
mock_vol_type_get_all_by_group.return_value = vol_types
# TODO(geguileo): One `volume_type_ids` gets sorted out make proper
# changes here
# self.group1.volume_type_ids = volume_type_ids
# self.group1.save()
# self.group2.volume_type_ids = volume_type_ids
# self.group2.save()
# self.group3.volume_type_ids = volume_type_ids
# self.group3.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/detail' %
fake.PROJECT_ID,
version=mv.GROUP_VOLUME)
res_dict = self.controller.detail(req)
self.assertEqual(1, len(res_dict))
index = 0
for group in [self.group3, self.group2, self.group1]:
self.assertEqual(group.id,
res_dict['groups'][index]['id'])
self.assertEqual([fake.VOLUME_TYPE_ID, fake.VOLUME_TYPE2_ID],
res_dict['groups'][index]['volume_types'])
self.assertEqual('test_group',
res_dict['groups'][index]['name'])
self.assertTrue({'availability_zone', 'description',
'status'}.issubset(
set(res_dict['groups'][index].keys())))
index += 1
@ddt.data(False, True)
def test_create_group_json(self, use_group_type_name):
# Create volume types and group type
vol_type = 'test'
vol_type_id = db.volume_type_create(
self.ctxt,
{'name': vol_type, 'extra_specs': {}}).get('id')
grp_type_name = 'test_grp_type'
grp_type = db.group_type_create(
self.ctxt,
{'name': grp_type_name, 'group_specs': {}}).get('id')
if use_group_type_name:
grp_type = grp_type_name
body = {"group": {"name": "group1",
"volume_types": [vol_type_id],
"group_type": grp_type,
"description":
"Group 1", }}
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=mv.GROUP_VOLUME)
res_dict = self.controller.create(req, body=body)
self.assertEqual(1, len(res_dict))
self.assertIn('id', res_dict['group'])
group_id = res_dict['group']['id']
objects.Group.get_by_id(self.ctxt, group_id)
def test_create_group_with_no_body(self):
# omit body from the request
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=mv.GROUP_VOLUME)
self.assertRaises(exception.ValidationError, self.controller.create,
req, body=None)
@ddt.data(("", webob.exc.HTTPBadRequest),
(" ", exception.InvalidInput),
("a" * 256, exception.InvalidInput))
@ddt.unpack
def test_create_group_with_invalid_availability_zone(
self, az_name, exceptions):
vol_type = 'test'
vol_type_id = db.volume_type_create(
self.ctxt,
{'name': vol_type, 'extra_specs': {}}).get('id')
grp_type_name = 'test_grp_type'
grp_type = db.group_type_create(
self.ctxt,
{'name': grp_type_name, 'group_specs': {}}).get('id')
body = {"group": {"name": "group1",
"volume_types": [vol_type_id],
"group_type": grp_type,
"availability_zone": az_name}}
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=mv.GROUP_VOLUME)
self.assertRaises(exceptions, self.controller.create,
req, body=body)
def test_delete_group_available(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": False}}
res_dict = self.controller.delete_group(
req, self.group1.id, body=body)
group = objects.Group.get_by_id(
self.ctxt, self.group1.id)
self.assertEqual(http_client.ACCEPTED, res_dict.status_int)
self.assertEqual(fields.GroupStatus.DELETING, group.status)
def test_delete_group_available_no_delete_volumes(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": False}}
res_dict = self.controller.delete_group(
req, self.group1.id, body=body)
group = objects.Group.get_by_id(
self.ctxt, self.group1.id)
self.assertEqual(http_client.ACCEPTED, res_dict.status_int)
self.assertEqual(fields.GroupStatus.DELETING,
group.status)
def test_delete_group_with_group_NotFound(self):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID,
fake.WILL_NOT_BE_FOUND_ID),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": False}}
self.assertRaises(exception.GroupNotFound,
self.controller.delete_group,
req, fake.WILL_NOT_BE_FOUND_ID, body=body)
def test_delete_group_with_invalid_group(self):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID,
self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": False}}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete_group,
req, self.group1.id, body=body)
def test_delete_group_invalid_delete_volumes(self):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID,
self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}}
res_dict = self.controller.delete_group(
req, self.group1.id, body=body)
group = objects.Group.get_by_id(
self.ctxt, self.group1.id)
self.assertEqual(http_client.ACCEPTED, res_dict.status_int)
self.assertEqual(fields.GroupStatus.DELETING, group.status)
def test_delete_group_no_host(self):
self.group1.host = None
self.group1.status = fields.GroupStatus.ERROR
self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID,
self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}}
res_dict = self.controller.delete_group(
req, self.group1.id, body=body)
self.assertEqual(http_client.ACCEPTED, res_dict.status_int)
group = objects.Group.get_by_id(
context.get_admin_context(read_deleted='yes'),
self.group1.id)
self.assertEqual(fields.GroupStatus.DELETED, group.status)
self.assertIsNone(group.host)
@mock.patch('cinder.quota.GROUP_QUOTAS.reserve',
return_value='reservations')
@mock.patch('cinder.quota.GROUP_QUOTAS.commit')
def test_create_delete_group_update_quota(self, mock_commit, mock_reserve):
name = 'mygroup'
description = 'group 1'
grp_type = {'id': fake.GROUP_TYPE_ID, 'name': 'group_type'}
fake_type = {'id': fake.VOLUME_TYPE_ID, 'name': 'fake_type'}
self.mock_object(db, 'volume_types_get_by_name_or_id',
return_value=[fake_type])
self.mock_object(db, 'group_type_get', return_value=grp_type)
self.mock_object(self.group_api, '_cast_create_group')
self.mock_object(self.group_api, 'update_quota')
group = self.group_api.create(self.ctxt, name, description,
grp_type['id'], [fake_type['id']])
# Verify that quota reservation and commit was called
mock_reserve.assert_called_once_with(self.ctxt,
project_id=self.ctxt.project_id,
groups=1)
mock_commit.assert_called_once_with(self.ctxt, 'reservations')
self.assertEqual(fields.GroupStatus.CREATING, group.status)
self.assertIsNone(group.host)
group.status = fields.GroupStatus.ERROR
self.group_api.delete(self.ctxt, group)
self.group_api.update_quota.assert_called_once_with(
self.ctxt, group, -1, self.ctxt.project_id)
group = objects.Group.get_by_id(
context.get_admin_context(read_deleted='yes'),
group.id)
self.assertEqual(fields.GroupStatus.DELETED, group.status)
@mock.patch('cinder.group.api.API.create')
def test_create_group_failed_exceeded_quota(self, mock_group_create):
mock_group_create.side_effect = exception.GroupLimitExceeded(allowed=1)
name = 'group1'
body = {"group": {"group_type": fake.GROUP_TYPE_ID,
"volume_types": [fake.VOLUME_TYPE_ID],
"name": name,
"description":
"Group 1", }}
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=mv.GROUP_VOLUME)
ex = self.assertRaises(exception.GroupLimitExceeded,
self.controller.create,
req, body=body)
self.assertEqual(http_client.REQUEST_ENTITY_TOO_LARGE, ex.code)
def test_delete_group_with_invalid_body(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"invalid_request_element": {"delete-volumes": False}}
self.assertRaises(exception.ValidationError,
self.controller.delete_group,
req, self.group1.id, body=body)
def test_delete_group_with_invalid_delete_volumes_value_in_body(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": "abcd"}}
self.assertRaises(exception.ValidationError,
self.controller.delete_group,
req, self.group1.id, body=body)
def test_delete_group_with_empty_delete_volumes_value_in_body(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": ""}}
self.assertRaises(exception.ValidationError,
self.controller.delete_group,
req, self.group1.id, body=body)
def test_delete_group_with_group_snapshot(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
g_snapshot = utils.create_group_snapshot(self.ctxt, self.group1.id)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete_group,
req, self.group1.id, body=body)
g_snapshot.destroy()
res_dict = self.controller.delete_group(
req, self.group1.id, body=body)
group = objects.Group.get_by_id(
self.ctxt, self.group1.id)
self.assertEqual(http_client.ACCEPTED, res_dict.status_int)
self.assertEqual(fields.GroupStatus.DELETING, group.status)
def test_delete_group_delete_volumes(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
vol = utils.create_volume(self.ctxt, group_id=self.group1.id)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}}
res_dict = self.controller.delete_group(
req, self.group1.id, body=body)
group = objects.Group.get_by_id(
self.ctxt, self.group1.id)
self.assertEqual(http_client.ACCEPTED, res_dict.status_int)
self.assertEqual(fields.GroupStatus.DELETING, group.status)
vol.destroy()
def test_delete_group_delete_volumes_with_attached_volumes(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
vol = utils.create_volume(self.ctxt, group_id=self.group1.id,
attach_status='attached')
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete_group,
req, self.group1.id, body=body)
vol.destroy()
def test_delete_group_delete_volumes_with_snapshots(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
vol = utils.create_volume(self.ctxt, group_id=self.group1.id)
utils.create_snapshot(self.ctxt, vol.id)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.delete_group,
req, self.group1.id, body=body)
vol.destroy()
def test_delete_group_delete_volumes_with_deleted_snapshots(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
vol = utils.create_volume(self.ctxt, group_id=self.group1.id)
utils.create_snapshot(self.ctxt, vol.id,
status=fields.SnapshotStatus.DELETED,
deleted=True)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"delete": {"delete-volumes": True}}
res_dict = self.controller.delete_group(
req, self.group1.id, body=body)
group = objects.Group.get_by_id(
self.ctxt, self.group1.id)
self.assertEqual(http_client.ACCEPTED, res_dict.status_int)
self.assertEqual(fields.GroupStatus.DELETING, group.status)
vol.destroy()
def test_create_group_failed_no_group_type(self):
name = 'group1'
body = {"group": {"volume_types": [fake.VOLUME_TYPE_ID],
"name": name,
"description":
"Group 1", }}
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=mv.GROUP_VOLUME)
self.assertRaises(exception.ValidationError,
self.controller.create,
req, body=body)
@ddt.data(None, "", " ", "a" * 256)
def test_create_group_failed_invalid_group_type(self, group_type):
name = 'group1'
body = {"group": {"volume_types": [fake.VOLUME_TYPE_ID],
"name": name,
"description": "Group 1",
"group_type": group_type}}
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=mv.GROUP_VOLUME)
self.assertRaises(exception.ValidationError, self.controller.create,
req, body=body)
def test_create_group_failed_no_volume_types(self):
name = 'group1'
body = {"group": {"group_type": fake.GROUP_TYPE_ID,
"name": name,
"description":
"Group 1", }}
req = fakes.HTTPRequest.blank('/v3/%s/groups' % fake.PROJECT_ID,
version=mv.GROUP_VOLUME)
self.assertRaises(exception.ValidationError,
self.controller.create,
req, body=body)
def test_update_group_success(self):
volume_type_id = fake.VOLUME_TYPE_ID
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.host = 'test_host'
# TODO(geguileo): One `volume_type_ids` gets sorted out make proper
# changes here
# self.group1.volume_type_ids = [volume_type_id]
self.group1.save()
remove_volume = utils.create_volume(
self.ctxt,
volume_type_id=volume_type_id,
group_id=self.group1.id)
remove_volume2 = utils.create_volume(
self.ctxt,
volume_type_id=volume_type_id,
group_id=self.group1.id,
status='error')
remove_volume3 = utils.create_volume(
self.ctxt,
volume_type_id=volume_type_id,
group_id=self.group1.id,
status='error_deleting')
self.assertEqual(fields.GroupStatus.AVAILABLE,
self.group1.status)
group_volumes = db.volume_get_all_by_generic_group(
self.ctxt.elevated(),
self.group1.id)
group_vol_ids = [group_vol['id'] for group_vol in group_volumes]
self.assertIn(remove_volume.id, group_vol_ids)
self.assertIn(remove_volume2.id, group_vol_ids)
self.assertIn(remove_volume3.id, group_vol_ids)
add_volume = utils.create_volume(
self.ctxt,
volume_type_id=volume_type_id)
add_volume2 = utils.create_volume(
self.ctxt,
volume_type_id=volume_type_id)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
name = 'newgroup'
description = 'New Group Description'
add_volumes = add_volume.id + "," + add_volume2.id
remove_volumes = ','.join(
[remove_volume.id, remove_volume2.id, remove_volume3.id])
body = {"group": {"name": name,
"description": description,
"add_volumes": add_volumes,
"remove_volumes": remove_volumes, }}
res_dict = self.controller.update(
req, self.group1.id, body=body)
group = objects.Group.get_by_id(
self.ctxt, self.group1.id)
self.assertEqual(http_client.ACCEPTED, res_dict.status_int)
self.assertEqual(fields.GroupStatus.UPDATING,
group.status)
remove_volume.destroy()
remove_volume2.destroy()
remove_volume3.destroy()
add_volume.destroy()
add_volume2.destroy()
@ddt.data(fields.GroupStatus.CREATING, fields.GroupStatus.UPDATING)
def test_update_group_any_state(self, status):
self.group1.status = status
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"group": {"name": "new name",
"description": "new description",
"add_volumes": None,
"remove_volumes": None, }}
res_dict = self.controller.update(
req, self.group1.id, body=body)
self.assertEqual(http_client.ACCEPTED, res_dict.status_int)
group = objects.Group.get_by_id(self.ctxt, self.group1.id)
self.assertEqual("new name", group.name)
self.assertEqual("new description", group.description)
def test_update_group_add_volume_not_found(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"group": {"name": None,
"description": None,
"add_volumes": "fake-volume-uuid",
"remove_volumes": None, }}
self.assertRaises(exception.InvalidVolume,
self.controller.update,
req, self.group1.id, body=body)
def test_update_group_remove_volume_not_found(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"group": {"name": None,
"description": "new description",
"add_volumes": None,
"remove_volumes": "fake-volume-uuid", }}
self.assertRaises(exception.InvalidVolume,
self.controller.update,
req, self.group1.id, body=body)
def test_update_group_empty_parameters(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"group": {"name": None,
"description": None,
"add_volumes": None,
"remove_volumes": None, }}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.update,
req, self.group1.id, body=body)
def test_update_group_add_volume_invalid_state(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
add_volume = utils.create_volume(
self.ctxt,
volume_type_id=fake.VOLUME_TYPE_ID,
status='wrong_status')
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
add_volumes = add_volume.id
body = {"group": {"name": "group1",
"description": "",
"add_volumes": add_volumes,
"remove_volumes": None, }}
self.assertRaises(exception.InvalidVolume,
self.controller.update,
req, self.group1.id, body=body)
add_volume.destroy()
def test_update_group_add_volume_invalid_volume_type(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
wrong_type = fake.VOLUME_TYPE2_ID
add_volume = utils.create_volume(
self.ctxt,
volume_type_id=wrong_type)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
add_volumes = add_volume.id
body = {"group": {"name": "group1",
"description": "",
"add_volumes": add_volumes,
"remove_volumes": None, }}
self.assertRaises(exception.InvalidVolume,
self.controller.update,
req, self.group1.id, body=body)
add_volume.destroy()
def test_update_group_add_volume_already_in_group(self):
self.group1.status = fields.GroupStatus.AVAILABLE
self.group1.save()
add_volume = utils.create_volume(
self.ctxt,
group_id=fake.GROUP2_ID)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
add_volumes = add_volume.id
body = {"group": {"name": "group1",
"description": "",
"add_volumes": add_volumes,
"remove_volumes": None, }}
self.assertRaises(exception.InvalidVolume,
self.controller.update,
req, self.group1.id, body=body)
add_volume.destroy()
@ddt.data(fields.GroupStatus.CREATING, fields.GroupStatus.UPDATING)
def test_update_group_invalid_state(self, status):
self.group1.status = status
add_volume = utils.create_volume(
self.ctxt,
volume_type_id=self.volume_type1.id,
host=self.group1.host)
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/update' %
(fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_VOLUME)
body = {"group": {"name": "new name",
"description": None,
"add_volumes": add_volume.id,
"remove_volumes": None, }}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.update,
req, self.group1.id, body=body)
vol = objects.Volume.get_by_id(self.ctxt, add_volume.id)
self.assertEqual(add_volume.status, vol.status)
add_volume.destroy()
@ddt.data((mv.GROUP_TYPE, 'fake_group_001',
fields.GroupStatus.AVAILABLE,
exception.VersionNotFoundForAPIMethod),
(mv.GROUP_SNAPSHOT_RESET_STATUS, 'fake_group_001',
fields.GroupStatus.AVAILABLE,
exception.VersionNotFoundForAPIMethod),
(mv.GROUP_VOLUME_RESET_STATUS, 'fake_group_001',
fields.GroupStatus.AVAILABLE,
exception.GroupNotFound),
(mv.GROUP_VOLUME_RESET_STATUS, None,
'invalid_test_status',
exception.InvalidGroupStatus),
(mv.GROUP_VOLUME_RESET_STATUS, 'fake_group_001',
None,
exception.ValidationError)
)
@ddt.unpack
def test_reset_group_status_illegal(self, version, group_id,
status, exceptions):
g_id = group_id or self.group2.id
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, g_id),
version=version)
body = {"reset_status": {
"status": status
}}
self.assertRaises(exceptions,
self.controller.reset_status,
req, g_id, body=body)
def test_reset_group_without_status(self):
g_id = self.group2.id
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, g_id),
version=mv.GROUP_VOLUME_RESET_STATUS)
body = {"reset_status": {}}
self.assertRaises(exception.ValidationError,
self.controller.reset_status,
req, g_id, body=body)
def test_reset_group_status(self):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group2.id),
version=mv.GROUP_VOLUME_RESET_STATUS)
body = {"reset_status": {
"status": fields.GroupStatus.AVAILABLE
}}
response = self.controller.reset_status(req,
self.group2.id, body=body)
group = objects.Group.get_by_id(self.ctxt, self.group2.id)
self.assertEqual(http_client.ACCEPTED, response.status_int)
self.assertEqual(fields.GroupStatus.AVAILABLE, group.status)
@ddt.data(True, False)
@mock.patch('cinder.scheduler.rpcapi.SchedulerAPI.validate_host_capacity')
def test_create_group_from_src_snap(self, valid_host, mock_validate_host):
self.mock_object(volume_api.API, "create", v3_fakes.fake_volume_create)
group = utils.create_group(self.ctxt,
group_type_id=fake.GROUP_TYPE_ID,
volume_type_ids=[fake.VOLUME_TYPE_ID])
volume = utils.create_volume(
self.ctxt,
group_id=group.id,
volume_type_id=fake.VOLUME_TYPE_ID)
group_snapshot = utils.create_group_snapshot(
self.ctxt, group_id=group.id,
group_type_id=group.group_type_id)
snapshot = utils.create_snapshot(
self.ctxt,
volume.id,
group_snapshot_id=group_snapshot.id,
status=fields.SnapshotStatus.AVAILABLE,
volume_type_id=volume.volume_type_id)
mock_validate_host.return_value = valid_host
test_grp_name = 'test grp'
body = {"create-from-src": {"name": test_grp_name,
"description": "Group 1",
"group_snapshot_id": group_snapshot.id}}
req = fakes.HTTPRequest.blank('/v3/%s/groups/action' %
fake.PROJECT_ID,
version=mv.GROUP_SNAPSHOTS)
if valid_host:
res_dict = self.controller.create_from_src(req, body=body)
self.assertIn('id', res_dict['group'])
self.assertEqual(test_grp_name, res_dict['group']['name'])
grp_ref = objects.Group.get_by_id(
self.ctxt.elevated(), res_dict['group']['id'])
else:
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create_from_src, req, body=body)
groups = objects.GroupList.get_all_by_project(self.ctxt,
fake.PROJECT_ID)
grp_ref = objects.Group.get_by_id(
self.ctxt.elevated(), groups[0]['id'])
grp_ref.destroy()
snapshot.destroy()
volume.destroy()
group.destroy()
group_snapshot.destroy()
@ddt.data(True, False)
@mock.patch('cinder.scheduler.rpcapi.SchedulerAPI.validate_host_capacity')
def test_create_group_from_src_grp(self, host_valid, mock_validate_host):
self.mock_object(volume_api.API, "create", v3_fakes.fake_volume_create)
source_grp = utils.create_group(self.ctxt,
group_type_id=fake.GROUP_TYPE_ID,
volume_type_ids=[fake.VOLUME_TYPE_ID])
volume = utils.create_volume(
self.ctxt,
group_id=source_grp.id,
volume_type_id=fake.VOLUME_TYPE_ID)
mock_validate_host.return_value = host_valid
test_grp_name = 'test cg'
body = {"create-from-src": {"name": test_grp_name,
"description": "Consistency Group 1",
"source_group_id": source_grp.id}}
req = fakes.HTTPRequest.blank('/v3/%s/groups/action' %
fake.PROJECT_ID,
version=mv.GROUP_SNAPSHOTS)
if host_valid:
res_dict = self.controller.create_from_src(req, body=body)
self.assertIn('id', res_dict['group'])
self.assertEqual(test_grp_name, res_dict['group']['name'])
grp = objects.Group.get_by_id(
self.ctxt, res_dict['group']['id'])
grp.destroy()
volume.destroy()
source_grp.destroy()
else:
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.create_from_src, req, body=body)
groups = objects.GroupList.get_all_by_project(self.ctxt,
fake.PROJECT_ID)
grp = objects.Group.get_by_id(
self.ctxt.elevated(), groups[0]['id'])
grp.destroy()
volume.destroy()
source_grp.destroy()
@mock.patch('cinder.volume.volume_utils.is_replicated_spec',
return_value=True)
@mock.patch('cinder.volume.volume_utils.is_group_a_type',
return_value=True)
def test_enable_replication(self, mock_rep_grp_type, mock_rep_vol_type):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id),
version=mv.GROUP_REPLICATION)
self.group3.status = fields.GroupStatus.AVAILABLE
self.group3.save()
body = {"enable_replication": {}}
response = self.controller.enable_replication(req,
self.group3.id,
body=body)
group = objects.Group.get_by_id(self.ctxt, self.group3.id)
self.assertEqual(202, response.status_int)
self.assertEqual(fields.GroupStatus.AVAILABLE, group.status)
self.assertEqual(fields.ReplicationStatus.ENABLING,
group.replication_status)
@ddt.data((True, False), (False, True), (False, False))
@ddt.unpack
@mock.patch('cinder.volume.volume_utils.is_replicated_spec')
@mock.patch('cinder.volume.volume_utils.is_group_a_type')
def test_enable_replication_wrong_type(self, is_grp_rep_type,
is_vol_rep_type,
mock_rep_grp_type,
mock_rep_vol_type):
mock_rep_grp_type.return_value = is_grp_rep_type
mock_rep_vol_type.return_value = is_vol_rep_type
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id),
version=mv.GROUP_REPLICATION)
self.group3.status = fields.GroupStatus.AVAILABLE
self.group3.save()
body = {"enable_replication": {}}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.enable_replication,
req, self.group3.id, body=body)
@mock.patch('cinder.volume.volume_utils.is_replicated_spec',
return_value=False)
@mock.patch('cinder.volume.volume_utils.is_group_a_type',
return_value=True)
def test_enable_replication_wrong_group_type(self, mock_rep_grp_type,
mock_rep_vol_type):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id),
version=mv.GROUP_REPLICATION)
self.group3.status = fields.GroupStatus.AVAILABLE
self.group3.save()
body = {"enable_replication": {}}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.enable_replication,
req, self.group3.id, body=body)
@mock.patch('cinder.volume.volume_utils.is_replicated_spec',
return_value=True)
@mock.patch('cinder.volume.volume_utils.is_group_a_type',
return_value=True)
@ddt.data((mv.GROUP_REPLICATION, True,
fields.GroupStatus.CREATING,
webob.exc.HTTPBadRequest),
(mv.GROUP_REPLICATION, False,
fields.GroupStatus.AVAILABLE,
exception.GroupNotFound),
(INVALID_GROUP_REPLICATION, True,
fields.GroupStatus.AVAILABLE,
exception.VersionNotFoundForAPIMethod),
)
@ddt.unpack
def test_enable_replication_negative(self, version, not_fake,
status, exceptions,
mock_rep_grp_type, mock_rep_vol_type):
if not_fake:
group_id = self.group3.id
else:
group_id = fake.GROUP_ID
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, group_id),
version=version)
if not_fake:
self.group3.status = status
self.group3.save()
body = {"enable_replication": {}}
self.assertRaises(exceptions,
self.controller.enable_replication,
req, group_id, body=body)
@mock.patch('cinder.volume.volume_utils.is_replicated_spec',
return_value=True)
@mock.patch('cinder.volume.volume_utils.is_group_a_type',
return_value=True)
def test_disable_replication(self, mock_rep_grp_type, mock_rep_vol_type):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id),
version=mv.GROUP_REPLICATION)
self.group3.status = fields.GroupStatus.AVAILABLE
self.group3.replication_status = fields.ReplicationStatus.ENABLED
self.group3.save()
body = {"disable_replication": {}}
response = self.controller.disable_replication(req,
self.group3.id,
body=body)
group = objects.Group.get_by_id(self.ctxt, self.group3.id)
self.assertEqual(202, response.status_int)
self.assertEqual(fields.GroupStatus.AVAILABLE, group.status)
self.assertEqual(fields.ReplicationStatus.DISABLING,
group.replication_status)
@mock.patch('cinder.volume.volume_utils.is_replicated_spec',
return_value=True)
@mock.patch('cinder.volume.volume_utils.is_group_a_type',
return_value=True)
@ddt.data((mv.GROUP_REPLICATION, True,
fields.GroupStatus.CREATING,
fields.ReplicationStatus.ENABLED,
webob.exc.HTTPBadRequest),
(mv.GROUP_REPLICATION, True,
fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.DISABLED,
webob.exc.HTTPBadRequest),
(mv.GROUP_REPLICATION, False,
fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.DISABLED,
exception.GroupNotFound),
(INVALID_GROUP_REPLICATION, True,
fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.ENABLED,
exception.VersionNotFoundForAPIMethod),
)
@ddt.unpack
def test_disable_replication_negative(self, version, not_fake,
status, rep_status, exceptions,
mock_rep_grp_type,
mock_rep_vol_type):
if not_fake:
group_id = self.group3.id
else:
group_id = fake.GROUP_ID
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, group_id),
version=version)
if not_fake:
self.group3.status = status
self.group3.replication_status = rep_status
self.group3.save()
body = {"disable_replication": {}}
self.assertRaises(exceptions,
self.controller.disable_replication,
req, group_id, body=body)
@mock.patch('cinder.volume.volume_utils.is_replicated_spec',
return_value=True)
@mock.patch('cinder.volume.volume_utils.is_group_a_type',
return_value=True)
def test_failover_replication(self, mock_rep_grp_type, mock_rep_vol_type):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id),
version=mv.GROUP_REPLICATION)
self.group3.status = fields.GroupStatus.AVAILABLE
self.group3.replication_status = fields.ReplicationStatus.ENABLED
self.group3.save()
body = {"failover_replication": {}}
response = self.controller.failover_replication(req,
self.group3.id,
body=body)
group = objects.Group.get_by_id(self.ctxt, self.group3.id)
self.assertEqual(202, response.status_int)
self.assertEqual(fields.GroupStatus.AVAILABLE, group.status)
self.assertEqual(fields.ReplicationStatus.FAILING_OVER,
group.replication_status)
@mock.patch('cinder.volume.volume_utils.is_replicated_spec',
return_value=True)
@mock.patch('cinder.volume.volume_utils.is_group_a_type',
return_value=True)
@ddt.data((mv.GROUP_REPLICATION, True,
fields.GroupStatus.CREATING,
fields.ReplicationStatus.ENABLED,
webob.exc.HTTPBadRequest),
(mv.GROUP_REPLICATION, True,
fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.DISABLED,
webob.exc.HTTPBadRequest),
(mv.GROUP_REPLICATION, False,
fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.DISABLED,
exception.GroupNotFound),
(INVALID_GROUP_REPLICATION, True,
fields.GroupStatus.AVAILABLE,
fields.ReplicationStatus.ENABLED,
exception.VersionNotFoundForAPIMethod),
)
@ddt.unpack
def test_failover_replication_negative(self, version, not_fake,
status, rep_status, exceptions,
mock_rep_grp_type,
mock_rep_vol_type):
if not_fake:
group_id = self.group3.id
else:
group_id = fake.GROUP_ID
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, group_id),
version=version)
if not_fake:
self.group3.status = status
self.group3.replication_status = rep_status
self.group3.save()
body = {"failover_replication": {}}
self.assertRaises(exceptions,
self.controller.failover_replication,
req, group_id, body=body)
@mock.patch('cinder.volume.volume_utils.is_replicated_spec',
return_value=True)
@mock.patch('cinder.volume.volume_utils.is_group_a_type',
return_value=True)
@mock.patch('cinder.volume.rpcapi.VolumeAPI.list_replication_targets')
def test_list_replication_targets(self, mock_list_rep_targets,
mock_rep_grp_type, mock_rep_vol_type):
req = fakes.HTTPRequest.blank('/v3/%s/groups/%s/action' %
(fake.PROJECT_ID, self.group3.id),
version=mv.GROUP_REPLICATION)
targets = {
'replication_targets': [
{'backend_id': 'lvm_backend_1'}
]
}
mock_list_rep_targets.return_value = targets
self.group3.status = fields.GroupStatus.AVAILABLE
self.group3.save()
body = {"list_replication_targets": {}}
response = self.controller.list_replication_targets(
req, self.group3.id, body=body)
self.assertIn('replication_targets', response)
self.assertEqual('lvm_backend_1',
response['replication_targets'][0]['backend_id'])
def test_show_group_with_project_id(self):
# If the microversion >= 3.58 and "is_admin=True", "project_id" should
# be contained in the response body.
req = fakes.HTTPRequest.blank(
'/v3/%s/groups/%s' % (fake.PROJECT_ID, self.group1.id),
version=mv.GROUP_GROUPSNAPSHOT_PROJECT_ID,
use_admin_context=True)
res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict))
self.assertEqual(fake.PROJECT_ID,
res_dict['group']['project_id'])
# If the microversion < 3.58, "project_id" should not be
# contained in the response body.
req = fakes.HTTPRequest.blank(
'/v3/%s/groups/%s' %
(fake.PROJECT_ID, self.group1.id),
version=mv.get_prior_version(mv.GROUP_GROUPSNAPSHOT_PROJECT_ID),
use_admin_context=True)
res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict))
self.assertNotIn('project_id', res_dict['group'])
def test_list_groups_with_project_id(self):
self.group1.group_type_id = fake.GROUP_TYPE_ID
self.group1.save()
self.group2.group_type_id = fake.GROUP_TYPE2_ID
self.group2.save()
req = fakes.HTTPRequest.blank(
'/v3/%s/groups/detail' % self.ctxt.project_id,
version=mv.GROUP_GROUPSNAPSHOT_PROJECT_ID,
use_admin_context=True)
res_dict = self.controller.detail(req)
self.assertEqual(1, len(res_dict))
self.assertEqual(self.group1.project_id,
res_dict['groups'][0]['project_id'])
self.assertEqual(self.group2.project_id,
res_dict['groups'][1]['project_id'])
def test_show_group_without_project_id(self):
# If the microversion >= 3.58 and "is_admin=False", "project_id" should
# not be contained in the response body.
req = fakes.HTTPRequest.blank(
'/v3/%s/groups/%s' % (fake.PROJECT_ID, self.group3.id),
version=mv.GROUP_GROUPSNAPSHOT_PROJECT_ID)
res_dict = self.controller.show(req, self.group1.id)
self.assertEqual(1, len(res_dict))
self.assertNotIn('project_id', res_dict['group'])