From 7407a1a86c4039bdc541e9a26cc68c9c93f49bc3 Mon Sep 17 00:00:00 2001 From: Ryu Ishimoto Date: Fri, 5 Aug 2011 18:29:32 +0900 Subject: Added virtual interfaces REST API extension controller --- nova/api/openstack/contrib/virtual_interfaces.py | 102 +++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 nova/api/openstack/contrib/virtual_interfaces.py (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/virtual_interfaces.py b/nova/api/openstack/contrib/virtual_interfaces.py new file mode 100644 index 000000000..3466d31c7 --- /dev/null +++ b/nova/api/openstack/contrib/virtual_interfaces.py @@ -0,0 +1,102 @@ +# Copyright (C) 2011 Midokura KK +# 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. + +"""The virtual interfaces extension.""" + +from webob import exc +import webob + +from nova import compute +from nova import exception +from nova import log as logging +from nova.api.openstack import common +from nova.api.openstack import extensions +from nova.api.openstack import faults + + +LOG = logging.getLogger("nova.api.virtual_interfaces") + + +def _translate_vif_summary_view(_context, vif): + """Maps keys for attachment summary view.""" + d = {} + d['id'] = vif['uuid'] + d['macAddress'] = vif['address'] + d['serverId'] = vif['instance_id'] + return d + + +class ServerVirtualInterfaceController(object): + """The instance VIF API controller for the Openstack API. + """ + + _serialization_metadata = { + 'application/xml': { + 'attributes': { + 'serverVirtualInterface': ['id', + 'macAddress']}}} + + def __init__(self): + self.compute_api = compute.API() + super(ServerVirtualInterfaceController, self).__init__() + + def _items(self, req, server_id, entity_maker): + """Returns a list of VIFs, transformed through entity_maker.""" + context = req.environ['nova.context'] + + try: + instance = self.compute_api.get(context, server_id) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + + vifs = instance['virtual_interfaces'] + limited_list = common.limited(vifs, req) + res = [entity_maker(context, vif) for vif in limited_list] + return {'serverVirtualInterfaces': res} + + def index(self, req, server_id): + """Returns the list of VIFs for a given instance.""" + return self._items(req, server_id, + entity_maker=_translate_vif_summary_view) + + +class VirtualInterfaces(extensions.ExtensionDescriptor): + + def get_name(self): + return "VirtualInterfaces" + + def get_alias(self): + return "os-virtual_interfaces" + + def get_description(self): + return "Virtual interface support" + + def get_namespace(self): + return "http://docs.openstack.org/ext/virtual_interfaces/api/v1.1" + + def get_updated(self): + return "2011-08-05T00:00:00+00:00" + + def get_resources(self): + resources = [] + + res = extensions.ResourceExtension('os-virtual_interfaces', + ServerVirtualInterfaceController(), + parent=dict( + member_name='server', + collection_name='servers')) + resources.append(res) + + return resources -- cgit From ca7bf95e610bdc47f01b8fb7b459269bb8e5df66 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Thu, 11 Aug 2011 18:11:59 -0700 Subject: Initial version --- nova/api/__init__.py | 6 ++ nova/api/ec2/__init__.py | 3 - nova/api/openstack/create_instance_helper.py | 4 +- nova/api/openstack/userdatarequesthandler.py | 110 +++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 nova/api/openstack/userdatarequesthandler.py (limited to 'nova/api') diff --git a/nova/api/__init__.py b/nova/api/__init__.py index 747015af5..6e6b092b3 100644 --- a/nova/api/__init__.py +++ b/nova/api/__init__.py @@ -15,3 +15,9 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from nova import flags + + +flags.DEFINE_boolean('use_forwarded_for', False, + 'Treat X-Forwarded-For as the canonical remote address. ' + 'Only enable this if you have a sanitizing proxy.') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 8b6e47cfb..e497b499a 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -37,9 +37,6 @@ from nova.auth import manager FLAGS = flags.FLAGS LOG = logging.getLogger("nova.api") -flags.DEFINE_boolean('use_forwarded_for', False, - 'Treat X-Forwarded-For as the canonical remote address. ' - 'Only enable this if you have a sanitizing proxy.') flags.DEFINE_integer('lockout_attempts', 5, 'Number of failed auths before lockout.') flags.DEFINE_integer('lockout_minutes', 15, diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 1425521a9..144697790 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -122,6 +122,7 @@ class CreateInstanceHelper(object): raise exc.HTTPBadRequest(explanation=msg) zone_blob = server_dict.get('blob') + user_data = server_dict.get('user_data') name = server_dict['name'] self._validate_server_name(name) name = name.strip() @@ -161,7 +162,8 @@ class CreateInstanceHelper(object): zone_blob=zone_blob, reservation_id=reservation_id, min_count=min_count, - max_count=max_count)) + max_count=max_count, + user_data=user_data)) except quota.QuotaError as error: self._handle_quota_error(error) except exception.ImageNotFound as error: diff --git a/nova/api/openstack/userdatarequesthandler.py b/nova/api/openstack/userdatarequesthandler.py new file mode 100644 index 000000000..5daa37e95 --- /dev/null +++ b/nova/api/openstack/userdatarequesthandler.py @@ -0,0 +1,110 @@ +# 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. + +"""User data request handler.""" + +import base64 +import webob.dec +import webob.exc + +from nova import log as logging +from nova import context +from nova import exception +from nova import db +from nova import flags +from nova import wsgi + + +LOG = logging.getLogger('nova.api.openstack.userdata') +FLAGS = flags.FLAGS + + +class Controller(object): + """ The server user-data API controller for the Openstack API """ + + def __init__(self): + super(Controller, self).__init__() + + @staticmethod + def _format_user_data(instance_ref): + return base64.b64decode(instance_ref['user_data']) + + def get_user_data(self, address): + ctxt = context.get_admin_context() + try: + instance_ref = db.instance_get_by_fixed_ip(ctxt, address) + except exception.NotFound: + instance_ref = None + if not instance_ref: + return None + + data = {'user-data': self._format_user_data(instance_ref)} + return data + + +class UserdataRequestHandler(wsgi.Application): + """Serve user-data from the OS API.""" + + def __init__(self): + self.cc = Controller() + + 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 = "10.0.1.6"#req.remote_addr + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + + data = self.cc.get_user_data(remote_address) + if data is None: + LOG.error(_('Failed to get user data for ip: %s'), remote_address) + raise webob.exc.HTTPNotFound() + data = self.lookup(req.path_info, data) + if data is None: + raise webob.exc.HTTPNotFound() + return self.print_data(data) -- cgit From 7507ba23004c989c75962c47efbd2ce5e5178a90 Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Thu, 11 Aug 2011 18:22:35 -0700 Subject: added userdata entry in the api paste ini --- nova/api/openstack/userdatarequesthandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/userdatarequesthandler.py b/nova/api/openstack/userdatarequesthandler.py index 5daa37e95..f0205419b 100644 --- a/nova/api/openstack/userdatarequesthandler.py +++ b/nova/api/openstack/userdatarequesthandler.py @@ -96,7 +96,7 @@ class UserdataRequestHandler(wsgi.Application): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): - remote_address = "10.0.1.6"#req.remote_addr + remote_address = req.remote_addr if FLAGS.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) -- cgit From 9ce9ef1166075e539442c61c65cf21b8d6e90cdd Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 11 Aug 2011 21:03:37 -0700 Subject: add keystone middlewares for ec2 api --- nova/api/auth.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ nova/api/ec2/__init__.py | 55 +++++++++++++++++++++++++++-- 2 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 nova/api/auth.py (limited to 'nova/api') diff --git a/nova/api/auth.py b/nova/api/auth.py new file mode 100644 index 000000000..034057d77 --- /dev/null +++ b/nova/api/auth.py @@ -0,0 +1,91 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 OpenStack, LLC +# +# 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. +""" +Common Auth Middleware. + +""" + +from nova import context +from nova import flags +from nova import wsgi +import webob.dec +import webob.exc + + +FLAGS = flags.FLAGS +flags.DEFINE_boolean('use_forwarded_for', False, + 'Treat X-Forwarded-For as the canonical remote address. ' + 'Only enable this if you have a sanitizing proxy.') + + +class InjectContext(wsgi.Middleware): + """Add a 'nova.context' to WSGI environ.""" + def __init__(self, context, *args, **kwargs): + self.context = context + super(InjectContext, self).__init__(*args, **kwargs) + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + req.environ['nova.context'] = self.context + return self.application + + +class AdminContext(wsgi.Middleware): + """Return an admin context no matter what""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + # Build a context, including the auth_token... + remote_address = req.remote_addr + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext('admin', + 'admin', + is_admin=True, + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application + + +class KeystoneContext(wsgi.Middleware): + """Make a request context from keystone headers""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + try: + user_id = req.headers['X_USER'] + except: + return webob.exc.HTTPUnauthorized() + # get the roles + roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] + project_id = req.headers['X_TENANT'] + # Get the auth token + auth_token = req.headers.get('X_AUTH_TOKEN', + req.headers.get('X_STORAGE_TOKEN')) + + # Build a context, including the auth_token... + remote_address = req.remote_addr + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + roles=roles, + auth_token=auth_token, + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 8b6e47cfb..f3e6fa124 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -20,6 +20,7 @@ Starting point for routing EC2 requests. """ +import httplib2 import webob import webob.dec import webob.exc @@ -37,15 +38,17 @@ from nova.auth import manager FLAGS = flags.FLAGS LOG = logging.getLogger("nova.api") -flags.DEFINE_boolean('use_forwarded_for', False, - 'Treat X-Forwarded-For as the canonical remote address. ' - 'Only enable this if you have a sanitizing proxy.') flags.DEFINE_integer('lockout_attempts', 5, 'Number of failed auths before lockout.') flags.DEFINE_integer('lockout_minutes', 15, 'Number of minutes to lockout if triggered.') flags.DEFINE_integer('lockout_window', 15, 'Number of minutes for lockout window.') +flags.DEFINE_integer('lockout_window', 15, + 'Number of minutes for lockout window.') +flags.DEFINE_string('keystone_ec2_url', + 'http://localhost:5000/v2.0/ec2tokens', + 'URL to get token from ec2 request.') class RequestLogging(wsgi.Middleware): @@ -138,6 +141,49 @@ class Lockout(wsgi.Middleware): return res +class ToToken(wsgi.Middleware): + """Authenticate an EC2 request with keystone and convert to token.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + # Read request signature and access id. + try: + signature = req.params['Signature'] + access = req.params['AWSAccessKeyId'] + except KeyError, e: + raise webob.exc.HTTPBadRequest() + + # Make a copy of args for authentication and signature verification. + auth_params = dict(req.params) + # Not part of authentication args + auth_params.pop('Signature') + + # Authenticate the request. + client = httplib2.Http() + creds = {'ec2Credentials': {'access': access, + 'signature': signature, + 'host': req.host, + 'verb': req.method, + 'path': req.path, + 'params': auth_params, + }} + headers = {'Content-Type': 'application/json'}, + resp, content = client.request(FLAGS.keystone_ec2_url, + 'POST', + headers=headers, + body=utils.dumps(creds)) + # NOTE(vish): We could save a call to keystone by + # having keystone return token, tenant, + # user, and roles from this call. + result = utils.loads(content) + # TODO(vish): check for errors + token_id = result['auth']['token']['id'] + + # Authenticated! + req.headers['X-Auth-Token'] = token_id + return self.application + + class Authenticate(wsgi.Middleware): """Authenticate an EC2 request and add 'nova.context' to WSGI environ.""" @@ -196,6 +242,7 @@ class Requestify(wsgi.Middleware): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): + LOG.audit("in request", context=req.environ['nova.context']) non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod', 'SignatureVersion', 'Version', 'Timestamp'] args = dict(req.params) @@ -286,6 +333,8 @@ class Authorizer(wsgi.Middleware): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): context = req.environ['nova.context'] + LOG.warn(req.environ['nova.context'].__dict__) + LOG.warn(req.environ['ec2.request'].__dict__) controller = req.environ['ec2.request'].controller.__class__.__name__ action = req.environ['ec2.request'].action allowed_roles = self.action_roles[controller].get(action, ['none']) -- cgit From e294303750f032f22dadaba7eb0c743effa8c3f5 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 11 Aug 2011 21:30:07 -0700 Subject: remove accidentally duplicated flag --- nova/api/ec2/__init__.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index f3e6fa124..a93285dba 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -44,8 +44,6 @@ flags.DEFINE_integer('lockout_minutes', 15, 'Number of minutes to lockout if triggered.') flags.DEFINE_integer('lockout_window', 15, 'Number of minutes for lockout window.') -flags.DEFINE_integer('lockout_window', 15, - 'Number of minutes for lockout window.') flags.DEFINE_string('keystone_ec2_url', 'http://localhost:5000/v2.0/ec2tokens', 'URL to get token from ec2 request.') -- cgit From 7295b93192d2b151c108d7631c3b404ef65fdedf Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 12 Aug 2011 01:21:47 -0700 Subject: remove extra log statements --- nova/api/ec2/__init__.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index a93285dba..1ae9a126a 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -240,7 +240,6 @@ class Requestify(wsgi.Middleware): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): - LOG.audit("in request", context=req.environ['nova.context']) non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod', 'SignatureVersion', 'Version', 'Timestamp'] args = dict(req.params) @@ -331,8 +330,6 @@ class Authorizer(wsgi.Middleware): @webob.dec.wsgify(RequestClass=wsgi.Request) def __call__(self, req): context = req.environ['nova.context'] - LOG.warn(req.environ['nova.context'].__dict__) - LOG.warn(req.environ['ec2.request'].__dict__) controller = req.environ['ec2.request'].controller.__class__.__name__ action = req.environ['ec2.request'].action allowed_roles = self.action_roles[controller].get(action, ['none']) -- cgit From 9ab61aaa194a787b41b1d634c1b56c98574dcbc9 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 12 Aug 2011 11:28:47 -0700 Subject: updates from review --- nova/api/auth.py | 26 +++++--------------------- nova/api/ec2/__init__.py | 4 ++-- 2 files changed, 7 insertions(+), 23 deletions(-) (limited to 'nova/api') diff --git a/nova/api/auth.py b/nova/api/auth.py index 034057d77..cd3e3e8a0 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -18,11 +18,12 @@ Common Auth Middleware. """ +import webob.dec +import webob.exc + from nova import context from nova import flags from nova import wsgi -import webob.dec -import webob.exc FLAGS = flags.FLAGS @@ -33,6 +34,7 @@ flags.DEFINE_boolean('use_forwarded_for', False, class InjectContext(wsgi.Middleware): """Add a 'nova.context' to WSGI environ.""" + def __init__(self, context, *args, **kwargs): self.context = context super(InjectContext, self).__init__(*args, **kwargs) @@ -43,24 +45,6 @@ class InjectContext(wsgi.Middleware): return self.application -class AdminContext(wsgi.Middleware): - """Return an admin context no matter what""" - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - # Build a context, including the auth_token... - remote_address = req.remote_addr - if FLAGS.use_forwarded_for: - remote_address = req.headers.get('X-Forwarded-For', remote_address) - ctx = context.RequestContext('admin', - 'admin', - is_admin=True, - remote_address=remote_address) - - req.environ['nova.context'] = ctx - return self.application - - class KeystoneContext(wsgi.Middleware): """Make a request context from keystone headers""" @@ -68,7 +52,7 @@ class KeystoneContext(wsgi.Middleware): def __call__(self, req): try: user_id = req.headers['X_USER'] - except: + except KeyError: return webob.exc.HTTPUnauthorized() # get the roles roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 1ae9a126a..2ae370f88 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -148,7 +148,7 @@ class ToToken(wsgi.Middleware): try: signature = req.params['Signature'] access = req.params['AWSAccessKeyId'] - except KeyError, e: + except KeyError: raise webob.exc.HTTPBadRequest() # Make a copy of args for authentication and signature verification. @@ -191,7 +191,7 @@ class Authenticate(wsgi.Middleware): try: signature = req.params['Signature'] access = req.params['AWSAccessKeyId'] - except KeyError, e: + except KeyError: raise webob.exc.HTTPBadRequest() # Make a copy of args for authentication and signature verification. -- cgit From 8666aca320ce95840a378231bfe81bc4e759df6e Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Mon, 15 Aug 2011 11:50:54 -0700 Subject: Fixed merging issue --- nova/api/openstack/create_instance_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 7e9d48c02..c8798536e 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -164,7 +164,7 @@ class CreateInstanceHelper(object): reservation_id=reservation_id, min_count=min_count, max_count=max_count, - user_data=user_data)) + user_data=user_data, availability_zone=availability_zone)) except quota.QuotaError as error: self._handle_quota_error(error) -- cgit From 9a4b1deb5f9abdc88809ff80bccdfb503e66dccd Mon Sep 17 00:00:00 2001 From: Tushar Patil Date: Mon, 15 Aug 2011 15:09:42 -0700 Subject: Removed newly added userdatarequesthandler for OS API, there is no need to add this handler since the existing Ec2 API metadatarequesthandler does the same job --- nova/api/__init__.py | 6 -- nova/api/ec2/__init__.py | 3 + nova/api/openstack/userdatarequesthandler.py | 110 --------------------------- 3 files changed, 3 insertions(+), 116 deletions(-) delete mode 100644 nova/api/openstack/userdatarequesthandler.py (limited to 'nova/api') diff --git a/nova/api/__init__.py b/nova/api/__init__.py index 6e6b092b3..747015af5 100644 --- a/nova/api/__init__.py +++ b/nova/api/__init__.py @@ -15,9 +15,3 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from nova import flags - - -flags.DEFINE_boolean('use_forwarded_for', False, - 'Treat X-Forwarded-For as the canonical remote address. ' - 'Only enable this if you have a sanitizing proxy.') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 2e9278b52..96df97393 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -37,6 +37,9 @@ from nova.auth import manager FLAGS = flags.FLAGS LOG = logging.getLogger("nova.api") +flags.DEFINE_boolean('use_forwarded_for', False, + 'Treat X-Forwarded-For as the canonical remote address. ' + 'Only enable this if you have a sanitizing proxy.') flags.DEFINE_integer('lockout_attempts', 5, 'Number of failed auths before lockout.') flags.DEFINE_integer('lockout_minutes', 15, diff --git a/nova/api/openstack/userdatarequesthandler.py b/nova/api/openstack/userdatarequesthandler.py deleted file mode 100644 index f0205419b..000000000 --- a/nova/api/openstack/userdatarequesthandler.py +++ /dev/null @@ -1,110 +0,0 @@ -# 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. - -"""User data request handler.""" - -import base64 -import webob.dec -import webob.exc - -from nova import log as logging -from nova import context -from nova import exception -from nova import db -from nova import flags -from nova import wsgi - - -LOG = logging.getLogger('nova.api.openstack.userdata') -FLAGS = flags.FLAGS - - -class Controller(object): - """ The server user-data API controller for the Openstack API """ - - def __init__(self): - super(Controller, self).__init__() - - @staticmethod - def _format_user_data(instance_ref): - return base64.b64decode(instance_ref['user_data']) - - def get_user_data(self, address): - ctxt = context.get_admin_context() - try: - instance_ref = db.instance_get_by_fixed_ip(ctxt, address) - except exception.NotFound: - instance_ref = None - if not instance_ref: - return None - - data = {'user-data': self._format_user_data(instance_ref)} - return data - - -class UserdataRequestHandler(wsgi.Application): - """Serve user-data from the OS API.""" - - def __init__(self): - self.cc = Controller() - - 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) - - data = self.cc.get_user_data(remote_address) - if data is None: - LOG.error(_('Failed to get user data for ip: %s'), remote_address) - raise webob.exc.HTTPNotFound() - data = self.lookup(req.path_info, data) - if data is None: - raise webob.exc.HTTPNotFound() - return self.print_data(data) -- cgit From 83b45a371665fd069fc7e372628f82874258fd08 Mon Sep 17 00:00:00 2001 From: Jesse Andrews Date: Tue, 16 Aug 2011 00:31:54 -0700 Subject: redux of floating ip api --- nova/api/openstack/contrib/floating_ips.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 44b35c385..722320534 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -78,11 +78,14 @@ class FloatingIPController(object): def index(self, req): context = req.environ['nova.context'] - floating_ips = self.network_api.list_floating_ips(context) + try: + floating_ips = self.network_api.list_floating_ips(context) + except exception.FloatingIpNotFoundForProject: + floating_ips = [] return _translate_floating_ips_view(floating_ips) - def create(self, req): + def create(self, req, body): context = req.environ['nova.context'] try: @@ -95,9 +98,7 @@ class FloatingIPController(object): else: raise - return {'allocated': { - "id": ip['id'], - "floating_ip": ip['address']}} + return _translate_floating_ip_view(ip) def delete(self, req, id): context = req.environ['nova.context'] @@ -125,26 +126,22 @@ class FloatingIPController(object): except rpc.RemoteError: raise - return {'associated': - { - "floating_ip_id": id, - "floating_ip": floating_ip, - "fixed_ip": fixed_ip}} + floating_ip = self.network_api.get_floating_ip(context, id) + return _translate_floating_ip_view(floating_ip) def disassociate(self, req, id, body=None): """ POST /floating_ips/{id}/disassociate """ context = req.environ['nova.context'] floating_ip = self.network_api.get_floating_ip(context, id) address = floating_ip['address'] - fixed_ip = floating_ip['fixed_ip']['address'] try: self.network_api.disassociate_floating_ip(context, address) except rpc.RemoteError: raise - return {'disassociated': {'floating_ip': address, - 'fixed_ip': fixed_ip}} + floating_ip = self.network_api.get_floating_ip(context, id) + return _translate_floating_ip_view(floating_ip) def _get_ip_by_id(self, context, value): """Checks that value is id and then returns its address.""" -- cgit From 92c6ee9dc7eeaa44bf6162387b5815fc0cdb1c71 Mon Sep 17 00:00:00 2001 From: Ryu Ishimoto Date: Tue, 16 Aug 2011 17:51:45 +0900 Subject: Fixed the naming of the extension --- nova/api/openstack/contrib/virtual_interfaces.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/virtual_interfaces.py b/nova/api/openstack/contrib/virtual_interfaces.py index 3466d31c7..38246aeb5 100644 --- a/nova/api/openstack/contrib/virtual_interfaces.py +++ b/nova/api/openstack/contrib/virtual_interfaces.py @@ -72,13 +72,13 @@ class ServerVirtualInterfaceController(object): entity_maker=_translate_vif_summary_view) -class VirtualInterfaces(extensions.ExtensionDescriptor): +class Virtual_interfaces(extensions.ExtensionDescriptor): def get_name(self): - return "VirtualInterfaces" + return "Virtual_interfaces" def get_alias(self): - return "os-virtual_interfaces" + return "os-virtual-interfaces" def get_description(self): return "Virtual interface support" @@ -92,7 +92,7 @@ class VirtualInterfaces(extensions.ExtensionDescriptor): def get_resources(self): resources = [] - res = extensions.ResourceExtension('os-virtual_interfaces', + res = extensions.ResourceExtension('os-virtual-interfaces', ServerVirtualInterfaceController(), parent=dict( member_name='server', -- cgit From fb43ea94e81e5eec51b73c2aab4a8a38cdf71361 Mon Sep 17 00:00:00 2001 From: Jesse Andrews Date: Tue, 16 Aug 2011 11:46:22 -0700 Subject: make delete more consistant --- nova/api/openstack/contrib/floating_ips.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 722320534..1276c0118 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -105,13 +105,13 @@ class FloatingIPController(object): ip = self.network_api.get_floating_ip(context, id) if 'fixed_ip' in ip: - self.disassociate(req, id) + try: + self.disassociate(req, id) + except exception.ApiError: + LOG.warn("disassociate failure %s", id) self.network_api.release_floating_ip(context, address=ip['address']) - - return {'released': { - "id": ip['id'], - "floating_ip": ip['address']}} + return exc.HTTPAccepted() def associate(self, req, id, body): """ /floating_ips/{id}/associate fixed ip in body """ -- cgit From 83177757632b381d42cc5107fe7d1cba8830a10a Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Tue, 16 Aug 2011 16:59:36 -0700 Subject: all tests passing --- nova/api/openstack/contrib/floating_ips.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 1276c0118..751b27c9f 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -85,7 +85,7 @@ class FloatingIPController(object): return _translate_floating_ips_view(floating_ips) - def create(self, req, body): + def create(self, req, body=None): context = req.environ['nova.context'] try: -- cgit From 79f3b1512166a37790c5cb2863140d696c717455 Mon Sep 17 00:00:00 2001 From: Troy Toman Date: Wed, 17 Aug 2011 02:41:17 -0500 Subject: Changed return code to 413 for metadata, personality and instance quota issues --- nova/api/openstack/common.py | 3 ++- nova/api/openstack/create_instance_helper.py | 13 ++++++++++--- nova/api/openstack/faults.py | 2 +- nova/api/openstack/server_metadata.py | 3 ++- 4 files changed, 15 insertions(+), 6 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index b2a675653..d9eb832f2 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -241,7 +241,8 @@ def check_img_metadata_quota_limit(context, metadata): quota_metadata = quota.allowed_metadata_items(context, num_metadata) if quota_metadata < num_metadata: expl = _("Image metadata limit exceeded") - raise webob.exc.HTTPBadRequest(explanation=expl) + raise webob.exc.HTTPRequestEntityTooLarge(explanation=expl, + headers={'Retry-After': 0}) class MetadataXMLDeserializer(wsgi.XMLDeserializer): diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py index 4e1da549e..b4a08dac0 100644 --- a/nova/api/openstack/create_instance_helper.py +++ b/nova/api/openstack/create_instance_helper.py @@ -180,13 +180,20 @@ class CreateInstanceHelper(object): """ if error.code == "OnsetFileLimitExceeded": expl = _("Personality file limit exceeded") - raise exc.HTTPBadRequest(explanation=expl) + raise exc.HTTPRequestEntityTooLarge(explanation=error.message, + headers={'Retry-After': 0}) if error.code == "OnsetFilePathLimitExceeded": expl = _("Personality file path too long") - raise exc.HTTPBadRequest(explanation=expl) + raise exc.HTTPRequestEntityTooLarge(explanation=error.message, + headers={'Retry-After': 0}) if error.code == "OnsetFileContentLimitExceeded": expl = _("Personality file content too long") - raise exc.HTTPBadRequest(explanation=expl) + raise exc.HTTPRequestEntityTooLarge(explanation=error.message, + headers={'Retry-After': 0}) + if error.code == "InstanceLimitExceeded": + expl = _("Instance quotas have been exceeded") + raise exc.HTTPRequestEntityTooLarge(explanation=error.message, + headers={'Retry-After': 0}) # if the original error is okay, just reraise it raise error diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py index 1ab45d4f1..0ed6f1ff0 100644 --- a/nova/api/openstack/faults.py +++ b/nova/api/openstack/faults.py @@ -53,7 +53,7 @@ class Fault(webob.exc.HTTPException): fault_name: { 'code': code, 'message': self.wrapped_exc.explanation}} - if code == 413: + if code == 413 and self.wrapped_exc.headers['Retry-After']: retry = self.wrapped_exc.headers['Retry-After'] fault_data[fault_name]['retryAfter'] = retry diff --git a/nova/api/openstack/server_metadata.py b/nova/api/openstack/server_metadata.py index 2b235f79a..8ac3319c9 100644 --- a/nova/api/openstack/server_metadata.py +++ b/nova/api/openstack/server_metadata.py @@ -151,7 +151,8 @@ class Controller(object): def _handle_quota_error(self, error): """Reraise quota errors as api-specific http exceptions.""" if error.code == "MetadataLimitExceeded": - raise exc.HTTPBadRequest(explanation=error.message) + raise exc.HTTPRequestEntityTooLarge(explanation=error.message, + headers={'Retry-After': 0}) raise error -- cgit From 228b185f1366df62da42b646ce98711de4195a5d Mon Sep 17 00:00:00 2001 From: Troy Toman Date: Wed, 17 Aug 2011 03:03:25 -0500 Subject: Removed a change from faults.py that was not required." --- nova/api/openstack/faults.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py index 0ed6f1ff0..1ab45d4f1 100644 --- a/nova/api/openstack/faults.py +++ b/nova/api/openstack/faults.py @@ -53,7 +53,7 @@ class Fault(webob.exc.HTTPException): fault_name: { 'code': code, 'message': self.wrapped_exc.explanation}} - if code == 413 and self.wrapped_exc.headers['Retry-After']: + if code == 413: retry = self.wrapped_exc.headers['Retry-After'] fault_data[fault_name]['retryAfter'] = retry -- cgit From 77e1e0d3359bce9e5e30134f141151fc271a2e4b Mon Sep 17 00:00:00 2001 From: Ryu Ishimoto Date: Wed, 17 Aug 2011 19:05:29 +0900 Subject: Removed serverId from the response --- nova/api/openstack/contrib/virtual_interfaces.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/virtual_interfaces.py b/nova/api/openstack/contrib/virtual_interfaces.py index 38246aeb5..86d1128fd 100644 --- a/nova/api/openstack/contrib/virtual_interfaces.py +++ b/nova/api/openstack/contrib/virtual_interfaces.py @@ -34,7 +34,6 @@ def _translate_vif_summary_view(_context, vif): d = {} d['id'] = vif['uuid'] d['macAddress'] = vif['address'] - d['serverId'] = vif['instance_id'] return d -- cgit From 751c8b4ff0e94b4f665af5541b9249637623d193 Mon Sep 17 00:00:00 2001 From: Ryu Ishimoto Date: Wed, 17 Aug 2011 19:58:26 +0900 Subject: Added XML support and changed JSON output keys --- nova/api/openstack/contrib/virtual_interfaces.py | 28 +++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/virtual_interfaces.py b/nova/api/openstack/contrib/virtual_interfaces.py index 86d1128fd..715a54d52 100644 --- a/nova/api/openstack/contrib/virtual_interfaces.py +++ b/nova/api/openstack/contrib/virtual_interfaces.py @@ -24,16 +24,17 @@ from nova import log as logging from nova.api.openstack import common from nova.api.openstack import extensions from nova.api.openstack import faults +from nova.api.openstack import wsgi LOG = logging.getLogger("nova.api.virtual_interfaces") def _translate_vif_summary_view(_context, vif): - """Maps keys for attachment summary view.""" + """Maps keys for VIF summary view.""" d = {} d['id'] = vif['uuid'] - d['macAddress'] = vif['address'] + d['mac_address'] = vif['address'] return d @@ -41,12 +42,6 @@ class ServerVirtualInterfaceController(object): """The instance VIF API controller for the Openstack API. """ - _serialization_metadata = { - 'application/xml': { - 'attributes': { - 'serverVirtualInterface': ['id', - 'macAddress']}}} - def __init__(self): self.compute_api = compute.API() super(ServerVirtualInterfaceController, self).__init__() @@ -63,7 +58,7 @@ class ServerVirtualInterfaceController(object): vifs = instance['virtual_interfaces'] limited_list = common.limited(vifs, req) res = [entity_maker(context, vif) for vif in limited_list] - return {'serverVirtualInterfaces': res} + return {'virtual_interfaces': res} def index(self, req, server_id): """Returns the list of VIFs for a given instance.""" @@ -91,11 +86,24 @@ class Virtual_interfaces(extensions.ExtensionDescriptor): def get_resources(self): resources = [] + metadata = _get_metadata() + body_serializers = { + 'application/xml': wsgi.XMLDictSerializer(metadata=metadata, + xmlns=wsgi.XMLNS_V11)} + serializer = wsgi.ResponseSerializer(body_serializers, None) res = extensions.ResourceExtension('os-virtual-interfaces', ServerVirtualInterfaceController(), parent=dict( member_name='server', - collection_name='servers')) + collection_name='servers'), + serializer=serializer) resources.append(res) return resources + + +def _get_metadata(): + metadata = { + "attributes": { + 'virtual_interface': ["id", "mac_address"]}} + return metadata -- cgit From 4407405244c3797ed1c0433eec7686e15340dca7 Mon Sep 17 00:00:00 2001 From: Ryu Ishimoto Date: Wed, 17 Aug 2011 20:12:24 +0900 Subject: Cleaned up the file --- nova/api/openstack/contrib/virtual_interfaces.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/virtual_interfaces.py b/nova/api/openstack/contrib/virtual_interfaces.py index 715a54d52..2d3850e12 100644 --- a/nova/api/openstack/contrib/virtual_interfaces.py +++ b/nova/api/openstack/contrib/virtual_interfaces.py @@ -38,6 +38,13 @@ def _translate_vif_summary_view(_context, vif): return d +def _get_metadata(): + metadata = { + "attributes": { + 'virtual_interface': ["id", "mac_address"]}} + return metadata + + class ServerVirtualInterfaceController(object): """The instance VIF API controller for the Openstack API. """ @@ -100,10 +107,3 @@ class Virtual_interfaces(extensions.ExtensionDescriptor): resources.append(res) return resources - - -def _get_metadata(): - metadata = { - "attributes": { - 'virtual_interface': ["id", "mac_address"]}} - return metadata -- cgit From 5415a59d473fb9ed374e746fb36f30fc664c4dec Mon Sep 17 00:00:00 2001 From: Ryu Ishimoto Date: Wed, 17 Aug 2011 20:17:09 +0900 Subject: Updated get_updated time --- nova/api/openstack/contrib/virtual_interfaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/virtual_interfaces.py b/nova/api/openstack/contrib/virtual_interfaces.py index 2d3850e12..b3bb00a8f 100644 --- a/nova/api/openstack/contrib/virtual_interfaces.py +++ b/nova/api/openstack/contrib/virtual_interfaces.py @@ -88,7 +88,7 @@ class Virtual_interfaces(extensions.ExtensionDescriptor): return "http://docs.openstack.org/ext/virtual_interfaces/api/v1.1" def get_updated(self): - return "2011-08-05T00:00:00+00:00" + return "2011-08-17T00:00:00+00:00" def get_resources(self): resources = [] -- cgit From 2e44657a20cdd620d982b252ca35413c07fd3c2b Mon Sep 17 00:00:00 2001 From: Ryu Ishimoto Date: Wed, 17 Aug 2011 20:23:21 +0900 Subject: Cleaned up the extension metadata API data --- nova/api/openstack/contrib/virtual_interfaces.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/virtual_interfaces.py b/nova/api/openstack/contrib/virtual_interfaces.py index b3bb00a8f..dab61efc8 100644 --- a/nova/api/openstack/contrib/virtual_interfaces.py +++ b/nova/api/openstack/contrib/virtual_interfaces.py @@ -76,10 +76,10 @@ class ServerVirtualInterfaceController(object): class Virtual_interfaces(extensions.ExtensionDescriptor): def get_name(self): - return "Virtual_interfaces" + return "VirtualInterfaces" def get_alias(self): - return "os-virtual-interfaces" + return "virtual_interfaces" def get_description(self): return "Virtual interface support" @@ -98,12 +98,11 @@ class Virtual_interfaces(extensions.ExtensionDescriptor): 'application/xml': wsgi.XMLDictSerializer(metadata=metadata, xmlns=wsgi.XMLNS_V11)} serializer = wsgi.ResponseSerializer(body_serializers, None) - res = extensions.ResourceExtension('os-virtual-interfaces', - ServerVirtualInterfaceController(), - parent=dict( - member_name='server', - collection_name='servers'), - serializer=serializer) + res = extensions.ResourceExtension( + 'os-virtual-interfaces', + controller=ServerVirtualInterfaceController(), + parent=dict(member_name='server', collection_name='servers'), + serializer=serializer) resources.append(res) return resources -- cgit From 4f3a33859c350ff13b2fd94e33de4f10a7f93bc1 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Wed, 17 Aug 2011 10:05:01 -0700 Subject: fix some naming inconsistencies, make associate/disassociate PUTs --- nova/api/openstack/contrib/floating_ips.py | 35 ++++++++++++------------------ 1 file changed, 14 insertions(+), 21 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 751b27c9f..af3eee16a 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -102,45 +102,38 @@ class FloatingIPController(object): def delete(self, req, id): context = req.environ['nova.context'] - ip = self.network_api.get_floating_ip(context, id) + floating_ip = self.network_api.get_floating_ip(context, id) - if 'fixed_ip' in ip: - try: - self.disassociate(req, id) - except exception.ApiError: - LOG.warn("disassociate failure %s", id) + if 'fixed_ip' in floating_ip: + self.network_api.disassociate_floating_ip(context, floating_ip['address']) - self.network_api.release_floating_ip(context, address=ip['address']) + self.network_api.release_floating_ip(context, address=floating_ip['address']) return exc.HTTPAccepted() def associate(self, req, id, body): - """ /floating_ips/{id}/associate fixed ip in body """ + """PUT /floating_ips/{id}/associate fixed ip in body """ context = req.environ['nova.context'] floating_ip = self._get_ip_by_id(context, id) - fixed_ip = body['associate_address']['fixed_ip'] + fixed_ip = body['floating_ip']['fixed_ip'] - try: - self.network_api.associate_floating_ip(context, - floating_ip, fixed_ip) - except rpc.RemoteError: - raise + self.network_api.associate_floating_ip(context, + floating_ip, fixed_ip) floating_ip = self.network_api.get_floating_ip(context, id) return _translate_floating_ip_view(floating_ip) def disassociate(self, req, id, body=None): - """ POST /floating_ips/{id}/disassociate """ + """PUT /floating_ips/{id}/disassociate """ context = req.environ['nova.context'] floating_ip = self.network_api.get_floating_ip(context, id) address = floating_ip['address'] - try: + # no-op if this ip is already disassociated + if 'fixed_ip' in floating_ip: self.network_api.disassociate_floating_ip(context, address) - except rpc.RemoteError: - raise + floating_ip = self.network_api.get_floating_ip(context, id) - floating_ip = self.network_api.get_floating_ip(context, id) return _translate_floating_ip_view(floating_ip) def _get_ip_by_id(self, context, value): @@ -170,8 +163,8 @@ class Floating_ips(extensions.ExtensionDescriptor): res = extensions.ResourceExtension('os-floating-ips', FloatingIPController(), member_actions={ - 'associate': 'POST', - 'disassociate': 'POST'}) + 'associate': 'PUT', + 'disassociate': 'PUT'}) resources.append(res) return resources -- cgit From 65d7db1136557b7af1f0b9413bacc8fc59e7211f Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Wed, 17 Aug 2011 10:23:44 -0700 Subject: pep8 fix --- nova/api/openstack/contrib/floating_ips.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index af3eee16a..2f5fdd001 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -105,9 +105,11 @@ class FloatingIPController(object): floating_ip = self.network_api.get_floating_ip(context, id) if 'fixed_ip' in floating_ip: - self.network_api.disassociate_floating_ip(context, floating_ip['address']) + self.network_api.disassociate_floating_ip(context, + floating_ip['address']) - self.network_api.release_floating_ip(context, address=floating_ip['address']) + self.network_api.release_floating_ip(context, + address=floating_ip['address']) return exc.HTTPAccepted() def associate(self, req, id, body): -- cgit From 9011bf57d8caf8a0bd11dfb33cf968b2b65fe294 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 18 Aug 2011 11:21:35 -0500 Subject: Added rescue mode extension. --- nova/api/openstack/contrib/rescue.py | 72 ++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 nova/api/openstack/contrib/rescue.py (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/rescue.py b/nova/api/openstack/contrib/rescue.py new file mode 100644 index 000000000..efb882fd6 --- /dev/null +++ b/nova/api/openstack/contrib/rescue.py @@ -0,0 +1,72 @@ +# Copyright 2011 Openstack, LLC. +# +# 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. + +"""The rescue mode extension.""" + +import webob +from webob import exc + +from nova import compute +from nova import log as logging +from nova.api.openstack import extensions as exts +from nova.api.openstack import faults + +LOG = logging.getLogger("nova.api.contrib.rescue") + + +class Rescue(exts.ExtensionDescriptor): + """The Rescue API controller for the OpenStack API.""" + def __init__(self): + super(Rescue, self).__init__() + self.compute_api = compute.API() + + def _rescue(self, input_dict, req, instance_id): + """Enable or disable rescue mode.""" + context = req.environ["nova.context"] + action = input_dict["rescue"]["action"] + + try: + if action == "rescue": + self.compute_api.rescue(context, instance_id) + elif action == "unrescue": + self.compute_api.unrescue(context, instance_id) + except Exception, e: + LOG.exception(_("Error in %(action)s: %(e)s") % locals()) + return faults.Fault(exc.HTTPBadRequest()) + + return webob.Response(status_int=202) + + def get_name(self): + return "Rescue" + + def get_alias(self): + return "rescue" + + def get_description(self): + return "Instance rescue mode" + + def get_namespace(self): + return "http://docs.openstack.org/ext/rescue/api/v1.1" + + def get_updated(self): + return "2011-08-18T00:00:00+00:00" + + def get_actions(self): + """Return the actions the extension adds, as required by contract.""" + actions = [ + exts.ActionExtension("servers", "rescue", self._rescue), + exts.ActionExtension("servers", "unrescue", self._rescue), + ] + + return actions -- cgit From a68c1cde2e73e6d39d7ff6024cd3ff289c465619 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 18 Aug 2011 12:20:40 -0500 Subject: Refactored a little and updated unit test. --- nova/api/openstack/contrib/rescue.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/rescue.py b/nova/api/openstack/contrib/rescue.py index efb882fd6..dac269efb 100644 --- a/nova/api/openstack/contrib/rescue.py +++ b/nova/api/openstack/contrib/rescue.py @@ -31,10 +31,10 @@ class Rescue(exts.ExtensionDescriptor): super(Rescue, self).__init__() self.compute_api = compute.API() - def _rescue(self, input_dict, req, instance_id): - """Enable or disable rescue mode.""" + def _rescue(self, input_dict, req, instance_id, exit_rescue=False): + """Rescue an instance.""" context = req.environ["nova.context"] - action = input_dict["rescue"]["action"] + action = "unrescue" if exit_rescue else "rescue" try: if action == "rescue": @@ -47,6 +47,10 @@ class Rescue(exts.ExtensionDescriptor): return webob.Response(status_int=202) + def _unrescue(self, input_dict, req, instance_id): + """Unrescue an instance.""" + self._rescue(input_dict, req, instance_id, exit_rescue=True) + def get_name(self): return "Rescue" @@ -66,7 +70,7 @@ class Rescue(exts.ExtensionDescriptor): """Return the actions the extension adds, as required by contract.""" actions = [ exts.ActionExtension("servers", "rescue", self._rescue), - exts.ActionExtension("servers", "unrescue", self._rescue), + exts.ActionExtension("servers", "unrescue", self._unrescue), ] return actions -- cgit From a9d87715133ae79518cef6aafd87c95e26f20765 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 18 Aug 2011 12:25:22 -0500 Subject: Minor housecleaning. --- nova/api/openstack/contrib/rescue.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/rescue.py b/nova/api/openstack/contrib/rescue.py index dac269efb..65ce2874b 100644 --- a/nova/api/openstack/contrib/rescue.py +++ b/nova/api/openstack/contrib/rescue.py @@ -22,17 +22,22 @@ from nova import log as logging from nova.api.openstack import extensions as exts from nova.api.openstack import faults + LOG = logging.getLogger("nova.api.contrib.rescue") class Rescue(exts.ExtensionDescriptor): - """The Rescue API controller for the OpenStack API.""" + """The Rescue controller for the OpenStack API.""" def __init__(self): super(Rescue, self).__init__() self.compute_api = compute.API() def _rescue(self, input_dict, req, instance_id, exit_rescue=False): - """Rescue an instance.""" + """Rescue an instance. + + If exit_rescue is True, rescue mode should be torn down and the + instance restored to its original state. + """ context = req.environ["nova.context"] action = "unrescue" if exit_rescue else "rescue" -- cgit From fe28c88a6bfff9d8e0d83751ab89e83173aaf092 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 18 Aug 2011 14:56:22 -0500 Subject: Review feedback. --- nova/api/openstack/contrib/rescue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/rescue.py b/nova/api/openstack/contrib/rescue.py index 65ce2874b..5ee071696 100644 --- a/nova/api/openstack/contrib/rescue.py +++ b/nova/api/openstack/contrib/rescue.py @@ -48,7 +48,7 @@ class Rescue(exts.ExtensionDescriptor): self.compute_api.unrescue(context, instance_id) except Exception, e: LOG.exception(_("Error in %(action)s: %(e)s") % locals()) - return faults.Fault(exc.HTTPBadRequest()) + return faults.Fault(exc.HTTPInternalServerError()) return webob.Response(status_int=202) @@ -60,7 +60,7 @@ class Rescue(exts.ExtensionDescriptor): return "Rescue" def get_alias(self): - return "rescue" + return "os-rescue" def get_description(self): return "Instance rescue mode" -- cgit From 508b45a3fda9caa92c90282045495acb6e2f638b Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 18 Aug 2011 15:08:51 -0500 Subject: Better docstring for _unrescue(). --- nova/api/openstack/contrib/rescue.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/rescue.py b/nova/api/openstack/contrib/rescue.py index 5ee071696..a30ed6dff 100644 --- a/nova/api/openstack/contrib/rescue.py +++ b/nova/api/openstack/contrib/rescue.py @@ -53,7 +53,11 @@ class Rescue(exts.ExtensionDescriptor): return webob.Response(status_int=202) def _unrescue(self, input_dict, req, instance_id): - """Unrescue an instance.""" + """Unrescue an instance. + + We pass exit_rescue=True here so _rescue() knows we would like to exit + rescue mode. + """ self._rescue(input_dict, req, instance_id, exit_rescue=True) def get_name(self): -- cgit From 041dcdb2eba968d5be17c9a10bf333e1307f0537 Mon Sep 17 00:00:00 2001 From: Alex Meade Date: Thu, 18 Aug 2011 16:56:23 -0400 Subject: Added 'update' method to ServersXMLSerializer --- nova/api/openstack/servers.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 335ecad86..41e63ec3c 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -923,6 +923,12 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): node.setAttribute('adminPass', server_dict['server']['adminPass']) return self.to_xml_string(node, True) + def update(self, server_dict): + xml_doc = minidom.Document() + node = self._server_to_xml_detailed(xml_doc, + server_dict['server']) + return self.to_xml_string(node, True) + def create_resource(version='1.0'): controller = { -- cgit From f86a5cc4bc43923077ffe1d4098e550841f1c4f0 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 18 Aug 2011 15:58:12 -0500 Subject: Review feedback. --- nova/api/openstack/contrib/rescue.py | 40 +++++++++++++++++------------------- 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/rescue.py b/nova/api/openstack/contrib/rescue.py index a30ed6dff..399bb7f35 100644 --- a/nova/api/openstack/contrib/rescue.py +++ b/nova/api/openstack/contrib/rescue.py @@ -26,39 +26,37 @@ from nova.api.openstack import faults LOG = logging.getLogger("nova.api.contrib.rescue") +def wrap_errors(fn): + """"Ensure errors are not passed along.""" + def wrapped(*args): + try: + fn(*args) + except Exception, e: + return faults.Fault(exc.HTTPInternalServerError()) + return wrapped + + class Rescue(exts.ExtensionDescriptor): """The Rescue controller for the OpenStack API.""" def __init__(self): super(Rescue, self).__init__() self.compute_api = compute.API() - def _rescue(self, input_dict, req, instance_id, exit_rescue=False): - """Rescue an instance. - - If exit_rescue is True, rescue mode should be torn down and the - instance restored to its original state. - """ + @wrap_errors + def _rescue(self, input_dict, req, instance_id): + """Rescue an instance.""" context = req.environ["nova.context"] - action = "unrescue" if exit_rescue else "rescue" - - try: - if action == "rescue": - self.compute_api.rescue(context, instance_id) - elif action == "unrescue": - self.compute_api.unrescue(context, instance_id) - except Exception, e: - LOG.exception(_("Error in %(action)s: %(e)s") % locals()) - return faults.Fault(exc.HTTPInternalServerError()) + self.compute_api.rescue(context, instance_id) return webob.Response(status_int=202) + @wrap_errors def _unrescue(self, input_dict, req, instance_id): - """Unrescue an instance. + """Rescue an instance.""" + context = req.environ["nova.context"] + self.compute_api.unrescue(context, instance_id) - We pass exit_rescue=True here so _rescue() knows we would like to exit - rescue mode. - """ - self._rescue(input_dict, req, instance_id, exit_rescue=True) + return webob.Response(status_int=202) def get_name(self): return "Rescue" -- cgit From 22ba538b3cb3ddd22cef0fc06b136db433a8d202 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 18 Aug 2011 16:07:02 -0500 Subject: Oops. --- nova/api/openstack/contrib/rescue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/rescue.py b/nova/api/openstack/contrib/rescue.py index 399bb7f35..3de128895 100644 --- a/nova/api/openstack/contrib/rescue.py +++ b/nova/api/openstack/contrib/rescue.py @@ -52,7 +52,7 @@ class Rescue(exts.ExtensionDescriptor): @wrap_errors def _unrescue(self, input_dict, req, instance_id): - """Rescue an instance.""" + """Unrescue an instance.""" context = req.environ["nova.context"] self.compute_api.unrescue(context, instance_id) -- cgit From 509ce9d3016731c183bb565e8726a27010eaf02a Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 18 Aug 2011 15:41:20 -0700 Subject: declare the use_forwarded_for flag --- nova/api/ec2/__init__.py | 1 + nova/api/ec2/metadatarequesthandler.py | 1 + 2 files changed, 2 insertions(+) (limited to 'nova/api') diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 2ae370f88..52f381dbb 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -47,6 +47,7 @@ flags.DEFINE_integer('lockout_window', 15, flags.DEFINE_string('keystone_ec2_url', 'http://localhost:5000/v2.0/ec2tokens', 'URL to get token from ec2 request.') +flags.DECLARE('use_forwarded_for', 'nova.api.auth') class RequestLogging(wsgi.Middleware): diff --git a/nova/api/ec2/metadatarequesthandler.py b/nova/api/ec2/metadatarequesthandler.py index 1dc275c90..0198bf490 100644 --- a/nova/api/ec2/metadatarequesthandler.py +++ b/nova/api/ec2/metadatarequesthandler.py @@ -30,6 +30,7 @@ from nova.api.ec2 import cloud LOG = logging.getLogger('nova.api.ec2.metadata') FLAGS = flags.FLAGS +flags.DECLARE('use_forwarded_for', 'nova.api.auth') class MetadataRequestHandler(wsgi.Application): -- cgit From 5366332a84b89bc5a056bd7f43e528a908e8d188 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Fri, 19 Aug 2011 13:15:42 -0700 Subject: incorporate feedback from brian waldon and brian lamar. Move associate/disassociate to server actions --- nova/api/openstack/contrib/floating_ips.py | 69 +++++++++++++++++++----------- 1 file changed, 45 insertions(+), 24 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 2f5fdd001..b305ebdcb 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -15,8 +15,9 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License -from webob import exc +import webob +from nova import compute from nova import exception from nova import log as logging from nova import network @@ -71,7 +72,7 @@ class FloatingIPController(object): try: floating_ip = self.network_api.get_floating_ip(context, id) except exception.NotFound: - return faults.Fault(exc.HTTPNotFound()) + return faults.Fault(webob.exc.HTTPNotFound()) return _translate_floating_ip_view(floating_ip) @@ -110,40 +111,49 @@ class FloatingIPController(object): self.network_api.release_floating_ip(context, address=floating_ip['address']) - return exc.HTTPAccepted() + return webob.exc.HTTPAccepted() - def associate(self, req, id, body): - """PUT /floating_ips/{id}/associate fixed ip in body """ + def _get_ip_by_id(self, context, value): + """Checks that value is id and then returns its address.""" + return self.network_api.get_floating_ip(context, value)['address'] + + +class Floating_ips(extensions.ExtensionDescriptor): + def __init__(self): + self.compute_api = compute.API() + self.network_api = network.API() + super(Floating_ips, self).__init__() + + def _add_floating_ip(self, input_dict, req, instance_id): + """Associate floating_ip to an instance.""" context = req.environ['nova.context'] - floating_ip = self._get_ip_by_id(context, id) - fixed_ip = body['floating_ip']['fixed_ip'] + try: + address = input_dict['addFloatingIp']['address'] + except KeyError: + msg = _("Address not specified") + raise webob.exc.HTTPBadRequest(explanation=msg) - self.network_api.associate_floating_ip(context, - floating_ip, fixed_ip) + self.compute_api.associate_floating_ip(context, instance_id, address) - floating_ip = self.network_api.get_floating_ip(context, id) - return _translate_floating_ip_view(floating_ip) + return webob.Response(status_int=202) - def disassociate(self, req, id, body=None): - """PUT /floating_ips/{id}/disassociate """ + def _remove_floating_ip(self, input_dict, req, instance_id): + """Dissociate floating_ip from an instance.""" context = req.environ['nova.context'] - floating_ip = self.network_api.get_floating_ip(context, id) - address = floating_ip['address'] - # no-op if this ip is already disassociated + try: + address = input_dict['removeFloatingIp']['address'] + except KeyError: + msg = _("Address not specified") + raise webob.exc.HTTPBadRequest(explanation=msg) + + floating_ip = self.network_api.get_floating_ip_by_ip(context, address) if 'fixed_ip' in floating_ip: self.network_api.disassociate_floating_ip(context, address) - floating_ip = self.network_api.get_floating_ip(context, id) - - return _translate_floating_ip_view(floating_ip) - - def _get_ip_by_id(self, context, value): - """Checks that value is id and then returns its address.""" - return self.network_api.get_floating_ip(context, value)['address'] + return webob.Response(status_int=202) -class Floating_ips(extensions.ExtensionDescriptor): def get_name(self): return "Floating_ips" @@ -170,3 +180,14 @@ class Floating_ips(extensions.ExtensionDescriptor): resources.append(res) return resources + + def get_actions(self): + """Return the actions the extension adds, as required by contract.""" + actions = [ + extensions.ActionExtension("servers", "addFloatingIp", + self._add_floating_ip), + extensions.ActionExtension("servers", "removeFloatingIp", + self._remove_floating_ip), + ] + + return actions -- cgit From 468893c667c7ce6cddb9d62906dfcb807fcd6da1 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Fri, 19 Aug 2011 13:25:33 -0700 Subject: a few tweaks - remove unused member functions, add comment --- nova/api/openstack/contrib/floating_ips.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index b305ebdcb..3b400807a 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -80,6 +80,7 @@ class FloatingIPController(object): context = req.environ['nova.context'] try: + # FIXME - why does self.network_api.list_floating_ips raise this? floating_ips = self.network_api.list_floating_ips(context) except exception.FloatingIpNotFoundForProject: floating_ips = [] @@ -174,9 +175,7 @@ class Floating_ips(extensions.ExtensionDescriptor): res = extensions.ResourceExtension('os-floating-ips', FloatingIPController(), - member_actions={ - 'associate': 'PUT', - 'disassociate': 'PUT'}) + member_actions={}) resources.append(res) return resources -- cgit From ce4ac4be2b813a8f025a9f2891fbc1ed4101c496 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Fri, 19 Aug 2011 13:31:49 -0700 Subject: tweak to comment --- nova/api/openstack/contrib/floating_ips.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 3b400807a..0f27f2f27 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -80,7 +80,7 @@ class FloatingIPController(object): context = req.environ['nova.context'] try: - # FIXME - why does self.network_api.list_floating_ips raise this? + # FIXME(ja) - why does self.network_api.list_floating_ips raise? floating_ips = self.network_api.list_floating_ips(context) except exception.FloatingIpNotFoundForProject: floating_ips = [] -- cgit From 9b65cdf0b2d5cc7ed7adcaca0dde4d6e2a10bf95 Mon Sep 17 00:00:00 2001 From: Anthony Young Date: Fri, 19 Aug 2011 14:16:57 -0700 Subject: better handle malformed input, and add associated tests --- nova/api/openstack/contrib/floating_ips.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 0f27f2f27..40086f778 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -131,6 +131,9 @@ class Floating_ips(extensions.ExtensionDescriptor): try: address = input_dict['addFloatingIp']['address'] + except TypeError: + msg = _("Missing parameter dict") + raise webob.exc.HTTPBadRequest(explanation=msg) except KeyError: msg = _("Address not specified") raise webob.exc.HTTPBadRequest(explanation=msg) @@ -145,6 +148,9 @@ class Floating_ips(extensions.ExtensionDescriptor): try: address = input_dict['removeFloatingIp']['address'] + except TypeError: + msg = _("Missing parameter dict") + raise webob.exc.HTTPBadRequest(explanation=msg) except KeyError: msg = _("Address not specified") raise webob.exc.HTTPBadRequest(explanation=msg) -- cgit