summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorTrey Morris <trey.morris@rackspace.com>2011-05-24 16:19:46 -0500
committerTrey Morris <trey.morris@rackspace.com>2011-05-24 16:19:46 -0500
commitd3c6f77f287f8078606ca7fc99a8121cadb76fd4 (patch)
treee37cb63b7448436fefff8d11388d56ee64615177 /nova/api
parent08a22883fd5bf58b5b74645d1b2065a0be8c733b (diff)
parent330b3febe9970a0358cbc145ea88faeb3da121d5 (diff)
moved auto assign floating ip functionality from compute manager to network manager
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/ec2/__init__.py10
-rw-r--r--nova/api/ec2/admin.py2
-rw-r--r--nova/api/ec2/cloud.py50
-rw-r--r--nova/api/ec2/ec2utils.py2
-rw-r--r--nova/api/openstack/__init__.py9
-rw-r--r--nova/api/openstack/accounts.py2
-rw-r--r--nova/api/openstack/auth.py17
-rw-r--r--nova/api/openstack/common.py11
-rw-r--r--nova/api/openstack/flavors.py3
-rw-r--r--nova/api/openstack/images.py2
-rw-r--r--nova/api/openstack/limits.py31
-rw-r--r--nova/api/openstack/servers.py130
-rw-r--r--nova/api/openstack/users.py2
-rw-r--r--nova/api/openstack/views/images.py17
-rw-r--r--nova/api/openstack/views/limits.py109
-rw-r--r--nova/api/openstack/views/servers.py8
16 files changed, 309 insertions, 96 deletions
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index a3c3b25a1..cd59340bd 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -46,8 +46,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_list('lockout_memcached_servers', None,
- 'Memcached servers or None for in process cache.')
class RequestLogging(wsgi.Middleware):
@@ -107,11 +105,11 @@ class Lockout(wsgi.Middleware):
def __init__(self, application):
"""middleware can use fake for testing."""
- if FLAGS.lockout_memcached_servers:
+ if FLAGS.memcached_servers:
import memcache
else:
from nova import fakememcache as memcache
- self.mc = memcache.Client(FLAGS.lockout_memcached_servers,
+ self.mc = memcache.Client(FLAGS.memcached_servers,
debug=0)
super(Lockout, self).__init__(application)
@@ -322,9 +320,7 @@ class Executor(wsgi.Application):
except exception.InstanceNotFound as ex:
LOG.info(_('InstanceNotFound raised: %s'), unicode(ex),
context=context)
- ec2_id = ec2utils.id_to_ec2_id(ex.instance_id)
- message = _('Instance %s not found') % ec2_id
- return self._error(req, context, type(ex).__name__, message)
+ return self._error(req, context, type(ex).__name__, ex.message)
except exception.VolumeNotFound as ex:
LOG.info(_('VolumeNotFound raised: %s'), unicode(ex),
context=context)
diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py
index 6a5609d4a..ea94d9c1f 100644
--- a/nova/api/ec2/admin.py
+++ b/nova/api/ec2/admin.py
@@ -266,7 +266,7 @@ class AdminController(object):
def _vpn_for(self, context, project_id):
"""Get the VPN instance for a project ID."""
for instance in db.instance_get_all_by_project(context, project_id):
- if (instance['image_id'] == FLAGS.vpn_image_id
+ if (instance['image_id'] == str(FLAGS.vpn_image_id)
and not instance['state_description'] in
['shutting_down', 'shutdown']):
return instance
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 42ade8dca..de34a2874 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -35,6 +35,7 @@ from nova import crypto
from nova import db
from nova import exception
from nova import flags
+from nova import ipv6
from nova import log as logging
from nova import network
from nova import utils
@@ -49,8 +50,6 @@ flags.DECLARE('service_down_time', 'nova.scheduler.driver')
LOG = logging.getLogger("nova.api.cloud")
-InvalidInputException = exception.InvalidInputException
-
def _gen_key(context, user_id, key_name):
"""Generate a key
@@ -61,8 +60,7 @@ def _gen_key(context, user_id, key_name):
# creation before creating key_pair
try:
db.key_pair_get(context, user_id, key_name)
- raise exception.Duplicate(_("The key_pair %s already exists")
- % key_name)
+ raise exception.KeyPairExists(key_name=key_name)
except exception.NotFound:
pass
private_key, public_key, fingerprint = crypto.generate_key_pair()
@@ -159,7 +157,7 @@ class CloudController(object):
floating_ip = db.instance_get_floating_address(ctxt,
instance_ref['id'])
ec2_id = ec2utils.id_to_ec2_id(instance_ref['id'])
- image_ec2_id = self._image_ec2_id(instance_ref['image_id'], 'ami')
+ image_ec2_id = self.image_ec2_id(instance_ref['image_id'])
data = {
'user-data': base64.b64decode(instance_ref['user_data']),
'meta-data': {
@@ -188,8 +186,8 @@ class CloudController(object):
for image_type in ['kernel', 'ramdisk']:
if instance_ref.get('%s_id' % image_type):
- ec2_id = self._image_ec2_id(instance_ref['%s_id' % image_type],
- self._image_type(image_type))
+ ec2_id = self.image_ec2_id(instance_ref['%s_id' % image_type],
+ self._image_type(image_type))
data['meta-data']['%s-id' % image_type] = ec2_id
if False: # TODO(vish): store ancestor ids
@@ -399,11 +397,11 @@ class CloudController(object):
ip_protocol = str(ip_protocol)
if ip_protocol.upper() not in ['TCP', 'UDP', 'ICMP']:
- raise InvalidInputException(_('%s is not a valid ipProtocol') %
- (ip_protocol,))
+ raise exception.InvalidIpProtocol(protocol=ip_protocol)
if ((min(from_port, to_port) < -1) or
(max(from_port, to_port) > 65535)):
- raise InvalidInputException(_('Invalid port range'))
+ raise exception.InvalidPortRange(from_port=from_port,
+ to_port=to_port)
values['protocol'] = ip_protocol
values['from_port'] = from_port
@@ -703,13 +701,13 @@ class CloudController(object):
instances = self.compute_api.get_all(context, **kwargs)
for instance in instances:
if not context.is_admin:
- if instance['image_id'] == FLAGS.vpn_image_id:
+ if instance['image_id'] == str(FLAGS.vpn_image_id):
continue
i = {}
instance_id = instance['id']
ec2_id = ec2utils.id_to_ec2_id(instance_id)
i['instanceId'] = ec2_id
- i['imageId'] = self._image_ec2_id(instance['image_id'])
+ i['imageId'] = self.image_ec2_id(instance['image_id'])
i['instanceState'] = {
'code': instance['state'],
'name': instance['state_description']}
@@ -721,9 +719,10 @@ class CloudController(object):
fixed = instance['fixed_ip']
floating_addr = fixed['floating_ips'][0]['address']
if instance['fixed_ip']['network'] and 'use_v6' in kwargs:
- i['dnsNameV6'] = utils.to_global_ipv6(
+ i['dnsNameV6'] = ipv6.to_global(
instance['fixed_ip']['network']['cidr_v6'],
- instance['fixed_ip']['mac_address']['address'])
+ instance['fixed_ip']['mac_address']['address'],
+ instance['project_id'])
i['privateDnsName'] = fixed_addr
i['privateIpAddress'] = fixed_addr
@@ -900,7 +899,7 @@ class CloudController(object):
return image_type
@staticmethod
- def _image_ec2_id(image_id, image_type='ami'):
+ def image_ec2_id(image_id, image_type='ami'):
"""Returns image ec2_id using id and three letter type."""
template = image_type + '-%08x'
return ec2utils.id_to_ec2_id(int(image_id), template=template)
@@ -909,25 +908,25 @@ class CloudController(object):
try:
internal_id = ec2utils.ec2_id_to_id(ec2_id)
return self.image_service.show(context, internal_id)
- except exception.NotFound:
+ except (exception.InvalidEc2Id, exception.ImageNotFound):
try:
return self.image_service.show_by_name(context, ec2_id)
except exception.NotFound:
- raise exception.NotFound(_('Image %s not found') % ec2_id)
+ raise exception.ImageNotFound(image_id=ec2_id)
def _format_image(self, image):
"""Convert from format defined by BaseImageService to S3 format."""
i = {}
image_type = self._image_type(image.get('container_format'))
- ec2_id = self._image_ec2_id(image.get('id'), image_type)
+ ec2_id = self.image_ec2_id(image.get('id'), image_type)
name = image.get('name')
i['imageId'] = ec2_id
kernel_id = image['properties'].get('kernel_id')
if kernel_id:
- i['kernelId'] = self._image_ec2_id(kernel_id, 'aki')
+ i['kernelId'] = self.image_ec2_id(kernel_id, 'aki')
ramdisk_id = image['properties'].get('ramdisk_id')
if ramdisk_id:
- i['ramdiskId'] = self._image_ec2_id(ramdisk_id, 'ari')
+ i['ramdiskId'] = self.image_ec2_id(ramdisk_id, 'ari')
i['imageOwnerId'] = image['properties'].get('owner_id')
if name:
i['imageLocation'] = "%s (%s)" % (image['properties'].
@@ -957,8 +956,7 @@ class CloudController(object):
try:
image = self._get_image(context, ec2_id)
except exception.NotFound:
- raise exception.NotFound(_('Image %s not found') %
- ec2_id)
+ raise exception.ImageNotFound(image_id=ec2_id)
images.append(image)
else:
images = self.image_service.detail(context)
@@ -978,8 +976,8 @@ class CloudController(object):
metadata = {'properties': {'image_location': image_location}}
image = self.image_service.create(context, metadata)
image_type = self._image_type(image.get('container_format'))
- image_id = self._image_ec2_id(image['id'],
- image_type)
+ image_id = self.image_ec2_id(image['id'],
+ image_type)
msg = _("Registered image %(image_location)s with"
" id %(image_id)s") % locals()
LOG.audit(msg, context=context)
@@ -992,7 +990,7 @@ class CloudController(object):
try:
image = self._get_image(context, image_id)
except exception.NotFound:
- raise exception.NotFound(_('Image %s not found') % image_id)
+ raise exception.ImageNotFound(image_id=image_id)
result = {'imageId': image_id, 'launchPermission': []}
if image['is_public']:
result['launchPermission'].append({'group': 'all'})
@@ -1015,7 +1013,7 @@ class CloudController(object):
try:
image = self._get_image(context, image_id)
except exception.NotFound:
- raise exception.NotFound(_('Image %s not found') % image_id)
+ raise exception.ImageNotFound(image_id=image_id)
internal_id = image['id']
del(image['id'])
diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py
index 3b34f6ea5..163aa4ed2 100644
--- a/nova/api/ec2/ec2utils.py
+++ b/nova/api/ec2/ec2utils.py
@@ -24,7 +24,7 @@ def ec2_id_to_id(ec2_id):
try:
return int(ec2_id.split('-')[-1], 16)
except ValueError:
- raise exception.NotFound(_("Id %s Not Found") % ec2_id)
+ raise exception.InvalidEc2Id(ec2_id=ec2_id)
def id_to_ec2_id(instance_id, template='i-%08x'):
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index 5e76a06f7..348b70d5b 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -112,9 +112,6 @@ class APIRouter(wsgi.Router):
parent_resource=dict(member_name='server',
collection_name='servers'))
- _limits = limits.LimitsController()
- mapper.resource("limit", "limits", controller=_limits)
-
super(APIRouter, self).__init__(mapper)
@@ -145,6 +142,9 @@ class APIRouterV10(APIRouter):
parent_resource=dict(member_name='server',
collection_name='servers'))
+ mapper.resource("limit", "limits",
+ controller=limits.LimitsControllerV10())
+
mapper.resource("ip", "ips", controller=ips.Controller(),
collection=dict(public='GET', private='GET'),
parent_resource=dict(member_name='server',
@@ -178,3 +178,6 @@ class APIRouterV11(APIRouter):
mapper.resource("flavor", "flavors",
controller=flavors.ControllerV11(),
collection={'detail': 'GET'})
+
+ mapper.resource("limit", "limits",
+ controller=limits.LimitsControllerV11())
diff --git a/nova/api/openstack/accounts.py b/nova/api/openstack/accounts.py
index 6e3763e47..00fdd4540 100644
--- a/nova/api/openstack/accounts.py
+++ b/nova/api/openstack/accounts.py
@@ -48,7 +48,7 @@ class Controller(common.OpenstackController):
"""We cannot depend on the db layer to check for admin access
for the auth manager, so we do it here"""
if not context.is_admin:
- raise exception.NotAuthorized(_("Not admin user."))
+ raise exception.AdminRequired()
def index(self, req):
raise faults.Fault(webob.exc.HTTPNotImplemented())
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index 311e6bde9..6c6ee22a2 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -17,7 +17,6 @@
import datetime
import hashlib
-import json
import time
import webob.exc
@@ -25,11 +24,9 @@ import webob.dec
from nova import auth
from nova import context
-from nova import db
from nova import exception
from nova import flags
from nova import log as logging
-from nova import manager
from nova import utils
from nova import wsgi
from nova.api.openstack import faults
@@ -102,11 +99,11 @@ class AuthMiddleware(wsgi.Middleware):
token, user = self._authorize_user(username, key, req)
if user and token:
res = webob.Response()
- res.headers['X-Auth-Token'] = token.token_hash
+ res.headers['X-Auth-Token'] = token['token_hash']
res.headers['X-Server-Management-Url'] = \
- token.server_management_url
- res.headers['X-Storage-Url'] = token.storage_url
- res.headers['X-CDN-Management-Url'] = token.cdn_management_url
+ token['server_management_url']
+ res.headers['X-Storage-Url'] = token['storage_url']
+ res.headers['X-CDN-Management-Url'] = token['cdn_management_url']
res.content_type = 'text/plain'
res.status = '204'
LOG.debug(_("Successfully authenticated '%s'") % username)
@@ -130,11 +127,11 @@ class AuthMiddleware(wsgi.Middleware):
except exception.NotFound:
return None
if token:
- delta = datetime.datetime.now() - token.created_at
+ delta = datetime.datetime.utcnow() - token['created_at']
if delta.days >= 2:
- self.db.auth_token_destroy(ctxt, token.token_hash)
+ self.db.auth_token_destroy(ctxt, token['token_hash'])
else:
- return self.auth.get_user(token.user_id)
+ return self.auth.get_user(token['user_id'])
return None
def _authorize_user(self, username, key, req):
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index 0b6dc944a..32cd689ca 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import re
from urlparse import urlparse
import webob
@@ -124,16 +125,22 @@ def get_image_id_from_image_hash(image_service, context, image_hash):
"should have numerical format") % image_id
LOG.error(msg)
raise Exception(msg)
- raise exception.NotFound(image_hash)
+ raise exception.ImageNotFound(image_id=image_hash)
def get_id_from_href(href):
"""Return the id portion of a url as an int.
- Given: http://www.foo.com/bar/123?q=4
+ Given: 'http://www.foo.com/bar/123?q=4'
+ Returns: 123
+
+ In order to support local hrefs, the href argument can be just an id:
+ Given: '123'
Returns: 123
"""
+ if re.match(r'\d+$', str(href)):
+ return int(href)
try:
return int(urlparse(href).path.split('/')[-1])
except:
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index 40787bd17..4c5971cf6 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -45,6 +45,9 @@ class Controller(common.OpenstackController):
items = self._get_flavors(req, is_detail=True)
return dict(flavors=items)
+ def _get_view_builder(self, req):
+ raise NotImplementedError()
+
def _get_flavors(self, req, is_detail=True):
"""Helper function that returns a list of flavor dicts."""
ctxt = req.environ['nova.context']
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index 77baf5947..34d4c27fc 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -127,7 +127,7 @@ class Controller(common.OpenstackController):
raise webob.exc.HTTPBadRequest()
image = self._compute_service.snapshot(context, server_id, image_name)
- return self.get_builder(req).build(image, detail=True)
+ return dict(image=self.get_builder(req).build(image, detail=True))
def get_builder(self, request):
"""Indicates that you must use a Controller subclass."""
diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py
index 9877af191..47bc238f1 100644
--- a/nova/api/openstack/limits.py
+++ b/nova/api/openstack/limits.py
@@ -33,7 +33,7 @@ from webob.dec import wsgify
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
-from nova.wsgi import Middleware
+from nova.api.openstack.views import limits as limits_views
# Convenience constants for the limits dictionary passed to Limiter().
@@ -51,8 +51,8 @@ class LimitsController(common.OpenstackController):
_serialization_metadata = {
"application/xml": {
"attributes": {
- "limit": ["verb", "URI", "regex", "value", "unit",
- "resetTime", "remaining", "name"],
+ "limit": ["verb", "URI", "uri", "regex", "value", "unit",
+ "resetTime", "next-available", "remaining", "name"],
},
"plurals": {
"rate": "limit",
@@ -67,12 +67,21 @@ class LimitsController(common.OpenstackController):
abs_limits = {}
rate_limits = req.environ.get("nova.limits", [])
- return {
- "limits": {
- "rate": rate_limits,
- "absolute": abs_limits,
- },
- }
+ builder = self._get_view_builder(req)
+ return builder.build(rate_limits, abs_limits)
+
+ def _get_view_builder(self, req):
+ raise NotImplementedError()
+
+
+class LimitsControllerV10(LimitsController):
+ def _get_view_builder(self, req):
+ return limits_views.ViewBuilderV10()
+
+
+class LimitsControllerV11(LimitsController):
+ def _get_view_builder(self, req):
+ return limits_views.ViewBuilderV11()
class Limit(object):
@@ -186,7 +195,7 @@ DEFAULT_LIMITS = [
]
-class RateLimitingMiddleware(Middleware):
+class RateLimitingMiddleware(wsgi.Middleware):
"""
Rate-limits requests passing through this middleware. All limit information
is stored in memory for this implementation.
@@ -200,7 +209,7 @@ class RateLimitingMiddleware(Middleware):
@param application: WSGI application to wrap
@param limits: List of dictionaries describing limits
"""
- Middleware.__init__(self, application)
+ wsgi.Middleware.__init__(self, application)
self._limiter = Limiter(limits or DEFAULT_LIMITS)
@wsgify(RequestClass=wsgi.Request)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 415c0995f..8f2de2afe 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -14,28 +14,25 @@
# under the License.
import base64
-import hashlib
import traceback
from webob import exc
from xml.dom import minidom
from nova import compute
-from nova import context
from nova import exception
from nova import flags
from nova import log as logging
from nova import quota
from nova import utils
-from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
import nova.api.openstack.views.addresses
import nova.api.openstack.views.flavors
+import nova.api.openstack.views.images
import nova.api.openstack.views.servers
from nova.auth import manager as auth_manager
from nova.compute import instance_types
-from nova.compute import power_state
import nova.api.openstack
from nova.scheduler import api as scheduler_api
@@ -78,6 +75,21 @@ class Controller(common.OpenstackController):
""" Returns a list of server details for a given user """
return self._items(req, is_detail=True)
+ def _image_id_from_req_data(self, data):
+ raise NotImplementedError()
+
+ def _flavor_id_from_req_data(self, data):
+ raise NotImplementedError()
+
+ def _get_view_builder(self, req):
+ raise NotImplementedError()
+
+ def _limit_items(self, items, req):
+ raise NotImplementedError()
+
+ def _action_rebuild(self, info, request, instance_id):
+ raise NotImplementedError()
+
def _items(self, req, is_detail):
"""Returns a list of servers for a given user.
@@ -320,10 +332,6 @@ class Controller(common.OpenstackController):
return faults.Fault(exc.HTTPBadRequest())
return exc.HTTPAccepted()
- def _action_rebuild(self, input_dict, req, id):
- LOG.debug(_("Rebuild server action is not implemented"))
- return faults.Fault(exc.HTTPNotImplemented())
-
def _action_resize(self, input_dict, req, id):
""" Resizes a given instance to the flavor size requested """
try:
@@ -564,9 +572,8 @@ class Controller(common.OpenstackController):
"""
image_id = image_meta['id']
if image_meta['status'] != 'active':
- raise exception.Invalid(
- _("Cannot build from image %(image_id)s, status not active") %
- locals())
+ raise exception.ImageUnacceptable(image_id=image_id,
+ reason=_("status is not active"))
if image_meta.get('container_format') != 'ami':
return None, None
@@ -574,14 +581,12 @@ class Controller(common.OpenstackController):
try:
kernel_id = image_meta['properties']['kernel_id']
except KeyError:
- raise exception.NotFound(
- _("Kernel not found for image %(image_id)s") % locals())
+ raise exception.KernelNotFoundForImage(image_id=image_id)
try:
ramdisk_id = image_meta['properties']['ramdisk_id']
except KeyError:
- raise exception.NotFound(
- _("Ramdisk not found for image %(image_id)s") % locals())
+ raise exception.RamdiskNotFoundForImage(image_id=image_id)
return kernel_id, ramdisk_id
@@ -598,19 +603,35 @@ class ControllerV10(Controller):
return nova.api.openstack.views.servers.ViewBuilderV10(
addresses_builder)
- def _get_addresses_view_builder(self, req):
- return nova.api.openstack.views.addresses.ViewBuilderV10(req)
-
def _limit_items(self, items, req):
return common.limited(items, req)
def _parse_update(self, context, server_id, inst_dict, update_dict):
if 'adminPass' in inst_dict['server']:
update_dict['admin_pass'] = inst_dict['server']['adminPass']
- try:
- self.compute_api.set_admin_password(context, server_id)
- except exception.TimeoutException:
- return exc.HTTPRequestTimeout()
+ self.compute_api.set_admin_password(context, server_id)
+
+ def _action_rebuild(self, info, request, instance_id):
+ context = request.environ['nova.context']
+ instance_id = int(instance_id)
+
+ try:
+ image_id = info["rebuild"]["imageId"]
+ except (KeyError, TypeError):
+ msg = _("Could not parse imageId from request.")
+ LOG.debug(msg)
+ return faults.Fault(exc.HTTPBadRequest(explanation=msg))
+
+ try:
+ self.compute_api.rebuild(context, instance_id, image_id)
+ except exception.BuildInProgress:
+ msg = _("Instance %d is currently being rebuilt.") % instance_id
+ LOG.debug(msg)
+ return faults.Fault(exc.HTTPConflict(explanation=msg))
+
+ response = exc.HTTPAccepted()
+ response.empty_body = True
+ return response
class ControllerV11(Controller):
@@ -632,9 +653,6 @@ class ControllerV11(Controller):
return nova.api.openstack.views.servers.ViewBuilderV11(
addresses_builder, flavor_builder, image_builder, base_url)
- def _get_addresses_view_builder(self, req):
- return nova.api.openstack.views.addresses.ViewBuilderV11(req)
-
def _action_change_password(self, input_dict, req, id):
context = req.environ['nova.context']
if (not 'changePassword' in input_dict
@@ -651,6 +669,63 @@ class ControllerV11(Controller):
def _limit_items(self, items, req):
return common.limited_by_marker(items, req)
+ def _validate_metadata(self, metadata):
+ """Ensure that we can work with the metadata given."""
+ try:
+ metadata.iteritems()
+ except AttributeError as ex:
+ msg = _("Unable to parse metadata key/value pairs.")
+ LOG.debug(msg)
+ raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+
+ def _decode_personalities(self, personalities):
+ """Decode the Base64-encoded personalities."""
+ for personality in personalities:
+ try:
+ path = personality["path"]
+ contents = personality["contents"]
+ except (KeyError, TypeError):
+ msg = _("Unable to parse personality path/contents.")
+ LOG.info(msg)
+ raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+
+ try:
+ personality["contents"] = base64.b64decode(contents)
+ except TypeError:
+ msg = _("Personality content could not be Base64 decoded.")
+ LOG.info(msg)
+ raise faults.Fault(exc.HTTPBadRequest(explanation=msg))
+
+ def _action_rebuild(self, info, request, instance_id):
+ context = request.environ['nova.context']
+ instance_id = int(instance_id)
+
+ try:
+ image_ref = info["rebuild"]["imageRef"]
+ except (KeyError, TypeError):
+ msg = _("Could not parse imageRef from request.")
+ LOG.debug(msg)
+ return faults.Fault(exc.HTTPBadRequest(explanation=msg))
+
+ image_id = common.get_id_from_href(image_ref)
+ personalities = info["rebuild"].get("personality", [])
+ metadata = info["rebuild"].get("metadata", {})
+
+ self._validate_metadata(metadata)
+ self._decode_personalities(personalities)
+
+ try:
+ self.compute_api.rebuild(context, instance_id, image_id, metadata,
+ personalities)
+ except exception.BuildInProgress:
+ msg = _("Instance %d is currently being rebuilt.") % instance_id
+ LOG.debug(msg)
+ return faults.Fault(exc.HTTPConflict(explanation=msg))
+
+ response = exc.HTTPAccepted()
+ response.empty_body = True
+ return response
+
def _get_server_admin_password(self, server):
""" Determine the admin password for a server on creation """
password = server.get('adminPass')
@@ -683,8 +758,9 @@ class ServerCreateRequestXMLDeserializer(object):
"""Marshal the server attribute of a parsed request"""
server = {}
server_node = self._find_first_child_named(node, 'server')
- for attr in ["name", "imageId", "flavorId"]:
- server[attr] = server_node.getAttribute(attr)
+ for attr in ["name", "imageId", "flavorId", "imageRef", "flavorRef"]:
+ if server_node.getAttribute(attr):
+ server[attr] = server_node.getAttribute(attr)
metadata = self._extract_metadata(server_node)
if metadata is not None:
server["metadata"] = metadata
diff --git a/nova/api/openstack/users.py b/nova/api/openstack/users.py
index 077ccfc79..7ae4c3232 100644
--- a/nova/api/openstack/users.py
+++ b/nova/api/openstack/users.py
@@ -48,7 +48,7 @@ class Controller(common.OpenstackController):
"""We cannot depend on the db layer to check for admin access
for the auth manager, so we do it here"""
if not context.is_admin:
- raise exception.NotAuthorized(_("Not admin user"))
+ raise exception.AdminRequired()
def index(self, req):
"""Return all users in brief"""
diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py
index 9dec8a355..2773c9c13 100644
--- a/nova/api/openstack/views/images.py
+++ b/nova/api/openstack/views/images.py
@@ -46,6 +46,14 @@ class ViewBuilder(object):
except KeyError:
image['status'] = image['status'].upper()
+ def _build_server(self, image, instance_id):
+ """Indicates that you must use a ViewBuilder subclass."""
+ raise NotImplementedError
+
+ def generate_server_ref(self, server_id):
+ """Return an href string pointing to this server."""
+ return os.path.join(self._url, "servers", str(server_id))
+
def generate_href(self, image_id):
"""Return an href string pointing to this object."""
return os.path.join(self._url, "images", str(image_id))
@@ -66,7 +74,7 @@ class ViewBuilder(object):
if "instance_id" in properties:
try:
- image["serverId"] = int(properties["instance_id"])
+ self._build_server(image, int(properties["instance_id"]))
except ValueError:
pass
@@ -85,12 +93,17 @@ class ViewBuilder(object):
class ViewBuilderV10(ViewBuilder):
"""OpenStack API v1.0 Image Builder"""
- pass
+
+ def _build_server(self, image, instance_id):
+ image["serverId"] = instance_id
class ViewBuilderV11(ViewBuilder):
"""OpenStack API v1.1 Image Builder"""
+ def _build_server(self, image, instance_id):
+ image["serverRef"] = self.generate_server_ref(instance_id)
+
def build(self, image_obj, detail=False):
"""Return a standardized image structure for display by the API."""
image = ViewBuilder.build(self, image_obj, detail)
diff --git a/nova/api/openstack/views/limits.py b/nova/api/openstack/views/limits.py
new file mode 100644
index 000000000..22d1c260d
--- /dev/null
+++ b/nova/api/openstack/views/limits.py
@@ -0,0 +1,109 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010-2011 OpenStack LLC.
+# 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.
+
+import time
+
+from nova.api.openstack import common
+
+
+class ViewBuilder(object):
+ """Openstack API base limits view builder."""
+
+ def _build_rate_limits(self, rate_limits):
+ raise NotImplementedError()
+
+ def _build_rate_limit(self, rate_limit):
+ raise NotImplementedError()
+
+ def _build_absolute_limits(self, absolute_limit):
+ raise NotImplementedError()
+
+ def build(self, rate_limits, absolute_limits):
+ rate_limits = self._build_rate_limits(rate_limits)
+ absolute_limits = self._build_absolute_limits(absolute_limits)
+
+ output = {
+ "limits": {
+ "rate": rate_limits,
+ "absolute": absolute_limits,
+ },
+ }
+
+ return output
+
+
+class ViewBuilderV10(ViewBuilder):
+ """Openstack API v1.0 limits view builder."""
+
+ def _build_rate_limits(self, rate_limits):
+ return [self._build_rate_limit(r) for r in rate_limits]
+
+ def _build_rate_limit(self, rate_limit):
+ return {
+ "verb": rate_limit["verb"],
+ "URI": rate_limit["URI"],
+ "regex": rate_limit["regex"],
+ "value": rate_limit["value"],
+ "remaining": int(rate_limit["remaining"]),
+ "unit": rate_limit["unit"],
+ "resetTime": rate_limit["resetTime"],
+ }
+
+ def _build_absolute_limits(self, absolute_limit):
+ return {}
+
+
+class ViewBuilderV11(ViewBuilder):
+ """Openstack API v1.1 limits view builder."""
+
+ def _build_rate_limits(self, rate_limits):
+ limits = []
+ for rate_limit in rate_limits:
+ _rate_limit_key = None
+ _rate_limit = self._build_rate_limit(rate_limit)
+
+ # check for existing key
+ for limit in limits:
+ if limit["uri"] == rate_limit["URI"] and \
+ limit["regex"] == limit["regex"]:
+ _rate_limit_key = limit
+ break
+
+ # ensure we have a key if we didn't find one
+ if not _rate_limit_key:
+ _rate_limit_key = {
+ "uri": rate_limit["URI"],
+ "regex": rate_limit["regex"],
+ "limit": [],
+ }
+ limits.append(_rate_limit_key)
+
+ _rate_limit_key["limit"].append(_rate_limit)
+
+ return limits
+
+ def _build_rate_limit(self, rate_limit):
+ return {
+ "verb": rate_limit["verb"],
+ "value": rate_limit["value"],
+ "remaining": int(rate_limit["remaining"]),
+ "unit": rate_limit["unit"],
+ "next-available": rate_limit["resetTime"],
+ }
+
+ def _build_absolute_limits(self, absolute_limit):
+ return {}
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index e52bfaea3..0be468edc 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -63,10 +63,12 @@ class ViewBuilder(object):
power_state.BLOCKED: 'ACTIVE',
power_state.SUSPENDED: 'SUSPENDED',
power_state.PAUSED: 'PAUSED',
- power_state.SHUTDOWN: 'ACTIVE',
- power_state.SHUTOFF: 'ACTIVE',
+ power_state.SHUTDOWN: 'SHUTDOWN',
+ power_state.SHUTOFF: 'SHUTOFF',
power_state.CRASHED: 'ERROR',
- power_state.FAILED: 'ERROR'}
+ power_state.FAILED: 'ERROR',
+ power_state.BUILDING: 'BUILD',
+ }
inst_dict = {
'id': int(inst['id']),