Files
deb-nova/nova/tests/functional/api_sample_tests/test_volumes.py
Krisztian Gacsal cb3a6bfe1a Pass attachment_id to Cinder when detach a volume
Cinder needs the attachment_id to properly identify which attachment
of a volume to detach. This patch adapts the Cinder driver to pass
the required information. The attachment_id is necessary for volumes
that enable multiple attachments in order to terminate the connection
properly. The attachment_id is retrieved in the API layer where
available and sent through RPC, the RPC version is bumped to 4.7.

Also the translation functions are modified to retrieve all necessary
information for the volume info coming from Cinder including the multiple
attachments. The Nova API returns the volume info in the same format
as earlier.

Co-Authored-By: Ildiko Vancsa <ildiko.vancsa@ericsson.com>

Partially-implements: blueprint multi-attach-volume
Change-Id: I3cdc49924acbdd21d1e6678a3bb4cf7de7f1db1a
2016-01-20 22:16:27 +01:00

337 lines
13 KiB
Python

# Copyright 2012 Nebula, Inc.
# Copyright 2014 IBM Corp.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
import datetime
from nova import context
from nova import objects
from nova.tests.functional.api_sample_tests import api_sample_base
from nova.tests.functional.api_sample_tests import test_servers
from nova.tests.unit.api.openstack import fakes
from nova.tests.unit import fake_block_device
from nova.tests.unit import fake_instance
CONF = cfg.CONF
CONF.import_opt('osapi_compute_extension',
'nova.api.openstack.compute.legacy_v2.extensions')
class SnapshotsSampleJsonTests(api_sample_base.ApiSampleTestBaseV21):
extension_name = "os-volumes"
create_subs = {
'snapshot_name': 'snap-001',
'description': 'Daily backup',
'volume_id': '521752a6-acf6-4b2d-bc7a-119f9148cd8c'
}
def _get_flags(self):
f = super(SnapshotsSampleJsonTests, self)._get_flags()
f['osapi_compute_extension'] = CONF.osapi_compute_extension[:]
f['osapi_compute_extension'].append(
'nova.api.openstack.compute.contrib.volumes.Volumes')
return f
def setUp(self):
super(SnapshotsSampleJsonTests, self).setUp()
self.stub_out("nova.volume.cinder.API.get_all_snapshots",
fakes.stub_snapshot_get_all)
self.stub_out("nova.volume.cinder.API.get_snapshot",
fakes.stub_snapshot_get)
def _create_snapshot(self):
self.stub_out("nova.volume.cinder.API.create_snapshot",
fakes.stub_snapshot_create)
response = self._do_post("os-snapshots",
"snapshot-create-req",
self.create_subs)
return response
def test_snapshots_create(self):
response = self._create_snapshot()
self._verify_response("snapshot-create-resp",
self.create_subs, response, 200)
def test_snapshots_delete(self):
self.stub_out("nova.volume.cinder.API.delete_snapshot",
fakes.stub_snapshot_delete)
self._create_snapshot()
response = self._do_delete('os-snapshots/100')
self.assertEqual(202, response.status_code)
self.assertEqual('', response.content)
def test_snapshots_detail(self):
response = self._do_get('os-snapshots/detail')
self._verify_response('snapshots-detail-resp', {}, response, 200)
def test_snapshots_list(self):
response = self._do_get('os-snapshots')
self._verify_response('snapshots-list-resp', {}, response, 200)
def test_snapshots_show(self):
response = self._do_get('os-snapshots/100')
subs = {
'snapshot_name': 'Default name',
'description': 'Default description'
}
self._verify_response('snapshots-show-resp', subs, response, 200)
class VolumesSampleJsonTest(test_servers.ServersSampleBase):
extension_name = "os-volumes"
def _get_flags(self):
f = super(VolumesSampleJsonTest, self)._get_flags()
f['osapi_compute_extension'] = CONF.osapi_compute_extension[:]
f['osapi_compute_extension'].append(
'nova.api.openstack.compute.contrib.volumes.Volumes')
return f
def _get_volume_id(self):
return 'a26887c6-c47b-4654-abb5-dfadf7d3f803'
def _stub_volume(self, id, displayname="Volume Name",
displaydesc="Volume Description", size=100):
volume = {
'id': id,
'size': size,
'availability_zone': 'zone1:host1',
'status': 'in-use',
'attach_status': 'attached',
'name': 'vol name',
'display_name': displayname,
'display_description': displaydesc,
'created_at': datetime.datetime(2008, 12, 1, 11, 1, 55),
'snapshot_id': None,
'volume_type_id': 'fakevoltype',
'volume_metadata': [],
'volume_type': {'name': 'Backup'},
'multiattach': False,
'attachments': {'3912f2b4-c5ba-4aec-9165-872876fe202e':
{'mountpoint': '/',
'attachment_id':
'a26887c6-c47b-4654-abb5-dfadf7d3f803'
}
}
}
return volume
def _stub_volume_get(self, context, volume_id):
return self._stub_volume(volume_id)
def _stub_volume_delete(self, context, *args, **param):
pass
def _stub_volume_get_all(self, context, search_opts=None):
id = self._get_volume_id()
return [self._stub_volume(id)]
def _stub_volume_create(self, context, size, name, description, snapshot,
**param):
id = self._get_volume_id()
return self._stub_volume(id)
def setUp(self):
super(VolumesSampleJsonTest, self).setUp()
fakes.stub_out_networking(self)
fakes.stub_out_rate_limiting(self.stubs)
self.stub_out("nova.volume.cinder.API.delete",
self._stub_volume_delete)
self.stub_out("nova.volume.cinder.API.get", self._stub_volume_get)
self.stub_out("nova.volume.cinder.API.get_all",
self._stub_volume_get_all)
def _post_volume(self):
subs_req = {
'volume_name': "Volume Name",
'volume_desc': "Volume Description",
}
self.stub_out("nova.volume.cinder.API.create",
self._stub_volume_create)
response = self._do_post('os-volumes', 'os-volumes-post-req',
subs_req)
self._verify_response('os-volumes-post-resp', subs_req, response, 200)
def test_volumes_show(self):
subs = {
'volume_name': "Volume Name",
'volume_desc': "Volume Description",
}
vol_id = self._get_volume_id()
response = self._do_get('os-volumes/%s' % vol_id)
self._verify_response('os-volumes-get-resp', subs, response, 200)
def test_volumes_index(self):
subs = {
'volume_name': "Volume Name",
'volume_desc': "Volume Description",
}
response = self._do_get('os-volumes')
self._verify_response('os-volumes-index-resp', subs, response, 200)
def test_volumes_detail(self):
# For now, index and detail are the same.
# See the volumes api
subs = {
'volume_name': "Volume Name",
'volume_desc': "Volume Description",
}
response = self._do_get('os-volumes/detail')
self._verify_response('os-volumes-detail-resp', subs, response, 200)
def test_volumes_create(self):
self._post_volume()
def test_volumes_delete(self):
self._post_volume()
vol_id = self._get_volume_id()
response = self._do_delete('os-volumes/%s' % vol_id)
self.assertEqual(202, response.status_code)
self.assertEqual('', response.content)
class VolumeAttachmentsSample(test_servers.ServersSampleBase):
extension_name = "os-volumes"
def _stub_db_bdms_get_all_by_instance(self, server_id):
def fake_bdms_get_all_by_instance(context, instance_uuid,
use_slave=False):
bdms = [
fake_block_device.FakeDbBlockDeviceDict(
{'id': 1, 'volume_id': 'a26887c6-c47b-4654-abb5-dfadf7d3f803',
'instance_uuid': server_id, 'source_type': 'volume',
'destination_type': 'volume', 'device_name': '/dev/sdd'}),
fake_block_device.FakeDbBlockDeviceDict(
{'id': 2, 'volume_id': 'a26887c6-c47b-4654-abb5-dfadf7d3f804',
'instance_uuid': server_id, 'source_type': 'volume',
'destination_type': 'volume', 'device_name': '/dev/sdc'})
]
return bdms
self.stub_out('nova.db.block_device_mapping_get_all_by_instance',
fake_bdms_get_all_by_instance)
def _stub_compute_api_get(self):
def fake_compute_api_get(self, context, instance_id,
want_objects=False, expected_attrs=None):
if want_objects:
return fake_instance.fake_instance_obj(
context, **{'uuid': instance_id})
else:
return {'uuid': instance_id}
self.stub_out('nova.compute.api.API.get', fake_compute_api_get)
def _get_flags(self):
f = super(VolumeAttachmentsSample, self)._get_flags()
f['osapi_compute_extension'] = CONF.osapi_compute_extension[:]
f['osapi_compute_extension'].append(
'nova.api.openstack.compute.contrib.volumes.Volumes')
f['osapi_compute_extension'].append(
'nova.api.openstack.compute.contrib.'
'volume_attachment_update.Volume_attachment_update')
return f
def test_attach_volume_to_server(self):
self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get)
self.stub_out('nova.volume.cinder.API.check_attach',
lambda *a, **k: None)
self.stub_out('nova.volume.cinder.API.reserve_volume',
lambda *a, **k: None)
device_name = '/dev/vdd'
bdm = objects.BlockDeviceMapping()
bdm['device_name'] = device_name
self.stub_out(
'nova.compute.manager.ComputeManager.reserve_block_device_name',
lambda *a, **k: bdm)
self.stub_out(
'nova.compute.manager.ComputeManager.attach_volume',
lambda *a, **k: None)
self.stub_out(
'nova.objects.BlockDeviceMapping.get_by_volume_and_instance',
classmethod(lambda *a, **k: None))
volume = fakes.stub_volume_get(None, context.get_admin_context(),
'a26887c6-c47b-4654-abb5-dfadf7d3f803')
subs = {
'volume_id': volume['id'],
'device': device_name
}
server_id = self._post_server()
response = self._do_post('servers/%s/os-volume_attachments'
% server_id,
'attach-volume-to-server-req', subs)
self._verify_response('attach-volume-to-server-resp', subs,
response, 200)
def test_list_volume_attachments(self):
server_id = self._post_server()
self._stub_db_bdms_get_all_by_instance(server_id)
response = self._do_get('servers/%s/os-volume_attachments'
% server_id)
self._verify_response('list-volume-attachments-resp', {},
response, 200)
def test_volume_attachment_detail(self):
server_id = self._post_server()
attach_id = "a26887c6-c47b-4654-abb5-dfadf7d3f803"
self._stub_db_bdms_get_all_by_instance(server_id)
self._stub_compute_api_get()
response = self._do_get('servers/%s/os-volume_attachments/%s'
% (server_id, attach_id))
self._verify_response('volume-attachment-detail-resp', {},
response, 200)
def test_volume_attachment_delete(self):
server_id = self._post_server()
attach_id = "a26887c6-c47b-4654-abb5-dfadf7d3f803"
self._stub_db_bdms_get_all_by_instance(server_id)
self._stub_compute_api_get()
self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get)
self.stub_out('nova.compute.api.API.detach_volume',
lambda *a, **k: None)
response = self._do_delete('servers/%s/os-volume_attachments/%s'
% (server_id, attach_id))
self.assertEqual(202, response.status_code)
self.assertEqual('', response.content)
def test_volume_attachment_update(self):
self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get)
subs = {
'volume_id': 'a26887c6-c47b-4654-abb5-dfadf7d3f805'
}
server_id = self._post_server()
attach_id = 'a26887c6-c47b-4654-abb5-dfadf7d3f803'
self._stub_db_bdms_get_all_by_instance(server_id)
self._stub_compute_api_get()
self.stub_out('nova.volume.cinder.API.get', fakes.stub_volume_get)
self.stub_out('nova.compute.api.API.swap_volume',
lambda *a, **k: None)
response = self._do_put('servers/%s/os-volume_attachments/%s'
% (server_id, attach_id),
'update-volume-req',
subs)
self.assertEqual(202, response.status_code)
self.assertEqual('', response.content)