Change in vdsm[master]: cache: Replace utils.memoized with cache.memoized()
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: cache: Replace utils.memoized with cache.memoized()
......................................................................
cache: Replace utils.memoized with cache.memoized()
Repalce users of utils.memoized with cache.memoized() and remove
utils.memoized. No behavior change is expected.
Change-Id: I12e2f2919cf92ff7d0758d70e2ed40523d66174f
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M lib/vdsm/netinfo.py
M lib/vdsm/utils.py
M tests/vmApiTests.py
M vdsm/caps.py
M vdsm/dmidecodeUtil.py
M vdsm/ppc64HardwareInfo.py
M vdsm/supervdsmServer
M vdsm/virt/sampling.py
8 files changed, 30 insertions(+), 51 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/10/34710/1
diff --git a/lib/vdsm/netinfo.py b/lib/vdsm/netinfo.py
index d2ef90b..8c56863 100644
--- a/lib/vdsm/netinfo.py
+++ b/lib/vdsm/netinfo.py
@@ -43,7 +43,7 @@
from .ipwrapper import routeShowGateways
from . import libvirtconnection
from .netconfpersistence import RunningConfig
-from .utils import memoized
+from .cache import memoized
from .netlink import link as nl_link
from .netlink import addr as nl_addr
from .netlink import route as nl_route
@@ -395,7 +395,7 @@
return paddr
-@memoized
+@memoized()
def _getAllDefaultBondingOptions():
"""
Return default options per mode, in a dictionary of dictionaries. All keys
@@ -405,7 +405,7 @@
return json.loads(defaults.read())
-@memoized
+@memoized()
def _getDefaultBondingOptions(mode=None):
"""
Return default options for the given mode. If it is None, return options
diff --git a/lib/vdsm/utils.py b/lib/vdsm/utils.py
index feb4079..131145c 100644
--- a/lib/vdsm/utils.py
+++ b/lib/vdsm/utils.py
@@ -51,6 +51,7 @@
import vdsm.infra.zombiereaper as zombiereaper
from cpopen import CPopen
+from . import cache
from . import constants
# Buffsize is 1K because I tested it on some use cases and 1K was fastest. If
@@ -828,32 +829,6 @@
fcntl.fcntl(fd, fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
-class memoized(object):
- """
- Decorator that caches a function's return value each time it is called.
- If called later with the same arguments, the cached value is returned, and
- not re-evaluated. There is no support for uncachable arguments.
-
- Adaptation from http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize
- """
- def __init__(self, func):
- self.func = func
- self.cache = {}
- functools.update_wrapper(self, func)
-
- def __call__(self, *args):
- try:
- return self.cache[args]
- except KeyError:
- value = self.func(*args)
- self.cache[args] = value
- return value
-
- def __get__(self, obj, objtype):
- """Support instance methods."""
- return functools.partial(self.__call__, obj)
-
-
def validateMinimalKeySet(dictionary, reqParams):
if not all(key in dictionary for key in reqParams):
raise ValueError
@@ -993,7 +968,7 @@
sys.exit(-3)
-@memoized
+(a)cache.memoized()
def isOvirtNode():
return (os.path.exists('/etc/rhev-hypervisor-release') or
bool(glob.glob('/etc/ovirt-node-*-release')))
diff --git a/tests/vmApiTests.py b/tests/vmApiTests.py
index eb89fb1..4f940cf 100644
--- a/tests/vmApiTests.py
+++ b/tests/vmApiTests.py
@@ -25,14 +25,14 @@
from virt import vmexitreason
from vdsm import define
from testlib import VdsmTestCase as TestCaseBase
-from vdsm import utils
+from vdsm import cache
from rpc import vdsmapi
import vmfakelib as fake
class TestSchemaCompliancyBase(TestCaseBase):
- @utils.memoized
+ @cache.memoized()
def _getAPI(self):
testPath = os.path.realpath(__file__)
dirName = os.path.split(testPath)[0]
diff --git a/vdsm/caps.py b/vdsm/caps.py
index dba9b5b..6e261d8 100644
--- a/vdsm/caps.py
+++ b/vdsm/caps.py
@@ -40,6 +40,7 @@
import dsaversion
from vdsm import netinfo
import hooks
+from vdsm import cache
from vdsm import utils
import storage.hba
from network.configurators import qos
@@ -218,12 +219,12 @@
ENABLED = 1
-(a)utils.memoized
+(a)cache.memoized()
def _getCapsXMLStr():
return libvirtconnection.get().getCapabilities()
-(a)utils.memoized
+(a)cache.memoized()
def _getCpuTopology(capabilities):
if capabilities is None:
capabilities = _getCapsXMLStr()
@@ -277,7 +278,7 @@
return None
-(a)utils.memoized
+(a)cache.memoized()
def _getLiveSnapshotSupport(arch, capabilities=None):
if capabilities is None:
capabilities = _getCapsXMLStr()
@@ -294,7 +295,7 @@
return None
-(a)utils.memoized
+(a)cache.memoized()
def getLiveMergeSupport():
"""
Determine if libvirt provides the necessary features to enable live merge.
@@ -318,7 +319,7 @@
return True
-(a)utils.memoized
+(a)cache.memoized()
def getNumaTopology():
capabilities = _getCapsXMLStr()
caps = minidom.parseString(capabilities)
@@ -369,7 +370,7 @@
return memDict
-(a)utils.memoized
+(a)cache.memoized()
def getNumaNodeDistance():
nodeDistance = {}
retcode, out, err = utils.execCmd(['numactl', '--hardware'])
@@ -385,7 +386,7 @@
return nodeDistance
-(a)utils.memoized
+(a)cache.memoized()
def getAutoNumaBalancingInfo():
retcode, out, err = utils.execCmd(['sysctl', '-n', '-e',
'kernel.numa_balancing'])
@@ -399,7 +400,7 @@
return AutoNumaBalancingStatus.UNKNOWN
-(a)utils.memoized
+(a)cache.memoized()
def _getEmulatedMachines(arch, capabilities=None):
if capabilities is None:
capabilities = _getCapsXMLStr()
@@ -461,7 +462,7 @@
return allModels
-(a)utils.memoized
+(a)cache.memoized()
def _getCompatibleCpuModels():
c = libvirtconnection.get()
allModels = _getAllCpuModels()
@@ -532,7 +533,7 @@
return kdumpStatus
-(a)utils.memoized
+(a)cache.memoized()
def getos():
if os.path.exists('/etc/rhev-hypervisor-release'):
return OSName.RHEVH
@@ -550,7 +551,7 @@
return OSName.UNKNOWN
-(a)utils.memoized
+(a)cache.memoized()
def osversion():
version = release = ''
@@ -700,7 +701,7 @@
return info
-(a)utils.memoized
+(a)cache.memoized()
def _getVersionInfo():
if not hasattr(libvirt, 'VIR_MIGRATE_ABORT_ON_ERROR'):
return _dropVersion('3.4',
diff --git a/vdsm/dmidecodeUtil.py b/vdsm/dmidecodeUtil.py
index add2526..f0c72fa 100644
--- a/vdsm/dmidecodeUtil.py
+++ b/vdsm/dmidecodeUtil.py
@@ -18,7 +18,7 @@
# Refer to the README and COPYING files for full details of the license
#
-from vdsm import utils
+from vdsm import cache
# This function gets dict and returns new dict that includes only string
@@ -35,7 +35,7 @@
return ret
-(a)utils.memoized
+(a)cache.memoized()
def getAllDmidecodeInfo():
import dmidecode
@@ -45,7 +45,7 @@
return myLeafDict
-(a)utils.memoized
+(a)cache.memoized()
def getHardwareInfoStructure():
dmiInfo = getAllDmidecodeInfo()
sysStruct = {}
diff --git a/vdsm/ppc64HardwareInfo.py b/vdsm/ppc64HardwareInfo.py
index 23f7388..1a89639 100644
--- a/vdsm/ppc64HardwareInfo.py
+++ b/vdsm/ppc64HardwareInfo.py
@@ -16,6 +16,7 @@
# Refer to the README and COPYING files for full details of the license
#
+from vdsm import cache
from vdsm import utils
import os
@@ -31,7 +32,7 @@
return 'unavailable'
-(a)utils.memoized
+(a)cache.memoized()
def getHardwareInfoStructure():
infoStructure = {'systemSerialNumber': 'unavailable',
'systemFamily': 'unavailable',
@@ -59,7 +60,7 @@
return infoStructure
-(a)utils.memoized
+(a)cache.memoized()
def getCpuTopology(capabilities):
topology = {}
diff --git a/vdsm/supervdsmServer b/vdsm/supervdsmServer
index c6c139b..aba32fb 100755
--- a/vdsm/supervdsmServer
+++ b/vdsm/supervdsmServer
@@ -51,6 +51,7 @@
except ImportError:
_glusterEnabled = False
+from vdsm import cache
from vdsm import utils
from vdsm.tool import restore_nets
from parted_utils import getDevicePartedInfo as _getDevicePartedInfo
@@ -378,7 +379,7 @@
raise OSError(errno.EINVAL, "Could not reload-rules for device "
"%s" % guid)
- @utils.memoized
+ @cache.memoized()
def __udevVersion(self):
cmd = [EXT_UDEVADM, '--version']
rc, out, err = utils.execCmd(cmd)
diff --git a/vdsm/virt/sampling.py b/vdsm/virt/sampling.py
index 6bb4211..b1c3db6 100644
--- a/vdsm/virt/sampling.py
+++ b/vdsm/virt/sampling.py
@@ -30,6 +30,7 @@
import errno
import re
+from vdsm import cache
from vdsm import utils
from vdsm import netinfo
from vdsm.ipwrapper import getLinks
@@ -524,7 +525,7 @@
if not self._stopEvent.isSet():
self._log.error("Error while sampling stats", exc_info=True)
- @utils.memoized
+ @cache.memoized()
def _boot_time(self):
# Try to get boot time only once, if N/A just log the error and never
# include it in the response.
--
To view, visit http://gerrit.ovirt.org/34710
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I12e2f2919cf92ff7d0758d70e2ed40523d66174f
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
7 years, 8 months
Change in vdsm[master]: [WIP]fencenode: split fenceNode into its own module
by ykaplan@redhat.com
Yeela Kaplan has uploaded a new change for review.
Change subject: [WIP]fencenode: split fenceNode into its own module
......................................................................
[WIP]fencenode: split fenceNode into its own module
Change-Id: I7bd5e7246cf6da21e355849014a8fc71d5edbde6
Signed-off-by: Yeela Kaplan <ykaplan(a)redhat.com>
---
M debian/vdsm-python.install
M lib/vdsm/Makefile.am
A lib/vdsm/fencenode.py
M vdsm.spec.in
M vdsm/API.py
5 files changed, 120 insertions(+), 80 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/97/43597/1
diff --git a/debian/vdsm-python.install b/debian/vdsm-python.install
index d9402c3..901edb6 100644
--- a/debian/vdsm-python.install
+++ b/debian/vdsm-python.install
@@ -10,6 +10,7 @@
./usr/lib/python2.7/dist-packages/vdsm/exception.py
./usr/lib/python2.7/dist-packages/vdsm/executor.py
./usr/lib/python2.7/dist-packages/vdsm/ipwrapper.py
+./usr/lib/python2.7/dist-packages/vdsm/fencenode.py
./usr/lib/python2.7/dist-packages/vdsm/jsonrpcvdscli.py
./usr/lib/python2.7/dist-packages/vdsm/libvirtconnection.py
./usr/lib/python2.7/dist-packages/vdsm/netconfpersistence.py
diff --git a/lib/vdsm/Makefile.am b/lib/vdsm/Makefile.am
index 95e236f..875507c 100644
--- a/lib/vdsm/Makefile.am
+++ b/lib/vdsm/Makefile.am
@@ -30,6 +30,7 @@
exception.py \
executor.py \
ipwrapper.py \
+ fencenode.py \
jsonrpcvdscli.py \
libvirtconnection.py \
netconfpersistence.py \
diff --git a/lib/vdsm/fencenode.py b/lib/vdsm/fencenode.py
new file mode 100644
index 0000000..3456b2f
--- /dev/null
+++ b/lib/vdsm/fencenode.py
@@ -0,0 +1,100 @@
+#
+# Copyright 2015 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+
+import logging
+import os
+import signal
+
+import utils
+import constants
+from define import doneCode, errCode
+
+
+(a)utils.traceback(on=logging.name)
+def fence(script, data):
+ # non-status actions are sent asyncronously. deathSignal is set to
+ # make sure that no stray fencing scripts are left behind if Vdsm
+ # crashes.
+ rc, out, err = utils.execCmd(
+ [script], deathSignal=signal.SIGTERM, data=data)
+ logging.debug('rc %s data %s out %s err %s', rc,
+ hidePasswd(data), out, err)
+ return rc, out, err
+
+
+def hidePasswd(text):
+ cleantext = ''
+ for line in text.splitlines(True):
+ if line.startswith('passwd='):
+ line = 'passwd=XXXX\n'
+ cleantext += line
+ return cleantext
+
+
+def fenceNode(addr, port, agent, username, password, action,
+ secure=False, options='', policy=None):
+
+ logging.debug('fenceNode(addr=%s,port=%s,agent=%s,user=%s,passwd=%s,'
+ 'action=%s,secure=%s,options=%s,policy=%s)',
+ addr, port, agent, username, password, action, secure,
+ options, policy)
+
+ if action not in ('status', 'on', 'off', 'reboot'):
+ raise ValueError('illegal action ' + action)
+
+ script = constants.EXT_FENCE_PREFIX + agent
+
+ data = ('agent=fence_%s\nipaddr=%s\nlogin=%s\naction=%s\n'
+ 'passwd=%s\n') % (agent, addr, username, action, password.value)
+ if port != '':
+ data += 'port=%s\n' % (port,)
+ if utils.tobool(secure):
+ data += 'secure=yes\n'
+ data += options
+
+ try:
+ rc, out, err = fence(script, data)
+ except OSError as e:
+ if e.errno == os.errno.ENOENT:
+ return errCode['fenceAgent']
+ raise
+
+ logging.debug('rc %s data %s out %s err %s',
+ rc, hidePasswd(data), out, err)
+
+ if not 0 <= rc <= 2:
+ return {'status': {'code': 1,
+ 'message': out + err}}
+
+ message = doneCode['message']
+ if action == 'status':
+ if rc == 0:
+ power = 'on'
+ elif rc == 2:
+ power = 'off'
+ else:
+ power = 'unknown'
+ message = out + err
+ return {'status': {'code': 0, 'message': message},
+ 'power': power}
+ if rc != 0:
+ message = out + err
+ return {'status': {'code': rc, 'message': message},
+ 'power': 'unknown', 'operationStatus': 'initiated'}
diff --git a/vdsm.spec.in b/vdsm.spec.in
index 966a974..9e69162 100644
--- a/vdsm.spec.in
+++ b/vdsm.spec.in
@@ -1071,6 +1071,7 @@
%{python_sitelib}/%{vdsm_name}/exception.py*
%{python_sitelib}/%{vdsm_name}/executor.py*
%{python_sitelib}/%{vdsm_name}/ipwrapper.py*
+%{python_sitelib}/%{vdsm_name}/fencenode.py*
%{python_sitelib}/%{vdsm_name}/jsonrpcvdscli.py*
%{python_sitelib}/%{vdsm_name}/libvirtconnection.py*
%{python_sitelib}/%{vdsm_name}/netinfo.py*
diff --git a/vdsm/API.py b/vdsm/API.py
index 0a93d2a..0719926 100644
--- a/vdsm/API.py
+++ b/vdsm/API.py
@@ -21,8 +21,6 @@
# pylint: disable=R0904
from contextlib import contextmanager
-import os
-import signal
import six
import sys
import time
@@ -38,6 +36,7 @@
from clientIF import clientIF
from vdsm import netinfo
from vdsm import constants
+from vdsm import fencenode
import storage.misc
import storage.clusterlock
import storage.volume
@@ -1187,6 +1186,11 @@
APIBase.__init__(self)
# General Host functions
+ def ping(self):
+ "Ping the server. Useful for tests"
+ updateTimestamp()
+ return {'status': doneCode}
+
def fenceNode(self, addr, port, agent, username, password, action,
secure=False, options='', policy=None):
"""Send a fencing command to a remote node.
@@ -1194,44 +1198,24 @@
agent is one of (rsa, ilo, drac5, ipmilan, etc)
action can be one of (status, on, off, reboot)."""
- @utils.traceback(on=self.log.name)
- def fence(script, inp):
- # non-status actions are sent asyncronously. deathSignal is set to
- # make sure that no stray fencing scripts are left behind if Vdsm
- # crashes.
- rc, out, err = utils.execCmd(
- [script], deathSignal=signal.SIGTERM,
- data=inp)
- self.log.debug('rc %s inp %s out %s err %s', rc,
- hidePasswd(inp), out, err)
- return rc, out, err
-
- def hidePasswd(text):
- cleantext = ''
- for line in text.splitlines(True):
- if line.startswith('passwd='):
- line = 'passwd=XXXX\n'
- cleantext += line
- return cleantext
-
def should_fence(policy):
# skip fence execution if map of storage domains with host id is
# entered and at least one storage domain connection from host is
# alive
if policy is None:
- self.log.debug('No policy specified')
+ logging.debug('No policy specified')
return True
hostIdMap = policy.get('storageDomainHostIdMap')
if not hostIdMap:
- self.log.warning('Invalid policy specified')
+ logging.warning('Invalid policy specified')
return True
result = self._irs.getHostLeaseStatus(hostIdMap)
- if result['status']['code'] != 0:
- self.log.error(
- "Error getting host lease status, error code '%s'",
- result['status']['code'])
+ rc = result['status']['code']
+ if rc != 0:
+ logging.error(
+ "Error getting host lease status, error code '%s'", rc)
return True
# HOST_STATUS_LIVE means that host renewed its lease in last 80
@@ -1239,65 +1223,18 @@
# fencing, even when it's unreachable from engine
for sd, status in result['domains'].iteritems():
if status == storage.clusterlock.HOST_STATUS_LIVE:
- self.log.debug("Host has live lease on '%s'", sd)
+ logging.debug("Host has live lease on '%s'", sd)
return False
- self.log.debug("Host doesn't have any live lease")
+ logging.debug("Host doesn't have any live lease")
return True
- self.log.debug('fenceNode(addr=%s,port=%s,agent=%s,user=%s,passwd=%s,'
- 'action=%s,secure=%s,options=%s,policy=%s)',
- addr, port, agent, username, password, action, secure,
- options, policy)
-
- if action not in ('status', 'on', 'off', 'reboot'):
- raise ValueError('illegal action ' + action)
-
if action != 'status' and not should_fence(policy):
- self.log.debug("Skipping execution of action '%s'", action)
+ logging.debug("Skipping execution of action '%s'", action)
return {'status': doneCode, 'operationStatus': 'skipped'}
- script = constants.EXT_FENCE_PREFIX + agent
-
- inp = ('agent=fence_%s\nipaddr=%s\nlogin=%s\naction=%s\n'
- 'passwd=%s\n') % (agent, addr, username, action, password.value)
- if port != '':
- inp += 'port=%s\n' % (port,)
- if utils.tobool(secure):
- inp += 'secure=yes\n'
- inp += options
-
- try:
- rc, out, err = fence(script, inp)
- except OSError as e:
- if e.errno == os.errno.ENOENT:
- return errCode['fenceAgent']
- raise
- self.log.debug('rc %s in %s out %s err %s', rc,
- hidePasswd(inp), out, err)
- if not 0 <= rc <= 2:
- return {'status': {'code': 1,
- 'message': out + err}}
- message = doneCode['message']
- if action == 'status':
- if rc == 0:
- power = 'on'
- elif rc == 2:
- power = 'off'
- else:
- power = 'unknown'
- message = out + err
- return {'status': {'code': 0, 'message': message},
- 'power': power}
- if rc != 0:
- message = out + err
- return {'status': {'code': rc, 'message': message},
- 'power': 'unknown', 'operationStatus': 'initiated'}
-
- def ping(self):
- "Ping the server. Useful for tests"
- updateTimestamp()
- return {'status': doneCode}
+ return fencenode.fenceNode(addr, port, agent, username, password,
+ action, secure, options, policy)
def getCapabilities(self):
"""
--
To view, visit https://gerrit.ovirt.org/43597
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I7bd5e7246cf6da21e355849014a8fc71d5edbde6
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Yeela Kaplan <ykaplan(a)redhat.com>
7 years, 8 months
Change in vdsm[master]: wip: tests: change testing framework to pytest
by igoihman@redhat.com
Irit Goihman has uploaded a new change for review.
Change subject: wip: tests: change testing framework to pytest
......................................................................
wip: tests: change testing framework to pytest
Change-Id: Ife527b9274c31a72b57da9b958a1dd2f9f1e5ea0
Signed-off-by: Irit Goihman <igoihman(a)redhat.com>
---
A tests/pytest.ini
M tests/testrunner.py
2 files changed, 4 insertions(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/17/60217/1
diff --git a/tests/pytest.ini b/tests/pytest.ini
new file mode 100644
index 0000000..521a323
--- /dev/null
+++ b/tests/pytest.ini
@@ -0,0 +1,2 @@
+[pytest]
+python_files = *tests.py *test.py *Test.py *Tests.py
diff --git a/tests/testrunner.py b/tests/testrunner.py
index 27f6f7e..38d970e 100644
--- a/tests/testrunner.py
+++ b/tests/testrunner.py
@@ -26,6 +26,7 @@
import pthreading
pthreading.monkey_patch()
+import pytest
import warnings
import logging
@@ -85,4 +86,4 @@
# Mock panic() calls for tests
panic.panic = panicMock
- testlib.run()
+ pytest.main()
--
To view, visit https://gerrit.ovirt.org/60217
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ife527b9274c31a72b57da9b958a1dd2f9f1e5ea0
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Irit Goihman <igoihman(a)redhat.com>
7 years, 8 months
Change in vdsm[master]: qemuimg: Memoize _supports_qcow2_compat
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: qemuimg: Memoize _supports_qcow2_compat
......................................................................
qemuimg: Memoize _supports_qcow2_compat
We used to run qemu-img twice when creating or converting qcow2 images.
The first run check if qemu-img supports the qcow2 "comapt" option, and
the second run uses the result to format the qemu-img command.
Now we run qemu-img once for "create" and "convert" to learn about its
capabilities, and use the cached value on the next runs. If qemu-img
executable is modified, we drop the cache, in case a new version was
installed with different capabilities.
Change-Id: Ic63f5e8c06993df8e4066bf7ac2dabfb4b4bdbfb
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M lib/vdsm/qemuimg.py
M tests/qemuimgTests.py
2 files changed, 8 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/11/34711/1
diff --git a/lib/vdsm/qemuimg.py b/lib/vdsm/qemuimg.py
index cf428b2..f295acc 100644
--- a/lib/vdsm/qemuimg.py
+++ b/lib/vdsm/qemuimg.py
@@ -23,6 +23,7 @@
import signal
from . import utils
+from . import cache
_qemuimg = utils.CommandPath("qemu-img",
"/usr/bin/qemu-img",) # Fedora, EL6
@@ -221,6 +222,7 @@
raise QImgError(rc, out, err)
+(a)cache.memoized(cache.file_validator(_qemuimg.cmd))
def _supports_qcow2_compat(command):
"""
qemu-img "create" and "convert" commands support a "compat" option in
diff --git a/tests/qemuimgTests.py b/tests/qemuimgTests.py
index 813a497..a642374 100644
--- a/tests/qemuimgTests.py
+++ b/tests/qemuimgTests.py
@@ -118,6 +118,9 @@
class CreateTests(TestCaseBase):
+ def setUp(self):
+ qemuimg._supports_qcow2_compat.invalidate()
+
def test_no_format(self):
def create(cmd, **kw):
expected = [QEMU_IMG, 'create', 'image']
@@ -161,6 +164,9 @@
class ConvertTests(TestCaseBase):
+ def setUp(self):
+ qemuimg._supports_qcow2_compat.invalidate()
+
def test_no_format(self):
def convert(cmd, **kw):
expected = [QEMU_IMG, 'convert', '-t', 'none', 'src', 'dst']
--
To view, visit http://gerrit.ovirt.org/34711
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic63f5e8c06993df8e4066bf7ac2dabfb4b4bdbfb
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
7 years, 8 months
Change in vdsm[master]: network: switch to concurrent.thread()
by fromani@redhat.com
Francesco Romani has uploaded a new change for review.
Change subject: network: switch to concurrent.thread()
......................................................................
network: switch to concurrent.thread()
This patch:
1. replaces good usages of threading.Thread() with
more modern concurrent.thread()
2. replaces bad usages of threading.Thread() - inheritance
with composition, leveraging concurrent.thread()
there are no intended changes in behaviour.
Change-Id: Ie7918a6762b35000b02187a0356273e40bba57a8
Signed-off-by: Francesco Romani <fromani(a)redhat.com>
---
M lib/vdsm/network/configurators/ifcfg.py
M lib/vdsm/network/ip/dhclient.py
2 files changed, 7 insertions(+), 8 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/59/61459/1
diff --git a/lib/vdsm/network/configurators/ifcfg.py b/lib/vdsm/network/configurators/ifcfg.py
index e9f67e1..9168e05 100644
--- a/lib/vdsm/network/configurators/ifcfg.py
+++ b/lib/vdsm/network/configurators/ifcfg.py
@@ -29,7 +29,6 @@
import re
import selinux
import shutil
-import threading
import uuid
import six
@@ -39,6 +38,7 @@
from vdsm.config import config
from vdsm import commands
from vdsm import cmdutils
+from vdsm import concurrent
from vdsm import constants
from vdsm import dsaversion
from vdsm import hooks
@@ -846,9 +846,9 @@
if not iface.blockingdhcp and (iface.ipv4.bootproto == 'dhcp' or
iface.ipv6.dhcpv6):
# wait for dhcp in another thread, so vdsm won't get stuck (BZ#498940)
- t = threading.Thread(target=_exec_ifup, name='ifup-waiting-on-dhcp',
- args=(iface, cgroup))
- t.daemon = True
+ t = concurrent.thread(_exec_ifup,
+ name='ifup-waiting-on-dhcp',
+ args=(iface, cgroup))
t.start()
else:
if not iface.master and (iface.ipv4 or iface.ipv6):
diff --git a/lib/vdsm/network/ip/dhclient.py b/lib/vdsm/network/ip/dhclient.py
index 318217c..f67a876 100644
--- a/lib/vdsm/network/ip/dhclient.py
+++ b/lib/vdsm/network/ip/dhclient.py
@@ -24,9 +24,9 @@
import logging
import os
import subprocess
-import threading
from vdsm import cmdutils
+from vdsm import concurrent
from vdsm.network import errors as ne
from vdsm.network import netinfo
from vdsm.commands import execCmd
@@ -73,9 +73,8 @@
if blocking:
return self._dhclient()
else:
- t = threading.Thread(target=self._dhclient, name='vdsm-dhclient-%s'
- % self.iface)
- t.daemon = True
+ t = concurrent.thread(self._dhclient,
+ name='vdsm-dhclient-%s' % self.iface)
t.start()
def shutdown(self):
--
To view, visit https://gerrit.ovirt.org/61459
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie7918a6762b35000b02187a0356273e40bba57a8
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Francesco Romani <fromani(a)redhat.com>
7 years, 8 months
Change in vdsm[master]: sdm: Jobs may be run only when status=pending
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: sdm: Jobs may be run only when status=pending
......................................................................
sdm: Jobs may be run only when status=pending
An SDM Job begins its lifecycle in pending state and at some point a scheduler
will call its run() method to run it. The job should not run from any state
other than pending. If a job was aborted while waiting to be executed we
should print a message and return. If the job is in any other state we have a
bug and an exception should be raised.
Change-Id: I601f6db19527635c1a00c3a4c2910eac4ac5371b
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M tests/storage_sdm_api_test.py
M vdsm/storage/sdm/api/base.py
2 files changed, 32 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/38/61138/1
diff --git a/tests/storage_sdm_api_test.py b/tests/storage_sdm_api_test.py
index c8d93d2..3306c30 100644
--- a/tests/storage_sdm_api_test.py
+++ b/tests/storage_sdm_api_test.py
@@ -21,6 +21,7 @@
import uuid
from testlib import VdsmTestCase
+from testlib import expandPermutations, permutations
from sdmtestlib import wait_for_job
from vdsm import jobs
@@ -30,6 +31,7 @@
from storage.sdm.api import base
+@expandPermutations
class ApiBaseTests(VdsmTestCase):
def run_job(self, job):
@@ -58,6 +60,20 @@
self.assertEqual(jobs.STATUS.FAILED, job.status)
self.assertIsInstance(job.error, exception.VdsmException)
+ def test_run_aborted_job(self):
+ job = TestingJob()
+ job.abort()
+ job.run()
+ self.assertEqual(jobs.STATUS.ABORTED, job.status)
+
+ @permutations((
+ (jobs.STATUS.DONE,), (jobs.STATUS.RUNNING,), (jobs.STATUS.FAILED,)
+ ))
+ def test_run_from_invalid_state(self, status):
+ job = TestingJob()
+ job.set_status(status)
+ self.assertRaises(base.CannotRunJob, job.run)
+
class TestingJob(base.Job):
@@ -66,6 +82,12 @@
super(TestingJob, self).__init__(job_id, 'testing_job', 'host_id')
self.exception = exception
+ def set_status(self, value):
+ self._status = value
+
+ def _abort(self):
+ pass
+
def _run(self):
assert(self.status == jobs.STATUS.RUNNING)
assert(vars.job_id == self.id)
diff --git a/vdsm/storage/sdm/api/base.py b/vdsm/storage/sdm/api/base.py
index a342300..eb88f35 100644
--- a/vdsm/storage/sdm/api/base.py
+++ b/vdsm/storage/sdm/api/base.py
@@ -27,6 +27,10 @@
from vdsm.storage.threadlocal import vars
+class CannotRunJob(Exception):
+ pass
+
+
class Job(jobs.Job):
_JOB_TYPE = "storage"
log = logging.getLogger('storage.sdmjob')
@@ -37,6 +41,12 @@
self.host_id = host_id
def run(self):
+ if self._status == jobs.STATUS.ABORTED:
+ self.log.info("Job id=%s aborted while pending", self.id)
+ return
+ if self._status != jobs.STATUS.PENDING:
+ raise CannotRunJob("Job id=%s cannot be started from %r status",
+ self.id, self.status)
self._status = jobs.STATUS.RUNNING
vars.job_id = self.id
try:
--
To view, visit https://gerrit.ovirt.org/61138
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I601f6db19527635c1a00c3a4c2910eac4ac5371b
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 8 months
Change in vdsm[master]: wip: copy_data take resource locks
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: wip: copy_data take resource locks
......................................................................
wip: copy_data take resource locks
Change-Id: I1b008c38dab5478f13175659bd2103e994802cf7
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M tests/storage_sdm_copy_data_test.py
M vdsm/storage/sdm/api/copy_data.py
2 files changed, 72 insertions(+), 13 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/53/61353/1
diff --git a/tests/storage_sdm_copy_data_test.py b/tests/storage_sdm_copy_data_test.py
index def8934..8d40cd6 100644
--- a/tests/storage_sdm_copy_data_test.py
+++ b/tests/storage_sdm_copy_data_test.py
@@ -49,6 +49,7 @@
rm = FakeResourceManager()
with MonkeyPatchScope([
(storage.sdm.api.copy_data, 'sdCache', env.sdcache),
+ (storage.sdm.api.copy_data, 'rmanager', rm),
(blockVolume, 'rmanager', rm),
]):
src_vols = make_qemu_chain(env, self.SIZE, src_fmt,
diff --git a/vdsm/storage/sdm/api/copy_data.py b/vdsm/storage/sdm/api/copy_data.py
index aff0185..269a10f 100644
--- a/vdsm/storage/sdm/api/copy_data.py
+++ b/vdsm/storage/sdm/api/copy_data.py
@@ -29,8 +29,12 @@
from storage import volume
from storage.sdc import sdCache
+from storage import resourceManager as rm
+from storage.resourceFactories import IMAGE_NAMESPACE
from . import base
+
+rmanager = rm.ResourceManager.getInstance()
class Job(base.Job):
@@ -55,20 +59,20 @@
self._operation.abort()
def _run(self):
- # TODO: LOCKING!
- with self._source.prepare(), self._dest.prepare(writable=True):
- # Do not start copying if we have already been aborted
- if self._status == jobs.STATUS.ABORTED:
- return
+ with protected_endpoints(self._source, self._dest):
+ with self._source.prepare(), self._dest.prepare(writable=True):
+ # Do not start copying if we have already been aborted
+ if self._status == jobs.STATUS.ABORTED:
+ return
- self._operation = qemuimg.convert(
- self._source.path,
- self._dest.path,
- srcFormat=self._source.qemu_format,
- dstFormat=self._dest.qemu_format,
- backing=self._dest.backing_path,
- backingFormat=self._dest.backing_qemu_format)
- self._operation.wait_for_completion()
+ self._operation = qemuimg.convert(
+ self._source.path,
+ self._dest.path,
+ srcFormat=self._source.qemu_format,
+ dstFormat=self._dest.qemu_format,
+ backing=self._dest.backing_path,
+ backingFormat=self._dest.backing_qemu_format)
+ self._operation.wait_for_completion()
def _create_endpoint(params):
@@ -79,6 +83,60 @@
raise ValueError("Invalid or unsupported endpoint %r" % params)
+class protected_endpoints(object):
+
+ def __init__(self, src, dest):
+ self._domain_locks = self._get_domain_locks((src, dest))
+ self._image_locks = self._get_image_locks(src, dest)
+ self._held_locks = list()
+
+ def __enter__(self):
+ try:
+ for lock in self._domain_locks + self._image_locks:
+ rmanager.acquireResource(lock.ns, lock.name, lock.mode)
+ self._held_locks.insert(0, lock)
+ except:
+ self._release()
+ raise
+
+ def __exit__(self, type, value, tb):
+ self._release()
+
+ def _get_domain_locks(self, endpoints):
+ locks = set()
+ for endpoint in endpoints:
+ if isinstance(endpoint, CopyDataDivEndpoint):
+ lock = LockInfo(sc.STORAGE, endpoint.sd_id, rm.LockType.shared)
+ locks.add(lock)
+ return sorted(locks)
+
+ def _get_image_locks(self, src, dest):
+ locks = []
+ if isinstance(src, CopyDataDivEndpoint):
+ lock = LockInfo(IMAGE_NAMESPACE, src.img_id, rm.LockType.shared)
+ locks.append(lock)
+ if isinstance(dest, CopyDataDivEndpoint):
+ lock = LockInfo(IMAGE_NAMESPACE, dest.img_id,
+ rm.LockType.exclusive)
+ locks.append(lock)
+ return sorted(locks)
+
+ def _release(self):
+ for lock in self._held_locks:
+ rmanager.releaseResource(lock.ns, lock.name)
+
+
+class LockInfo(object):
+
+ def __init__(self, ns, name, mode):
+ self.ns = ns
+ self.name = name
+ self.mode = mode
+
+ def __cmp__(self, other):
+ return cmp(self.ns + self.name, other.ns + other.name)
+
+
class CopyDataDivEndpoint(properties.Owner):
sd_id = properties.UUID(required=True)
img_id = properties.UUID(required=True)
--
To view, visit https://gerrit.ovirt.org/61353
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I1b008c38dab5478f13175659bd2103e994802cf7
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 8 months
Change in vdsm[master]: wip: copy_data locking
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: wip: copy_data locking
......................................................................
wip: copy_data locking
Change-Id: Ie44e1f1709ee81c14b3d165bbf3a6597ac50b68e
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M tests/storage_sdm_copy_data_test.py
M vdsm/storage/sdm/api/copy_data.py
2 files changed, 94 insertions(+), 16 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/25/60825/1
diff --git a/tests/storage_sdm_copy_data_test.py b/tests/storage_sdm_copy_data_test.py
index 3e90778..b6e2f98 100644
--- a/tests/storage_sdm_copy_data_test.py
+++ b/tests/storage_sdm_copy_data_test.py
@@ -37,6 +37,11 @@
import storage.sdm.api.copy_data
+class FakeTaskContext(object):
+ def getSharedLock(*args):
+ pass
+
+
@expandPermutations
class CopyDataTests(VdsmTestCase):
SIZE = 1048576
@@ -54,8 +59,11 @@
with make_env() as env:
rm = FakeResourceManager()
+ fake_task_ctx = FakeTaskContext()
with MonkeyPatchScope([
+ (storage.sdm.api.copy_data, 'rmanager', rm),
(storage.sdm.api.copy_data, 'sdCache', env.sdcache),
+ (storage.sdm.api.copy_data.vars, 'task', fake_task_ctx),
(blockVolume, 'rmanager', rm),
]):
src_img_id = str(uuid.uuid4())
diff --git a/vdsm/storage/sdm/api/copy_data.py b/vdsm/storage/sdm/api/copy_data.py
index 82e0e61..5956477 100644
--- a/vdsm/storage/sdm/api/copy_data.py
+++ b/vdsm/storage/sdm/api/copy_data.py
@@ -19,6 +19,7 @@
#
from __future__ import absolute_import
+from collections import namedtuple
from contextlib import contextmanager
import logging
@@ -26,11 +27,19 @@
from vdsm import properties
from vdsm import qemuimg
from vdsm.storage import constants as sc
+from vdsm.storage.threadlocal import vars
-from storage import volume
+from storage import resourceManager as rm
+from storage import sd, volume
+from storage.resourceFactories import IMAGE_NAMESPACE
from storage.sdc import sdCache
from . import base
+
+rmanager = rm.ResourceManager.getInstance()
+
+
+DivEndpointImageLock = namedtuple('DivEndpointImageLock', 'ns, img, type')
class Job(base.Job):
@@ -43,8 +52,13 @@
def __init__(self, job_id, host_id, source, destination):
super(Job, self).__init__(job_id, 'copy_data', host_id)
self._operation = None
+ self._sorted_domain_locks = []
+ self._sorted_image_locks = []
+
self.source = _create_endpoint(source)
self.destination = _create_endpoint(destination)
+ self._determine_vdsm_locks()
+ self._acquire_vdsm_domains()
@property
def progress(self):
@@ -54,22 +68,78 @@
if self._operation:
self._operation.abort()
- def _run(self):
- # TODO: LOCKING!
- with self.source.prepare(), \
- self.destination.prepare(writable=True):
- # Do not start copying if we have already been aborted
- if self._status == jobs.STATUS.ABORTED:
- return
+ def _determine_vdsm_locks(self):
+ # Locks taken:
+ # - Domain (one shared lock per domain involved in the operation)
+ # - Image (shared on source, exclusive on destination):
+ # - Activates logical volumes when using block storage
+ # - Protects source image against modification on this host
+ # - Reserves destination image for exclusive access on this host
+ # Locking convention:
+ # - All domain locks must be taken before any image locks.
+ # - Locks must be taken in sorted order to prevent deadlock.
+ # Caveats:
+ # - These locks don't block other hosts. We would need to use
+ # volume leases for all operations to get this protection. For now
+ # engine is responsible for avoiding conflicting commands.
+ # - Taking an exclusive lock on the destination image prevents
+ # concurrent copy_data operations from taking place on the same host.
+ # To allow this, we'll need to lock the destination image in shared
+ # mode and the destination volume in exclusive mode. This requires
+ # changes to the resourceManager.
+ domain_locks = set()
+ image_locks = list()
+ if isinstance(self.source, CopyDataDivEndpoint):
+ domain_locks.add(self.source.sd_id)
+ img_res_ns = sd.getNamespace(self.source.sd_id, IMAGE_NAMESPACE)
+ lock = DivEndpointImageLock(img_res_ns, self.source.img_id,
+ rm.LockType.shared)
+ image_locks.append(lock)
+ if isinstance(self.destination, CopyDataDivEndpoint):
+ domain_locks.add(self.destination.sd_id)
+ img_res_ns = sd.getNamespace(self.destination.sd_id,
+ IMAGE_NAMESPACE)
+ lock = DivEndpointImageLock(img_res_ns, self.destination.img_id,
+ rm.LockType.exclusive)
+ image_locks.append(lock)
- self._operation = qemuimg.convert(
- self.source.path,
- self.destination.path,
- srcFormat=self.source.qemu_format,
- dstFormat=self.destination.qemu_format,
- backing=self.destination.backing_path,
- backingFormat=self.destination.backing_qemu_format)
- self._operation.wait_for_completion()
+ # Sort multiple locks taken at each level
+ self._sorted_domain_locks = sorted(list(domain_locks))
+ self._sorted_image_locks = sorted(image_locks,
+ key=lambda info: info.ns + info.img)
+
+ def _acquire_vdsm_domains(self):
+ # These locks must be acquired before the job is scheduled.
+ for domain in self._sorted_domain_locks:
+ vars.task.getSharedLock(sc.STORAGE, domain)
+
+ @contextmanager
+ def _acquired_vdsm_images(self):
+ locks = []
+ try:
+ for lock_info in self._sorted_image_locks:
+ locks.append(rmanager.acquireResource(*lock_info))
+ yield
+ finally:
+ for lock in locks:
+ lock.release()
+
+ def _run(self):
+ with self._acquired_vdsm_images():
+ with self.source.prepare(), \
+ self.destination.prepare(writable=True):
+ # Do not start copying if we have already been aborted
+ if self._status == jobs.STATUS.ABORTED:
+ return
+
+ self._operation = qemuimg.convert(
+ self.source.path,
+ self.destination.path,
+ srcFormat=self.source.qemu_format,
+ dstFormat=self.destination.qemu_format,
+ backing=self.destination.backing_path,
+ backingFormat=self.destination.backing_qemu_format)
+ self._operation.wait_for_completion()
def _create_endpoint(params):
--
To view, visit https://gerrit.ovirt.org/60825
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie44e1f1709ee81c14b3d165bbf3a6597ac50b68e
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 8 months
Change in vdsm[master]: tests: Move TestingJob to sdmtestlib
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: tests: Move TestingJob to sdmtestlib
......................................................................
tests: Move TestingJob to sdmtestlib
The TestingJob currently used in storage_sdm_api_test.py is needed for testing
Job handling in the hsm tests. Move it to a common place where it can be
reused.
Change-Id: Ibf4b1707f3e6a56918a52511f23e21ead1bd5adc
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M tests/sdmtestlib.py
M tests/storage_sdm_api_test.py
2 files changed, 21 insertions(+), 19 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/56/61256/1
diff --git a/tests/sdmtestlib.py b/tests/sdmtestlib.py
index 8f95218..2844a6c 100644
--- a/tests/sdmtestlib.py
+++ b/tests/sdmtestlib.py
@@ -18,8 +18,28 @@
#
import time
+import uuid
+
+from vdsm import jobs
+from vdsm.storage.threadlocal import vars
+
+from storage.sdm.api import base
def wait_for_job(job):
while job.active:
time.sleep(1)
+
+
+class TestingJob(base.Job):
+
+ def __init__(self, exception=None):
+ job_id = str(uuid.uuid4())
+ super(TestingJob, self).__init__(job_id, 'testing_job', 'host_id')
+ self.exception = exception
+
+ def _run(self):
+ assert(self.status == jobs.STATUS.RUNNING)
+ assert(vars.job_id == self.id)
+ if self.exception:
+ raise self.exception
diff --git a/tests/storage_sdm_api_test.py b/tests/storage_sdm_api_test.py
index a5f00bc..3c4367e 100644
--- a/tests/storage_sdm_api_test.py
+++ b/tests/storage_sdm_api_test.py
@@ -18,16 +18,12 @@
# Refer to the README and COPYING files for full details of the license
#
-import uuid
-
from testlib import VdsmTestCase
-from sdmtestlib import wait_for_job
+from sdmtestlib import TestingJob, wait_for_job
from vdsm import jobs
from vdsm import exception
from vdsm.storage.threadlocal import vars
-
-from storage.sdm.api import base
class ApiBaseTests(VdsmTestCase):
@@ -63,17 +59,3 @@
self.run_job(job)
self.assertEqual(jobs.STATUS.FAILED, job.status)
self.assertIsInstance(job.error, exception.VdsmException)
-
-
-class TestingJob(base.Job):
-
- def __init__(self, exception=None):
- job_id = str(uuid.uuid4())
- super(TestingJob, self).__init__(job_id, 'testing_job', 'host_id')
- self.exception = exception
-
- def _run(self):
- assert(self.status == jobs.STATUS.RUNNING)
- assert(vars.job_id == self.id)
- if self.exception:
- raise self.exception
--
To view, visit https://gerrit.ovirt.org/61256
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ibf4b1707f3e6a56918a52511f23e21ead1bd5adc
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 8 months
Change in vdsm[master]: sdm: Wire up when_finished callback
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: sdm: Wire up when_finished callback
......................................................................
sdm: Wire up when_finished callback
Change-Id: I8db3abc6e57a4a06b90ea1c7bb4186b0b936a152
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M tests/storage_sdm_api_test.py
M vdsm/storage/sdm/api/base.py
2 files changed, 7 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/55/61255/1
diff --git a/tests/storage_sdm_api_test.py b/tests/storage_sdm_api_test.py
index c8d93d2..a5f00bc 100644
--- a/tests/storage_sdm_api_test.py
+++ b/tests/storage_sdm_api_test.py
@@ -33,10 +33,16 @@
class ApiBaseTests(VdsmTestCase):
def run_job(self, job):
+ def finished_cb():
+ called[0] = True
+
+ called = [False]
+ job.when_finished(finished_cb)
self.assertEqual(jobs.STATUS.PENDING, job.status)
self.assertIsNone(getattr(vars, 'job_id', None))
job.run()
wait_for_job(job)
+ self.assertTrue(called[0])
self.assertIsNone(getattr(vars, 'job_id', None))
def test_states(self):
diff --git a/vdsm/storage/sdm/api/base.py b/vdsm/storage/sdm/api/base.py
index a342300..23603df 100644
--- a/vdsm/storage/sdm/api/base.py
+++ b/vdsm/storage/sdm/api/base.py
@@ -51,4 +51,5 @@
else:
self._status = jobs.STATUS.DONE
finally:
+ self._finished_cb()
vars.job_id = None
--
To view, visit https://gerrit.ovirt.org/61255
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I8db3abc6e57a4a06b90ea1c7bb4186b0b936a152
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 8 months