diff options
| author | Justin Santa Barbara <justin@fathomdb.com> | 2011-03-28 17:04:14 -0700 |
|---|---|---|
| committer | Justin Santa Barbara <justin@fathomdb.com> | 2011-03-28 17:04:14 -0700 |
| commit | 012a5878737f8648e99c9ae9b84d3a86ee86131d (patch) | |
| tree | 988b948ba850011e230826f7ed8aeb414758eff6 | |
| parent | 87bc3bca7904135656ed3a99efc19952be95dcbf (diff) | |
| parent | e025ede777b0ee6652e035dfde467037f0485de2 (diff) | |
Merged with trunk
| -rwxr-xr-x | bin/nova-ajax-console-proxy | 2 | ||||
| -rwxr-xr-x | contrib/nova.sh | 33 | ||||
| -rw-r--r-- | nova/adminclient.py | 13 | ||||
| -rw-r--r-- | nova/api/ec2/cloud.py | 2 | ||||
| -rw-r--r-- | nova/api/openstack/__init__.py | 18 | ||||
| -rw-r--r-- | nova/api/openstack/backup_schedules.py | 6 | ||||
| -rw-r--r-- | nova/api/openstack/extensions.py | 2 | ||||
| -rw-r--r-- | nova/api/openstack/shared_ip_groups.py | 6 | ||||
| -rw-r--r-- | nova/compute/manager.py | 40 | ||||
| -rw-r--r-- | nova/tests/api/openstack/extensions/__init__.py | 15 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_images.py | 13 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_servers.py | 22 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_shared_ip_groups.py | 30 | ||||
| -rw-r--r-- | nova/tests/test_auth.py | 8 | ||||
| -rw-r--r-- | nova/virt/driver.py | 4 | ||||
| -rw-r--r-- | nova/virt/libvirt_conn.py | 81 |
16 files changed, 225 insertions, 70 deletions
diff --git a/bin/nova-ajax-console-proxy b/bin/nova-ajax-console-proxy index b4ba157e1..0342c620a 100755 --- a/bin/nova-ajax-console-proxy +++ b/bin/nova-ajax-console-proxy @@ -115,7 +115,7 @@ class AjaxConsoleProxy(object): {'args': data['args'], 'last_activity': time.time()} conn = rpc.Connection.instance(new=True) - consumer = rpc.TopicConsumer( + consumer = rpc.TopicAdapterConsumer( connection=conn, topic=FLAGS.ajax_console_proxy_topic) consumer.register_callback(Callback()) diff --git a/contrib/nova.sh b/contrib/nova.sh index 55dfb971c..d7d34dcbd 100755 --- a/contrib/nova.sh +++ b/contrib/nova.sh @@ -18,6 +18,9 @@ if [ ! -n "$HOST_IP" ]; then fi USE_MYSQL=${USE_MYSQL:-0} +INTERFACE=${INTERFACE:-eth0} +FLOATING_RANGE=${FLOATING_RANGE:-10.6.0.0/27} +FIXED_RANGE=${FIXED_RANGE:-10.0.0.0/24} MYSQL_PASS=${MYSQL_PASS:-nova} TEST=${TEST:-0} USE_LDAP=${USE_LDAP:-0} @@ -72,11 +75,14 @@ if [ "$CMD" == "install" ]; then sudo modprobe kvm sudo /etc/init.d/libvirt-bin restart sudo modprobe nbd - sudo apt-get install -y python-twisted python-sqlalchemy python-mox python-greenlet python-carrot - sudo apt-get install -y python-migrate python-eventlet python-gflags python-ipy python-tempita - sudo apt-get install -y python-libvirt python-libxml2 python-routes python-cheetah - sudo apt-get install -y python-netaddr python-paste python-pastedeploy python-glance - sudo apt-get install -y python-multiprocessing + sudo apt-get install -y python-twisted python-mox python-ipy python-paste + sudo apt-get install -y python-migrate python-gflags python-greenlet + sudo apt-get install -y python-libvirt python-libxml2 python-routes + sudo apt-get install -y python-netaddr python-pastedeploy python-eventlet + sudo apt-get install -y python-novaclient python-glance python-cheetah + sudo apt-get install -y python-carrot python-tempita python-sqlalchemy + sudo apt-get install -y python-suds + if [ "$USE_IPV6" == 1 ]; then sudo apt-get install -y radvd @@ -105,7 +111,7 @@ function screen_it { screen -S nova -p $1 -X stuff "$2$NL" } -if [ "$CMD" == "run" ]; then +if [ "$CMD" == "run" ] || [ "$CMD" == "run_detached" ]; then cat >$NOVA_DIR/bin/nova.conf << NOVA_CONF_EOF --verbose @@ -113,6 +119,8 @@ if [ "$CMD" == "run" ]; then --dhcpbridge_flagfile=$NOVA_DIR/bin/nova.conf --network_manager=nova.network.manager.$NET_MAN --my_ip=$HOST_IP +--public_interface=$INTERFACE +--vlan_interface=$INTERFACE --sql_connection=$SQL_CONN --auth_driver=nova.auth.$AUTH --libvirt_type=$LIBVIRT_TYPE @@ -168,10 +176,13 @@ NOVA_CONF_EOF # create a project called 'admin' with project manager of 'admin' $NOVA_DIR/bin/nova-manage project create admin admin # create a small network - $NOVA_DIR/bin/nova-manage network create 10.0.0.0/8 1 32 + $NOVA_DIR/bin/nova-manage network create $FIXED_RANGE 1 32 # create some floating ips - $NOVA_DIR/bin/nova-manage floating create `hostname` 10.6.0.0/27 + $NOVA_DIR/bin/nova-manage floating create `hostname` $FLOATING_RANGE + + # convert old images + $NOVA_DIR/bin/nova-manage image convert $DIR/images # nova api crashes if we start it with a regular screen command, # so send the start command by forcing text into the window. @@ -187,8 +198,10 @@ NOVA_CONF_EOF $NOVA_DIR/bin/nova-manage project zipfile admin admin $NOVA_DIR/nova.zip unzip -o $NOVA_DIR/nova.zip -d $NOVA_DIR/ - screen_it test ". $NOVA_DIR/novarc" - screen -S nova -x + screen_it test "export PATH=$NOVA_DIR/bin:$PATH;. $NOVA_DIR/novarc" + if [ "$CMD" != "run_detached" ]; then + screen -S nova -x + fi fi if [ "$CMD" == "run" ] || [ "$CMD" == "terminate" ]; then 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.""" 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') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 7f2bb1155..8b9acc11d 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', @@ -119,10 +114,6 @@ class APIRouter(wsgi.Router): mapper.resource("image", "images", controller=images.Controller(), collection={'detail': 'GET'}) - mapper.resource("shared_ip_group", "shared_ip_groups", - collection={'detail': 'GET'}, - controller=shared_ip_groups.Controller()) - _limits = limits.LimitsController() mapper.resource("limit", "limits", controller=_limits) @@ -143,6 +134,15 @@ class APIRouterV10(APIRouter): controller=flavors.ControllerV10(), collection={'detail': 'GET'}) + mapper.resource("shared_ip_group", "shared_ip_groups", + collection={'detail': 'GET'}, + controller=shared_ip_groups.Controller()) + + 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/api/openstack/extensions.py b/nova/api/openstack/extensions.py index 9566d3250..1c39dd0e2 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -372,7 +372,7 @@ class ExtensionManager(object): for f in os.listdir(path): mod_name, file_ext = os.path.splitext(os.path.split(f)[-1]) - if file_ext.startswith('_'): + if mod_name.startswith('_'): continue if file_ext.lower() != '.py': continue diff --git a/nova/api/openstack/shared_ip_groups.py b/nova/api/openstack/shared_ip_groups.py index 5d78f9377..ee7991d7f 100644 --- a/nova/api/openstack/shared_ip_groups.py +++ b/nova/api/openstack/shared_ip_groups.py @@ -42,11 +42,11 @@ class Controller(wsgi.Controller): def index(self, req): """ Returns a list of Shared IP Groups for the user """ - return dict(sharedIpGroups=[]) + raise faults.Fault(exc.HTTPNotImplemented()) def show(self, req, id): """ Shows in-depth information on a specific Shared IP Group """ - return _translate_keys({}) + raise faults.Fault(exc.HTTPNotImplemented()) def update(self, req, id): """ You can't update a Shared IP Group """ @@ -58,7 +58,7 @@ class Controller(wsgi.Controller): def detail(self, req): """ Returns a complete list of Shared IP Groups """ - return _translate_detail_keys({}) + raise faults.Fault(exc.HTTPNotImplemented()) def create(self, req): """ Creates a new Shared IP group """ diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 468771f46..e0a5e2b3f 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -141,12 +141,6 @@ class ComputeManager(manager.SchedulerDependentManager): """ self.driver.init_host(host=self.host) - def periodic_tasks(self, context=None): - """Tasks to be run at a periodic interval.""" - super(ComputeManager, self).periodic_tasks(context) - if FLAGS.rescue_timeout > 0: - self.driver.poll_rescued_instances(FLAGS.rescue_timeout) - def _update_state(self, context, instance_id): """Update the state of an instance from the driver info.""" # FIXME(ja): include other fields from state? @@ -1033,11 +1027,20 @@ class ComputeManager(manager.SchedulerDependentManager): error_list = [] try: + if FLAGS.rescue_timeout > 0: + self.driver.poll_rescued_instances(FLAGS.rescue_timeout) + except Exception as ex: + LOG.warning(_("Error during poll_rescued_instances: %s"), + unicode(ex)) + error_list.append(ex) + + try: self._poll_instance_states(context) except Exception as ex: LOG.warning(_("Error during instance poll: %s"), unicode(ex)) error_list.append(ex) + return error_list def _poll_instance_states(self, context): @@ -1051,16 +1054,33 @@ class ComputeManager(manager.SchedulerDependentManager): for db_instance in db_instances: name = db_instance['name'] + db_state = db_instance['state'] vm_instance = vm_instances.get(name) + if vm_instance is None: - LOG.info(_("Found instance '%(name)s' in DB but no VM. " - "Setting state to shutoff.") % locals()) - vm_state = power_state.SHUTOFF + # NOTE(justinsb): We have to be very careful here, because a + # concurrent operation could be in progress (e.g. a spawn) + if db_state == power_state.NOSTATE: + # Assume that NOSTATE => spawning + # TODO(justinsb): This does mean that if we crash during a + # spawn, the machine will never leave the spawning state, + # but this is just the way nova is; this function isn't + # trying to correct that problem. + # We could have a separate task to correct this error. + # TODO(justinsb): What happens during a live migration? + LOG.info(_("Found instance '%(name)s' in DB but no VM. " + "State=%(db_state)s, so assuming spawn is in " + "progress.") % locals()) + vm_state = db_state + else: + LOG.info(_("Found instance '%(name)s' in DB but no VM. " + "State=%(db_state)s, so setting state to " + "shutoff.") % locals()) + vm_state = power_state.SHUTOFF else: vm_state = vm_instance.state vms_not_found_in_db.remove(name) - db_state = db_instance['state'] if vm_state != db_state: LOG.info(_("DB/VM state mismatch. Changing state from " "'%(db_state)s' to '%(vm_state)s'") % locals()) diff --git a/nova/tests/api/openstack/extensions/__init__.py b/nova/tests/api/openstack/extensions/__init__.py new file mode 100644 index 000000000..848908a95 --- /dev/null +++ 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. diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 1cdccadd6..738bdda19 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -43,9 +43,14 @@ from nova.tests.api.openstack import fakes FLAGS = flags.FLAGS -class BaseImageServiceTests(object): +class _BaseImageServiceTests(test.TestCase): """Tasks to test for all image services""" + def __init__(self, *args, **kwargs): + super(_BaseImageServiceTests, self).__init__(*args, **kwargs) + self.service = None + self.context = None + def test_create(self): fixture = self._make_fixture('test image') num_images = len(self.service.index(self.context)) @@ -116,8 +121,7 @@ class BaseImageServiceTests(object): return fixture -class LocalImageServiceTest(test.TestCase, - BaseImageServiceTests): +class LocalImageServiceTest(_BaseImageServiceTests): """Tests the local image service""" @@ -147,8 +151,7 @@ class LocalImageServiceTest(test.TestCase, self.assertEqual(3, len(found_image_ids), len(found_image_ids)) -class GlanceImageServiceTest(test.TestCase, - BaseImageServiceTests): +class GlanceImageServiceTest(_BaseImageServiceTests): """Tests the Glance image service, in particular that metadata translation works properly. 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') diff --git a/nova/tests/api/openstack/test_shared_ip_groups.py b/nova/tests/api/openstack/test_shared_ip_groups.py index b4de2ef41..c2bd7e45a 100644 --- a/nova/tests/api/openstack/test_shared_ip_groups.py +++ b/nova/tests/api/openstack/test_shared_ip_groups.py @@ -16,25 +16,49 @@ # under the License. import stubout +import webob from nova import test from nova.api.openstack import shared_ip_groups +from nova.tests.api.openstack import fakes class SharedIpGroupsTest(test.TestCase): def setUp(self): super(SharedIpGroupsTest, self).setUp() self.stubs = stubout.StubOutForTesting() + fakes.FakeAuthManager.reset_fake_data() + fakes.FakeAuthDatabase.data = {} + fakes.stub_out_auth(self.stubs) def tearDown(self): self.stubs.UnsetAll() super(SharedIpGroupsTest, self).tearDown() def test_get_shared_ip_groups(self): - pass + req = webob.Request.blank('/v1.0/shared_ip_groups') + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 501) def test_create_shared_ip_group(self): - pass + req = webob.Request.blank('/v1.0/shared_ip_groups') + req.method = 'POST' + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 501) + + def test_update_shared_ip_group(self): + req = webob.Request.blank('/v1.0/shared_ip_groups/12') + req.method = 'PUT' + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 501) def test_delete_shared_ip_group(self): - pass + req = webob.Request.blank('/v1.0/shared_ip_groups/12') + req.method = 'DELETE' + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 501) + + def test_deprecated_v11(self): + req = webob.Request.blank('/v1.1/shared_ip_groups') + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 404) diff --git a/nova/tests/test_auth.py b/nova/tests/test_auth.py index 885596f56..f8a1b1564 100644 --- a/nova/tests/test_auth.py +++ b/nova/tests/test_auth.py @@ -80,10 +80,10 @@ class user_and_project_generator(object): self.manager.delete_project(self.project) -class AuthManagerTestCase(object): +class _AuthManagerBaseTestCase(test.TestCase): def setUp(self): FLAGS.auth_driver = self.auth_driver - super(AuthManagerTestCase, self).setUp() + super(_AuthManagerBaseTestCase, self).setUp() self.flags(connection_type='fake') self.manager = manager.AuthManager(new=True) @@ -331,11 +331,11 @@ class AuthManagerTestCase(object): self.assertTrue(user.is_admin()) -class AuthManagerLdapTestCase(AuthManagerTestCase, test.TestCase): +class AuthManagerLdapTestCase(_AuthManagerBaseTestCase): auth_driver = 'nova.auth.ldapdriver.FakeLdapDriver' -class AuthManagerDbTestCase(AuthManagerTestCase, test.TestCase): +class AuthManagerDbTestCase(_AuthManagerBaseTestCase): auth_driver = 'nova.auth.dbdriver.DbDriver' diff --git a/nova/virt/driver.py b/nova/virt/driver.py index f85796a97..55281b741 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -243,3 +243,7 @@ class ComputeDriver(object): def inject_network_info(self, instance): """inject network info for specified instance""" raise NotImplementedError() + + def poll_rescued_instances(self, timeout): + """Poll for rescued instances""" + raise NotImplementedError() diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 5962507d7..80eb64f3c 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,67 @@ 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 = {'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', + 'owner_id': instance['project_id'], + 'ramdisk_id': instance['ramdisk_id'], + } + } + + # Make the snapshot + snapshot_name = uuid.uuid4().hex + snapshot_xml = """ + <domainsnapshot> + <name>%s</name> + </domainsnapshot> + """ % 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): @@ -760,11 +822,12 @@ class LibvirtConnection(driver.ComputeDriver): 'dns': network_ref['dns'], 'address_v6': address_v6, 'gateway_v6': network_ref['gateway_v6'], - 'netmask_v6': network_ref['netmask_v6'], - 'use_ipv6': FLAGS.use_ipv6} + 'netmask_v6': network_ref['netmask_v6']} nets.append(net_info) - net = str(Template(ifc_template, searchList=[{'interfaces': nets}])) + net = str(Template(ifc_template, + searchList=[{'interfaces': nets, + 'use_ipv6': FLAGS.use_ipv6}])) if key or net: inst_name = inst['name'] |
