Return 'DeleteOnTermination' in VolumeAttachment struct
Change-Id: I0e5bbad355a6879209cfd59dddfa6fbdf817dfbe
This commit is contained in:
parent
a136181b3c
commit
3fc223c1e7
|
@ -172,7 +172,6 @@ Volume related:
|
|||
- ModifyVolumeAttribute
|
||||
- kmsKeyId Volume property
|
||||
- iops Volume property
|
||||
- deleteOnTermination property (supported for describing instances only)
|
||||
- volumeType (current implementation isn't AWS compatible) Volume property
|
||||
|
||||
VPC related:
|
||||
|
|
|
@ -19,6 +19,7 @@ from oslo_log import log as logging
|
|||
from ec2api.api import clients
|
||||
from ec2api.api import common
|
||||
from ec2api.api import ec2utils
|
||||
from ec2api import context as ec2_context
|
||||
from ec2api.db import api as db_api
|
||||
from ec2api import exception
|
||||
from ec2api.i18n import _
|
||||
|
@ -71,8 +72,11 @@ def attach_volume(context, volume_id, instance_id, device):
|
|||
raise exception.UnsupportedOperation()
|
||||
cinder = clients.cinder(context)
|
||||
os_volume = cinder.volumes.get(volume['os_id'])
|
||||
return _format_attachment(context, volume, os_volume,
|
||||
instance_id=instance_id)
|
||||
attachment = _format_attachment(context, volume, os_volume,
|
||||
instance_id=instance_id)
|
||||
# NOTE(andrey-mp): nova sets deleteOnTermination=False for attached volume
|
||||
attachment['deleteOnTermination'] = False
|
||||
return attachment
|
||||
|
||||
|
||||
def detach_volume(context, volume_id, instance_id=None, device=None,
|
||||
|
@ -129,7 +133,8 @@ class VolumeDescriber(common.TaggableItemsDescriber):
|
|||
|
||||
def format(self, volume, os_volume):
|
||||
return _format_volume(self.context, volume, os_volume,
|
||||
self.instances, self.snapshots)
|
||||
self.instances, self.os_instances,
|
||||
self.snapshots)
|
||||
|
||||
def get_db_items(self):
|
||||
self.instances = {i['os_id']: i
|
||||
|
@ -139,6 +144,11 @@ class VolumeDescriber(common.TaggableItemsDescriber):
|
|||
return super(VolumeDescriber, self).get_db_items()
|
||||
|
||||
def get_os_items(self):
|
||||
nova = clients.nova(ec2_context.get_os_admin_context())
|
||||
os_instances = nova.servers.list(
|
||||
search_opts={'all_tenants': True,
|
||||
'project_id': self.context.project_id})
|
||||
self.os_instances = {i.id: i for i in os_instances}
|
||||
return clients.cinder(self.context).volumes.list()
|
||||
|
||||
def get_name(self, os_item):
|
||||
|
@ -162,7 +172,7 @@ def describe_volumes(context, volume_id=None, filter=None,
|
|||
return result
|
||||
|
||||
|
||||
def _format_volume(context, volume, os_volume, instances={},
|
||||
def _format_volume(context, volume, os_volume, instances={}, os_instances={},
|
||||
snapshots={}, snapshot_id=None):
|
||||
valid_ec2_api_volume_status_map = {
|
||||
'attaching': 'in-use',
|
||||
|
@ -180,7 +190,8 @@ def _format_volume(context, volume, os_volume, instances={},
|
|||
}
|
||||
if ec2_volume['status'] == 'in-use':
|
||||
ec2_volume['attachmentSet'] = (
|
||||
[_format_attachment(context, volume, os_volume, instances)])
|
||||
[_format_attachment(context, volume, os_volume, instances,
|
||||
os_instances)])
|
||||
else:
|
||||
ec2_volume['attachmentSet'] = {}
|
||||
if snapshot_id is None and os_volume.snapshot_id:
|
||||
|
@ -193,7 +204,7 @@ def _format_volume(context, volume, os_volume, instances={},
|
|||
|
||||
|
||||
def _format_attachment(context, volume, os_volume, instances={},
|
||||
instance_id=None):
|
||||
os_instances={}, instance_id=None):
|
||||
os_attachment = next(iter(os_volume.attachments), {})
|
||||
os_instance_id = os_attachment.get('server_id')
|
||||
if not instance_id and os_instance_id:
|
||||
|
@ -207,4 +218,13 @@ def _format_attachment(context, volume, os_volume, instances={},
|
|||
if os_volume.status in ('attaching', 'detaching') else
|
||||
'attached' if os_attachment else 'detached'),
|
||||
'volumeId': volume['id']}
|
||||
if os_instance_id in os_instances:
|
||||
os_instance = os_instances[os_instance_id]
|
||||
volumes_attached = getattr(os_instance,
|
||||
'os-extended-volumes:volumes_attached', [])
|
||||
volume_attached = next((va for va in volumes_attached
|
||||
if va['id'] == volume['os_id']), None)
|
||||
if volume_attached and 'delete_on_termination' in volume_attached:
|
||||
ec2_attachment['deleteOnTermination'] = (
|
||||
volume_attached['delete_on_termination'])
|
||||
return ec2_attachment
|
||||
|
|
|
@ -169,8 +169,7 @@ class VolumeTest(base.EC2TestCase):
|
|||
self.assertEqual('in-use', volume['State'])
|
||||
self.assertEqual(1, len(volume['Attachments']))
|
||||
attachment = volume['Attachments'][0]
|
||||
if CONF.aws.run_incompatible_tests:
|
||||
self.assertFalse(attachment['DeleteOnTermination'])
|
||||
self.assertFalse(attachment['DeleteOnTermination'])
|
||||
self.assertIsNotNone(attachment['Device'])
|
||||
self.assertEqual(instance_id, attachment['InstanceId'])
|
||||
self.assertEqual(volume_id, attachment['VolumeId'])
|
||||
|
|
|
@ -1540,7 +1540,8 @@ EC2_VOLUME_2 = {
|
|||
'attachmentSet': [{'status': 'attached',
|
||||
'instanceId': ID_EC2_INSTANCE_2,
|
||||
'volumeId': ID_EC2_VOLUME_2,
|
||||
'device': ROOT_DEVICE_NAME_INSTANCE_2}],
|
||||
'device': ROOT_DEVICE_NAME_INSTANCE_2,
|
||||
'deleteOnTermination': False}],
|
||||
'encrypted': False,
|
||||
'volumeType': None,
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import mock
|
||||
|
||||
import ec2api.api.clients
|
||||
from ec2api.tests.unit import base
|
||||
from ec2api.tests.unit import fakes
|
||||
from ec2api.tests.unit import matchers
|
||||
|
@ -22,11 +23,24 @@ from ec2api.tests.unit import tools
|
|||
|
||||
class VolumeTestCase(base.ApiTestCase):
|
||||
|
||||
def test_describe_volumes(self):
|
||||
def setUp(self):
|
||||
super(VolumeTestCase, self).setUp()
|
||||
get_os_admin_context_patcher = (
|
||||
mock.patch('ec2api.context.get_os_admin_context'))
|
||||
self.get_os_admin_context = get_os_admin_context_patcher.start()
|
||||
self.addCleanup(get_os_admin_context_patcher.stop)
|
||||
self.get_os_admin_context.return_value = (
|
||||
self._create_context(auth_token='admin_token'))
|
||||
|
||||
@mock.patch('ec2api.api.clients.nova', wraps=ec2api.api.clients.nova)
|
||||
def test_describe_volumes(self, nova_client_getter):
|
||||
self.cinder.volumes.list.return_value = [
|
||||
fakes.OSVolume(fakes.OS_VOLUME_1),
|
||||
fakes.OSVolume(fakes.OS_VOLUME_2),
|
||||
fakes.OSVolume(fakes.OS_VOLUME_3)]
|
||||
self.nova.servers.list.return_value = [
|
||||
fakes.OSInstance_full(fakes.OS_INSTANCE_1),
|
||||
fakes.OSInstance_full(fakes.OS_INSTANCE_2)]
|
||||
|
||||
self.set_mock_db_items(fakes.DB_VOLUME_1, fakes.DB_VOLUME_2,
|
||||
fakes.DB_INSTANCE_1, fakes.DB_INSTANCE_2,
|
||||
|
@ -40,6 +54,8 @@ class VolumeTestCase(base.ApiTestCase):
|
|||
fakes.EC2_VOLUME_3]},
|
||||
orderless_lists=True))
|
||||
|
||||
nova_client_getter.assert_called_with(
|
||||
self.get_os_admin_context.return_value)
|
||||
self.db_api.get_items.assert_any_call(mock.ANY, 'vol')
|
||||
|
||||
self.db_api.get_items_by_ids = tools.CopyingMock(
|
||||
|
@ -49,6 +65,8 @@ class VolumeTestCase(base.ApiTestCase):
|
|||
self.assertThat(resp, matchers.DictMatches(
|
||||
{'volumeSet': [fakes.EC2_VOLUME_1]},
|
||||
orderless_lists=True))
|
||||
nova_client_getter.assert_called_with(
|
||||
self.get_os_admin_context.return_value)
|
||||
self.db_api.get_items_by_ids.assert_any_call(
|
||||
mock.ANY, set([fakes.ID_EC2_VOLUME_1]))
|
||||
|
||||
|
@ -73,6 +91,7 @@ class VolumeTestCase(base.ApiTestCase):
|
|||
|
||||
def test_describe_volumes_auto_remove(self):
|
||||
self.cinder.volumes.list.return_value = []
|
||||
self.nova.servers.list.return_value = []
|
||||
self.set_mock_db_items(fakes.DB_VOLUME_1, fakes.DB_VOLUME_2)
|
||||
resp = self.execute('DescribeVolumes', {})
|
||||
self.assertThat(resp, matchers.DictMatches(
|
||||
|
@ -87,6 +106,8 @@ class VolumeTestCase(base.ApiTestCase):
|
|||
self.cinder.volumes.list.return_value = [
|
||||
fakes.OSVolume(fakes.OS_VOLUME_1),
|
||||
fakes.OSVolume(fakes.OS_VOLUME_2)]
|
||||
self.nova.servers.list.return_value = [
|
||||
fakes.OSInstance_full(fakes.OS_INSTANCE_2)]
|
||||
|
||||
self.assert_execution_error(
|
||||
'InvalidVolume.NotFound', 'DescribeVolumes',
|
||||
|
@ -150,6 +171,7 @@ class VolumeTestCase(base.ApiTestCase):
|
|||
def test_format_volume_maps_status(self):
|
||||
fake_volume = fakes.OSVolume(fakes.OS_VOLUME_1)
|
||||
self.cinder.volumes.list.return_value = [fake_volume]
|
||||
self.nova.servers.list.return_value = []
|
||||
self.set_mock_db_items(fakes.DB_VOLUME_1)
|
||||
|
||||
fake_volume.status = 'creating'
|
||||
|
@ -180,7 +202,8 @@ class VolumeTestCase(base.ApiTestCase):
|
|||
{'VolumeId': fakes.ID_EC2_VOLUME_3,
|
||||
'InstanceId': fakes.ID_EC2_INSTANCE_2,
|
||||
'Device': '/dev/vdf'})
|
||||
self.assertEqual({'device': '/dev/vdf',
|
||||
self.assertEqual({'deleteOnTermination': False,
|
||||
'device': '/dev/vdf',
|
||||
'instanceId': fakes.ID_EC2_INSTANCE_2,
|
||||
'status': 'attaching',
|
||||
'volumeId': fakes.ID_EC2_VOLUME_3},
|
||||
|
|
Loading…
Reference in New Issue