160 lines
5.6 KiB
Python
160 lines
5.6 KiB
Python
# Copyright 2012 New Dream Network, LLC (DreamHost)
|
|
#
|
|
# 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.
|
|
|
|
import httplib2
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from oslo_service import wsgi as base_wsgi
|
|
from oslo_utils import encodeutils
|
|
import six
|
|
import six.moves.urllib.parse as urlparse
|
|
import webob
|
|
|
|
from neutron._i18n import _, _LE
|
|
from neutron.agent.linux import daemon
|
|
from neutron.agent.linux import utils as agent_utils
|
|
from neutron.common import config
|
|
from neutron.common import exceptions
|
|
from neutron.common import utils
|
|
from neutron.conf.agent.metadata import namespace_proxy as namespace
|
|
from neutron import wsgi
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class NetworkMetadataProxyHandler(object):
|
|
"""Proxy AF_INET metadata request through Unix Domain socket.
|
|
|
|
The Unix domain socket allows the proxy access resource that are not
|
|
accessible within the isolated tenant context.
|
|
"""
|
|
|
|
def __init__(self, network_id=None, router_id=None):
|
|
self.network_id = network_id
|
|
self.router_id = router_id
|
|
|
|
if network_id is None and router_id is None:
|
|
raise exceptions.NetworkIdOrRouterIdRequiredError()
|
|
|
|
@webob.dec.wsgify(RequestClass=base_wsgi.Request)
|
|
def __call__(self, req):
|
|
LOG.debug("Request: %s", req)
|
|
try:
|
|
return self._proxy_request(req.remote_addr,
|
|
req.method,
|
|
req.path_info,
|
|
req.query_string,
|
|
req.body)
|
|
except Exception:
|
|
LOG.exception(_LE("Unexpected error."))
|
|
msg = _('An unknown error has occurred. '
|
|
'Please try your request again.')
|
|
explanation = six.text_type(msg)
|
|
return webob.exc.HTTPInternalServerError(explanation=explanation)
|
|
|
|
def _proxy_request(self, remote_address, method, path_info,
|
|
query_string, body):
|
|
headers = {
|
|
'X-Forwarded-For': remote_address,
|
|
}
|
|
|
|
if self.router_id:
|
|
headers['X-Neutron-Router-ID'] = self.router_id
|
|
else:
|
|
headers['X-Neutron-Network-ID'] = self.network_id
|
|
|
|
url = urlparse.urlunsplit((
|
|
'http',
|
|
'169.254.169.254', # a dummy value to make the request proper
|
|
path_info,
|
|
query_string,
|
|
''))
|
|
|
|
h = httplib2.Http()
|
|
resp, content = h.request(
|
|
url,
|
|
method=method,
|
|
headers=headers,
|
|
body=body,
|
|
connection_type=agent_utils.UnixDomainHTTPConnection)
|
|
|
|
if resp.status == 200:
|
|
LOG.debug(resp)
|
|
LOG.debug(encodeutils.safe_decode(content, errors='replace'))
|
|
response = webob.Response()
|
|
response.status = resp.status
|
|
response.headers['Content-Type'] = resp['content-type']
|
|
response.body = wsgi.encode_body(content)
|
|
return response
|
|
elif resp.status == 400:
|
|
return webob.exc.HTTPBadRequest()
|
|
elif resp.status == 404:
|
|
return webob.exc.HTTPNotFound()
|
|
elif resp.status == 409:
|
|
return webob.exc.HTTPConflict()
|
|
elif resp.status == 500:
|
|
msg = _(
|
|
'Remote metadata server experienced an internal server error.'
|
|
)
|
|
LOG.debug(msg)
|
|
explanation = six.text_type(msg)
|
|
return webob.exc.HTTPInternalServerError(explanation=explanation)
|
|
else:
|
|
raise Exception(_('Unexpected response code: %s') % resp.status)
|
|
|
|
|
|
class ProxyDaemon(daemon.Daemon):
|
|
def __init__(self, pidfile, port, network_id=None, router_id=None,
|
|
user=None, group=None, watch_log=True):
|
|
uuid = network_id or router_id
|
|
super(ProxyDaemon, self).__init__(pidfile, uuid=uuid, user=user,
|
|
group=group, watch_log=watch_log)
|
|
self.network_id = network_id
|
|
self.router_id = router_id
|
|
self.port = port
|
|
|
|
def run(self):
|
|
handler = NetworkMetadataProxyHandler(
|
|
self.network_id,
|
|
self.router_id)
|
|
proxy = wsgi.Server('neutron-network-metadata-proxy')
|
|
proxy.start(handler, self.port)
|
|
|
|
# Drop privileges after port bind
|
|
super(ProxyDaemon, self).run()
|
|
|
|
proxy.wait()
|
|
|
|
|
|
def main():
|
|
namespace.register_namespace_proxy_opts(cfg.CONF)
|
|
# Don't read any default configuration file, just handle cmdline opts
|
|
cfg.CONF(project='neutron',
|
|
default_config_files=[], default_config_dirs=[])
|
|
config.setup_logging()
|
|
utils.log_opt_values(LOG)
|
|
|
|
proxy = ProxyDaemon(cfg.CONF.pid_file,
|
|
cfg.CONF.metadata_port,
|
|
network_id=cfg.CONF.network_id,
|
|
router_id=cfg.CONF.router_id,
|
|
user=cfg.CONF.metadata_proxy_user,
|
|
group=cfg.CONF.metadata_proxy_group,
|
|
watch_log=cfg.CONF.metadata_proxy_watch_log)
|
|
|
|
if cfg.CONF.daemonize:
|
|
proxy.start()
|
|
else:
|
|
proxy.run()
|