First pass at feature parity. Includes Image ID hash
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user