summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/compute/api.py11
-rw-r--r--nova/conductor/api.py10
-rw-r--r--nova/conductor/manager.py1
-rw-r--r--nova/conductor/rpcapi.py8
-rw-r--r--nova/db/api.py11
-rw-r--r--nova/db/sqlalchemy/api.py19
-rw-r--r--nova/network/l3.py2
-rw-r--r--nova/openstack/common/db/sqlalchemy/session.py10
-rw-r--r--nova/openstack/common/importutils.py10
-rw-r--r--nova/openstack/common/jsonutils.py36
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py5
-rw-r--r--nova/tests/conductor/test_conductor.py10
-rw-r--r--nova/tests/fake_libvirt_utils.py4
-rw-r--r--nova/tests/network/test_manager.py38
-rw-r--r--nova/tests/test_libvirt.py25
-rw-r--r--nova/tests/test_virt_drivers.py2
-rwxr-xr-xnova/virt/libvirt/driver.py23
-rwxr-xr-xnova/virt/libvirt/imagebackend.py65
-rw-r--r--nova/virt/libvirt/snapshots.py89
-rwxr-xr-xnova/virt/libvirt/utils.py6
20 files changed, 164 insertions, 221 deletions
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 0119f6602..43b87d6f2 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -1250,8 +1250,8 @@ class API(base.Base):
# search_opts in get_all
def get_active_by_window(self, context, begin, end=None, project_id=None):
"""Get instances that were continuously active over a window."""
- return self.db.instance_get_active_by_window(context, begin, end,
- project_id)
+ return self.db.instance_get_active_by_window_joined(context, begin,
+ end, project_id)
#NOTE(bcwaldon): this doesn't really belong in this class
def get_instance_type(self, context, instance_type_id):
@@ -2953,13 +2953,14 @@ class SecurityGroupAPI(base.Base):
security_groups.add(security_group)
# ..then we find the instances that are members of these groups..
- instances = set()
+ instances = {}
for security_group in security_groups:
for instance in security_group['instances']:
- instances.add(instance)
+ if instance['uuid'] not in instances:
+ instances[instance['uuid']] = instance
# ..then we send a request to refresh the rules for each instance.
- for instance in instances:
+ for instance in instances.values():
if instance['host']:
self.security_group_rpcapi.refresh_instance_security_rules(
context, instance['host'], instance)
diff --git a/nova/conductor/api.py b/nova/conductor/api.py
index a30f8dffa..3b193fff8 100644
--- a/nova/conductor/api.py
+++ b/nova/conductor/api.py
@@ -93,11 +93,6 @@ class LocalAPI(object):
return self._manager.instance_get_all_hung_in_rebooting(context,
timeout)
- def instance_get_active_by_window(self, context, begin, end=None,
- project_id=None, host=None):
- return self._manager.instance_get_active_by_window(
- context, begin, end, project_id, host)
-
def instance_get_active_by_window_joined(self, context, begin, end=None,
project_id=None, host=None):
return self._manager.instance_get_active_by_window_joined(
@@ -384,11 +379,6 @@ class API(object):
return self.conductor_rpcapi.instance_get_all_hung_in_rebooting(
context, timeout)
- def instance_get_active_by_window(self, context, begin, end=None,
- project_id=None, host=None):
- return self.conductor_rpcapi.instance_get_active_by_window(
- context, begin, end, project_id, host)
-
def instance_get_active_by_window_joined(self, context, begin, end=None,
project_id=None, host=None):
return self.conductor_rpcapi.instance_get_active_by_window_joined(
diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py
index b53ab8fcc..0d2031a4a 100644
--- a/nova/conductor/manager.py
+++ b/nova/conductor/manager.py
@@ -239,6 +239,7 @@ class ConductorManager(manager.SchedulerDependentManager):
def instance_get_active_by_window(self, context, begin, end=None,
project_id=None, host=None):
+ # Unused, but cannot remove until major RPC version bump
result = self.db.instance_get_active_by_window(context, begin, end,
project_id, host)
return jsonutils.to_primitive(result)
diff --git a/nova/conductor/rpcapi.py b/nova/conductor/rpcapi.py
index 169da4ed7..f5ce0b4cb 100644
--- a/nova/conductor/rpcapi.py
+++ b/nova/conductor/rpcapi.py
@@ -74,6 +74,7 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy):
1.39 - Added notify_usage_exists
1.40 - Added security_groups_trigger_handler and
security_groups_trigger_members_refresh
+ Remove instance_get_active_by_window
"""
BASE_RPC_API_VERSION = '1.0'
@@ -244,13 +245,6 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy):
timeout=timeout)
return self.call(context, msg, version='1.15')
- def instance_get_active_by_window(self, context, begin, end=None,
- project_id=None, host=None):
- msg = self.make_msg('instance_get_active_by_window',
- begin=begin, end=end, project_id=project_id,
- host=host)
- return self.call(context, msg, version='1.15')
-
def instance_get_active_by_window_joined(self, context, begin, end=None,
project_id=None, host=None):
msg = self.make_msg('instance_get_active_by_window_joined',
diff --git a/nova/db/api.py b/nova/db/api.py
index 8d3f0fa4d..ffd153a46 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -606,17 +606,6 @@ def instance_get_all_by_filters(context, filters, sort_key='created_at',
marker=marker)
-def instance_get_active_by_window(context, begin, end=None, project_id=None,
- host=None):
- """Get instances active during a certain time window.
-
- Specifying a project_id will filter for a certain project.
- Specifying a host will filter for instances on a given compute host.
- """
- return IMPL.instance_get_active_by_window(context, begin, end,
- project_id, host)
-
-
def instance_get_active_by_window_joined(context, begin, end=None,
project_id=None, host=None):
"""Get instances and joins active during a certain time window.
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 874c7cf7b..d0a58e44f 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1656,25 +1656,6 @@ def regex_filter(query, model, filters):
return query
-@require_context
-def instance_get_active_by_window(context, begin, end=None,
- project_id=None, host=None):
- """Return instances that were active during window."""
- session = get_session()
- query = session.query(models.Instance)
-
- query = query.filter(or_(models.Instance.terminated_at == None,
- models.Instance.terminated_at > begin))
- if end:
- query = query.filter(models.Instance.launched_at < end)
- if project_id:
- query = query.filter_by(project_id=project_id)
- if host:
- query = query.filter_by(host=host)
-
- return query.all()
-
-
@require_admin_context
def instance_get_active_by_window_joined(context, begin, end=None,
project_id=None, host=None):
diff --git a/nova/network/l3.py b/nova/network/l3.py
index 14abf41eb..30a9f1ecd 100644
--- a/nova/network/l3.py
+++ b/nova/network/l3.py
@@ -101,9 +101,9 @@ class LinuxNetL3(L3Driver):
def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
network=None):
- linux_net.bind_floating_ip(floating_ip, l3_interface_id)
linux_net.ensure_floating_forward(floating_ip, fixed_ip,
l3_interface_id, network)
+ linux_net.bind_floating_ip(floating_ip, l3_interface_id)
def remove_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
network=None):
diff --git a/nova/openstack/common/db/sqlalchemy/session.py b/nova/openstack/common/db/sqlalchemy/session.py
index 2125b006d..bc889fc36 100644
--- a/nova/openstack/common/db/sqlalchemy/session.py
+++ b/nova/openstack/common/db/sqlalchemy/session.py
@@ -246,12 +246,6 @@ import time
from eventlet import db_pool
from eventlet import greenthread
-try:
- import MySQLdb
- from MySQLdb.constants import CLIENT as mysql_client_constants
-except ImportError:
- MySQLdb = None
- mysql_client_constants = None
from sqlalchemy.exc import DisconnectionError, OperationalError, IntegrityError
import sqlalchemy.interfaces
import sqlalchemy.orm
@@ -259,10 +253,14 @@ from sqlalchemy.pool import NullPool, StaticPool
from sqlalchemy.sql.expression import literal_column
from nova.openstack.common import cfg
+from nova.openstack.common import importutils
from nova.openstack.common import log as logging
from nova.openstack.common.gettextutils import _
from nova.openstack.common import timeutils
+MySQLdb = importutils.try_import('MySQLdb')
+if MySQLdb is not None:
+ from MySQLdb.constants import CLIENT as mysql_client_constants
sql_opts = [
cfg.StrOpt('sql_connection',
diff --git a/nova/openstack/common/importutils.py b/nova/openstack/common/importutils.py
index f45372b4d..9dec764fb 100644
--- a/nova/openstack/common/importutils.py
+++ b/nova/openstack/common/importutils.py
@@ -29,7 +29,7 @@ def import_class(import_str):
try:
__import__(mod_str)
return getattr(sys.modules[mod_str], class_str)
- except (ValueError, AttributeError), exc:
+ except (ValueError, AttributeError):
raise ImportError('Class %s cannot be found (%s)' %
(class_str,
traceback.format_exception(*sys.exc_info())))
@@ -57,3 +57,11 @@ def import_module(import_str):
"""Import a module."""
__import__(import_str)
return sys.modules[import_str]
+
+
+def try_import(import_str, default=None):
+ """Try to import a module and if it fails return default."""
+ try:
+ return import_module(import_str)
+ except ImportError:
+ return default
diff --git a/nova/openstack/common/jsonutils.py b/nova/openstack/common/jsonutils.py
index 5a90d5c5e..290435450 100644
--- a/nova/openstack/common/jsonutils.py
+++ b/nova/openstack/common/jsonutils.py
@@ -34,6 +34,7 @@ This module provides a few things:
import datetime
+import functools
import inspect
import itertools
import json
@@ -42,7 +43,8 @@ import xmlrpclib
from nova.openstack.common import timeutils
-def to_primitive(value, convert_instances=False, level=0):
+def to_primitive(value, convert_instances=False, convert_datetime=True,
+ level=0):
"""Convert a complex object into primitives.
Handy for JSON serialization. We can optionally handle instances,
@@ -84,6 +86,10 @@ def to_primitive(value, convert_instances=False, level=0):
# The try block may not be necessary after the class check above,
# but just in case ...
try:
+ recursive = functools.partial(to_primitive,
+ convert_instances=convert_instances,
+ convert_datetime=convert_datetime,
+ level=level)
# It's not clear why xmlrpclib created their own DateTime type, but
# for our purposes, make it a datetime type which is explicitly
# handled
@@ -91,36 +97,22 @@ def to_primitive(value, convert_instances=False, level=0):
value = datetime.datetime(*tuple(value.timetuple())[:6])
if isinstance(value, (list, tuple)):
- o = []
- for v in value:
- o.append(to_primitive(v, convert_instances=convert_instances,
- level=level))
- return o
+ return [recursive(v) for v in value]
elif isinstance(value, dict):
- o = {}
- for k, v in value.iteritems():
- o[k] = to_primitive(v, convert_instances=convert_instances,
- level=level)
- return o
- elif isinstance(value, datetime.datetime):
+ return dict((k, recursive(v)) for k, v in value.iteritems())
+ elif convert_datetime and isinstance(value, datetime.datetime):
return timeutils.strtime(value)
elif hasattr(value, 'iteritems'):
- return to_primitive(dict(value.iteritems()),
- convert_instances=convert_instances,
- level=level + 1)
+ return recursive(dict(value.iteritems()), level=level + 1)
elif hasattr(value, '__iter__'):
- return to_primitive(list(value),
- convert_instances=convert_instances,
- level=level)
+ return recursive(list(value))
elif convert_instances and hasattr(value, '__dict__'):
# Likely an instance of something. Watch for cycles.
# Ignore class member vars.
- return to_primitive(value.__dict__,
- convert_instances=convert_instances,
- level=level + 1)
+ return recursive(value.__dict__, level=level + 1)
else:
return value
- except TypeError, e:
+ except TypeError:
# Class objects are tricky since they may define something like
# __iter__ defined but it isn't callable as list().
return unicode(value)
diff --git a/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py b/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py
index 13a4e9d61..440c97fbd 100644
--- a/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py
+++ b/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py
@@ -65,7 +65,8 @@ def get_fake_db_instance(start, end, instance_id, tenant_id):
'terminated_at': end}
-def fake_instance_get_active_by_window(self, context, begin, end, project_id):
+def fake_instance_get_active_by_window_joined(self, context, begin, end,
+ project_id):
return [get_fake_db_instance(START,
STOP,
x,
@@ -79,7 +80,7 @@ class SimpleTenantUsageTest(test.TestCase):
self.stubs.Set(api.API, "get_instance_type",
fake_instance_type_get)
self.stubs.Set(api.API, "get_active_by_window",
- fake_instance_get_active_by_window)
+ fake_instance_get_active_by_window_joined)
self.admin_context = context.RequestContext('fakeadmin_0',
'faketenant_0',
is_admin=True)
diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py
index 08598b756..bd73328e8 100644
--- a/nova/tests/conductor/test_conductor.py
+++ b/nova/tests/conductor/test_conductor.py
@@ -335,16 +335,6 @@ class _BaseTestCase(object):
self.mox.ReplayAll()
self.conductor.instance_get_all_hung_in_rebooting(self.context, 123)
- def test_instance_get_active_by_window(self):
- self.mox.StubOutWithMock(db, 'instance_get_active_by_window_joined')
- db.instance_get_active_by_window(self.context, 'fake-begin',
- 'fake-end', 'fake-proj',
- 'fake-host')
- self.mox.ReplayAll()
- self.conductor.instance_get_active_by_window(self.context,
- 'fake-begin', 'fake-end',
- 'fake-proj', 'fake-host')
-
def test_instance_get_active_by_window_joined(self):
self.mox.StubOutWithMock(db, 'instance_get_active_by_window_joined')
db.instance_get_active_by_window_joined(self.context, 'fake-begin',
diff --git a/nova/tests/fake_libvirt_utils.py b/nova/tests/fake_libvirt_utils.py
index b3d842468..285a4b7e3 100644
--- a/nova/tests/fake_libvirt_utils.py
+++ b/nova/tests/fake_libvirt_utils.py
@@ -144,3 +144,7 @@ def fetch_image(context, target, image_id, user_id, project_id):
def get_instance_path(instance):
# TODO(mikal): we should really just call the real one here
return os.path.join(CONF.instances_path, instance['name'])
+
+
+def pick_disk_driver_name(is_block_dev=False):
+ return "qemu"
diff --git a/nova/tests/network/test_manager.py b/nova/tests/network/test_manager.py
index 0dcd02f78..c0a434e6c 100644
--- a/nova/tests/network/test_manager.py
+++ b/nova/tests/network/test_manager.py
@@ -726,7 +726,7 @@ class VlanNetworkTestCase(test.TestCase):
'floating_ip_fixed_ip_associate',
fake1)
self.stubs.Set(self.network.db, 'floating_ip_disassociate', fake1)
- self.stubs.Set(self.network.driver, 'bind_floating_ip', fake8)
+ self.stubs.Set(self.network.driver, 'ensure_floating_forward', fake8)
self.assertRaises(exception.NoFloatingIpInterface,
self.network._associate_floating_ip,
ctxt,
@@ -776,6 +776,42 @@ class VlanNetworkTestCase(test.TestCase):
mox.IgnoreArg())
self.assertTrue(self.local)
+ def test_add_floating_ip_nat_before_bind(self):
+ # Tried to verify order with documented mox record/verify
+ # functionality, but it doesn't seem to work since I can't make it
+ # fail. I'm using stubs and a flag for now, but if this mox feature
+ # can be made to work, it would be a better way to test this.
+ #
+ # self.mox.StubOutWithMock(self.network.driver,
+ # 'ensure_floating_forward')
+ # self.mox.StubOutWithMock(self.network.driver, 'bind_floating_ip')
+ #
+ # self.network.driver.ensure_floating_forward(mox.IgnoreArg(),
+ # mox.IgnoreArg(),
+ # mox.IgnoreArg(),
+ # mox.IgnoreArg())
+ # self.network.driver.bind_floating_ip(mox.IgnoreArg(),
+ # mox.IgnoreArg())
+ # self.mox.ReplayAll()
+
+ nat_called = [False]
+
+ def fake_nat(*args, **kwargs):
+ nat_called[0] = True
+
+ def fake_bind(*args, **kwargs):
+ self.assertTrue(nat_called[0])
+
+ self.stubs.Set(self.network.driver,
+ 'ensure_floating_forward',
+ fake_nat)
+ self.stubs.Set(self.network.driver, 'bind_floating_ip', fake_bind)
+
+ self.network.l3driver.add_floating_ip('fakefloat',
+ 'fakefixed',
+ 'fakeiface',
+ 'fakenet')
+
def test_floating_ip_init_host(self):
def get_all_by_host(_context, _host):
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index 4b9a4eb7c..c93bb0168 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -273,7 +273,7 @@ class LibvirtConnTestCase(test.TestCase):
'nova.virt.libvirt.driver.libvirt_utils',
fake_libvirt_utils))
self.useFixture(fixtures.MonkeyPatch(
- 'nova.virt.libvirt.snapshots.libvirt_utils',
+ 'nova.virt.libvirt.imagebackend.libvirt_utils',
fake_libvirt_utils))
def fake_extend(image, size):
@@ -433,6 +433,29 @@ class LibvirtConnTestCase(test.TestCase):
self.assertEquals(type(cfg.devices[7]),
vconfig.LibvirtConfigGuestGraphics)
+ def test_get_guest_config_bug_1118829(self):
+ self.flags(libvirt_type='uml')
+ conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
+ instance_ref = db.instance_create(self.context, self.test_instance)
+
+ disk_info = {'disk_bus': 'virtio',
+ 'cdrom_bus': 'ide',
+ 'mapping': {u'vda': {'bus': 'virtio',
+ 'type': 'disk',
+ 'dev': u'vda'},
+ 'root': {'bus': 'virtio',
+ 'type': 'disk',
+ 'dev': 'vda'}}}
+
+ # NOTE(jdg): For this specific test leave this blank
+ # This will exercise the failed code path still,
+ # and won't require fakes and stubs of the iscsi discovery
+ block_device_info = {}
+ cfg = conn.get_guest_config(instance_ref, [], None, disk_info,
+ None, block_device_info)
+ instance_ref = db.instance_get(self.context, instance_ref['id'])
+ self.assertEquals(instance_ref['root_device_name'], '/dev/vda')
+
def test_get_guest_config_with_root_device_name(self):
self.flags(libvirt_type='uml')
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py
index 67452bc24..a94fdc3c5 100644
--- a/nova/tests/test_virt_drivers.py
+++ b/nova/tests/test_virt_drivers.py
@@ -84,7 +84,7 @@ class _FakeDriverBackendTestCase(object):
'nova.virt.libvirt.driver.libvirt_utils',
fake_libvirt_utils))
self.useFixture(fixtures.MonkeyPatch(
- 'nova.virt.libvirt.snapshots.libvirt_utils',
+ 'nova.virt.libvirt.imagebackend.libvirt_utils',
fake_libvirt_utils))
self.useFixture(fixtures.MonkeyPatch(
'nova.virt.libvirt.firewall.libvirt',
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index cf926a14c..485f661e5 100755
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -116,8 +116,8 @@ libvirt_opts = [
cfg.IntOpt('libvirt_inject_partition',
default=1,
help='The partition to inject to : '
- '-1 => inspect (libguestfs only), 0 => not partitioned, '
- '>0 => partition number'),
+ '-2 => disable, -1 => inspect (libguestfs only), '
+ '0 => not partitioned, >0 => partition number'),
cfg.BoolOpt('use_usb_tablet',
default=True,
help='Sync virtual and real mouse cursors in Windows VMs'),
@@ -827,7 +827,8 @@ class LibvirtDriver(driver.ComputeDriver):
# can be validated.
if self.has_min_version(MIN_LIBVIRT_LIVESNAPSHOT_VERSION,
MIN_QEMU_LIVESNAPSHOT_VERSION,
- REQ_HYPERVISOR_LIVESNAPSHOT):
+ REQ_HYPERVISOR_LIVESNAPSHOT) \
+ and not source_format == "lvm":
live_snapshot = True
else:
live_snapshot = False
@@ -843,15 +844,17 @@ class LibvirtDriver(driver.ComputeDriver):
if state == power_state.RUNNING or state == power_state.PAUSED:
virt_dom.managedSave(0)
+ snapshot_backend = self.image_backend.snapshot(disk_path,
+ snapshot_name,
+ image_type=source_format)
+
if live_snapshot:
LOG.info(_("Beginning live snapshot process"),
instance=instance)
else:
LOG.info(_("Beginning cold snapshot process"),
instance=instance)
- snapshot = self.image_backend.snapshot(disk_path, snapshot_name,
- image_type=source_format)
- snapshot.create()
+ snapshot_backend.snapshot_create()
update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
snapshot_directory = CONF.libvirt_snapshots_directory
@@ -866,10 +869,10 @@ class LibvirtDriver(driver.ComputeDriver):
self._live_snapshot(virt_dom, disk_path, out_path,
image_format)
else:
- snapshot.extract(out_path, image_format)
+ snapshot_backend.snapshot_extract(out_path, image_format)
finally:
if not live_snapshot:
- snapshot.delete()
+ snapshot_backend.snapshot_delete()
# NOTE(dkang): because previous managedSave is not called
# for LXC, _create_domain must not be called.
if CONF.libvirt_type != 'lxc' and not live_snapshot:
@@ -1534,7 +1537,7 @@ class LibvirtDriver(driver.ComputeDriver):
raise
# File injection
- else:
+ elif CONF.libvirt_inject_partition != -2:
target_partition = None
if not instance['kernel_id']:
target_partition = CONF.libvirt_inject_partition
@@ -1798,7 +1801,7 @@ class LibvirtDriver(driver.ComputeDriver):
# for nova.api.ec2.cloud.CloudController.get_metadata()
self.virtapi.instance_update(
nova_context.get_admin_context(), instance['uuid'],
- {'root_device_name': '/dev/' + disk_mapping['disk']['dev']})
+ {'root_device_name': root_device_name})
guest.os_type = vm_mode.get_from_instance(instance)
diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py
index 0d5e8cb66..74148a866 100755
--- a/nova/virt/libvirt/imagebackend.py
+++ b/nova/virt/libvirt/imagebackend.py
@@ -25,8 +25,8 @@ from nova.openstack.common import fileutils
from nova.openstack.common import lockutils
from nova import utils
from nova.virt.disk import api as disk
+from nova.virt import images
from nova.virt.libvirt import config as vconfig
-from nova.virt.libvirt import snapshots
from nova.virt.libvirt import utils as libvirt_utils
__imagebackend_opts = [
@@ -129,22 +129,25 @@ class Image(object):
self.create_image(call_if_not_exists, base, size,
*args, **kwargs)
- @abc.abstractmethod
- def snapshot(self, name):
- """Create snapshot object for this image
+ def snapshot_create(self):
+ raise NotImplementedError
- :name: snapshot name
- """
- pass
+ def snapshot_extract(self, target, out_format):
+ raise NotImplementedError
+
+ def snapshot_delete(self):
+ raise NotImplementedError
class Raw(Image):
- def __init__(self, instance=None, name=None, path=None):
+ def __init__(self, instance=None, disk_name=None, path=None,
+ snapshot_name=None):
super(Raw, self).__init__("file", "raw", is_block_dev=False)
self.path = (path or
os.path.join(libvirt_utils.get_instance_path(instance),
- name))
+ disk_name))
+ self.snapshot_name = snapshot_name
def create_image(self, prepare_template, base, size, *args, **kwargs):
@lockutils.synchronized(base, 'nova-', external=True,
@@ -164,17 +167,25 @@ class Raw(Image):
with utils.remove_path_on_error(self.path):
copy_raw_image(base, self.path, size)
- def snapshot(self, name):
- return snapshots.RawSnapshot(self.path, name)
+ def snapshot_create(self):
+ pass
+
+ def snapshot_extract(self, target, out_format):
+ images.convert_image(self.path, target, out_format)
+
+ def snapshot_delete(self):
+ pass
class Qcow2(Image):
- def __init__(self, instance=None, name=None, path=None):
+ def __init__(self, instance=None, disk_name=None, path=None,
+ snapshot_name=None):
super(Qcow2, self).__init__("file", "qcow2", is_block_dev=False)
self.path = (path or
os.path.join(libvirt_utils.get_instance_path(instance),
- name))
+ disk_name))
+ self.snapshot_name = snapshot_name
def create_image(self, prepare_template, base, size, *args, **kwargs):
@lockutils.synchronized(base, 'nova-', external=True,
@@ -190,8 +201,16 @@ class Qcow2(Image):
with utils.remove_path_on_error(self.path):
copy_qcow2_image(base, self.path, size)
- def snapshot(self, name):
- return snapshots.Qcow2Snapshot(self.path, name)
+ def snapshot_create(self):
+ libvirt_utils.create_snapshot(self.path, self.snapshot_name)
+
+ def snapshot_extract(self, target, out_format):
+ libvirt_utils.extract_snapshot(self.path, 'qcow2',
+ self.snapshot_name, target,
+ out_format)
+
+ def snapshot_delete(self):
+ libvirt_utils.delete_snapshot(self.path, self.snapshot_name)
class Lvm(Image):
@@ -199,7 +218,8 @@ class Lvm(Image):
def escape(filename):
return filename.replace('_', '__')
- def __init__(self, instance=None, name=None, path=None):
+ def __init__(self, instance=None, disk_name=None, path=None,
+ snapshot_name=None):
super(Lvm, self).__init__("block", "raw", is_block_dev=True)
if path:
@@ -214,7 +234,7 @@ class Lvm(Image):
' flag to use LVM images.'))
self.vg = CONF.libvirt_images_volume_group
self.lv = '%s_%s' % (self.escape(instance['name']),
- self.escape(name))
+ self.escape(disk_name))
self.path = os.path.join('/dev', self.vg, self.lv)
self.sparse = CONF.libvirt_sparse_logical_volumes
@@ -254,9 +274,6 @@ class Lvm(Image):
with excutils.save_and_reraise_exception():
libvirt_utils.remove_logical_volumes(path)
- def snapshot(self, name):
- return snapshots.LvmSnapshot(self.path, name)
-
class Backend(object):
def __init__(self, use_cow):
@@ -275,7 +292,7 @@ class Backend(object):
raise RuntimeError(_('Unknown image_type=%s') % image_type)
return image
- def image(self, instance, name, image_type=None):
+ def image(self, instance, disk_name, image_type=None):
"""Constructs image for selected backend
:instance: Instance name.
@@ -284,9 +301,9 @@ class Backend(object):
Optional, is CONF.libvirt_images_type by default.
"""
backend = self.backend(image_type)
- return backend(instance=instance, name=name)
+ return backend(instance=instance, disk_name=disk_name)
- def snapshot(self, path, snapshot_name, image_type=None):
+ def snapshot(self, disk_path, snapshot_name, image_type=None):
"""Returns snapshot for given image
:path: path to image
@@ -294,4 +311,4 @@ class Backend(object):
:image_type: type of image
"""
backend = self.backend(image_type)
- return backend(path=path).snapshot(snapshot_name)
+ return backend(path=disk_path, snapshot_name=snapshot_name)
diff --git a/nova/virt/libvirt/snapshots.py b/nova/virt/libvirt/snapshots.py
deleted file mode 100644
index c85550eae..000000000
--- a/nova/virt/libvirt/snapshots.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 Grid Dynamics
-# All Rights Reserved.
-#
-# 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.
-
-import abc
-
-from nova.virt import images
-from nova.virt.libvirt import utils as libvirt_utils
-
-
-class Snapshot(object):
- @abc.abstractmethod
- def create(self):
- """Create new snapshot."""
- pass
-
- @abc.abstractmethod
- def extract(self, target, out_format):
- """Extract snapshot content to file
-
- :target: path to extraction
- :out_format: format of extraction (raw, qcow2, ...)
- """
- pass
-
- @abc.abstractmethod
- def delete(self):
- """Delete snapshot."""
- pass
-
-
-class RawSnapshot(object):
- def __init__(self, path, name):
- self.path = path
- self.name = name
-
- def create(self):
- pass
-
- def extract(self, target, out_format):
- images.convert_image(self.path, target, out_format)
-
- def delete(self):
- pass
-
-
-class Qcow2Snapshot(object):
- def __init__(self, path, name):
- self.path = path
- self.name = name
-
- def create(self):
- libvirt_utils.create_snapshot(self.path, self.name)
-
- def extract(self, target, out_format):
- libvirt_utils.extract_snapshot(self.path, 'qcow2',
- self.name, target,
- out_format)
-
- def delete(self):
- libvirt_utils.delete_snapshot(self.path, self.name)
-
-
-class LvmSnapshot(object):
- def __init__(self, path, name):
- self.path = path
- self.name = name
-
- def create(self):
- raise NotImplementedError(_("LVM snapshots not implemented"))
-
- def extract(self, target, out_format):
- raise NotImplementedError(_("LVM snapshots not implemented"))
-
- def delete(self):
- raise NotImplementedError(_("LVM snapshots not implemented"))
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
index 1830cac33..b8e0cafec 100755
--- a/nova/virt/libvirt/utils.py
+++ b/nova/virt/libvirt/utils.py
@@ -237,6 +237,7 @@ def clear_logical_volume(path):
vol_size = logical_volume_size(path)
bs = 1024 * 1024
direct_flags = ('oflag=direct',)
+ sync_flags = ()
remaining_bytes = vol_size
# The loop caters for versions of dd that
@@ -248,11 +249,14 @@ def clear_logical_volume(path):
'if=/dev/zero', 'of=%s' % path,
'seek=%s' % seek_blocks, 'count=%s' % zero_blocks)
zero_cmd += direct_flags
+ zero_cmd += sync_flags
if zero_blocks:
utils.execute(*zero_cmd, run_as_root=True)
remaining_bytes %= bs
bs /= 1024 # Limit to 3 iterations
- direct_flags = () # Only use O_DIRECT with initial block size
+ # Use O_DIRECT with initial block size and fdatasync otherwise
+ direct_flags = ()
+ sync_flags = ('conv=fdatasync',)
def remove_logical_volumes(*paths):