From 29b1b4c79360e550cc89a9736dee56576aa5b972 Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Sun, 1 Jul 2012 19:14:05 +1200 Subject: Make metadata content match the requested version of the metadata API. Currently the same metadata is returned no matter what metadata version is requested. Metadata is currently implemented to version 2009-04-04 (apart from existing TODOs) as defined here: http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html This change adds version checking conditionals throughout get_ec2_metadata and only includes the metadata if the requested version should contain it. When there is a difference between Amazon API implementation and documentation I've gone with replicating the implementation. So far differences include: - undocumented hostname introduced in 1.0, deprecated in favour of local-hostname - kernel-id and ramdisk-id seem have to been implemented in 2007-12-15, not 2008-02-01 as documented get_ec2_metadata has also been reordered so that data is added oldest to newest by API version. In a future change I may attempt to add unimplemented API versions (2011-01-01, 2012-06-01). Change-Id: Ibf6e6e3b9807f29dccd31006515df550bf9bf57b --- nova/api/metadata/base.py | 71 +++++++++++++++++++++++++++++---------------- nova/tests/test_metadata.py | 12 ++++++++ 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/nova/api/metadata/base.py b/nova/api/metadata/base.py index 582bdec82..faa53f93e 100644 --- a/nova/api/metadata/base.py +++ b/nova/api/metadata/base.py @@ -115,27 +115,16 @@ class InstanceMetadata(): floating_ip = floating_ips and floating_ips[0] or '' fmt_sgroups = [x['name'] for x in self.security_groups] - data = { - 'meta-data': { - 'ami-launch-index': self.instance['launch_index'], - 'ami-manifest-path': 'FIXME', - 'block-device-mapping': self.mappings, - 'hostname': hostname, - 'instance-action': 'none', - 'instance-type': self.instance['instance_type']['name'], - 'local-hostname': hostname, - 'local-ipv4': self.address, - 'placement': {'availability-zone': self.availability_zone}, - 'public-hostname': hostname, - 'public-ipv4': floating_ip, - 'reservation-id': self.instance['reservation_id'], - 'security-groups': fmt_sgroups}} - - for key in self.ec2_ids: - data['meta-data'][key] = self.ec2_ids[key] - if self.userdata_b64 is not None: - data['user-data'] = self.userdata_b64 + meta_data = { + 'ami-id': self.ec2_ids['ami-id'], + 'ami-launch-index': self.instance['launch_index'], + 'ami-manifest-path': 'FIXME', + 'instance-id': self.ec2_ids['instance-id'], + 'hostname': hostname, + 'local-ipv4': self.address, + 'reservation-id': self.instance['reservation_id'], + 'security-groups': fmt_sgroups} # public keys are strangely rendered in ec2 metadata service # meta-data/public-keys/ returns '0=keyname' (with no trailing /) @@ -147,17 +136,49 @@ class InstanceMetadata(): # meta-data/public-keys/0/ : 'openssh-key' # meta-data/public-keys/0/openssh-key : '%s' % publickey if self.instance['key_name']: - data['meta-data']['public-keys'] = { + meta_data['public-keys'] = { '0': {'_name': "0=" + self.instance['key_name'], 'openssh-key': self.instance['key_data']}} - if False: # TODO(vish): store ancestor ids - data['ancestor-ami-ids'] = [] - if False: # TODO(vish): store product codes - data['product-codes'] = [] + if self._check_version('2007-01-19', version): + meta_data['local-hostname'] = hostname + meta_data['public-hostname'] = hostname + meta_data['public-ipv4'] = floating_ip + + if False and self._check_version('2007-03-01', version): + # TODO(vish): store product codes + meta_data['product-codes'] = [] + + if self._check_version('2007-08-29', version): + meta_data['instance-type'] = self.instance['instance_type']['name'] + + if False and self._check_version('2007-10-10', version): + # TODO(vish): store ancestor ids + meta_data['ancestor-ami-ids'] = [] + + if self._check_version('2007-12-15', version): + meta_data['block-device-mapping'] = self.mappings + if 'kernel-id' in self.ec2_ids: + meta_data['kernel-id'] = self.ec2_ids['kernel-id'] + if 'ramdisk-id' in self.ec2_ids: + meta_data['ramdisk-id'] = self.ec2_ids['ramdisk-id'] + + if self._check_version('2008-02-01', version): + meta_data['placement'] = {'availability-zone': + self.availability_zone} + + if self._check_version('2008-09-01', version): + meta_data['instance-action'] = 'none' + + data = {'meta-data': meta_data} + if self.userdata_b64 is not None: + data['user-data'] = self.userdata_b64 return data + def _check_version(self, required, requested): + return VERSIONS.index(requested) >= VERSIONS.index(required) + def lookup(self, path): if path == "" or path[0] != "/": path = os.path.normpath("/" + path) diff --git a/nova/tests/test_metadata.py b/nova/tests/test_metadata.py index cf48512f0..62822f721 100644 --- a/nova/tests/test_metadata.py +++ b/nova/tests/test_metadata.py @@ -211,6 +211,18 @@ class MetadataTestCase(test.TestCase): self.assertTrue(re.match('aki-[0-9a-f]{8}', data['meta-data']['kernel-id'])) + def test_check_version(self): + inst = copy(self.instance) + md = fake_InstanceMetadata(self.stubs, inst) + + self.assertTrue(md._check_version('1.0', '2009-04-04')) + self.assertFalse(md._check_version('2009-04-04', '1.0')) + + self.assertFalse(md._check_version('2009-04-04', '2008-09-01')) + self.assertTrue(md._check_version('2008-09-01', '2009-04-04')) + + self.assertTrue(md._check_version('2009-04-04', '2009-04-04')) + class MetadataHandlerTestCase(test.TestCase): """Test that metadata is returning proper values.""" -- cgit