added tests to ensure the easy api works as a backend for Compute API
This commit is contained in:
@@ -31,7 +31,6 @@ The general flow of a request is:
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
import urllib
|
||||
|
||||
import routes
|
||||
@@ -39,6 +38,7 @@ import webob
|
||||
|
||||
from nova import context
|
||||
from nova import flags
|
||||
from nova import utils
|
||||
from nova import wsgi
|
||||
|
||||
# prxy compute_api in amazon tests
|
||||
@@ -65,7 +65,7 @@ class JsonParamsMiddleware(wsgi.Middleware):
|
||||
return
|
||||
|
||||
params_json = request.params['json']
|
||||
params_parsed = json.loads(params_json)
|
||||
params_parsed = utils.loads(params_json)
|
||||
params = {}
|
||||
for k, v in params_parsed.iteritems():
|
||||
if k in ('self', 'context'):
|
||||
@@ -125,7 +125,7 @@ class ServiceWrapper(wsgi.Controller):
|
||||
method = getattr(self.service_handle, action)
|
||||
|
||||
result = method(context, **params)
|
||||
if type(result) is dict:
|
||||
if type(result) is dict or type(result) is list:
|
||||
return self._serialize(result, req)
|
||||
else:
|
||||
return result
|
||||
@@ -140,11 +140,11 @@ class Proxy(object):
|
||||
def __do_request(self, path, context, **kwargs):
|
||||
req = webob.Request.blank(path)
|
||||
req.method = 'POST'
|
||||
req.body = urllib.urlencode({'json': json.dumps(kwargs)})
|
||||
req.body = urllib.urlencode({'json': utils.dumps(kwargs)})
|
||||
req.environ['openstack.context'] = context
|
||||
resp = req.get_response(self.app)
|
||||
try:
|
||||
return json.loads(resp.body)
|
||||
return utils.loads(resp.body)
|
||||
except Exception:
|
||||
return resp.body
|
||||
|
||||
|
||||
@@ -118,7 +118,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_instances(context,
|
||||
project_id=project_id):
|
||||
if instance['fixed_ip']:
|
||||
line = '%s slots=%d' % (instance['fixed_ip']['address'],
|
||||
instance['vcpus'])
|
||||
@@ -442,7 +443,8 @@ class CloudController(object):
|
||||
# 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_ref = self.compute_api.get_instance(context,
|
||||
instance_id=internal_id)
|
||||
output = rpc.call(context,
|
||||
'%s.%s' % (FLAGS.compute_topic,
|
||||
instance_ref['host']),
|
||||
@@ -541,7 +543,8 @@ class CloudController(object):
|
||||
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)
|
||||
instance_ref = self.compute_api.get_instance(context,
|
||||
instance_id=internal_id)
|
||||
host = instance_ref['host']
|
||||
rpc.cast(context,
|
||||
db.queue_get_for(context, FLAGS.compute_topic, host),
|
||||
@@ -722,14 +725,15 @@ class CloudController(object):
|
||||
|
||||
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)
|
||||
instance_ref = self.compute_api.get_instance(context,
|
||||
instance_id=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)
|
||||
network_topic = self.compute_api.get_network_topic(
|
||||
context, instance_id=internal_id)
|
||||
rpc.cast(context,
|
||||
network_topic,
|
||||
{"method": "associate_floating_ip",
|
||||
@@ -754,8 +758,9 @@ class CloudController(object):
|
||||
def run_instances(self, context, **kwargs):
|
||||
max_count = int(kwargs.get('max_count', 1))
|
||||
instances = self.compute_api.create_instances(context,
|
||||
instance_types.get_by_type(kwargs.get('instance_type', None)),
|
||||
kwargs['image_id'],
|
||||
instance_type=instance_types.get_by_type(
|
||||
kwargs.get('instance_type', None)),
|
||||
image_id=kwargs['image_id'],
|
||||
min_count=int(kwargs.get('min_count', max_count)),
|
||||
max_count=max_count,
|
||||
kernel_id=kwargs.get('kernel_id', None),
|
||||
@@ -765,7 +770,7 @@ class CloudController(object):
|
||||
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)
|
||||
hostname_format='ec2')
|
||||
return self._format_run_instances(context,
|
||||
instances[0]['reservation_id'])
|
||||
|
||||
@@ -775,26 +780,26 @@ class CloudController(object):
|
||||
logging.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)
|
||||
self.compute_api.delete_instance(context, instance_id=internal_id)
|
||||
return True
|
||||
|
||||
def reboot_instances(self, context, instance_id, **kwargs):
|
||||
"""instance_id is a list of instance ids"""
|
||||
for ec2_id in instance_id:
|
||||
internal_id = ec2_id_to_internal_id(ec2_id)
|
||||
self.compute_api.reboot(context, internal_id)
|
||||
self.compute_api.reboot(context, instance_id=internal_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)
|
||||
self.compute_api.rescue(context, instance_id=internal_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)
|
||||
self.compute_api.unrescue(context, instance_id=internal_id)
|
||||
return True
|
||||
|
||||
def update_instance(self, context, ec2_id, **kwargs):
|
||||
@@ -805,7 +810,8 @@ class CloudController(object):
|
||||
changes[field] = kwargs[field]
|
||||
if changes:
|
||||
internal_id = ec2_id_to_internal_id(ec2_id)
|
||||
inst = self.compute_api.get_instance(context, internal_id)
|
||||
inst = self.compute_api.get_instance(context,
|
||||
instance_id=internal_id)
|
||||
db.instance_update(context, inst['id'], kwargs)
|
||||
return True
|
||||
|
||||
|
||||
@@ -36,10 +36,19 @@ from nova.db import base
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
def generate_default_hostname(internal_id):
|
||||
def id_to_default_hostname(internal_id):
|
||||
"""Default function to generate a hostname given an instance reference."""
|
||||
return str(internal_id)
|
||||
|
||||
def id_to_ec2_hostname(internal_id):
|
||||
digits = []
|
||||
while internal_id != 0:
|
||||
internal_id, remainder = divmod(internal_id, 36)
|
||||
digits.append('0123456789abcdefghijklmnopqrstuvwxyz'[remainder])
|
||||
return "i-%s" % ''.join(reversed(digits))
|
||||
|
||||
HOSTNAME_FORMATTERS = {'default': id_to_default_hostname,
|
||||
'ec2': id_to_ec2_hostname}
|
||||
|
||||
class ComputeAPI(base.Base):
|
||||
"""API for interacting with the compute manager."""
|
||||
@@ -75,7 +84,7 @@ class ComputeAPI(base.Base):
|
||||
display_name='', description='', key_name=None,
|
||||
key_data=None, security_group='default',
|
||||
user_data=None,
|
||||
generate_hostname=generate_default_hostname):
|
||||
hostname_format='default'):
|
||||
"""Create the number of instances requested if quote and
|
||||
other arguments check out ok."""
|
||||
|
||||
@@ -144,6 +153,7 @@ class ComputeAPI(base.Base):
|
||||
|
||||
elevated = context.elevated()
|
||||
instances = []
|
||||
generate_hostname = HOSTNAME_FORMATTERS[hostname_format]
|
||||
logging.debug(_("Going to run %s instances..."), num_instances)
|
||||
for num in range(num_instances):
|
||||
instance = dict(mac_address=utils.generate_mac(),
|
||||
@@ -177,7 +187,7 @@ class ComputeAPI(base.Base):
|
||||
"args": {"topic": FLAGS.compute_topic,
|
||||
"instance_id": instance_id}})
|
||||
|
||||
return instances
|
||||
return [dict(x.iteritems()) for x in instances]
|
||||
|
||||
def ensure_default_security_group(self, context):
|
||||
""" Create security group for the security context if it
|
||||
@@ -254,7 +264,8 @@ class ComputeAPI(base.Base):
|
||||
return self.db.instance_get_all(context)
|
||||
|
||||
def get_instance(self, context, instance_id):
|
||||
return self.db.instance_get_by_internal_id(context, instance_id)
|
||||
rv = self.db.instance_get_by_internal_id(context, instance_id)
|
||||
return dict(rv.iteritems())
|
||||
|
||||
def reboot(self, context, instance_id):
|
||||
"""Reboot the given instance."""
|
||||
|
||||
@@ -22,6 +22,7 @@ import logging
|
||||
from M2Crypto import BIO
|
||||
from M2Crypto import RSA
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
@@ -293,6 +294,7 @@ class CloudTestCase(test.TestCase):
|
||||
self.assertEqual('Foo Img', img.metadata['description'])
|
||||
self._fake_set_image_description(self.context, 'ami-testing', '')
|
||||
self.assertEqual('', img.metadata['description'])
|
||||
shutil.rmtree(pathdir)
|
||||
|
||||
def test_update_of_instance_display_fields(self):
|
||||
inst = db.instance_create(self.context, {})
|
||||
|
||||
@@ -75,7 +75,7 @@ class ComputeTestCase(test.TestCase):
|
||||
ref = self.compute_api.create_instances(self.context,
|
||||
FLAGS.default_instance_type, None, **instance)
|
||||
try:
|
||||
self.assertNotEqual(ref[0].display_name, None)
|
||||
self.assertNotEqual(ref[0]['display_name'], None)
|
||||
finally:
|
||||
db.instance_destroy(self.context, ref[0]['id'])
|
||||
|
||||
@@ -87,9 +87,12 @@ class ComputeTestCase(test.TestCase):
|
||||
'project_id': self.project.id}
|
||||
group = db.security_group_create(self.context, values)
|
||||
ref = self.compute_api.create_instances(self.context,
|
||||
FLAGS.default_instance_type, None, security_group=['default'])
|
||||
instance_type=FLAGS.default_instance_type,
|
||||
image_id=None,
|
||||
security_group=['default'])
|
||||
try:
|
||||
self.assertEqual(len(ref[0]['security_groups']), 1)
|
||||
self.assertEqual(len(db.security_group_get_by_instance(
|
||||
self.context, ref[0]['id'])), 1)
|
||||
finally:
|
||||
db.security_group_destroy(self.context, group['id'])
|
||||
db.instance_destroy(self.context, ref[0]['id'])
|
||||
|
||||
@@ -28,7 +28,8 @@ from nova import exception
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.api import easy
|
||||
|
||||
from nova.compute import api as compute_api
|
||||
from nova.tests import cloud_unittest
|
||||
|
||||
class FakeService(object):
|
||||
def echo(self, context, data):
|
||||
@@ -83,3 +84,19 @@ class EasyTestCase(test.TestCase):
|
||||
proxy = easy.Proxy(self.router)
|
||||
rv = proxy.fake.echo(self.context, data='baz')
|
||||
self.assertEqual(rv['data'], 'baz')
|
||||
|
||||
|
||||
class EasyCloudTestCase(cloud_unittest.CloudTestCase):
|
||||
def setUp(self):
|
||||
super(EasyCloudTestCase, self).setUp()
|
||||
compute_handle = compute_api.ComputeAPI(self.cloud.network_manager,
|
||||
self.cloud.image_service)
|
||||
easy.register_service('compute', compute_handle)
|
||||
self.router = easy.JsonParamsMiddleware(easy.SundayMorning())
|
||||
proxy = easy.Proxy(self.router)
|
||||
self.cloud.compute_api = proxy.compute
|
||||
|
||||
def tearDown(self):
|
||||
super(EasyCloudTestCase, self).tearDown()
|
||||
easy.EASY_ROUTES = {}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ System-level utilities and helper functions.
|
||||
|
||||
import datetime
|
||||
import inspect
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
@@ -361,3 +362,33 @@ def utf8(value):
|
||||
return value.encode("utf-8")
|
||||
assert isinstance(value, str)
|
||||
return value
|
||||
|
||||
|
||||
def to_primitive(value):
|
||||
if type(value) is type([]) or type(value) is type((None,)):
|
||||
o = []
|
||||
for v in value:
|
||||
o.append(to_primitive(v))
|
||||
return o
|
||||
elif type(value) is type({}):
|
||||
o = {}
|
||||
for k, v in value.iteritems():
|
||||
o[k] = to_primitive(v)
|
||||
return o
|
||||
elif isinstance(value, datetime.datetime):
|
||||
return str(value)
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
def dumps(value):
|
||||
try:
|
||||
return json.dumps(value)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
return json.dumps(to_primitive(value))
|
||||
|
||||
|
||||
def loads(s):
|
||||
return json.loads(s)
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
Utility methods for working with WSGI servers
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
from xml.dom import minidom
|
||||
@@ -35,6 +34,7 @@ import webob
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from nova import utils
|
||||
|
||||
logging.getLogger("routes.middleware").addHandler(logging.StreamHandler())
|
||||
|
||||
@@ -322,7 +322,7 @@ class Serializer(object):
|
||||
try:
|
||||
is_xml = (datastring[0] == '<')
|
||||
if not is_xml:
|
||||
return json.loads(datastring)
|
||||
return utils.loads(datastring)
|
||||
return self._from_xml(datastring)
|
||||
except:
|
||||
return None
|
||||
@@ -355,7 +355,7 @@ class Serializer(object):
|
||||
return result
|
||||
|
||||
def _to_json(self, data):
|
||||
return json.dumps(data)
|
||||
return utils.dumps(data)
|
||||
|
||||
def _to_xml(self, data):
|
||||
metadata = self.metadata.get('application/xml', {})
|
||||
|
||||
Reference in New Issue
Block a user