separate Metadata logic away from the web service
The changes here are coming as a result of starting on blueprint config-drive-v2 [1]. I wanted to separate out the "Metadata" from the "Metadata Server". Thus, the creation of nova/api/metadata/base.py. The InstanceMetadata in base.py contains most of the logic for presenting metadata. As a result, the Metadata webservice in handler.py greatly simplified. This should make it easier to render duplicate data to a config drive. Additional changes here: * a few more tests * removal of the separate 'Versions' handler. Its now replaced by the single handler. Change-Id: I35fcfd8d7f247763954afc0a9f752f629b243e9b
This commit is contained in:
@@ -3,27 +3,11 @@
|
||||
############
|
||||
[composite:metadata]
|
||||
use = egg:Paste#urlmap
|
||||
/: metaversions
|
||||
/latest: meta
|
||||
/1.0: meta
|
||||
/2007-01-19: meta
|
||||
/2007-03-01: meta
|
||||
/2007-08-29: meta
|
||||
/2007-10-10: meta
|
||||
/2007-12-15: meta
|
||||
/2008-02-01: meta
|
||||
/2008-09-01: meta
|
||||
/2009-04-04: meta
|
||||
|
||||
[pipeline:metaversions]
|
||||
pipeline = ec2faultwrap logrequest metaverapp
|
||||
/: meta
|
||||
|
||||
[pipeline:meta]
|
||||
pipeline = ec2faultwrap logrequest metaapp
|
||||
|
||||
[app:metaverapp]
|
||||
paste.app_factory = nova.api.metadata.handler:Versions.factory
|
||||
|
||||
[app:metaapp]
|
||||
paste.app_factory = nova.api.metadata.handler:MetadataRequestHandler.factory
|
||||
|
||||
|
||||
255
nova/api/metadata/base.py
Normal file
255
nova/api/metadata/base.py
Normal file
@@ -0,0 +1,255 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Instance Metadata information."""
|
||||
|
||||
import base64
|
||||
import os
|
||||
|
||||
from nova.api.ec2 import ec2utils
|
||||
from nova import block_device
|
||||
from nova import compute
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import network
|
||||
from nova import volume
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DECLARE('dhcp_domain', 'nova.network.manager')
|
||||
|
||||
_DEFAULT_MAPPINGS = {'ami': 'sda1',
|
||||
'ephemeral0': 'sda2',
|
||||
'root': block_device.DEFAULT_ROOT_DEV_NAME,
|
||||
'swap': 'sda3'}
|
||||
|
||||
VERSIONS = [
|
||||
'1.0',
|
||||
'2007-01-19',
|
||||
'2007-03-01',
|
||||
'2007-08-29',
|
||||
'2007-10-10',
|
||||
'2007-12-15',
|
||||
'2008-02-01',
|
||||
'2008-09-01',
|
||||
'2009-04-04',
|
||||
]
|
||||
|
||||
|
||||
class InvalidMetadataEc2Version(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidMetadataPath(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InstanceMetadata():
|
||||
"""Instance metadata."""
|
||||
|
||||
def __init__(self, instance, address=None):
|
||||
self.instance = instance
|
||||
|
||||
ctxt = context.get_admin_context()
|
||||
|
||||
services = db.service_get_all_by_host(ctxt.elevated(),
|
||||
instance['host'])
|
||||
self.availability_zone = ec2utils.get_availability_zone_by_host(
|
||||
services, instance['host'])
|
||||
|
||||
self.ip_info = ec2utils.get_ip_info_for_instance(ctxt, instance)
|
||||
|
||||
self.security_groups = db.security_group_get_by_instance(ctxt,
|
||||
instance['id'])
|
||||
|
||||
self.mappings = _format_instance_mapping(ctxt, instance)
|
||||
|
||||
if instance.get('user_data', None) != None:
|
||||
self.userdata_b64 = base64.b64decode(instance['user_data'])
|
||||
else:
|
||||
self.userdata_b64 = None
|
||||
|
||||
self.ec2_ids = {}
|
||||
|
||||
self.ec2_ids['instance-id'] = ec2utils.id_to_ec2_id(instance['id'])
|
||||
self.ec2_ids['ami-id'] = ec2utils.glance_id_to_ec2_id(ctxt,
|
||||
instance['image_ref'])
|
||||
|
||||
for image_type in ['kernel', 'ramdisk']:
|
||||
if self.instance.get('%s_id' % image_type):
|
||||
image_id = self.instance['%s_id' % image_type]
|
||||
image_type = ec2utils.image_type(image_type)
|
||||
ec2_id = ec2utils.glance_id_to_ec2_id(ctxt, image_id,
|
||||
image_type)
|
||||
self.ec2_ids['%s-id' % image_type] = ec2_id
|
||||
|
||||
self.address = address
|
||||
|
||||
def get_ec2_metadata(self, version):
|
||||
if version == "latest":
|
||||
version = VERSIONS[-1]
|
||||
|
||||
if version not in VERSIONS:
|
||||
raise InvalidMetadataEc2Version(version)
|
||||
|
||||
hostname = "%s.%s" % (self.instance['hostname'], FLAGS.dhcp_domain)
|
||||
floating_ips = self.ip_info['floating_ips']
|
||||
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 != None:
|
||||
data['user-data'] = self.userdata_b64
|
||||
|
||||
# public-keys should be in meta-data only if user specified one
|
||||
if self.instance['key_name']:
|
||||
data['meta-data']['public-keys'] = {
|
||||
'0': {'_name': 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'] = []
|
||||
|
||||
return data
|
||||
|
||||
def lookup(self, path):
|
||||
if path == "" or path[0] != "/":
|
||||
path = os.path.normpath("/" + path)
|
||||
else:
|
||||
path = os.path.normpath(path)
|
||||
|
||||
if path == "/":
|
||||
return VERSIONS + ["latest"]
|
||||
|
||||
items = path.split('/')[1:]
|
||||
|
||||
try:
|
||||
md = self.get_ec2_metadata(items[0])
|
||||
except InvalidMetadataEc2Version:
|
||||
raise InvalidMetadataPath(path)
|
||||
|
||||
data = md
|
||||
for i in range(1, len(items)):
|
||||
if isinstance(data, dict) or isinstance(data, list):
|
||||
if items[i] in data:
|
||||
data = data[items[i]]
|
||||
else:
|
||||
raise InvalidMetadataPath(path)
|
||||
else:
|
||||
if i != len(items) - 1:
|
||||
raise InvalidMetadataPath(path)
|
||||
data = data[items[i]]
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_metadata_by_address(address):
|
||||
ctxt = context.get_admin_context()
|
||||
fixed_ip = network.API().get_fixed_ip_by_address(ctxt, address)
|
||||
|
||||
instance = db.instance_get(ctxt, fixed_ip['instance_id'])
|
||||
return InstanceMetadata(instance, address)
|
||||
|
||||
|
||||
def _format_instance_mapping(ctxt, instance):
|
||||
root_device_name = instance['root_device_name']
|
||||
if root_device_name is None:
|
||||
return _DEFAULT_MAPPINGS
|
||||
|
||||
mappings = {}
|
||||
mappings['ami'] = block_device.strip_dev(root_device_name)
|
||||
mappings['root'] = root_device_name
|
||||
default_ephemeral_device = instance.get('default_ephemeral_device')
|
||||
if default_ephemeral_device:
|
||||
mappings['ephemeral0'] = default_ephemeral_device
|
||||
default_swap_device = instance.get('default_swap_device')
|
||||
if default_swap_device:
|
||||
mappings['swap'] = default_swap_device
|
||||
ebs_devices = []
|
||||
|
||||
# 'ephemeralN', 'swap' and ebs
|
||||
for bdm in db.block_device_mapping_get_all_by_instance(
|
||||
ctxt, instance['uuid']):
|
||||
if bdm['no_device']:
|
||||
continue
|
||||
|
||||
# ebs volume case
|
||||
if (bdm['volume_id'] or bdm['snapshot_id']):
|
||||
ebs_devices.append(bdm['device_name'])
|
||||
continue
|
||||
|
||||
virtual_name = bdm['virtual_name']
|
||||
if not virtual_name:
|
||||
continue
|
||||
|
||||
if block_device.is_swap_or_ephemeral(virtual_name):
|
||||
mappings[virtual_name] = bdm['device_name']
|
||||
|
||||
# NOTE(yamahata): I'm not sure how ebs device should be numbered.
|
||||
# Right now sort by device name for deterministic
|
||||
# result.
|
||||
if ebs_devices:
|
||||
nebs = 0
|
||||
ebs_devices.sort()
|
||||
for ebs in ebs_devices:
|
||||
mappings['ebs%d' % nebs] = ebs
|
||||
nebs += 1
|
||||
|
||||
return mappings
|
||||
|
||||
|
||||
def ec2_md_print(data):
|
||||
if isinstance(data, dict):
|
||||
output = ''
|
||||
for key in sorted(data.keys()):
|
||||
if key == '_name':
|
||||
continue
|
||||
output += key
|
||||
if isinstance(data[key], dict):
|
||||
if '_name' in data[key]:
|
||||
output += '=' + str(data[key]['_name'])
|
||||
else:
|
||||
output += '/'
|
||||
output += '\n'
|
||||
return output[:-1]
|
||||
elif isinstance(data, list):
|
||||
return '\n'.join(data)
|
||||
else:
|
||||
return str(data)
|
||||
@@ -18,115 +18,31 @@
|
||||
|
||||
"""Metadata request handler."""
|
||||
|
||||
import base64
|
||||
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from nova.api.ec2 import ec2utils
|
||||
from nova import block_device
|
||||
from nova import compute
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova.api.metadata import base
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import network
|
||||
from nova import volume
|
||||
from nova import wsgi
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DECLARE('use_forwarded_for', 'nova.api.auth')
|
||||
flags.DECLARE('dhcp_domain', 'nova.network.manager')
|
||||
|
||||
if FLAGS.memcached_servers:
|
||||
import memcache
|
||||
else:
|
||||
from nova.common import memorycache as memcache
|
||||
|
||||
_DEFAULT_MAPPINGS = {'ami': 'sda1',
|
||||
'ephemeral0': 'sda2',
|
||||
'root': block_device.DEFAULT_ROOT_DEV_NAME,
|
||||
'swap': 'sda3'}
|
||||
|
||||
|
||||
class Versions(wsgi.Application):
|
||||
|
||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
"""Respond to a request for all versions."""
|
||||
# available api versions
|
||||
versions = [
|
||||
'1.0',
|
||||
'2007-01-19',
|
||||
'2007-03-01',
|
||||
'2007-08-29',
|
||||
'2007-10-10',
|
||||
'2007-12-15',
|
||||
'2008-02-01',
|
||||
'2008-09-01',
|
||||
'2009-04-04',
|
||||
]
|
||||
return ''.join('%s\n' % v for v in versions)
|
||||
|
||||
|
||||
class MetadataRequestHandler(wsgi.Application):
|
||||
"""Serve metadata."""
|
||||
|
||||
def __init__(self):
|
||||
self.network_api = network.API()
|
||||
self.compute_api = compute.API(
|
||||
network_api=self.network_api,
|
||||
volume_api=volume.API())
|
||||
self._cache = memcache.Client(FLAGS.memcached_servers, debug=0)
|
||||
|
||||
def _format_instance_mapping(self, ctxt, instance_ref):
|
||||
root_device_name = instance_ref['root_device_name']
|
||||
if root_device_name is None:
|
||||
return _DEFAULT_MAPPINGS
|
||||
|
||||
mappings = {}
|
||||
mappings['ami'] = block_device.strip_dev(root_device_name)
|
||||
mappings['root'] = root_device_name
|
||||
default_ephemeral_device = instance_ref.get('default_ephemeral_device')
|
||||
if default_ephemeral_device:
|
||||
mappings['ephemeral0'] = default_ephemeral_device
|
||||
default_swap_device = instance_ref.get('default_swap_device')
|
||||
if default_swap_device:
|
||||
mappings['swap'] = default_swap_device
|
||||
ebs_devices = []
|
||||
|
||||
# 'ephemeralN', 'swap' and ebs
|
||||
for bdm in db.block_device_mapping_get_all_by_instance(
|
||||
ctxt, instance_ref['uuid']):
|
||||
if bdm['no_device']:
|
||||
continue
|
||||
|
||||
# ebs volume case
|
||||
if (bdm['volume_id'] or bdm['snapshot_id']):
|
||||
ebs_devices.append(bdm['device_name'])
|
||||
continue
|
||||
|
||||
virtual_name = bdm['virtual_name']
|
||||
if not virtual_name:
|
||||
continue
|
||||
|
||||
if block_device.is_swap_or_ephemeral(virtual_name):
|
||||
mappings[virtual_name] = bdm['device_name']
|
||||
|
||||
# NOTE(yamahata): I'm not sure how ebs device should be numbered.
|
||||
# Right now sort by device name for deterministic
|
||||
# result.
|
||||
if ebs_devices:
|
||||
nebs = 0
|
||||
ebs_devices.sort()
|
||||
for ebs in ebs_devices:
|
||||
mappings['ebs%d' % nebs] = ebs
|
||||
nebs += 1
|
||||
|
||||
return mappings
|
||||
|
||||
def get_metadata(self, address):
|
||||
if not address:
|
||||
raise exception.FixedIpNotFoundForAddress(address=address)
|
||||
@@ -136,110 +52,21 @@ class MetadataRequestHandler(wsgi.Application):
|
||||
if data:
|
||||
return data
|
||||
|
||||
ctxt = context.get_admin_context()
|
||||
try:
|
||||
fixed_ip = self.network_api.get_fixed_ip_by_address(ctxt, address)
|
||||
instance_ref = db.instance_get(ctxt, fixed_ip['instance_id'])
|
||||
data = base.get_metadata_by_address(address)
|
||||
except exception.NotFound:
|
||||
return None
|
||||
|
||||
hostname = "%s.%s" % (instance_ref['hostname'], FLAGS.dhcp_domain)
|
||||
host = instance_ref['host']
|
||||
services = db.service_get_all_by_host(ctxt.elevated(), host)
|
||||
availability_zone = ec2utils.get_availability_zone_by_host(services,
|
||||
host)
|
||||
|
||||
ip_info = ec2utils.get_ip_info_for_instance(ctxt, instance_ref)
|
||||
floating_ips = ip_info['floating_ips']
|
||||
floating_ip = floating_ips and floating_ips[0] or ''
|
||||
|
||||
ec2_id = ec2utils.id_to_ec2_id(instance_ref['id'])
|
||||
image_id = instance_ref['image_ref']
|
||||
ctxt = context.get_admin_context()
|
||||
image_ec2_id = ec2utils.glance_id_to_ec2_id(ctxt, image_id)
|
||||
security_groups = db.security_group_get_by_instance(ctxt,
|
||||
instance_ref['id'])
|
||||
security_groups = [x['name'] for x in security_groups]
|
||||
mappings = self._format_instance_mapping(ctxt, instance_ref)
|
||||
data = {
|
||||
'user-data': base64.b64decode(instance_ref['user_data']),
|
||||
'meta-data': {
|
||||
'ami-id': image_ec2_id,
|
||||
'ami-launch-index': instance_ref['launch_index'],
|
||||
'ami-manifest-path': 'FIXME',
|
||||
'block-device-mapping': mappings,
|
||||
'hostname': hostname,
|
||||
'instance-action': 'none',
|
||||
'instance-id': ec2_id,
|
||||
'instance-type': instance_ref['instance_type']['name'],
|
||||
'local-hostname': hostname,
|
||||
'local-ipv4': address,
|
||||
'placement': {'availability-zone': availability_zone},
|
||||
'public-hostname': hostname,
|
||||
'public-ipv4': floating_ip,
|
||||
'reservation-id': instance_ref['reservation_id'],
|
||||
'security-groups': security_groups}}
|
||||
|
||||
# public-keys should be in meta-data only if user specified one
|
||||
if instance_ref['key_name']:
|
||||
data['meta-data']['public-keys'] = {
|
||||
'0': {'_name': instance_ref['key_name'],
|
||||
'openssh-key': instance_ref['key_data']}}
|
||||
|
||||
for image_type in ['kernel', 'ramdisk']:
|
||||
if instance_ref.get('%s_id' % image_type):
|
||||
image_id = instance_ref['%s_id' % image_type]
|
||||
image_type = ec2utils.image_type(image_type)
|
||||
ec2_id = ec2utils.glance_id_to_ec2_id(ctxt,
|
||||
image_id,
|
||||
image_type)
|
||||
data['meta-data']['%s-id' % image_type] = ec2_id
|
||||
|
||||
if False: # TODO(vish): store ancestor ids
|
||||
data['ancestor-ami-ids'] = []
|
||||
if False: # TODO(vish): store product codes
|
||||
data['product-codes'] = []
|
||||
|
||||
self._cache.set(cache_key, data, 15)
|
||||
|
||||
return data
|
||||
|
||||
def print_data(self, data):
|
||||
if isinstance(data, dict):
|
||||
output = ''
|
||||
for key in data:
|
||||
if key == '_name':
|
||||
continue
|
||||
output += key
|
||||
if isinstance(data[key], dict):
|
||||
if '_name' in data[key]:
|
||||
output += '=' + str(data[key]['_name'])
|
||||
else:
|
||||
output += '/'
|
||||
output += '\n'
|
||||
# Cut off last \n
|
||||
return output[:-1]
|
||||
elif isinstance(data, list):
|
||||
return '\n'.join(data)
|
||||
else:
|
||||
return str(data)
|
||||
|
||||
def lookup(self, path, data):
|
||||
items = path.split('/')
|
||||
for item in items:
|
||||
if item:
|
||||
if not isinstance(data, dict):
|
||||
return data
|
||||
if not item in data:
|
||||
return None
|
||||
data = data[item]
|
||||
return data
|
||||
|
||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
remote_address = req.remote_addr
|
||||
if FLAGS.use_forwarded_for:
|
||||
remote_address = req.headers.get('X-Forwarded-For', remote_address)
|
||||
|
||||
try:
|
||||
meta_data = self.get_metadata(remote_address)
|
||||
except Exception:
|
||||
@@ -252,7 +79,10 @@ class MetadataRequestHandler(wsgi.Application):
|
||||
if meta_data is None:
|
||||
LOG.error(_('Failed to get metadata for ip: %s'), remote_address)
|
||||
raise webob.exc.HTTPNotFound()
|
||||
data = self.lookup(req.path_info, meta_data)
|
||||
if data is None:
|
||||
|
||||
try:
|
||||
data = meta_data.lookup(req.path_info)
|
||||
except base.InvalidMetadataPath:
|
||||
raise webob.exc.HTTPNotFound()
|
||||
return self.print_data(data)
|
||||
|
||||
return base.ec2_md_print(data)
|
||||
|
||||
@@ -19,8 +19,12 @@
|
||||
"""Tests for metadata service."""
|
||||
|
||||
import base64
|
||||
from copy import copy
|
||||
|
||||
import stubout
|
||||
import webob
|
||||
|
||||
from nova.api.metadata import base
|
||||
from nova.api.metadata import handler
|
||||
from nova import db
|
||||
from nova.db.sqlalchemy import api
|
||||
@@ -30,119 +34,110 @@ from nova import network
|
||||
from nova import test
|
||||
from nova.tests import fake_network
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
USER_DATA_STRING = ("This is an encoded string")
|
||||
ENCODE_USER_DATA_STRING = base64.b64encode(USER_DATA_STRING)
|
||||
|
||||
INSTANCES = (
|
||||
{'id': 1,
|
||||
'uuid': 'b65cee2f-8c69-4aeb-be2f-f79742548fc2',
|
||||
'name': 'fake',
|
||||
'project_id': 'test',
|
||||
'key_name': "mykey",
|
||||
'key_data': "ssh-rsa AAAAB3Nzai....N3NtHw== someuser@somehost",
|
||||
'host': 'test',
|
||||
'launch_index': 1,
|
||||
'instance_type': {'name': 'm1.tiny'},
|
||||
'reservation_id': 'r-xxxxxxxx',
|
||||
'user_data': ENCODE_USER_DATA_STRING,
|
||||
'image_ref': 7,
|
||||
'vcpus': 1,
|
||||
'fixed_ips': [],
|
||||
'root_device_name': '/dev/sda1',
|
||||
'info_cache': {'network_info': []},
|
||||
'hostname': 'test'},
|
||||
)
|
||||
|
||||
|
||||
def return_non_existing_address(*args, **kwarg):
|
||||
raise exception.NotFound()
|
||||
|
||||
|
||||
class MetadataTestCase(test.TestCase):
|
||||
"""Test that metadata is returning proper values."""
|
||||
def fake_InstanceMetadata(stubs, inst_data, address=None, sgroups=None):
|
||||
|
||||
if sgroups == None:
|
||||
sgroups = [{'name': 'default'}]
|
||||
|
||||
def sg_get(*args, **kwargs):
|
||||
return sgroups
|
||||
|
||||
stubs.Set(api, 'security_group_get_by_instance', sg_get)
|
||||
return base.InstanceMetadata(inst_data, address=address)
|
||||
|
||||
|
||||
def fake_request(stubs, mdinst, relpath, address="127.0.0.1",
|
||||
fake_get_metadata=None, headers=None):
|
||||
|
||||
def get_metadata(address):
|
||||
return mdinst
|
||||
|
||||
app = handler.MetadataRequestHandler()
|
||||
|
||||
if fake_get_metadata == None:
|
||||
fake_get_metadata = get_metadata
|
||||
|
||||
if stubs:
|
||||
stubs.Set(app, 'get_metadata', fake_get_metadata)
|
||||
|
||||
request = webob.Request.blank(relpath)
|
||||
request.remote_addr = address
|
||||
|
||||
if headers != None:
|
||||
request.headers.update(headers)
|
||||
|
||||
response = request.get_response(app)
|
||||
return response
|
||||
|
||||
|
||||
class MetadataTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(MetadataTestCase, self).setUp()
|
||||
self.instance = ({'id': 1,
|
||||
'uuid': 'b65cee2f-8c69-4aeb-be2f-f79742548fc2',
|
||||
'name': 'fake',
|
||||
'project_id': 'test',
|
||||
'key_name': None,
|
||||
'host': 'test',
|
||||
'launch_index': 1,
|
||||
'instance_type': {'name': 'm1.tiny'},
|
||||
'reservation_id': 'r-xxxxxxxx',
|
||||
'user_data': '',
|
||||
'image_ref': 7,
|
||||
'vcpus': 1,
|
||||
'fixed_ips': [],
|
||||
'root_device_name': '/dev/sda1',
|
||||
'info_cache': {'network_info': []},
|
||||
'hostname': 'test'})
|
||||
|
||||
def fake_get_floating_ips_by_fixed_address(self, context, fixed_ip):
|
||||
return ['1.2.3.4', '5.6.7.8']
|
||||
|
||||
def instance_get(*args, **kwargs):
|
||||
return self.instance
|
||||
|
||||
def instance_get_list(*args, **kwargs):
|
||||
return [self.instance]
|
||||
|
||||
def get_fixed_ip_by_address(*args, **kwargs):
|
||||
return {'instance_id': self.instance['id']}
|
||||
|
||||
fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs,
|
||||
spectacular=True)
|
||||
self.stubs.Set(network.API, 'get_floating_ips_by_fixed_address',
|
||||
fake_get_floating_ips_by_fixed_address)
|
||||
self.stubs.Set(network.API, 'get_fixed_ip_by_address',
|
||||
get_fixed_ip_by_address)
|
||||
self.stubs.Set(api, 'instance_get', instance_get)
|
||||
self.stubs.Set(api, 'instance_get_all_by_filters', instance_get_list)
|
||||
self.app = handler.MetadataRequestHandler()
|
||||
network_manager = fake_network.FakeNetworkManager()
|
||||
self.stubs.Set(self.app.compute_api.network_api,
|
||||
'get_instance_uuids_by_ip_filter',
|
||||
network_manager.get_instance_uuids_by_ip_filter)
|
||||
|
||||
def request(self, relative_url):
|
||||
request = webob.Request.blank(relative_url)
|
||||
request.remote_addr = "127.0.0.1"
|
||||
return request.get_response(self.app).body
|
||||
|
||||
def test_base(self):
|
||||
self.assertEqual(self.request('/'), 'meta-data/\nuser-data')
|
||||
self.instance = INSTANCES[0]
|
||||
|
||||
def test_user_data(self):
|
||||
self.instance['user_data'] = base64.b64encode('happy')
|
||||
self.assertEqual(self.request('/user-data'), 'happy')
|
||||
inst = copy(self.instance)
|
||||
inst['user_data'] = base64.b64encode("happy")
|
||||
md = fake_InstanceMetadata(self.stubs, inst)
|
||||
self.assertEqual(
|
||||
md.get_ec2_metadata(version='2009-04-04')['user-data'], "happy")
|
||||
|
||||
def test_no_user_data(self):
|
||||
inst = copy(self.instance)
|
||||
del inst['user_data']
|
||||
md = fake_InstanceMetadata(self.stubs, inst)
|
||||
obj = object()
|
||||
self.assertEqual(
|
||||
md.get_ec2_metadata(version='2009-04-04').get('user-data', obj),
|
||||
obj)
|
||||
|
||||
def test_security_groups(self):
|
||||
def sg_get(*args, **kwargs):
|
||||
return [{'name': 'default'}, {'name': 'other'}]
|
||||
self.stubs.Set(api, 'security_group_get_by_instance', sg_get)
|
||||
self.assertEqual(self.request('/meta-data/security-groups'),
|
||||
'default\nother')
|
||||
inst = copy(self.instance)
|
||||
sgroups = [{'name': 'default'}, {'name': 'other'}]
|
||||
expected = ['default', 'other']
|
||||
|
||||
def test_user_data_non_existing_fixed_address(self):
|
||||
self.stubs.Set(network.API, 'get_fixed_ip_by_address',
|
||||
return_non_existing_address)
|
||||
request = webob.Request.blank('/user-data')
|
||||
request.remote_addr = "127.1.1.1"
|
||||
response = request.get_response(self.app)
|
||||
self.assertEqual(response.status_int, 404)
|
||||
|
||||
def test_user_data_none_fixed_address(self):
|
||||
request = webob.Request.blank('/user-data')
|
||||
request.remote_addr = None
|
||||
response = request.get_response(self.app)
|
||||
self.assertEqual(response.status_int, 500)
|
||||
|
||||
def test_user_data_invalid_url(self):
|
||||
request = webob.Request.blank('/user-data-invalid')
|
||||
request.remote_addr = "127.0.0.1"
|
||||
response = request.get_response(self.app)
|
||||
self.assertEqual(response.status_int, 404)
|
||||
|
||||
def test_user_data_with_use_forwarded_header(self):
|
||||
self.instance['user_data'] = ENCODE_USER_DATA_STRING
|
||||
self.flags(use_forwarded_for=True)
|
||||
request = webob.Request.blank('/user-data')
|
||||
request.remote_addr = "127.0.0.1"
|
||||
response = request.get_response(self.app)
|
||||
self.assertEqual(response.status_int, 200)
|
||||
self.assertEqual(response.body, USER_DATA_STRING)
|
||||
md = fake_InstanceMetadata(self.stubs, inst, sgroups=sgroups)
|
||||
data = md.get_ec2_metadata(version='2009-04-04')
|
||||
self.assertEqual(data['meta-data']['security-groups'], expected)
|
||||
|
||||
def test_local_hostname_fqdn(self):
|
||||
self.assertEqual(self.request('/meta-data/local-hostname'),
|
||||
md = fake_InstanceMetadata(self.stubs, copy(self.instance))
|
||||
data = md.get_ec2_metadata(version='2009-04-04')
|
||||
self.assertEqual(data['meta-data']['local-hostname'],
|
||||
"%s.%s" % (self.instance['hostname'], FLAGS.dhcp_domain))
|
||||
|
||||
def test_get_instance_mapping(self):
|
||||
"""Make sure that _get_instance_mapping works"""
|
||||
def test_format_instance_mapping(self):
|
||||
"""Make sure that _format_instance_mappings works"""
|
||||
ctxt = None
|
||||
instance_ref0 = {'id': 0,
|
||||
'uuid': 'e5fe5518-0288-4fa3-b0c4-c79764101b85',
|
||||
@@ -180,9 +175,78 @@ class MetadataTestCase(test.TestCase):
|
||||
'swap': '/dev/sdc',
|
||||
'ebs0': '/dev/sdh'}
|
||||
|
||||
self.assertEqual(self.app._format_instance_mapping(ctxt,
|
||||
instance_ref0),
|
||||
handler._DEFAULT_MAPPINGS)
|
||||
self.assertEqual(self.app._format_instance_mapping(ctxt,
|
||||
instance_ref1),
|
||||
self.assertEqual(base._format_instance_mapping(ctxt, instance_ref0),
|
||||
base._DEFAULT_MAPPINGS)
|
||||
self.assertEqual(base._format_instance_mapping(ctxt, instance_ref1),
|
||||
expected)
|
||||
|
||||
|
||||
class MetadataHandlerTestCase(test.TestCase):
|
||||
"""Test that metadata is returning proper values."""
|
||||
|
||||
def setUp(self):
|
||||
super(MetadataHandlerTestCase, self).setUp()
|
||||
|
||||
self.instance = INSTANCES[0]
|
||||
self.mdinst = fake_InstanceMetadata(self.stubs, self.instance,
|
||||
address=None, sgroups=None)
|
||||
|
||||
def test_root(self):
|
||||
expected = "\n".join(base.VERSIONS) + "\nlatest"
|
||||
response = fake_request(self.stubs, self.mdinst, "/")
|
||||
self.assertEqual(response.body, expected)
|
||||
|
||||
response = fake_request(self.stubs, self.mdinst, "/foo/../")
|
||||
self.assertEqual(response.body, expected)
|
||||
|
||||
def test_version_root(self):
|
||||
response = fake_request(self.stubs, self.mdinst, "/2009-04-04")
|
||||
self.assertEqual(response.body, 'meta-data/\nuser-data')
|
||||
|
||||
response = fake_request(self.stubs, self.mdinst, "/9999-99-99")
|
||||
self.assertEqual(response.status_int, 404)
|
||||
|
||||
def test_user_data_non_existing_fixed_address(self):
|
||||
self.stubs.Set(network.API, 'get_fixed_ip_by_address',
|
||||
return_non_existing_address)
|
||||
response = fake_request(None, self.mdinst, "/2009-04-04/user-data",
|
||||
"127.1.1.1")
|
||||
self.assertEqual(response.status_int, 404)
|
||||
|
||||
def test_fixed_address_none(self):
|
||||
response = fake_request(None, self.mdinst,
|
||||
relpath="/2009-04-04/user-data", address=None)
|
||||
self.assertEqual(response.status_int, 500)
|
||||
|
||||
def test_invalid_path_is_404(self):
|
||||
response = fake_request(self.stubs, self.mdinst,
|
||||
relpath="/2009-04-04/user-data-invalid")
|
||||
self.assertEqual(response.status_int, 404)
|
||||
|
||||
def test_user_data_with_use_forwarded_header(self):
|
||||
expected_addr = "192.192.192.2"
|
||||
|
||||
def fake_get_metadata(address):
|
||||
if address == expected_addr:
|
||||
return self.mdinst
|
||||
else:
|
||||
raise Exception("Expected addr of %s, got %s" %
|
||||
(expected_addr, address))
|
||||
|
||||
self.flags(use_forwarded_for=True)
|
||||
response = fake_request(self.stubs, self.mdinst,
|
||||
relpath="/2009-04-04/user-data",
|
||||
address="168.168.168.1",
|
||||
fake_get_metadata=fake_get_metadata,
|
||||
headers={'X-Forwarded-For': expected_addr})
|
||||
|
||||
self.assertEqual(response.status_int, 200)
|
||||
self.assertEqual(response.body,
|
||||
base64.b64decode(self.instance['user_data']))
|
||||
|
||||
response = fake_request(self.stubs, self.mdinst,
|
||||
relpath="/2009-04-04/user-data",
|
||||
address="168.168.168.1",
|
||||
fake_get_metadata=fake_get_metadata,
|
||||
headers=None)
|
||||
self.assertEqual(response.status_int, 500)
|
||||
|
||||
Reference in New Issue
Block a user