Change in vdsm[master]: gluster: Allow gluster mount with additional servers

ahino at redhat.com ahino at redhat.com
Thu May 7 14:29:20 UTC 2015


Ala Hino has uploaded a new change for review.

Change subject: gluster: Allow gluster mount with additional servers
......................................................................

gluster: Allow gluster mount with additional servers

Currently, engine supports mounting single gluster server.  With this change,
mount will include the other two gluster servers defined in the replica.  To do
so, vdsm uses gluster get info api in order to get the IPs of the other two
servers and then, builds mount command using gluster 'backup-volfile-servers'
option.

Change-Id: I2478a5edc1fc9d24eb96d64a32a98a2467ce2989
Bug-Url: https://bugzilla.redhat.com/1177777
Signed-off-by: Ala Hino <ahino at redhat.com>
---
A tests/glusterStorageServerTests.py
M vdsm/storage/storageServer.py
M vdsm/storage/storage_exception.py
M vdsm/supervdsmServer
4 files changed, 146 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/65/40665/1

diff --git a/tests/glusterStorageServerTests.py b/tests/glusterStorageServerTests.py
new file mode 100644
index 0000000..8af184e
--- /dev/null
+++ b/tests/glusterStorageServerTests.py
@@ -0,0 +1,35 @@
+
+from testlib import VdsmTestCase
+from storage.storageServer import GlusterFSConnection
+
+class GlusterFSConnectionTests(VdsmTestCase):
+
+    def testParsingGlusterPath(self):
+        gluster = GlusterFSConnection("10.20.30.40:/my_lovely_vol", "")
+        remotePath = gluster._getGlusterServerAndVolume()
+        self.assertEquals(remotePath[0], "10.20.30.40")
+        self.assertEquals(remotePath[1], "my_lovely_vol")
+
+    def testPreparingGlusterVolumeInfoCmd(self):
+        gluster = GlusterFSConnection("10.20.30.40:/my_lovely_vol", "")
+        command = gluster._getGlusterVolCmd("10.20.30.40", "my_lovely_vol", "--xml")
+        self.assertEquals(command,
+                     ['/usr/sbin/gluster',
+                      '--mode=script',
+                      'volume',
+                      'info',
+                      'my_lovely_vol',
+                      '--remote-host=10.20.30.40',
+                      '--xml'
+                      ]
+                     )
+
+    def testParsingGlusterVolumeInfoXml(self):
+        xml = r'<?xml version="1.0" encoding="UTF-8" standalone="yes"?><cliOutput><opRet>0</opRet><opErrno>0</opErrno><opErrstr/><volInfo><volumes><volume><name>ala_volume</name><id>7644c01a-72a7-420d-9a08a66002f5c9fc</id><status>1</status><statusStr>Started</statusStr><brickCount>3</brickCount><distCount>1</distCount><stripeCount>1</stripeCount><replicaCount>1</replicaCount><disperseCount>0</disperseCount><redundancyCount>0</redundancyCount><type>0</type><typeStr>Distribute</typeStr><transport>0</transport> <xlators/><bricks><brick uuid="dcf2d5d9-fee8-4dd2-aaf1-ca2021b596c4">10.35.160.202:/home/ala_volume<name>10.35.160.202:/home/ala_volume</name><hostUuid>dcf2d5d9-fee8-4dd2-aaf1-ca2021b596c4</hostUuid></brick><brick uuid="3f208209-dec5-49e2-a3e1-1028dea32cf6">10.35.160.6:/home/ala_volume<name>10.35.160.6:/home/ala_volume</name><hostUuid>3f208209-dec5-49e2-a3e1-1028dea32cf6</hostUuid></brick><brick uuid="b9d8d956-e1ab-4a0a-8ea5-989c4957a9f1">10.35.160.203:/home/ala_volume<!
 name>10.35.160.203:/home/ala_volume</name><hostUuid>b9d8d956-e1ab-4a0a-8ea5-989c4957a9f1</hostUuid></brick></bricks><optCount>0</optCount><options/></volume><count>1</count></volumes></volInfo></cliOutput>'
+
+        gluster = GlusterFSConnection("10.20.30.40:/my_lovely_vol", "")
+        servers = gluster._getGlusterBackupServersFromVolInfoXml(xml, "10.35.160.202")
+        self.assertEquals(servers, ["10.35.160.6", "10.35.160.203"])
+
+        backupOpts = gluster._getGlusterBackupServersOption(servers)
+        self.assertEquals(backupOpts, "backup-volfile-servers=10.35.160.6:10.35.160.203")
diff --git a/vdsm/storage/storageServer.py b/vdsm/storage/storageServer.py
index 73ebcfe..14b2039 100644
--- a/vdsm/storage/storageServer.py
+++ b/vdsm/storage/storageServer.py
@@ -29,10 +29,13 @@
 from functools import partial
 import six
 import sys
+import xml.etree.ElementTree as ET
 
 from vdsm.compat import pickle
 from vdsm.config import config
 from vdsm import udevadm
+from vdsm import utils
+import supervdsm
 
 import mount
 import fileUtils
@@ -41,6 +44,15 @@
 from sync import asyncmethod, AsyncCallStub
 from mount import MountError
 import storage_exception as se
+
+
+_glusterCommandPath = utils.CommandPath("gluster", "/usr/sbin/gluster")
+
+
+if hasattr(ET, 'ParseError'):
+    _etreeExceptions = (ET.ParseError, AttributeError, ValueError)
+else:
+    _etreeExceptions = (SyntaxError, AttributeError, ValueError)
 
 
 class AliasAlreadyRegisteredError(RuntimeError):
@@ -260,6 +272,7 @@
 
 
 class GlusterFSConnection(object):
+    log = logging.getLogger("Storage.StorageServer.GlusterFSConnection")
 
     def __init__(self, spec, vfsType="glusterfs", options=""):
         self._vfsType = vfsType
@@ -267,10 +280,24 @@
         self._options = options
 
     def connect(self):
+        remotePath = self._getGlusterServerAndVolume()
+        primaryServer = remotePath[0]
+        volume = remotePath[1]
+        self.log.debug("Gluster primary sever: %s Volume: %s",
+                       primaryServer,
+                       volume)
+        xml = supervdsm.getProxy().getGlusterVolumeInfo(primaryServer, volume,
+                                                        "--xml")
+        self.log.debug("Volume info xml: %s", xml)
+        servers = self._getGlusterBackupServersFromVolInfoXml(xml,
+                                                              primaryServer)
+        self.log.debug("Gluster backup servers: %s", servers)
+        optionsString = self._getGlusterBackupServersOption(servers)
+        self.log.debug("Gluster options: %s", optionsString)
         localPath = self._getLocalPath()
         mountCon = MountConnection(self._remotePath,
                                    "glusterfs",
-                                   self._options,
+                                   optionsString,
                                    localPath
                                    )
         return mountCon.connect()
@@ -282,6 +309,7 @@
                                    self._options,
                                    localPath
                                    )
+
         return mountCon.isConnected()
 
     def disconnect(self):
@@ -299,6 +327,67 @@
                             "glusterSD"
                             )
 
+    def _getGlusterServerAndVolume(self):
+        remotePath = self._remotePath.split(":/")
+        return (remotePath[0], remotePath[1])
+
+    def _getGlusterBackupServersFromVolInfoXml(self, xml, primaryServer):
+        servers = []
+        try:
+            root = ET.fromstring('\n'.join(xml))
+        except _etreeExceptions:
+            root = ET.fromstring(xml)
+
+        for brick in root.iter('brick'):
+            servers.append(brick.text.split(":/")[0])
+        servers.remove(primaryServer)
+
+        return servers
+
+    def _getGlusterBackupServersOption(self, servers):
+        optionsString = "backup-volfile-servers=%s:%s" % (servers[0],
+                                                          servers[1])
+
+        return optionsString
+
+    def _getGlusterVolCmd(self, server, volume, options=None):
+        command = [_glusterCommandPath.cmd, "--mode=script", "volume"]
+        command += ["info"]
+
+        if volume:
+            command.append(volume)
+
+        if server:
+            command += ['--remote-host=%s' % server]
+
+        if options:
+            command.append(options)
+
+        self.log.debug("Gluster volume info command: %s", command)
+        return command
+
+    def _execGlusterXml(self, cmd):
+        rc, out, err = utils.execCmd(cmd)
+        if rc != 0:
+            raise se.GlusterCmdExecFailedException(rc, out, err)
+        try:
+            tree = ET.fromstring('\n'.join(out))
+            rv = int(tree.find('opRet').text)
+            msg = tree.find('opErrstr').text
+            errNo = int(tree.find('opErrno').text)
+        except _etreeExceptions:
+            raise se.GlusterXmlErrorException(err=out)
+        if rv == 0:
+            return out
+        else:
+            if errNo != 0:
+                rv = errNo
+            raise se.GlusterCmdFailedException(rc=rv, err=[msg])
+
+    def _getGlusterVolumeInfo(self, server, volume, options=None):
+        command = self._getGlusterVolCmd(server, volume, options)
+        return self._execGlusterXml(command)
+
     def __eq__(self, other):
         if not isinstance(other, GlusterFSConnection):
             return False
diff --git a/vdsm/storage/storage_exception.py b/vdsm/storage/storage_exception.py
index 1cfc8e4..74e9e7b 100644
--- a/vdsm/storage/storage_exception.py
+++ b/vdsm/storage/storage_exception.py
@@ -1514,6 +1514,21 @@
                  issue and how to resolve it"""
 
 
+class GlusterCmdExecFailedException(StorageException):
+    code = 615
+    message = "Command execution failed"
+
+
+class GlusterXmlErrorException(StorageException):
+    code = 616
+    message = "XML error"
+
+
+class GlusterCmdFailedException(StorageException):
+    code = 617
+    message = "Command failed"
+
+
 #################################################
 #  SPM/HSM Exceptions
 #################################################
diff --git a/vdsm/supervdsmServer b/vdsm/supervdsmServer
index ef7a710..d833032 100755
--- a/vdsm/supervdsmServer
+++ b/vdsm/supervdsmServer
@@ -32,6 +32,7 @@
 import logging
 import logging.config
 from vdsm.infra import sigutils
+from storage.storageServer import GlusterFSConnection
 
 import numaUtils
 
@@ -415,6 +416,11 @@
         return hba._rescan()
 
     @logDecorator
+    def getGlusterVolumeInfo(self, server, volume, options=None):
+        gluster = GlusterFSConnection("", "")
+        return gluster._getGlusterVolumeInfo(server, volume, options)
+
+    @logDecorator
     def set_rp_filter_loose(self, dev):
         sysctl.set_rp_filter_loose(dev)
 


-- 
To view, visit https://gerrit.ovirt.org/40665
To unsubscribe, visit https://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I2478a5edc1fc9d24eb96d64a32a98a2467ce2989
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Ala Hino <ahino at redhat.com>


More information about the vdsm-patches mailing list