summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvladimir.p <vladimir@zadarastorage.com>2011-08-24 09:20:54 -0700
committervladimir.p <vladimir@zadarastorage.com>2011-08-24 09:20:54 -0700
commit163923f57d075fa5d46a5b54073fc6cd30e5c624 (patch)
treeda035edd4328ad2ea3aa313392fbb51204eaf9f3
parent8ad9373648dea11783545ad88429def4691a2925 (diff)
parentaf5c4805368a59a757c657193b9582bb2bf0547d (diff)
merged with 1487
-rw-r--r--nova/api/openstack/servers.py29
-rw-r--r--nova/compute/api.py5
-rw-r--r--nova/compute/manager.py6
-rw-r--r--nova/ipv6/rfc2462.py4
-rw-r--r--nova/tests/api/openstack/test_server_actions.py141
-rw-r--r--nova/tests/api/openstack/test_servers.py135
-rw-r--r--nova/tests/test_ipv6.py19
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."""