summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/source/devref/threading.rst2
-rw-r--r--nova/api/openstack/compute/contrib/cloudpipe.py3
-rw-r--r--nova/cloudpipe/pipelib.py3
-rw-r--r--nova/compute/manager.py7
-rw-r--r--nova/compute/resource_tracker.py11
-rw-r--r--nova/crypto.py5
-rw-r--r--nova/flags.py3
-rw-r--r--nova/network/linux_net.py16
-rw-r--r--nova/network/manager.py3
-rw-r--r--nova/objectstore/s3server.py7
-rw-r--r--nova/openstack/common/fileutils.py35
-rw-r--r--nova/openstack/common/lockutils.py233
-rw-r--r--nova/tests/network/test_linux_net.py33
-rw-r--r--nova/tests/test_imagebackend.py13
-rw-r--r--nova/tests/test_libvirt.py3
-rw-r--r--nova/tests/test_misc.py94
-rw-r--r--nova/tests/test_utils.py37
-rw-r--r--nova/utils.py192
-rw-r--r--nova/virt/baremetal/driver.py10
-rw-r--r--nova/virt/configdrive.py3
-rw-r--r--nova/virt/firewall.py6
-rw-r--r--nova/virt/hyperv/vmops.py4
-rw-r--r--nova/virt/libvirt/driver.py7
-rw-r--r--nova/virt/libvirt/imagebackend.py16
-rw-r--r--nova/virt/libvirt/utils.py3
-rw-r--r--nova/virt/libvirt/volume.py5
-rw-r--r--nova/volume/iscsi.py3
-rw-r--r--openstack-common.conf2
28 files changed, 364 insertions, 395 deletions
diff --git a/doc/source/devref/threading.rst b/doc/source/devref/threading.rst
index 356ca2f6f..f1ab0cc06 100644
--- a/doc/source/devref/threading.rst
+++ b/doc/source/devref/threading.rst
@@ -12,7 +12,7 @@ view, each OpenStack service runs in a single thread.
The use of green threads reduces the likelihood of race conditions, but does
not completely eliminate them. In some cases, you may need to use the
-``@utils.synchronized(...)`` decorator to avoid races.
+``@lockutils.synchronized(...)`` decorator to avoid races.
In addition, since there is only one operating system thread, a call that
blocks that main thread will block the entire process.
diff --git a/nova/api/openstack/compute/contrib/cloudpipe.py b/nova/api/openstack/compute/contrib/cloudpipe.py
index 48b641348..afc24b95d 100644
--- a/nova/api/openstack/compute/contrib/cloudpipe.py
+++ b/nova/api/openstack/compute/contrib/cloudpipe.py
@@ -25,6 +25,7 @@ from nova import db
from nova import exception
from nova import flags
from nova import network
+from nova.openstack.common import fileutils
from nova.openstack.common import log as logging
from nova.openstack.common import timeutils
from nova import utils
@@ -69,7 +70,7 @@ class CloudpipeController(object):
# NOTE(vish): One of the drawbacks of doing this in the api is
# the keys will only be on the api node that launched
# the cloudpipe.
- utils.ensure_tree(FLAGS.keys_path)
+ fileutils.ensure_tree(FLAGS.keys_path)
def _get_all_cloudpipes(self, context):
"""Get all cloudpipes"""
diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py
index 206ce1835..9d83862a4 100644
--- a/nova/cloudpipe/pipelib.py
+++ b/nova/cloudpipe/pipelib.py
@@ -33,6 +33,7 @@ from nova import db
from nova import exception
from nova import flags
from nova.openstack.common import cfg
+from nova.openstack.common import fileutils
from nova.openstack.common import log as logging
from nova import utils
@@ -151,7 +152,7 @@ class CloudPipe(object):
key_name)
private_key = result['private_key']
key_dir = os.path.join(FLAGS.keys_path, context.user_id)
- utils.ensure_tree(key_dir)
+ fileutils.ensure_tree(key_dir)
key_path = os.path.join(key_dir, '%s.pem' % key_name)
with open(key_path, 'w') as f:
f.write(private_key)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index a988246f1..300b2dfeb 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -64,6 +64,7 @@ from nova.openstack.common import cfg
from nova.openstack.common import excutils
from nova.openstack.common import importutils
from nova.openstack.common import jsonutils
+from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova.openstack.common.notifier import api as notifier
from nova.openstack.common import rpc
@@ -848,7 +849,7 @@ class ComputeManager(manager.SchedulerDependentManager):
if injected_files is None:
injected_files = []
- @utils.synchronized(instance['uuid'])
+ @lockutils.synchronized(instance['uuid'], 'nova-')
def do_run_instance():
self._run_instance(context, request_spec,
filter_properties, requested_networks, injected_files,
@@ -958,7 +959,7 @@ class ComputeManager(manager.SchedulerDependentManager):
if not bdms:
bdms = self._get_instance_volume_bdms(context, instance["uuid"])
- @utils.synchronized(instance['uuid'])
+ @lockutils.synchronized(instance['uuid'], 'nova-')
def do_terminate_instance(instance, bdms):
try:
self._delete_instance(context, instance, bdms)
@@ -2031,7 +2032,7 @@ class ComputeManager(manager.SchedulerDependentManager):
@wrap_instance_fault
def reserve_block_device_name(self, context, instance, device, volume_id):
- @utils.synchronized(instance['uuid'])
+ @lockutils.synchronized(instance['uuid'], 'nova-')
def do_reserve():
result = compute_utils.get_device_name_for_instance(context,
instance,
diff --git a/nova/compute/resource_tracker.py b/nova/compute/resource_tracker.py
index 3105b3d6e..7acaa3dc1 100644
--- a/nova/compute/resource_tracker.py
+++ b/nova/compute/resource_tracker.py
@@ -27,6 +27,7 @@ from nova import notifications
from nova.openstack.common import cfg
from nova.openstack.common import importutils
from nova.openstack.common import jsonutils
+from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova import utils
@@ -121,7 +122,7 @@ class ResourceTracker(object):
claim = self.begin_resource_claim(context, instance_ref, limits)
return ResourceContextManager(context, claim, self)
- @utils.synchronized(COMPUTE_RESOURCE_SEMAPHORE)
+ @lockutils.synchronized(COMPUTE_RESOURCE_SEMAPHORE, 'nova-')
def begin_resource_claim(self, context, instance_ref, limits=None):
"""Indicate that some resources are needed for an upcoming compute
instance build operation.
@@ -293,7 +294,7 @@ class ResourceTracker(object):
return can_claim_cpu
- @utils.synchronized(COMPUTE_RESOURCE_SEMAPHORE)
+ @lockutils.synchronized(COMPUTE_RESOURCE_SEMAPHORE, 'nova-')
def finish_resource_claim(self, claim):
"""Indicate that the compute operation that previously claimed the
resources identified by 'claim' has now completed and the resources
@@ -308,7 +309,7 @@ class ResourceTracker(object):
if self.claims.pop(claim.claim_id, None):
LOG.debug(_("Finishing claim: %s") % claim)
- @utils.synchronized(COMPUTE_RESOURCE_SEMAPHORE)
+ @lockutils.synchronized(COMPUTE_RESOURCE_SEMAPHORE, 'nova-')
def abort_resource_claim(self, context, claim):
"""Indicate that the operation that claimed the resources identified by
'claim_id' has either failed or been aborted and the resources are no
@@ -328,7 +329,7 @@ class ResourceTracker(object):
self._update_usage_from_instance(self.compute_node, claim.instance)
self._update(context, self.compute_node)
- @utils.synchronized(COMPUTE_RESOURCE_SEMAPHORE)
+ @lockutils.synchronized(COMPUTE_RESOURCE_SEMAPHORE, 'nova-')
def update_usage(self, context, instance):
"""Update the resource usage and stats after a change in an
instance
@@ -347,7 +348,7 @@ class ResourceTracker(object):
def disabled(self):
return self.compute_node is None
- @utils.synchronized(COMPUTE_RESOURCE_SEMAPHORE)
+ @lockutils.synchronized(COMPUTE_RESOURCE_SEMAPHORE, 'nova-')
def update_available_resource(self, context):
"""Override in-memory calculations of compute node resource usage based
on data audited from the hypervisor layer.
diff --git a/nova/crypto.py b/nova/crypto.py
index cd2080c1a..0fe9da96f 100644
--- a/nova/crypto.py
+++ b/nova/crypto.py
@@ -33,6 +33,7 @@ from nova import db
from nova import exception
from nova import flags
from nova.openstack.common import cfg
+from nova.openstack.common import fileutils
from nova.openstack.common import log as logging
from nova.openstack.common import timeutils
from nova import utils
@@ -112,7 +113,7 @@ def ensure_ca_filesystem():
'genrootca.sh')
start = os.getcwd()
- utils.ensure_tree(ca_dir)
+ fileutils.ensure_tree(ca_dir)
os.chdir(ca_dir)
utils.execute("sh", genrootca_sh_path)
os.chdir(start)
@@ -301,7 +302,7 @@ def _sign_csr(csr_text, ca_folder):
start = os.getcwd()
# Change working dir to CA
- utils.ensure_tree(ca_folder)
+ fileutils.ensure_tree(ca_folder)
os.chdir(ca_folder)
utils.execute('openssl', 'ca', '-batch', '-out', outbound, '-config',
'./openssl.cnf', '-infiles', inbound)
diff --git a/nova/flags.py b/nova/flags.py
index d36284961..3927e8f88 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -91,9 +91,6 @@ core_opts = [
cfg.StrOpt('state_path',
default='$pybasedir',
help="Top-level directory for maintaining nova's state"),
- cfg.StrOpt('lock_path',
- default='$pybasedir',
- help='Directory to use for lock files'),
]
debug_opts = [
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index f991b3659..66156914c 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -28,7 +28,9 @@ from nova import db
from nova import exception
from nova import flags
from nova.openstack.common import cfg
+from nova.openstack.common import fileutils
from nova.openstack.common import importutils
+from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova import utils
@@ -344,7 +346,7 @@ class IptablesManager(object):
self._apply()
- @utils.synchronized('iptables', external=True)
+ @lockutils.synchronized('iptables', 'nova-', external=True)
def _apply(self):
"""Apply the current in-memory set of iptables rules.
@@ -791,7 +793,7 @@ def kill_dhcp(dev):
# NOTE(ja): Sending a HUP only reloads the hostfile, so any
# configuration options (like dchp-range, vlan, ...)
# aren't reloaded.
-@utils.synchronized('dnsmasq_start')
+@lockutils.synchronized('dnsmasq_start', 'nova-')
def restart_dhcp(context, dev, network_ref):
"""(Re)starts a dnsmasq server for a given network.
@@ -858,7 +860,7 @@ def restart_dhcp(context, dev, network_ref):
_add_dnsmasq_accept_rules(dev)
-@utils.synchronized('radvd_start')
+@lockutils.synchronized('radvd_start', 'nova-')
def update_ra(context, dev, network_ref):
conffile = _ra_file(dev, 'conf')
conf_str = """
@@ -957,7 +959,7 @@ def _device_exists(device):
def _dhcp_file(dev, kind):
"""Return path to a pid, leases or conf file for a bridge/device."""
- utils.ensure_tree(FLAGS.networks_path)
+ fileutils.ensure_tree(FLAGS.networks_path)
return os.path.abspath('%s/nova-%s.%s' % (FLAGS.networks_path,
dev,
kind))
@@ -965,7 +967,7 @@ def _dhcp_file(dev, kind):
def _ra_file(dev, kind):
"""Return path to a pid or conf file for a bridge/device."""
- utils.ensure_tree(FLAGS.networks_path)
+ fileutils.ensure_tree(FLAGS.networks_path)
return os.path.abspath('%s/nova-ra-%s.%s' % (FLAGS.networks_path,
dev,
kind))
@@ -1116,7 +1118,7 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
return interface
@classmethod
- @utils.synchronized('ensure_vlan', external=True)
+ @lockutils.synchronized('ensure_vlan', 'nova-', external=True)
def ensure_vlan(_self, vlan_num, bridge_interface, mac_address=None):
"""Create a vlan unless it already exists."""
interface = 'vlan%s' % vlan_num
@@ -1141,7 +1143,7 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
return interface
@classmethod
- @utils.synchronized('ensure_bridge', external=True)
+ @lockutils.synchronized('ensure_bridge', 'nova-', external=True)
def ensure_bridge(_self, bridge, interface, net_attrs=None, gateway=True):
"""Create a bridge unless it already exists.
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 06380a97f..4e20be5a4 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -66,6 +66,7 @@ from nova.openstack.common import cfg
from nova.openstack.common import excutils
from nova.openstack.common import importutils
from nova.openstack.common import jsonutils
+from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova.openstack.common.notifier import api as notifier
from nova.openstack.common import rpc
@@ -854,7 +855,7 @@ class NetworkManager(manager.SchedulerDependentManager):
def _import_ipam_lib(self, ipam_lib):
self.ipam = importutils.import_module(ipam_lib).get_ipam_lib(self)
- @utils.synchronized('get_dhcp')
+ @lockutils.synchronized('get_dhcp', 'nova-')
def _get_dhcp_ip(self, context, network_ref, host=None):
"""Get the proper dhcp address to listen on."""
# NOTE(vish): this is for compatibility
diff --git a/nova/objectstore/s3server.py b/nova/objectstore/s3server.py
index b98a2024f..dfc2cc972 100644
--- a/nova/objectstore/s3server.py
+++ b/nova/objectstore/s3server.py
@@ -46,6 +46,7 @@ import webob
from nova import flags
from nova.openstack.common import cfg
+from nova.openstack.common import fileutils
from nova import utils
from nova import wsgi
@@ -93,7 +94,7 @@ class S3Application(wsgi.Router):
mapper.connect('/{bucket_name}/',
controller=lambda *a, **kw: BucketHandler(self)(*a, **kw))
self.directory = os.path.abspath(root_directory)
- utils.ensure_tree(self.directory)
+ fileutils.ensure_tree(self.directory)
self.bucket_depth = bucket_depth
super(S3Application, self).__init__(mapper)
@@ -285,7 +286,7 @@ class BucketHandler(BaseRequestHandler):
os.path.exists(path)):
self.set_status(403)
return
- utils.ensure_tree(path)
+ fileutils.ensure_tree(path)
self.finish()
def delete(self, bucket_name):
@@ -334,7 +335,7 @@ class ObjectHandler(BaseRequestHandler):
self.set_status(403)
return
directory = os.path.dirname(path)
- utils.ensure_tree(directory)
+ fileutils.ensure_tree(directory)
object_file = open(path, "w")
object_file.write(self.request.body)
object_file.close()
diff --git a/nova/openstack/common/fileutils.py b/nova/openstack/common/fileutils.py
new file mode 100644
index 000000000..4746ad498
--- /dev/null
+++ b/nova/openstack/common/fileutils.py
@@ -0,0 +1,35 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+# 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 errno
+import os
+
+
+def ensure_tree(path):
+ """Create a directory (and any ancestor directories required)
+
+ :param path: Directory to create
+ """
+ try:
+ os.makedirs(path)
+ except OSError as exc:
+ if exc.errno == errno.EEXIST:
+ if not os.path.isdir(path):
+ raise
+ else:
+ raise
diff --git a/nova/openstack/common/lockutils.py b/nova/openstack/common/lockutils.py
new file mode 100644
index 000000000..2840ce6f7
--- /dev/null
+++ b/nova/openstack/common/lockutils.py
@@ -0,0 +1,233 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 OpenStack LLC.
+# 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 errno
+import functools
+import os
+import shutil
+import tempfile
+import time
+import weakref
+
+from eventlet import greenthread
+from eventlet import semaphore
+
+from nova.openstack.common import cfg
+from nova.openstack.common import fileutils
+from nova.openstack.common import log as logging
+
+
+LOG = logging.getLogger(__name__)
+
+
+util_opts = [
+ cfg.BoolOpt('disable_process_locking', default=False,
+ help='Whether to disable inter-process locks'),
+ cfg.StrOpt('lock_path',
+ default=os.path.abspath(os.path.join(os.path.dirname(__file__),
+ '../')),
+ help='Directory to use for lock files')
+]
+
+
+CONF = cfg.CONF
+CONF.register_opts(util_opts)
+
+
+class _InterProcessLock(object):
+ """Lock implementation which allows multiple locks, working around
+ issues like bugs.debian.org/cgi-bin/bugreport.cgi?bug=632857 and does
+ not require any cleanup. Since the lock is always held on a file
+ descriptor rather than outside of the process, the lock gets dropped
+ automatically if the process crashes, even if __exit__ is not executed.
+
+ There are no guarantees regarding usage by multiple green threads in a
+ single process here. This lock works only between processes. Exclusive
+ access between local threads should be achieved using the semaphores
+ in the @synchronized decorator.
+
+ Note these locks are released when the descriptor is closed, so it's not
+ safe to close the file descriptor while another green thread holds the
+ lock. Just opening and closing the lock file can break synchronisation,
+ so lock files must be accessed only using this abstraction.
+ """
+
+ def __init__(self, name):
+ self.lockfile = None
+ self.fname = name
+
+ def __enter__(self):
+ self.lockfile = open(self.fname, 'w')
+
+ while True:
+ try:
+ # Using non-blocking locks since green threads are not
+ # patched to deal with blocking locking calls.
+ # Also upon reading the MSDN docs for locking(), it seems
+ # to have a laughable 10 attempts "blocking" mechanism.
+ self.trylock()
+ return self
+ except IOError, e:
+ if e.errno in (errno.EACCES, errno.EAGAIN):
+ # external locks synchronise things like iptables
+ # updates - give it some time to prevent busy spinning
+ time.sleep(0.01)
+ else:
+ raise
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ try:
+ self.unlock()
+ self.lockfile.close()
+ except IOError:
+ LOG.exception(_("Could not release the acquired lock `%s`"),
+ self.fname)
+
+ def trylock(self):
+ raise NotImplementedError()
+
+ def unlock(self):
+ raise NotImplementedError()
+
+
+class _WindowsLock(_InterProcessLock):
+ def trylock(self):
+ msvcrt.locking(self.lockfile, msvcrt.LK_NBLCK, 1)
+
+ def unlock(self):
+ msvcrt.locking(self.lockfile, msvcrt.LK_UNLCK, 1)
+
+
+class _PosixLock(_InterProcessLock):
+ def trylock(self):
+ fcntl.lockf(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
+
+ def unlock(self):
+ fcntl.lockf(self.lockfile, fcntl.LOCK_UN)
+
+
+if os.name == 'nt':
+ import msvcrt
+ InterProcessLock = _WindowsLock
+else:
+ import fcntl
+ InterProcessLock = _PosixLock
+
+_semaphores = weakref.WeakValueDictionary()
+
+
+def synchronized(name, lock_file_prefix, external=False, lock_path=None):
+ """Synchronization decorator.
+
+ Decorating a method like so::
+
+ @synchronized('mylock')
+ def foo(self, *args):
+ ...
+
+ ensures that only one thread will execute the bar method at a time.
+
+ Different methods can share the same lock::
+
+ @synchronized('mylock')
+ def foo(self, *args):
+ ...
+
+ @synchronized('mylock')
+ def bar(self, *args):
+ ...
+
+ This way only one of either foo or bar can be executing at a time.
+
+ The lock_file_prefix argument is used to provide lock files on disk with a
+ meaningful prefix. The prefix should end with a hyphen ('-') if specified.
+
+ The external keyword argument denotes whether this lock should work across
+ multiple processes. This means that if two different workers both run a
+ a method decorated with @synchronized('mylock', external=True), only one
+ of them will execute at a time.
+
+ The lock_path keyword argument is used to specify a special location for
+ external lock files to live. If nothing is set, then CONF.lock_path is
+ used as a default.
+ """
+
+ def wrap(f):
+ @functools.wraps(f)
+ def inner(*args, **kwargs):
+ # NOTE(soren): If we ever go natively threaded, this will be racy.
+ # See http://stackoverflow.com/questions/5390569/dyn
+ # amically-allocating-and-destroying-mutexes
+ sem = _semaphores.get(name, semaphore.Semaphore())
+ if name not in _semaphores:
+ # this check is not racy - we're already holding ref locally
+ # so GC won't remove the item and there was no IO switch
+ # (only valid in greenthreads)
+ _semaphores[name] = sem
+
+ with sem:
+ LOG.debug(_('Got semaphore "%(lock)s" for method '
+ '"%(method)s"...'), {'lock': name,
+ 'method': f.__name__})
+ if external and not CONF.disable_process_locking:
+ LOG.debug(_('Attempting to grab file lock "%(lock)s" for '
+ 'method "%(method)s"...'),
+ {'lock': name, 'method': f.__name__})
+ cleanup_dir = False
+
+ # We need a copy of lock_path because it is non-local
+ local_lock_path = lock_path
+ if not local_lock_path:
+ local_lock_path = CONF.lock_path
+
+ if not local_lock_path:
+ cleanup_dir = True
+ local_lock_path = tempfile.mkdtemp()
+
+ if not os.path.exists(local_lock_path):
+ cleanup_dir = True
+ fileutils.ensure_tree(local_lock_path)
+
+ # NOTE(mikal): the lock name cannot contain directory
+ # separators
+ safe_name = name.replace(os.sep, '_')
+ lock_file_name = '%s%s' % (lock_file_prefix, safe_name)
+ lock_file_path = os.path.join(local_lock_path,
+ lock_file_name)
+
+ try:
+ lock = InterProcessLock(lock_file_path)
+ with lock:
+ LOG.debug(_('Got file lock "%(lock)s" at %(path)s '
+ 'for method "%(method)s"...'),
+ {'lock': name,
+ 'path': lock_file_path,
+ 'method': f.__name__})
+ retval = f(*args, **kwargs)
+ finally:
+ # NOTE(vish): This removes the tempdir if we needed
+ # to create one. This is used to cleanup
+ # the locks left behind by unit tests.
+ if cleanup_dir:
+ shutil.rmtree(local_lock_path)
+ else:
+ retval = f(*args, **kwargs)
+
+ return retval
+ return inner
+ return wrap
diff --git a/nova/tests/network/test_linux_net.py b/nova/tests/network/test_linux_net.py
index 72bf71674..f69023ef8 100644
--- a/nova/tests/network/test_linux_net.py
+++ b/nova/tests/network/test_linux_net.py
@@ -23,6 +23,7 @@ from nova import context
from nova import db
from nova import flags
from nova.network import linux_net
+from nova.openstack.common import fileutils
from nova.openstack.common import importutils
from nova.openstack.common import log as logging
from nova import test
@@ -236,18 +237,18 @@ class LinuxNetworkTestCase(test.TestCase):
self.flags(use_single_default_gateway=True)
self.mox.StubOutWithMock(self.driver, 'write_to_file')
- self.mox.StubOutWithMock(utils, 'ensure_tree')
+ self.mox.StubOutWithMock(fileutils, 'ensure_tree')
self.mox.StubOutWithMock(os, 'chmod')
self.driver.write_to_file(mox.IgnoreArg(), mox.IgnoreArg())
self.driver.write_to_file(mox.IgnoreArg(), mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
os.chmod(mox.IgnoreArg(), mox.IgnoreArg())
os.chmod(mox.IgnoreArg(), mox.IgnoreArg())
@@ -259,18 +260,18 @@ class LinuxNetworkTestCase(test.TestCase):
self.flags(use_single_default_gateway=True)
self.mox.StubOutWithMock(self.driver, 'write_to_file')
- self.mox.StubOutWithMock(utils, 'ensure_tree')
+ self.mox.StubOutWithMock(fileutils, 'ensure_tree')
self.mox.StubOutWithMock(os, 'chmod')
self.driver.write_to_file(mox.IgnoreArg(), mox.IgnoreArg())
self.driver.write_to_file(mox.IgnoreArg(), mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
- utils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
+ fileutils.ensure_tree(mox.IgnoreArg())
os.chmod(mox.IgnoreArg(), mox.IgnoreArg())
os.chmod(mox.IgnoreArg(), mox.IgnoreArg())
diff --git a/nova/tests/test_imagebackend.py b/nova/tests/test_imagebackend.py
index f0bb71829..088cb0a82 100644
--- a/nova/tests/test_imagebackend.py
+++ b/nova/tests/test_imagebackend.py
@@ -18,6 +18,7 @@
import os
from nova import flags
+from nova.openstack.common import fileutils
from nova import test
from nova.tests import fake_libvirt_utils
from nova.virt.libvirt import imagebackend
@@ -56,8 +57,8 @@ class _ImageTestCase(test.TestCase):
os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
fn = self.mox.CreateMockAnything()
fn(target=self.TEMPLATE_PATH)
- self.mox.StubOutWithMock(imagebackend.utils, 'ensure_tree')
- imagebackend.utils.ensure_tree(self.TEMPLATE_DIR)
+ self.mox.StubOutWithMock(imagebackend.fileutils, 'ensure_tree')
+ imagebackend.fileutils.ensure_tree(self.TEMPLATE_DIR)
self.mox.ReplayAll()
image = self.image_class(self.INSTANCE, self.NAME)
@@ -83,7 +84,7 @@ class _ImageTestCase(test.TestCase):
os.path.exists(self.TEMPLATE_PATH).AndReturn(False)
fn = self.mox.CreateMockAnything()
fn(target=self.TEMPLATE_PATH)
- self.mox.StubOutWithMock(imagebackend.utils, 'ensure_tree')
+ self.mox.StubOutWithMock(imagebackend.fileutils, 'ensure_tree')
self.mox.ReplayAll()
image = self.image_class(self.INSTANCE, self.NAME)
@@ -117,7 +118,8 @@ class RawTestCase(_ImageTestCase):
def prepare_mocks(self):
fn = self.mox.CreateMockAnything()
- self.mox.StubOutWithMock(imagebackend.utils.synchronized, '__call__')
+ self.mox.StubOutWithMock(imagebackend.lockutils.synchronized,
+ '__call__')
self.mox.StubOutWithMock(imagebackend.libvirt_utils, 'copy_image')
self.mox.StubOutWithMock(imagebackend.disk, 'extend')
return fn
@@ -167,7 +169,8 @@ class Qcow2TestCase(_ImageTestCase):
def prepare_mocks(self):
fn = self.mox.CreateMockAnything()
- self.mox.StubOutWithMock(imagebackend.utils.synchronized, '__call__')
+ self.mox.StubOutWithMock(imagebackend.lockutils.synchronized,
+ '__call__')
self.mox.StubOutWithMock(imagebackend.libvirt_utils,
'create_cow_image')
self.mox.StubOutWithMock(imagebackend.libvirt_utils, 'copy_image')
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index 7e73516b0..c84440e58 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -37,6 +37,7 @@ from nova import context
from nova import db
from nova import exception
from nova import flags
+from nova.openstack.common import fileutils
from nova.openstack.common import importutils
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
@@ -448,7 +449,7 @@ class CacheConcurrencyTestCase(test.TestCase):
# use for tests. So, create the path here so utils.synchronized()
# won't delete it out from under one of the threads.
self.lock_path = os.path.join(FLAGS.instances_path, 'locks')
- utils.ensure_tree(self.lock_path)
+ fileutils.ensure_tree(self.lock_path)
def fake_exists(fname):
basedir = os.path.join(FLAGS.instances_path, FLAGS.base_dir_name)
diff --git a/nova/tests/test_misc.py b/nova/tests/test_misc.py
index 10443ecde..4e989f315 100644
--- a/nova/tests/test_misc.py
+++ b/nova/tests/test_misc.py
@@ -24,7 +24,6 @@ from eventlet import greenthread
from nova import exception
from nova import test
-from nova import utils
class ExceptionTestCase(test.TestCase):
@@ -63,96 +62,3 @@ class ProjectTestCase(test.TestCase):
helpful_msg = (_("The following migrations are missing a downgrade:"
"\n\t%s") % '\n\t'.join(sorted(missing_downgrade)))
self.assert_(not missing_downgrade, helpful_msg)
-
-
-class LockTestCase(test.TestCase):
- def test_synchronized_wrapped_function_metadata(self):
- @utils.synchronized('whatever')
- def foo():
- """Bar"""
- pass
- self.assertEquals(foo.__doc__, 'Bar', "Wrapped function's docstring "
- "got lost")
- self.assertEquals(foo.__name__, 'foo', "Wrapped function's name "
- "got mangled")
-
- def test_synchronized_internally(self):
- """We can lock across multiple green threads"""
- saved_sem_num = len(utils._semaphores)
- seen_threads = list()
-
- @utils.synchronized('testlock2', external=False)
- def f(id):
- for x in range(10):
- seen_threads.append(id)
- greenthread.sleep(0)
-
- threads = []
- pool = greenpool.GreenPool(10)
- for i in range(10):
- threads.append(pool.spawn(f, i))
-
- for thread in threads:
- thread.wait()
-
- self.assertEquals(len(seen_threads), 100)
- # Looking at the seen threads, split it into chunks of 10, and verify
- # that the last 9 match the first in each chunk.
- for i in range(10):
- for j in range(9):
- self.assertEquals(seen_threads[i * 10],
- seen_threads[i * 10 + 1 + j])
-
- self.assertEqual(saved_sem_num, len(utils._semaphores),
- "Semaphore leak detected")
-
- def test_nested_external_works(self):
- """We can nest external syncs"""
- with utils.tempdir() as tempdir:
- self.flags(lock_path=tempdir)
- sentinel = object()
-
- @utils.synchronized('testlock1', external=True)
- def outer_lock():
-
- @utils.synchronized('testlock2', external=True)
- def inner_lock():
- return sentinel
- return inner_lock()
-
- self.assertEqual(sentinel, outer_lock())
-
- def test_synchronized_externally(self):
- """We can lock across multiple processes"""
- with utils.tempdir() as tempdir:
- self.flags(lock_path=tempdir)
- rpipe1, wpipe1 = os.pipe()
- rpipe2, wpipe2 = os.pipe()
-
- @utils.synchronized('testlock1', external=True)
- def f(rpipe, wpipe):
- try:
- os.write(wpipe, "foo")
- except OSError, e:
- self.assertEquals(e.errno, errno.EPIPE)
- return
-
- rfds, _wfds, _efds = select.select([rpipe], [], [], 1)
- self.assertEquals(len(rfds), 0, "The other process, which was"
- " supposed to be locked, "
- "wrote on its end of the "
- "pipe")
- os.close(rpipe)
-
- pid = os.fork()
- if pid > 0:
- os.close(wpipe1)
- os.close(rpipe2)
-
- f(rpipe1, wpipe2)
- else:
- os.close(rpipe1)
- os.close(wpipe2)
-
- f(rpipe2, wpipe1)
- os._exit(0)
diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py
index dbd2c1cbe..f97d6eeb7 100644
--- a/nova/tests/test_utils.py
+++ b/nova/tests/test_utils.py
@@ -543,35 +543,6 @@ class MonkeyPatchTestCase(test.TestCase):
in nova.tests.monkey_patch_example.CALLED_FUNCTION)
-class TestFileLocks(test.TestCase):
- def test_concurrent_green_lock_succeeds(self):
- """Verify spawn_n greenthreads with two locks run concurrently."""
- self.completed = False
- with utils.tempdir() as tmpdir:
-
- def locka(wait):
- a = utils.InterProcessLock(os.path.join(tmpdir, 'a'))
- with a:
- wait.wait()
- self.completed = True
-
- def lockb(wait):
- b = utils.InterProcessLock(os.path.join(tmpdir, 'b'))
- with b:
- wait.wait()
-
- wait1 = eventlet.event.Event()
- wait2 = eventlet.event.Event()
- pool = greenpool.GreenPool()
- pool.spawn_n(locka, wait1)
- pool.spawn_n(lockb, wait2)
- wait2.send()
- eventlet.sleep(0)
- wait1.send()
- pool.waitall()
- self.assertTrue(self.completed)
-
-
class AuditPeriodTest(test.TestCase):
def setUp(self):
@@ -778,11 +749,3 @@ class MkfsTestCase(test.TestCase):
utils.mkfs('ext4', '/my/block/dev')
utils.mkfs('swap', '/my/swap/block/dev')
-
-
-class EnsureTree(test.TestCase):
- def test_ensure_tree(self):
- with utils.tempdir() as tmpdir:
- testdir = '%s/foo/bar/baz' % (tmpdir,)
- utils.ensure_tree(testdir)
- self.assertTrue(os.path.isdir(testdir))
diff --git a/nova/utils.py b/nova/utils.py
index 43089b0ab..01df19b27 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -574,183 +574,6 @@ def utf8(value):
return value
-class _InterProcessLock(object):
- """Lock implementation which allows multiple locks, working around
- issues like bugs.debian.org/cgi-bin/bugreport.cgi?bug=632857 and does
- not require any cleanup. Since the lock is always held on a file
- descriptor rather than outside of the process, the lock gets dropped
- automatically if the process crashes, even if __exit__ is not executed.
-
- There are no guarantees regarding usage by multiple green threads in a
- single process here. This lock works only between processes. Exclusive
- access between local threads should be achieved using the semaphores
- in the @synchronized decorator.
-
- Note these locks are released when the descriptor is closed, so it's not
- safe to close the file descriptor while another green thread holds the
- lock. Just opening and closing the lock file can break synchronisation,
- so lock files must be accessed only using this abstraction.
- """
-
- def __init__(self, name):
- self.lockfile = None
- self.fname = name
-
- def __enter__(self):
- self.lockfile = open(self.fname, 'w')
-
- while True:
- try:
- # Using non-blocking locks since green threads are not
- # patched to deal with blocking locking calls.
- # Also upon reading the MSDN docs for locking(), it seems
- # to have a laughable 10 attempts "blocking" mechanism.
- self.trylock()
- return self
- except IOError, e:
- if e.errno in (errno.EACCES, errno.EAGAIN):
- # external locks synchronise things like iptables
- # updates - give it some time to prevent busy spinning
- time.sleep(0.01)
- else:
- raise
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- try:
- self.unlock()
- self.lockfile.close()
- except IOError:
- LOG.exception(_("Could not release the acquired lock `%s`")
- % self.fname)
-
- def trylock(self):
- raise NotImplementedError()
-
- def unlock(self):
- raise NotImplementedError()
-
-
-class _WindowsLock(_InterProcessLock):
- def trylock(self):
- msvcrt.locking(self.lockfile, msvcrt.LK_NBLCK, 1)
-
- def unlock(self):
- msvcrt.locking(self.lockfile, msvcrt.LK_UNLCK, 1)
-
-
-class _PosixLock(_InterProcessLock):
- def trylock(self):
- fcntl.lockf(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
-
- def unlock(self):
- fcntl.lockf(self.lockfile, fcntl.LOCK_UN)
-
-
-if os.name == 'nt':
- import msvcrt
- InterProcessLock = _WindowsLock
-else:
- import fcntl
- InterProcessLock = _PosixLock
-
-_semaphores = weakref.WeakValueDictionary()
-
-
-def synchronized(name, external=False, lock_path=None):
- """Synchronization decorator.
-
- Decorating a method like so::
-
- @synchronized('mylock')
- def foo(self, *args):
- ...
-
- ensures that only one thread will execute the bar method at a time.
-
- Different methods can share the same lock::
-
- @synchronized('mylock')
- def foo(self, *args):
- ...
-
- @synchronized('mylock')
- def bar(self, *args):
- ...
-
- This way only one of either foo or bar can be executing at a time.
-
- The external keyword argument denotes whether this lock should work across
- multiple processes. This means that if two different workers both run a
- a method decorated with @synchronized('mylock', external=True), only one
- of them will execute at a time.
-
- The lock_path keyword argument is used to specify a special location for
- external lock files to live. If nothing is set, then FLAGS.lock_path is
- used as a default.
- """
-
- def wrap(f):
- @functools.wraps(f)
- def inner(*args, **kwargs):
- # NOTE(soren): If we ever go natively threaded, this will be racy.
- # See http://stackoverflow.com/questions/5390569/dyn
- # amically-allocating-and-destroying-mutexes
- sem = _semaphores.get(name, semaphore.Semaphore())
- if name not in _semaphores:
- # this check is not racy - we're already holding ref locally
- # so GC won't remove the item and there was no IO switch
- # (only valid in greenthreads)
- _semaphores[name] = sem
-
- with sem:
- LOG.debug(_('Got semaphore "%(lock)s" for method '
- '"%(method)s"...'), {'lock': name,
- 'method': f.__name__})
- if external and not FLAGS.disable_process_locking:
- LOG.debug(_('Attempting to grab file lock "%(lock)s" for '
- 'method "%(method)s"...'),
- {'lock': name, 'method': f.__name__})
- cleanup_dir = False
-
- # We need a copy of lock_path because it is non-local
- local_lock_path = lock_path
- if not local_lock_path:
- local_lock_path = FLAGS.lock_path
-
- if not local_lock_path:
- cleanup_dir = True
- local_lock_path = tempfile.mkdtemp()
-
- if not os.path.exists(local_lock_path):
- cleanup_dir = True
- ensure_tree(local_lock_path)
-
- # NOTE(mikal): the lock name cannot contain directory
- # separators
- safe_name = name.replace(os.sep, '_')
- lock_file_path = os.path.join(local_lock_path,
- 'nova-%s' % safe_name)
- try:
- lock = InterProcessLock(lock_file_path)
- with lock:
- LOG.debug(_('Got file lock "%(lock)s" for '
- 'method "%(method)s"...'),
- {'lock': name, 'method': f.__name__})
- retval = f(*args, **kwargs)
- finally:
- # NOTE(vish): This removes the tempdir if we needed
- # to create one. This is used to cleanup
- # the locks left behind by unit tests.
- if cleanup_dir:
- shutil.rmtree(local_lock_path)
- else:
- retval = f(*args, **kwargs)
-
- return retval
- return inner
- return wrap
-
-
def delete_if_exists(pathname):
"""delete a file, but ignore file not found error"""
@@ -1302,21 +1125,6 @@ class UndoManager(object):
self._rollback()
-def ensure_tree(path):
- """Create a directory (and any ancestor directories required)
-
- :param path: Directory to create
- """
- try:
- os.makedirs(path)
- except OSError as exc:
- if exc.errno == errno.EEXIST:
- if not os.path.isdir(path):
- raise
- else:
- raise
-
-
def mkfs(fs, path, label=None):
"""Format a file or block device
diff --git a/nova/virt/baremetal/driver.py b/nova/virt/baremetal/driver.py
index 544e38c17..166eacba6 100644
--- a/nova/virt/baremetal/driver.py
+++ b/nova/virt/baremetal/driver.py
@@ -42,6 +42,8 @@ from nova import exception
from nova import flags
from nova import notifications
from nova.openstack.common import cfg
+from nova.openstack.common import fileutils
+from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova import utils
from nova.virt.baremetal import dom
@@ -303,10 +305,10 @@ class BareMetalDriver(driver.ComputeDriver):
if not os.path.exists(target):
base_dir = os.path.join(FLAGS.instances_path, '_base')
if not os.path.exists(base_dir):
- utils.ensure_tree(base_dir)
+ fileutils.ensure_tree(base_dir)
base = os.path.join(base_dir, fname)
- @utils.synchronized(fname)
+ @lockutils.synchronized(fname, 'nova-')
def call_if_not_exists(base, fetch_func, *args, **kwargs):
if not os.path.exists(base):
fetch_func(target=base, *args, **kwargs)
@@ -331,7 +333,7 @@ class BareMetalDriver(driver.ComputeDriver):
fname + suffix)
# ensure directories exist and are writable
- utils.ensure_tree(basepath(suffix=''))
+ fileutils.ensure_tree(basepath(suffix=''))
utils.execute('chmod', '0777', basepath(suffix=''))
LOG.info(_('instance %s: Creating image'), inst['name'],
@@ -339,7 +341,7 @@ class BareMetalDriver(driver.ComputeDriver):
if FLAGS.baremetal_type == 'lxc':
container_dir = '%s/rootfs' % basepath(suffix='')
- utils.ensure_tree(container_dir)
+ fileutils.ensure_tree(container_dir)
# NOTE(vish): No need add the suffix to console.log
libvirt_utils.write_to_file(basepath('console.log', ''), '', 007)
diff --git a/nova/virt/configdrive.py b/nova/virt/configdrive.py
index 86ef13ed0..7b4cb718b 100644
--- a/nova/virt/configdrive.py
+++ b/nova/virt/configdrive.py
@@ -24,6 +24,7 @@ import tempfile
from nova import exception
from nova import flags
from nova.openstack.common import cfg
+from nova.openstack.common import fileutils
from nova.openstack.common import log as logging
from nova import utils
from nova import version
@@ -66,7 +67,7 @@ class ConfigDriveBuilder(object):
def _add_file(self, path, data):
filepath = os.path.join(self.tempdir, path)
dirname = os.path.dirname(filepath)
- utils.ensure_tree(dirname)
+ fileutils.ensure_tree(dirname)
with open(filepath, 'w') as f:
f.write(data)
diff --git a/nova/virt/firewall.py b/nova/virt/firewall.py
index 3e2ba5d33..d066a9c21 100644
--- a/nova/virt/firewall.py
+++ b/nova/virt/firewall.py
@@ -24,8 +24,8 @@ from nova import network
from nova.network import linux_net
from nova.openstack.common import cfg
from nova.openstack.common import importutils
+from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
-from nova import utils
from nova.virt import netutils
@@ -430,7 +430,7 @@ class IptablesFirewallDriver(FirewallDriver):
self.do_refresh_instance_rules(instance)
self.iptables.apply()
- @utils.synchronized('iptables', external=True)
+ @lockutils.synchronized('iptables', 'nova-', external=True)
def _inner_do_refresh_rules(self, instance, ipv4_rules,
ipv6_rules):
self.remove_filters_for_instance(instance)
@@ -453,7 +453,7 @@ class IptablesFirewallDriver(FirewallDriver):
self._do_refresh_provider_fw_rules()
self.iptables.apply()
- @utils.synchronized('iptables', external=True)
+ @lockutils.synchronized('iptables', 'nova-', external=True)
def _do_refresh_provider_fw_rules(self):
"""Internal, synchronized version of refresh_provider_fw_rules."""
self._purge_provider_fw_rules()
diff --git a/nova/virt/hyperv/vmops.py b/nova/virt/hyperv/vmops.py
index 8c86016c2..92d9a408a 100644
--- a/nova/virt/hyperv/vmops.py
+++ b/nova/virt/hyperv/vmops.py
@@ -27,8 +27,8 @@ from nova import db
from nova import exception
from nova import flags
from nova.openstack.common import cfg
+from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
-from nova import utils
from nova.virt.hyperv import baseops
from nova.virt.hyperv import constants
from nova.virt.hyperv import vmutils
@@ -595,7 +595,7 @@ class VMOps(baseops.BaseOps):
If cow is True, it will make a CoW image instead of a copy.
"""
- @utils.synchronized(fname)
+ @lockutils.synchronized(fname, 'nova-')
def call_if_not_exists(path, fn, *args, **kwargs):
if not os.path.exists(path):
fn(target=path, *args, **kwargs)
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index a0b80ab1c..6e85bace9 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -67,6 +67,7 @@ from nova import flags
from nova.image import glance
from nova.openstack.common import cfg
from nova.openstack.common import excutils
+from nova.openstack.common import fileutils
from nova.openstack.common import importutils
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
@@ -853,7 +854,7 @@ class LibvirtDriver(driver.ComputeDriver):
# Export the snapshot to a raw image
snapshot_directory = FLAGS.libvirt_snapshots_directory
- utils.ensure_tree(snapshot_directory)
+ fileutils.ensure_tree(snapshot_directory)
with utils.tempdir(dir=snapshot_directory) as tmpdir:
try:
out_path = os.path.join(tmpdir, snapshot_name)
@@ -1270,7 +1271,7 @@ class LibvirtDriver(driver.ComputeDriver):
return image(fname, image_type='raw')
# ensure directories exist and are writable
- utils.ensure_tree(basepath(suffix=''))
+ fileutils.ensure_tree(basepath(suffix=''))
LOG.info(_('Creating image'), instance=instance)
libvirt_utils.write_to_file(basepath('libvirt.xml'), libvirt_xml)
@@ -1279,7 +1280,7 @@ class LibvirtDriver(driver.ComputeDriver):
container_dir = os.path.join(FLAGS.instances_path,
instance['name'],
'rootfs')
- utils.ensure_tree(container_dir)
+ fileutils.ensure_tree(container_dir)
# NOTE(dprince): for rescue console.log may already exist... chown it.
self._chown_console_log_for_instance(instance['name'])
diff --git a/nova/virt/libvirt/imagebackend.py b/nova/virt/libvirt/imagebackend.py
index 040884e17..3dc8e2037 100644
--- a/nova/virt/libvirt/imagebackend.py
+++ b/nova/virt/libvirt/imagebackend.py
@@ -22,6 +22,8 @@ import os
from nova import flags
from nova.openstack.common import cfg
from nova.openstack.common import excutils
+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.libvirt import config
@@ -112,7 +114,8 @@ class Image(object):
:filename: Name of the file in the image directory
:size: Size of created image in bytes (optional)
"""
- @utils.synchronized(filename, external=True, lock_path=self.lock_path)
+ @lockutils.synchronized(filename, 'nova-', external=True,
+ lock_path=self.lock_path)
def call_if_not_exists(target, *args, **kwargs):
if not os.path.exists(target):
fetch_func(target=target, *args, **kwargs)
@@ -120,7 +123,7 @@ class Image(object):
if not os.path.exists(self.path):
base_dir = os.path.join(FLAGS.instances_path, '_base')
if not os.path.exists(base_dir):
- utils.ensure_tree(base_dir)
+ fileutils.ensure_tree(base_dir)
base = os.path.join(base_dir, filename)
self.create_image(call_if_not_exists, base, size,
@@ -143,7 +146,8 @@ class Raw(Image):
instance, name)
def create_image(self, prepare_template, base, size, *args, **kwargs):
- @utils.synchronized(base, external=True, lock_path=self.lock_path)
+ @lockutils.synchronized(base, 'nova-', external=True,
+ lock_path=self.lock_path)
def copy_raw_image(base, target, size):
libvirt_utils.copy_image(base, target)
if size:
@@ -170,7 +174,8 @@ class Qcow2(Image):
instance, name)
def create_image(self, prepare_template, base, size, *args, **kwargs):
- @utils.synchronized(base, external=True, lock_path=self.lock_path)
+ @lockutils.synchronized(base, 'nova-', external=True,
+ lock_path=self.lock_path)
def copy_qcow2_image(base, target, size):
qcow2_base = base
if size:
@@ -216,7 +221,8 @@ class Lvm(Image):
self.sparse = FLAGS.libvirt_sparse_logical_volumes
def create_image(self, prepare_template, base, size, *args, **kwargs):
- @utils.synchronized(base, external=True, lock_path=self.lock_path)
+ @lockutils.synchronized(base, 'nova-', external=True,
+ lock_path=self.lock_path)
def create_lvm_image(base, size):
base_size = disk.get_disk_size(base)
resize = size > base_size
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
index 782eec2bd..fe54cacec 100644
--- a/nova/virt/libvirt/utils.py
+++ b/nova/virt/libvirt/utils.py
@@ -28,6 +28,7 @@ from lxml import etree
from nova import exception
from nova import flags
from nova.openstack.common import cfg
+from nova.openstack.common import fileutils
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
from nova import utils
@@ -523,7 +524,7 @@ def write_stored_info(target, field=None, value=None):
return
info_file = get_info_filename(target)
- utils.ensure_tree(os.path.dirname(info_file))
+ fileutils.ensure_tree(os.path.dirname(info_file))
d = read_stored_info(info_file)
d[field] = value
diff --git a/nova/virt/libvirt/volume.py b/nova/virt/libvirt/volume.py
index 2a018c724..fd10f431b 100644
--- a/nova/virt/libvirt/volume.py
+++ b/nova/virt/libvirt/volume.py
@@ -22,6 +22,7 @@ import time
from nova import exception
from nova import flags
+from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova import utils
from nova.virt.libvirt import config
@@ -123,7 +124,7 @@ class LibvirtISCSIVolumeDriver(LibvirtVolumeDriver):
'-v', property_value)
return self._run_iscsiadm(iscsi_properties, iscsi_command, **kwargs)
- @utils.synchronized('connect_volume')
+ @lockutils.synchronized('connect_volume', 'nova-')
def connect_volume(self, connection_info, mount_device):
"""Attach the volume to instance_name"""
iscsi_properties = connection_info['data']
@@ -193,7 +194,7 @@ class LibvirtISCSIVolumeDriver(LibvirtVolumeDriver):
sup = super(LibvirtISCSIVolumeDriver, self)
return sup.connect_volume(connection_info, mount_device)
- @utils.synchronized('connect_volume')
+ @lockutils.synchronized('connect_volume', 'nova-')
def disconnect_volume(self, connection_info, mount_device):
"""Detach the volume from instance_name"""
sup = super(LibvirtISCSIVolumeDriver, self)
diff --git a/nova/volume/iscsi.py b/nova/volume/iscsi.py
index 275132ce6..018a86552 100644
--- a/nova/volume/iscsi.py
+++ b/nova/volume/iscsi.py
@@ -24,6 +24,7 @@ import os
from nova import exception
from nova import flags
from nova.openstack.common import cfg
+from nova.openstack.common import fileutils
from nova.openstack.common import log as logging
from nova import utils
@@ -109,7 +110,7 @@ class TgtAdm(TargetAdmin):
# Note(jdg) tid and lun aren't used by TgtAdm but remain for
# compatibility
- utils.ensure_tree(FLAGS.volumes_dir)
+ fileutils.ensure_tree(FLAGS.volumes_dir)
vol_id = name.split(':')[1]
volume_conf = """
diff --git a/openstack-common.conf b/openstack-common.conf
index 398bfb4ae..9a09fd5a8 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -1,7 +1,7 @@
[DEFAULT]
# The list of modules to copy from openstack-common
-modules=cfg,context,excutils,gettextutils,importutils,iniparser,jsonutils,local,log,network_utils,notifier,plugin,policy,setup,timeutils,rpc
+modules=cfg,context,excutils,fileutils,gettextutils,importutils,iniparser,jsonutils,local,lockutils,log,network_utils,notifier,plugin,policy,setup,timeutils,rpc
# The base module to hold the copy of openstack.common
base=nova