Change in vdsm[master]: do not use OOP for padding snapshot's memory volume
by ahadas@redhat.com
Arik Hadas has uploaded a new change for review.
Change subject: do not use OOP for padding snapshot's memory volume
......................................................................
do not use OOP for padding snapshot's memory volume
Change-Id: I2a94354e188019f3afd209633979ec5a5b35293b
Signed-off-by: Arik Hadas <ahadas(a)redhat.com>
---
M vdsm/virt/vm.py
1 file changed, 3 insertions(+), 9 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/38/26538/1
diff --git a/vdsm/virt/vm.py b/vdsm/virt/vm.py
index 6711bc6..b8ce533 100644
--- a/vdsm/virt/vm.py
+++ b/vdsm/virt/vm.py
@@ -3543,15 +3543,9 @@
'_srcDomXML': self._dom.XMLDesc(0),
'elapsedTimeOffset': time.time() - self._startTime}
- def _padMemoryVolume(memoryVolPath, sdUUID):
- sdType = sd.name2type(
- self.cif.irs.getStorageDomainInfo(sdUUID)['info']['type'])
- if sdType in sd.FILE_DOMAIN_TYPES:
- if sdType == sd.NFS_DOMAIN:
- oop.getProcessPool(sdUUID).fileUtils. \
- padToBlockSize(memoryVolPath)
- else:
- fileUtils.padToBlockSize(memoryVolPath)
+ def _padMemoryVolume(memoryVolPath):
+ if not utils.isBlockDevice(memoryVolPath):
+ fileUtils.padToBlockSize(memoryVolPath)
snap = xml.dom.minidom.Element('domainsnapshot')
disks = xml.dom.minidom.Element('disks')
--
To view, visit http://gerrit.ovirt.org/26538
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I2a94354e188019f3afd209633979ec5a5b35293b
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Arik Hadas <ahadas(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: clientIF: Remove unnecessary device is disk check
by sgotliv@redhat.com
Sergey Gotliv has uploaded a new change for review.
Change subject: clientIF: Remove unnecessary device is disk check
......................................................................
clientIF: Remove unnecessary device is disk check
Change-Id: I98317e805e6770df5dacd3237a383aaca78fde1e
Signed-off-by: Sergey Gotliv <sgotliv(a)redhat.com>
---
M vdsm/clientIF.py
1 file changed, 1 insertion(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/63/22363/1
diff --git a/vdsm/clientIF.py b/vdsm/clientIF.py
index c083991..124f8e5 100644
--- a/vdsm/clientIF.py
+++ b/vdsm/clientIF.py
@@ -244,7 +244,7 @@
def prepareVolumePath(self, drive, vmId=None):
if type(drive) is dict:
# PDIV drive format
- if drive['device'] == 'disk' and vm.isVdsmImage(drive):
+ if vm.isVdsmImage(drive):
res = self.irs.prepareImage(
drive['domainID'], drive['poolID'],
drive['imageID'], drive['volumeID'])
--
To view, visit http://gerrit.ovirt.org/22363
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I98317e805e6770df5dacd3237a383aaca78fde1e
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Sergey Gotliv <sgotliv(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: Added gluster tag support in getAllTasks()
by barumuga@redhat.com
Hello Ayal Baron, Timothy Asir, Saggi Mizrahi, Federico Simoncelli, Dan Kenigsberg,
I'd like you to do a code review. Please visit
http://gerrit.ovirt.org/7579
to review the following change.
Change subject: Added gluster tag support in getAllTasks()
......................................................................
Added gluster tag support in getAllTasks()
If param tag is empty, all tasks including gluster tasks are returned,
else tasks those tags are in param tag list are returned.
As below verbs are not consumed by engine/RHS-C yet, its OK to differ in
compatibility issue now.
glusterVolumeRebalanceStart
glusterVolumeRebalanceStatus
glusterVolumeReplaceBrickStart
glusterVolumeReplaceBrickStatus
glusterVolumeRemoveBrickStart
glusterVolumeRemoveBrickStatus
Change-Id: I9c765cbfebb5ba22f0d21efa04c824ea4daf6432
Signed-off-by: Bala.FA <barumuga(a)redhat.com>
---
M tests/gluster_cli_tests.py
M vdsm/gluster/cli.py
M vdsm/gluster/exception.py
M vdsm/storage/taskManager.py
4 files changed, 367 insertions(+), 95 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/79/7579/1
diff --git a/tests/gluster_cli_tests.py b/tests/gluster_cli_tests.py
index f442893..9c6357c 100644
--- a/tests/gluster_cli_tests.py
+++ b/tests/gluster_cli_tests.py
@@ -28,6 +28,7 @@
from gluster import cli as gcli
except ImportError:
pass
+import xml.etree.cElementTree as etree
class GlusterCliTests(TestCaseBase):
@@ -115,3 +116,74 @@
def test_parsePeerStatus(self):
self._parsePeerStatus_empty_test()
self._parsePeerStatus_test()
+
+ def _parseVolumeStatusAll_test(self):
+ out = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<cliOutput>
+ <opRet>0</opRet>
+ <opErrno>0</opErrno>
+ <opErrstr></opErrstr>
+ <volumes>
+ <volume>
+ <name>V1</name>
+ <id>03eace73-9197-49d0-a877-831bc6e9dac2</id>
+ <tasks>
+ <task>
+ <name>rebalance</name>
+ <id>12345473-9197-49d0-a877-831bc6e9dac2</id>
+ </task>
+ </tasks>
+ </volume>
+ <volume>
+ <name>V2</name>
+ <id>03eace73-1237-49d0-a877-831bc6e9dac2</id>
+ <tasks>
+ <task>
+ <name>replace-brick</name>
+ <id>12345473-1237-49d0-a877-831bc6e9dac2</id>
+ <sourceBrick>192.168.122.167:/tmp/V2-b1</sourceBrick>
+ <destBrick>192.168.122.168:/tmp/V2-b1</destBrick>
+ </task>
+ </tasks>
+ </volume>
+ <volume>
+ <name>V3</name>
+ <id>03eace73-1237-1230-a877-831bc6e9dac2</id>
+ <tasks>
+ <task>
+ <name>remove-brick</name>
+ <id>12345473-1237-1230-a877-831bc6e9dac2</id>
+ <BrickCount>2</BrickCount>
+ <brick>192.168.122.167:/tmp/V3-b1</brick>
+ <brick>192.168.122.168:/tmp/V3-b1</brick>
+ </task>
+ </tasks>
+ </volume>
+ </volumes>
+</cliOutput>"""
+ tree = etree.fromstring(out)
+ status = gcli._parseVolumeStatusAll(tree)
+ self.assertEquals(status,
+ {'12345473-1237-1230-a877-831bc6e9dac2':
+ {'bricks': ['192.168.122.167:/tmp/V3-b1',
+ '192.168.122.168:/tmp/V3-b1'],
+ 'taskType': 'remove-brick',
+ 'volumeId':
+ '03eace73-1237-1230-a877-831bc6e9dac2',
+ 'volumeName': 'V3'},
+ '12345473-1237-49d0-a877-831bc6e9dac2':
+ {'bricks': ['192.168.122.167:/tmp/V2-b1',
+ '192.168.122.168:/tmp/V2-b1'],
+ 'taskType': 'replace-brick',
+ 'volumeId':
+ '03eace73-1237-49d0-a877-831bc6e9dac2',
+ 'volumeName': 'V2'},
+ '12345473-9197-49d0-a877-831bc6e9dac2':
+ {'bricks': [],
+ 'taskType': 'rebalance',
+ 'volumeId':
+ '03eace73-9197-49d0-a877-831bc6e9dac2',
+ 'volumeName': 'V1'}})
+
+ def test_parseVolumeStatusAll(self):
+ self._parseVolumeStatusAll_test()
diff --git a/vdsm/gluster/cli.py b/vdsm/gluster/cli.py
index 95de106..1f464f6 100644
--- a/vdsm/gluster/cli.py
+++ b/vdsm/gluster/cli.py
@@ -84,6 +84,55 @@
raise ge.GlusterCmdFailedException(rc=rv, err=[msg])
+class TaskType:
+ REBALANCE = 'rebalance'
+ REPLACE_BRICK = 'replace-brick'
+ REMOVE_BRICK = 'remove-brick'
+
+
+def _parseVolumeStatusAll(tree):
+ """
+ returns {TaskId: {'volumeName': VolumeName,
+ 'volumeId': VolumeId,
+ 'taskType': TaskType,
+ 'bricks': BrickList}, ...}
+ """
+ tasks = {}
+ for el in tree.findall('volumes/volume'):
+ volumeName = el.find('name').text
+ volumeId = el.find('id').text
+ for c in el.findall('tasks/task'):
+ taskType = c.find('name').text
+ taskId = c.find('id').text
+ bricks = []
+ if taskType == TaskType.REPLACE_BRICK:
+ bricks.append(c.find('sourceBrick').text)
+ bricks.append(c.find('destBrick').text)
+ elif taskType == TaskType.REMOVE_BRICK:
+ for b in c.findall('brick'):
+ bricks.append(b.text)
+ elif taskType == TaskType.REBALANCE:
+ pass
+ tasks[taskId] = {'volumeName': volumeName,
+ 'volumeId': volumeId,
+ 'taskType': taskType,
+ 'bricks': bricks}
+ return tasks
+
+
+@exportToSuperVdsm
+def volumeStatusAll():
+ command = _getGlusterVolCmd() + ["status", "all"]
+ try:
+ xmltree, out = _execGlusterXml(command)
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeStatusAllFailedException(rc=e.rc, err=e.err)
+ try:
+ return _parseVolumeStatusAll(xmltree)
+ except:
+ raise ge.GlusterXmlErrorException(err=out)
+
+
def _parseVolumeInfo(out):
if not out[0].strip():
del out[0]
@@ -300,11 +349,15 @@
command.append("start")
if force:
command.append("force")
- rc, out, err = _execGluster(command)
- if rc:
- raise ge.GlusterVolumeRebalanceStartFailedException(rc, out, err)
- else:
- return True
+ try:
+ xmltree, out = _execGlusterXml(command)
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeRebalanceStartFailedException(rc=e.rc,
+ err=e.err)
+ try:
+ return {'taskId': xmltree.find('id').text}
+ except:
+ raise ge.GlusterXmlErrorException(err=out)
@exportToSuperVdsm
@@ -312,84 +365,147 @@
command = _getGlusterVolCmd() + ["rebalance", volumeName, "stop"]
if force:
command.append('force')
- rc, out, err = _execGluster(command)
- if rc:
- raise ge.GlusterVolumeRebalanceStopFailedException(rc, out, err)
- else:
+ try:
+ _execGlusterXml(command)
return True
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeRebalanceStopFailedException(rc=e.rc,
+ err=e.err)
+
+
+class TaskStatus():
+ RUNNING = 'RUNNING'
+ FAILED = 'FAILED'
+ COMPLETED = 'COMPLETED'
+
+
+def _parseVolumeRebalanceRemoveBrickStatus(xmltree, mode):
+ """
+ returns {'taskId': UUID,
+ 'host': [{'name': NAME,
+ 'id': HOSTID,
+ 'filesScanned': INT,
+ 'filesMoved': INT,
+ 'filesFailed': INT,
+ 'totalSizeMoved': INT,
+ 'status': TaskStatus},...]
+ 'summary': {'filesScanned': INT,
+ 'filesMoved': INT,
+ 'filesFailed': INT,
+ 'totalSizeMoved': INT,
+ 'status': TaskStatus}}
+ """
+ if mode == 'rebalance':
+ tree = xmltree.find('volRebalance')
+ elif mode == 'remove-brick':
+ tree = xmltree.find('volRemoveBrick')
+ else:
+ return
+ status = \
+ {'taskId': tree.find('id').text,
+ 'summary': \
+ {'filesScanned': int(tree.find('summary/filesScanned').text),
+ 'filesMoved': int(tree.find('summary/filesMoved').text),
+ 'filesFailed': int(tree.find('summary/filesFailed').text),
+ 'totalSizeMoved': int(tree.find('summary/totalSizeMoved').text),
+ 'status': tree.find('summary/status').text},
+ 'host': []}
+ for el in tree.findall('node'):
+ status['host'].append({'name': el.find('name').text,
+ 'id': el.find('id').text,
+ 'filesScanned':
+ int(el.find('filesScanned').text),
+ 'filesMoved': int(el.find('filesMoved').text),
+ 'filesFailed': int(el.find('filesFailed').text),
+ 'totalSizeMoved':
+ int(el.find('totalSizeMoved').text),
+ 'status': el.find('status').text})
+ return status
+
+
+def _parseVolumeRebalanceStatus(tree):
+ return _parseVolumeRebalanceRemoveBrickStatus(tree, 'rebalance')
@exportToSuperVdsm
def volumeRebalanceStatus(volumeName):
- rc, out, err = _execGluster(_getGlusterVolCmd() + ["rebalance", volumeName,
- "status"])
- if rc:
- raise ge.GlusterVolumeRebalanceStatusFailedException(rc, out, err)
- if 'in progress' in out[0]:
- return BrickStatus.RUNNING, "\n".join(out)
- elif 'complete' in out[0]:
- return BrickStatus.COMPLETED, "\n".join(out)
- else:
- return BrickStatus.UNKNOWN, "\n".join(out)
+ command = _getGlusterVolCmd() + ["rebalance", volumeName, "status"]
+ try:
+ xmltree, out = _execGlusterXml(command)
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeRebalanceStatusFailedException(rc=e.rc,
+ err=e.err)
+ try:
+ return _parseVolumeRebalanceStatus(xmltree)
+ except:
+ raise ge.GlusterXmlErrorException(err=out)
@exportToSuperVdsm
def volumeReplaceBrickStart(volumeName, existingBrick, newBrick):
- rc, out, err = _execGluster(_getGlusterVolCmd() + ["replace-brick",
- volumeName,
- existingBrick, newBrick,
- "start"])
- if rc:
- raise ge.GlusterVolumeReplaceBrickStartFailedException(rc, out, err)
- else:
- return True
+ command = _getGlusterVolCmd() + ["replace-brick", volumeName,
+ existingBrick, newBrick, "start"]
+ try:
+ xmltree, out = _execGlusterXml(command)
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeReplaceBrickStartFailedException(rc=e.rc,
+ err=e.err)
+ try:
+ return {'taskId': xmltree.find('id').text}
+ except:
+ raise ge.GlusterXmlErrorException(err=out)
@exportToSuperVdsm
def volumeReplaceBrickAbort(volumeName, existingBrick, newBrick):
- rc, out, err = _execGluster(_getGlusterVolCmd() + ["replace-brick",
- volumeName,
- existingBrick, newBrick,
- "abort"])
- if rc:
- raise ge.GlusterVolumeReplaceBrickAbortFailedException(rc, out, err)
- else:
+ command = _getGlusterVolCmd() + ["replace-brick", volumeName,
+ existingBrick, newBrick, "abort"]
+ try:
+ _execGlusterXml(command)
return True
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeReplaceBrickAbortFailedException(rc=e.rc,
+ err=e.err)
@exportToSuperVdsm
def volumeReplaceBrickPause(volumeName, existingBrick, newBrick):
- rc, out, err = _execGluster(_getGlusterVolCmd() + ["replace-brick",
- volumeName,
- existingBrick, newBrick,
- "pause"])
- if rc:
- raise ge.GlusterVolumeReplaceBrickPauseFailedException(rc, out, err)
- else:
+ command = _getGlusterVolCmd() + ["replace-brick", volumeName,
+ existingBrick, newBrick, "pause"]
+ try:
+ _execGlusterXml(command)
return True
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeReplaceBrickPauseFailedException(rc=e.rc,
+ err=e.err)
+
+
+def _parseVolumeReplaceBrickStatus(tree):
+ """
+ returns {'taskId': UUID,
+ 'filesMoved': INT,
+ 'movingFile': STRING,
+ 'status': TaskStatus}}
+ """
+ return {'taskId': tree.find('volReplaceBrick/id').text,
+ 'filesMoved': int(tree.find('volReplaceBrick/filesMoved').text),
+ 'movingFile': tree.find('volReplaceBrick/movingFile').text,
+ 'status': tree.find('volReplaceBrick/status').text}
@exportToSuperVdsm
def volumeReplaceBrickStatus(volumeName, existingBrick, newBrick):
- rc, out, err = _execGluster(_getGlusterVolCmd() + ["replace-brick",
- volumeName,
- existingBrick, newBrick,
- "status"])
- if rc:
- raise ge.GlusterVolumeReplaceBrickStatusFailedException(rc, out,
- err)
- message = "\n".join(out)
- statLine = out[0].strip().upper()
- if BrickStatus.PAUSED in statLine:
- return BrickStatus.PAUSED, message
- elif statLine.endswith('MIGRATION COMPLETE'):
- return BrickStatus.COMPLETED, message
- elif statLine.startswith('NUMBER OF FILES MIGRATED'):
- return BrickStatus.RUNNING, message
- elif statLine.endswith("UNKNOWN"):
- return BrickStatus.UNKNOWN, message
- else:
- return BrickStatus.NA, message
+ command = _getGlusterVolCmd() + ["replace-brick", volumeName,
+ existingBrick, newBrick, "status"]
+ try:
+ xmltree, out = _execGlusterXml(command)
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeReplaceBrickStatusFailedException(rc=e.rc,
+ err=e.err)
+ try:
+ return _parseVolumeReplaceBrickStatus(xmltree)
+ except:
+ raise ge.GlusterXmlErrorException(err=out)
@exportToSuperVdsm
@@ -399,12 +515,12 @@
existingBrick, newBrick, "commit"]
if force:
command.append('force')
- rc, out, err = _execGluster(command)
- if rc:
- raise ge.GlusterVolumeReplaceBrickCommitFailedException(rc, out,
- err)
- else:
+ try:
+ _execGlusterXml(command)
return True
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeReplaceBrickCommitFailedException(rc=e.rc,
+ err=e.err)
@exportToSuperVdsm
@@ -413,12 +529,15 @@
if replicaCount:
command += ["replica", "%s" % replicaCount]
command += brickList + ["start"]
-
- rc, out, err = _execGluster(command)
- if rc:
- raise ge.GlusterVolumeRemoveBrickStartFailedException(rc, out, err)
- else:
- return True
+ try:
+ xmltree, out = _execGlusterXml(command)
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeRemoveBrickStartFailedException(rc=e.rc,
+ err=e.err)
+ try:
+ return {'taskId': xmltree.find('id').text}
+ except:
+ raise ge.GlusterXmlErrorException(err=out)
@exportToSuperVdsm
@@ -427,12 +546,16 @@
if replicaCount:
command += ["replica", "%s" % replicaCount]
command += brickList + ["stop"]
- rc, out, err = _execGluster(command)
-
- if rc:
- raise ge.GlusterVolumeRemoveBrickStopFailedException(rc, out, err)
- else:
+ try:
+ _execGlusterXml(command)
return True
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeRemoveBrickStopFailedException(rc=e.rc,
+ err=e.err)
+
+
+def _parseVolumeRemoveBrickStatus(tree):
+ return _parseVolumeRebalanceRemoveBrickStatus(tree, 'remove-brick')
@exportToSuperVdsm
@@ -441,12 +564,15 @@
if replicaCount:
command += ["replica", "%s" % replicaCount]
command += brickList + ["status"]
- rc, out, err = _execGluster(command)
-
- if rc:
- raise ge.GlusterVolumeRemoveBrickStatusFailedException(rc, out, err)
- else:
- return "\n".join(out)
+ try:
+ xmltree, out = _execGlusterXml(command)
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeRemoveBrickStatusFailedException(rc=e.rc,
+ err=e.err)
+ try:
+ return _parseVolumeRemoveBrickStatus(xmltree)
+ except:
+ raise ge.GlusterXmlErrorException(err=out)
@exportToSuperVdsm
@@ -455,12 +581,12 @@
if replicaCount:
command += ["replica", "%s" % replicaCount]
command += brickList + ["commit"]
- rc, out, err = _execGluster(command)
-
- if rc:
- raise ge.GlusterVolumeRemoveBrickCommitFailedException(rc, out, err)
- else:
+ try:
+ _execGlusterXml(command)
return True
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeRemoveBrickCommitFailedException(rc=e.rc,
+ err=e.err)
@exportToSuperVdsm
@@ -469,12 +595,12 @@
if replicaCount:
command += ["replica", "%s" % replicaCount]
command += brickList + ["force"]
- rc, out, err = _execGluster(command)
-
- if rc:
- raise ge.GlusterVolumeRemoveBrickForceFailedException(rc, out, err)
- else:
+ try:
+ _execGlusterXml(command)
return True
+ except ge.GlusterCmdFailedException, e:
+ raise ge.GlusterVolumeRemoveBrickForceFailedException(rc=e.rc,
+ err=e.err)
@exportToSuperVdsm
diff --git a/vdsm/gluster/exception.py b/vdsm/gluster/exception.py
index f4f497b..f209885 100644
--- a/vdsm/gluster/exception.py
+++ b/vdsm/gluster/exception.py
@@ -323,6 +323,11 @@
message = "Volume remove brick force failed"
+class GlusterVolumeStatusAllFailedException(GlusterVolumeException):
+ code = 4158
+ message = "Volume status all failed"
+
+
# Host
class GlusterHostException(GlusterException):
code = 4400
diff --git a/vdsm/storage/taskManager.py b/vdsm/storage/taskManager.py
index 3bc12f3..0a269cd 100644
--- a/vdsm/storage/taskManager.py
+++ b/vdsm/storage/taskManager.py
@@ -25,6 +25,12 @@
import storage_exception as se
from task import Task, Job, TaskCleanType
from threadPool import ThreadPool
+try:
+ from gluster import cli as gcli
+ from gluster import exception as ge
+ _glusterEnabled = True
+except ImportError:
+ _glusterEnabled = False
class TaskManager:
@@ -113,19 +119,82 @@
self.log.debug("Return: %s", subRes)
return subRes
- def getAllTasks(self):
+ def _getAllGlusterTasks(self):
"""
- Return Tasks for all public tasks.
+ Return all gluster tasks
+ """
+ subRes = {}
+ if not _glusterEnabled:
+ return subRes
+
+ for taskId, value in gcli.volumeStatusAll():
+ msg = ''
+ state = ''
+ try:
+ if value['taskType'] == gcli.TaskType.REBALANCE:
+ status = gcli.volumeRebalanceStatus(value['volumeName'])
+ msg = ('Files [scanned: %d, moved: %d, failed: %d], '
+ 'Total size moved: %d') % \
+ (status['summary']['filesScanned'],
+ status['summary']['filesMoved'],
+ status['summary']['filesFailed'],
+ status['summary']['totalSizeMoved'])
+ state = status['summary']['status']
+ elif value['taskType'] == gcli.TaskType.REMOVE_BRICK:
+ status = gcli.volumeRemoveBrickStatus(value['volumeName'],
+ value['bricks'])
+ msg = ('Files [scanned: %d, moved: %d, failed: %d], '
+ 'Total size moved: %d') % \
+ (status['summary']['filesScanned'],
+ status['summary']['filesMoved'],
+ status['summary']['filesFailed'],
+ status['summary']['totalSizeMoved'])
+ state = status['summary']['status']
+ elif value['taskType'] == gcli.TaskType.REPLACE_BRICK:
+ status = gcli.volumeReplaceBrickStatus(value['volumeName'],
+ value['bricks'][0],
+ value['bricks'][1])
+ msg = 'Files moved: %d, Moving file: %s' % \
+ (status['filesMoved'], status['movingFile'])
+ state = status['status']
+ except ge.GlusterException:
+ self.log.error("gluster exception occured", exc_info=True)
+
+ subRes[taskId] = {"id": taskId,
+ "verb": value['volumeName'],
+ "state": state,
+ "code": value['taskType'],
+ "message": msg,
+ "result": '',
+ "tag": 'gluster'}
+ return subRes
+
+ def getAllTasks(self, tag=[]):
+ """
+ Return Tasks for all public tasks if param tag is empty,
+ else return tasks those tags are in param tag.
"""
self.log.debug("Entry.")
subRes = {}
for taskID, task in self._tasks.items():
try:
- subRes[taskID] = task.getDetails()
+ if not tag:
+ subRes[taskID] = task.getDetails()
+ elif task.getTags() in tag:
+ subRes[taskID] = task.getDetails()
except se.UnknownTask:
# Return info for existing tasks only.
self.log.warn("Unknown task %s. Maybe task was already "
"cleared.", taskID)
+
+ try:
+ if not tag:
+ subRes.update(self._getAllGlusterTasks())
+ elif 'gluster' in tag:
+ subRes.update(self._getAllGlusterTasks())
+ except ge.GlusterException:
+ self.log.error("gluster exception occured", exc_info=True)
+
self.log.debug("Return: %s", subRes)
return subRes
--
To view, visit http://gerrit.ovirt.org/7579
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I9c765cbfebb5ba22f0d21efa04c824ea4daf6432
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Bala.FA <barumuga(a)redhat.com>
Gerrit-Reviewer: Ayal Baron <abaron(a)redhat.com>
Gerrit-Reviewer: Dan Kenigsberg <danken(a)redhat.com>
Gerrit-Reviewer: Federico Simoncelli <fsimonce(a)redhat.com>
Gerrit-Reviewer: Saggi Mizrahi <smizrahi(a)redhat.com>
Gerrit-Reviewer: Timothy Asir <tjeyasin(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: vm: Modify and save state after hotunplugging the disk
by xfrancis@redhat.com
Xavi Francisco has uploaded a new change for review.
Change subject: vm: Modify and save state after hotunplugging the disk
......................................................................
vm: Modify and save state after hotunplugging the disk
The rationale behind this patch is to modify the way the hotunplug
process is executed. Now when the hotunplug command is called we first
remove the disk from the internal state and after that the libvirt
hotunplug command is executed.The problem with that approach is
that if VDSM restarts between the modification of the internal
state and the actual unplugging of the disk, the disk never gets
unplugged leaving the system in an inconsistent state.
This patch solves the issue by executing the modification of the
internal state after the actual unplugging has successfully happened.
This state is later saved so if either the disk has been unplugged or
not VDSM is able to recover its state in case of a restart.
Change-Id: I5169fa16591283de33aa82c5730626bfd5d3eaf5
Signed-off-by: Xavi Francisco <xfrancis(a)redhat.com>
---
M vdsm/virt/vm.py
1 file changed, 12 insertions(+), 20 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/87/28187/1
diff --git a/vdsm/virt/vm.py b/vdsm/virt/vm.py
index bd670b9..1eb2adc 100644
--- a/vdsm/virt/vm.py
+++ b/vdsm/virt/vm.py
@@ -3449,20 +3449,6 @@
driveXml = drive.getXML().toprettyxml(encoding='utf-8')
self.log.debug("Hotunplug disk xml: %s", driveXml)
# Remove found disk from vm's drives list
- if isVdsmImage(drive):
- self.sdIds.remove(drive.domainID)
- self._devices[DISK_DEVICES].remove(drive)
- # Find and remove disk device from vm's conf
- diskDev = None
- for dev in self.conf['devices'][:]:
- if (dev['type'] == DISK_DEVICES and
- dev['path'] == drive.path):
- with self._confLock:
- self.conf['devices'].remove(dev)
- diskDev = dev
- break
-
- self.saveState()
hooks.before_disk_hotunplug(driveXml, self.conf,
params=drive.custom)
@@ -3472,19 +3458,25 @@
self.log.error("Hotunplug failed", exc_info=True)
if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN:
return errCode['noVM']
- self._devices[DISK_DEVICES].append(drive)
- # Restore disk device in vm's conf and _devices
- if diskDev:
- with self._confLock:
- self.conf['devices'].append(diskDev)
- self.saveState()
return {
'status': {'code': errCode['hotunplugDisk']['status']['code'],
'message': e.message}}
else:
+ if isVdsmImage(drive):
+ self.sdIds.remove(drive.domainID)
+ self._devices[DISK_DEVICES].remove(drive)
+ # Find and remove disk device from vm's conf
+ for dev in self.conf['devices'][:]:
+ if (dev['type'] == DISK_DEVICES and
+ dev['path'] == drive.path):
+ with self._confLock:
+ self.conf['devices'].remove(dev)
+ break
hooks.after_disk_hotunplug(driveXml, self.conf,
params=drive.custom)
self._cleanupDrives(drive)
+ finally:
+ self.saveState()
return {'status': doneCode, 'vmList': self.status()}
--
To view, visit http://gerrit.ovirt.org/28187
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I5169fa16591283de33aa82c5730626bfd5d3eaf5
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Xavi Francisco <xfrancis(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: incorporate stop/start logic to functional tests
by ykleinbe@redhat.com
Yoav Kleinberger has uploaded a new change for review.
Change subject: incorporate stop/start logic to functional tests
......................................................................
incorporate stop/start logic to functional tests
Change-Id: I55b86578a681de2b09ac5ee01ca2e3b9443ed13a
Signed-off-by: Yoav Kleinberger <ykleinbe(a)redhat.com>
---
M tests/functional/new/basicLocalFSStorageDomainTest.py
A tests/functional/new/controlvdsm.py
2 files changed, 65 insertions(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/23/29423/1
diff --git a/tests/functional/new/basicLocalFSStorageDomainTest.py b/tests/functional/new/basicLocalFSStorageDomainTest.py
index a590886..3280e16 100644
--- a/tests/functional/new/basicLocalFSStorageDomainTest.py
+++ b/tests/functional/new/basicLocalFSStorageDomainTest.py
@@ -6,10 +6,22 @@
import uuid
import storage.volume
import storage.image
+import logging
+logging.basicConfig(level=logging.DEBUG, format='%(asctime)s TEST %(levelname)s: %(message)s')
from . import testlib
+from . import controlvdsm
class TestBasicLocalFSStorageDomain:
- def notest_flow_connect_create_storage_domain_format_disconnect(self):
+ def setup(self):
+ control_vdsm = controlvdsm.ControlVDSM()
+ control_vdsm.cleanup()
+
+ @classmethod
+ def teardown_class(cls):
+ control_vdsm = controlvdsm.ControlVDSM()
+ control_vdsm.cleanup()
+
+ def test_flow_connect_create_storage_domain_format_disconnect(self):
with testlib.TemporaryDirectory() as directory:
with testlib.VDSMTestTools() as (vdsm, verify):
diff --git a/tests/functional/new/controlvdsm.py b/tests/functional/new/controlvdsm.py
new file mode 100644
index 0000000..a7e8e79
--- /dev/null
+++ b/tests/functional/new/controlvdsm.py
@@ -0,0 +1,52 @@
+import subprocess
+import logging
+import vdsm.vdscli
+import socket
+import vdsm.config
+import time
+
+class ControlVDSM:
+ def cleanup(self):
+ self._stop_service()
+ assert not self._service_running()
+ self._brutally_clean_files()
+# self._restart_service()
+ return self._check_connection()
+
+ def _check_connection(self):
+ useSSL = vdsm.config.config.getboolean('vars', 'ssl')
+ vdsmClient = vdsm.vdscli.connect(useSSL=useSSL)
+ RETRIES = 5
+ for _ in range(RETRIES):
+ try:
+ vdsmClient.getStorageDomainsList()
+ logging.info('VDSM ready for testing')
+ except socket.error as e:
+ logging.warning('could not talk to VDSM: %s' % e)
+ time.sleep(1)
+
+ raise Exception('could not connect to VDSM')
+
+ def _stop_service(self):
+ self._run("sudo service vdsmd stop")
+
+ def _service_running(self):
+ return_code = subprocess.call('sudo service vdsmd status', shell=True, stdout=open('/dev/null','w'), stderr=open('/dev/null','w'))
+ logging.info('vdsm running: %s' % (return_code == 0))
+ return return_code == 0
+
+ def _restart_service(self):
+ self._run("sudo vdsm-tool configure --force")
+ self._run("sudo service vdsmd start")
+
+ def _run(self, command):
+ logging.info('running: %s' % command)
+ return_code = subprocess.call(command, shell=True, close_fds=True, stdout=open('/dev/null','w'), stderr=open('/dev/null','w'))
+ if return_code != 0:
+ logging.warning('failure! command was: %s' % command)
+ else:
+ logging.info('finished.')
+
+ def _brutally_clean_files(self):
+ logging.warning('removing /rhev/data-center without asking too many questions')
+ self._run('sudo rm -fr /rhev/data-center/*')
--
To view, visit http://gerrit.ovirt.org/29423
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I55b86578a681de2b09ac5ee01ca2e3b9443ed13a
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Yoav Kleinberger <ykleinbe(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: tests: alignmentScanTests.py is a functional test
by ykleinbe@redhat.com
Yoav Kleinberger has uploaded a new change for review.
Change subject: tests: alignmentScanTests.py is a functional test
......................................................................
tests: alignmentScanTests.py is a functional test
Since alignmentScanTests invokes actual storage code, and does not mock
it out, it should be categorized as a functional test.
Change-Id: I4e20d17c3ebee1203bb5a721ce44d5867570ce8e
Signed-off-by: Yoav Kleinberger <ykleinbe(a)redhat.com>
---
M tests/Makefile.am
M tests/functional/Makefile.am
R tests/functional/alignmentScanTests.py
3 files changed, 1 insertion(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/45/29745/1
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4ef8f7d..f7f4f97 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -23,7 +23,6 @@
SUBDIRS = functional
test_modules = \
- alignmentScanTests.py \
blocksdTests.py \
bridgeTests.py \
cPopenTests.py \
diff --git a/tests/functional/Makefile.am b/tests/functional/Makefile.am
index b87fdf0..4fc2cc8 100644
--- a/tests/functional/Makefile.am
+++ b/tests/functional/Makefile.am
@@ -33,6 +33,7 @@
vmRecoveryTests.py \
storageTests.py \
veth.py \
+ alignmentScanTests.py \
$(NULL)
dist_vdsmfunctests_DATA = \
diff --git a/tests/alignmentScanTests.py b/tests/functional/alignmentScanTests.py
similarity index 100%
rename from tests/alignmentScanTests.py
rename to tests/functional/alignmentScanTests.py
--
To view, visit http://gerrit.ovirt.org/29745
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I4e20d17c3ebee1203bb5a721ce44d5867570ce8e
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Yoav Kleinberger <ykleinbe(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: tests: use 'localhost' explicitly in test
by ykleinbe@redhat.com
Yoav Kleinberger has uploaded a new change for review.
Change subject: tests: use 'localhost' explicitly in test
......................................................................
tests: use 'localhost' explicitly in test
Previously tests could on some machines (in case the machine has a
non-default hostname). Now, since we use 'localhost' explicitly, this
will not happen.
Change-Id: I89990cff46e64120262e250eee9238b49c4edee4
Signed-off-by: Yoav Kleinberger <ykleinbe(a)redhat.com>
---
M tests/functional/storageTests.py
1 file changed, 2 insertions(+), 1 deletion(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/07/28107/1
diff --git a/tests/functional/storageTests.py b/tests/functional/storageTests.py
index 80ba312..76ca91d 100644
--- a/tests/functional/storageTests.py
+++ b/tests/functional/storageTests.py
@@ -79,7 +79,8 @@
isSSL = config.getboolean('vars', 'ssl')
if isSSL and os.geteuid() != 0:
raise SkipTest("Must be root to use SSL connection to server")
- self.s = vdscli.connect(useSSL=isSSL)
+ address = 'localhost:%s' % config.get('addresses', 'management_port')
+ self.s = vdscli.connect(hostPort=address, useSSL=isSSL)
def assertVdsOK(self, vdsResult):
# code == 0 means OK
--
To view, visit http://gerrit.ovirt.org/28107
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I89990cff46e64120262e250eee9238b49c4edee4
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Yoav Kleinberger <ykleinbe(a)redhat.com>
7 years, 5 months
Change in vdsm[master]: multipath: Move all calls to multipath exe to a single method
by smizrahi@redhat.com
Saggi Mizrahi has uploaded a new change for review.
Change subject: multipath: Move all calls to multipath exe to a single method
......................................................................
multipath: Move all calls to multipath exe to a single method
This makes the code a bit cleaner
Change-Id: I52afc07a07a925ed7572eb369deb7c203edb04cd
Signed-off-by: Saggi Mizrahi <smizrahi(a)redhat.com>
---
M vdsm/storage/multipath.py
1 file changed, 11 insertions(+), 4 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/55/19255/1
diff --git a/vdsm/storage/multipath.py b/vdsm/storage/multipath.py
index 924d747..c31b5c3 100644
--- a/vdsm/storage/multipath.py
+++ b/vdsm/storage/multipath.py
@@ -94,6 +94,10 @@
)
+def _runCmd(args):
+ return misc.execCmd([constants.EXT_MULTIPATH] + args, sudo=True)
+
+
def rescan():
"""
Forces multipath daemon to rescan the list of available devices and
@@ -108,8 +112,8 @@
supervdsm.getProxy().forceScsiScan()
# Now let multipath daemon pick up new devices
- cmd = [constants.EXT_MULTIPATH, "-r"]
- misc.execCmd(cmd, sudo=True)
+
+ _runCmd("-r")
def isEnabled():
@@ -154,6 +158,10 @@
return False
+def flushAll():
+ _runCmd("-F")
+
+
def setupMultipath():
"""
Set up the multipath daemon configuration to the known and
@@ -173,8 +181,7 @@
raise se.MultipathSetupError()
misc.persistFile(MPATH_CONF)
- # Flush all unused multipath device maps
- misc.execCmd([constants.EXT_MULTIPATH, "-F"], sudo=True)
+ flushAll()
cmd = [constants.EXT_VDSM_TOOL, "service-reload", "multipathd"]
rc = misc.execCmd(cmd, sudo=True)[0]
--
To view, visit http://gerrit.ovirt.org/19255
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I52afc07a07a925ed7572eb369deb7c203edb04cd
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Saggi Mizrahi <smizrahi(a)redhat.com>
7 years, 6 months
Change in vdsm[master]: [WIP]Java Bindings: Proton support in Java Bindings
by smizrahi@redhat.com
Saggi Mizrahi has uploaded a new change for review.
Change subject: [WIP]Java Bindings: Proton support in Java Bindings
......................................................................
[WIP]Java Bindings: Proton support in Java Bindings
Change-Id: I94c52e118cb63d7df84b89a9b93da7b9e477be91
Signed-off-by: Saggi Mizrahi <smizrahi(a)redhat.com>
---
A client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonAuthenticator.java
A client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonClient.java
A client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonListener.java
A client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonReactor.java
A client/java/vdsm-json-rpc/src/test/java/org/ovirt/vdsm/jsonrpc/AmqpReactorTestHelper.java
A client/java/vdsm-json-rpc/src/test/java/org/ovirt/vdsm/jsonrpc/TestJsonRpcClientAMQP.java
6 files changed, 844 insertions(+), 0 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/28/15428/1
diff --git a/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonAuthenticator.java b/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonAuthenticator.java
new file mode 100644
index 0000000..35c9099
--- /dev/null
+++ b/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonAuthenticator.java
@@ -0,0 +1,98 @@
+package org.ovirt.vdsm.reactors;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.qpid.proton.driver.Connector;
+import org.apache.qpid.proton.engine.Sasl;
+import org.apache.qpid.proton.engine.Sasl.SaslOutcome;
+import org.apache.qpid.proton.engine.Sasl.SaslState;
+import org.ovirt.vdsm.reactors.ProtonAuthenticator.AuthenticatorType;
+
+public final class ProtonAuthenticator {
+
+ public enum AuthenticatorType {
+
+ SERVER, CLIENT
+ }
+
+ public enum ConnectionState {
+
+ AUTHENTICATING, CONNECTED, FAILED
+ }
+ private ConnectionState _state;
+ final private AuthenticatorType _authType;
+ final private Connector<?> _connector;
+
+ public ProtonAuthenticator(Connector<?> connector,
+ AuthenticatorType authType) {
+ _authType = authType;
+ setState(ConnectionState.AUTHENTICATING);
+ _connector = connector;
+ final Sasl sasl = _connector.sasl();
+ if (authType == AuthenticatorType.CLIENT) {
+ sasl.setMechanisms(new String[]{"ANONYMOUS"});
+ sasl.client();
+ }
+ }
+
+ private void setState(ConnectionState state) {
+ _state = state;
+ }
+
+ public ConnectionState getState() {
+ return _state;
+ }
+
+ public void authenticate() {
+ final Sasl sasl = _connector.sasl();
+
+ while (true) {
+ try {
+ this._connector.process();
+ } catch (IOException ex) {
+ return;
+ }
+ final SaslState state = sasl.getState();
+ switch (state) {
+ case PN_SASL_CONF:
+ if (_authType == AuthenticatorType.SERVER) {
+ sasl.setMechanisms(new String[]{"ANONYMOUS"});
+ sasl.server();
+ }
+ break;
+ case PN_SASL_STEP:
+ if (_authType == AuthenticatorType.SERVER) {
+ final String[] mechs = sasl.getRemoteMechanisms();
+ if (mechs.length < 1) {
+ sasl.done(SaslOutcome.PN_SASL_AUTH);
+ break;
+ }
+
+ final String mech = mechs[0];
+ if (mech.equals("ANONYMOUS")) {
+ sasl.done(SaslOutcome.PN_SASL_OK);
+ } else {
+ sasl.done(SaslOutcome.PN_SASL_AUTH);
+ }
+ }
+ return;
+ case PN_SASL_PASS:
+ this.setState(ConnectionState.CONNECTED);
+ return;
+ case PN_SASL_FAIL:
+ this.setState(ConnectionState.FAILED);
+ return;
+ case PN_SASL_IDLE:
+
+ break;
+ default:
+ return;
+ }
+ }
+ }
+
+ public AuthenticatorType getAuthType() {
+ return _authType;
+ }
+}
diff --git a/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonClient.java b/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonClient.java
new file mode 100644
index 0000000..4baffbf
--- /dev/null
+++ b/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonClient.java
@@ -0,0 +1,224 @@
+package org.ovirt.vdsm.reactors;
+
+import java.nio.ByteBuffer;
+import java.util.Calendar;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Future;
+
+import javax.swing.event.EventListenerList;
+
+import org.apache.qpid.proton.amqp.Binary;
+import org.apache.qpid.proton.amqp.messaging.Data;
+import org.apache.qpid.proton.amqp.messaging.Section;
+import org.apache.qpid.proton.engine.Delivery;
+import org.apache.qpid.proton.engine.EndpointState;
+import org.apache.qpid.proton.engine.Link;
+import org.apache.qpid.proton.engine.Receiver;
+import org.apache.qpid.proton.engine.Sender;
+import org.apache.qpid.proton.engine.Session;
+import org.apache.qpid.proton.message.Message;
+import org.apache.qpid.proton.message.MessageFactory;
+import org.apache.qpid.proton.message.impl.MessageFactoryImpl;
+
+public final class ProtonClient implements ReactorClient {
+ private final ProtonReactor _reactor;
+ final private Session _ssn;
+
+ private Sender _sender;
+ private Receiver _receiver;
+
+ private final int _CREDIT = 10;
+ private final ConcurrentLinkedQueue<ByteBuffer> _outbox;
+ private final EventListenerList _eventListeners;
+ private final int _deliveryTimeoutSec;
+ private final MessageFactory _msgFactory;
+
+ public ProtonClient(ProtonReactor reactor, Session session) {
+ _ssn = session;
+ _sender = null;
+ _receiver = null;
+ _outbox = new ConcurrentLinkedQueue<>();
+ _eventListeners = new EventListenerList();
+ _deliveryTimeoutSec = 60 * 3;
+ _reactor = reactor;
+ _msgFactory = new MessageFactoryImpl();
+ }
+
+ @Override
+ public void addEventListener(EventListener el) {
+ synchronized (_eventListeners) {
+ _eventListeners.add(EventListener.class, el);
+ }
+ }
+
+ @Override
+ public void removeEventListener(EventListener el) {
+ synchronized (_eventListeners) {
+ _eventListeners.remove(EventListener.class, el);
+ }
+ }
+
+ private void emitOnMessageReceived(ByteBuffer message) {
+ synchronized (_eventListeners) {
+ final Class<EventListener> cls = EventListener.class;
+ final EventListener[] els = _eventListeners.getListeners(cls);
+ for (EventListener el : els) {
+ el.onMessageReceived(this, message);
+ }
+ }
+ }
+
+ @Override
+ public void sendMessage(ByteBuffer message) {
+ _outbox.add(message);
+ _reactor.wakeup();
+ }
+
+ public void addLink(Link link) {
+ assert (link.getSession().equals(_ssn));
+
+ if (link instanceof Sender) {
+ if (_sender != null) {
+ // already have a sender
+ link.close();
+ return;
+ }
+
+ _sender = (Sender) link;
+ } else {
+ assert (link instanceof Receiver);
+ if (_receiver != null) {
+ // already have a receiver
+ link.close();
+ return;
+ }
+
+ _receiver = (Receiver) link;
+ _receiver.flow(_CREDIT);
+ }
+ link.open();
+ }
+
+ private Message _popOutgoingMessage() {
+ final ByteBuffer data = _outbox.poll();
+ if (data == null) {
+ return null;
+ }
+
+ final Section body = new Data(Binary.create(data));
+ final Message msg = _msgFactory.createMessage();
+ msg.setBody(body);
+ msg.setAddress(_sender.getTarget().toString());
+ return msg;
+ }
+
+ public void queueDeliveries() {
+ if (_sender == null) {
+ final String uuid = UUID.randomUUID().toString();
+ _sender = _ssn.sender("Sender-" + uuid);
+ }
+
+ while (_sender.getCredit() > 0) {
+ final Message m = _popOutgoingMessage();
+ if (m == null) {
+ return;
+ }
+
+ final String uuid = UUID.randomUUID().toString();
+ final Delivery d = _sender
+ .delivery(("outgoing-" + uuid).getBytes());
+ d.setContext(m);
+ }
+ }
+
+ public void processDelivery(Delivery delivery) {
+ assert (_ssn.equals(delivery.getLink().getSession()));
+
+ if (delivery.isReadable()) {
+ _processIncomingDelivery(delivery);
+ } else {
+ assert (delivery.isWritable());
+ _processOutgoingDelivery(delivery);
+ }
+ }
+
+ private void _processOutgoingDelivery(Delivery delivery) {
+ final Sender link = (Sender) delivery.getLink();
+ assert (link.equals(_sender));
+
+ final Message msg = (Message) delivery.getContext();
+ // TBD: Buffer can be reused forever. Change in case of
+ // performance issues.
+ ByteBuffer buff;
+ int i = 1;
+ int written = 0;
+ do {
+ buff = ByteBuffer.allocate(i * 4096);
+ written = msg.encode(buff.array(), 0, buff.capacity());
+ i++;
+ } while (written == buff.capacity());
+
+ link.send(buff.array(), 0, written);
+ if (link.advance()) {
+ // Attach timeout to the delivery
+ final Calendar calendar = Calendar.getInstance();
+ calendar.add(Calendar.SECOND, _deliveryTimeoutSec);
+ delivery.setContext(calendar);
+ }
+ }
+
+ private void _processIncomingDelivery(Delivery delivery) {
+ int total = 0;
+ int read = 0;
+ ByteBuffer buff = ByteBuffer.allocate(4096);
+
+ while (read >= 0) {
+ total += read;
+ if (total >= buff.capacity()) {
+ final ByteBuffer buff2 = ByteBuffer
+ .allocate(buff.capacity() * 2);
+ buff2.put(buff);
+ buff = buff2;
+ }
+ read = _receiver.recv(buff.array(), total, buff.capacity() - total);
+ }
+
+ final Message msg = _msgFactory.createMessage();
+ msg.decode(buff.array(), 0, total);
+
+ assert (msg.getBody() instanceof Data);
+ final Data body = (Data) msg.getBody();
+ final ByteBuffer bb = body.getValue().asByteBuffer();
+ delivery.settle();
+ emitOnMessageReceived(bb);
+ }
+
+ @Override
+ public Future<Void> close() {
+ final Session ssn = _ssn;
+ return _reactor.queueOperation(new Callable<Void>() {
+ @Override
+ public Void call() {
+ ssn.close();
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public boolean closed() {
+ return _ssn.getLocalState().equals(EndpointState.CLOSED);
+ }
+
+ public void removeLink(Link link) {
+ if (link.equals(_sender)) {
+ _sender = null;
+ } else {
+ assert (link.equals(_receiver));
+ _receiver = null;
+ }
+ link.close();
+ }
+}
diff --git a/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonListener.java b/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonListener.java
new file mode 100644
index 0000000..35896f4
--- /dev/null
+++ b/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonListener.java
@@ -0,0 +1,42 @@
+package org.ovirt.vdsm.reactors;
+
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+
+import org.apache.qpid.proton.driver.Listener;
+
+public final class ProtonListener implements ReactorListener {
+ private final EventListener _eventListener;
+ private Listener<ProtonListener> _listener;
+ private final ProtonReactor _reactor;
+
+ public ProtonListener(ProtonReactor reactor, EventListener eventListener) {
+ _eventListener = eventListener;
+ _reactor = reactor;
+ }
+
+ public void setListener(Listener<ProtonListener> l) {
+ _listener = l;
+ }
+
+ public void accept(ReactorClient client) {
+ _eventListener.onAcccept(this, client);
+ }
+
+ @Override
+ public Future<Void> close() {
+ final Listener<ProtonListener> l = _listener;
+ return _reactor.queueOperation(new Callable<Void>() {
+ @Override
+ public Void call() {
+ try {
+ l.close();
+ } catch (IOException e) {
+ // already closed
+ }
+ return null;
+ }
+ });
+ }
+}
diff --git a/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonReactor.java b/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonReactor.java
new file mode 100644
index 0000000..b5a38b4
--- /dev/null
+++ b/client/java/vdsm-json-rpc/src/main/java/org/ovirt/vdsm/reactors/ProtonReactor.java
@@ -0,0 +1,452 @@
+package org.ovirt.vdsm.reactors;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+
+import org.apache.qpid.proton.driver.Connector;
+import org.apache.qpid.proton.driver.Driver;
+import org.apache.qpid.proton.driver.Listener;
+import org.apache.qpid.proton.driver.impl.DriverFactoryImpl;
+import org.apache.qpid.proton.engine.Connection;
+import org.apache.qpid.proton.engine.Delivery;
+import org.apache.qpid.proton.engine.EndpointState;
+import org.apache.qpid.proton.engine.EngineFactory;
+import org.apache.qpid.proton.engine.Link;
+import org.apache.qpid.proton.engine.Receiver;
+import org.apache.qpid.proton.engine.Session;
+import org.apache.qpid.proton.engine.impl.EngineFactoryImpl;
+import org.ovirt.vdsm.reactors.ProtonAuthenticator.AuthenticatorType;
+import org.ovirt.vdsm.reactors.ProtonAuthenticator.ConnectionState;
+import org.ovirt.vdsm.util.ChainedOperation;
+import org.ovirt.vdsm.util.ReactorScheduler;
+
+public final class ProtonReactor implements Reactor {
+
+ private final Driver _driver;
+ private final ReactorScheduler _scheduler;
+ private boolean _isRunning;
+ final Object _syncRoot = new Object();
+ final ProtonReactor reactor = this;
+ private EngineFactory _engineFactory;
+
+ public boolean isRunning() {
+ return _isRunning;
+ }
+
+ public ProtonReactor() throws IOException {
+ _engineFactory = new EngineFactoryImpl();
+ _driver = new DriverFactoryImpl().createDriver();
+ _isRunning = false;
+ _scheduler = new ReactorScheduler();
+ }
+
+ @Override
+ public void finalize() throws Throwable {
+ try {
+ _driver.destroy();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ // Creates a listener, returns null if failed to bind or reactor is not
+ // running;
+ @Override
+ public Future<ReactorListener> createListener(final String host,
+ final int port,
+ final ReactorListener.EventListener eventListener) {
+
+ return queueOperation(new Callable<ReactorListener>() {
+ @Override
+ public ProtonListener call() {
+
+ final ProtonListener listener = new ProtonListener(reactor, eventListener);
+ final Listener<ProtonListener> l = _driver.createListener(host,
+ port, listener);
+
+ listener.setListener(l);
+
+ if (l == null) {
+ return null;
+ }
+
+ return listener;
+ }
+ });
+ }
+
+ @Override
+ public Future<ReactorClient> createClient(final String host, final int port) {
+ final Driver driver = _driver;
+ final EngineFactory engineFactory = _engineFactory;
+
+ return queueOperation(new ChainedOperation.Operation<ReactorClient>() {
+ final private int _INIT = 1;
+ final private int _AUTHENTICATE = 2;
+ final private int _DONE = 3;
+ private int _state;
+ final private Driver _driver;
+ final private ProtonReactor _reactor;
+ private Connector<ProtonAuthenticator> _connector;
+ private ProtonAuthenticator _auth;
+ private boolean _done;
+ private boolean _cancelled;
+ private ReactorClient _result;
+ private EngineFactory _engineFactory;
+
+ {
+ _driver = driver;
+ _reactor = reactor;
+ _state = _INIT;
+ _done = false;
+ _cancelled = false;
+ _engineFactory = engineFactory;
+ }
+
+ @Override
+ public void call(final boolean cancelled) {
+ switch (_state) {
+ case _INIT:
+ if (cancelled) {
+ _cancelled = true;
+ _done = true;
+ return;
+ }
+
+ _connector = this._driver.createConnector(host, port, null);
+
+ final Connection connection = engineFactory.createConnection();
+ _connector.setConnection(connection);
+ _auth = new ProtonAuthenticator(_connector,
+ AuthenticatorType.CLIENT);
+ _connector.setContext(_auth);
+ connection.open();
+ _state = _AUTHENTICATE;
+ case _AUTHENTICATE:
+ if (cancelled) {
+ _cancelled = true;
+ _close();
+ return;
+ }
+
+ switch (_auth.getState()) {
+ case AUTHENTICATING:
+ _auth.authenticate();
+ try {
+ _connector.process();
+ } catch (IOException e) {
+ // ignore
+ }
+ return;
+ case FAILED:
+ _close();
+ return;
+ case CONNECTED:
+ // Success !
+ break;
+ }
+
+ Session ssn = _connector.getConnection().session();
+ ssn.open();
+ _result = new ProtonClient(_reactor, ssn);
+ ssn.setContext(_result);
+ _done = true;
+ _state = _DONE;
+ }
+ }
+
+ private void _close() {
+ _connector.getConnection().close();
+ _connector.close();
+ _done = true;
+ _result = null;
+ }
+
+ @Override
+ public boolean isDone() {
+ return _done;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return _cancelled;
+ }
+
+ @Override
+ public ReactorClient getResult() {
+ return _result;
+ }
+ });
+ }
+
+ // Queues operation to be run in the serving loop.
+ public <T> Future<T> queueOperation(Callable<T> cb) {
+ final FutureTask<T> task = new FutureTask<>(cb);
+ _queueFuture(task);
+ return task;
+ }
+
+ public <T> Future<T> queueOperation(ChainedOperation.Operation<T> op) {
+ final ChainedOperation<T> task = new ChainedOperation<>(op);
+ _queueFuture(task);
+ return task;
+ }
+
+ private void _queueFuture(Future<?> op) {
+ synchronized (_scheduler) {
+ _scheduler.queueFuture(op);
+ wakeup();
+ }
+ }
+
+ private void _waitEvents() {
+ _driver.doWait(0);
+ }
+
+ public void wakeup() {
+ _driver.wakeup();
+ }
+
+ @Override
+ public void serve() {
+ synchronized (_syncRoot) {
+ _isRunning = true;
+ }
+
+ while (_isRunning) {
+ //_waitEvents();
+ synchronized (_scheduler) {
+ _scheduler.performPendingOperations();
+ }
+ _acceptConnectionRequests();
+ _processConnectors();
+ }
+ }
+
+ private void _processConnectors() {
+ for (Connector<?> connector = _driver.connector(); connector != null; connector = _driver
+ .connector()) {
+ if (connector.isClosed()) {
+ connector.destroy();
+ continue;
+ }
+
+ try {
+ connector.process();
+ } catch (IOException e) {
+ continue;
+ }
+
+ final Object ctx = connector.getContext();
+ assert (ctx instanceof ProtonAuthenticator);
+
+ if (ctx instanceof ProtonAuthenticator) {
+ final ProtonAuthenticator auth = (ProtonAuthenticator) ctx;
+ ConnectionState cs = auth.getState();
+ if (cs.equals(ConnectionState.AUTHENTICATING)) {
+ auth.authenticate();
+ cs = auth.getState();
+ }
+
+ if (cs.equals(ConnectionState.CONNECTED)) {
+ if (connector.getConnection() == null) {
+ connector.setConnection(_engineFactory.createConnection());
+ }
+ _processConnector(connector);
+ }
+ }
+
+ try {
+ connector.process();
+ } catch (IOException e) {
+ continue;
+ }
+ }
+ }
+
+ private void _processConnector(Connector<?> connector) {
+ _initConnection(connector);
+ _openPendingSessions(connector);
+ _openLinks(connector);
+ _queueOutgoingDeliveries(connector);
+ _processDeliveries(connector);
+ _cleanDeliveries(connector);
+ _cleanLinks(connector);
+ _cleanSessions(connector);
+ }
+
+ private void _cleanSessions(Connector<?> connector) {
+ final Connection conn = connector.getConnection();
+ final EnumSet<EndpointState> localState = EnumSet
+ .of(EndpointState.ACTIVE);
+ final EnumSet<EndpointState> remoteState = EnumSet
+ .of(EndpointState.CLOSED);
+
+ for (Session ssn = conn.sessionHead(localState, remoteState); ssn != null; ssn
+ .next(localState, remoteState)) {
+
+ ssn.close();
+ }
+ }
+
+ private void _cleanLinks(Connector<?> connector) {
+ final Connection conn = connector.getConnection();
+ final EnumSet<EndpointState> localState = EnumSet
+ .of(EndpointState.ACTIVE);
+ final EnumSet<EndpointState> remoteState = EnumSet
+ .of(EndpointState.CLOSED);
+
+ for (Link link = conn.linkHead(localState, remoteState); link != null; link = link
+ .next(localState, remoteState)) {
+
+ final ProtonClient ssn = _getClient(link.getSession());
+ ssn.removeLink(link);
+ }
+ }
+
+ private void _cleanDeliveries(Connector<?> connector) {
+ final Connection conn = connector.getConnection();
+ final EnumSet<EndpointState> localState = EnumSet
+ .of(EndpointState.ACTIVE);
+ final EnumSet<EndpointState> remoteState = EnumSet
+ .allOf(EndpointState.class);
+ for (Link link = conn.linkHead(localState, remoteState); link != null; link = link
+ .next(localState, remoteState)) {
+
+ if (link instanceof Receiver) {
+ // We settle all incoming deliveries upon receive
+ continue;
+ }
+
+ Delivery d;
+ final Calendar now = Calendar.getInstance();
+ for (Iterator<Delivery> iter = link.unsettled(); iter.hasNext();) {
+ d = iter.next();
+ Object ctx = d.getContext();
+ if (!(ctx instanceof Calendar)) {
+ // Has not been sent yet
+ continue;
+ }
+
+ final Calendar timeout = (Calendar) ctx;
+ boolean remoteClosed = link.getRemoteState().equals(
+ EndpointState.CLOSED);
+ boolean timedOut = now.after(timeout);
+ if (d.remotelySettled() || timedOut || remoteClosed) {
+ d.settle();
+ d.free();
+ }
+ }
+
+ }
+
+ }
+
+ private void _processDeliveries(Connector<?> connector) {
+ final Connection conn = connector.getConnection();
+ for (Delivery delivery = conn.getWorkHead(); delivery != null; delivery = delivery
+ .getWorkNext()) {
+
+ final ProtonClient client = _getClient(delivery.getLink()
+ .getSession());
+ client.processDelivery(delivery);
+ }
+ }
+
+ private void _queueOutgoingDeliveries(Connector<?> connector) {
+ final Connection conn = connector.getConnection();
+
+ final EnumSet<EndpointState> localState = EnumSet
+ .of(EndpointState.ACTIVE);
+ final EnumSet<EndpointState> remoteState = EnumSet
+ .allOf(EndpointState.class);
+
+ for (Session ssn = conn.sessionHead(localState, remoteState); ssn != null; ssn = ssn
+ .next(localState, remoteState)) {
+
+ final ProtonClient client = _getClient(ssn);
+ client.queueDeliveries();
+ }
+ }
+
+ private void _openLinks(Connector<?> connector) {
+ final Connection conn = connector.getConnection();
+ final EnumSet<EndpointState> localState = EnumSet
+ .of(EndpointState.UNINITIALIZED);
+ final EnumSet<EndpointState> remoteState = EnumSet
+ .allOf(EndpointState.class);
+ for (Link link = conn.linkHead(localState, remoteState); link != null; link = link
+ .next(localState, remoteState)) {
+
+ // configure the link
+ link.setSource(link.getRemoteSource());
+ link.setTarget(link.getRemoteTarget());
+
+ final ProtonClient client = _getClient(link.getSession());
+ client.addLink(link);
+ }
+ }
+
+ private ProtonClient _getClient(Session ssn) {
+ return (ProtonClient) ssn.getContext();
+ }
+
+ private void _openPendingSessions(Connector<?> connector) {
+ final Connection conn = connector.getConnection();
+ final EnumSet<EndpointState> localState = EnumSet
+ .of(EndpointState.UNINITIALIZED);
+ final EnumSet<EndpointState> remoteState = EnumSet
+ .allOf(EndpointState.class);
+
+ for (Session ssn = conn.sessionHead(localState, remoteState); ssn != null; ssn = ssn
+ .next(localState, remoteState)) {
+
+ final ProtonClient client = new ProtonClient(this, ssn);
+ ssn.setContext(client);
+ final Object ctx = connector.getContext();
+ assert (ctx instanceof ProtonAuthenticator);
+ ProtonAuthenticator auth = (ProtonAuthenticator) ctx;
+ if (auth.getAuthType() == AuthenticatorType.SERVER) {
+ ssn.open();
+ final ProtonListener l = (ProtonListener) ctx;
+ l.accept(client);
+ } else {
+ ssn.close();
+ }
+ }
+ }
+
+ private void _initConnection(Connector<?> connector) {
+ final Connection conn = connector.getConnection();
+ if (conn.getLocalState().equals(EndpointState.UNINITIALIZED)) {
+ conn.open();
+ }
+ }
+
+ private void _acceptConnectionRequests() {
+ for (final Listener<?> l : _driver.listeners()) {
+
+ @SuppressWarnings("unchecked")
+ final Connector<ProtonAuthenticator> connector = (Connector<ProtonAuthenticator>) l
+ .accept();
+ if (connector == null) {
+ continue;
+ }
+ connector.setContext(new ProtonAuthenticator(connector,
+ AuthenticatorType.SERVER));
+ }
+ }
+
+ public void stop() {
+ synchronized (_syncRoot) {
+ _isRunning = false;
+ }
+
+ wakeup();
+ }
+}
\ No newline at end of file
diff --git a/client/java/vdsm-json-rpc/src/test/java/org/ovirt/vdsm/jsonrpc/AmqpReactorTestHelper.java b/client/java/vdsm-json-rpc/src/test/java/org/ovirt/vdsm/jsonrpc/AmqpReactorTestHelper.java
new file mode 100644
index 0000000..46d9cc3
--- /dev/null
+++ b/client/java/vdsm-json-rpc/src/test/java/org/ovirt/vdsm/jsonrpc/AmqpReactorTestHelper.java
@@ -0,0 +1,18 @@
+package org.ovirt.vdsm.jsonrpc;
+
+import java.io.IOException;
+import org.ovirt.vdsm.reactors.ProtonReactor;
+import org.ovirt.vdsm.reactors.Reactor;
+
+public class AmqpReactorTestHelper implements ReactorTestHelper {
+ @Override
+ public Reactor createReactor() throws IOException {
+ return new ProtonReactor();
+ }
+
+ @Override
+ public String getUriScheme() {
+ return "amqp";
+ }
+
+}
diff --git a/client/java/vdsm-json-rpc/src/test/java/org/ovirt/vdsm/jsonrpc/TestJsonRpcClientAMQP.java b/client/java/vdsm-json-rpc/src/test/java/org/ovirt/vdsm/jsonrpc/TestJsonRpcClientAMQP.java
new file mode 100644
index 0000000..9e0c24c
--- /dev/null
+++ b/client/java/vdsm-json-rpc/src/test/java/org/ovirt/vdsm/jsonrpc/TestJsonRpcClientAMQP.java
@@ -0,0 +1,10 @@
+package org.ovirt.vdsm.jsonrpc;
+
+public class TestJsonRpcClientAMQP extends TestJsonRpcClient {
+
+ @Override
+ protected ReactorTestHelper getHelper() {
+ return new AmqpReactorTestHelper();
+ }
+
+}
--
To view, visit http://gerrit.ovirt.org/15428
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I94c52e118cb63d7df84b89a9b93da7b9e477be91
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Saggi Mizrahi <smizrahi(a)redhat.com>
7 years, 6 months
Change in vdsm[master]: vdsm hostdev: add support for USB devices
by mpoledni@redhat.com
Martin Polednik has uploaded a new change for review.
Change subject: vdsm hostdev: add support for USB devices
......................................................................
vdsm hostdev: add support for USB devices
Libvirt allows passthrough of USB devices (not busses) - this patch
exposes the functionality in vdsm
Change-Id: Iac74e7537d56bcb940ef07a4654d45cbcdbb1fb0
Signed-off-by: Martin Polednik <mpoledni(a)redhat.com>
---
M vdsm/caps.py
M vdsm/rpc/vdsmapi-schema.json
M vdsm/virt/vm.py
3 files changed, 71 insertions(+), 10 deletions(-)
git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/54/29054/1
diff --git a/vdsm/caps.py b/vdsm/caps.py
index d937d4e..8c16af6 100644
--- a/vdsm/caps.py
+++ b/vdsm/caps.py
@@ -530,7 +530,12 @@
# back that we could use to uniquely identify and initiate a device
continue
- if capability in ('pci',):
+ if capability in ('pci', 'usb_device'):
+ # Libvirt only allows to attach USB device with capability 'usb',
+ # but the bus identifies itself as 'usb' while device as
+ # 'usb_device'
+ if dev['capability'] == 'usb_device':
+ dev['capability'] = 'usb'
devices.append(dev)
return devices
diff --git a/vdsm/rpc/vdsmapi-schema.json b/vdsm/rpc/vdsmapi-schema.json
index 921f3d4..ac10144 100644
--- a/vdsm/rpc/vdsmapi-schema.json
+++ b/vdsm/rpc/vdsmapi-schema.json
@@ -3156,33 +3156,55 @@
'specParams': 'VmRngDeviceSpecParams'}}
##
+# @StartupPolicy:
+#
+# Possible policies for startup with device
+#
+# @mandatory: fail if missing for any reason (the default)
+#
+# @requisite: fail if missing on boot up, drop if missing
+# on migrate/restore/revert
+#
+# @optional: drop if missing at any start attempt
+#
+# Since: 4.16.0
+##
+{'enum': 'StartupPolicy', 'data': ['mandatory', 'requisite', 'optional']}
+
+##
# @HostDeviceCapability:
#
# Properties of a host device.
#
# @pci: PCI device
#
+# @usb: USB device
+#
# Since: 4.16.0
##
-{'enum': 'HostDeviceCapability', 'data': ['pci']}
+{'enum': 'HostDeviceCapability', 'data': ['pci', 'usb']}
##
# @HostDeviceSpecParams:
#
# Properties of a host device.
#
-# @bootorder: #optional If specified, this device is part of the boot
-# sequence at the specified position
+# @bootorder: #optional If specified, this device is part of the boot
+# sequence at the specified position (for @pci and @usb)
#
-# @bar: #optional ROM visibility in the guest's memory map (for @pci)
+# @bar: #optional ROM visibility in the guest's
+# memory map (for @pci)
#
-# @file: #optional Binary file to be used as device's ROM (for @pci)
+# @file: #optional Binary file to be used as device's ROM (for @pci)
+#
+# @startupPolicy: #optional Possible boot handling with attached device
+# (for @usb)
#
# Since: 4.16.0
##
{'type': 'HostDeviceSpecParams',
- 'data': {'*bootorder': 'int', '*bar': 'bool',
- '*file': 'str'}}
+ 'data': {'*bootorder': 'int', '*bar': 'bool', '*file': 'str',
+ '*startupPolicy': 'StartupPolicy'}}
##
# @HostDevice:
diff --git a/vdsm/virt/vm.py b/vdsm/virt/vm.py
index 6143cfa..6a6978e 100644
--- a/vdsm/virt/vm.py
+++ b/vdsm/virt/vm.py
@@ -1631,13 +1631,16 @@
self.log.debug('Detaching hostdev %s', self.name)
self._node.dettach()
- def getPciAddr(self):
+ def _parsecaps(self):
capsxml = _domParseStr(self._node.XMLDesc(0)).childNodes[0]
self.log.debug('Got nodeDevice XML:\n%s',
capsxml.toprettyxml(encoding='utf-8'))
- capsxml = capsxml.getElementsByTagName('capability')[0]
+ return capsxml.getElementsByTagName('capability')[0]
+
+ def getPciAddr(self):
+ capsxml = self._parsecaps()
domain = capsxml.getElementsByTagName('domain')[0]. \
firstChild.nodeValue
bus = capsxml.getElementsByTagName('bus')[0].firstChild.nodeValue
@@ -1646,6 +1649,26 @@
self.log.debug('PCI device %s at address {domain: %s bus: %s '
'slot: %s}', self.name, domain, bus, slot)
return {'domain': domain, 'bus': bus, 'slot': slot}
+
+ def getUsbAddr(self):
+ capsxml = self._parsecaps()
+ addr = {}
+
+ addr['bus'] = capsxml.getElementsByTagName('bus')[0].firstChild. \
+ nodeValue
+ addr['device'] = capsxml.getElementsByTagName('device')[0]. \
+ firstChild.nodeValue
+ # TODO: handle nonexistant product_id and vendor_id by not adding them
+ # to addr
+ addr['product_id'] = capsxml.getElementsByTagName('product')[0].\
+ getAttribute('id')
+ addr['vendor_id'] = capsxml.getElementsByTagName('vendor')[0].\
+ getAttribute('id')
+
+ self.log.debug('USB device %s {product: %s, vendor: %s} at address '
+ '{bus: %s device: %s}', self.name, addr['product_id'],
+ addr['vendor_id'], addr['bus'], addr['device'])
+ return addr
def getXML(self):
"""
@@ -1683,6 +1706,17 @@
rom.setAttrs(**romAttrs)
+ elif self.capability == 'usb_device':
+ addr = self.getPciAddr()
+ try:
+ source.appendChildWithArgs('vendor', None, addr['vendor'])
+ source.appendChildWithArgs('vendor', None, addr['product'])
+ except:
+ source.appendChildWithArgs('address', None, **addr)
+
+ if 'startupPolicy' in self.specParams:
+ source.setAttrs(startupPolicy=self.specParams['startupPolicy'])
+
return hostdev
--
To view, visit http://gerrit.ovirt.org/29054
To unsubscribe, visit http://gerrit.ovirt.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Iac74e7537d56bcb940ef07a4654d45cbcdbb1fb0
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Martin Polednik <mpoledni(a)redhat.com>
7 years, 7 months