From ceb8cd14f968aa063bd6a19999340f77c5603568 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Tue, 15 Mar 2011 21:04:38 -0700 Subject: Fixed DescribeUser in ec2 admin client --- nova/adminclient.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'nova') diff --git a/nova/adminclient.py b/nova/adminclient.py index fc3c5c5fe..f570e12c2 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -324,14 +324,11 @@ class NovaAdminClient(object): def get_user(self, name): """Grab a single user by name.""" - try: - return self.apiconn.get_object('DescribeUser', - {'Name': name}, - UserInfo) - except boto.exception.BotoServerError, e: - if e.status == 400 and e.error_code == 'NotFound': - return None - raise + user = self.apiconn.get_object('DescribeUser', + {'Name': name}, + UserInfo) + if user.username != None: + return user def has_user(self, username): """Determine if user exists.""" -- cgit From ec524aae3224a806fa41f6ae6c2975a1ba124f15 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 25 Mar 2011 15:18:57 +0100 Subject: Toss an __init__ in the test extensions dir. This gets it included in the tarball. --- nova/tests/api/openstack/extensions/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 nova/tests/api/openstack/extensions/__init__.py (limited to 'nova') diff --git a/nova/tests/api/openstack/extensions/__init__.py b/nova/tests/api/openstack/extensions/__init__.py new file mode 100644 index 000000000..e69de29bb -- cgit From af8aa36ca07c5e51016df68c0acc7449378fac2f Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 25 Mar 2011 18:23:36 +0100 Subject: Add license and copyright to nova/tests/api/openstack/extensions/__init__.py --- nova/tests/api/openstack/extensions/__init__.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'nova') diff --git a/nova/tests/api/openstack/extensions/__init__.py b/nova/tests/api/openstack/extensions/__init__.py index e69de29bb..848908a95 100644 --- a/nova/tests/api/openstack/extensions/__init__.py +++ b/nova/tests/api/openstack/extensions/__init__.py @@ -0,0 +1,15 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. -- cgit From 7cdc3add34b109e3f956f785b60a5aa5cf273e53 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 28 Mar 2011 12:24:41 +0200 Subject: Do not load extensions that start with a "_" --- nova/api/openstack/extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index 9d98d849a..439612faa 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -317,7 +317,7 @@ class ExtensionManager(object): LOG.audit(_('Loading extension file: %s'), f) mod_name, file_ext = os.path.splitext(os.path.split(f)[-1]) ext_path = os.path.join(self.path, f) - if file_ext.lower() == '.py': + if file_ext.lower() == '.py' and not mod_name.startswith('_'): mod = imp.load_source(mod_name, ext_path) ext_name = mod_name[0].upper() + mod_name[1:] try: -- cgit From d25968ab494f65ed90981e440169e31a7488befe Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 28 Mar 2011 15:21:53 +0200 Subject: Add friendlier message if an extension fails to include a correctly named class or factory. --- nova/api/openstack/extensions.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index 439612faa..4a7236863 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -321,7 +321,14 @@ class ExtensionManager(object): mod = imp.load_source(mod_name, ext_path) ext_name = mod_name[0].upper() + mod_name[1:] try: - new_ext = getattr(mod, ext_name)() + new_ext_class = getattr(mod, ext_name, None) + if not new_ext_class: + LOG.warning(_('Did not find expected name ' + '"%(ext_name)" in %(file)s'), + { 'ext_name': ext_name, + 'file': ext_path }) + continue + new_ext = new_ext_class() self._check_extension(new_ext) self.extensions[new_ext.get_alias()] = new_ext except AttributeError as ex: -- cgit From 9786a19ec0bc5176cc01b56d473a977b85800977 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 28 Mar 2011 15:34:20 +0200 Subject: Spell "warn" correctly. --- nova/api/openstack/extensions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index 4a7236863..259d24a7d 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -323,10 +323,10 @@ class ExtensionManager(object): try: new_ext_class = getattr(mod, ext_name, None) if not new_ext_class: - LOG.warning(_('Did not find expected name ' - '"%(ext_name)" in %(file)s'), - { 'ext_name': ext_name, - 'file': ext_path }) + LOG.warn(_('Did not find expected name ' + '"%(ext_name)" in %(file)s'), + { 'ext_name': ext_name, + 'file': ext_path }) continue new_ext = new_ext_class() self._check_extension(new_ext) -- cgit From dbf14e9b6cc337233ef95b03fd1c2fdba8ebf8a7 Mon Sep 17 00:00:00 2001 From: termie Date: Mon, 28 Mar 2011 10:47:08 -0700 Subject: add snapshot support for libvirt --- nova/virt/libvirt_conn.py | 71 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 36457ee87..4456ffc12 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -38,12 +38,15 @@ Supports KVM, QEMU, UML, and XEN. import multiprocessing import os -import shutil -import sys import random +import shutil import subprocess +import sys +import tempfile +import time import uuid from xml.dom import minidom +from xml.etree import ElementTree from eventlet import greenthread from eventlet import tpool @@ -111,6 +114,8 @@ flags.DEFINE_string('live_migration_flag', 'Define live migration behavior.') flags.DEFINE_integer('live_migration_bandwidth', 0, 'Define live migration behavior') +flags.DEFINE_string('qemu_img', 'qemu-img', + 'binary to use for qemu-img commands') def get_connection(read_only): @@ -397,10 +402,64 @@ class LibvirtConnection(driver.ComputeDriver): @exception.wrap_exception def snapshot(self, instance, image_id): - """ Create snapshot from a running VM instance """ - raise NotImplementedError( - _("Instance snapshotting is not supported for libvirt" - "at this time")) + """Create snapshot from a running VM instance. + + This command only works with qemu 0.14+, the qemu_img flag is + provided so that a locally compiled binary of qemu-img can be used + to support this command. + + """ + image_service = utils.import_object(FLAGS.image_service) + virt_dom = self._conn.lookupByName(instance['name']) + elevated = context.get_admin_context() + + base = image_service.show(elevated, instance['image_id']) + + metadata = {'type': 'machine', + 'is_public': False, + 'properties': {'architecture': base['architecture'], + 'kernel_id': instance['kernel_id'], + 'image_location': 'snapshot', + 'image_state': 'available', + 'owner_id': instance['project_id'], + 'ramdisk_id': instance['ramdisk_id'], + } + } + + # Make the snapshot + snapshot_name = uuid.uuid4().hex + snapshot_xml = """ + + %s + + """ % snapshot_name + snapshot_ptr = virt_dom.snapshotCreateXML(snapshot_xml, 0) + + # Find the disk + xml_desc = virt_dom.XMLDesc(0) + domain = ElementTree.fromstring(xml_desc) + source = domain.find('devices/disk/source') + disk_path = source.get('file') + + # Export the snapshot to a raw image + temp_dir = tempfile.mkdtemp() + out_path = os.path.join(temp_dir, snapshot_name) + qemu_img_cmd = '%s convert -f qcow2 -O raw -s %s %s %s' % ( + FLAGS.qemu_img, + snapshot_name, + disk_path, + out_path) + utils.execute(qemu_img_cmd) + + # Upload that image to the image service + with open(out_path) as image_file: + image_service.update(elevated, + image_id, + metadata, + image_file) + + # Clean up + shutil.rmtree(temp_dir) @exception.wrap_exception def reboot(self, instance): -- cgit From 1e4024b72218a07d1e535878337547cf16406dd8 Mon Sep 17 00:00:00 2001 From: termie Date: Mon, 28 Mar 2011 10:47:11 -0700 Subject: update glance params per review --- nova/virt/libvirt_conn.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4456ffc12..80eb64f3c 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -415,9 +415,12 @@ class LibvirtConnection(driver.ComputeDriver): base = image_service.show(elevated, instance['image_id']) - metadata = {'type': 'machine', + metadata = {'disk_format': base['disk_format'], + 'container_format': base['container_format'], 'is_public': False, 'properties': {'architecture': base['architecture'], + 'type': base['type'], + 'name': '%s.%s' % (base['name'], image_id), 'kernel_id': instance['kernel_id'], 'image_location': 'snapshot', 'image_state': 'available', -- cgit From b6df504c33cfa0fe02e31962578b77d841e1e6d8 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Mon, 28 Mar 2011 14:31:12 -0400 Subject: backup_schedule tests corrected; controller moved to APIRouterV10; making controller fully HTTPNotImplemented --- nova/api/openstack/__init__.py | 10 +++++----- nova/api/openstack/backup_schedules.py | 6 +++++- nova/tests/api/openstack/test_servers.py | 22 ++++++++++++++++------ 3 files changed, 26 insertions(+), 12 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 8fabbce8e..149abfeb8 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -106,11 +106,6 @@ class APIRouter(wsgi.Router): controller=accounts.Controller(), collection={'detail': 'GET'}) - mapper.resource("backup_schedule", "backup_schedule", - controller=backup_schedules.Controller(), - parent_resource=dict(member_name='server', - collection_name='servers')) - mapper.resource("console", "consoles", controller=consoles.Controller(), parent_resource=dict(member_name='server', @@ -141,6 +136,11 @@ class APIRouterV10(APIRouter): controller=flavors.ControllerV10(), collection={'detail': 'GET'}) + mapper.resource("backup_schedule", "backup_schedule", + controller=backup_schedules.Controller(), + parent_resource=dict(member_name='server', + collection_name='servers')) + class APIRouterV11(APIRouter): """Define routes specific to OpenStack API V1.1.""" diff --git a/nova/api/openstack/backup_schedules.py b/nova/api/openstack/backup_schedules.py index 7abb5f884..f2d2d86e8 100644 --- a/nova/api/openstack/backup_schedules.py +++ b/nova/api/openstack/backup_schedules.py @@ -42,7 +42,11 @@ class Controller(wsgi.Controller): def index(self, req, server_id): """ Returns the list of backup schedules for a given instance """ - return _translate_keys({}) + return faults.Fault(exc.HTTPNotImplemented()) + + def show(self, req, server_id, id): + """ Returns a single backup schedule for a given instance """ + return faults.Fault(exc.HTTPNotImplemented()) def create(self, req, server_id): """ No actual update method required, since the existing API allows diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 737b43c7b..989385a8c 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -483,21 +483,31 @@ class ServersTest(test.TestCase): req.get_response(fakes.wsgi_app()) def test_create_backup_schedules(self): - req = webob.Request.blank('/v1.0/servers/1/backup_schedules') + req = webob.Request.blank('/v1.0/servers/1/backup_schedule') req.method = 'POST' res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status, '404 Not Found') + self.assertEqual(res.status_int, 501) def test_delete_backup_schedules(self): - req = webob.Request.blank('/v1.0/servers/1/backup_schedules') + req = webob.Request.blank('/v1.0/servers/1/backup_schedule/1') req.method = 'DELETE' res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status, '404 Not Found') + self.assertEqual(res.status_int, 501) def test_get_server_backup_schedules(self): - req = webob.Request.blank('/v1.0/servers/1/backup_schedules') + req = webob.Request.blank('/v1.0/servers/1/backup_schedule') res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status, '404 Not Found') + self.assertEqual(res.status_int, 501) + + def test_get_server_backup_schedule(self): + req = webob.Request.blank('/v1.0/servers/1/backup_schedule/1') + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 501) + + def test_server_backup_schedule_deprecated_v11(self): + req = webob.Request.blank('/v1.1/servers/1/backup_schedule') + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 404) def test_get_all_server_details_v1_0(self): req = webob.Request.blank('/v1.0/servers/detail') -- cgit From dea3af64186ff204de7d5ca9852af267e648823e Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 28 Mar 2011 20:36:07 +0200 Subject: Remove now useless try/except block. --- nova/api/openstack/extensions.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index 259d24a7d..e2f833d57 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -320,20 +320,16 @@ class ExtensionManager(object): if file_ext.lower() == '.py' and not mod_name.startswith('_'): mod = imp.load_source(mod_name, ext_path) ext_name = mod_name[0].upper() + mod_name[1:] - try: - new_ext_class = getattr(mod, ext_name, None) - if not new_ext_class: - LOG.warn(_('Did not find expected name ' - '"%(ext_name)" in %(file)s'), - { 'ext_name': ext_name, - 'file': ext_path }) - continue - new_ext = new_ext_class() - self._check_extension(new_ext) - self.extensions[new_ext.get_alias()] = new_ext - except AttributeError as ex: - LOG.exception(_("Exception loading extension: %s"), - unicode(ex)) + new_ext_class = getattr(mod, ext_name, None) + if not new_ext_class: + LOG.warn(_('Did not find expected name ' + '"%(ext_name)" in %(file)s'), + { 'ext_name': ext_name, + 'file': ext_path }) + continue + new_ext = new_ext_class() + self._check_extension(new_ext) + self.extensions[new_ext.get_alias()] = new_ext class ResponseExtension(object): -- cgit From 9fdf9967234d8553c3548ad03fc3b2691285fa7d Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Mon, 28 Mar 2011 11:56:19 -0700 Subject: Added image name and description mapping to ec2 api --- nova/api/ec2/cloud.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 0da642318..9e34d3317 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -890,6 +890,8 @@ class CloudController(object): i['imageOwnerId'] = image['properties'].get('owner_id') i['imageLocation'] = image['properties'].get('image_location') i['imageState'] = image['properties'].get('image_state') + i['displayName'] = image.get('name') + i['description'] = image.get('description') i['type'] = image_type i['isPublic'] = str(image['properties'].get('is_public', '')) == 'True' i['architecture'] = image['properties'].get('architecture') -- cgit From 7040eadcc7e86d063c5c69391dedafa181711913 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 28 Mar 2011 22:21:18 +0200 Subject: pep8 --- nova/api/openstack/extensions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index e2f833d57..b9b7f998d 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -324,8 +324,8 @@ class ExtensionManager(object): if not new_ext_class: LOG.warn(_('Did not find expected name ' '"%(ext_name)" in %(file)s'), - { 'ext_name': ext_name, - 'file': ext_path }) + {'ext_name': ext_name, + 'file': ext_path}) continue new_ext = new_ext_class() self._check_extension(new_ext) -- cgit