summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorJason Koelker <jason@koelker.net>2011-06-14 10:47:54 -0500
committerJason Koelker <jason@koelker.net>2011-06-14 10:47:54 -0500
commit1cc98ecfcb529b23fb4cb84964aef2c8b6a2c63f (patch)
tree3922e01959d5ad4c0abe70b5402ad0b69d6eb986 /nova/api
parent4d422639ca03cbccdc8dd2d9ac1ee363b21909c7 (diff)
parent1f430f7dd8e8e5af639d91048237048bdf8f21a9 (diff)
merge with trey
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/direct.py2
-rw-r--r--nova/api/ec2/__init__.py1
-rw-r--r--nova/api/ec2/admin.py4
-rw-r--r--nova/api/ec2/cloud.py26
-rw-r--r--nova/api/openstack/__init__.py2
-rw-r--r--nova/api/openstack/auth.py17
-rw-r--r--nova/api/openstack/common.py46
-rw-r--r--nova/api/openstack/extensions.py4
-rw-r--r--nova/api/openstack/images.py91
-rw-r--r--nova/api/openstack/servers.py4
-rw-r--r--nova/api/openstack/versions.py2
-rw-r--r--nova/api/openstack/views/limits.py9
-rw-r--r--nova/api/openstack/zones.py28
13 files changed, 150 insertions, 86 deletions
diff --git a/nova/api/direct.py b/nova/api/direct.py
index ea20042a7..ec79151b1 100644
--- a/nova/api/direct.py
+++ b/nova/api/direct.py
@@ -324,7 +324,7 @@ class Limited(object):
def __init__(self, proxy):
self._proxy = proxy
- if not self.__doc__:
+ if not self.__doc__: # pylint: disable=E0203
self.__doc__ = proxy.__doc__
if not self._allowed:
self._allowed = []
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index 1915d007d..890d57fe7 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -242,6 +242,7 @@ class Authorizer(wsgi.Middleware):
'CreateKeyPair': ['all'],
'DeleteKeyPair': ['all'],
'DescribeSecurityGroups': ['all'],
+ 'ImportPublicKey': ['all'],
'AuthorizeSecurityGroupIngress': ['netadmin'],
'RevokeSecurityGroupIngress': ['netadmin'],
'CreateSecurityGroup': ['netadmin'],
diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py
index aeebd86fb..57d0a0339 100644
--- a/nova/api/ec2/admin.py
+++ b/nova/api/ec2/admin.py
@@ -324,7 +324,3 @@ class AdminController(object):
rv.append(host_dict(host, compute, instances, volume, volumes,
now))
return {'hosts': rv}
-
- def describe_host(self, _context, name, **_kwargs):
- """Returns status info for single node."""
- return host_dict(db.host_get(name))
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 117611d4b..3c3f259b4 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -136,6 +136,13 @@ class CloudController(object):
return services[0]['availability_zone']
return 'unknown zone'
+ def _get_image_state(self, image):
+ # NOTE(vish): fallback status if image_state isn't set
+ state = image.get('status')
+ if state == 'active':
+ state = 'available'
+ return image['properties'].get('image_state', state)
+
def get_metadata(self, address):
ctxt = context.get_admin_context()
instance_ref = self.compute_api.get_all(ctxt, fixed_ip=address)
@@ -793,7 +800,7 @@ class CloudController(object):
if instance['fixed_ip']['network'] and 'use_v6' in kwargs:
i['dnsNameV6'] = ipv6.to_global(
instance['fixed_ip']['network']['cidr_v6'],
- instance['fixed_ip']['mac_address']['address'],
+ instance['fixed_ip']['virtual_interface']['address'],
instance['project_id'])
i['privateDnsName'] = fixed_addr
@@ -895,6 +902,16 @@ class CloudController(object):
if kwargs.get('ramdisk_id'):
ramdisk = self._get_image(context, kwargs['ramdisk_id'])
kwargs['ramdisk_id'] = ramdisk['id']
+ image = self._get_image(context, kwargs['image_id'])
+
+ if image:
+ image_state = self._get_image_state(image)
+ else:
+ raise exception.ImageNotFound(image_id=kwargs['image_id'])
+
+ if image_state != 'available':
+ raise exception.ApiError(_('Image must be available'))
+
instances = self.compute_api.create(context,
instance_type=instance_types.get_instance_type_by_name(
kwargs.get('instance_type', None)),
@@ -1010,11 +1027,8 @@ class CloudController(object):
get('image_location'), name)
else:
i['imageLocation'] = image['properties'].get('image_location')
- # NOTE(vish): fallback status if image_state isn't set
- state = image.get('status')
- if state == 'active':
- state = 'available'
- i['imageState'] = image['properties'].get('image_state', state)
+
+ i['imageState'] = self._get_image_state(image)
i['displayName'] = name
i['description'] = image.get('description')
display_mapping = {'aki': 'kernel',
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index d8fb5265b..c116e4220 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -101,7 +101,7 @@ class APIRouter(base_wsgi.Router):
mapper.resource("zone", "zones",
controller=zones.create_resource(),
collection={'detail': 'GET', 'info': 'GET',
- 'select': 'GET'})
+ 'select': 'POST'})
mapper.resource("user", "users",
controller=users.create_resource(),
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index b49bf449b..7c3e683d6 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -49,19 +49,22 @@ class AuthMiddleware(wsgi.Middleware):
if not self.has_authentication(req):
return self.authenticate(req)
user = self.get_user_by_authentication(req)
- accounts = self.auth.get_projects(user=user)
if not user:
token = req.headers["X-Auth-Token"]
msg = _("%(user)s could not be found with token '%(token)s'")
LOG.warn(msg % locals())
return faults.Fault(webob.exc.HTTPUnauthorized())
- if accounts:
- #we are punting on this til auth is settled,
- #and possibly til api v1.1 (mdragon)
- account = accounts[0]
- else:
- return faults.Fault(webob.exc.HTTPUnauthorized())
+ try:
+ account = req.headers["X-Auth-Project-Id"]
+ except KeyError:
+ # FIXME(usrleon): It needed only for compatibility
+ # while osapi clients don't use this header
+ accounts = self.auth.get_projects(user=user)
+ if accounts:
+ account = accounts[0]
+ else:
+ return faults.Fault(webob.exc.HTTPUnauthorized())
if not self.auth.is_admin(user) and \
not self.auth.is_project_member(user, account):
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index 571e46766..ce7e2805c 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -35,6 +35,37 @@ XML_NS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0'
XML_NS_V11 = 'http://docs.openstack.org/compute/api/v1.1'
+def get_pagination_params(request):
+ """Return marker, limit tuple from request.
+
+ :param request: `wsgi.Request` possibly containing 'marker' and 'limit'
+ GET variables. 'marker' is the id of the last element
+ the client has seen, and 'limit' is the maximum number
+ of items to return. If 'limit' is not specified, 0, or
+ > max_limit, we default to max_limit. Negative values
+ for either marker or limit will cause
+ exc.HTTPBadRequest() exceptions to be raised.
+
+ """
+ try:
+ marker = int(request.GET.get('marker', 0))
+ except ValueError:
+ raise webob.exc.HTTPBadRequest(_('marker param must be an integer'))
+
+ try:
+ limit = int(request.GET.get('limit', 0))
+ except ValueError:
+ raise webob.exc.HTTPBadRequest(_('limit param must be an integer'))
+
+ if limit < 0:
+ raise webob.exc.HTTPBadRequest(_('limit param must be positive'))
+
+ if marker < 0:
+ raise webob.exc.HTTPBadRequest(_('marker param must be positive'))
+
+ return(marker, limit)
+
+
def limited(items, request, max_limit=FLAGS.osapi_max_limit):
"""
Return a slice of items according to requested offset and limit.
@@ -71,19 +102,10 @@ def limited(items, request, max_limit=FLAGS.osapi_max_limit):
def limited_by_marker(items, request, max_limit=FLAGS.osapi_max_limit):
"""Return a slice of items according to the requested marker and limit."""
+ (marker, limit) = get_pagination_params(request)
- try:
- marker = int(request.GET.get('marker', 0))
- except ValueError:
- raise webob.exc.HTTPBadRequest(_('marker param must be an integer'))
-
- try:
- limit = int(request.GET.get('limit', max_limit))
- except ValueError:
- raise webob.exc.HTTPBadRequest(_('limit param must be an integer'))
-
- if limit < 0:
- raise webob.exc.HTTPBadRequest(_('limit param must be positive'))
+ if limit == 0:
+ limit = max_limit
limit = min(max_limit, limit)
start_index = 0
diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py
index 881b61733..54e17e23d 100644
--- a/nova/api/openstack/extensions.py
+++ b/nova/api/openstack/extensions.py
@@ -137,7 +137,7 @@ class ActionExtensionResource(wsgi.Resource):
def __init__(self, application):
controller = ActionExtensionController(application)
- super(ActionExtensionResource, self).__init__(controller)
+ wsgi.Resource.__init__(self, controller)
def add_action(self, action_name, handler):
self.controller.add_action(action_name, handler)
@@ -164,7 +164,7 @@ class RequestExtensionResource(wsgi.Resource):
def __init__(self, application):
controller = RequestExtensionController(application)
- super(RequestExtensionResource, self).__init__(controller)
+ wsgi.Resource.__init__(self, controller)
def add_handler(self, handler):
self.controller.add_handler(handler)
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index 59d9e3082..5ffd8e96a 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -47,30 +47,6 @@ class Controller(object):
self._image_service = image_service or \
nova.image.get_default_image_service()
- def index(self, req):
- """Return an index listing of images available to the request.
-
- :param req: `wsgi.Request` object
- """
- context = req.environ['nova.context']
- filters = self._get_filters(req)
- images = self._image_service.index(context, filters)
- images = common.limited(images, req)
- builder = self.get_builder(req).build
- return dict(images=[builder(image, detail=False) for image in images])
-
- def detail(self, req):
- """Return a detailed index listing of images available to the request.
-
- :param req: `wsgi.Request` object.
- """
- context = req.environ['nova.context']
- filters = self._get_filters(req)
- images = self._image_service.detail(context, filters)
- images = common.limited(images, req)
- builder = self.get_builder(req).build
- return dict(images=[builder(image, detail=True) for image in images])
-
def _get_filters(self, req):
"""
Return a dictionary of query param filters from the request
@@ -123,7 +99,7 @@ class Controller(object):
raise webob.exc.HTTPBadRequest()
try:
- server_id = body["image"]["serverId"]
+ server_id = self._server_id_from_req_data(body)
image_name = body["image"]["name"]
except KeyError:
raise webob.exc.HTTPBadRequest()
@@ -135,6 +111,9 @@ class Controller(object):
"""Indicates that you must use a Controller subclass."""
raise NotImplementedError
+ def _server_id_from_req_data(self, data):
+ raise NotImplementedError()
+
class ControllerV10(Controller):
"""Version 1.0 specific controller logic."""
@@ -144,6 +123,35 @@ class ControllerV10(Controller):
base_url = request.application_url
return images_view.ViewBuilderV10(base_url)
+ def index(self, req):
+ """Return an index listing of images available to the request.
+
+ :param req: `wsgi.Request` object
+
+ """
+ context = req.environ['nova.context']
+ filters = self._get_filters(req)
+ images = self._image_service.index(context, filters)
+ images = common.limited(images, req)
+ builder = self.get_builder(req).build
+ return dict(images=[builder(image, detail=False) for image in images])
+
+ def detail(self, req):
+ """Return a detailed index listing of images available to the request.
+
+ :param req: `wsgi.Request` object.
+
+ """
+ context = req.environ['nova.context']
+ filters = self._get_filters(req)
+ images = self._image_service.detail(context, filters)
+ images = common.limited(images, req)
+ builder = self.get_builder(req).build
+ return dict(images=[builder(image, detail=True) for image in images])
+
+ def _server_id_from_req_data(self, data):
+ return data['image']['serverId']
+
class ControllerV11(Controller):
"""Version 1.1 specific controller logic."""
@@ -153,6 +161,37 @@ class ControllerV11(Controller):
base_url = request.application_url
return images_view.ViewBuilderV11(base_url)
+ def index(self, req):
+ """Return an index listing of images available to the request.
+
+ :param req: `wsgi.Request` object
+
+ """
+ context = req.environ['nova.context']
+ filters = self._get_filters(req)
+ (marker, limit) = common.get_pagination_params(req)
+ images = self._image_service.index(
+ context, filters=filters, marker=marker, limit=limit)
+ builder = self.get_builder(req).build
+ return dict(images=[builder(image, detail=False) for image in images])
+
+ def detail(self, req):
+ """Return a detailed index listing of images available to the request.
+
+ :param req: `wsgi.Request` object.
+
+ """
+ context = req.environ['nova.context']
+ filters = self._get_filters(req)
+ (marker, limit) = common.get_pagination_params(req)
+ images = self._image_service.detail(
+ context, filters=filters, marker=marker, limit=limit)
+ builder = self.get_builder(req).build
+ return dict(images=[builder(image, detail=True) for image in images])
+
+ def _server_id_from_req_data(self, data):
+ return data['image']['serverRef']
+
def create_resource(version='1.0'):
controller = {
@@ -168,7 +207,7 @@ def create_resource(version='1.0'):
metadata = {
"attributes": {
"image": ["id", "name", "updated", "created", "status",
- "serverId", "progress"],
+ "serverId", "progress", "serverRef"],
"link": ["rel", "type", "href"],
},
}
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 82d8be4aa..9cf5e8721 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -153,6 +153,7 @@ class Controller(object):
msg = _("Server name is not defined")
return exc.HTTPBadRequest(msg)
+ zone_blob = body['server'].get('blob')
name = body['server']['name']
self._validate_server_name(name)
name = name.strip()
@@ -172,7 +173,8 @@ class Controller(object):
key_data=key_data,
metadata=body['server'].get('metadata', {}),
injected_files=injected_files,
- admin_password=password)
+ admin_password=password,
+ zone_blob=zone_blob)
except quota.QuotaError as error:
self._handle_quota_error(error)
except exception.ImageNotFound as error:
diff --git a/nova/api/openstack/versions.py b/nova/api/openstack/versions.py
index 9db160102..4c682302f 100644
--- a/nova/api/openstack/versions.py
+++ b/nova/api/openstack/versions.py
@@ -35,7 +35,7 @@ class Versions(wsgi.Resource):
'application/xml': wsgi.XMLDictSerializer(metadata=metadata),
}
- super(Versions, self).__init__(None, serializers=serializers)
+ wsgi.Resource.__init__(self, None, serializers=serializers)
def dispatch(self, request, *args):
"""Respond to a request for all OpenStack API versions."""
diff --git a/nova/api/openstack/views/limits.py b/nova/api/openstack/views/limits.py
index e21c9f2fd..934b4921a 100644
--- a/nova/api/openstack/views/limits.py
+++ b/nova/api/openstack/views/limits.py
@@ -29,9 +29,6 @@ class ViewBuilder(object):
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)
@@ -67,12 +64,6 @@ class ViewBuilder(object):
limits[name] = value
return limits
- def _build_rate_limits(self, rate_limits):
- raise NotImplementedError()
-
- def _build_rate_limit(self, rate_limit):
- raise NotImplementedError()
-
class ViewBuilderV10(ViewBuilder):
"""Openstack API v1.0 limits view builder."""
diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py
index 8061b3b67..b2f7898cb 100644
--- a/nova/api/openstack/zones.py
+++ b/nova/api/openstack/zones.py
@@ -27,9 +27,6 @@ from nova.scheduler import api
FLAGS = flags.FLAGS
-flags.DEFINE_string('build_plan_encryption_key',
- None,
- '128bit (hex) encryption key for scheduler build plans.')
LOG = logging.getLogger('nova.api.openstack.zones')
@@ -53,6 +50,14 @@ def _scrub_zone(zone):
'deleted', 'deleted_at', 'updated_at'))
+def check_encryption_key(func):
+ def wrapped(*args, **kwargs):
+ if not FLAGS.build_plan_encryption_key:
+ raise exception.Error(_("--build_plan_encryption_key not set"))
+ return func(*args, **kwargs)
+ return wrapped
+
+
class Controller(object):
def index(self, req):
@@ -103,19 +108,13 @@ class Controller(object):
zone = api.zone_update(context, zone_id, body["zone"])
return dict(zone=_scrub_zone(zone))
- def select(self, req):
+ @check_encryption_key
+ def select(self, req, body):
"""Returns a weighted list of costs to create instances
of desired capabilities."""
ctx = req.environ['nova.context']
- qs = req.environ['QUERY_STRING']
- param_dict = urlparse.parse_qs(qs)
- param_dict.pop("fresh", None)
- # parse_qs returns a dict where the values are lists,
- # since query strings can have multiple values for the
- # same key. We need to convert that to single values.
- for key in param_dict:
- param_dict[key] = param_dict[key][0]
- build_plan = api.select(ctx, specs=param_dict)
+ specs = json.loads(body)
+ build_plan = api.select(ctx, specs=specs)
cooked = self._scrub_build_plan(build_plan)
return {"weights": cooked}
@@ -123,9 +122,6 @@ class Controller(object):
"""Remove all the confidential data and return a sanitized
version of the build plan. Include an encrypted full version
of the weighting entry so we can get back to it later."""
- if not FLAGS.build_plan_encryption_key:
- raise exception.FlagNotSet(flag='build_plan_encryption_key')
-
encryptor = crypto.encryptor(FLAGS.build_plan_encryption_key)
cooked = []
for entry in build_plan: