summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorKen Pepple <ken.pepple@gmail.com>2011-04-10 14:34:25 -0700
committerKen Pepple <ken.pepple@gmail.com>2011-04-10 14:34:25 -0700
commita3a5bd3b6fb83ae64c8566a012073b54745eccb7 (patch)
treef26aee7c2f350e36a273bd082e19c33f10f2f7f4 /nova
parente892deef8ba1f6c424dd8fa3fc6330d09245c89e (diff)
parent50f3f759468fa549b686dcef98633946163588e6 (diff)
merge trunk
Diffstat (limited to 'nova')
-rw-r--r--nova/api/ec2/cloud.py4
-rw-r--r--nova/api/openstack/image_metadata.py12
-rw-r--r--nova/api/openstack/views/images.py10
-rw-r--r--nova/api/openstack/views/servers.py2
-rw-r--r--nova/compute/api.py6
-rw-r--r--nova/network/linux_net.py2
-rw-r--r--nova/tests/api/openstack/test_image_metadata.py39
-rw-r--r--nova/tests/api/openstack/test_servers.py4
-rw-r--r--nova/tests/test_cloud.py31
-rw-r--r--nova/virt/xenapi/vm_utils.py17
10 files changed, 115 insertions, 12 deletions
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 4ed8a9ecf..651ec47f9 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -984,7 +984,7 @@ class CloudController(object):
except exception.NotFound:
raise exception.NotFound(_('Image %s not found') % image_id)
result = {'imageId': image_id, 'launchPermission': []}
- if image['properties']['is_public']:
+ if image['is_public']:
result['launchPermission'].append({'group': 'all'})
return result
@@ -1009,7 +1009,7 @@ class CloudController(object):
internal_id = image['id']
del(image['id'])
- image['properties']['is_public'] = (operation_type == 'add')
+ image['is_public'] = (operation_type == 'add')
return self.image_service.update(context, internal_id, image)
def update_image(self, context, image_id, **kwargs):
diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py
index e673e5f7b..1eccc0174 100644
--- a/nova/api/openstack/image_metadata.py
+++ b/nova/api/openstack/image_metadata.py
@@ -18,6 +18,7 @@
from webob import exc
from nova import flags
+from nova import quota
from nova import utils
from nova import wsgi
from nova.api.openstack import common
@@ -40,6 +41,15 @@ class Controller(common.OpenstackController):
metadata = image.get('properties', {})
return metadata
+ def _check_quota_limit(self, context, metadata):
+ if metadata is None:
+ return
+ num_metadata = len(metadata)
+ quota_metadata = quota.allowed_metadata_items(context, num_metadata)
+ if quota_metadata < num_metadata:
+ expl = _("Image metadata limit exceeded")
+ raise exc.HTTPBadRequest(explanation=expl)
+
def index(self, req, image_id):
"""Returns the list of metadata for a given instance"""
context = req.environ['nova.context']
@@ -62,6 +72,7 @@ class Controller(common.OpenstackController):
if 'metadata' in body:
for key, value in body['metadata'].iteritems():
metadata[key] = value
+ self._check_quota_limit(context, metadata)
img['properties'] = metadata
self.image_service.update(context, image_id, img, None)
return dict(metadata=metadata)
@@ -78,6 +89,7 @@ class Controller(common.OpenstackController):
img = self.image_service.show(context, image_id)
metadata = self._get_metadata(context, image_id, img)
metadata[id] = body[id]
+ self._check_quota_limit(context, metadata)
img['properties'] = metadata
self.image_service.update(context, image_id, img, None)
diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py
index 16195b050..9dec8a355 100644
--- a/nova/api/openstack/views/images.py
+++ b/nova/api/openstack/views/images.py
@@ -34,11 +34,11 @@ class ViewBuilder(object):
def _format_status(self, image):
"""Update the status field to standardize format."""
status_mapping = {
- 'pending': 'queued',
- 'decrypting': 'preparing',
- 'untarring': 'saving',
- 'available': 'active',
- 'killed': 'failed',
+ 'pending': 'QUEUED',
+ 'decrypting': 'PREPARING',
+ 'untarring': 'SAVING',
+ 'available': 'ACTIVE',
+ 'killed': 'FAILED',
}
try:
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 59a0ab06f..e52bfaea3 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -82,7 +82,7 @@ class ViewBuilder(object):
# Return the metadata as a dictionary
metadata = {}
for item in inst.get('metadata', []):
- metadata[item['key']] = item['value']
+ metadata[item['key']] = str(item['value'])
inst_dict['metadata'] = metadata
inst_dict['hostId'] = ''
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 83ad6b0c9..041e0e74a 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -370,11 +370,15 @@ class API(base.Base):
instance_id)
raise
- if (instance['state_description'] == 'terminating'):
+ if instance['state_description'] == 'terminating':
LOG.warning(_("Instance %s is already being terminated"),
instance_id)
return
+ if instance['state_description'] == 'migrating':
+ LOG.warning(_("Instance %s is being migrated"), instance_id)
+ return
+
self.update(context,
instance['id'],
state_description='terminating',
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index ed6c943c7..ec5579dee 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -503,7 +503,7 @@ def ensure_bridge(bridge, interface, net_attrs=None):
if fields and fields[0] == "0.0.0.0" and fields[-1] == interface:
gateway = fields[1]
_execute('sudo', 'route', 'del', 'default', 'gw', gateway,
- 'dev', interface)
+ 'dev', interface, check_exit_code=False)
out, err = _execute('sudo', 'ip', 'addr', 'show', 'dev', interface,
'scope', 'global')
for line in out.split("\n"):
diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py
index 7c3287006..543c59629 100644
--- a/nova/tests/api/openstack/test_image_metadata.py
+++ b/nova/tests/api/openstack/test_image_metadata.py
@@ -65,6 +65,19 @@ class ImageMetaDataTest(unittest.TestCase):
'key2': 'value2'
},
'size': 5882349},
+ {'status': 'active',
+ 'name': 'image3',
+ 'deleted': False,
+ 'container_format': None,
+ 'created_at': '2011-03-22T17:40:15',
+ 'disk_format': None,
+ 'updated_at': '2011-03-22T17:40:15',
+ 'id': '3',
+ 'location': 'file:///var/lib/glance/images/2',
+ 'is_public': True,
+ 'deleted_at': None,
+ 'properties': {},
+ 'size': 5882349},
]
def setUp(self):
@@ -75,6 +88,10 @@ class ImageMetaDataTest(unittest.TestCase):
fakes.FakeAuthManager.auth_data = {}
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_auth(self.stubs)
+ # NOTE(dprince) max out properties/metadata in image 3 for testing
+ img3 = self.IMAGE_FIXTURES[2]
+ for num in range(FLAGS.quota_metadata_items):
+ img3['properties']['key%i' % num] = "blah"
fakes.stub_out_glance(self.stubs, self.IMAGE_FIXTURES)
def tearDown(self):
@@ -162,3 +179,25 @@ class ImageMetaDataTest(unittest.TestCase):
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(404, res.status_int)
+
+ def test_too_many_metadata_items_on_create(self):
+ data = {"metadata": {}}
+ for num in range(FLAGS.quota_metadata_items + 1):
+ data['metadata']['key%i' % num] = "blah"
+ json_string = str(data).replace("\'", "\"")
+ req = webob.Request.blank('/v1.1/images/2/meta')
+ req.environ['api.version'] = '1.1'
+ req.method = 'POST'
+ req.body = json_string
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(400, res.status_int)
+
+ def test_too_many_metadata_items_on_put(self):
+ req = webob.Request.blank('/v1.1/images/3/meta/blah')
+ req.environ['api.version'] = '1.1'
+ req.method = 'PUT'
+ req.body = '{"blah": "blah"}'
+ req.headers["content-type"] = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(400, res.status_int)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index f10bc1d7b..34513734b 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -751,7 +751,7 @@ class ServersTest(test.TestCase):
self.assertEqual(s['imageId'], '10')
self.assertEqual(s['flavorId'], 1)
self.assertEqual(s['status'], 'BUILD')
- self.assertEqual(s['metadata']['seq'], i)
+ self.assertEqual(s['metadata']['seq'], str(i))
def test_get_all_server_details_v1_1(self):
req = webob.Request.blank('/v1.1/servers/detail')
@@ -765,7 +765,7 @@ class ServersTest(test.TestCase):
self.assertEqual(s['imageRef'], 'http://localhost/v1.1/images/10')
self.assertEqual(s['flavorRef'], 'http://localhost/v1.1/flavors/1')
self.assertEqual(s['status'], 'BUILD')
- self.assertEqual(s['metadata']['seq'], i)
+ self.assertEqual(s['metadata']['seq'], str(i))
def test_get_all_server_details_with_host(self):
'''
diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py
index 5cb969979..5f76a9005 100644
--- a/nova/tests/test_cloud.py
+++ b/nova/tests/test_cloud.py
@@ -247,6 +247,37 @@ class CloudTestCase(test.TestCase):
self.assertRaises(NotFound, describe_images,
self.context, ['ami-fake'])
+ def test_describe_image_attribute(self):
+ describe_image_attribute = self.cloud.describe_image_attribute
+
+ def fake_show(meh, context, id):
+ return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
+ 'type': 'machine'}, 'is_public': True}
+
+ self.stubs.Set(local.LocalImageService, 'show', fake_show)
+ self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show)
+ result = describe_image_attribute(self.context, 'ami-00000001',
+ 'launchPermission')
+ self.assertEqual([{'group': 'all'}], result['launchPermission'])
+
+ def test_modify_image_attribute(self):
+ modify_image_attribute = self.cloud.modify_image_attribute
+
+ def fake_show(meh, context, id):
+ return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
+ 'type': 'machine'}, 'is_public': False}
+
+ def fake_update(meh, context, image_id, metadata, data=None):
+ return metadata
+
+ self.stubs.Set(local.LocalImageService, 'show', fake_show)
+ self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show)
+ self.stubs.Set(local.LocalImageService, 'update', fake_update)
+ result = modify_image_attribute(self.context, 'ami-00000001',
+ 'launchPermission', 'add',
+ user_group=['all'])
+ self.assertEqual(True, result['is_public'])
+
def test_console_output(self):
instance_type = FLAGS.default_instance_type
max_count = 1
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 46fc6baa4..d2045a557 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -49,6 +49,8 @@ LOG = logging.getLogger("nova.virt.xenapi.vm_utils")
FLAGS = flags.FLAGS
flags.DEFINE_string('default_os_type', 'linux', 'Default OS type')
+flags.DEFINE_integer('block_device_creation_timeout', 10,
+ 'time to wait for a block device to be created')
XENAPI_POWER_STATE = {
'Halted': power_state.SHUTDOWN,
@@ -896,6 +898,16 @@ def remap_vbd_dev(dev):
return remapped_dev
+def _wait_for_device(dev):
+ """Wait for device node to appear"""
+ for i in xrange(0, FLAGS.block_device_creation_timeout):
+ if os.path.exists('/dev/%s' % dev):
+ return
+ time.sleep(1)
+
+ raise StorageError(_('Timeout waiting for device %s to be created') % dev)
+
+
def with_vdi_attached_here(session, vdi_ref, read_only, f):
this_vm_ref = get_this_vm_ref(session)
vbd_rec = {}
@@ -924,6 +936,11 @@ def with_vdi_attached_here(session, vdi_ref, read_only, f):
if dev != orig_dev:
LOG.debug(_('VBD %(vbd_ref)s plugged into wrong dev, '
'remapping to %(dev)s') % locals())
+ if dev != 'autodetect':
+ # NOTE(johannes): Unit tests will end up with a device called
+ # 'autodetect' which obviously won't exist. It's not ideal,
+ # but the alternatives were much messier
+ _wait_for_device(dev)
return f(dev)
finally:
LOG.debug(_('Destroying VBD for VDI %s ... '), vdi_ref)