summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorAnthony Young <sleepsonthefloor@gmail.com>2011-01-10 13:06:36 -0800
committerAnthony Young <sleepsonthefloor@gmail.com>2011-01-10 13:06:36 -0800
commit220259d6ca9131c738a2d4ea9cd28ca64edbcfeb (patch)
tree27266ddb476586ed2bb87bd53d4fef9c974da8a8 /nova/api
parent9b99e385967c4ba21d94d82aa62115fc11634118 (diff)
parent16c420ee156f8c7716c7e84b66af35cbccf5090c (diff)
downloadnova-220259d6ca9131c738a2d4ea9cd28ca64edbcfeb.tar.gz
nova-220259d6ca9131c738a2d4ea9cd28ca64edbcfeb.tar.xz
nova-220259d6ca9131c738a2d4ea9cd28ca64edbcfeb.zip
merge trunk
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/__init__.py1
-rw-r--r--nova/api/ec2/__init__.py151
-rw-r--r--nova/api/ec2/admin.py38
-rw-r--r--nova/api/ec2/apirequest.py12
-rw-r--r--nova/api/ec2/cloud.py378
-rw-r--r--nova/api/ec2/metadatarequesthandler.py11
-rw-r--r--nova/api/openstack/__init__.py36
-rw-r--r--nova/api/openstack/auth.py6
-rw-r--r--nova/api/openstack/images.py7
-rw-r--r--nova/api/openstack/ratelimiting/__init__.py6
-rw-r--r--nova/api/openstack/servers.py92
11 files changed, 441 insertions, 297 deletions
diff --git a/nova/api/__init__.py b/nova/api/__init__.py
index 26fed847b..803470570 100644
--- a/nova/api/__init__.py
+++ b/nova/api/__init__.py
@@ -24,7 +24,6 @@ Root WSGI middleware for all API controllers.
:ec2api_subdomain: subdomain running the EC2 API (default: ec2)
"""
-import logging
import routes
import webob.dec
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index 51d33bcc6..2fa1f636c 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -20,7 +20,7 @@ Starting point for routing EC2 requests.
"""
-import logging
+import datetime
import routes
import webob
import webob.dec
@@ -29,6 +29,7 @@ import webob.exc
from nova import context
from nova import exception
from nova import flags
+from nova import log as logging
from nova import wsgi
from nova.api.ec2 import apirequest
from nova.api.ec2 import admin
@@ -37,6 +38,7 @@ 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.')
@@ -52,10 +54,6 @@ flags.DEFINE_list('lockout_memcached_servers', None,
'Memcached servers or None for in process cache.')
-_log = logging.getLogger("api")
-_log.setLevel(logging.DEBUG)
-
-
class API(wsgi.Middleware):
"""Routing for all EC2 API requests."""
@@ -64,6 +62,40 @@ class API(wsgi.Middleware):
if FLAGS.use_lockout:
self.application = Lockout(self.application)
+ @webob.dec.wsgify
+ def __call__(self, req):
+ rv = req.get_response(self.application)
+ self.log_request_completion(rv, req)
+ return rv
+
+ def log_request_completion(self, response, request):
+ controller = request.environ.get('ec2.controller', None)
+ if controller:
+ controller = controller.__class__.__name__
+ action = request.environ.get('ec2.action', None)
+ ctxt = request.environ.get('ec2.context', None)
+ seconds = 'X'
+ microseconds = 'X'
+ if ctxt:
+ delta = datetime.datetime.utcnow() - \
+ ctxt.timestamp
+ seconds = delta.seconds
+ microseconds = delta.microseconds
+ LOG.info(
+ "%s.%ss %s %s %s %s:%s %s [%s] %s %s",
+ seconds,
+ microseconds,
+ request.remote_addr,
+ request.method,
+ request.path_info,
+ controller,
+ action,
+ response.status_int,
+ request.user_agent,
+ request.content_type,
+ response.content_type,
+ context=ctxt)
+
class Lockout(wsgi.Middleware):
"""Lockout for x minutes on y failed auths in a z minute period.
@@ -98,7 +130,7 @@ class Lockout(wsgi.Middleware):
failures_key = "authfailures-%s" % access_key
failures = int(self.mc.get(failures_key) or 0)
if failures >= FLAGS.lockout_attempts:
- detail = "Too many failed authentications."
+ detail = _("Too many failed authentications.")
raise webob.exc.HTTPForbidden(detail=detail)
res = req.get_response(self.application)
if res.status_int == 403:
@@ -107,9 +139,9 @@ class Lockout(wsgi.Middleware):
# NOTE(vish): To use incr, failures has to be a string.
self.mc.set(failures_key, '1', time=FLAGS.lockout_window * 60)
elif failures >= FLAGS.lockout_attempts:
- _log.warn('Access key %s has had %d failed authentications'
- ' and will be locked out for %d minutes.' %
- (access_key, failures, FLAGS.lockout_minutes))
+ LOG.warn(_('Access key %s has had %d failed authentications'
+ ' and will be locked out for %d minutes.'),
+ access_key, failures, FLAGS.lockout_minutes)
self.mc.set(failures_key, str(failures),
time=FLAGS.lockout_minutes * 60)
return res
@@ -142,8 +174,9 @@ class Authenticate(wsgi.Middleware):
req.method,
req.host,
req.path)
- except exception.Error, ex:
- logging.debug(_("Authentication Failure: %s") % ex)
+ # Be explicit for what exceptions are 403, the rest bubble as 500
+ except (exception.NotFound, exception.NotAuthorized) as ex:
+ LOG.audit(_("Authentication Failure: %s"), str(ex))
raise webob.exc.HTTPForbidden()
# Authenticated!
@@ -154,6 +187,8 @@ class Authenticate(wsgi.Middleware):
project=project,
remote_address=remote_address)
req.environ['ec2.context'] = ctxt
+ LOG.audit(_('Authenticated Request For %s:%s)'), user.name,
+ project.name, context=req.environ['ec2.context'])
return self.application
@@ -189,9 +224,9 @@ class Router(wsgi.Middleware):
except:
raise webob.exc.HTTPBadRequest()
- _log.debug(_('action: %s') % action)
+ LOG.debug(_('action: %s'), action)
for key, value in args.items():
- _log.debug(_('arg: %s\t\tval: %s') % (key, value))
+ LOG.debug(_('arg: %s\t\tval: %s'), key, value)
# Success!
req.environ['ec2.controller'] = controller
@@ -263,6 +298,9 @@ class Authorizer(wsgi.Middleware):
if self._matches_any_role(context, allowed_roles):
return self.application
else:
+ LOG.audit(_("Unauthorized request for controller=%s "
+ "and action=%s"), controller_name, action,
+ context=context)
raise webob.exc.HTTPUnauthorized()
def _matches_any_role(self, context, roles):
@@ -294,27 +332,88 @@ class Executor(wsgi.Application):
args = req.environ['ec2.action_args']
api_request = apirequest.APIRequest(controller, action)
+ result = None
try:
result = api_request.send(context, **args)
- req.headers['Content-Type'] = 'text/xml'
- return result
+ except exception.NotFound as ex:
+ LOG.info(_('NotFound raised: %s'), str(ex), context=context)
+ return self._error(req, context, type(ex).__name__, str(ex))
except exception.ApiError as ex:
-
+ LOG.exception(_('ApiError raised: %s'), str(ex), context=context)
if ex.code:
- return self._error(req, ex.code, ex.message)
+ return self._error(req, context, ex.code, str(ex))
else:
- return self._error(req, type(ex).__name__, ex.message)
- # TODO(vish): do something more useful with unknown exceptions
+ return self._error(req, context, type(ex).__name__, str(ex))
except Exception as ex:
- return self._error(req, type(ex).__name__, str(ex))
-
- def _error(self, req, code, message):
- logging.error("%s: %s", code, message)
+ extra = {'environment': req.environ}
+ LOG.exception(_('Unexpected error raised: %s'), str(ex),
+ extra=extra, context=context)
+ return self._error(req,
+ context,
+ 'UnknownError',
+ _('An unknown error has occurred. '
+ 'Please try your request again.'))
+ else:
+ resp = webob.Response()
+ resp.status = 200
+ resp.headers['Content-Type'] = 'text/xml'
+ resp.body = str(result)
+ return resp
+
+ def _error(self, req, context, code, message):
+ LOG.error("%s: %s", code, message, context=context)
resp = webob.Response()
resp.status = 400
resp.headers['Content-Type'] = 'text/xml'
resp.body = str('<?xml version="1.0"?>\n'
- '<Response><Errors><Error><Code>%s</Code>'
- '<Message>%s</Message></Error></Errors>'
- '<RequestID>?</RequestID></Response>' % (code, message))
+ '<Response><Errors><Error><Code>%s</Code>'
+ '<Message>%s</Message></Error></Errors>'
+ '<RequestID>%s</RequestID></Response>' %
+ (code, message, context.request_id))
return resp
+
+
+class Versions(wsgi.Application):
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ """Respond to a request for all EC2 versions."""
+ # available api versions
+ versions = [
+ '1.0',
+ '2007-01-19',
+ '2007-03-01',
+ '2007-08-29',
+ '2007-10-10',
+ '2007-12-15',
+ '2008-02-01',
+ '2008-09-01',
+ '2009-04-04',
+ ]
+ return ''.join('%s\n' % v for v in versions)
+
+
+def authenticate_factory(global_args, **local_args):
+ def authenticator(app):
+ return Authenticate(app)
+ return authenticator
+
+
+def router_factory(global_args, **local_args):
+ def router(app):
+ return Router(app)
+ return router
+
+
+def authorizer_factory(global_args, **local_args):
+ def authorizer(app):
+ return Authorizer(app)
+ return authorizer
+
+
+def executor_factory(global_args, **local_args):
+ return Executor()
+
+
+def versions_factory(global_args, **local_args):
+ return Versions()
diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py
index fac01369e..758b612e8 100644
--- a/nova/api/ec2/admin.py
+++ b/nova/api/ec2/admin.py
@@ -24,9 +24,13 @@ import base64
from nova import db
from nova import exception
+from nova import log as logging
from nova.auth import manager
+LOG = logging.getLogger('nova.api.ec2.admin')
+
+
def user_dict(user, base64_file=None):
"""Convert the user object to a result dict"""
if user:
@@ -75,17 +79,18 @@ class AdminController(object):
return {'userSet':
[user_dict(u) for u in manager.AuthManager().get_users()]}
- def register_user(self, _context, name, **_kwargs):
+ def register_user(self, context, name, **_kwargs):
"""Creates a new user, and returns generated credentials."""
+ LOG.audit(_("Creating new user: %s"), name, context=context)
return user_dict(manager.AuthManager().create_user(name))
- def deregister_user(self, _context, name, **_kwargs):
+ def deregister_user(self, context, name, **_kwargs):
"""Deletes a single user (NOT undoable.)
Should throw an exception if the user has instances,
volumes, or buckets remaining.
"""
+ LOG.audit(_("Deleting user: %s"), name, context=context)
manager.AuthManager().delete_user(name)
-
return True
def describe_roles(self, context, project_roles=True, **kwargs):
@@ -105,15 +110,27 @@ class AdminController(object):
operation='add', **kwargs):
"""Add or remove a role for a user and project."""
if operation == 'add':
+ if project:
+ LOG.audit(_("Adding role %s to user %s for project %s"), role,
+ user, project, context=context)
+ else:
+ LOG.audit(_("Adding sitewide role %s to user %s"), role, user,
+ context=context)
manager.AuthManager().add_role(user, role, project)
elif operation == 'remove':
+ if project:
+ LOG.audit(_("Removing role %s from user %s for project %s"),
+ role, user, project, context=context)
+ else:
+ LOG.audit(_("Removing sitewide role %s from user %s"), role,
+ user, context=context)
manager.AuthManager().remove_role(user, role, project)
else:
- raise exception.ApiError('operation must be add or remove')
+ raise exception.ApiError(_('operation must be add or remove'))
return True
- def generate_x509_for_user(self, _context, name, project=None, **kwargs):
+ def generate_x509_for_user(self, context, name, project=None, **kwargs):
"""Generates and returns an x509 certificate for a single user.
Is usually called from a client that will wrap this with
access and secret key info, and return a zip file.
@@ -122,6 +139,8 @@ class AdminController(object):
project = name
project = manager.AuthManager().get_project(project)
user = manager.AuthManager().get_user(name)
+ LOG.audit(_("Getting x509 for user: %s on project: %s"), name,
+ project, context=context)
return user_dict(user, base64.b64encode(project.get_credentials(user)))
def describe_project(self, context, name, **kwargs):
@@ -137,6 +156,8 @@ class AdminController(object):
def register_project(self, context, name, manager_user, description=None,
member_users=None, **kwargs):
"""Creates a new project"""
+ LOG.audit(_("Create project %s managed by %s"), name, manager_user,
+ context=context)
return project_dict(
manager.AuthManager().create_project(
name,
@@ -146,6 +167,7 @@ class AdminController(object):
def deregister_project(self, context, name):
"""Permanently deletes a project."""
+ LOG.audit(_("Delete project: %s"), name, context=context)
manager.AuthManager().delete_project(name)
return True
@@ -159,11 +181,15 @@ class AdminController(object):
**kwargs):
"""Add or remove a user from a project."""
if operation == 'add':
+ LOG.audit(_("Adding user %s to project %s"), user, project,
+ context=context)
manager.AuthManager().add_to_project(user, project)
elif operation == 'remove':
+ LOG.audit(_("Removing user %s from project %s"), user, project,
+ context=context)
manager.AuthManager().remove_from_project(user, project)
else:
- raise exception.ApiError('operation must be add or remove')
+ raise exception.ApiError(_('operation must be add or remove'))
return True
# FIXME(vish): these host commands don't work yet, perhaps some of the
diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py
index a90fbeb0c..d0b417db1 100644
--- a/nova/api/ec2/apirequest.py
+++ b/nova/api/ec2/apirequest.py
@@ -20,13 +20,13 @@
APIRequest class
"""
-import logging
import re
# TODO(termie): replace minidom with etree
from xml.dom import minidom
-_log = logging.getLogger("api")
-_log.setLevel(logging.DEBUG)
+from nova import log as logging
+
+LOG = logging.getLogger("nova.api.request")
_c2u = re.compile('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))')
@@ -94,7 +94,7 @@ class APIRequest(object):
except AttributeError:
_error = _('Unsupported API request: controller = %s,'
'action = %s') % (self.controller, self.action)
- _log.warning(_error)
+ LOG.exception(_error)
# TODO: Raise custom exception, trap in apiserver,
# and reraise as 400 error.
raise Exception(_error)
@@ -142,7 +142,7 @@ class APIRequest(object):
response = xml.toxml()
xml.unlink()
- _log.debug(response)
+ LOG.debug(response)
return response
def _render_dict(self, xml, el, data):
@@ -151,7 +151,7 @@ class APIRequest(object):
val = data[key]
el.appendChild(self._render_data(xml, key, val))
except:
- _log.debug(data)
+ LOG.debug(data)
raise
def _render_data(self, xml, el_name, data):
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index a59131ab5..17b9a14fb 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -24,26 +24,27 @@ datastore.
import base64
import datetime
-import logging
-import re
+import IPy
import os
+from nova import compute
from nova import context
-import IPy
-
from nova import crypto
from nova import db
from nova import exception
from nova import flags
-from nova import quota
+from nova import log as logging
+from nova import network
from nova import rpc
from nova import utils
-from nova.compute import api as compute_api
+from nova import volume
from nova.compute import instance_types
FLAGS = flags.FLAGS
-flags.DECLARE('storage_availability_zone', 'nova.volume.manager')
+flags.DECLARE('service_down_time', 'nova.scheduler.driver')
+
+LOG = logging.getLogger("nova.api.cloud")
InvalidInputException = exception.InvalidInputException
@@ -71,16 +72,16 @@ def _gen_key(context, user_id, key_name):
return {'private_key': private_key, 'fingerprint': fingerprint}
-def ec2_id_to_internal_id(ec2_id):
- """Convert an ec2 ID (i-[base 36 number]) to an internal id (int)"""
+def ec2_id_to_id(ec2_id):
+ """Convert an ec2 ID (i-[base 36 number]) to an instance id (int)"""
return int(ec2_id[2:], 36)
-def internal_id_to_ec2_id(internal_id):
- """Convert an internal ID (int) to an ec2 ID (i-[base 36 number])"""
+def id_to_ec2_id(instance_id):
+ """Convert an instance ID (int) to an ec2 ID (i-[base 36 number])"""
digits = []
- while internal_id != 0:
- internal_id, remainder = divmod(internal_id, 36)
+ while instance_id != 0:
+ instance_id, remainder = divmod(instance_id, 36)
digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[remainder])
return "i-%s" % ''.join(reversed(digits))
@@ -91,10 +92,11 @@ class CloudController(object):
sent to the other nodes.
"""
def __init__(self):
- self.network_manager = utils.import_object(FLAGS.network_manager)
self.image_service = utils.import_object(FLAGS.image_service)
- self.compute_api = compute_api.ComputeAPI(self.network_manager,
- self.image_service)
+ self.network_api = network.API()
+ self.volume_api = volume.API()
+ self.compute_api = compute.API(self.image_service, self.network_api,
+ self.volume_api)
self.setup()
def __str__(self):
@@ -118,7 +120,8 @@ class CloudController(object):
def _get_mpi_data(self, context, project_id):
result = {}
- for instance in self.compute_api.get_instances(context, project_id):
+ for instance in self.compute_api.get_all(context,
+ project_id=project_id):
if instance['fixed_ip']:
line = '%s slots=%d' % (instance['fixed_ip']['address'],
instance['vcpus'])
@@ -129,18 +132,9 @@ class CloudController(object):
result[key] = [line]
return result
- def _trigger_refresh_security_group(self, context, security_group):
- nodes = set([instance['host'] for instance in security_group.instances
- if instance['host'] is not None])
- for node in nodes:
- rpc.cast(context,
- '%s.%s' % (FLAGS.compute_topic, node),
- {"method": "refresh_security_group",
- "args": {"security_group_id": security_group.id}})
-
def get_metadata(self, address):
ctxt = context.get_admin_context()
- instance_ref = db.fixed_ip_get_instance(ctxt, address)
+ instance_ref = self.compute_api.get_all(ctxt, fixed_ip=address)
if instance_ref is None:
return None
mpi = self._get_mpi_data(ctxt, instance_ref['project_id'])
@@ -152,7 +146,7 @@ class CloudController(object):
hostname = instance_ref['hostname']
floating_ip = db.instance_get_floating_address(ctxt,
instance_ref['id'])
- ec2_id = internal_id_to_ec2_id(instance_ref['internal_id'])
+ ec2_id = id_to_ec2_id(instance_ref['id'])
data = {
'user-data': base64.b64decode(instance_ref['user_data']),
'meta-data': {
@@ -188,9 +182,46 @@ class CloudController(object):
return data
def describe_availability_zones(self, context, **kwargs):
+ if ('zone_name' in kwargs and
+ 'verbose' in kwargs['zone_name'] and
+ context.is_admin):
+ return self._describe_availability_zones_verbose(context,
+ **kwargs)
+ else:
+ return self._describe_availability_zones(context, **kwargs)
+
+ def _describe_availability_zones(self, context, **kwargs):
return {'availabilityZoneInfo': [{'zoneName': 'nova',
'zoneState': 'available'}]}
+ def _describe_availability_zones_verbose(self, context, **kwargs):
+ rv = {'availabilityZoneInfo': [{'zoneName': 'nova',
+ 'zoneState': 'available'}]}
+
+ services = db.service_get_all(context)
+ now = datetime.datetime.utcnow()
+ hosts = []
+ for host in [service['host'] for service in services]:
+ if not host in hosts:
+ hosts.append(host)
+ for host in hosts:
+ rv['availabilityZoneInfo'].append({'zoneName': '|- %s' % host,
+ 'zoneState': ''})
+ hsvcs = [service for service in services \
+ if service['host'] == host]
+ for svc in hsvcs:
+ delta = now - (svc['updated_at'] or svc['created_at'])
+ alive = (delta.seconds <= FLAGS.service_down_time)
+ art = (alive and ":-)") or "XXX"
+ active = 'enabled'
+ if svc['disabled']:
+ active = 'disabled'
+ rv['availabilityZoneInfo'].append({
+ 'zoneName': '| |- %s' % svc['binary'],
+ 'zoneState': '%s %s %s' % (active, art,
+ svc['updated_at'])})
+ return rv
+
def describe_regions(self, context, region_name=None, **kwargs):
if FLAGS.region_list:
regions = []
@@ -208,6 +239,7 @@ class CloudController(object):
FLAGS.cc_host,
FLAGS.cc_port,
FLAGS.ec2_suffix)}]
+ return {'regionInfo': regions}
def describe_snapshots(self,
context,
@@ -243,6 +275,7 @@ class CloudController(object):
return {'keypairsSet': result}
def create_key_pair(self, context, key_name, **kwargs):
+ LOG.audit(_("Create key pair %s"), key_name, context=context)
data = _gen_key(context, context.user.id, key_name)
return {'keyName': key_name,
'keyFingerprint': data['fingerprint'],
@@ -250,6 +283,7 @@ class CloudController(object):
# TODO(vish): when context is no longer an object, pass it here
def delete_key_pair(self, context, key_name, **kwargs):
+ LOG.audit(_("Delete key pair %s"), key_name, context=context)
try:
db.key_pair_destroy(context, context.user.id, key_name)
except exception.NotFound:
@@ -356,6 +390,8 @@ class CloudController(object):
return False
def revoke_security_group_ingress(self, context, group_name, **kwargs):
+ LOG.audit(_("Revoke security group ingress %s"), group_name,
+ context=context)
self.compute_api.ensure_default_security_group(context)
security_group = db.security_group_get_by_name(context,
context.project_id,
@@ -373,7 +409,8 @@ class CloudController(object):
match = False
if match:
db.security_group_rule_destroy(context, rule['id'])
- self._trigger_refresh_security_group(context, security_group)
+ self.compute_api.trigger_security_group_rules_refresh(context,
+ security_group['id'])
return True
raise exception.ApiError(_("No rule for the specified parameters."))
@@ -382,6 +419,8 @@ class CloudController(object):
# for these operations, so support for newer API versions
# is sketchy.
def authorize_security_group_ingress(self, context, group_name, **kwargs):
+ LOG.audit(_("Authorize security group ingress %s"), group_name,
+ context=context)
self.compute_api.ensure_default_security_group(context)
security_group = db.security_group_get_by_name(context,
context.project_id,
@@ -396,7 +435,8 @@ class CloudController(object):
security_group_rule = db.security_group_rule_create(context, values)
- self._trigger_refresh_security_group(context, security_group)
+ self.compute_api.trigger_security_group_rules_refresh(context,
+ security_group['id'])
return True
@@ -418,6 +458,7 @@ class CloudController(object):
return source_project_id
def create_security_group(self, context, group_name, group_description):
+ LOG.audit(_("Create Security Group %s"), group_name, context=context)
self.compute_api.ensure_default_security_group(context)
if db.security_group_exists(context, context.project_id, group_name):
raise exception.ApiError(_('group %s already exists') % group_name)
@@ -432,6 +473,7 @@ class CloudController(object):
group_ref)]}
def delete_security_group(self, context, group_name, **kwargs):
+ LOG.audit(_("Delete security group %s"), group_name, context=context)
security_group = db.security_group_get_by_name(context,
context.project_id,
group_name)
@@ -439,10 +481,12 @@ class CloudController(object):
return True
def get_console_output(self, context, instance_id, **kwargs):
+ LOG.audit(_("Get console output for instance %s"), instance_id,
+ context=context)
# instance_id is passed in as a list of instances
ec2_id = instance_id[0]
- internal_id = ec2_id_to_internal_id(ec2_id)
- instance_ref = self.compute_api.get_instance(context, internal_id)
+ instance_id = ec2_id_to_id(ec2_id)
+ instance_ref = self.compute_api.get(context, instance_id)
output = rpc.call(context,
'%s.%s' % (FLAGS.compute_topic,
instance_ref['host']),
@@ -460,27 +504,22 @@ class CloudController(object):
return self.compute_api.get_ajax_console(context, internal_id)
def describe_volumes(self, context, volume_id=None, **kwargs):
- if context.user.is_admin():
- volumes = db.volume_get_all(context)
- else:
- volumes = db.volume_get_all_by_project(context, context.project_id)
-
+ volumes = self.volume_api.get_all(context)
# NOTE(vish): volume_id is an optional list of volume ids to filter by.
volumes = [self._format_volume(context, v) for v in volumes
- if volume_id is None or v['ec2_id'] in volume_id]
-
+ if volume_id is None or v['id'] in volume_id]
return {'volumeSet': volumes}
def _format_volume(self, context, volume):
instance_ec2_id = None
instance_data = None
if volume.get('instance', None):
- internal_id = volume['instance']['internal_id']
- instance_ec2_id = internal_id_to_ec2_id(internal_id)
+ instance_id = volume['instance']['id']
+ instance_ec2_id = id_to_ec2_id(instance_id)
instance_data = '%s[%s]' % (instance_ec2_id,
volume['instance']['host'])
v = {}
- v['volumeId'] = volume['ec2_id']
+ v['volumeId'] = volume['id']
v['status'] = volume['status']
v['size'] = volume['size']
v['availabilityZone'] = volume['availability_zone']
@@ -507,88 +546,51 @@ class CloudController(object):
return v
def create_volume(self, context, size, **kwargs):
- # check quota
- if quota.allowed_volumes(context, 1, size) < 1:
- logging.warn("Quota exceeeded for %s, tried to create %sG volume",
- context.project_id, size)
- raise quota.QuotaError("Volume quota exceeded. You cannot "
- "create a volume of size %s" % size)
- vol = {}
- vol['size'] = size
- vol['user_id'] = context.user.id
- vol['project_id'] = context.project_id
- vol['availability_zone'] = FLAGS.storage_availability_zone
- vol['status'] = "creating"
- vol['attach_status'] = "detached"
- vol['display_name'] = kwargs.get('display_name')
- vol['display_description'] = kwargs.get('display_description')
- volume_ref = db.volume_create(context, vol)
-
- rpc.cast(context,
- FLAGS.scheduler_topic,
- {"method": "create_volume",
- "args": {"topic": FLAGS.volume_topic,
- "volume_id": volume_ref['id']}})
-
+ LOG.audit(_("Create volume of %s GB"), size, context=context)
+ volume = self.volume_api.create(context, size,
+ kwargs.get('display_name'),
+ kwargs.get('display_description'))
# TODO(vish): Instance should be None at db layer instead of
# trying to lazy load, but for now we turn it into
# a dict to avoid an error.
return {'volumeSet': [self._format_volume(context, dict(volume_ref))]}
+ def delete_volume(self, context, volume_id, **kwargs):
+ self.volume_api.delete(context, volume_id)
+ return True
+
+ def update_volume(self, context, volume_id, **kwargs):
+ updatable_fields = ['display_name', 'display_description']
+ changes = {}
+ for field in updatable_fields:
+ if field in kwargs:
+ changes[field] = kwargs[field]
+ if changes:
+ self.volume_api.update(context, volume_id, kwargs)
+ return True
+
def attach_volume(self, context, volume_id, instance_id, device, **kwargs):
- volume_ref = db.volume_get_by_ec2_id(context, volume_id)
- if not re.match("^/dev/[a-z]d[a-z]+$", device):
- raise exception.ApiError(_("Invalid device specified: %s. "
- "Example device: /dev/vdb") % device)
- # TODO(vish): abstract status checking?
- if volume_ref['status'] != "available":
- raise exception.ApiError(_("Volume status must be available"))
- if volume_ref['attach_status'] == "attached":
- raise exception.ApiError(_("Volume is already attached"))
- internal_id = ec2_id_to_internal_id(instance_id)
- instance_ref = self.compute_api.get_instance(context, internal_id)
- host = instance_ref['host']
- rpc.cast(context,
- db.queue_get_for(context, FLAGS.compute_topic, host),
- {"method": "attach_volume",
- "args": {"volume_id": volume_ref['id'],
- "instance_id": instance_ref['id'],
- "mountpoint": device}})
- return {'attachTime': volume_ref['attach_time'],
- 'device': volume_ref['mountpoint'],
- 'instanceId': instance_ref['id'],
+ LOG.audit(_("Attach volume %s to instacne %s at %s"), volume_id,
+ instance_id, device, context=context)
+ self.compute_api.attach_volume(context, instance_id, volume_id, device)
+ volume = self.volume_api.get(context, volume_id)
+ return {'attachTime': volume['attach_time'],
+ 'device': volume['mountpoint'],
+ 'instanceId': instance_id,
'requestId': context.request_id,
- 'status': volume_ref['attach_status'],
- 'volumeId': volume_ref['id']}
+ 'status': volume['attach_status'],
+ 'volumeId': volume_id}
def detach_volume(self, context, volume_id, **kwargs):
- volume_ref = db.volume_get_by_ec2_id(context, volume_id)
- instance_ref = db.volume_get_instance(context.elevated(),
- volume_ref['id'])
- if not instance_ref:
- raise exception.ApiError(_("Volume isn't attached to anything!"))
- # TODO(vish): abstract status checking?
- if volume_ref['status'] == "available":
- raise exception.ApiError(_("Volume is already detached"))
- try:
- host = instance_ref['host']
- rpc.cast(context,
- db.queue_get_for(context, FLAGS.compute_topic, host),
- {"method": "detach_volume",
- "args": {"instance_id": instance_ref['id'],
- "volume_id": volume_ref['id']}})
- except exception.NotFound:
- # If the instance doesn't exist anymore,
- # then we need to call detach blind
- db.volume_detached(context)
- internal_id = instance_ref['internal_id']
- ec2_id = internal_id_to_ec2_id(internal_id)
- return {'attachTime': volume_ref['attach_time'],
- 'device': volume_ref['mountpoint'],
- 'instanceId': internal_id,
+ LOG.audit(_("Detach volume %s"), volume_id, context=context)
+ volume = self.volume_api.get(context, volume_id)
+ instance = self.compute_api.detach_volume(context, volume_id)
+ return {'attachTime': volume['attach_time'],
+ 'device': volume['mountpoint'],
+ 'instanceId': id_to_ec2_id(instance['id']),
'requestId': context.request_id,
- 'status': volume_ref['attach_status'],
- 'volumeId': volume_ref['id']}
+ 'status': volume['attach_status'],
+ 'volumeId': volume_id}
def _convert_to_set(self, lst, label):
if lst == None or lst == []:
@@ -597,41 +599,32 @@ class CloudController(object):
lst = [lst]
return [{label: x} for x in lst]
- def update_volume(self, context, volume_id, **kwargs):
- updatable_fields = ['display_name', 'display_description']
- changes = {}
- for field in updatable_fields:
- if field in kwargs:
- changes[field] = kwargs[field]
- if changes:
- db.volume_update(context, volume_id, kwargs)
- return True
-
def describe_instances(self, context, **kwargs):
- return self._format_describe_instances(context)
+ return self._format_describe_instances(context, **kwargs)
- def _format_describe_instances(self, context):
- return {'reservationSet': self._format_instances(context)}
+ def _format_describe_instances(self, context, **kwargs):
+ return {'reservationSet': self._format_instances(context, **kwargs)}
def _format_run_instances(self, context, reservation_id):
- i = self._format_instances(context, reservation_id)
+ i = self._format_instances(context, reservation_id=reservation_id)
assert len(i) == 1
return i[0]
- def _format_instances(self, context, reservation_id=None):
+ def _format_instances(self, context, instance_id=None, **kwargs):
reservations = {}
- if reservation_id:
- instances = db.instance_get_all_by_reservation(context,
- reservation_id)
+ # NOTE(vish): instance_id is an optional list of ids to filter by
+ if instance_id:
+ instance_id = [ec2_id_to_id(x) for x in instance_id]
+ instances = [self.compute_api.get(context, x) for x in instance_id]
else:
- instances = self.compute_api.get_instances(context)
+ instances = self.compute_api.get_all(context, **kwargs)
for instance in instances:
if not context.user.is_admin():
if instance['image_id'] == FLAGS.vpn_image_id:
continue
i = {}
- internal_id = instance['internal_id']
- ec2_id = internal_id_to_ec2_id(internal_id)
+ instance_id = instance['id']
+ ec2_id = id_to_ec2_id(instance_id)
i['instanceId'] = ec2_id
i['imageId'] = instance['image_id']
i['instanceState'] = {
@@ -684,8 +677,8 @@ class CloudController(object):
ec2_id = None
if (floating_ip_ref['fixed_ip']
and floating_ip_ref['fixed_ip']['instance']):
- internal_id = floating_ip_ref['fixed_ip']['instance']['ec2_id']
- ec2_id = internal_id_to_ec2_id(internal_id)
+ instance_id = floating_ip_ref['fixed_ip']['instance']['ec2_id']
+ ec2_id = id_to_ec2_id(instance_id)
address_rv = {'public_ip': address,
'instance_id': ec2_id}
if context.user.is_admin():
@@ -696,69 +689,30 @@ class CloudController(object):
return {'addressesSet': addresses}
def allocate_address(self, context, **kwargs):
- # check quota
- if quota.allowed_floating_ips(context, 1) < 1:
- logging.warn(_("Quota exceeeded for %s, tried to allocate "
- "address"),
- context.project_id)
- raise quota.QuotaError(_("Address quota exceeded. You cannot "
- "allocate any more addresses"))
- # NOTE(vish): We don't know which network host should get the ip
- # when we allocate, so just send it to any one. This
- # will probably need to move into a network supervisor
- # at some point.
- public_ip = rpc.call(context,
- FLAGS.network_topic,
- {"method": "allocate_floating_ip",
- "args": {"project_id": context.project_id}})
+ LOG.audit(_("Allocate address"), context=context)
+ public_ip = self.network_api.allocate_floating_ip(context)
return {'addressSet': [{'publicIp': public_ip}]}
def release_address(self, context, public_ip, **kwargs):
- floating_ip_ref = db.floating_ip_get_by_address(context, public_ip)
- # NOTE(vish): We don't know which network host should get the ip
- # when we deallocate, so just send it to any one. This
- # will probably need to move into a network supervisor
- # at some point.
- rpc.cast(context,
- FLAGS.network_topic,
- {"method": "deallocate_floating_ip",
- "args": {"floating_address": floating_ip_ref['address']}})
+ LOG.audit(_("Release address %s"), public_ip, context=context)
+ self.network_api.release_floating_ip(context, public_ip)
return {'releaseResponse': ["Address released."]}
def associate_address(self, context, instance_id, public_ip, **kwargs):
- internal_id = ec2_id_to_internal_id(instance_id)
- instance_ref = self.compute_api.get_instance(context, internal_id)
- fixed_address = db.instance_get_fixed_address(context,
- instance_ref['id'])
- floating_ip_ref = db.floating_ip_get_by_address(context, public_ip)
- # NOTE(vish): Perhaps we should just pass this on to compute and
- # let compute communicate with network.
- network_topic = self.compute_api.get_network_topic(context,
- internal_id)
- rpc.cast(context,
- network_topic,
- {"method": "associate_floating_ip",
- "args": {"floating_address": floating_ip_ref['address'],
- "fixed_address": fixed_address}})
+ LOG.audit(_("Associate address %s to instance %s"), public_ip,
+ instance_id, context=context)
+ instance_id = ec2_id_to_id(instance_id)
+ self.compute_api.associate_floating_ip(context, instance_id, public_ip)
return {'associateResponse': ["Address associated."]}
def disassociate_address(self, context, public_ip, **kwargs):
- floating_ip_ref = db.floating_ip_get_by_address(context, public_ip)
- # NOTE(vish): Get the topic from the host name of the network of
- # the associated fixed ip.
- if not floating_ip_ref.get('fixed_ip'):
- raise exception.ApiError('Address is not associated.')
- host = floating_ip_ref['fixed_ip']['network']['host']
- topic = db.queue_get_for(context, FLAGS.network_topic, host)
- rpc.cast(context,
- topic,
- {"method": "disassociate_floating_ip",
- "args": {"floating_address": floating_ip_ref['address']}})
+ LOG.audit(_("Disassociate address %s"), public_ip, context=context)
+ self.network_api.disassociate_floating_ip(context, public_ip)
return {'disassociateResponse': ["Address disassociated."]}
def run_instances(self, context, **kwargs):
max_count = int(kwargs.get('max_count', 1))
- instances = self.compute_api.create_instances(context,
+ instances = self.compute_api.create(context,
instance_types.get_by_type(kwargs.get('instance_type', None)),
kwargs['image_id'],
min_count=int(kwargs.get('min_count', max_count)),
@@ -766,40 +720,43 @@ class CloudController(object):
kernel_id=kwargs.get('kernel_id', None),
ramdisk_id=kwargs.get('ramdisk_id'),
display_name=kwargs.get('display_name'),
- description=kwargs.get('display_description'),
+ display_description=kwargs.get('display_description'),
key_name=kwargs.get('key_name'),
user_data=kwargs.get('user_data'),
security_group=kwargs.get('security_group'),
- generate_hostname=internal_id_to_ec2_id)
+ availability_zone=kwargs.get('placement', {}).get(
+ 'AvailabilityZone'),
+ generate_hostname=id_to_ec2_id)
return self._format_run_instances(context,
instances[0]['reservation_id'])
def terminate_instances(self, context, instance_id, **kwargs):
"""Terminate each instance in instance_id, which is a list of ec2 ids.
instance_id is a kwarg so its name cannot be modified."""
- logging.debug("Going to start terminating instances")
+ LOG.debug(_("Going to start terminating instances"))
for ec2_id in instance_id:
- internal_id = ec2_id_to_internal_id(ec2_id)
- self.compute_api.delete_instance(context, internal_id)
+ instance_id = ec2_id_to_id(ec2_id)
+ self.compute_api.delete(context, instance_id)
return True
def reboot_instances(self, context, instance_id, **kwargs):
"""instance_id is a list of instance ids"""
+ LOG.audit(_("Reboot instance %r"), instance_id, context=context)
for ec2_id in instance_id:
- internal_id = ec2_id_to_internal_id(ec2_id)
- self.compute_api.reboot(context, internal_id)
+ instance_id = ec2_id_to_id(ec2_id)
+ self.compute_api.reboot(context, instance_id)
return True
def rescue_instance(self, context, instance_id, **kwargs):
"""This is an extension to the normal ec2_api"""
- internal_id = ec2_id_to_internal_id(instance_id)
- self.compute_api.rescue(context, internal_id)
+ instance_id = ec2_id_to_id(instance_id)
+ self.compute_api.rescue(context, instance_id)
return True
def unrescue_instance(self, context, instance_id, **kwargs):
"""This is an extension to the normal ec2_api"""
- internal_id = ec2_id_to_internal_id(instance_id)
- self.compute_api.unrescue(context, internal_id)
+ instance_id = ec2_id_to_id(instance_id)
+ self.compute_api.unrescue(context, instance_id)
return True
def update_instance(self, context, ec2_id, **kwargs):
@@ -809,24 +766,8 @@ class CloudController(object):
if field in kwargs:
changes[field] = kwargs[field]
if changes:
- internal_id = ec2_id_to_internal_id(ec2_id)
- inst = self.compute_api.get_instance(context, internal_id)
- db.instance_update(context, inst['id'], kwargs)
- return True
-
- def delete_volume(self, context, volume_id, **kwargs):
- # TODO: return error if not authorized
- volume_ref = db.volume_get_by_ec2_id(context, volume_id)
- if volume_ref['status'] != "available":
- raise exception.ApiError(_("Volume status must be available"))
- now = datetime.datetime.utcnow()
- db.volume_update(context, volume_ref['id'], {'status': 'deleting',
- 'terminated_at': now})
- host = volume_ref['host']
- rpc.cast(context,
- db.queue_get_for(context, FLAGS.volume_topic, host),
- {"method": "delete_volume",
- "args": {"volume_id": volume_ref['id']}})
+ instance_id = ec2_id_to_id(ec2_id)
+ self.compute_api.update(context, instance_id, **kwargs)
return True
def describe_images(self, context, image_id=None, **kwargs):
@@ -837,6 +778,7 @@ class CloudController(object):
return {'imagesSet': images}
def deregister_image(self, context, image_id, **kwargs):
+ LOG.audit(_("De-registering image %s"), image_id, context=context)
self.image_service.deregister(context, image_id)
return {'imageId': image_id}
@@ -844,7 +786,8 @@ class CloudController(object):
if image_location is None and 'name' in kwargs:
image_location = kwargs['name']
image_id = self.image_service.register(context, image_location)
- logging.debug("Registered %s as %s" % (image_location, image_id))
+ LOG.audit(_("Registered image %s with id %s"), image_location,
+ image_id, context=context)
return {'imageId': image_id}
def describe_image_attribute(self, context, image_id, attribute, **kwargs):
@@ -872,6 +815,7 @@ class CloudController(object):
raise exception.ApiError(_('only group "all" is supported'))
if not operation_type in ['add', 'remove']:
raise exception.ApiError(_('operation_type must be add or remove'))
+ LOG.audit(_("Updating image %s publicity"), image_id, context=context)
return self.image_service.modify(context, image_id, operation_type)
def update_image(self, context, image_id, **kwargs):
diff --git a/nova/api/ec2/metadatarequesthandler.py b/nova/api/ec2/metadatarequesthandler.py
index f832863a9..848f0b034 100644
--- a/nova/api/ec2/metadatarequesthandler.py
+++ b/nova/api/ec2/metadatarequesthandler.py
@@ -18,15 +18,15 @@
"""Metadata request handler."""
-import logging
-
import webob.dec
import webob.exc
+from nova import log as logging
from nova import flags
from nova.api.ec2 import cloud
+LOG = logging.getLogger('nova.api.ec2.metadata')
FLAGS = flags.FLAGS
@@ -72,10 +72,13 @@ class MetadataRequestHandler(object):
remote_address = req.headers.get('X-Forwarded-For', remote_address)
meta_data = cc.get_metadata(remote_address)
if meta_data is None:
- logging.error(_('Failed to get metadata for ip: %s') %
- remote_address)
+ LOG.error(_('Failed to get metadata for ip: %s'), remote_address)
raise webob.exc.HTTPNotFound()
data = self.lookup(req.path_info, meta_data)
if data is None:
raise webob.exc.HTTPNotFound()
return self.print_data(data)
+
+
+def metadata_factory(global_args, **local_args):
+ return MetadataRequestHandler()
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index ea6dff004..ad203c51f 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -20,30 +20,24 @@
WSGI middleware for OpenStack API controllers.
"""
-import json
-import time
-
-import logging
import routes
-import traceback
import webob.dec
import webob.exc
import webob
-from nova import context
from nova import flags
+from nova import log as logging
from nova import utils
from nova import wsgi
from nova.api.openstack import faults
from nova.api.openstack import backup_schedules
from nova.api.openstack import flavors
from nova.api.openstack import images
-from nova.api.openstack import ratelimiting
from nova.api.openstack import servers
from nova.api.openstack import sharedipgroups
-from nova.auth import manager
+LOG = logging.getLogger('nova.api.openstack')
FLAGS = flags.FLAGS
flags.DEFINE_string('os_api_auth',
'nova.api.openstack.auth.AuthMiddleware',
@@ -73,8 +67,7 @@ class API(wsgi.Middleware):
try:
return req.get_response(self.application)
except Exception as ex:
- logging.warn(_("Caught error: %s") % str(ex))
- logging.error(traceback.format_exc())
+ LOG.exception(_("Caught error: %s"), str(ex))
exc = webob.exc.HTTPInternalServerError(explanation=str(ex))
return faults.Fault(exc)
@@ -90,7 +83,7 @@ class APIRouter(wsgi.Router):
server_members = {'action': 'POST'}
if FLAGS.allow_admin_api:
- logging.debug("Including admin operations in API.")
+ LOG.debug(_("Including admin operations in API."))
server_members['pause'] = 'POST'
server_members['unpause'] = 'POST'
server_members["diagnostics"] = "GET"
@@ -115,3 +108,24 @@ class APIRouter(wsgi.Router):
controller=sharedipgroups.Controller())
super(APIRouter, self).__init__(mapper)
+
+
+class Versions(wsgi.Application):
+ @webob.dec.wsgify
+ def __call__(self, req):
+ """Respond to a request for all OpenStack API versions."""
+ response = {
+ "versions": [
+ dict(status="CURRENT", id="v1.0")]}
+ metadata = {
+ "application/xml": {
+ "attributes": dict(version=["status", "id"])}}
+ return wsgi.Serializer(req.environ, metadata).to_content_type(response)
+
+
+def router_factory(global_cof, **local_conf):
+ return APIRouter()
+
+
+def versions_factory(global_conf, **local_conf):
+ return Versions()
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index 1dfdd5318..00e817c8d 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -134,3 +134,9 @@ class AuthMiddleware(wsgi.Middleware):
token = self.db.auth_create_token(ctxt, token_dict)
return token, user
return None, None
+
+
+def auth_factory(global_conf, **local_conf):
+ def auth(app):
+ return AuthMiddleware(app)
+ return auth
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index 867ee5a7e..0b239aab8 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -17,15 +17,14 @@
from webob import exc
+from nova import compute
from nova import flags
from nova import utils
from nova import wsgi
import nova.api.openstack
-import nova.image.service
-
from nova.api.openstack import common
from nova.api.openstack import faults
-from nova.compute import api as compute_api
+import nova.image.service
FLAGS = flags.FLAGS
@@ -131,7 +130,7 @@ class Controller(wsgi.Controller):
env = self._deserialize(req.body, req)
instance_id = env["image"]["serverId"]
name = env["image"]["name"]
- return compute_api.ComputeAPI().snapshot(context, instance_id, name)
+ return compute.API().snapshot(context, instance_id, name)
def update(self, req, id):
# Users may not modify public images, and that's all that
diff --git a/nova/api/openstack/ratelimiting/__init__.py b/nova/api/openstack/ratelimiting/__init__.py
index cbb4b897e..81b83142f 100644
--- a/nova/api/openstack/ratelimiting/__init__.py
+++ b/nova/api/openstack/ratelimiting/__init__.py
@@ -219,3 +219,9 @@ class WSGIAppProxy(object):
# No delay
return None
return float(resp.getheader('X-Wait-Seconds'))
+
+
+def ratelimit_factory(global_conf, **local_conf):
+ def rl(app):
+ return RateLimitingMiddleware(app)
+ return rl
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 15082cb54..4cc44d352 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -15,17 +15,17 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
import traceback
from webob import exc
+from nova import compute
from nova import exception
+from nova import log as logging
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
from nova.auth import manager as auth_manager
-from nova.compute import api as compute_api
from nova.compute import instance_types
from nova.compute import power_state
import nova.api.openstack
@@ -51,7 +51,7 @@ def _translate_detail_keys(inst):
inst_dict = {}
mapped_keys = dict(status='state', imageId='image_id',
- flavorId='instance_type', name='display_name', id='internal_id')
+ flavorId='instance_type', name='display_name', id='id')
for k, v in mapped_keys.iteritems():
inst_dict[k] = inst[v]
@@ -67,7 +67,7 @@ def _translate_detail_keys(inst):
def _translate_keys(inst):
""" Coerces into dictionary format, excluding all model attributes
save for id and name """
- return dict(server=dict(id=inst['internal_id'], name=inst['display_name']))
+ return dict(server=dict(id=inst['id'], name=inst['display_name']))
class Controller(wsgi.Controller):
@@ -80,7 +80,7 @@ class Controller(wsgi.Controller):
"status", "progress"]}}}
def __init__(self):
- self.compute_api = compute_api.ComputeAPI()
+ self.compute_api = compute.API()
super(Controller, self).__init__()
def index(self, req):
@@ -96,8 +96,7 @@ class Controller(wsgi.Controller):
entity_maker - either _translate_detail_keys or _translate_keys
"""
- instance_list = self.compute_api.get_instances(
- req.environ['nova.context'])
+ instance_list = self.compute_api.get_all(req.environ['nova.context'])
limited_list = common.limited(instance_list, req)
res = [entity_maker(inst)['server'] for inst in limited_list]
return dict(servers=res)
@@ -105,8 +104,7 @@ class Controller(wsgi.Controller):
def show(self, req, id):
""" Returns server details by server id """
try:
- instance = self.compute_api.get_instance(
- req.environ['nova.context'], int(id))
+ instance = self.compute_api.get(req.environ['nova.context'], id)
return _translate_detail_keys(instance)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
@@ -114,8 +112,7 @@ class Controller(wsgi.Controller):
def delete(self, req, id):
""" Destroys a server """
try:
- self.compute_api.delete_instance(req.environ['nova.context'],
- int(id))
+ self.compute_api.delete(req.environ['nova.context'], id)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
@@ -128,12 +125,12 @@ class Controller(wsgi.Controller):
key_pair = auth_manager.AuthManager.get_key_pairs(
req.environ['nova.context'])[0]
- instances = self.compute_api.create_instances(
+ instances = self.compute_api.create(
req.environ['nova.context'],
instance_types.get_by_flavor_id(env['server']['flavorId']),
env['server']['imageId'],
display_name=env['server']['name'],
- description=env['server']['name'],
+ display_description=env['server']['name'],
key_name=key_pair['name'],
key_data=key_pair['public_key'])
return _translate_keys(instances[0])
@@ -151,10 +148,8 @@ class Controller(wsgi.Controller):
update_dict['display_name'] = inst_dict['server']['name']
try:
- ctxt = req.environ['nova.context']
- self.compute_api.update_instance(ctxt,
- id,
- **update_dict)
+ self.compute_api.update(req.environ['nova.context'], id,
+ **update_dict)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPNoContent()
@@ -175,6 +170,50 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
+ def lock(self, req, id):
+ """
+ lock the instance with id
+ admin only operation
+
+ """
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.lock(context, id)
+ except:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::lock %s"), readable)
+ return faults.Fault(exc.HTTPUnprocessableEntity())
+ return exc.HTTPAccepted()
+
+ def unlock(self, req, id):
+ """
+ unlock the instance with id
+ admin only operation
+
+ """
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.unlock(context, id)
+ except:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::unlock %s"), readable)
+ return faults.Fault(exc.HTTPUnprocessableEntity())
+ return exc.HTTPAccepted()
+
+ def get_lock(self, req, id):
+ """
+ return the boolean state of (instance with id)'s lock
+
+ """
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.get_lock(context, id)
+ except:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::get_lock %s"), readable)
+ return faults.Fault(exc.HTTPUnprocessableEntity())
+ return exc.HTTPAccepted()
+
def pause(self, req, id):
""" Permit Admins to Pause the server. """
ctxt = req.environ['nova.context']
@@ -182,7 +221,7 @@ class Controller(wsgi.Controller):
self.compute_api.pause(ctxt, id)
except:
readable = traceback.format_exc()
- logging.error(_("Compute.api::pause %s"), readable)
+ LOG.exception(_("Compute.api::pause %s"), readable)
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
@@ -193,7 +232,7 @@ class Controller(wsgi.Controller):
self.compute_api.unpause(ctxt, id)
except:
readable = traceback.format_exc()
- logging.error(_("Compute.api::unpause %s"), readable)
+ LOG.exception(_("Compute.api::unpause %s"), readable)
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
@@ -204,7 +243,7 @@ class Controller(wsgi.Controller):
self.compute_api.suspend(context, id)
except:
readable = traceback.format_exc()
- logging.error(_("compute.api::suspend %s"), readable)
+ LOG.exception(_("compute.api::suspend %s"), readable)
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
@@ -215,7 +254,7 @@ class Controller(wsgi.Controller):
self.compute_api.resume(context, id)
except:
readable = traceback.format_exc()
- logging.error(_("compute.api::resume %s"), readable)
+ LOG.exception(_("compute.api::resume %s"), readable)
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
@@ -236,4 +275,13 @@ class Controller(wsgi.Controller):
def actions(self, req, id):
"""Permit Admins to retrieve server actions."""
ctxt = req.environ["nova.context"]
- return self.compute_api.get_actions(ctxt, id)
+ items = self.compute_api.get_actions(ctxt, id)
+ actions = []
+ # TODO(jk0): Do not do pre-serialization here once the default
+ # serializer is updated
+ for item in items:
+ actions.append(dict(
+ created_at=str(item.created_at),
+ action=item.action,
+ error=item.error))
+ return dict(actions=actions)