Change in vdsm[master]: GuestIF Refactoring
by Vinzenz Feenstra
Vinzenz Feenstra has uploaded a new change for review.
Change subject: GuestIF Refactoring
......................................................................
GuestIF Refactoring
Change-Id: Ib357d770a26ef1dc80b89a32bf6808551a7d622d
Signed-off-by: Vinzenz Feenstra <vfeenstr(a)redhat.com>
---
M vdsm/guestIF.py
1 file changed, 114 insertions(+), 76 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/18/24618/1
diff --git a/vdsm/guestIF.py b/vdsm/guestIF.py
index 229a55d..96ad68c 100644
--- a/vdsm/guestIF.py
+++ b/vdsm/guestIF.py
@@ -39,6 +39,115 @@
union(set(range(0x86, 0x9F + 1)))
+class UnknownMessageError(Exception):
+ def __init__(self, message, args):
+ Exception.__init__(self, 'Unknown or unsupported guest agent message '
+ '"%s" received with args "%s"' % (message,
+ str(args)))
+
+
+class MessageHandler(object):
+ def __init__(self, agent):
+ self.log = agent.log
+ self._agent = agent
+
+ def __call__(self, message, args):
+ handler = self.getattr(self, message.replace('-', '_'), None)
+ if handler:
+ handler(args)
+ else:
+ raise UnknownMessageError(message, args)
+
+ def applications(self, args):
+ self._agent.guestInfo['appsList'] = args['applications']
+
+ def fqdn(self, args):
+ self._agent.guestInfo['guestFQDN'] = args['fqdn']
+
+ def host_name(self, args):
+ self._agent.guestInfo['guestName'] = args['name']
+
+ def os_version(self, args):
+ self._agent.guestInfo['guestOs'] = args['version']
+
+ def session_lock(self, args):
+ self.agent.guestInfo['session'] = 'Locked'
+
+ def session_logoff(self, args):
+ self.agent.guestInfo['session'] = 'LoggedOff'
+
+ def session_logon(self, args):
+ self.agent.guestInfo['session'] = 'UserLoggedOn'
+
+ def session_unlock(self, args):
+ self.agent.guestInfo['session'] = 'Active'
+
+ def session_shutdown(self, args):
+ self.log.debug('Guest system shuts down')
+
+ def session_startup(self, args):
+ self.log.debug('Guest system started or restarted')
+
+ def uninstalled(self, args):
+ self.log.debug('Guest agent was uninstalled')
+ self._agent.guestInfo['appsList'] = []
+
+ def heartbeat(self, args):
+ self._agent.guestStatus = 'Up'
+ self._agent.guestInfo['memUsage'] = int(args['free-ram'])
+ # ovirt-guest-agent reports the following fields in 'memory-stat':
+ # 'mem_total', 'mem_free', 'mem_unused', 'swap_in', 'swap_out',
+ # 'pageflt' and 'majflt'
+ if 'memory-stat' in args:
+ for (k, v) in args['memory-stat'].iteritems():
+ # Convert the value to string since 64-bit integer is not
+ # supported in XMLRPC
+ self._agent.guestInfo['memoryStats'][k] = str(v)
+
+ if 'apiVersion' in args:
+ # The guest agent supports API Versioning
+ self._agent._handleAPIVersion(args['apiVersion'])
+ elif self._agent.effectiveApiVersion != _IMPLICIT_API_VERSION_ZERO:
+ # Older versions of the guest agent (before the introduction
+ # of API versioning) do not report this field
+ # Disable the API if not already disabled (e.g. after
+ # downgrade of the guest agent)
+ self.log.debug("API versioning no longer reported by guest.")
+ self._agent.effectiveApiVersion = _IMPLICIT_API_VERSION_ZERO
+
+ def network_interfaces(self, args):
+ interfaces = []
+ old_ips = ''
+ for iface in args['interfaces']:
+ iface['inet'] = iface.get('inet', [])
+ iface['inet6'] = iface.get('inet6', [])
+ interfaces.append(iface)
+ # Provide the old information which includes
+ # only the IP addresses.
+ old_ips += ' '.join(iface['inet']) + ' '
+ self._agent.guestInfo['netIfaces'] = interfaces
+ self._agent.guestInfo['guestIPs'] = old_ips.strip()
+
+ def active_user(self, args):
+ currentUser = args['name']
+ if ((currentUser != self._agent.guestInfo['username']) and
+ not (currentUser == 'Unknown' and
+ self._agent.guestInfo['username'] == 'None')):
+ self._agent.guestInfo['username'] = currentUser
+ self._agent.guestInfo['lastLogin'] = time.time()
+ self.log.debug("username: %s", repr(self.guestInfo['username']))
+
+ def disks_usage(self, args):
+ disks = []
+ for disk in args['disks']:
+ # Converting to string because XML-RPC doesn't support 64-bit
+ # integers.
+ disk['total'] = str(disk['total'])
+ disk['used'] = str(disk['used'])
+ disks.append(disk)
+ self._agent.guestInfo['disksUsage'] = disks
+
+
def _filterXmlChars(u):
"""
The set of characters allowed in XML documents is described in
@@ -109,6 +218,7 @@
def __init__(self, socketName, channelListener, log, user='Unknown',
ips='', connect=True):
+ self.handler = MessageHandler(self)
self.effectiveApiVersion = _IMPLICIT_API_VERSION_ZERO
self.log = log
self._socketName = socketName
@@ -223,82 +333,10 @@
self.log.log(logging.TRACE, "Guest's message %s: %s", message, args)
if self.guestStatus is None:
self.guestStatus = 'Up'
- if message == 'heartbeat':
- self.guestStatus = 'Up'
- self.guestInfo['memUsage'] = int(args['free-ram'])
- # ovirt-guest-agent reports the following fields in 'memory-stat':
- # 'mem_total', 'mem_free', 'mem_unused', 'swap_in', 'swap_out',
- # 'pageflt' and 'majflt'
- if 'memory-stat' in args:
- for (k, v) in args['memory-stat'].iteritems():
- # Convert the value to string since 64-bit integer is not
- # supported in XMLRPC
- self.guestInfo['memoryStats'][k] = str(v)
-
- if 'apiVersion' in args:
- # The guest agent supports API Versioning
- self._handleAPIVersion(args['apiVersion'])
- elif self.effectiveApiVersion != _IMPLICIT_API_VERSION_ZERO:
- # Older versions of the guest agent (before the introduction
- # of API versioning) do not report this field
- # Disable the API if not already disabled (e.g. after
- # downgrade of the guest agent)
- self.log.debug("API versioning no longer reported by guest.")
- self.effectiveApiVersion = _IMPLICIT_API_VERSION_ZERO
- elif message == 'host-name':
- self.guestInfo['guestName'] = args['name']
- elif message == 'os-version':
- self.guestInfo['guestOs'] = args['version']
- elif message == 'network-interfaces':
- interfaces = []
- old_ips = ''
- for iface in args['interfaces']:
- iface['inet'] = iface.get('inet', [])
- iface['inet6'] = iface.get('inet6', [])
- interfaces.append(iface)
- # Provide the old information which includes
- # only the IP addresses.
- old_ips += ' '.join(iface['inet']) + ' '
- self.guestInfo['netIfaces'] = interfaces
- self.guestInfo['guestIPs'] = old_ips.strip()
- elif message == 'applications':
- self.guestInfo['appsList'] = args['applications']
- elif message == 'active-user':
- currentUser = args['name']
- if ((currentUser != self.guestInfo['username']) and
- not (currentUser == 'Unknown' and
- self.guestInfo['username'] == 'None')):
- self.guestInfo['username'] = currentUser
- self.guestInfo['lastLogin'] = time.time()
- self.log.debug("username: %s", repr(self.guestInfo['username']))
- elif message == 'session-logon':
- self.guestInfo['session'] = "UserLoggedOn"
- elif message == 'session-lock':
- self.guestInfo['session'] = "Locked"
- elif message == 'session-unlock':
- self.guestInfo['session'] = "Active"
- elif message == 'session-logoff':
- self.guestInfo['session'] = "LoggedOff"
- elif message == 'uninstalled':
- self.log.debug("RHEV agent was uninstalled.")
- self.guestInfo['appsList'] = []
- elif message == 'session-startup':
- self.log.debug("Guest system is started or restarted.")
- elif message == 'fqdn':
- self.guestInfo['guestFQDN'] = args['fqdn']
- elif message == 'session-shutdown':
- self.log.debug("Guest system shuts down.")
- elif message == 'disks-usage':
- disks = []
- for disk in args['disks']:
- # Converting to string because XML-RPC doesn't support 64-bit
- # integers.
- disk['total'] = str(disk['total'])
- disk['used'] = str(disk['used'])
- disks.append(disk)
- self.guestInfo['disksUsage'] = disks
- else:
- self.log.error('Unknown message type %s', message)
+ try:
+ self.handler(message, args)
+ except UnknownMessageError as e:
+ self.log.error(e)
def stop(self):
self._stopped = True
--
To view, visit http://gerrit.ovirt.org/24618
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib357d770a26ef1dc80b89a32bf6808551a7d622d
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Vinzenz Feenstra <vfeenstr(a)redhat.com>
7 years, 2 months
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>
7 years, 2 months
Change in vdsm[master]: tests: Add time per test if nose-timer is available
by asegurap@redhat.com
Antoni Segura Puimedon has uploaded a new change for review.
Change subject: tests: Add time per test if nose-timer is available
......................................................................
tests: Add time per test if nose-timer is available
This patch adds an output like:
resourceManagerTests.ResourceManagerTests.testStressTest: 12.8313s
resourceManagerTests.ResourceManagerTests.testResourceAcquireTimeout: 1.0069s
resourceManagerTests.ResourceManagerTests.testResourceLockSwitch: 0.0108s
resourceManagerTests.ResourceManagerTests.testResourceAutorelease: 0.0101s
resourceManagerTests.ResourceManagerTests.testResourceInvalidation: 0.0093s
resourceManagerTests.ResourceManagerTests.testCrashOnSwitch: 0.0067s
resourceManagerTests.ResourceManagerTests.testResourceSwitchLockTypeFail:
0.0062s
resourceManagerTests.ResourceManagerTests.testCancelExclusiveBetweenShared:
0.0049s
resourceManagerTests.ResourceManagerTests.testAcquireResourceExclusive: 0.0042s
resourceManagerTests.ResourceManagerTests.testCancelRequest: 0.0031s
resourceManagerTests.ResourceManagerTests.testRequestRefCmp: 0.0030s
resourceManagerTests.ResourceManagerTests.testRequestRecancel: 0.0030s
resourceManagerTests.ResourceManagerTests.testResourceStatuses: 0.0029s
resourceManagerTests.ResourceManagerTests.testAccessAttributeNotExposedByRequestRef:
0.0029s
resourceManagerTests.ResourceManagerTests.testFailCreateAfterSwitch: 0.0025s
resourceManagerTests.ResourceManagerTests.testRequestWithBadCallbackOnCancel:
0.0025s
resourceManagerTests.ResourceManagerTests.testAcquireResourceShared: 0.0023s
resourceManagerTests.ResourceManagerTests.testRereleaseResource: 0.0022s
resourceManagerTests.ResourceManagerTests.testResourceWrapper: 0.0020s
resourceManagerTests.ResourceManagerTests.testRequestWithBadCallbackOnGrant:
0.0020s
resourceManagerTests.ResourceManagerTests.testRequestRefStr: 0.0020s
resourceManagerTests.ResourceManagerTests.testAccessAttributeNotExposedByWrapper:
0.0019s
resourceManagerTests.ResourceManagerTests.testErrorInFactory: 0.0017s
resourceManagerTests.ResourceManagerTests.testAcquireNonExistingResource:
0.0016s
resourceManagerTests.ResourceManagerTests.testRequestInvalidResource: 0.0015s
resourceManagerTests.ResourceManagerTests.testRequestRegrant: 0.0015s
resourceManagerTests.ResourceManagerTests.testResourceAcquireInvalidTimeout:
0.0014s
resourceManagerTests.ResourceManagerTests.testReleaseInvalidResource: 0.0014s
resourceManagerTests.ResourceManagerTests.testSingleton: 0.0014s
resourceManagerTests.ResourceManagerTests.testForceRegisterNamespace: 0.0013s
resourceManagerTests.ResourceManagerTests.testListNamespaces: 0.0013s
resourceManagerTests.ResourceManagerTests.testReregisterNamespace: 0.0012s
resourceManagerTests.ResourceManagerTests.testRegisterInvalidNamespace: 0.0012s
At the end of the usual tests if nose-timer is detected in the
system. It shows the tests over 500ms in yellow and those over 5s
in red.
If you want to get this output, do:
pip install nose-timer
Change-Id: I4960b30532a84abd259fb268c7e40a1751847a96
Signed-off-by: Antoni S. Puimedon <asegurap(a)redhat.com>
---
M tests/run_tests_local.sh.in
M tests/testrunner.py
2 files changed, 18 insertions(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/71/30171/1
diff --git a/tests/run_tests_local.sh.in b/tests/run_tests_local.sh.in
index d19f22c..f51473d 100644
--- a/tests/run_tests_local.sh.in
+++ b/tests/run_tests_local.sh.in
@@ -7,4 +7,9 @@
@top_srcdir(a)/tests/makecert.sh
fi
-PYTHONDONTWRITEBYTECODE=1 LC_ALL=C PYTHONPATH="@top_srcdir@/lib:@top_srcdir@/vdsm:@top_srcdir@/client:@top_srcdir@/vdsm_api:$PYTHONPATH" "$PYTHON_EXE" @top_srcdir(a)/tests/testrunner.py --local-modules $@
+python -c 'import nosetimer' > /dev/null 2>&1
+if [ $? -eq 0 ]; then
+ EXTRA_ARGS='--with-timer'
+fi
+
+PYTHONDONTWRITEBYTECODE=1 LC_ALL=C PYTHONPATH="@top_srcdir@/lib:@top_srcdir@/vdsm:@top_srcdir@/client:@top_srcdir@/vdsm_api:$PYTHONPATH" "$PYTHON_EXE" @top_srcdir(a)/tests/testrunner.py --local-modules $@ "$EXTRA_ARGS"
diff --git a/tests/testrunner.py b/tests/testrunner.py
index eed743f..ec24c2b 100644
--- a/tests/testrunner.py
+++ b/tests/testrunner.py
@@ -40,6 +40,16 @@
from nose import config
from nose import core
from nose import result
+try:
+ from nosetimer.plugin import TimerPlugin
+except ImportError:
+ timer_plugin = None
+else:
+ timer_plugin = TimerPlugin()
+ timer_plugin.enabled = True
+ timer_plugin.timer_ok = 500 # Okay <= 500ms
+ timer_plugin.timer_warning = 5000 # Warn > 5s
+ timer_plugin.timer_no_color = False # Enable color output
from testValidation import SlowTestsPlugin, StressTestsPlugin
@@ -378,6 +388,8 @@
plugins=core.DefaultPluginManager())
conf.plugins.addPlugin(SlowTestsPlugin())
conf.plugins.addPlugin(StressTestsPlugin())
+ if timer_plugin is not None:
+ conf.plugins.addPlugin(timer_plugin)
runner = VdsmTestRunner(stream=conf.stream,
verbosity=conf.verbosity,
--
To view, visit http://gerrit.ovirt.org/30171
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I4960b30532a84abd259fb268c7e40a1751847a96
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Antoni Segura Puimedon <asegurap(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: tests: Add a live merge functional test
by alitke@redhat.com
Adam Litke has uploaded a new change for review.
Change subject: tests: Add a live merge functional test
......................................................................
tests: Add a live merge functional test
Test whether we can successfully merge the active layer. Uses lots of
the functional test infrastructure! Only runs if vdsm says it can
support live merge.
Change-Id: Idd5a2f7eedaef9e90981256de66fc3ed21658e89
Signed-off-by: Adam Litke <alitke(a)redhat.com>
---
M tests/functional/utils.py
M tests/functional/virtTests.py
2 files changed, 185 insertions(+), 5 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/24/29824/1
diff --git a/tests/functional/utils.py b/tests/functional/utils.py
index 494be98..e3cdba6 100644
--- a/tests/functional/utils.py
+++ b/tests/functional/utils.py
@@ -228,3 +228,41 @@
def updateVmPolicy(self, vmId, vcpuLimit):
result = self.vdscli.updateVmPolicy([vmId, vcpuLimit])
return result['status']['code'], result['status']['message']
+
+ def getTaskStatus(self, taskId):
+ result = self.vdscli.getTaskStatus(taskId)
+ return result['status']['code'], result['status']['message'],\
+ result['taskStatus']
+
+ def getVolumeInfo(self, sdId, spId, imgId, volId):
+ result = self.vdscli.getVolumeInfo(sdId, spId, imgId, volId)
+ return result['status']['code'], result['status']['message'],\
+ result['info']
+
+ def createVolume(self, sdId, spId, imgId, size, volFormat, preallocate,
+ diskType, volId, desc, baseImgId, baseVolId):
+ result = self.vdscli.createVolume(sdId, spId, imgId, size, volFormat,
+ preallocate, diskType, volId, desc,
+ baseImgId, baseVolId)
+ return result['status']['code'], result['status']['message'],\
+ result['uuid']
+
+ def deleteVolume(self, sdId, spId, imgId, volIds, postZero=False,
+ force=False):
+ result = self.vdscli.deleteVolume(sdId, spId, imgId, volIds, postZero,
+ force)
+ return result['status']['code'], result['status']['message'],\
+ result['uuid']
+
+ def snapshot(self, vmId, snapDrives, snapMemVolHandle=''):
+ result = self.vdscli.snapshot(vmId, snapDrives, snapMemVolHandle)
+ return result['status']['code'], result['status']['message']
+
+ def merge(self, vmId, drive, base, top, bandwidth, jobId):
+ result = self.vdscli.merge(vmId, drive, base, top, bandwidth, jobId)
+ return result['status']['code'], result['status']['message']
+
+ def list(self, fullStatus=False, vmList=()):
+ result = self.vdscli.list(fullStatus, vmList)
+ return result['status']['code'], result['status']['message'], \
+ result['vmList']
diff --git a/tests/functional/virtTests.py b/tests/functional/virtTests.py
index 94ce240..b811b92 100644
--- a/tests/functional/virtTests.py
+++ b/tests/functional/virtTests.py
@@ -22,6 +22,7 @@
import math
import tempfile
import logging
+import uuid
from stat import S_IROTH
from functools import partial, wraps
@@ -32,7 +33,8 @@
from testrunner import temporaryPath
from vdsm.utils import CommandPath, RollbackContext
-import storageTests as storage
+import storageTests
+import storage
from storage.misc import execCmd
from utils import VdsProxy, SUCCESS
@@ -109,6 +111,18 @@
return method(self, *args, **kwargs)
else:
raise SkipTest('KVM is not enabled')
+ return wrapped
+
+
+def requireLiveMerge(method):
+ @wraps(method)
+ def wrapped(self, *args, **kwargs):
+ status, msg, result = self.vdsm.getVdsCapabilities()
+ self.assertEqual(status, SUCCESS, msg)
+ if result.get('liveMerge') == 'true':
+ return method(self, *args, **kwargs)
+ else:
+ raise SkipTest('Live Merge is not available')
return wrapped
@@ -227,9 +241,9 @@
@requireKVM
@permutations([['localfs'], ['iscsi'], ['nfs']])
def testVmWithStorage(self, backendType):
- disk = storage.StorageTest()
+ disk = storageTests.StorageTest()
disk.setUp()
- conf = storage.storageLayouts[backendType]
+ conf = storageTests.storageLayouts[backendType]
drives = disk.generateDriveConf(conf)
customization = {'vmId': '88888888-eeee-ffff-aaaa-111111111111',
'vmName': 'testVmWithStorage' + backendType,
@@ -247,8 +261,8 @@
def testVmWithDevice(self, *devices):
customization = {'vmId': '77777777-ffff-3333-bbbb-222222222222',
'vmName': 'testVm', 'devices': [], 'display': 'vnc'}
- storageLayout = storage.storageLayouts['localfs']
- diskSpecs = storage.StorageTest.generateDriveConf(storageLayout)
+ storageLayout = storageTests.storageLayouts['localfs']
+ diskSpecs = storageTests.StorageTest.generateDriveConf(storageLayout)
pciSpecs = {'bus': '0x00', 'domain': '0x0000',
'function': '0x0', 'type': 'pci'}
ccidSpecs = {'slot': '0', 'controller': '0', 'type': 'ccid'}
@@ -412,3 +426,131 @@
self.vdsm.updateVmPolicy(customization['vmId'],
'50')
self.assertEqual(status, SUCCESS, msg)
+
+
+@expandPermutations
+class LiveMergeTest(VirtTestBase):
+ def _waitTask(self, taskId):
+ def assertTaskOK():
+ status, msg, result = self.vdsm.getTaskStatus(taskId)
+ self.assertEqual(status, SUCCESS, msg)
+ self.assertEquals(result['taskState'], 'finished')
+
+ self.retryAssert(assertTaskOK, timeout=60)
+
+ def _waitBlockJobs(self, vmId, jobIds):
+ def assertJobsGone():
+ status, msg, result = self.vdsm.getVmStats(vmId)
+ self.assertEqual(status, SUCCESS, msg)
+ self.assertTrue('vmJobs' in result)
+ self.assertTrue(all([x not in result['vmJobs'].keys()
+ for x in jobIds]))
+
+ self.retryAssert(assertJobsGone, timeout=60)
+
+ def _snapshotVM(self, vmId, drives, rollback):
+ snapDrives = []
+ for drive in drives:
+ sd = drive['domainID']
+ sp = drive['poolID']
+ img = drive['imageID']
+ vol = drive['volumeID']
+ newVol = str(uuid.uuid4())
+ volFormat = storage.volume.COW_FORMAT
+ preallocate = storage.volume.SPARSE_VOL
+ desc = 'snapshot for %s' % vol
+
+ # Create volume and wait
+ status, msg, result = self.vdsm.getVolumeInfo(sd, sp, img, vol)
+ self.assertEqual(status, SUCCESS, msg)
+ size = result['capacity']
+ diskType = result['disktype']
+
+ status, msg, taskId = self.vdsm.createVolume(sd, sp, img, size,
+ volFormat,
+ preallocate, diskType,
+ newVol, desc, img,
+ vol)
+ self.assertEqual(status, SUCCESS, msg)
+ self._waitTask(taskId)
+ undo = lambda sd=sd, sp=sp, img=img, vol=newVol: \
+ self._waitTask(self.vdsm.deleteVolume(sd, sp, img, vol)[2])
+ rollback.prependDefer(undo)
+
+ snapDrives.append({'domainID': sd,
+ 'imageID': img,
+ 'volumeID': newVol,
+ 'baseVolumeID': vol})
+
+ # Create snapshot
+ status, msg = self.vdsm.snapshot(vmId, snapDrives)
+ self.assertEqual(status, SUCCESS, msg)
+ return snapDrives
+
+ def _orderChain(self, vmId, dev, chain):
+ parentMap = {}
+ for vol in chain:
+ status, msg, info = self.vdsm.getVolumeInfo(dev['domainID'],
+ dev['poolID'],
+ dev['imageID'], vol)
+ self.assertEqual(status, SUCCESS, msg)
+ parent = info['parent']
+ parentMap[vol] = parent
+
+ vol = dev['volumeID']
+ chain = list()
+ while True:
+ chain.insert(0, vol)
+ vol = parentMap.get(vol, '00000000-0000-0000-0000-000000000000')
+ if vol == '00000000-0000-0000-0000-000000000000':
+ break
+ return chain
+
+ def _getVolumeChains(self, vmId):
+ chains = {}
+ status, msg, result = self.vdsm.list(True, (vmId,))
+ self.assertEqual(status, SUCCESS, msg)
+ vmDef = result[0]
+ for dev in vmDef['devices']:
+ if dev['device'] != 'disk':
+ continue
+ chains[dev['imageID']] = self._orderChain(vmId, dev,
+ [x['volumeID'] for x in
+ dev['volumeChain']])
+ return chains
+
+ @requireKVM
+ @requireLiveMerge
+ def testCapable(self):
+ pass
+
+ @permutations([['localfs']])
+ def testMergeActiveLayer(self, backendType):
+ disk = storageTests.StorageTest()
+ disk.setUp()
+ conf = storageTests.storageLayouts[backendType]
+ drives = disk.generateDriveConf(conf)
+ vmId = '12121212-abab-baba-abab-222222222222'
+ customization = {'vmId': vmId,
+ 'vmName': 'testMergeActive' + backendType,
+ 'drives': drives,
+ 'display': 'vnc'}
+
+ with RollbackContext() as rollback:
+ disk.createVdsmStorageLayout(conf, 3, rollback)
+ with RunningVm(self.vdsm, customization) as vm:
+ self._waitForStartup(vm, VM_MINIMAL_UPTIME)
+ snapDrives = self._snapshotVM(vmId, drives, rollback)
+ chains = {}
+ jobIds = []
+ for drive in snapDrives:
+ base = drive['baseVolumeID']
+ top = drive['volumeID']
+ jobId = str(uuid.uuid4())
+ chains[drive['imageID']] = [base, top]
+ status, msg = self.vdsm.merge(vmId, drive, base, top, 0,
+ jobId)
+ jobIds.append(jobId)
+ self._waitBlockJobs(vmId, jobIds)
+ actual = self._getVolumeChains(vmId)
+ self.assertEquals(chains, actual)
--
To view, visit http://gerrit.ovirt.org/29824
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Idd5a2f7eedaef9e90981256de66fc3ed21658e89
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Adam Litke <alitke(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: lvm: Exclude faulty devices from lvm long filter
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: lvm: Exclude faulty devices from lvm long filter
......................................................................
lvm: Exclude faulty devices from lvm long filter
lvm commands use filter to limit access to relevant devices. When the
filter includes a faulty device, lvm commands may block for several
minutes (stuck in D state). We have seen getDevicesList command stuck
for up to 10 minutes because of faulty devices in the long filter.
We used to build the filter from all multipath devices. Now we build the
filter only from devices which have at least one active paths.
# multipath -ll
360060160f4a0300038ed7058b5e9e311 dm-0 DGC ,VRAID
size=15G features='0' hwhandler='1 emc' wp=rw
|-+- policy='service-time 0' prio=0 status=enabled
| `- 4:0:3:0 sdd 8:48 failed faulty running
`-+- policy='service-time 0' prio=0 status=enabled
`- 4:0:2:0 sdb 8:16 failed faulty running
360060160f4a030003268ab211002e411 dm-1 DGC ,VRAID
size=30G features='1 queue_if_no_path' hwhandler='1 emc' wp=rw
|-+- policy='service-time 0' prio=4 status=active
| `- 4:0:3:1 sde 8:64 active ready running
`-+- policy='service-time 0' prio=1 status=enabled
`- 4:0:2:1 sdc 8:32 active ready running
Previously, both devices were included in the filter, now only
360060160f4a030003268ab211002e411 will be included in lvm filter.
A faulty device which became active again will be included in lvm filter
after the next refresh (every 5 minutes), or after trying edit or create
a new storage domain.
lvm uses also short filter, including devices used by the certain vg or
lv. It is possible that we also have to exclude such devices from the
short filter. This will be handled later if needed.
Change-Id: I6d7a973bcefa95813fdc289847760c0955aca30c
Bug-Url: https://bugzilla.redhat.com/880738
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M vdsm/storage/lvm.py
M vdsm/storage/multipath.py
2 files changed, 13 insertions(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/75/31875/1
diff --git a/vdsm/storage/lvm.py b/vdsm/storage/lvm.py
index 86edf55..9cfad01 100644
--- a/vdsm/storage/lvm.py
+++ b/vdsm/storage/lvm.py
@@ -244,7 +244,7 @@
if not self._filterStale:
return self._extraCfg
- self._extraCfg = _buildConfig(multipath.getMPDevNamesIter())
+ self._extraCfg = _buildConfig(multipath.getActiveMPDevNamesIter())
_updateLvmConf(self._extraCfg)
self._filterStale = False
diff --git a/vdsm/storage/multipath.py b/vdsm/storage/multipath.py
index ba98866..2b30995 100644
--- a/vdsm/storage/multipath.py
+++ b/vdsm/storage/multipath.py
@@ -382,6 +382,18 @@
yield os.path.join(devicemapper.DMPATH_PREFIX, name)
+def getActiveMPDevNamesIter():
+ status = devicemapper.getPathsStatus()
+ for dmId, guid in getMPDevsIter():
+ active = [slave for slave in devicemapper.getSlaves(dmId)
+ if status.get(slave) == "active"]
+ if not active:
+ log.warning("Skipping device %s - no active slave", guid)
+ continue
+ log.debug("Found device %s %s", guid, active)
+ yield os.path.join(devicemapper.DMPATH_PREFIX, guid)
+
+
def getMPDevsIter():
"""
Collect the list of all the multipath block devices.
--
To view, visit http://gerrit.ovirt.org/31875
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I6d7a973bcefa95813fdc289847760c0955aca30c
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
8 years
Change in vdsm[master]: clusterlock: Remove uneeded workaround
by Nir Soffer
Nir Soffer has uploaded a new change for review.
Change subject: clusterlock: Remove uneeded workaround
......................................................................
clusterlock: Remove uneeded workaround
Sanlock version XXXX had a off-by-one bug when calling get_hosts with a
host id, returning info for the next host. This bug is fixed in version
XXX. Now we can use the hostId parameter, making the call more efficient
and simpligying clusterlock code.
Change-Id: Ide75e749fbc2916540c2b526b78fedc247b5c6f9
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
M vdsm.spec.in
M vdsm/storage/clusterlock.py
2 files changed, 5 insertions(+), 16 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/62/31162/1
diff --git a/vdsm.spec.in b/vdsm.spec.in
index 5ba6fc6..fc39eb9 100644
--- a/vdsm.spec.in
+++ b/vdsm.spec.in
@@ -167,7 +167,7 @@
Requires: iscsi-initiator-utils >= 6.2.0.873-21
%endif
-Requires: sanlock >= 2.8, sanlock-python
+Requires: sanlock >= XXX, sanlock-python
%if 0%{?rhel}
Requires: python-ethtool >= 0.6-3
diff --git a/vdsm/storage/clusterlock.py b/vdsm/storage/clusterlock.py
index 24a5d81..17cdd53 100644
--- a/vdsm/storage/clusterlock.py
+++ b/vdsm/storage/clusterlock.py
@@ -265,26 +265,15 @@
return False
def getHostStatus(self, hostId):
- # Note: get_hosts has off-by-one bug when asking for particular host
- # id, so get all hosts info and filter.
- # See https://bugzilla.redhat.com/1111210
try:
- hosts = sanlock.get_hosts(self._sdUUID)
+ hosts = sanlock.get_hosts(self._sdUUID, hostId)
except sanlock.SanlockException as e:
self.log.debug("Unable to get host %d status in lockspace %s: %s",
hostId, self._sdUUID, e)
return HOST_STATUS_UNAVAILABLE
-
- for info in hosts:
- if info['host_id'] == hostId:
- status = info['flags']
- return self.STATUS_NAME[status]
-
- # get_hosts with host_id=0 returns only hosts with timestamp != 0,
- # which means that no host is using this host id now. If there a
- # timestamp, sanlock will return HOST_UNKNOWN and then HOST_LIVE or
- # HOST_FAIL.
- return HOST_STATUS_FREE
+ else:
+ status = hosts[0]['flags']
+ return self.STATUS_NAME[status]
# The hostId parameter is maintained here only for compatibility with
# ClusterLock. We could consider to remove it in the future but keeping it
--
To view, visit http://gerrit.ovirt.org/31162
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ide75e749fbc2916540c2b526b78fedc247b5c6f9
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Nir Soffer <nsoffer(a)redhat.com>
8 years
Change in vdsm[master]: [WIP]vdsm: add support for S3/S4 suspend calls
by mpoledni@redhat.com
Martin Polednik has uploaded a new change for review.
Change subject: [WIP]vdsm: add support for S3/S4 suspend calls
......................................................................
[WIP]vdsm: add support for S3/S4 suspend calls
Change-Id: Ic30016c5cd555f5771dde8db3f1340e1c11b3da7
Signed-off-by: Martin Polednik <mpoledni(a)redhat.com>
---
M vdsm/API.py
M vdsm/BindingXMLRPC.py
M vdsm/guestIF.py
M vdsm_api/vdsmapi-schema.json
4 files changed, 58 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/89/19389/1
diff --git a/vdsm/API.py b/vdsm/API.py
index 37bb908..96119ed 100644
--- a/vdsm/API.py
+++ b/vdsm/API.py
@@ -296,6 +296,20 @@
else:
return errCode['nonresp']
+ def desktopSuspend(self, mode):
+ """
+ Sleep the guest operating system
+ """
+ try:
+ v = self._cif.vmContainer[self._UUID]
+ except KeyError:
+ return errCode['noVM']
+ v.guestAgent.desktopSuspend(mode)
+ if v.guestAgent.isResponsive():
+ return {'status': doneCode}
+ else:
+ return errCode['nonresp']
+
def desktopSendHcCommand(self, message):
"""
Send a command to the guest agent (depricated).
diff --git a/vdsm/BindingXMLRPC.py b/vdsm/BindingXMLRPC.py
index fb65ad4..046bb0c 100644
--- a/vdsm/BindingXMLRPC.py
+++ b/vdsm/BindingXMLRPC.py
@@ -352,6 +352,10 @@
vm = API.VM(vmId)
return vm.desktopLogoff(force)
+ def vmDesktopSuspend(self, vmId, mode):
+ vm = API.VM(vmId)
+ return vm.desktopSuspend(mode)
+
def vmDesktopLock(self, vmId):
vm = API.VM(vmId)
return vm.desktopLock()
@@ -836,6 +840,7 @@
(self.vmMigrationCreate, 'migrationCreate'),
(self.vmDesktopLogin, 'desktopLogin'),
(self.vmDesktopLogoff, 'desktopLogoff'),
+ (self.vmDesktopSuspend, 'desktopSuspend'),
(self.vmDesktopLock, 'desktopLock'),
(self.vmDesktopSendHcCommand, 'sendHcCmdToDesktop'),
(self.vmHibernate, 'hibernate'),
diff --git a/vdsm/guestIF.py b/vdsm/guestIF.py
index 6f09ef1..2bb654b 100644
--- a/vdsm/guestIF.py
+++ b/vdsm/guestIF.py
@@ -299,6 +299,15 @@
except:
self.log.error("desktopLogoff failed", exc_info=True)
+ def desktopSuspend(self, mode):
+ try:
+ self.log.debug('desktopSleep called')
+ cmds = ('guest-suspend-ram', 'guest-suspend-hybrid',
+ 'guest-suspend-disk')
+ self._forward(cmds['mode'], {'success_response': 'yes'})
+ except:
+ self.log.debug('desktopSleep failed', exc_info=True)
+
def desktopShutdown(self, timeout, msg):
try:
self.log.debug("desktopShutdown called")
diff --git a/vdsm_api/vdsmapi-schema.json b/vdsm_api/vdsmapi-schema.json
index 27c12c1..bc54041 100644
--- a/vdsm_api/vdsmapi-schema.json
+++ b/vdsm_api/vdsmapi-schema.json
@@ -5079,6 +5079,36 @@
'data': {'vmID': 'UUID', 'force': 'bool'}}
##
+# @guestSuspendMode
+#
+# Enumeration of supported suspend modes
+#
+# @ram: S3 sleep
+#
+# @disk: S4 sleep
+#
+# @hybrid: Combination of @ram and @disk
+#
+# Since: 4.12.0
+##
+{'enum': 'guestSuspendMode', 'data': ['ram', 'disk', 'hybrid']}
+
+
+##
+# @VM.desktopSuspend:
+#
+# Suspend the guest operating system
+#
+# @vmID: The UUID of the VM
+#
+# @mode: Type of suspension
+#
+# Since: 4.12.0
+##
+{'command': {'class': 'VM', 'name': 'desktopSuspend'},
+ 'data': {'vmID': 'UUID', 'mode': 'guestSuspendMode'}}
+
+##
# @VM.desktopSendHcCommand:
#
# Send an arbitrary command to the guest agent.
--
To view, visit http://gerrit.ovirt.org/19389
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic30016c5cd555f5771dde8db3f1340e1c11b3da7
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Martin Polednik <mpoledni(a)redhat.com>
8 years, 1 month
Change in vdsm[master]: vdsm: add support for hot(un)plug of pci-passthrough devices
by mpoledni@redhat.com
Martin Polednik has uploaded a new change for review.
Change subject: vdsm: add support for hot(un)plug of pci-passthrough devices
......................................................................
vdsm: add support for hot(un)plug of pci-passthrough devices
Hot(un)plug ability for pci-passthrough devices is extremely important
in order to allow for hotunplugging/migrating/hotplugging workflow as VM
cannot be migrated with pci device attached.
This patch implements the ability adding new API verbs: hotplugHostdev
and hotunplugHostdev, which attempts to append/remove the device from
domain XML and reports back the state.
Change-Id: I8fbf4a1d62789d9404e5977eb7eb01b17a1a43fb
Signed-off-by: Martin Polednik <mpoledni(a)redhat.com>
---
M client/vdsClient.py
M vdsm/API.py
M vdsm/BindingXMLRPC.py
M vdsm/vm.py
M vdsm_api/vdsmapi-schema.json
5 files changed, 166 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/23/22523/1
diff --git a/client/vdsClient.py b/client/vdsClient.py
index d549500..f00180e 100644
--- a/client/vdsClient.py
+++ b/client/vdsClient.py
@@ -245,6 +245,14 @@
params = {'vmId': args[0], 'drive': drive}
return self.ExecAndExit(self.s.hotunplugDisk(params))
+ def hotplugHostdev(self, args):
+ params = {'vmId': args[0], 'hostdevName': args[1]}
+ return self.ExecAndExit(self.s.hotplugNic(params))
+
+ def hotunplugHostdev(self, args):
+ params = {'vmId': args[0], 'hostdevName': args[2]}
+ return self.ExecAndExit(self.s.hotunplugNic(params))
+
def do_changeCD(self, args):
vmId = args[0]
file = self._parseDriveSpec(args[1])
diff --git a/vdsm/API.py b/vdsm/API.py
index 44d5817..51eb506 100644
--- a/vdsm/API.py
+++ b/vdsm/API.py
@@ -456,6 +456,40 @@
return curVm.hotunplugDisk(params)
+ def hotplugHostdev(self, params):
+ try:
+ utils.validateMinimalKeySet(params, ('vmId', 'hostdevName'))
+ except ValueError:
+ self.log.error('Missing one of required parameters: vmId, '
+ 'hostdevName')
+ return {'status': {'code': errCode['MissParam']['status']['code'],
+ 'message': 'Missing one of required '
+ 'parameters: vmId, hostdevName'}}
+ try:
+ curVm = self._cif.vmContainer[self._UUID]
+ except KeyError:
+ self.log.warning("vm %s doesn't exist", self._UUID)
+ return errCode['noVM']
+
+ return curVm.hotplugHostdev(params)
+
+ def hotunplugHostdev(self, params):
+ try:
+ utils.validateMinimalKeySet(params, ('vmId', 'hostdevName'))
+ except ValueError:
+ self.log.error('Missing one of required parameters: vmId, '
+ 'hostdevName')
+ return {'status': {'code': errCode['MissParam']['status']['code'],
+ 'message': 'Missing one of required '
+ 'parameters: vmId, hostdevName'}}
+ try:
+ curVm = self._cif.vmContainer[self._UUID]
+ except KeyError:
+ self.log.warning("vm %s doesn't exist", self._UUID)
+ return errCode['noVM']
+
+ return curVm.hotunplugHostdev(params)
+
def migrate(self, params):
"""
Migrate a VM to a remote host.
diff --git a/vdsm/BindingXMLRPC.py b/vdsm/BindingXMLRPC.py
index 5bcd84c..847ceaf 100644
--- a/vdsm/BindingXMLRPC.py
+++ b/vdsm/BindingXMLRPC.py
@@ -279,6 +279,14 @@
vm = API.VM(params['vmId'])
return vm.hotunplugNic(params)
+ def vmHotplugHostdev(self, params):
+ vm = API.VM(params['vmId'])
+ return vm.hotplugHostdev(params)
+
+ def vmHotunplugHostdev(self, params):
+ vm = API.VM(params['vmId'])
+ return vm.hotunplugHostdev(params)
+
def vmUpdateDevice(self, vmId, params):
vm = API.VM(vmId)
return vm.vmUpdateDevice(params)
@@ -861,6 +869,8 @@
(self.vmHotunplugDisk, 'hotunplugDisk'),
(self.vmHotplugNic, 'hotplugNic'),
(self.vmHotunplugNic, 'hotunplugNic'),
+ (self.vmHotplugHostdev, 'hotplugHostdev'),
+ (self.vmHotunplugHostdev, 'hotunplugHostdev'),
(self.vmUpdateDevice, 'vmUpdateDevice'))
def getIrsMethods(self):
diff --git a/vdsm/vm.py b/vdsm/vm.py
index 90a6c0d..224bf7b 100644
--- a/vdsm/vm.py
+++ b/vdsm/vm.py
@@ -3183,6 +3183,84 @@
return {'status': doneCode, 'vmList': self.status()}
+ def hotplugHostdev(self, params):
+ hostdevName = params['hostdevName']
+ hostdev = HostDevice(self.conf, self.log, **hostdevName)
+ hostdevXML = hostdev.getXML().toprettyxml(encoding='utf-8')
+ hostdev._deviceXML = hostdevXML
+ self.log.debug("Hotplug hostdev xml: %s", hostdevXML)
+
+ try:
+ self._dom.attachDevice(hostdevXML)
+ except libvirt.libvirtError as e:
+ self.log.error("Hotplug failed", exc_info=True)
+ if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN:
+ return errCode['noVM']
+ return {'status': {'code': errCode['hotplugNic']['status']['code'],
+ 'message': e.message}}
+ else:
+ # FIXME! We may have a problem here if vdsm dies right after
+ # we sent command to libvirt and before save conf. In this case
+ # we will gather almost all needed info about this NIC from
+ # the libvirt during recovery process.
+ self._devices[HOSTDEV_DEVICES].append(hostdev)
+ with self._confLock:
+ self.conf['devices'].append(hostdev)
+ self.saveState()
+
+ return {'status': doneCode, 'vmList': self.status()}
+
+ def hotunplugHostdev(self, params):
+ hostdevName = params['hostdevName']
+
+ # Find hostdev object in vm's hostdev list
+ hostdev = None
+ for dev in self._devices[HOSTDEV_DEVICES][:]:
+ if dev.name == hostdevName:
+ hostdev = dev
+ break
+
+ if hostdev:
+ hostdevXML = hostdev.getXML().toprettyxml(encoding='utf-8')
+ self.log.debug("Hotunplug hostdev xml: %s", hostdevXML)
+ self._devices[HOSTDEV_DEVICES].remove(hostdev)
+ else:
+ self.log.error("Hotunplug hostdev failed - hostdev not found: %s",
+ hostdevName)
+ return {'status': {'code': errCode['hotunplugNic']
+ ['status']['code'],
+ 'message': "NIC not found"}}
+
+ hostdevDev = None
+ for dev in self.conf['devices'][:]:
+ if (dev['type'] == HOSTDEV_DEVICES and
+ dev['name'] == hostdevName):
+ hostdevDev = dev
+ with self._confLock:
+ self.conf['devices'].remove(dev)
+ break
+
+ self.saveState()
+
+ try:
+ self._dom.detachDevice(hostdevXML)
+ except libvirt.libvirtError as e:
+ self.log.error("Hotunplug failed", exc_info=True)
+ if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN:
+ return errCode['noVM']
+ # Restore hostdev device in vm's conf and _devices
+ if hostdevDev:
+ with self._confLock:
+ self.conf['devices'].append(hostdevDev)
+ if hostdev:
+ self._devices[HOSTDEV_DEVICES].append(hostdev)
+ self.saveState()
+ return {
+ 'status': {'code': errCode['hotunplugNic']['status']['code'],
+ 'message': e.message}}
+
+ return {'status': doneCode, 'vmList': self.status()}
+
def _lookupDeviceByAlias(self, devType, alias):
for dev in self._devices[devType][:]:
if dev.alias == alias:
diff --git a/vdsm_api/vdsmapi-schema.json b/vdsm_api/vdsmapi-schema.json
index 1d4e499..85f2b28 100644
--- a/vdsm_api/vdsmapi-schema.json
+++ b/vdsm_api/vdsmapi-schema.json
@@ -5793,6 +5793,42 @@
'returns': 'VmDefinition'}
##
+# @VM.hotplugHostdev:
+#
+# Add a new host device to a running VM.
+#
+# @vmID: The UUID of the VM
+#
+# @hostdevName: The name of host's device
+#
+# Returns:
+# The VM definition, as updated
+#
+# Since: 4.14.0
+##
+{'command': {'class': 'VM', 'name': 'hotplugHostdev'},
+ 'data': {'vmID': 'UUID', 'hostdevName': 'str'},
+ 'returns': 'VmDefinition'}
+
+##
+# @VM.hotunplugHostdev:
+#
+# Remove a host device from a running VM.
+#
+# @vmID: The UUID of the VM
+#
+# @hostdevName: The name of host's device
+#
+# Returns:
+# The VM definition, as updated
+#
+# Since: 4.14.0
+##
+{'command': {'class': 'VM', 'name': 'hotunplugHostdev'},
+ 'data': {'vmID': 'UUID', 'hostdevName': 'str'},
+ 'returns': 'VmDefinition'}
+
+##
# @MigrateMode:
#
# An enumeration of VM migration modes.
--
To view, visit http://gerrit.ovirt.org/22523
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I8fbf4a1d62789d9404e5977eb7eb01b17a1a43fb
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Martin Polednik <mpoledni(a)redhat.com>
8 years, 1 month
Change in vdsm[master]: vdsmcli: Add a contrib command line client alternative
by asegurap@redhat.com
Antoni Segura Puimedon has uploaded a new change for review.
Change subject: vdsmcli: Add a contrib command line client alternative
......................................................................
vdsmcli: Add a contrib command line client alternative
This command line client is built by hand for the convenience of
developers and requires the click library:
pip install click
It easily supports commands and subcommands. For example:
vdsmcli network
is a command and 'show' and 'add' are its subcommands.
Examples:
toniel602 ~ # vdsmcli network show -k nics -k networks
============================================= Nics
eth6
eth5
eth4
eth3
eth2
eth1
============================================= Networks
foo
or
toniel602 ~ # vdsmcli --verbode --pprint network show -k networks
============================================= Networks
===============
foo
===============
{'addr': '',
'bootproto4': 'none',
'bridged': False,
'gateway': '',
'iface': 'bond42',
'interface': 'bond42',
'ipv6addrs': ['2620:52:0:223c:201:a4ff:feac:8796/64',
'fe80::201:a4ff:feac:8796/64'],
'ipv6gateway': 'fe80:52:0:223c::3fe',
'mtu': '1500',
'netmask': ''}
addition
toniel602 ~ # ./cvdsm.py -v -p network add foo --bridgeless --nic eth4
--nic eth5 --bond bond42
Networks: {u'foo': {'bonding': u'bond42', 'bridged': False}}
Bonds: {u'bond42': {'nics': (u'eth4', u'eth5')}}
Succeeded. Done
One could extend it with a vm command so that it was easy to do:
vdsmcli vm hotplug 34f5f608-91ed-48d1-af31-c3a3d788678e nic --mac 00:11:22:33:44:55 --model virtio
Change-Id: Ie5574b2b34f0b7b2174e9da0c4487f812ff20f5b
Signed-off-by: Antoni S. Puimedon <asegurap(a)redhat.com>
---
A contrib/vdsmcli.py
1 file changed, 113 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/52/28152/1
diff --git a/contrib/vdsmcli.py b/contrib/vdsmcli.py
new file mode 100644
index 0000000..2255201
--- /dev/null
+++ b/contrib/vdsmcli.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python2
+# 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 is a command line client for vdsm."""
+
+from pprint import pformat
+
+import click
+
+from vdsm import netinfo, vdscli
+
+_NET_DEVICE_TYPES = ('networks', 'bridges', 'vlans', 'bondings', 'nics')
+
+
+class Options(object):
+ def __init__(self, verbose=None, pprint=None):
+ self.verbose = verbose
+ self.pprint = pprint
+
+
+(a)click.group()
+(a)click.option('-v', '--verbose', is_flag=True)
+(a)click.option('-p', '--pprint', is_flag=True)
+(a)click.pass_context
+def cli(ctx, verbose, pprint):
+ ctx.obj = Options(verbose, pprint)
+
+
+(a)cli.group()
+(a)click.pass_obj
+def network(options):
+ """Network actions and information displaying."""
+ pass
+
+
+(a)network.command(help='Configure network')
+(a)click.pass_obj
+(a)click.option('--bridged/--bridgeless', default=True)
+(a)click.option('--vlan', default=None, type=click.INT)
+(a)click.option('--bond', default=None)
+(a)click.option('--bond-opts', default=None)
+(a)click.option('--nic', multiple=True)
+(a)click.argument('name')
+(a)click.argument('extra_attributes', required=False, nargs=-1)
+def add(options, bridged, vlan, bond, bond_opts, nic, name, extra_attributes):
+ conn = vdscli.connect()
+ networks = {name: {'bridged': bridged}}
+ if vlan:
+ networks[name]['vlan'] = vlan
+ if len(nic) == 1:
+ networks[name]['nic'] = nic[0]
+ if bond:
+ networks[name]['bonding'] = bond
+ bonds = {bond: {'nics': nic}}
+ if bond_opts:
+ bonds[bond]['options'] = bond_opts
+ else:
+ bonds = {}
+
+ for key, value in (elem.split('=') for elem in extra_attributes):
+ networks[name][key] = value
+
+ if options.verbose:
+ click.echo('Networks: %s' % (pformat(networks) if options.pprint else
+ networs))
+ click.echo('Bonds: %s' % (pformat(bonds) if options.pprint else bonds))
+ result = conn.setupNetworks(networks, bonds, {'connectivityCheck': False})
+ code = result['status']['code']
+ message = result['status']['message']
+ click.echo('%s. %s' % ('Succeeded' if code == 0 else 'Failed', message))
+
+
+(a)network.command(help='show network and net device information')
+(a)click.pass_obj
+(a)click.option('--kind', '-k', multiple=True, type=click.Choice(
+ _NET_DEVICE_TYPES))
+def show(options, kind):
+ if not kind:
+ kind = ('networks',)
+ conn = vdscli.connect()
+ info = netinfo.NetInfo(conn.getVdsCapabilities()['info'])
+ for k in kind:
+ click.echo('')
+ click.echo('=' * 45, nl=False)
+ click.echo(' ' + k[0].upper() + k[1:])
+ for name, attrs in getattr(info, k).iteritems():
+ if options.verbose:
+ click.echo('=' * 15)
+ click.echo(name)
+ if options.verbose:
+ click.echo('=' * 15)
+ click.echo(pformat(attrs) if options.pprint else attrs)
+ click.echo('')
+
+
+if __name__ == '__main__':
+ cli()
--
To view, visit http://gerrit.ovirt.org/28152
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie5574b2b34f0b7b2174e9da0c4487f812ff20f5b
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Antoni Segura Puimedon <asegurap(a)redhat.com>
8 years, 1 month
Change in vdsm[master]: volume: support multi line metadata values
by laravot@redhat.com
Liron Ar has uploaded a new change for review.
Change subject: volume: support multi line metadata values
......................................................................
volume: support multi line metadata values
Currently when creating the metadata dict it's assumed that each value
can be in a single line only while it's not necessarily true.
Change-Id: Ib03152b852294f7b69a8734c2aa1206ea2c1fabe
Signed-off-by: Liron Aravot <laravot(a)redhat.com>
---
M vdsm/storage/blockVolume.py
M vdsm/storage/volume.py
2 files changed, 12 insertions(+), 5 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/93/28493/1
diff --git a/vdsm/storage/blockVolume.py b/vdsm/storage/blockVolume.py
index d448e00..d7cf020 100644
--- a/vdsm/storage/blockVolume.py
+++ b/vdsm/storage/blockVolume.py
@@ -580,7 +580,6 @@
self.log.error(e, exc_info=True)
raise se.VolumeMetadataReadError("%s: %s" % (metaId, e))
-
def setMetadata(self, meta, metaId=None):
"""
Set the meta data hash as the new meta data of the Volume
diff --git a/vdsm/storage/volume.py b/vdsm/storage/volume.py
index aed932e..bfc1ff1 100644
--- a/vdsm/storage/volume.py
+++ b/vdsm/storage/volume.py
@@ -814,19 +814,27 @@
"""
pass
-
def metadata2dict(self, meta):
out = {}
+ key = None
+ value = None
+
+ def put(key, value):
+ out[key.strip()] = "\n".join(value).strip()
+
for l in meta:
if l.startswith("EOF"):
- return out
+ break
if l.find("=") < 0:
+ value.append(l)
continue
+ if key:
+ put(key, value)
key, value = l.split("=")
- out[key.strip()] = value.strip()
+ value = [value]
+ put(key, value)
return out
-
def metadata2info(self, meta):
return {
--
To view, visit http://gerrit.ovirt.org/28493
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib03152b852294f7b69a8734c2aa1206ea2c1fabe
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Liron Ar <laravot(a)redhat.com>
8 years, 1 month