summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2012-11-30 21:06:34 +0000
committerGerrit Code Review <review@openstack.org>2012-11-30 21:06:34 +0000
commit78c3bb8800c3345553bab3229e29d3797579ee6a (patch)
treed514914a5ac500ba6f6fbb648390629868509fce /nova/api
parent0fc6c8f0d24a015b7ee8dd02cd433533ff6ebd65 (diff)
parent4363a9a11bea40da13074ba1a0b1779a4be8cd39 (diff)
Merge "add metadata support for overlapping networks"
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/metadata/base.py9
-rw-r--r--nova/api/metadata/handler.py122
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