diff --git a/nova/api/metadata/base.py b/nova/api/metadata/base.py index d83e0cbda2..bac4b7c512 100644 --- a/nova/api/metadata/base.py +++ b/nova/api/metadata/base.py @@ -69,10 +69,13 @@ VERSIONS = [ FOLSOM = '2012-08-10' GRIZZLY = '2013-04-04' HAVANA = '2013-10-17' +LIBERTY = '2015-10-15' + OPENSTACK_VERSIONS = [ FOLSOM, GRIZZLY, HAVANA, + LIBERTY, ] VERSION = "version" @@ -80,6 +83,7 @@ CONTENT = "content" CONTENT_DIR = "content" MD_JSON_NAME = "meta_data.json" VD_JSON_NAME = "vendor_data.json" +NW_JSON_NAME = "network_data.json" UD_NAME = "user_data" PASS_NAME = "password" MIME_TYPE_TEXT_PLAIN = "text/plain" @@ -100,7 +104,7 @@ class InstanceMetadata(object): """Instance metadata.""" def __init__(self, instance, address=None, content=None, extra_md=None, - network_info=None, vd_driver=None): + network_info=None, vd_driver=None, network_metadata=None): """Creation of this object should basically cover all time consuming collection. Methods after that should not cause time delays due to network operations or lengthy cpu operations. @@ -147,6 +151,12 @@ class InstanceMetadata(object): if network_info is None: network_info = instance.info_cache.network_info + # expose network metadata + if network_metadata is None: + self.network_metadata = netutils.get_network_metadata(network_info) + else: + self.network_metadata = network_metadata + self.ip_info = \ ec2utils.get_ip_info_for_instance_from_nw_info(network_info) @@ -187,6 +197,7 @@ class InstanceMetadata(object): PASS_NAME: self._password, VD_JSON_NAME: self._vendor_data, MD_JSON_NAME: self._metadata_as_json, + NW_JSON_NAME: self._network_data, VERSION: self._handle_version, CONTENT: self._handle_content} @@ -338,6 +349,8 @@ class InstanceMetadata(object): ret.append(PASS_NAME) if self._check_os_version(HAVANA, version): ret.append(VD_JSON_NAME) + if self._check_os_version(LIBERTY, version): + ret.append(NW_JSON_NAME) return ret @@ -346,6 +359,11 @@ class InstanceMetadata(object): raise KeyError(path) return self.userdata_raw + def _network_data(self, version, path): + if self.network_metadata is None: + return jsonutils.dumps({}) + return jsonutils.dumps(self.network_metadata) + def _password(self, version, path): if self._check_os_version(GRIZZLY, version): return password.handle_password @@ -449,6 +467,10 @@ class InstanceMetadata(object): yield (path, self.lookup(path)) for (cid, content) in six.iteritems(self.content): + if self._check_version(LIBERTY, version, ALL_OPENSTACK_VERSIONS): + path = 'openstack/%s/%s' % (version, NW_JSON_NAME) + yield (path, self.lookup(path)) + yield ('%s/%s/%s' % ("openstack", CONTENT_DIR, cid), content) diff --git a/nova/tests/unit/test_metadata.py b/nova/tests/unit/test_metadata.py index 83695095f4..eece3176b1 100644 --- a/nova/tests/unit/test_metadata.py +++ b/nova/tests/unit/test_metadata.py @@ -98,7 +98,8 @@ def return_non_existing_address(*args, **kwarg): def fake_InstanceMetadata(stubs, inst_data, address=None, sgroups=None, content=None, extra_md=None, - vd_driver=None, network_info=None): + vd_driver=None, network_info=None, + network_metadata=None): content = content or [] extra_md = extra_md or {} if sgroups is None: @@ -111,7 +112,8 @@ def fake_InstanceMetadata(stubs, inst_data, address=None, stubs.Set(api, 'security_group_get_by_instance', sg_get) return base.InstanceMetadata(inst_data, address=address, content=content, extra_md=extra_md, - vd_driver=vd_driver, network_info=network_info) + vd_driver=vd_driver, network_info=network_info, + network_metadata=network_metadata) def fake_request(stubs, mdinst, relpath, address="127.0.0.1", @@ -305,6 +307,14 @@ class MetadataTestCase(test.TestCase): base.InstanceMetadata(fake_inst_obj(self.context), network_info=network_info) + @mock.patch.object(netutils, "get_network_metadata", autospec=True) + def test_InstanceMetadata_gets_network_metadata(self, mock_netutils): + network_data = {'links': [], 'networks': [], 'services': []} + mock_netutils.return_value = network_data + + md = base.InstanceMetadata(fake_inst_obj(self.context)) + self.assertEqual(network_data, md.network_metadata) + def test_InstanceMetadata_invoke_metadata_for_config_drive(self): fakes.stub_out_key_pair_funcs(self.stubs) inst = self.instance.obj_clone() @@ -381,6 +391,14 @@ class OpenStackMetadataTestCase(test.TestCase): listing = mdinst.lookup("/openstack/2012-08-10") self.assertIn("meta_data.json", listing) + def test_returns_apis_supported_in_liberty_version(self): + mdinst = fake_InstanceMetadata(self.stubs, self.instance) + liberty_supported_apis = mdinst.lookup("/openstack/2015-10-15") + + self.assertEqual([base.MD_JSON_NAME, base.UD_NAME, base.PASS_NAME, + base.VD_JSON_NAME, base.NW_JSON_NAME], + liberty_supported_apis) + def test_returns_apis_supported_in_havana_version(self): mdinst = fake_InstanceMetadata(self.stubs, self.instance) havana_supported_apis = mdinst.lookup("/openstack/2013-10-17") @@ -562,6 +580,43 @@ class OpenStackMetadataTestCase(test.TestCase): for k, v in mydata.items(): self.assertEqual(vd[k], v) + def test_network_data_presence(self): + inst = self.instance.obj_clone() + mdinst = fake_InstanceMetadata(self.stubs, inst) + + # verify that 2015-10-15 has the network_data.json file + result = mdinst.lookup("/openstack/2015-10-15") + self.assertIn('network_data.json', result) + + # verify that older version do not have it + result = mdinst.lookup("/openstack/2013-10-17") + self.assertNotIn('network_data.json', result) + + def test_network_data_response(self): + inst = self.instance.obj_clone() + + nw_data = { + "links": [{"ethernet_mac_address": "aa:aa:aa:aa:aa:aa", + "id": "nic0", "type": "ethernet", "vif_id": 1, + "mtu": 1500}], + "networks": [{"id": "network0", "ip_address": "10.10.0.2", + "link": "nic0", "netmask": "255.255.255.0", + "network_id": + "00000000-0000-0000-0000-000000000000", + "routes": [], "type": "ipv4"}], + "services": [{'address': '1.2.3.4', 'type': 'dns'}]} + + mdinst = fake_InstanceMetadata(self.stubs, inst, + network_metadata=nw_data) + + # verify that 2015-10-15 has the network_data.json file + nwpath = "/openstack/2015-10-15/network_data.json" + nw = jsonutils.loads(mdinst.lookup(nwpath)) + + # check the other expected values + for k, v in nw_data.items(): + self.assertEqual(nw[k], v) + class MetadataHandlerTestCase(test.TestCase): """Test that metadata is returning proper values."""