diff options
| author | Jenkins <jenkins@review.openstack.org> | 2012-11-30 21:06:34 +0000 |
|---|---|---|
| committer | Gerrit Code Review <review@openstack.org> | 2012-11-30 21:06:34 +0000 |
| commit | 78c3bb8800c3345553bab3229e29d3797579ee6a (patch) | |
| tree | d514914a5ac500ba6f6fbb648390629868509fce /nova/api | |
| parent | 0fc6c8f0d24a015b7ee8dd02cd433533ff6ebd65 (diff) | |
| parent | 4363a9a11bea40da13074ba1a0b1779a4be8cd39 (diff) | |
Merge "add metadata support for overlapping networks"
Diffstat (limited to 'nova/api')
| -rw-r--r-- | nova/api/metadata/base.py | 9 | ||||
| -rw-r--r-- | nova/api/metadata/handler.py | 122 |
2 files changed, 117 insertions, 14 deletions
diff --git a/nova/api/metadata/base.py b/nova/api/metadata/base.py index 8e5bcb370..b271662b8 100644 --- a/nova/api/metadata/base.py +++ b/nova/api/metadata/base.py @@ -384,7 +384,14 @@ 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_by_uuid(ctxt, fixed_ip['instance_uuid']) + return get_metadata_by_instance_id(fixed_ip['instance_uuid'], + address, + ctxt) + + +def get_metadata_by_instance_id(instance_id, address, ctxt=None): + ctxt = ctxt or context.get_admin_context() + instance = db.instance_get_by_uuid(ctxt, instance_id) return InstanceMetadata(instance, address) diff --git a/nova/api/metadata/handler.py b/nova/api/metadata/handler.py index bbf1f9318..06fdce30e 100644 --- a/nova/api/metadata/handler.py +++ b/nova/api/metadata/handler.py @@ -17,6 +17,8 @@ # under the License. """Metadata request handler.""" +import hashlib +import hmac import os import webob.dec @@ -28,10 +30,26 @@ from nova.openstack.common import cfg from nova.openstack.common import log as logging from nova import wsgi +CACHE_EXPIRATION = 15 # in seconds + CONF = cfg.CONF CONF.import_opt('memcached_servers', 'nova.config') CONF.import_opt('use_forwarded_for', 'nova.api.auth') +metadata_proxy_opts = [ + cfg.BoolOpt( + 'service_quantum_metadata_proxy', + default=False, + help='Set flag to indicate Quantum will proxy metadata requests and ' + 'resolve instance ids.'), + cfg.StrOpt( + 'quantum_metadata_proxy_shared_secret', + default='', + help='Shared secret to validate proxies Quantum metadata requests') +] + +CONF.register_opts(metadata_proxy_opts) + LOG = logging.getLogger(__name__) if CONF.memcached_servers: @@ -46,7 +64,7 @@ class MetadataRequestHandler(wsgi.Application): def __init__(self): self._cache = memcache.Client(CONF.memcached_servers, debug=0) - def get_metadata(self, address): + def get_metadata_by_remote_address(self, address): if not address: raise exception.FixedIpNotFoundForAddress(address=address) @@ -60,35 +78,113 @@ class MetadataRequestHandler(wsgi.Application): except exception.NotFound: return None - self._cache.set(cache_key, data, 15) + self._cache.set(cache_key, data, CACHE_EXPIRATION) + + return data + + def get_metadata_by_instance_id(self, instance_id, address): + cache_key = 'metadata-%s' % instance_id + data = self._cache.get(cache_key) + if data: + return data + + try: + data = base.get_metadata_by_instance_id(instance_id, address) + except exception.NotFound: + return None + + self._cache.set(cache_key, data, CACHE_EXPIRATION) return data @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): + if os.path.normpath("/" + req.path_info) == "/": + return(base.ec2_md_print(base.VERSIONS + ["latest"])) + + if CONF.service_quantum_metadata_proxy: + meta_data = self._handle_instance_id_request(req) + else: + if req.headers.get('X-Instance-ID'): + LOG.warn( + _("X-Instance-ID present in request headers. The " + "'service_quantum_metadata_proxy' option must be enabled" + " to process this header.")) + meta_data = self._handle_remote_ip_request(req) + + if meta_data is None: + raise webob.exc.HTTPNotFound() + + try: + data = meta_data.lookup(req.path_info) + except base.InvalidMetadataPath: + raise webob.exc.HTTPNotFound() + + return base.ec2_md_print(data) + + def _handle_remote_ip_request(self, req): remote_address = req.remote_addr if CONF.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) - if os.path.normpath("/" + req.path_info) == "/": - return(base.ec2_md_print(base.VERSIONS + ["latest"])) - try: - meta_data = self.get_metadata(remote_address) + meta_data = self.get_metadata_by_remote_address(remote_address) except Exception: LOG.exception(_('Failed to get metadata for ip: %s'), remote_address) msg = _('An unknown error has occurred. ' 'Please try your request again.') - exc = webob.exc.HTTPInternalServerError(explanation=unicode(msg)) - return exc + raise webob.exc.HTTPInternalServerError(explanation=unicode(msg)) + if meta_data is None: LOG.error(_('Failed to get metadata for ip: %s'), remote_address) - raise webob.exc.HTTPNotFound() + + return meta_data + + def _handle_instance_id_request(self, req): + instance_id = req.headers.get('X-Instance-ID') + signature = req.headers.get('X-Instance-ID-Signature') + remote_address = req.remote_addr + + # Ensure that only one header was passed + + if instance_id is None: + msg = _('X-Instance-ID header is missing from request.') + elif not isinstance(instance_id, basestring): + msg = _('Multiple X-Instance-ID headers found within request.') + else: + msg = None + + if msg: + raise webob.exc.HTTPBadRequest(explanation=msg) + + expected_signature = hmac.new( + CONF.quantum_metadata_proxy_shared_secret, + instance_id, + hashlib.sha256).hexdigest() + + if expected_signature != signature: + if instance_id: + w = _('X-Instance-ID-Signature: %(signature)s does not match ' + 'the expected value: %(expected_signature)s for id: ' + '%(instance_id)s. Request From: %(remote_address)s') + LOG.warn(w % locals()) + + msg = _('Invalid proxy request signature.') + raise webob.exc.HTTPForbidden(explanation=msg) try: - data = meta_data.lookup(req.path_info) - except base.InvalidMetadataPath: - raise webob.exc.HTTPNotFound() + meta_data = self.get_metadata_by_instance_id(instance_id, + remote_address) + except Exception: + LOG.exception(_('Failed to get metadata for instance id: %s'), + instance_id) + msg = _('An unknown error has occurred. ' + 'Please try your request again.') + raise webob.exc.HTTPInternalServerError(explanation=unicode(msg)) - return base.ec2_md_print(data) + if meta_data is None: + LOG.error(_('Failed to get metadata for instance id: %s'), + instance_id) + + return meta_data |
