1 # Copyright 2012 New Dream Network, LLC (DreamHost)
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
16 from oslo_config import cfg
17 from oslo_log import log as logging
18 from oslo_service import wsgi as base_wsgi
19 from oslo_utils import encodeutils
21 import six.moves.urllib.parse as urlparse
24 from neutron._i18n import _, _LE
25 from neutron.agent.linux import daemon
26 from neutron.agent.linux import utils as agent_utils
27 from neutron.common import config
28 from neutron.common import exceptions
29 from neutron.common import utils
30 from neutron import wsgi
32 LOG = logging.getLogger(__name__)
35 class NetworkMetadataProxyHandler(object):
36 """Proxy AF_INET metadata request through Unix Domain socket.
38 The Unix domain socket allows the proxy access resource that are not
39 accessible within the isolated tenant context.
42 def __init__(self, network_id=None, router_id=None):
43 self.network_id = network_id
44 self.router_id = router_id
46 if network_id is None and router_id is None:
47 raise exceptions.NetworkIdOrRouterIdRequiredError()
49 @webob.dec.wsgify(RequestClass=base_wsgi.Request)
50 def __call__(self, req):
51 LOG.debug("Request: %s", req)
53 return self._proxy_request(req.remote_addr,
59 LOG.exception(_LE("Unexpected error."))
60 msg = _('An unknown error has occurred. '
61 'Please try your request again.')
62 explanation = six.text_type(msg)
63 return webob.exc.HTTPInternalServerError(explanation=explanation)
65 def _proxy_request(self, remote_address, method, path_info,
68 'X-Forwarded-For': remote_address,
72 headers['X-Neutron-Router-ID'] = self.router_id
74 headers['X-Neutron-Network-ID'] = self.network_id
76 url = urlparse.urlunsplit((
78 '169.254.169.254', # a dummy value to make the request proper
84 resp, content = h.request(
89 connection_type=agent_utils.UnixDomainHTTPConnection)
91 if resp.status == 200:
93 LOG.debug(encodeutils.safe_decode(content, errors='replace'))
94 response = webob.Response()
95 response.status = resp.status
96 response.headers['Content-Type'] = resp['content-type']
97 response.body = wsgi.encode_body(content)
99 elif resp.status == 400:
100 return webob.exc.HTTPBadRequest()
101 elif resp.status == 404:
102 return webob.exc.HTTPNotFound()
103 elif resp.status == 409:
104 return webob.exc.HTTPConflict()
105 elif resp.status == 500:
107 'Remote metadata server experienced an internal server error.'
110 explanation = six.text_type(msg)
111 return webob.exc.HTTPInternalServerError(explanation=explanation)
113 raise Exception(_('Unexpected response code: %s') % resp.status)
116 class ProxyDaemon(daemon.Daemon):
117 def __init__(self, pidfile, port, network_id=None, router_id=None,
118 user=None, group=None, watch_log=True):
119 uuid = network_id or router_id
120 super(ProxyDaemon, self).__init__(pidfile, uuid=uuid, user=user,
121 group=group, watch_log=watch_log)
122 self.network_id = network_id
123 self.router_id = router_id
127 handler = NetworkMetadataProxyHandler(
130 proxy = wsgi.Server('neutron-network-metadata-proxy')
131 proxy.start(handler, self.port)
133 # Drop privileges after port bind
134 super(ProxyDaemon, self).run()
141 cfg.StrOpt('network_id',
142 help=_('Network that will have instance metadata '
144 cfg.StrOpt('router_id',
145 help=_('Router that will have connected instances\' '
146 'metadata proxied.')),
147 cfg.StrOpt('pid_file',
148 help=_('Location of pid file of this process.')),
149 cfg.BoolOpt('daemonize',
151 help=_('Run as daemon.')),
152 cfg.PortOpt('metadata_port',
154 help=_("TCP Port to listen for metadata server "
156 cfg.StrOpt('metadata_proxy_socket',
157 default='$state_path/metadata_proxy',
158 help=_('Location of Metadata Proxy UNIX domain '
160 cfg.StrOpt('metadata_proxy_user',
161 help=_("User (uid or name) running metadata proxy after "
162 "its initialization")),
163 cfg.StrOpt('metadata_proxy_group',
164 help=_("Group (gid or name) running metadata proxy after "
165 "its initialization")),
166 cfg.BoolOpt('metadata_proxy_watch_log',
168 help=_("Watch file log. Log watch should be disabled when "
169 "metadata_proxy_user/group has no read/write "
170 "permissions on metadata proxy log file.")),
173 cfg.CONF.register_cli_opts(opts)
174 # Don't get the default configuration file
175 cfg.CONF(project='neutron', default_config_files=[])
176 config.setup_logging()
177 utils.log_opt_values(LOG)
179 proxy = ProxyDaemon(cfg.CONF.pid_file,
180 cfg.CONF.metadata_port,
181 network_id=cfg.CONF.network_id,
182 router_id=cfg.CONF.router_id,
183 user=cfg.CONF.metadata_proxy_user,
184 group=cfg.CONF.metadata_proxy_group,
185 watch_log=cfg.CONF.metadata_proxy_watch_log)
187 if cfg.CONF.daemonize: