Implement BlockDeviceMappings for AWS::EC2::Instance
We should support the BlockDeviceMappings for AWS::EC2::Instance resource to be compatible with AWSCloudFormation. Implements blueprint implement-ec2instance-bdm Change-Id: Ib4d318b429494f312f4d6978631dd0a9721b32ee
This commit is contained in:
parent
22095896d6
commit
73cca5dd2b
@ -120,14 +120,14 @@ class Instance(resource.Resource):
|
||||
PLACEMENT_GROUP_NAME, PRIVATE_IP_ADDRESS, RAM_DISK_ID,
|
||||
SECURITY_GROUPS, SECURITY_GROUP_IDS, NETWORK_INTERFACES,
|
||||
SOURCE_DEST_CHECK, SUBNET_ID, TAGS, NOVA_SCHEDULER_HINTS, TENANCY,
|
||||
USER_DATA, VOLUMES,
|
||||
USER_DATA, VOLUMES, BLOCK_DEVICE_MAPPINGS
|
||||
) = (
|
||||
'ImageId', 'InstanceType', 'KeyName', 'AvailabilityZone',
|
||||
'DisableApiTermination', 'KernelId', 'Monitoring',
|
||||
'PlacementGroupName', 'PrivateIpAddress', 'RamDiskId',
|
||||
'SecurityGroups', 'SecurityGroupIds', 'NetworkInterfaces',
|
||||
'SourceDestCheck', 'SubnetId', 'Tags', 'NovaSchedulerHints', 'Tenancy',
|
||||
'UserData', 'Volumes',
|
||||
'UserData', 'Volumes', 'BlockDeviceMappings'
|
||||
)
|
||||
|
||||
_TAG_KEYS = (
|
||||
@ -148,6 +148,20 @@ class Instance(resource.Resource):
|
||||
'Device', 'VolumeId',
|
||||
)
|
||||
|
||||
_BLOCK_DEVICE_MAPPINGS_KEYS = (
|
||||
DEVICE_NAME, EBS, NO_DEVICE, VIRTUAL_NAME,
|
||||
) = (
|
||||
'DeviceName', 'Ebs', 'NoDevice', 'VirtualName',
|
||||
)
|
||||
|
||||
_EBS_KEYS = (
|
||||
DELETE_ON_TERMINATION, IOPS, SNAPSHOT_ID, VOLUME_SIZE,
|
||||
VOLUME_TYPE,
|
||||
) = (
|
||||
'DeleteOnTermination', 'Iops', 'SnapshotId', 'VolumeSize',
|
||||
'VolumeType'
|
||||
)
|
||||
|
||||
ATTRIBUTES = (
|
||||
AVAILABILITY_ZONE_ATTR, PRIVATE_DNS_NAME, PUBLIC_DNS_NAME, PRIVATE_IP,
|
||||
PUBLIC_IP,
|
||||
@ -308,6 +322,70 @@ class Instance(resource.Resource):
|
||||
}
|
||||
)
|
||||
),
|
||||
BLOCK_DEVICE_MAPPINGS: properties.Schema(
|
||||
properties.Schema.LIST,
|
||||
_('Block device mappings to attach to instance.'),
|
||||
schema=properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
schema={
|
||||
DEVICE_NAME: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('A device name where the volume will be '
|
||||
'attached in the system at /dev/device_name.'
|
||||
'e.g. vdb'),
|
||||
required=True,
|
||||
),
|
||||
EBS: properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
_('The ebs volume to attach to the instance.'),
|
||||
schema={
|
||||
DELETE_ON_TERMINATION: properties.Schema(
|
||||
properties.Schema.BOOLEAN,
|
||||
_('Indicate whether the volume should be '
|
||||
'deleted when the instance is terminated.'),
|
||||
default=True
|
||||
),
|
||||
IOPS: properties.Schema(
|
||||
properties.Schema.NUMBER,
|
||||
_('The number of I/O operations per second '
|
||||
'that the volume supports.'),
|
||||
implemented=False
|
||||
),
|
||||
SNAPSHOT_ID: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('The ID of the snapshot to create '
|
||||
'a volume from.'),
|
||||
),
|
||||
VOLUME_SIZE: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('The size of the volume, in GB. Must be '
|
||||
'equal or greater than the size of the '
|
||||
'snapshot. It is safe to leave this blank '
|
||||
'and have the Compute service infer '
|
||||
'the size.'),
|
||||
),
|
||||
VOLUME_TYPE: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('The volume type.'),
|
||||
implemented=False
|
||||
),
|
||||
},
|
||||
),
|
||||
NO_DEVICE: properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
_('The can be used to unmap a defined device.'),
|
||||
implemented=False
|
||||
),
|
||||
VIRTUAL_NAME: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('The name of the virtual device. The name must be '
|
||||
'in the form ephemeralX where X is a number '
|
||||
'starting from zero (0); for example, ephemeral0.'),
|
||||
implemented=False
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
attributes_schema = {
|
||||
@ -422,6 +500,33 @@ class Instance(resource.Resource):
|
||||
security_groups = None
|
||||
return security_groups
|
||||
|
||||
def _build_block_device_mapping(self, bdm):
|
||||
if not bdm:
|
||||
return None
|
||||
bdm_dict = {}
|
||||
for mapping in bdm:
|
||||
device_name = mapping.get(self.DEVICE_NAME)
|
||||
ebs = mapping.get(self.EBS)
|
||||
if ebs:
|
||||
mapping_parts = []
|
||||
snapshot_id = ebs.get(self.SNAPSHOT_ID)
|
||||
volume_size = ebs.get(self.VOLUME_SIZE)
|
||||
delete = ebs.get(self.DELETE_ON_TERMINATION)
|
||||
|
||||
if snapshot_id:
|
||||
mapping_parts.append(snapshot_id)
|
||||
mapping_parts.append('snap')
|
||||
if volume_size:
|
||||
mapping_parts.append(str(volume_size))
|
||||
else:
|
||||
mapping_parts.append('')
|
||||
if delete is not None:
|
||||
mapping_parts.append(str(delete))
|
||||
|
||||
bdm_dict[device_name] = ':'.join(mapping_parts)
|
||||
|
||||
return bdm_dict
|
||||
|
||||
def _get_nova_metadata(self, properties):
|
||||
if properties is None or properties.get(self.TAGS) is None:
|
||||
return None
|
||||
@ -460,6 +565,9 @@ class Instance(resource.Resource):
|
||||
nics = self._build_nics(self.properties[self.NETWORK_INTERFACES],
|
||||
security_groups=security_groups,
|
||||
subnet_id=self.properties[self.SUBNET_ID])
|
||||
block_device_mapping = self._build_block_device_mapping(
|
||||
self.properties.get(self.BLOCK_DEVICE_MAPPINGS))
|
||||
|
||||
server = None
|
||||
|
||||
# FIXME(shadower): the instance_user config option is deprecated. Once
|
||||
@ -482,7 +590,8 @@ class Instance(resource.Resource):
|
||||
meta=self._get_nova_metadata(self.properties),
|
||||
scheduler_hints=scheduler_hints,
|
||||
nics=nics,
|
||||
availability_zone=availability_zone)
|
||||
availability_zone=availability_zone,
|
||||
block_device_mapping=block_device_mapping)
|
||||
finally:
|
||||
# Avoid a race condition where the thread could be cancelled
|
||||
# before the ID is stored
|
||||
@ -670,6 +779,23 @@ class Instance(resource.Resource):
|
||||
'/'.join([self.SECURITY_GROUPS, self.SECURITY_GROUP_IDS]),
|
||||
self.NETWORK_INTERFACES)
|
||||
|
||||
# check bdm property
|
||||
# now we don't support without snapshot_id in bdm
|
||||
bdm = self.properties.get(self.BLOCK_DEVICE_MAPPINGS)
|
||||
if bdm:
|
||||
for mapping in bdm:
|
||||
ebs = mapping.get(self.EBS)
|
||||
if ebs:
|
||||
snapshot_id = ebs.get(self.SNAPSHOT_ID)
|
||||
if not snapshot_id:
|
||||
msg = _("SnapshotId is missing, this is required "
|
||||
"when specifying BlockDeviceMappings.")
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
else:
|
||||
msg = _("Ebs is missing, this is required "
|
||||
"when specifying BlockDeviceMappings.")
|
||||
raise exception.StackValidationFailed(message=msg)
|
||||
|
||||
def _detach_volumes_task(self):
|
||||
'''
|
||||
Detach volumes from the instance
|
||||
|
@ -230,7 +230,8 @@ def setup_mocks(mocks, stack, mock_image_constraint=True):
|
||||
security_groups=None,
|
||||
userdata=server_userdata, scheduler_hints=None,
|
||||
meta=None, nics=None,
|
||||
availability_zone=None).AndReturn(
|
||||
availability_zone=None,
|
||||
block_device_mapping=None).AndReturn(
|
||||
fc.servers.list()[4])
|
||||
return fc
|
||||
|
||||
|
@ -57,7 +57,13 @@ wp_template = '''
|
||||
{"Key": "bar", "Value": "eggs"},
|
||||
{"Key": "foo", "Value": "ham"},
|
||||
{"Key": "foo", "Value": "baz"}],
|
||||
"UserData" : "wordpress"
|
||||
"UserData" : "wordpress",
|
||||
"BlockDeviceMappings": [
|
||||
{
|
||||
"DeviceName": "vdb",
|
||||
"Ebs": {"SnapshotId": "9ef5496e-7426-446a-bbc8-01f84d9c9972",
|
||||
"DeleteOnTermination": "True"}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,6 +109,7 @@ class InstancesTest(HeatTestCase):
|
||||
tmpl, stack = self._get_test_template(stack_name, image_id)
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
instance = instances.Instance(name, resource_defns['WebServer'], stack)
|
||||
bdm = {"vdb": "9ef5496e-7426-446a-bbc8-01f84d9c9972:snap::True"}
|
||||
|
||||
self._mock_get_image_id_success(image_id or 'CentOS 5.2', 1)
|
||||
|
||||
@ -120,7 +127,8 @@ class InstancesTest(HeatTestCase):
|
||||
security_groups=None,
|
||||
userdata=mox.IgnoreArg(),
|
||||
scheduler_hints={'foo': ['spam', 'ham', 'baz'], 'bar': 'eggs'},
|
||||
meta=None, nics=None, availability_zone=None).AndReturn(
|
||||
meta=None, nics=None, availability_zone=None,
|
||||
block_device_mapping=bdm).AndReturn(
|
||||
return_server)
|
||||
|
||||
return instance
|
||||
@ -147,6 +155,167 @@ class InstancesTest(HeatTestCase):
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_instance_create_with_BlockDeviceMappings(self):
|
||||
return_server = self.fc.servers.list()[4]
|
||||
instance = self._create_test_instance(return_server,
|
||||
'create_with_bdm')
|
||||
# this makes sure the auto increment worked on instance creation
|
||||
self.assertTrue(instance.id > 0)
|
||||
|
||||
expected_ip = return_server.networks['public'][0]
|
||||
self.assertEqual(expected_ip, instance.FnGetAtt('PublicIp'))
|
||||
self.assertEqual(expected_ip, instance.FnGetAtt('PrivateIp'))
|
||||
self.assertEqual(expected_ip, instance.FnGetAtt('PrivateDnsName'))
|
||||
self.assertEqual(expected_ip, instance.FnGetAtt('PrivateDnsName'))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_build_block_device_mapping(self):
|
||||
return_server = self.fc.servers.list()[1]
|
||||
instance = self._create_test_instance(return_server,
|
||||
'test_build_bdm')
|
||||
self.assertIsNone(instance._build_block_device_mapping([]))
|
||||
self.assertIsNone(instance._build_block_device_mapping(None))
|
||||
|
||||
self.assertEqual({
|
||||
'vdb': '1234:snap:',
|
||||
'vdc': '5678:snap::False',
|
||||
}, instance._build_block_device_mapping([
|
||||
{'DeviceName': 'vdb', 'Ebs': {'SnapshotId': '1234'}},
|
||||
{'DeviceName': 'vdc', 'Ebs': {'SnapshotId': '5678',
|
||||
'DeleteOnTermination': False}},
|
||||
]))
|
||||
|
||||
self.assertEqual({
|
||||
'vdb': '1234:snap:1',
|
||||
'vdc': '5678:snap:2:True',
|
||||
}, instance._build_block_device_mapping([
|
||||
{'DeviceName': 'vdb', 'Ebs': {'SnapshotId': '1234',
|
||||
'VolumeSize': '1'}},
|
||||
{'DeviceName': 'vdc', 'Ebs': {'SnapshotId': '5678',
|
||||
'VolumeSize': '2',
|
||||
'DeleteOnTermination': True}},
|
||||
]))
|
||||
|
||||
def test_validate_BlockDeviceMappings_VolumeSize_valid_str(self):
|
||||
stack_name = 'val_VolumeSize_valid'
|
||||
tmpl, stack = self._setup_test_stack(stack_name)
|
||||
bdm = [{'DeviceName': 'vdb',
|
||||
'Ebs': {'SnapshotId': '1234',
|
||||
'VolumeSize': '1'}}]
|
||||
wsp = tmpl.t['Resources']['WebServer']['Properties']
|
||||
wsp['BlockDeviceMappings'] = bdm
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
instance = instances.Instance('validate_volume_size',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
self._mock_get_image_id_success('F17-x86_64-gold', 1)
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().MultipleTimes().AndReturn(self.fc)
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
self.assertIsNone(instance.validate())
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_validate_BlockDeviceMappings_VolumeSize_invalid_str(self):
|
||||
stack_name = 'val_VolumeSize_valid'
|
||||
tmpl, stack = self._setup_test_stack(stack_name)
|
||||
bdm = [{'DeviceName': 'vdb',
|
||||
'Ebs': {'SnapshotId': '1234',
|
||||
'VolumeSize': 10}}]
|
||||
wsp = tmpl.t['Resources']['WebServer']['Properties']
|
||||
wsp['BlockDeviceMappings'] = bdm
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
instance = instances.Instance('validate_volume_size',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
self._mock_get_image_id_success('F17-x86_64-gold', 1)
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().MultipleTimes().AndReturn(self.fc)
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
instance.validate)
|
||||
self.assertIn("Value must be a string", six.text_type(exc))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_validate_BlockDeviceMappings_without_Ebs_property(self):
|
||||
stack_name = 'without_Ebs'
|
||||
tmpl, stack = self._setup_test_stack(stack_name)
|
||||
bdm = [{'DeviceName': 'vdb'}]
|
||||
wsp = tmpl.t['Resources']['WebServer']['Properties']
|
||||
wsp['BlockDeviceMappings'] = bdm
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
instance = instances.Instance('validate_without_Ebs',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
self._mock_get_image_id_success('F17-x86_64-gold', 1)
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().MultipleTimes().AndReturn(self.fc)
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
instance.validate)
|
||||
self.assertIn("Ebs is missing, this is required",
|
||||
six.text_type(exc))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_validate_BlockDeviceMappings_without_SnapshotId_property(self):
|
||||
stack_name = 'without_SnapshotId'
|
||||
tmpl, stack = self._setup_test_stack(stack_name)
|
||||
bdm = [{'DeviceName': 'vdb',
|
||||
'Ebs': {'VolumeSize': '1'}}]
|
||||
wsp = tmpl.t['Resources']['WebServer']['Properties']
|
||||
wsp['BlockDeviceMappings'] = bdm
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
instance = instances.Instance('validate_without_SnapshotId',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
self._mock_get_image_id_success('F17-x86_64-gold', 1)
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().MultipleTimes().AndReturn(self.fc)
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
instance.validate)
|
||||
self.assertIn("SnapshotId is missing, this is required",
|
||||
six.text_type(exc))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_validate_BlockDeviceMappings_without_DeviceName_property(self):
|
||||
stack_name = 'without_DeviceName'
|
||||
tmpl, stack = self._setup_test_stack(stack_name)
|
||||
bdm = [{'Ebs': {'SnapshotId': '1234',
|
||||
'VolumeSize': '1'}}]
|
||||
wsp = tmpl.t['Resources']['WebServer']['Properties']
|
||||
wsp['BlockDeviceMappings'] = bdm
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
instance = instances.Instance('validate_without_DeviceName',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
self._mock_get_image_id_success('F17-x86_64-gold', 1)
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().MultipleTimes().AndReturn(self.fc)
|
||||
|
||||
self.m.ReplayAll()
|
||||
|
||||
exc = self.assertRaises(exception.StackValidationFailed,
|
||||
instance.validate)
|
||||
excepted_error = ('Property error : WebServer: BlockDeviceMappings '
|
||||
'Property error : BlockDeviceMappings: 0 Property '
|
||||
'error : 0: Property DeviceName not assigned')
|
||||
self.assertIn(excepted_error, six.text_type(exc))
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_instance_create_with_image_id(self):
|
||||
return_server = self.fc.servers.list()[1]
|
||||
instance = self._setup_test_instance(return_server,
|
||||
|
@ -197,7 +197,8 @@ class instancesTest(HeatTestCase):
|
||||
security_groups=None,
|
||||
userdata=server_userdata, scheduler_hints=None, meta=None,
|
||||
nics=[{'port-id': '64d913c1-bcb1-42d2-8f0a-9593dbcaf251'}],
|
||||
availability_zone=None).AndReturn(
|
||||
availability_zone=None,
|
||||
block_device_mapping=None).AndReturn(
|
||||
return_server)
|
||||
self.m.ReplayAll()
|
||||
|
||||
@ -250,7 +251,8 @@ class instancesTest(HeatTestCase):
|
||||
security_groups=None,
|
||||
userdata=server_userdata, scheduler_hints=None, meta=None,
|
||||
nics=[{'port-id': '64d913c1-bcb1-42d2-8f0a-9593dbcaf251'}],
|
||||
availability_zone=None).AndReturn(
|
||||
availability_zone=None,
|
||||
block_device_mapping=None).AndReturn(
|
||||
return_server)
|
||||
self.m.ReplayAll()
|
||||
|
||||
|
@ -137,7 +137,8 @@ class LoadBalancerTest(HeatTestCase):
|
||||
flavor=2, image=746, key_name=key_name,
|
||||
meta=None, nics=None, name=server_name,
|
||||
scheduler_hints=None, userdata=mox.IgnoreArg(),
|
||||
security_groups=None, availability_zone=None).AndReturn(
|
||||
security_groups=None, availability_zone=None,
|
||||
block_device_mapping=None).AndReturn(
|
||||
self.fc.servers.list()[1])
|
||||
if stub_meta:
|
||||
resource.Resource.metadata_set(mox.IgnoreArg()).AndReturn(None)
|
||||
|
@ -82,7 +82,8 @@ class nokeyTest(HeatTestCase):
|
||||
name=utils.PhysName(stack_name, instance.name),
|
||||
security_groups=None,
|
||||
userdata=server_userdata, scheduler_hints=None,
|
||||
meta=None, nics=None, availability_zone=None).AndReturn(
|
||||
meta=None, nics=None, availability_zone=None,
|
||||
block_device_mapping=None).AndReturn(
|
||||
self.fc.servers.list()[1])
|
||||
self.m.ReplayAll()
|
||||
|
||||
|
@ -168,7 +168,8 @@ class ServerTagsTest(HeatTestCase):
|
||||
name=utils.PhysName(stack_name, instance.name),
|
||||
security_groups=None,
|
||||
userdata=server_userdata, scheduler_hints=None,
|
||||
meta=nova_tags, nics=None, availability_zone=None).AndReturn(
|
||||
meta=nova_tags, nics=None, availability_zone=None,
|
||||
block_device_mapping=None).AndReturn(
|
||||
self.fc.servers.list()[1])
|
||||
|
||||
return instance
|
||||
@ -242,7 +243,8 @@ class ServerTagsTest(HeatTestCase):
|
||||
name=mox.IgnoreArg(),
|
||||
security_groups=None,
|
||||
userdata=mox.IgnoreArg(), scheduler_hints=None,
|
||||
meta=nova_tags, nics=None, availability_zone=None).AndReturn(
|
||||
meta=nova_tags, nics=None, availability_zone=None,
|
||||
block_device_mapping=None).AndReturn(
|
||||
self.fc.servers.list()[1])
|
||||
|
||||
return group
|
||||
@ -291,7 +293,8 @@ class ServerTagsTest(HeatTestCase):
|
||||
name=mox.IgnoreArg(),
|
||||
security_groups=None,
|
||||
userdata=mox.IgnoreArg(), scheduler_hints=None,
|
||||
meta=nova_tags, nics=None, availability_zone=None).AndReturn(
|
||||
meta=nova_tags, nics=None, availability_zone=None,
|
||||
block_device_mapping=None).AndReturn(
|
||||
self.fc.servers.list()[1])
|
||||
|
||||
return group
|
||||
|
@ -127,8 +127,9 @@ class SqlAlchemyTest(HeatTestCase):
|
||||
security_groups=None,
|
||||
userdata=mox.IgnoreArg(), scheduler_hints=None,
|
||||
meta=None, nics=None,
|
||||
availability_zone=None).MultipleTimes().AndReturn(
|
||||
fc.servers.list()[4])
|
||||
availability_zone=None,
|
||||
block_device_mapping=None).MultipleTimes().\
|
||||
AndReturn(fc.servers.list()[4])
|
||||
return fc
|
||||
|
||||
def _mock_delete(self, mocks):
|
||||
|
@ -171,7 +171,10 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
{"version": 4, "addr": "5.6.9.8"}],
|
||||
"private": [{"version": 4,
|
||||
"addr": "10.13.12.13"}]},
|
||||
"metadata": {"Server Label": "DB 1"}},
|
||||
"metadata": {"Server Label": "DB 1"},
|
||||
"os-extended-volumes:volumes_attached":
|
||||
[{"id":
|
||||
"66359157-dace-43ab-a7ed-a7e7cd7be59d"}]},
|
||||
{"id": 56789,
|
||||
"name": "server-with-metadata",
|
||||
"OS-EXT-SRV-ATTR:instance_name":
|
||||
@ -218,7 +221,7 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
return (202, None)
|
||||
|
||||
def get_servers_9999(self, **kw):
|
||||
r = {'server': self.get_servers_detail()[1]['servers'][0]}
|
||||
r = {'server': self.get_servers_detail()[1]['servers'][4]}
|
||||
return (200, r)
|
||||
|
||||
def get_servers_9102(self, **kw):
|
||||
|
Loading…
Reference in New Issue
Block a user