From 845b7ed197eedf6e6e69362fe9e01c076d2df3ce Mon Sep 17 00:00:00 2001 From: Santi Raffa Date: Tue, 17 Dec 2013 10:28:39 +0100 Subject: [PATCH] Gluster: mount automatically Add parameters to the Gluster disk template so Gluster can manage the mount point point autonomously. Signed-off-by: Santi Raffa Signed-off-by: Thomas Thrainer Reviewed-by: Thomas Thrainer --- lib/cmdlib/instance.py | 3 +- lib/cmdlib/instance_storage.py | 7 +++- lib/cmdlib/instance_utils.py | 2 +- lib/storage/gluster.py | 61 +++++++++++++++++++++++------ src/Ganeti/Constants.hs | 46 +++++++++++++++++++--- test/py/cmdlib/instance_unittest.py | 18 ++++++--- 6 files changed, 110 insertions(+), 27 deletions(-) diff --git a/lib/cmdlib/instance.py b/lib/cmdlib/instance.py index 11a2b03ac..9665b6f25 100644 --- a/lib/cmdlib/instance.py +++ b/lib/cmdlib/instance.py @@ -1557,7 +1557,8 @@ class LUInstanceRename(LogicalUnit): old_name = self.instance.name rename_file_storage = False - if (self.instance.disk_template in constants.DTS_FILEBASED and + if (self.instance.disk_template in (constants.DT_FILE, + constants.DT_SHARED_FILE) and self.op.new_name != self.instance.name): old_file_storage_dir = os.path.dirname( self.instance.disks[0].logical_id[1]) diff --git a/lib/cmdlib/instance_storage.py b/lib/cmdlib/instance_storage.py index 641b25383..d891925c1 100644 --- a/lib/cmdlib/instance_storage.py +++ b/lib/cmdlib/instance_storage.py @@ -460,7 +460,12 @@ def GenerateDiskTemplate( vg = disk.get(constants.IDISK_VG, vgname) return (vg, names[idx]) - elif template_name in constants.DTS_FILEBASED: + elif template_name == constants.DT_GLUSTER: + logical_id_fn = lambda _1, disk_index, _2: \ + (file_driver, "ganeti/%s.%d" % (instance_uuid, + disk_index)) + + elif template_name in constants.DTS_FILEBASED: # Gluster handled above logical_id_fn = \ lambda _, disk_index, disk: (file_driver, "%s/disk%d" % (file_storage_dir, diff --git a/lib/cmdlib/instance_utils.py b/lib/cmdlib/instance_utils.py index 752e66d6c..9e10fd1b0 100644 --- a/lib/cmdlib/instance_utils.py +++ b/lib/cmdlib/instance_utils.py @@ -292,7 +292,7 @@ def RemoveDisks(lu, instance, target_node_uuid=None, ignore_failures=False): CheckDiskTemplateEnabled(lu.cfg.GetClusterInfo(), instance.disk_template) - if instance.disk_template in constants.DTS_FILEBASED: + if instance.disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]: file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1]) if target_node_uuid: tgt = target_node_uuid diff --git a/lib/storage/gluster.py b/lib/storage/gluster.py index a2d2446f3..2b1c032c3 100644 --- a/lib/storage/gluster.py +++ b/lib/storage/gluster.py @@ -33,6 +33,7 @@ from ganeti import utils from ganeti import errors from ganeti import netutils from ganeti import constants +from ganeti import ssconf from ganeti.utils import io from ganeti.storage import base @@ -277,9 +278,8 @@ class GlusterVolume(object): class GlusterStorage(base.BlockDev): """File device using the Gluster backend. - This class represents a file storage backend device stored on Gluster. The - system administrator must mount the Gluster device himself at boot time before - Ganeti is run. + This class represents a file storage backend device stored on Gluster. Ganeti + mounts and unmounts the Gluster devices automatically. The unique_id for the file device is a (file_driver, file_path) tuple. @@ -290,14 +290,24 @@ class GlusterStorage(base.BlockDev): """ if children: base.ThrowError("Invalid setup for file device") - super(GlusterStorage, self).__init__(unique_id, children, size, params, - dyn_params) - if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2: - raise ValueError("Invalid configuration data %s" % str(unique_id)) - self.driver = unique_id[0] - self.dev_path = unique_id[1] - self.file = FileDeviceHelper(self.dev_path) + try: + driver, path = unique_id + except ValueError: # wrong number of arguments + raise ValueError("Invalid configuration data %s" % repr(unique_id)) + + server_addr = params[constants.GLUSTER_HOST] + port = params[constants.GLUSTER_PORT] + volume = params[constants.GLUSTER_VOLUME] + + self.volume = GlusterVolume(server_addr, port, volume) + self.path = path + self.driver = driver + self.full_path = io.PathJoin(self.volume.mount_point, self.path) + self.file = None + + super(GlusterStorage, self).__init__(unique_id, children, size, + params, dyn_params) self.Attach() @@ -341,7 +351,13 @@ class GlusterStorage(base.BlockDev): @return: True if the removal was successful """ - return self.file.Remove() + with self.volume.Mount(): + self.file = FileDeviceHelper(self.full_path) + if self.file.Remove(): + self.file = None + return True + else: + return False def Rename(self, new_id): """Renames the file. @@ -367,6 +383,14 @@ class GlusterStorage(base.BlockDev): @return: True if file exists """ + try: + self.volume.Mount() + self.file = FileDeviceHelper(self.full_path) + self.dev_path = self.full_path + except Exception as err: + self.volume.Unmount() + raise err + self.attached = self.file.Exists() return self.attached @@ -395,7 +419,18 @@ class GlusterStorage(base.BlockDev): if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2: raise ValueError("Invalid configuration data %s" % str(unique_id)) - dev_path = unique_id[1] + full_path = unique_id[1] + + server_addr = params[constants.GLUSTER_HOST] + port = params[constants.GLUSTER_PORT] + volume = params[constants.GLUSTER_VOLUME] + + volume_obj = GlusterVolume(server_addr, port, volume) + full_path = io.PathJoin(volume_obj.mount_point, full_path) + + # Possible optimization: defer actual creation to first Attach, rather + # than mounting and unmounting here, then remounting immediately after. + with volume_obj.Mount(): + FileDeviceHelper.CreateFile(full_path, size, create_folders=True) - FileDeviceHelper.Create(dev_path, size) return GlusterStorage(unique_id, children, size, params, dyn_params) diff --git a/src/Ganeti/Constants.hs b/src/Ganeti/Constants.hs index 427e5dba6..654429e48 100644 --- a/src/Ganeti/Constants.hs +++ b/src/Ganeti/Constants.hs @@ -2167,7 +2167,11 @@ diskDtTypes = (drbdMinRate, VTypeInt), (lvStripes, VTypeInt), (rbdAccess, VTypeString), - (rbdPool, VTypeString)] + (rbdPool, VTypeString), + (glusterHost, VTypeString), + (glusterVolume, VTypeString), + (glusterPort, VTypeInt) + ] diskDtParameters :: FrozenSet String diskDtParameters = ConstantUtils.mkSet (Map.keys diskDtTypes) @@ -3773,7 +3777,12 @@ diskLdDefaults = , (ldpAccess, PyValueEx diskKernelspace) ]) , (DTSharedFile, Map.empty) - , (DTGluster, Map.empty) + , (DTGluster, Map.fromList + [ (rbdAccess, PyValueEx diskKernelspace) + , (glusterHost, PyValueEx glusterHostDefault) + , (glusterVolume, PyValueEx glusterVolumeDefault) + , (glusterPort, PyValueEx glusterPortDefault) + ]) ] diskDtDefaults :: Map DiskTemplate (Map String PyValueEx) @@ -3806,7 +3815,12 @@ diskDtDefaults = , (rbdAccess, PyValueEx diskKernelspace) ]) , (DTSharedFile, Map.empty) - , (DTGluster, Map.empty) + , (DTGluster, Map.fromList + [ (rbdAccess, PyValueEx diskKernelspace) + , (glusterHost, PyValueEx glusterHostDefault) + , (glusterVolume, PyValueEx glusterVolumeDefault) + , (glusterPort, PyValueEx glusterPortDefault) + ]) ] niccDefaults :: Map String PyValueEx @@ -4592,6 +4606,26 @@ jstoreJobsPerArchiveDirectory = 10000 -- * Gluster settings --- | Where Ganeti should manage Gluster volume mountpoints -glusterMountpoint :: String -glusterMountpoint = "/var/run/ganeti/gluster" +-- | Name of the Gluster host setting +glusterHost :: String +glusterHost = "host" + +-- | Default value of the Gluster host setting +glusterHostDefault :: String +glusterHostDefault = "127.0.0.1" + +-- | Name of the Gluster volume setting +glusterVolume :: String +glusterVolume = "volume" + +-- | Default value of the Gluster volume setting +glusterVolumeDefault :: String +glusterVolumeDefault = "gv0" + +-- | Name of the Gluster port setting +glusterPort :: String +glusterPort = "port" + +-- | Default value of the Gluster port setting +glusterPortDefault :: Int +glusterPortDefault = 24007 diff --git a/test/py/cmdlib/instance_unittest.py b/test/py/cmdlib/instance_unittest.py index 448d7c97d..49a054e8a 100644 --- a/test/py/cmdlib/instance_unittest.py +++ b/test/py/cmdlib/instance_unittest.py @@ -1106,11 +1106,19 @@ class TestGenerateDiskTemplate(CmdlibTestCase): disk_template, disk_info, 2, disk_template, file_storage_dir="/tmp", file_driver=constants.FD_BLKTAP) - self.assertEqual(map(operator.attrgetter("logical_id"), result), [ - (constants.FD_BLKTAP, "/tmp/disk2"), - (constants.FD_BLKTAP, "/tmp/disk3"), - (constants.FD_BLKTAP, "/tmp/disk4"), - ]) + if disk_template == constants.DT_GLUSTER: + # Here "inst21662.example.com" is actually the instance UUID, not its + # name, so while this result looks wrong, it is actually correct. + expected = [(constants.FD_BLKTAP, + 'ganeti/inst21662.example.com.%d' % x) + for x in (2,3,4)] + else: + expected = [(constants.FD_BLKTAP, + '/tmp/disk%d' % x) + for x in (2,3,4)] + + self.assertEqual(map(operator.attrgetter("logical_id"), result), + expected) def testBlock(self): disk_info = [{ -- GitLab