diff options
author | Cerberus <matt.dietz@rackspace.com> | 2010-09-29 16:21:28 -0500 |
---|---|---|
committer | Cerberus <matt.dietz@rackspace.com> | 2010-09-29 16:21:28 -0500 |
commit | 128ec65cf39e74b53903dd9788a58c8eb513abe8 (patch) | |
tree | 9c3323c87d41d5e5e631d097e4a3235d1df591d2 | |
parent | 9c4319a83a6e7d61ffa6b78e9f17ea35821c5526 (diff) | |
download | nova-128ec65cf39e74b53903dd9788a58c8eb513abe8.tar.gz nova-128ec65cf39e74b53903dd9788a58c8eb513abe8.tar.xz nova-128ec65cf39e74b53903dd9788a58c8eb513abe8.zip |
Server creation up to, but not including, network configuration
-rw-r--r-- | nova/api/rackspace/servers.py | 97 | ||||
-rw-r--r-- | nova/db/sqlalchemy/models.py | 1 | ||||
-rw-r--r-- | nova/tests/api/rackspace/auth.py | 8 | ||||
-rw-r--r-- | nova/tests/api/rackspace/servers.py | 48 | ||||
-rw-r--r-- | nova/tests/api/rackspace/test_helper.py | 17 | ||||
-rw-r--r-- | nova/wsgi.py | 11 |
6 files changed, 123 insertions, 59 deletions
diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index aa955c222..cc971adc0 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -25,6 +25,7 @@ from nova import rpc from nova import utils from nova import wsgi from nova.api.rackspace import _id_translator +from nova.compute import instance_types from nova.compute import power_state from nova.wsgi import Serializer import nova.image.service @@ -39,11 +40,11 @@ def _instance_id_translator(): ids """ return _id_translator.RackspaceAPIIdTranslator( "instance", 'nova') -def _image_id_translator(): +def _image_service(): """ Helper method for initializing the image id translator """ service = nova.image.service.ImageService.load() - return _id_translator.RackspaceAPIIdTranslator( - "image", service.__class__.__name__) + return (service, _id_translator.RackspaceAPIIdTranslator( + "image", service.__class__.__name__)) def _filter_params(inst_dict): """ Extracts all updatable parameters for a server update request """ @@ -122,8 +123,11 @@ class Controller(wsgi.Controller): def show(self, req, id): """ Returns server details by server id """ + inst_id_trans = _instance_id_translator() + inst_id = inst_id_trans.from_rs_id(id) + user_id = req.environ['nova.context']['user']['id'] - inst = self.db_driver.instance_get(None, id) + inst = self.db_driver.instance_get_by_ec2_id(None, inst_id) if inst: if inst.user_id == user_id: return _entity_detail(inst) @@ -131,8 +135,11 @@ class Controller(wsgi.Controller): def delete(self, req, id): """ Destroys a server """ + inst_id_trans = _instance_id_translator() + inst_id = inst_id_trans.from_rs_id(id) + user_id = req.environ['nova.context']['user']['id'] - instance = self.db_driver.instance_get(None, id) + instance = self.db_driver.instance_get_by_ec2_id(None, inst_id) if instance and instance['user_id'] == user_id: self.db_driver.instance_destroy(None, id) return exc.HTTPAccepted() @@ -140,10 +147,16 @@ class Controller(wsgi.Controller): def create(self, req): """ Creates a new server for a given user """ - if not req.environ.has_key('inst_dict'): + + env = self._deserialize(req.body, req) + if not env: return exc.HTTPUnprocessableEntity() - inst = self._build_server_instance(req) + #try: + inst = self._build_server_instance(req, env) + #except Exception, e: + # print e + # return exc.HTTPUnprocessableEntity() rpc.cast( FLAGS.compute_topic, { @@ -153,6 +166,8 @@ class Controller(wsgi.Controller): def update(self, req, id): """ Updates the server name or password """ + inst_id_trans = _instance_id_translator() + inst_id = inst_id_trans.from_rs_id(id) user_id = req.environ['nova.context']['user']['id'] inst_dict = self._deserialize(req.body, req) @@ -160,7 +175,7 @@ class Controller(wsgi.Controller): if not inst_dict: return exc.HTTPUnprocessableEntity() - instance = self.db_driver.instance_get(None, id) + instance = self.db_driver.instance_get_by_ec2_id(None, inst_id) if not instance or instance.user_id != user_id: return exc.HTTPNotFound() @@ -174,56 +189,68 @@ class Controller(wsgi.Controller): if not req.environ.has_key('inst_dict'): return exc.HTTPUnprocessableEntity() - def _build_server_instance(self, req): + def _build_server_instance(self, req, env): """Build instance data structure and save it to the data store.""" ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) inst = {} inst_id_trans = _instance_id_translator() - image_id_trans = _image_id_translator() - env = req.environ['inst_dict'] user_id = req.environ['nova.context']['user']['id'] - inst['rs_id'] = inst_id_trans.to_rs_id() + instance_type, flavor = None, None + for k, v in instance_types.INSTANCE_TYPES.iteritems(): + if v['flavorid'] == env['server']['flavorId']: + instance_type, flavor = k, v + break + + if not flavor: + raise Exception, "Flavor not found" + image_id = env['server']['imageId'] - opaque_id = translator_instance().from_rs_id(image_id) + img_service, image_id_trans = _image_service() + + opaque_image_id = image_id_trans.to_rs_id(image_id) + image = img_service.show(opaque_image_id) - inst['name'] = env['server']['name'] - inst['image_id'] = opaque_id - inst['instance_type'] = env['server']['flavorId'] + if not image: + raise Exception, "Image not found" + + inst['server_name'] = env['server']['name'] + inst['image_id'] = opaque_image_id inst['user_id'] = user_id inst['launch_time'] = ltime inst['mac_address'] = utils.generate_mac() + inst['project_id'] = user_id - #TODO(dietz) These are the attributes I'm unsure of inst['state_description'] = 'scheduling' - inst['kernel_id'] = '' - inst['ramdisk_id'] = '' + inst['kernel_id'] = image.get('kernelId', FLAGS.default_kernel) + inst['ramdisk_id'] = image.get('ramdiskId', FLAGS.default_ramdisk) inst['reservation_id'] = utils.generate_uid('r') - inst['key_data'] = '' - inst['key_name'] = '' - inst['security_group'] = '' - # Flavor related attributes - inst['instance_type'] = '' - inst['memory_mb'] = '' - inst['vcpus'] = '' - inst['local_gb'] = '' + #TODO(dietz) this may be ill advised + key_pair_ref = self.db_driver.key_pair_get_all_by_user( + None, user_id)[0] - + inst['key_data'] = key_pair_ref['public_key'] + inst['key_name'] = key_pair_ref['name'] - #TODO(dietz): This seems necessary. How do these apply across - #the Rackspace implementation? - inst['project_id'] = '' + #TODO(dietz) stolen from ec2 api, see TODO there + inst['security_group'] = 'default' - self.network_manager = utils.import_object(FLAGS.rs_network_manager) - - address = self.network_manager.allocate_fixed_ip( None, inst['id']) + # Flavor related attributes + inst['instance_type'] = instance_type + inst['memory_mb'] = flavor['memory_mb'] + inst['vcpus'] = flavor['vcpus'] + inst['local_gb'] = flavor['local_gb'] ref = self.db_driver.instance_create(None, inst) - inst['id'] = ref.id + inst['id'] = inst_id_trans.to_rs_id(ref.ec2_id) + + #self.network_manager = utils.import_object(FLAGS.rs_network_manager) + # + #address = self.network_manager.allocate_fixed_ip( None, inst['id']) return inst diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 2a314b037..22446fc64 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -199,7 +199,6 @@ class Instance(BASE, NovaBase): id = Column(Integer, primary_key=True) ec2_id = Column(String(10), unique=True) - rs_id = Column(String(255)) admin_pass = Column(String(255)) user_id = Column(String(255)) diff --git a/nova/tests/api/rackspace/auth.py b/nova/tests/api/rackspace/auth.py index a6e10970f..56677c2f4 100644 --- a/nova/tests/api/rackspace/auth.py +++ b/nova/tests/api/rackspace/auth.py @@ -1,12 +1,14 @@ -import webob -import webob.dec +import datetime import unittest + import stubout +import webob +import webob.dec + import nova.api import nova.api.rackspace.auth from nova import auth from nova.tests.api.rackspace import test_helper -import datetime class Test(unittest.TestCase): def setUp(self): diff --git a/nova/tests/api/rackspace/servers.py b/nova/tests/api/rackspace/servers.py index e08c9c6fd..d759eba3c 100644 --- a/nova/tests/api/rackspace/servers.py +++ b/nova/tests/api/rackspace/servers.py @@ -26,6 +26,7 @@ import nova.api.rackspace from nova.api.rackspace import servers import nova.db.api from nova.db.sqlalchemy.models import Instance +import nova.rpc from nova.tests.api.test_helper import * from nova.tests.api.rackspace import test_helper @@ -52,8 +53,11 @@ class ServersTest(unittest.TestCase): test_helper.stub_for_testing(self.stubs) test_helper.stub_out_rate_limiting(self.stubs) test_helper.stub_out_auth(self.stubs) + test_helper.stub_out_id_translator(self.stubs) + test_helper.stub_out_key_pair_funcs(self.stubs) + test_helper.stub_out_image_service(self.stubs) self.stubs.Set(nova.db.api, 'instance_get_all', return_servers) - self.stubs.Set(nova.db.api, 'instance_get', return_server) + self.stubs.Set(nova.db.api, 'instance_get_by_ec2_id', return_server) self.stubs.Set(nova.db.api, 'instance_get_all_by_user', return_servers) @@ -67,9 +71,6 @@ class ServersTest(unittest.TestCase): self.assertEqual(res_dict['server']['id'], '1') self.assertEqual(res_dict['server']['name'], 'server1') - def test_get_backup_schedule(self): - pass - def test_get_server_list(self): req = webob.Request.blank('/v1.0/servers') res = req.get_response(nova.api.API()) @@ -82,19 +83,36 @@ class ServersTest(unittest.TestCase): self.assertEqual(s.get('imageId', None), None) i += 1 - #def test_create_instance(self): - # test_helper.stub_out_image_translator(self.stubs) - # body = dict(server=dict( - # name='server_test', imageId=2, flavorId=2, metadata={}, - # personality = {} - # )) - # req = webob.Request.blank('/v1.0/servers') - # req.method = 'POST' - # req.body = json.dumps(body) + def test_create_instance(self): + def instance_create(context, inst): + class Foo(object): + ec2_id = 1 + return Foo() - # res = req.get_response(nova.api.API()) + def fake_cast(*args, **kwargs): + pass - # print res + self.stubs.Set(nova.db.api, 'instance_create', instance_create) + self.stubs.Set(nova.rpc, 'cast', fake_cast) + + test_helper.stub_out_id_translator(self.stubs) + body = dict(server=dict( + name='server_test', imageId=2, flavorId=2, metadata={}, + personality = {} + )) + req = webob.Request.blank('/v1.0/servers') + req.method = 'POST' + req.body = json.dumps(body) + + res = req.get_response(nova.api.API()) + + self.assertEqual(res.status_int, 200) + + def test_update_no_body(self): + req = webob.Request.blank('/v1.0/servers/1') + req.method = 'PUT' + res = req.get_response(nova.api.API()) + self.assertEqual(res.status_int, 422) def test_update_bad_params(self): """ Confirm that update is filtering params """ diff --git a/nova/tests/api/rackspace/test_helper.py b/nova/tests/api/rackspace/test_helper.py index aa7fb382c..962f0ba4c 100644 --- a/nova/tests/api/rackspace/test_helper.py +++ b/nova/tests/api/rackspace/test_helper.py @@ -9,6 +9,7 @@ from nova import utils from nova import flags import nova.api.rackspace.auth import nova.api.rackspace._id_translator +from nova.image import service from nova.wsgi import Router FLAGS = flags.FLAGS @@ -40,7 +41,21 @@ def fake_wsgi(self, req): req.environ['inst_dict'] = json.loads(req.body) return self.application -def stub_out_image_translator(stubs): + + +def stub_out_key_pair_funcs(stubs): + def key_pair(context, user_id): + return [dict(name='key', public_key='public_key')] + stubs.Set(nova.db.api, 'key_pair_get_all_by_user', + key_pair) + +def stub_out_image_service(stubs): + def fake_image_show(meh, id): + return dict(kernelId=1, ramdiskId=1) + + stubs.Set(nova.image.service.LocalImageService, 'show', fake_image_show) + +def stub_out_id_translator(stubs): class FakeTranslator(object): def __init__(self, id_type, service_name): pass diff --git a/nova/wsgi.py b/nova/wsgi.py index 48c4dabc2..b91d91121 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -281,10 +281,13 @@ class Serializer(object): The string must be in the format of a supported MIME type. """ datastring = datastring.strip() - is_xml = (datastring[0] == '<') - if not is_xml: - return json.loads(datastring) - return self._from_xml(datastring) + try: + is_xml = (datastring[0] == '<') + if not is_xml: + return json.loads(datastring) + return self._from_xml(datastring) + except: + return None def _from_xml(self, datastring): xmldata = self.metadata.get('application/xml', {}) |