Change in vdsm[master]: fencing: Make getHostLeaseStatus API public
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: fencing: Make getHostLeaseStatus API public
......................................................................
fencing: Make getHostLeaseStatus API public
Getting host lease status will allow engine to make smarter decisions
when a host is non-responsive by using a proxy host to get the
non-responsive host status.
See http://pastebin.com/KqqeAdSu for example output from this API.
Change-Id: I415c1fee6256bf8d4e03ee542cc58e193162e9b8
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M client/vdsClient.py
M vdsm/API.py
M vdsm/rpc/BindingXMLRPC.py
M vdsm/rpc/Bridge.py
M vdsm/rpc/vdsmapi-schema.json
5 files changed, 62 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/57/29157/1
diff --git a/client/vdsClient.py b/client/vdsClient.py
index 2c09b28..aea1503 100644
--- a/client/vdsClient.py
+++ b/client/vdsClient.py
@@ -1760,6 +1760,18 @@
status = self.s.stopMonitoringDomain(sdUUID)
return status['status']['code'], status['status']['message']
+ def getHostLeaseStatus(self, args):
+ domains = {}
+ for pair in args:
+ sdUUID, hostId = pair.split('=', 1)
+ domains[sdUUID] = int(hostId)
+ response = self.s.getHostLeaseStatus(domains)
+ if response['status']['code']:
+ print "Cannot get host storage liveliness"
+ return response['status']['code'], response['status']['message']
+ pp.pprint(response['domains'])
+ return 0, ''
+
def snapshot(self, args):
vmUUID, sdUUID, imgUUID, baseVolUUID, volUUID = args
@@ -2579,6 +2591,11 @@
('<sdUUID>',
'Stop monitoring SD: sdUUID'
)),
+ 'getHostLeaseStatus': (serv.getHostLeaseStatus,
+ ('<sdUUID>=<hostId> [<sdUUID>=<hostId>] ...',
+ 'Returns host lease status for hostId on '
+ 'each domain.'
+ )),
'snapshot': (serv.snapshot,
('<vmId> <sdUUID> <imgUUID> <baseVolUUID> <volUUID>',
'Take a live snapshot'
diff --git a/vdsm/API.py b/vdsm/API.py
index e739294..0b44459 100644
--- a/vdsm/API.py
+++ b/vdsm/API.py
@@ -1497,6 +1497,9 @@
def stopMonitoringDomain(self, sdUUID):
return self._irs.stopMonitoringDomain(sdUUID)
+ def getHostLeaseStatus(self, domains):
+ return self._irs.getHostLeaseStatus(domains)
+
def getLVMVolumeGroups(self, storageType=None):
return self._irs.getVGList(storageType)
diff --git a/vdsm/rpc/BindingXMLRPC.py b/vdsm/rpc/BindingXMLRPC.py
index c1c7490..a06a3b4 100644
--- a/vdsm/rpc/BindingXMLRPC.py
+++ b/vdsm/rpc/BindingXMLRPC.py
@@ -917,6 +917,10 @@
api = API.Global()
return api.stopMonitoringDomain(sdUUID)
+ def getHostLeaseStatus(self, domains, options=None):
+ api = API.Global()
+ return api.getHostLeaseStatus(domains)
+
def vgsGetList(self, storageType=None, options=None):
api = API.Global()
return api.getLVMVolumeGroups(storageType)
@@ -1070,6 +1074,7 @@
(self.storageRepoGetStats, 'repoStats'),
(self.startMonitoringDomain, 'startMonitoringDomain'),
(self.stopMonitoringDomain, 'stopMonitoringDomain'),
+ (self.getHostLeaseStatus, 'getHostLeaseStatus'),
(self.vgsGetList, 'getVGList'),
(self.devicesGetList, 'getDeviceList'),
(self.devicesGetVisibility, 'getDevicesVisibility'),
diff --git a/vdsm/rpc/Bridge.py b/vdsm/rpc/Bridge.py
index 7e898de..ba700d1 100644
--- a/vdsm/rpc/Bridge.py
+++ b/vdsm/rpc/Bridge.py
@@ -349,6 +349,7 @@
'Host_getStorageRepoStats': {'ret': Host_getStorageRepoStats_Ret},
'Host_startMonitoringDomain': {},
'Host_stopMonitoringDomain': {},
+ 'Host_getHostLeaseStatus': {'ret': 'domains'},
'Host_getVMList': {'call': Host_getVMList_Call, 'ret': Host_getVMList_Ret},
'Host_getVMFullList': {'call': Host_getVMFullList_Call, 'ret': 'vmList'},
'Host_getAllVmStats': {'ret': 'statsList'},
diff --git a/vdsm/rpc/vdsmapi-schema.json b/vdsm/rpc/vdsmapi-schema.json
index 0c8a6f6..7617185 100644
--- a/vdsm/rpc/vdsmapi-schema.json
+++ b/vdsm/rpc/vdsmapi-schema.json
@@ -2052,6 +2052,42 @@
'returns': ''}
##
+# @HostIdMap:
+#
+# A mapping of hostId indexed by domain UUID.
+#
+# Since: 4.15.0
+##
+{'map': 'HostIdMap',
+ 'key': 'UUID', 'value': 'int'}
+
+##
+# @HostLeaseStatusMap:
+#
+# A mapping of status codes indexed by domain UUID.
+#
+# Since: 4.15.0
+##
+{'map': 'HostLeaseStatusMap',
+ 'key': 'UUID', 'value': 'str'}
+
+##
+# @Host.getHostLeaseStatus:
+#
+# Returns host status for for specified domains
+#
+# @domains: A mapping of hostId indexed by domain UUID
+#
+# Returns:
+# Host status code for each domain
+#
+# Since: 4.15.0
+##
+{'command': {'class': 'Host', 'name': 'getHostLeaseStatus'},
+ 'data': {'domains': 'HostIdMap'}
+ 'returns': {'domains': 'HostLeaseStatusMap'}}
+
+##
# @VmStatus:
#
# An enumeration of possible virtual machine statuses.
--
To view, visit http://gerrit.ovirt.org/29157
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I415c1fee6256bf8d4e03ee542cc58e193162e9b8
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
6 years, 6 months
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>
6 years, 6 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>
6 years, 6 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>
6 years, 6 months
Change in vdsm[master]: vdsm: add eventfd and EventFile synchronization
by Federico Simoncelli
Federico Simoncelli has uploaded a new change for review.
Change subject: vdsm: add eventfd and EventFile synchronization
......................................................................
vdsm: add eventfd and EventFile synchronization
Change-Id: I0d237f13c42b1f4505c90d30c6d3c3ecbd1e9fa7
Signed-off-by: Federico Simoncelli <fsimonce(a)redhat.com>
---
M lib/vdsm/Makefile.am
A lib/vdsm/eventfd.py
M tests/Makefile.am
A tests/eventfdTests.py
4 files changed, 251 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/87/33687/1
diff --git a/lib/vdsm/Makefile.am b/lib/vdsm/Makefile.am
index 4bebf28..e712cad 100644
--- a/lib/vdsm/Makefile.am
+++ b/lib/vdsm/Makefile.am
@@ -25,6 +25,7 @@
__init__.py \
compat.py \
define.py \
+ eventfd.py \
exception.py \
ipwrapper.py \
libvirtconnection.py \
diff --git a/lib/vdsm/eventfd.py b/lib/vdsm/eventfd.py
new file mode 100644
index 0000000..b2a7084
--- /dev/null
+++ b/lib/vdsm/eventfd.py
@@ -0,0 +1,140 @@
+#
+# Copyright 2014 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
+#
+
+"""\
+This module provides the support for eventfd(2).
+
+More information about eventfd and usage examples can be found in the
+eventfd(2) man page.
+
+The EventFile class provides a single synchronization object exposing
+the python Event interface and associated eventfds.
+
+The eventfd() context manager returns a file descriptor that can be
+used to provide the event notice to select, poll and epoll, e.g.
+
+ import os
+ import sys
+ import select
+ import threading
+ import time
+ from vdsm.eventfd import EventFile, DATASIZE
+
+ e = EventFile()
+ p = select.epoll()
+
+ threading.Timer(5, e.set).start()
+
+ with e.eventfd() as efd:
+ p.register(efd, select.EPOLLIN)
+ p.register(sys.stdin.fileno(), select.EPOLLIN)
+
+ print "Echoing lines until event is received"
+ event_received = False
+
+ while not event_received:
+ for fileno, event in p.poll():
+ if not event & select.EPOLLIN:
+ continue
+
+ if fileno == efd:
+ os.read(efd, DATASIZE)
+ event_received = True
+ elif fileno == sys.stdin.fileno():
+ print os.read(sys.stdin.fileno(), 1024),
+
+ print "Event received!"
+
+
+The Event set() semantic is preserved in the eventfd context manager:
+if the event is set then the eventfd already contains the notification.
+This is both to maintain the semantic and to avoid possible races as:
+
+ if not e.is_set():
+ with e.eventfd() as efd:
+ ...
+"""
+
+import os
+import ctypes
+import threading
+
+from contextlib import contextmanager
+
+_libc = ctypes.CDLL('libc.so.6', use_errno=True)
+
+EFD_NONBLOCK = os.O_NONBLOCK
+EFD_CLOEXEC = 02000000 # os.O_CLOEXEC in python 3.3
+EFD_SEMAPHORE = 00000001
+
+DATASIZE = ctypes.sizeof(ctypes.c_ulonglong)
+
+
+def eventfd(initval, flags):
+ return _libc.eventfd(initval, flags)
+
+
+class EventFile(object):
+ def __init__(self, event=None):
+ self.__lock = threading.Lock()
+ self.__fds = set()
+ self.__event = event or threading.Event()
+
+ @staticmethod
+ def __fire_event(fd):
+ os.write(fd, ctypes.c_ulonglong(1))
+
+ def open_eventfd(self):
+ with self.__lock:
+ fd = eventfd(0, 0)
+
+ self.__fds.add(fd)
+
+ if self.__event.is_set():
+ self.__fire_event(fd)
+
+ return fd
+
+ @contextmanager
+ def eventfd(self):
+ fd = self.open_eventfd()
+
+ yield fd
+
+ with self.__lock:
+ self.__fds.remove(fd)
+ os.close(fd)
+
+ def isSet(self):
+ return self.__event.isSet()
+
+ is_set = isSet
+
+ def set(self):
+ with self.__lock:
+ self.__event.set()
+ for fd in self.__fds:
+ self.__fire_event(fd)
+
+ def clear(self):
+ self.__event.clear()
+
+ def wait(self, timeout=None, balancing=True):
+ self.__event.wait(timeout)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 449d7b1..120712e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -31,6 +31,7 @@
clientifTests.py \
configNetworkTests.py \
domainMonitorTests.py \
+ eventfdTests.py \
fileVolumeTests.py \
fileUtilTests.py \
fuserTests.py \
diff --git a/tests/eventfdTests.py b/tests/eventfdTests.py
new file mode 100644
index 0000000..be15248
--- /dev/null
+++ b/tests/eventfdTests.py
@@ -0,0 +1,109 @@
+#
+# Copyright 2014 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 os
+import select
+from vdsm.eventfd import EventFile, DATASIZE
+from nose.tools import timed, raises, TimeExpired
+
+TEST_TIMEOUT = 1
+WAIT_TIMEOUT = 2
+
+
+def test_set():
+ e = EventFile()
+ e.set()
+ assert e.is_set()
+ assert e.isSet()
+
+
+def text_clear():
+ e = EventFile()
+ e.set()
+ assert e.is_set()
+ e.clear()
+ assert not e.is_set()
+
+
+@timed(TEST_TIMEOUT)
+def test_wait_set():
+ e = EventFile()
+ e.set()
+ e.wait(WAIT_TIMEOUT)
+
+
+@raises(TimeExpired)
+@timed(TEST_TIMEOUT)
+def test_wait_noset():
+ e = EventFile()
+ e.wait(WAIT_TIMEOUT)
+
+
+@timed(TEST_TIMEOUT)
+def test_eventfd_earlyset():
+ e = EventFile()
+ e.set()
+ with e.eventfd() as fd:
+ assert len(__select_and_read(fd)) == DATASIZE
+
+
+@timed(TEST_TIMEOUT)
+def test_eventfd_lateset():
+ e = EventFile()
+ with e.eventfd() as fd:
+ e.set()
+ assert len(__select_and_read(fd)) == DATASIZE
+
+
+@raises(TimeExpired)
+@timed(TEST_TIMEOUT)
+def test_eventfd_noset():
+ e = EventFile()
+ with e.eventfd() as fd:
+ assert len(__select_and_read(fd)) != DATASIZE
+
+
+@timed(TEST_TIMEOUT)
+def test_eventfd_multiple():
+ e = EventFile()
+ e.set()
+ with e.eventfd() as fd1:
+ assert len(__select_and_read(fd1)) == DATASIZE
+ with e.eventfd() as fd2:
+ assert len(__select_and_read(fd2)) == DATASIZE
+ with e.eventfd() as fd3:
+ assert len(__select_and_read(fd3)) == DATASIZE
+
+
+@raises(TimeExpired)
+@timed(TEST_TIMEOUT)
+def test_eventfd_clear():
+ e = EventFile()
+ e.set()
+ e.clear()
+ with e.eventfd() as fd:
+ assert len(__select_and_read(fd)) != DATASIZE
+
+
+def __select_and_read(fd):
+ rd, wr, ex = select.select((fd,), (), (), WAIT_TIMEOUT)
+ if fd in rd:
+ return os.read(fd, DATASIZE)
+ return ''
--
To view, visit http://gerrit.ovirt.org/33687
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I0d237f13c42b1f4505c90d30c6d3c3ecbd1e9fa7
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Federico Simoncelli <fsimonce(a)redhat.com>
6 years, 6 months
Change in vdsm[master]: debug: Add virt-alignment-scan.debug tool
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: debug: Add virt-alignment-scan.debug tool
......................................................................
debug: Add virt-alignment-scan.debug tool
Change-Id: I2ec4d0219ea039d57ee8809339a7e03d71980bf8
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
A vdsm/virt-alignment-scan.debug
1 file changed, 24 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/96/40896/1
diff --git a/vdsm/virt-alignment-scan.debug b/vdsm/virt-alignment-scan.debug
new file mode 100755
index 0000000..e904b45
--- /dev/null
+++ b/vdsm/virt-alignment-scan.debug
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Debug tool for virt-alignmnet-scan
+#
+# Installation:
+#
+# mv /usr/bin/virt-alignment-scan /usr/bin/virt-alignment-scan.real
+# ln -s /usr/share/vdsm/virt-alignment-scan.debug /usr/bin/virt-alignment-scan
+#
+# Uinstall
+#
+# mv /usr/bin/virt-alignment-scan.real /usr/bin/virt-alignment-scan
+
+log="/var/log/vdsm/virt-alignmnet-scan.debug.log"
+
+/usr/bin/virt-alignment-scan "$@"
+rc=$?
+if [ $rc -ne 0 ]; then
+ # See https://bugzilla.redhat.com/show_bug.cgi?id=1151838#c28
+ sleep 30
+ /usr/bin/libguestfs-test-tool >> $log 2>&1
+fi
+
+exit $rc
--
To view, visit https://gerrit.ovirt.org/40896
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I2ec4d0219ea039d57ee8809339a7e03d71980bf8
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
6 years, 6 months
Change in vdsm[master]: utils: Add changehash function for change detection
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: utils: Add changehash function for change detection
......................................................................
utils: Add changehash function for change detection
We use Python built-in hash to detect changes in vm state without sending
the state in each response. This function is not suitable for this
usage. Now we use generic utils.changehash(), implemented using md5
hexdigest.
Change-Id: I2242a594383e2d2fe64e3a581f18b8ac662648b0
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M lib/vdsm/utils.py
M vdsm/virt/vm.py
2 files changed, 13 insertions(+), 2 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/45/33045/1
diff --git a/lib/vdsm/utils.py b/lib/vdsm/utils.py
index 23c63e8..1b4a9d5 100644
--- a/lib/vdsm/utils.py
+++ b/lib/vdsm/utils.py
@@ -37,6 +37,7 @@
import glob
import io
import itertools
+import hashlib
import logging
import re
import sys
@@ -1133,3 +1134,13 @@
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
flags |= os.O_NONBLOCK
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
+
+
+def changehash(s):
+ """
+ Returns a hash of string s, suitable for change detection.
+
+ Tipically changehash(s) is sent to client frequently. When a client detect
+ that changehash(s) changed, it ask for s itself, which may be much bigger.
+ """
+ return hashlib.md5(s).hexdigest()
diff --git a/vdsm/virt/vm.py b/vdsm/virt/vm.py
index 941f283..b1567f9 100644
--- a/vdsm/virt/vm.py
+++ b/vdsm/virt/vm.py
@@ -1500,7 +1500,7 @@
self.guestAgent = guestagent.GuestAgent(
self._guestSocketFile, self.cif.channelListener, self.log)
self._lastXMLDesc = '<domain><uuid>%s</uuid></domain>' % self.id
- self._devXmlHash = '0'
+ self._devXmlHash = utils.changehash('')
self._released = False
self._releaseLock = threading.Lock()
self.saveState()
@@ -4495,7 +4495,7 @@
self._lastXMLDesc = self._dom.XMLDesc(0)
devxml = _domParseStr(self._lastXMLDesc).childNodes[0]. \
getElementsByTagName('devices')[0]
- self._devXmlHash = str(hash(devxml.toxml()))
+ self._devXmlHash = utils.changehash(devxml.toxml())
return self._lastXMLDesc
--
To view, visit http://gerrit.ovirt.org/33045
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I2242a594383e2d2fe64e3a581f18b8ac662648b0
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
6 years, 7 months
Change in vdsm[master]: clusterLock: Acquire, release, and inquire volume leases
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: clusterLock: Acquire, release, and inquire volume leases
......................................................................
clusterLock: Acquire, release, and inquire volume leases
Although we have been creating volume leases on storage for awhile now,
there have been no APIs to manage them. Add this support to
the clusterLock implementations.
Change-Id: Icca901ccd27358767c023cd55b7a3823531d2a5a
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M vdsm/storage/clusterlock.py
M vdsm/storage/sd.py
2 files changed, 80 insertions(+), 23 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/22/38622/1
diff --git a/vdsm/storage/clusterlock.py b/vdsm/storage/clusterlock.py
index 51d2906..4077068 100644
--- a/vdsm/storage/clusterlock.py
+++ b/vdsm/storage/clusterlock.py
@@ -73,6 +73,10 @@
"""Raised when the clusterlock class is not supporting inquire"""
+class ResourceLeasesNotSupportedError(Exception):
+ """Raised when the clusterlock class does not support resource leases"""
+
+
class SafeLease(object):
log = logging.getLogger("Storage.SafeLease")
@@ -145,7 +149,13 @@
raise se.AcquireLockFailure(self._sdUUID, rc, out, err)
self.log.debug("Clustered lock acquired successfully")
+ def acquireResource(self, resource, lockDisk, shared=False):
+ raise ResourceLeasesNotSupportedError()
+
def inquire(self):
+ raise InquireNotSupportedError()
+
+ def inquireResource(self, resource, lockDisk):
raise InquireNotSupportedError()
def getLockUtilFullPath(self):
@@ -165,6 +175,8 @@
self.log.debug("Cluster lock released successfully")
+ def releaseResource(self, resource, lockDisk):
+ raise ResourceLeasesNotSupportedError()
initSANLockLog = logging.getLogger("Storage.initSANLock")
@@ -286,13 +298,9 @@
# HOST_FAIL.
return HOST_STATUS_FREE
- # The hostId parameter is maintained here only for compatibility with
- # ClusterLock. We could consider to remove it in the future but keeping it
- # for logging purpose is desirable.
- def acquire(self, hostId):
+ def _acquire(self, resource, lockDisk, shared=False):
with nested(self._lock, SANLock._sanlock_lock):
- self.log.info("Acquiring cluster lock for domain %s (id: %s)",
- self._sdUUID, hostId)
+ self.log.info("Acquiring resource %s, shared=%s", resource, shared)
while True:
if SANLock._sanlock_fd is None:
@@ -304,27 +312,42 @@
"Cannot register to sanlock", str(e))
try:
- sanlock.acquire(self._sdUUID, SDM_LEASE_NAME,
- self.getLockDisk(),
- slkfd=SANLock._sanlock_fd)
+ sanlock.acquire(self._sdUUID, resource, lockDisk,
+ slkfd=SANLock._sanlock_fd, shared=shared)
except sanlock.SanlockException as e:
if e.errno != os.errno.EPIPE:
raise se.AcquireLockFailure(
self._sdUUID, e.errno,
- "Cannot acquire cluster lock", str(e))
+ "Cannot acquire sanlock resource", str(e))
SANLock._sanlock_fd = None
continue
break
- self.log.debug("Cluster lock for domain %s successfully acquired "
- "(id: %s)", self._sdUUID, hostId)
+ self.log.debug("Resource %s successfully acquired", resource)
+
+ # The hostId parameter is maintained here only for compatibility with
+ # ClusterLock. We could consider to remove it in the future but keeping it
+ # for logging purpose is desirable.
+ def acquire(self, hostId):
+ self.log.info("Acquiring cluster lock for domain %s (id: %s)",
+ self._sdUUID, hostId)
+ self._acquire(SDM_LEASE_NAME, self.getLockDisk())
+ self.log.debug("Cluster lock for domain %s successfully acquired "
+ "(id: %s)", self._sdUUID, hostId)
+
+ def acquireResource(self, resource, lockDisk, shared=False):
+ self._acquire(resource, lockDisk, shared)
+ res, owners = self._inquire(resource, lockDisk)
+ self.log.debug("ALITKE: acquire: res:%s owners:%s", res, owners)
+
+ def _inquire(self, resource, lockDisk):
+ res = sanlock.read_resource(*lockDisk[0])
+ owners = sanlock.read_resource_owners(self._sdUUID, resource, lockDisk)
+ return res, owners
def inquire(self):
- resource = sanlock.read_resource(self._leasesPath, SDM_LEASE_OFFSET)
- owners = sanlock.read_resource_owners(self._sdUUID, SDM_LEASE_NAME,
- self.getLockDisk())
-
+ resource, owners = self._inquire(SDM_LEASE_NAME, self.getLockDisk())
if len(owners) == 1:
return resource.get("version"), owners[0].get("host_id")
elif len(owners) > 1:
@@ -334,19 +357,32 @@
return None, None
- def release(self):
+ def inquireResource(self, resource, lockDisk):
+ resource, owners = self._inquire(SDM_LEASE_NAME, self.getLockDisk())
+ return (resource.get("version"),
+ [owner.get("host_id") for owner in owners])
+
+ def _release(self, resource, lockDisk):
with self._lock:
- self.log.info("Releasing cluster lock for domain %s", self._sdUUID)
+ self.log.info("Releasing resource %s", resource)
try:
- sanlock.release(self._sdUUID, SDM_LEASE_NAME,
- self.getLockDisk(), slkfd=SANLock._sanlock_fd)
+ sanlock.release(self._sdUUID, resource, lockDisk,
+ slkfd=SANLock._sanlock_fd)
except sanlock.SanlockException as e:
- raise se.ReleaseLockFailure(self._sdUUID, e)
+ raise se.ReleaseLockFailure(resource, e)
self._sanlockfd = None
- self.log.debug("Cluster lock for domain %s successfully released",
- self._sdUUID)
+ self.log.debug("Resource %s successfully released", resource)
+
+ def release(self):
+ self.log.info("Releasing cluster lock for domain %s", self._sdUUID)
+ self._release(SDM_LEASE_NAME, self.getLockDisk())
+ self.log.debug("Cluster lock for domain %s successfully released",
+ self._sdUUID)
+
+ def releaseResource(self, resource, lockDisk):
+ self._release(resource, lockDisk)
class LocalLock(object):
@@ -462,10 +498,16 @@
self.log.debug("Local lock for domain %s successfully acquired "
"(id: %s)", self._sdUUID, hostId)
+ def acquireResource(self, resource, lockDisk, shared=False):
+ raise ResourceLeasesNotSupportedError()
+
def inquire(self):
with self._globalLockMapSync:
hostId, lockFile = self._getLease()
return self.LVER, hostId if lockFile else None, None
+
+ def inquireResource(self, resource, lockDisk):
+ raise InquireNotSupportedError()
def release(self):
with self._globalLockMapSync:
@@ -483,3 +525,6 @@
self.log.debug("Local lock for domain %s successfully released",
self._sdUUID)
+
+ def releaseResource(self, resource, lockDisk):
+ raise ResourceLeasesNotSupportedError()
\ No newline at end of file
diff --git a/vdsm/storage/sd.py b/vdsm/storage/sd.py
index a771abf..9e6f281 100644
--- a/vdsm/storage/sd.py
+++ b/vdsm/storage/sd.py
@@ -514,6 +514,18 @@
def inquireClusterLock(self):
return self._clusterLock.inquire()
+ def acquireVolumeLease(self, imgUUID, volUUID, shared=False):
+ lockDisk = self.getVolumeLease(imgUUID, volUUID)
+ self._clusterLock.acquireResource(volUUID, [lockDisk], shared)
+
+ def releaseVolumeLease(self, imgUUID, volUUID):
+ lockDisk = self.getVolumeLease(imgUUID, volUUID)
+ self._clusterLock.releaseResource(volUUID, [lockDisk])
+
+ def inquireVolumeLease(self, imgUUID, volUUID):
+ lockDisk = self.getVolumeLease(imgUUID, volUUID)
+ return self._clusterLock.inquireResource(volUUID, [lockDisk])
+
def attach(self, spUUID):
self.invalidateMetadata()
pools = self.getPools()
--
To view, visit https://gerrit.ovirt.org/38622
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Icca901ccd27358767c023cd55b7a3823531d2a5a
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
6 years, 7 months
Change in vdsm[master]: storage: Factor out getAllVolumes
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: storage: Factor out getAllVolumes
......................................................................
storage: Factor out getAllVolumes
The SDM garbage collector needs to a mapping of volumes by imageID.
Factor out this logic from getAllVolumes so it can be reused later.
Change-Id: If8c55edf6416dddc4a4f30b91422b3e968df2b99
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M vdsm/storage/fileSD.py
1 file changed, 17 insertions(+), 12 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/77/40377/1
diff --git a/vdsm/storage/fileSD.py b/vdsm/storage/fileSD.py
index 1220939..042f836 100644
--- a/vdsm/storage/fileSD.py
+++ b/vdsm/storage/fileSD.py
@@ -18,7 +18,6 @@
# Refer to the README and COPYING files for full details of the license
#
-import collections
import os
import errno
import logging
@@ -405,6 +404,21 @@
"""
pass
+ def getImageToVolumesMap(self):
+ volMetaPattern = os.path.join(self.mountpoint, self.sdUUID,
+ sd.DOMAIN_IMAGES, "*", "*.meta")
+ volMetaPaths = self.oop.glob.glob(volMetaPattern)
+ images = {}
+ for metaPath in volMetaPaths:
+ head, tail = os.path.split(metaPath)
+ volUUID, volExt = os.path.splitext(tail)
+ imgUUID = os.path.basename(head)
+ try:
+ images[imgUUID].append(volUUID)
+ except KeyError:
+ images[imgUUID] = [volUUID]
+ return images
+
def getAllVolumes(self):
"""
Return dict {volUUID: ((imgUUIDs,), parentUUID)} of the domain.
@@ -422,17 +436,8 @@
Template volumes have no parent, and thus we report BLANK_UUID as their
parentUUID.
"""
- volMetaPattern = os.path.join(self.mountpoint, self.sdUUID,
- sd.DOMAIN_IMAGES, "*", "*.meta")
- volMetaPaths = self.oop.glob.glob(volMetaPattern)
-
- # First create mapping from images to volumes
- images = collections.defaultdict(list)
- for metaPath in volMetaPaths:
- head, tail = os.path.split(metaPath)
- volUUID, volExt = os.path.splitext(tail)
- imgUUID = os.path.basename(head)
- images[imgUUID].append(volUUID)
+ # First get the mapping from images to volumes
+ images = self.getImageToVolumesMap()
# Using images to volumes mapping, we can create volumes to images
# mapping, detecting template volumes and template images, based on
--
To view, visit https://gerrit.ovirt.org/40377
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: If8c55edf6416dddc4a4f30b91422b3e968df2b99
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
6 years, 7 months
Change in vdsm[master]: clusterlock: Add reference counting
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: clusterlock: Add reference counting
......................................................................
clusterlock: Add reference counting
The clusterLock can now be acquired by multiple threads of execution
since it is used by SDM verbs now. We need reference counting to ensure
that one thread does not release the clusterLock while another thread
still needs it.
Change-Id: I846116ae16e88a51bdce20f97ddf22859dea3086
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M vdsm/storage/clusterlock.py
1 file changed, 55 insertions(+), 45 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/78/40378/1
diff --git a/vdsm/storage/clusterlock.py b/vdsm/storage/clusterlock.py
index 86d47ad..96bba2b 100644
--- a/vdsm/storage/clusterlock.py
+++ b/vdsm/storage/clusterlock.py
@@ -211,6 +211,7 @@
def __init__(self, sdUUID, idsPath, leasesPath, *args):
self._lock = threading.Lock()
+ self._clusterLockUsers = 0
self._sdUUID = sdUUID
self._idsPath = idsPath
self._leasesPath = leasesPath
@@ -302,16 +303,22 @@
# ClusterLock. We could consider to remove it in the future but keeping it
# for logging purpose is desirable.
def acquireClusterLock(self, hostId):
- self.log.info("Acquiring cluster lock for domain %s (id: %s)",
- self._sdUUID, hostId)
- self._acquire(SDM_LEASE_NAME, self.getLockDisk())
- self.log.debug("Cluster lock for domain %s successfully acquired "
- "(id: %s)", self._sdUUID, hostId)
+ with nested(self._lock, SANLock._sanlock_lock):
+ self.log.info("Acquiring cluster lock for domain %s (id: %s)",
+ self._sdUUID, hostId)
+ if self._clusterLockUsers == 0:
+ self._acquire(SDM_LEASE_NAME, self.getLockDisk())
+ self._clusterLockUsers = self._clusterLockUsers + 1
+ self.log.debug("Cluster lock for domain %s successfully acquired "
+ "(id: %s, users: %i)", self._sdUUID, hostId,
+ self._clusterLockUsers)
def acquireResource(self, resource, lockDisk, shared=False):
- self.log.info("Acquiring resource lock for %s", resource)
- self._acquire(resource, lockDisk, shared)
- self.log.debug("Resource lock for %s successfully acquired", resource)
+ with nested(self._lock, SANLock._sanlock_lock):
+ self.log.info("Acquiring resource lock for %s", resource)
+ self._acquire(resource, lockDisk, shared)
+ self.log.debug("Resource lock for %s successfully acquired",
+ resource)
def inquireClusterLock(self):
resource, owners = self._inquire(SDM_LEASE_NAME, self.getLockDisk())
@@ -330,43 +337,47 @@
[owner.get("host_id") for owner in owners])
def releaseClusterLock(self):
- self.log.info("Releasing cluster lock for domain %s", self._sdUUID)
- self._release(SDM_LEASE_NAME, self.getLockDisk())
- self.log.debug("Cluster lock for domain %s successfully released",
- self._sdUUID)
+ with self._lock:
+ self.log.info("Releasing cluster lock for domain %s", self._sdUUID)
+ if self._clusterLockUsers == 1:
+ self._release(SDM_LEASE_NAME, self.getLockDisk())
+ self._clusterLockUsers = self._clusterLockUsers - 1
+ self.log.debug("Cluster lock for domain %s successfully released "
+ "(users: %i)", self._sdUUID, self._clusterLockUsers)
def releaseResource(self, resource, lockDisk):
- self.log.info("Releasing resource lock for %s", resource)
- self._release(resource, lockDisk)
- self.log.debug("Resource lock for %s successfully released", resource)
+ with self._lock:
+ self.log.info("Releasing resource lock for %s", resource)
+ self._release(resource, lockDisk)
+ self.log.debug("Resource lock for %s successfully released",
+ resource)
def _acquire(self, resource, lockDisk, shared=False):
- with nested(self._lock, SANLock._sanlock_lock):
- self.log.info("Acquiring resource %s, shared=%s", resource, shared)
+ self.log.info("Acquiring resource %s, shared=%s", resource, shared)
- while True:
- if SANLock._sanlock_fd is None:
- try:
- SANLock._sanlock_fd = sanlock.register()
- except sanlock.SanlockException as e:
- raise se.AcquireLockFailure(
- self._sdUUID, e.errno,
- "Cannot register to sanlock", str(e))
-
+ while True:
+ if SANLock._sanlock_fd is None:
try:
- sanlock.acquire(self._sdUUID, resource, lockDisk,
- slkfd=SANLock._sanlock_fd, shared=shared)
+ SANLock._sanlock_fd = sanlock.register()
except sanlock.SanlockException as e:
- if e.errno != os.errno.EPIPE:
- raise se.AcquireLockFailure(
- self._sdUUID, e.errno,
- "Cannot acquire sanlock resource", str(e))
- SANLock._sanlock_fd = None
- continue
+ raise se.AcquireLockFailure(
+ self._sdUUID, e.errno, "Cannot register to sanlock",
+ str(e))
- break
+ try:
+ sanlock.acquire(self._sdUUID, resource, lockDisk,
+ slkfd=SANLock._sanlock_fd, shared=shared)
+ except sanlock.SanlockException as e:
+ if e.errno != os.errno.EPIPE:
+ raise se.AcquireLockFailure(
+ self._sdUUID, e.errno,
+ "Cannot acquire sanlock resource", str(e))
+ SANLock._sanlock_fd = None
+ continue
- self.log.debug("Resource %s successfully acquired", resource)
+ break
+
+ self.log.debug("Resource %s successfully acquired", resource)
def _inquire(self, resource, lockDisk):
res = sanlock.read_resource(*lockDisk[0])
@@ -374,17 +385,16 @@
return res, owners
def _release(self, resource, lockDisk):
- with self._lock:
- self.log.info("Releasing resource %s", resource)
+ self.log.info("Releasing resource %s", resource)
- try:
- sanlock.release(self._sdUUID, resource, lockDisk,
- slkfd=SANLock._sanlock_fd)
- except sanlock.SanlockException as e:
- raise se.ReleaseLockFailure(resource, e)
+ try:
+ sanlock.release(self._sdUUID, resource, lockDisk,
+ slkfd=SANLock._sanlock_fd)
+ except sanlock.SanlockException as e:
+ raise se.ReleaseLockFailure(resource, e)
- self._sanlockfd = None
- self.log.debug("Resource %s successfully released", resource)
+ self._sanlockfd = None
+ self.log.debug("Resource %s successfully released", resource)
class LocalLock(object):
--
To view, visit https://gerrit.ovirt.org/40378
To unsubscribe, visit https://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I846116ae16e88a51bdce20f97ddf22859dea3086
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
6 years, 7 months