diff options
| author | vladimir.p <vladimir@zadarastorage.com> | 2011-08-24 09:20:54 -0700 |
|---|---|---|
| committer | vladimir.p <vladimir@zadarastorage.com> | 2011-08-24 09:20:54 -0700 |
| commit | 163923f57d075fa5d46a5b54073fc6cd30e5c624 (patch) | |
| tree | da035edd4328ad2ea3aa313392fbb51204eaf9f3 | |
| parent | 8ad9373648dea11783545ad88429def4691a2925 (diff) | |
| parent | af5c4805368a59a757c657193b9582bb2bf0547d (diff) | |
merged with 1487
| -rw-r--r-- | nova/api/openstack/servers.py | 29 | ||||
| -rw-r--r-- | nova/compute/api.py | 5 | ||||
| -rw-r--r-- | nova/compute/manager.py | 6 | ||||
| -rw-r--r-- | nova/ipv6/rfc2462.py | 4 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_server_actions.py | 141 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_servers.py | 135 | ||||
| -rw-r--r-- | nova/tests/test_ipv6.py | 19 |
7 files changed, 297 insertions, 42 deletions
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 553357404..27c67e79e 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -604,8 +604,10 @@ class ControllerV10(Controller): LOG.debug(msg) raise exc.HTTPBadRequest(explanation=msg) + password = utils.generate_password(16) + try: - self.compute_api.rebuild(context, instance_id, image_id) + self.compute_api.rebuild(context, instance_id, image_id, password) except exception.BuildInProgress: msg = _("Instance %s is currently being rebuilt.") % instance_id LOG.debug(msg) @@ -741,15 +743,26 @@ class ControllerV11(Controller): self._validate_metadata(metadata) self._decode_personalities(personalities) + password = info["rebuild"].get("adminPass", + utils.generate_password(16)) + try: - self.compute_api.rebuild(context, instance_id, image_href, name, - metadata, personalities) + self.compute_api.rebuild(context, instance_id, image_href, + password, name=name, metadata=metadata, + files_to_inject=personalities) except exception.BuildInProgress: msg = _("Instance %s is currently being rebuilt.") % instance_id LOG.debug(msg) raise exc.HTTPConflict(explanation=msg) + except exception.InstanceNotFound: + msg = _("Instance %s could not be found") % instance_id + raise exc.HTTPNotFound(explanation=msg) - return webob.Response(status_int=202) + instance = self.compute_api.routing_get(context, instance_id) + view = self._build_view(request, instance, is_detail=True) + view['server']['adminPass'] = password + + return view @common.check_snapshots_enabled def _action_create_image(self, input_dict, req, instance_id): @@ -816,6 +829,9 @@ class HeadersSerializer(wsgi.ResponseHeadersSerializer): def delete(self, response, data): response.status_int = 204 + def action(self, response, data): + response.status_int = 202 + class ServerXMLSerializer(wsgi.XMLDictSerializer): @@ -937,6 +953,11 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer): node.setAttribute('adminPass', server_dict['server']['adminPass']) return self.to_xml_string(node, True) + def action(self, server_dict): + #NOTE(bcwaldon): We need a way to serialize actions individually. This + # assumes all actions return a server entity + return self.create(server_dict) + def update(self, server_dict): xml_doc = minidom.Document() node = self._server_to_xml_detailed(xml_doc, diff --git a/nova/compute/api.py b/nova/compute/api.py index 69f76bf40..06f09f9a6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -1023,8 +1023,8 @@ class API(base.Base): self._cast_compute_message('reboot_instance', context, instance_id) @scheduler_api.reroute_compute("rebuild") - def rebuild(self, context, instance_id, image_href, name=None, - metadata=None, files_to_inject=None): + def rebuild(self, context, instance_id, image_href, admin_password, + name=None, metadata=None, files_to_inject=None): """Rebuild the given instance with the provided metadata.""" instance = db.api.instance_get(context, instance_id) @@ -1044,6 +1044,7 @@ class API(base.Base): self.db.instance_update(context, instance_id, values) rebuild_params = { + "new_pass": admin_password, "image_ref": image_href, "injected_files": files_to_inject, } diff --git a/nova/compute/manager.py b/nova/compute/manager.py index c207eccbb..ade15e310 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -527,6 +527,7 @@ class ComputeManager(manager.SchedulerDependentManager): :param context: `nova.RequestContext` object :param instance_id: Instance identifier (integer) :param image_ref: Image identifier (href or integer) + :param new_pass: password to set on rebuilt instance """ context = context.elevated() @@ -544,6 +545,11 @@ class ComputeManager(manager.SchedulerDependentManager): network_info = self.network_api.get_instance_nw_info(context, instance_ref) bd_mapping = self._setup_block_device_mapping(context, instance_id) + + # pull in new password here since the original password isn't in the db + instance_ref.admin_pass = kwargs.get('new_pass', + utils.generate_password(FLAGS.password_length)) + self.driver.spawn(context, instance_ref, network_info, bd_mapping) self._update_image_ref(context, instance_id, image_ref) diff --git a/nova/ipv6/rfc2462.py b/nova/ipv6/rfc2462.py index 0074efe98..acf42d201 100644 --- a/nova/ipv6/rfc2462.py +++ b/nova/ipv6/rfc2462.py @@ -30,8 +30,10 @@ def to_global(prefix, mac, project_id): maskIP = netaddr.IPNetwork(prefix).ip return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).\ format() - except TypeError: + except netaddr.AddrFormatError: raise TypeError(_('Bad mac for to_global_ipv6: %s') % mac) + except TypeError: + raise TypeError(_('Bad prefix for to_global_ipv6: %s') % prefix) def to_mac(ipv6_address): diff --git a/nova/tests/api/openstack/test_server_actions.py b/nova/tests/api/openstack/test_server_actions.py index bdd6824e7..3dfdeb79c 100644 --- a/nova/tests/api/openstack/test_server_actions.py +++ b/nova/tests/api/openstack/test_server_actions.py @@ -1,4 +1,5 @@ import base64 +import datetime import json import stubout @@ -6,8 +7,10 @@ import webob from nova import context from nova import utils +from nova import exception from nova import flags from nova.api.openstack import create_instance_helper +from nova.compute import instance_types from nova.compute import power_state import nova.db.api from nova import test @@ -19,61 +22,58 @@ FLAGS = flags.FLAGS def return_server_by_id(context, id): - return _get_instance() + return stub_instance(id) def instance_update(context, instance_id, kwargs): - return _get_instance() + return stub_instance(instance_id) -def return_server_with_power_state(power_state): +def return_server_with_attributes(**kwargs): def _return_server(context, id): - instance = _get_instance() - instance['state'] = power_state - return instance + return stub_instance(id, **kwargs) return _return_server +def return_server_with_power_state(power_state): + return return_server_with_attributes(power_state=power_state) + + def return_server_with_uuid_and_power_state(power_state): - def _return_server(context, id): - return return_server_with_power_state(power_state) - return _return_server + return return_server_with_power_state(power_state) -class MockSetAdminPassword(object): - def __init__(self): - self.instance_id = None - self.password = None +def stub_instance(id, power_state=0, metadata=None, + image_ref="10", flavor_id="1", name=None): - def __call__(self, context, instance_id, password): - self.instance_id = instance_id - self.password = password + if metadata is not None: + metadata_items = [{'key':k, 'value':v} for k, v in metadata.items()] + else: + metadata_items = [{'key':'seq', 'value':id}] + inst_type = instance_types.get_instance_type_by_flavor_id(int(flavor_id)) -def _get_instance(): instance = { - "id": 1, - "created_at": "2010-10-10 12:00:00", - "updated_at": "2010-11-11 11:00:00", + "id": int(id), + "created_at": datetime.datetime(2010, 10, 10, 12, 0, 0), + "updated_at": datetime.datetime(2010, 11, 11, 11, 0, 0), "admin_pass": "", - "user_id": "", - "project_id": "", - "image_ref": "5", + "user_id": "fake", + "project_id": "fake", + "image_ref": image_ref, "kernel_id": "", "ramdisk_id": "", "launch_index": 0, "key_name": "", "key_data": "", - "state": 0, + "state": power_state, "state_description": "", "memory_mb": 0, "vcpus": 0, "local_gb": 0, "hostname": "", "host": "", - "instance_type": { - "flavorid": 1, - }, + "instance_type": dict(inst_type), "user_data": "", "reservation_id": "", "mac_address": "", @@ -81,17 +81,34 @@ def _get_instance(): "launched_at": utils.utcnow(), "terminated_at": utils.utcnow(), "availability_zone": "", - "display_name": "test_server", + "display_name": name or "server%s" % id, "display_description": "", "locked": False, - "metadata": [], - #"address": , - #"floating_ips": [{"address":ip} for ip in public_addresses]} - "uuid": "deadbeef-feed-edee-beef-d0ea7beefedd"} + "metadata": metadata_items, + "access_ip_v4": "", + "access_ip_v6": "", + "uuid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + "virtual_interfaces": [], + } + + instance["fixed_ips"] = { + "address": '192.168.0.1', + "floating_ips": [], + } return instance +class MockSetAdminPassword(object): + def __init__(self): + self.instance_id = None + self.password = None + + def __call__(self, context, instance_id, password): + self.instance_id = instance_id + self.password = password + + class ServerActionsTest(test.TestCase): def setUp(self): @@ -598,6 +615,9 @@ class ServerActionsTestV11(test.TestCase): self.assertEqual(res.status_int, 400) def test_server_rebuild_accepted_minimum(self): + new_return_server = return_server_with_attributes(image_ref='2') + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + body = { "rebuild": { "imageRef": "http://localhost/images/2", @@ -611,6 +631,9 @@ class ServerActionsTestV11(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) + body = json.loads(res.body) + self.assertEqual(body['server']['image']['id'], '2') + self.assertEqual(len(body['server']['adminPass']), 16) def test_server_rebuild_rejected_when_building(self): body = { @@ -634,12 +657,15 @@ class ServerActionsTestV11(test.TestCase): self.assertEqual(res.status_int, 409) def test_server_rebuild_accepted_with_metadata(self): + metadata = {'new': 'metadata'} + + new_return_server = return_server_with_attributes(metadata=metadata) + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + body = { "rebuild": { "imageRef": "http://localhost/images/2", - "metadata": { - "new": "metadata", - }, + "metadata": metadata, }, } @@ -650,6 +676,8 @@ class ServerActionsTestV11(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) + body = json.loads(res.body) + self.assertEqual(body['server']['metadata'], metadata) def test_server_rebuild_accepted_with_bad_metadata(self): body = { @@ -719,6 +747,49 @@ class ServerActionsTestV11(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) + body = json.loads(res.body) + self.assertTrue('personality' not in body['server']) + + def test_server_rebuild_admin_pass(self): + new_return_server = return_server_with_attributes(image_ref='2') + self.stubs.Set(nova.db.api, 'instance_get', new_return_server) + + body = { + "rebuild": { + "imageRef": "http://localhost/images/2", + "adminPass": "asdf", + }, + } + + req = webob.Request.blank('/v1.1/fake/servers/1/action') + req.method = 'POST' + req.content_type = 'application/json' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 202) + body = json.loads(res.body) + self.assertEqual(body['server']['image']['id'], '2') + self.assertEqual(body['server']['adminPass'], 'asdf') + + def test_server_rebuild_server_not_found(self): + def server_not_found(self, instance_id): + raise exception.InstanceNotFound(instance_id=instance_id) + self.stubs.Set(nova.db.api, 'instance_get', server_not_found) + + body = { + "rebuild": { + "imageRef": "http://localhost/images/2", + }, + } + + req = webob.Request.blank('/v1.1/fake/servers/1/action') + req.method = 'POST' + req.content_type = 'application/json' + req.body = json.dumps(body) + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 404) def test_resize_server(self): diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index e5c1f2c34..3559e6de5 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -4562,3 +4562,138 @@ class ServerXMLSerializationTest(test.TestCase): str(ip['version'])) self.assertEqual(str(ip_elem.get('addr')), str(ip['addr'])) + + def test_action(self): + serializer = servers.ServerXMLSerializer() + + fixture = { + "server": { + "id": 1, + "uuid": FAKE_UUID, + 'created': self.TIMESTAMP, + 'updated': self.TIMESTAMP, + "progress": 0, + "name": "test_server", + "status": "BUILD", + "accessIPv4": "1.2.3.4", + "accessIPv6": "fead::1234", + "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", + "adminPass": "test_password", + "image": { + "id": "5", + "links": [ + { + "rel": "bookmark", + "href": self.IMAGE_BOOKMARK, + }, + ], + }, + "flavor": { + "id": "1", + "links": [ + { + "rel": "bookmark", + "href": self.FLAVOR_BOOKMARK, + }, + ], + }, + "addresses": { + "network_one": [ + { + "version": 4, + "addr": "67.23.10.138", + }, + { + "version": 6, + "addr": "::babe:67.23.10.138", + }, + ], + "network_two": [ + { + "version": 4, + "addr": "67.23.10.139", + }, + { + "version": 6, + "addr": "::babe:67.23.10.139", + }, + ], + }, + "metadata": { + "Open": "Stack", + "Number": "1", + }, + 'links': [ + { + 'href': self.SERVER_HREF, + 'rel': 'self', + }, + { + 'href': self.SERVER_BOOKMARK, + 'rel': 'bookmark', + }, + ], + } + } + + output = serializer.serialize(fixture, 'action') + root = etree.XML(output) + xmlutil.validate_schema(root, 'server') + + expected_server_href = self.SERVER_HREF + expected_server_bookmark = self.SERVER_BOOKMARK + expected_image_bookmark = self.IMAGE_BOOKMARK + expected_flavor_bookmark = self.FLAVOR_BOOKMARK + expected_now = self.TIMESTAMP + expected_uuid = FAKE_UUID + server_dict = fixture['server'] + + for key in ['name', 'id', 'uuid', 'created', 'accessIPv4', + 'updated', 'progress', 'status', 'hostId', + 'accessIPv6', 'adminPass']: + self.assertEqual(root.get(key), str(server_dict[key])) + + link_nodes = root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 2) + for i, link in enumerate(server_dict['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + metadata_root = root.find('{0}metadata'.format(NS)) + metadata_elems = metadata_root.findall('{0}meta'.format(NS)) + self.assertEqual(len(metadata_elems), 2) + for i, metadata_elem in enumerate(metadata_elems): + (meta_key, meta_value) = server_dict['metadata'].items()[i] + self.assertEqual(str(metadata_elem.get('key')), str(meta_key)) + self.assertEqual(str(metadata_elem.text).strip(), str(meta_value)) + + image_root = root.find('{0}image'.format(NS)) + self.assertEqual(image_root.get('id'), server_dict['image']['id']) + link_nodes = image_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['image']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + flavor_root = root.find('{0}flavor'.format(NS)) + self.assertEqual(flavor_root.get('id'), server_dict['flavor']['id']) + link_nodes = flavor_root.findall('{0}link'.format(ATOMNS)) + self.assertEqual(len(link_nodes), 1) + for i, link in enumerate(server_dict['flavor']['links']): + for key, value in link.items(): + self.assertEqual(link_nodes[i].get(key), value) + + addresses_root = root.find('{0}addresses'.format(NS)) + addresses_dict = server_dict['addresses'] + network_elems = addresses_root.findall('{0}network'.format(NS)) + self.assertEqual(len(network_elems), 2) + for i, network_elem in enumerate(network_elems): + network = addresses_dict.items()[i] + self.assertEqual(str(network_elem.get('id')), str(network[0])) + ip_elems = network_elem.findall('{0}ip'.format(NS)) + for z, ip_elem in enumerate(ip_elems): + ip = network[1][z] + self.assertEqual(str(ip_elem.get('version')), + str(ip['version'])) + self.assertEqual(str(ip_elem.get('addr')), + str(ip['addr'])) diff --git a/nova/tests/test_ipv6.py b/nova/tests/test_ipv6.py index 5c333b17e..04c1b5598 100644 --- a/nova/tests/test_ipv6.py +++ b/nova/tests/test_ipv6.py @@ -40,6 +40,25 @@ class IPv6RFC2462TestCase(test.TestCase): mac = ipv6.to_mac('2001:db8::216:3eff:fe33:4455') self.assertEquals(mac, '00:16:3e:33:44:55') + def test_to_global_with_bad_mac(self): + bad_mac = '02:16:3e:33:44:5Z' + self.assertRaises(TypeError, ipv6.to_global, + '2001:db8::', bad_mac, 'test') + + def test_to_global_with_bad_prefix(self): + bad_prefix = '82' + self.assertRaises(TypeError, ipv6.to_global, + bad_prefix, + '2001:db8::216:3eff:fe33:4455', + 'test') + + def test_to_global_with_bad_project(self): + bad_project = 'non-existent-project-name' + self.assertRaises(TypeError, ipv6.to_global, + '2001:db8::', + '2001:db8::a94a:8fe5:ff33:4455', + bad_project) + class IPv6AccountIdentiferTestCase(test.TestCase): """Unit tests for IPv6 account_identifier backend operations.""" |
