Add encrypted flag to volumes

Currently, the only way to determine whether a volume is encrypted
is by retrieving the encryption metadata about the volume (through
cinder.api.contrib.volume_encryption_metadata) and checking the
encryption_key_id value.  This patch adds an "encrypted" flag to
the basic volume api to enable other services (like Horizon: see
patch https://review.openstack.org/#/c/71125) to easily tell
whether a volume is encrypted using a basic get call,instead of
requiring an additional call.

Implements blueprint encrypt-cinder-volumes
https://blueprints.launchpad.net/nova/+spec/encrypt-cinder-volumes

Change-Id: Id8e422135f17795de06589930afd0309fde28fd1
This commit is contained in:
Brianna Poulos 2014-02-10 16:13:59 -05:00
parent ae5ae5079e
commit 3caa76a2b2
5 changed files with 75 additions and 3 deletions

View File

@ -106,6 +106,8 @@ def _translate_volume_summary_view(context, vol, image_id=None):
d['snapshot_id'] = vol['snapshot_id']
d['source_volid'] = vol['source_volid']
d['encrypted'] = vol['encryption_key_id'] is not None
if image_id:
d['image_id'] = image_id

View File

@ -66,10 +66,15 @@ class ViewBuilder(common.ViewBuilder):
'metadata': self._get_volume_metadata(volume),
'links': self._get_links(request, volume['id']),
'user_id': volume.get('user_id'),
'bootable': str(volume.get('bootable')).lower()
'bootable': str(volume.get('bootable')).lower(),
'encrypted': self._is_volume_encrypted(volume)
}
}
def _is_volume_encrypted(self, volume):
"""Determine if volume is encrypted."""
return volume.get('encryption_key_id') is not None
def _get_attachments(self, volume):
"""Retrieve the attachments of the volume object."""
attachments = []

View File

@ -90,6 +90,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'Volume Test Desc',
'availability_zone': 'zone1:host1',
'display_name': 'Volume Test Name',
'encrypted': False,
'attachments': [{'device': '/',
'server_id': 'fakeuuid',
'host_name': None,
@ -104,7 +105,8 @@ class VolumeApiTest(test.TestCase):
'id': '1',
'created_at': datetime.datetime(1, 1, 1,
1, 1, 1),
'size': 100}}
'size': 100,
'encrypted': False}}
self.assertEqual(res_dict, expected)
def test_volume_create_with_type(self):
@ -180,6 +182,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'Volume Test Desc',
'availability_zone': 'nova',
'display_name': 'Volume Test Name',
'encrypted': False,
'attachments': [{'device': '/',
'server_id': 'fakeuuid',
'host_name': None,
@ -247,6 +250,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
'display_name': 'Updated Test Name',
'encrypted': False,
'attachments': [{
'id': '1',
'volume_id': '1',
@ -282,6 +286,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
'display_name': 'displayname',
'encrypted': False,
'attachments': [{
'id': '1',
'volume_id': '1',
@ -331,6 +336,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
'display_name': 'Updated Test Name',
'encrypted': False,
'attachments': [{
'id': '1',
'volume_id': '1',
@ -386,6 +392,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
'display_name': 'displayname',
'encrypted': False,
'attachments': [{'device': '/',
'server_id': 'fakeuuid',
'host_name': None,
@ -425,6 +432,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
'display_name': 'displayname',
'encrypted': False,
'attachments': [{'device': '/',
'server_id': 'fakeuuid',
'host_name': None,
@ -453,6 +461,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
'display_name': 'displayname',
'encrypted': False,
'attachments': [{'device': '/',
'server_id': 'fakeuuid',
'host_name': None,
@ -492,6 +501,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
'display_name': 'displayname',
'encrypted': False,
'attachments': [{'device': '/',
'server_id': 'fakeuuid',
'host_name': None,
@ -642,6 +652,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
'display_name': 'displayname',
'encrypted': False,
'attachments': [{'device': '/',
'server_id': 'fakeuuid',
'host_name': None,
@ -673,6 +684,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
'display_name': 'displayname',
'encrypted': False,
'attachments': [],
'bootable': 'false',
'volume_type': 'vol_type_name',
@ -698,6 +710,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
'display_name': 'displayname',
'encrypted': False,
'attachments': [{'device': '/',
'server_id': 'fakeuuid',
'host_name': None,
@ -772,6 +785,7 @@ class VolumeApiTest(test.TestCase):
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
'display_name': 'displayname',
'encrypted': False,
'attachments': [{'device': '/',
'server_id': 'fakeuuid',
'host_name': None,
@ -789,6 +803,26 @@ class VolumeApiTest(test.TestCase):
'size': 1}}
self.assertEqual(res_dict, expected)
def test_volume_show_with_encrypted_volume(self):
def stub_volume_get(self, context, volume_id):
return stubs.stub_volume(volume_id, encryption_key_id='fake_id')
self.stubs.Set(volume_api.API, 'get', stub_volume_get)
req = fakes.HTTPRequest.blank('/v1/volumes/1')
res_dict = self.controller.show(req, 1)
self.assertEqual(res_dict['volume']['encrypted'], True)
def test_volume_show_with_unencrypted_volume(self):
def stub_volume_get(self, context, volume_id):
return stubs.stub_volume(volume_id, encryption_key_id=None)
self.stubs.Set(volume_api.API, 'get', stub_volume_get)
req = fakes.HTTPRequest.blank('/v1/volumes/1')
res_dict = self.controller.show(req, 1)
self.assertEqual(res_dict['volume']['encrypted'], False)
def test_volume_delete(self):
self.stubs.Set(db, 'volume_get', stubs.stub_volume_get_db)

View File

@ -43,6 +43,7 @@ def stub_volume(id, **kwargs):
'snapshot_id': None,
'source_volid': None,
'volume_type_id': '3e196c20-3c06-11e2-81c1-0800200c9a66',
'encryption_key_id': None,
'volume_admin_metadata': [{'key': 'attached_mode', 'value': 'rw'},
{'key': 'readonly', 'value': 'False'}],
'bootable': False,

View File

@ -116,7 +116,8 @@ class VolumeApiTest(test.TestCase):
'source_volid': None,
'status': 'fakestatus',
'user_id': 'fakeuser',
'volume_type': 'vol_type_name'}}
'volume_type': 'vol_type_name',
'encrypted': False}}
self.assertEqual(res_dict, ex)
def test_volume_create_with_type(self):
@ -204,6 +205,7 @@ class VolumeApiTest(test.TestCase):
'bootable': 'false',
'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
'description': 'Volume Test Desc',
'encrypted': False,
'id': '1',
'links':
[{'href': 'http://localhost/v2/fake/volumes/1',
@ -273,6 +275,7 @@ class VolumeApiTest(test.TestCase):
'volume': {
'status': 'fakestatus',
'description': 'displaydesc',
'encrypted': False,
'availability_zone': 'fakeaz',
'bootable': 'false',
'name': 'Updated Test Name',
@ -322,6 +325,7 @@ class VolumeApiTest(test.TestCase):
expected = {'volume': {
'status': 'fakestatus',
'description': 'displaydesc',
'encrypted': False,
'availability_zone': 'fakeaz',
'bootable': 'false',
'name': 'displayname',
@ -382,6 +386,7 @@ class VolumeApiTest(test.TestCase):
expected = {'volume': {
'status': 'fakestatus',
'description': 'displaydesc',
'encrypted': False,
'availability_zone': 'fakeaz',
'bootable': 'false',
'name': 'displayname',
@ -483,6 +488,7 @@ class VolumeApiTest(test.TestCase):
{
'status': 'fakestatus',
'description': 'displaydesc',
'encrypted': False,
'availability_zone': 'fakeaz',
'bootable': 'false',
'name': 'displayname',
@ -541,6 +547,7 @@ class VolumeApiTest(test.TestCase):
{
'status': 'fakestatus',
'description': 'displaydesc',
'encrypted': False,
'availability_zone': 'fakeaz',
'bootable': 'false',
'name': 'displayname',
@ -873,6 +880,7 @@ class VolumeApiTest(test.TestCase):
'volume': {
'status': 'fakestatus',
'description': 'displaydesc',
'encrypted': False,
'availability_zone': 'fakeaz',
'bootable': 'false',
'name': 'displayname',
@ -921,6 +929,7 @@ class VolumeApiTest(test.TestCase):
'volume': {
'status': 'fakestatus',
'description': 'displaydesc',
'encrypted': False,
'availability_zone': 'fakeaz',
'bootable': 'false',
'name': 'displayname',
@ -977,6 +986,7 @@ class VolumeApiTest(test.TestCase):
'volume': {
'status': 'fakestatus',
'description': 'displaydesc',
'encrypted': False,
'availability_zone': 'fakeaz',
'bootable': 'false',
'name': 'displayname',
@ -1012,6 +1022,26 @@ class VolumeApiTest(test.TestCase):
}
self.assertEqual(res_dict, expected)
def test_volume_show_with_encrypted_volume(self):
def stub_volume_get(self, context, volume_id):
return stubs.stub_volume(volume_id, encryption_key_id='fake_id')
self.stubs.Set(volume_api.API, 'get', stub_volume_get)
req = fakes.HTTPRequest.blank('/v2/volumes/1')
res_dict = self.controller.show(req, 1)
self.assertEqual(res_dict['volume']['encrypted'], True)
def test_volume_show_with_unencrypted_volume(self):
def stub_volume_get(self, context, volume_id):
return stubs.stub_volume(volume_id, encryption_key_id=None)
self.stubs.Set(volume_api.API, 'get', stub_volume_get)
req = fakes.HTTPRequest.blank('/v2/volumes/1')
res_dict = self.controller.show(req, 1)
self.assertEqual(res_dict['volume']['encrypted'], False)
def test_volume_delete(self):
self.stubs.Set(volume_api.API, 'get', stubs.stub_volume_get)