First pass at feature parity. Includes Image ID hash

This commit is contained in:
Sandy Walsh
2011-01-03 15:46:55 -08:00
8 changed files with 87 additions and 9 deletions

View File

@@ -51,6 +51,10 @@ flags.DEFINE_string('os_api_ratelimiting',
'nova.api.openstack.ratelimiting.RateLimitingMiddleware',
'Default ratelimiting implementation for the Openstack API')
flags.DEFINE_string('os_krm_mapping_file',
'krm_mapping.json',
'Location of OpenStack Flavor/OS:EC2 Kernel/Ramdisk/Machine JSON file.')
flags.DEFINE_bool('allow_admin_api',
False,
'When True, this API service will accept admin operations.')
@@ -109,7 +113,7 @@ class APIRouter(wsgi.Router):
collection={'detail': 'GET'})
mapper.resource("flavor", "flavors", controller=flavors.Controller(),
collection={'detail': 'GET'})
mapper.resource("sharedipgroup", "sharedipgroups",
mapper.resource("shared_ip_group", "shared_ip_groups",
controller=sharedipgroups.Controller())
super(APIRouter, self).__init__(mapper)

View File

@@ -15,7 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
import logging
import time
from webob import exc
from nova import wsgi
@@ -46,8 +48,8 @@ class Controller(wsgi.Controller):
def create(self, req, server_id):
""" No actual update method required, since the existing API allows
both create and update through a POST """
return faults.Fault(exc.HTTPNotFound())
return faults.Fault(exc.HTTPNotImplemented())
def delete(self, req, server_id, id):
""" Deletes an existing backup schedule """
return faults.Fault(exc.HTTPNotFound())
return faults.Fault(exc.HTTPNotImplemented())

View File

@@ -15,6 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from nova import exception
def limited(items, req):
"""Return a slice of items according to requested offset and limit.
@@ -34,3 +36,25 @@ def limited(items, req):
limit = min(1000, limit)
range_end = offset + limit
return items[offset:range_end]
def get_image_id_from_image_hash(image_service, context, image_hash):
"""Given an Image ID Hash, return an objectstore Image ID.
image_service - reference to objectstore compatible image service.
context - security context for image service requests.
image_hash - hash of the image ID.
"""
# FIX(sandy): This is terribly inefficient. It pulls all images
# from objectstore in order to find the match. ObjectStore
# should have a numeric counterpart to the string ID.
try:
items = image_service.detail(context)
except NotImplementedError:
items = image_service.index(context)
for image in items:
image_id = image['imageId']
if abs(hash(image_id)) == int(image_hash):
return image_id
raise exception.NotFound(image_hash)

View File

@@ -15,6 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import logging
from webob import exc
from nova import flags
@@ -27,6 +29,7 @@ from nova.api.openstack import common
from nova.api.openstack import faults
from nova.compute import api as compute_api
FLAGS = flags.FLAGS
@@ -89,6 +92,12 @@ def _filter_keys(item, keys):
return dict((k, v) for k, v in item.iteritems() if k in keys)
def _convert_image_id_to_hash(image):
image_id = abs(hash(image['imageId']))
image['imageId'] = image_id
image['id'] = image_id
class Controller(wsgi.Controller):
_serialization_metadata = {
@@ -113,6 +122,9 @@ class Controller(wsgi.Controller):
items = self._service.detail(req.environ['nova.context'])
except NotImplementedError:
items = self._service.index(req.environ['nova.context'])
for image in items:
_convert_image_id_to_hash(image)
items = common.limited(items, req)
items = [_translate_keys(item) for item in items]
items = [_translate_status(item) for item in items]
@@ -120,7 +132,12 @@ class Controller(wsgi.Controller):
def show(self, req, id):
"""Return data about the given image id"""
return dict(image=self._service.show(req.environ['nova.context'], id))
image_id = common.get_image_id_from_image_hash(self._service,
req.environ['nova.context'], id)
image = self._service.show(req.environ['nova.context'], image_id)
_convert_image_id_to_hash(image)
return dict(image=image)
def delete(self, req, id):
# Only public images are supported for now.

View File

@@ -15,13 +15,16 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
import logging
import traceback
from webob import exc
from nova import exception
from nova import flags
from nova import wsgi
from nova import utils
from nova.api.openstack import common
from nova.api.openstack import faults
from nova.auth import manager as auth_manager
@@ -35,6 +38,9 @@ LOG = logging.getLogger('server')
LOG.setLevel(logging.DEBUG)
FLAGS = flags.FLAGS
def _translate_detail_keys(inst):
""" Coerces into dictionary format, mapping everything to Rackspace-like
attributes for return"""
@@ -81,6 +87,7 @@ class Controller(wsgi.Controller):
def __init__(self):
self.compute_api = compute_api.ComputeAPI()
self._image_service = utils.import_object(FLAGS.image_service)
super(Controller, self).__init__()
def index(self, req):
@@ -120,6 +127,18 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
def _get_kernel_ramdisk_from_image(self, image_id):
mapping_filename = FLAGS.os_krm_mapping_file
with open(mapping_filename) as f:
mapping = json.load(f)
if mapping.has_key(image_id):
return mapping[image_id]
raise exception.NotFound(
_("No entry for image '%s' in mapping file '%s'") %
(image_id, mapping_filename))
def create(self, req):
""" Creates a new server for a given user """
env = self._deserialize(req.body, req)
@@ -128,10 +147,15 @@ class Controller(wsgi.Controller):
key_pair = auth_manager.AuthManager.get_key_pairs(
req.environ['nova.context'])[0]
image_id = common.get_image_id_from_image_hash(self._image_service,
req.environ['nova.context'], env['server']['imageId'])
kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image(image_id)
instances = self.compute_api.create_instances(
req.environ['nova.context'],
instance_types.get_by_flavor_id(env['server']['flavorId']),
env['server']['imageId'],
image_id,
kernel_id = kernel_id,
ramdisk_id = ramdisk_id,
display_name=env['server']['name'],
description=env['server']['name'],
key_name=key_pair['name'],
@@ -163,6 +187,7 @@ class Controller(wsgi.Controller):
""" Multi-purpose method used to reboot, rebuild, and
resize a server """
input_dict = self._deserialize(req.body, req)
#TODO(sandy): rebuild/resize not supported.
try:
reboot_type = input_dict['reboot']['type']
except Exception:

View File

@@ -15,6 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import logging
from webob import exc
from nova import wsgi
@@ -29,7 +31,7 @@ def _translate_keys(inst):
def _translate_detail_keys(inst):
""" Coerces a shared IP group instance into proper dictionary format with
correctly mapped attributes """
return dict(sharedIpGroup=inst)
return dict(sharedIpGroups=inst)
class Controller(wsgi.Controller):
@@ -46,6 +48,8 @@ class Controller(wsgi.Controller):
def show(self, req, id):
""" Shows in-depth information on a specific Shared IP Group """
if id == 'detail':
return _translate_detail_keys({})
return _translate_keys({})
def update(self, req, id):
@@ -54,7 +58,7 @@ class Controller(wsgi.Controller):
def delete(self, req, id):
""" Deletes a Shared IP Group """
raise faults.Fault(exc.HTTPNotFound())
raise faults.Fault(exc.HTTPNotImplemented())
def detail(self, req, id):
""" Returns a complete list of Shared IP Groups """
@@ -62,4 +66,4 @@ class Controller(wsgi.Controller):
def create(self, req):
""" Creates a new Shared IP group """
raise faults.Fault(exc.HTTPNotFound())
raise faults.Fault(exc.HTTPNotImplemented())

View File

@@ -102,6 +102,7 @@ class ComputeAPI(base.Base):
ramdisk_id = None
logging.debug("Creating a raw instance")
# Make sure we have access to kernel and ramdisk (if not raw)
logging.debug("Using Kernel=%s, Ramdisk=%s" % (kernel_id, ramdisk_id))
if kernel_id:
self.image_service.show(context, kernel_id)
if ramdisk_id:

View File

@@ -360,7 +360,8 @@ class VMHelper(HelperBase):
if i >= 3 and i <= 11:
ref = node.childNodes
# Name and Value
diags[ref[0].firstChild.data] = ref[6].firstChild.data
if len(ref) > 6:
diags[ref[0].firstChild.data] = ref[6].firstChild.data
return diags
except cls.XenAPI.Failure as e:
return {"Unable to retrieve diagnostics": e}