From: Apollon Oikonomopoulos <[email protected]> This patch introduces core file storage support, consisting of the following:
A configure-time switch for enabling/disabling shared file storage support and controlling the shared file storage location: --with-file-storage-dir=. Shared file storage configuration is then available as _autoconf.ENABLE_SHARED_FILE_STORAGE and _autoconf.SHARED_FILE_STORAGE_DIR and there is a cluster-wide ssconf key named "shared_file_storage_dir" for changing the file location. A new disk template named "sharedfile" (DT_SHARED_FILE), using ganeti.bdev.FileStorage. Auxiliary functions in lib/config.py to handle shared file storage. Signed-off-by: Apollon Oikonomopoulos <[email protected]> --- Makefile.am | 2 + configure.ac | 17 ++++++++++++++++ lib/backend.py | 5 +++- lib/bdev.py | 2 +- lib/bootstrap.py | 1 + lib/cli.py | 10 +++++++++ lib/cmdlib.py | 52 +++++++++++++++++++++++++++++++++++++++++++------- lib/config.py | 8 +++++++ lib/constants.py | 15 ++++++++++--- lib/objects.py | 5 ++- lib/ssconf.py | 10 +++++++++ scripts/gnt-cluster | 1 + 12 files changed, 112 insertions(+), 16 deletions(-) diff --git a/Makefile.am b/Makefile.am index 907a36e..3503320 100644 --- a/Makefile.am +++ b/Makefile.am @@ -526,6 +526,8 @@ lib/_autoconf.py: Makefile stamp-directories echo "XEN_INITRD = '$(XEN_INITRD)'"; \ echo "FILE_STORAGE_DIR = '$(FILE_STORAGE_DIR)'"; \ echo "ENABLE_FILE_STORAGE = $(ENABLE_FILE_STORAGE)"; \ + echo "SHARED_FILE_STORAGE_DIR = '$(SHARED_FILE_STORAGE_DIR)'"; \ + echo "ENABLE_SHARED_FILE_STORAGE = $(ENABLE_SHARED_FILE_STORAGE)"; \ echo "IALLOCATOR_SEARCH_PATH = [$(IALLOCATOR_SEARCH_PATH)]"; \ echo "KVM_PATH = '$(KVM_PATH)'"; \ echo "SOCAT_PATH = '$(SOCAT)'"; \ diff --git a/configure.ac b/configure.ac index a0cecfd..f0e4066 100644 --- a/configure.ac +++ b/configure.ac @@ -115,6 +115,23 @@ AC_ARG_WITH([file-storage-dir], AC_SUBST(FILE_STORAGE_DIR, $file_storage_dir) AC_SUBST(ENABLE_FILE_STORAGE, $enable_file_storage) +# --with-shared-file-storage-dir=... +AC_ARG_WITH([shared-file-storage-dir], + [AS_HELP_STRING([--with-shared-file-storage-dir=PATH], + [directory to store files for shared file-based backend] + [ (default is /srv/ganeti/shared-file-storage)] + )], + [[shared_file_storage_dir="$withval"; + if test "$withval" != no; then + enable_shared_file_storage=True + else + enable_shared_file_storage=False + fi + ]], + [[shared_file_storage_dir="/srv/ganeti/shared-file-storage"; enable_shared_file_storage="True"]]) +AC_SUBST(SHARED_FILE_STORAGE_DIR, $shared_file_storage_dir) +AC_SUBST(ENABLE_SHARED_FILE_STORAGE, $enable_shared_file_storage) + # --with-kvm-path=... AC_ARG_WITH([kvm-path], [AS_HELP_STRING([--with-kvm-path=PATH], diff --git a/lib/backend.py b/lib/backend.py index 2a158d6..0a0d4cc 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -2242,8 +2242,11 @@ def _TransformFileStorageDir(file_storage_dir): cfg = _GetConfig() file_storage_dir = os.path.normpath(file_storage_dir) base_file_storage_dir = cfg.GetFileStorageDir() + base_shared_file_storage_dir = cfg.GetSharedFileStorageDir() if (os.path.commonprefix([file_storage_dir, base_file_storage_dir]) != - base_file_storage_dir): + base_file_storage_dir) and \ + (os.path.commonprefix([file_storage_dir,base_shared_file_storage_dir]) != + base_shared_file_storage_dir): _Fail("File storage directory '%s' is not under base file" " storage directory '%s'", file_storage_dir, base_file_storage_dir) return file_storage_dir diff --git a/lib/bdev.py b/lib/bdev.py index f377f16..3d68aea 100644 --- a/lib/bdev.py +++ b/lib/bdev.py @@ -1943,7 +1943,7 @@ DEV_MAP = { constants.LD_DRBD8: DRBD8, } -if constants.ENABLE_FILE_STORAGE: +if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE: DEV_MAP[constants.LD_FILE] = FileStorage diff --git a/lib/bootstrap.py b/lib/bootstrap.py index 57b29cc..770568c 100644 --- a/lib/bootstrap.py +++ b/lib/bootstrap.py @@ -332,6 +332,7 @@ def InitCluster(cluster_name, mac_prefix, master_netdev=master_netdev, cluster_name=clustername.name, file_storage_dir=file_storage_dir, + shared_file_storage_dir=shared_file_storage_dir, enabled_hypervisors=enabled_hypervisors, beparams={constants.PP_DEFAULT: beparams}, nicparams={constants.PP_DEFAULT: nicparams}, diff --git a/lib/cli.py b/lib/cli.py index ce9f04b..90c64e0 100644 --- a/lib/cli.py +++ b/lib/cli.py @@ -70,6 +70,7 @@ __all__ = [ "FORCE_OPT", "FORCE_VARIANT_OPT", "GLOBAL_FILEDIR_OPT", + "GLOBAL_SHARED_FILEDIR_OPT", "HVLIST_OPT", "HVOPTS_OPT", "HYPERVISOR_OPT", @@ -863,6 +864,15 @@ GLOBAL_FILEDIR_OPT = cli_option("--file-storage-dir", dest="file_storage_dir", metavar="DIR", default=constants.DEFAULT_FILE_STORAGE_DIR) +GLOBAL_SHARED_FILEDIR_OPT = cli_option("--shared-file-storage-dir", + dest="shared_file_storage_dir", + help="Specify the default directory (cluster-" + "wide) for storing the shared file-based" + " disks [%s]" % + constants.DEFAULT_SHARED_FILE_STORAGE_DIR, + metavar="SHAREDDIR", + default=constants.DEFAULT_SHARED_FILE_STORAGE_DIR) + NOMODIFY_ETCHOSTS_OPT = cli_option("--no-etc-hosts", dest="modify_etc_hosts", help="Don't modify /etc/hosts", action="store_false", default=True) diff --git a/lib/cmdlib.py b/lib/cmdlib.py index b1889ab..b466a45 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -601,6 +601,17 @@ def _RequireFileStorage(): errors.ECODE_INVAL) +def _RequireSharedFileStorage(): + """Checks that file storage is enabled. + + @raise errors.OpPrereqError: when file storage is disabled + + """ + if not constants.ENABLE_SHARED_FILE_STORAGE: + raise errors.OpPrereqError("Shared file storage disabled at" + " configure time", errors.ECODE_INVAL) + + def _CheckDiskTemplate(template): """Ensure a given disk template is valid. @@ -611,6 +622,8 @@ def _CheckDiskTemplate(template): raise errors.OpPrereqError(msg, errors.ECODE_INVAL) if template == constants.DT_FILE: _RequireFileStorage() + elif template == constants.DT_SHARED_FILE: + _RequireSharedFileStorage() def _CheckStorageType(storage_type): @@ -931,7 +944,7 @@ def _GetStorageTypeArgs(cfg, storage_type): # Special case for file storage if storage_type == constants.ST_FILE: # storage.FileStorage wants a list of storage directories - return [[cfg.GetFileStorageDir()]] + return [[cfg.GetFileStorageDir(), cfg.GetSharedFileStorageDir()]] return [] @@ -3779,6 +3792,7 @@ class LUQueryClusterInfo(NoHooksLU): "master_netdev": cluster.master_netdev, "volume_group_name": cluster.volume_group_name, "file_storage_dir": cluster.file_storage_dir, + "shared_file_storage_dir": cluster.shared_file_storage_dir, "maintain_node_health": cluster.maintain_node_health, "ctime": cluster.ctime, "mtime": cluster.mtime, @@ -4587,7 +4601,7 @@ class LURenameInstance(LogicalUnit): inst = self.instance old_name = inst.name - if inst.disk_template == constants.DT_FILE: + if inst.disk_template in (constants.DT_FILE, constants.DT_SHARED_FILE): old_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1]) self.cfg.RenameInstance(inst.name, self.op.new_name) @@ -4598,7 +4612,7 @@ class LURenameInstance(LogicalUnit): # re-read the instance from the configuration after rename inst = self.cfg.GetInstanceInfo(self.op.new_name) - if inst.disk_template == constants.DT_FILE: + if inst.disk_template in (constants.DT_FILE, constants.DT_SHARED_FILE): new_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1]) result = self.rpc.call_file_storage_dir_rename(inst.primary_node, old_file_storage_dir, @@ -5934,6 +5948,21 @@ def _GenerateDiskTemplate(lu, template_name, disk_index)), mode=disk["mode"]) disks.append(disk_dev) + elif template_name == constants.DT_SHARED_FILE: + if len(secondary_nodes) != 0: + raise errors.ProgrammerError("Wrong template configuration") + + _RequireSharedFileStorage() + + for idx, disk in enumerate(disk_info): + disk_index = idx + base_index + disk_dev = objects.Disk(dev_type=constants.LD_FILE, size=disk["size"], + iv_name="disk/%d" % disk_index, + logical_id=(file_driver, + "%s/disk%d" % (file_storage_dir, + disk_index)), + mode=disk["mode"]) + disks.append(disk_dev) else: raise errors.ProgrammerError("Invalid disk template '%s'" % template_name) return disks @@ -5971,7 +6000,7 @@ def _CreateDisks(lu, instance, to_skip=None, target_node=None): pnode = target_node all_nodes = [pnode] - if instance.disk_template == constants.DT_FILE: + if instance.disk_template in (constants.DT_FILE, constants.DT_SHARED_FILE): file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1]) result = lu.rpc.call_file_storage_dir_create(pnode, file_storage_dir) @@ -6051,6 +6080,7 @@ def _ComputeDiskSize(disk_template, disks): # 128 MB are added for drbd metadata for each disk constants.DT_DRBD8: sum(d["size"] + 128 for d in disks), constants.DT_FILE: None, + constants.DT_SHARED_FILE: 0, } if disk_template not in req_size_dict: @@ -6798,7 +6828,7 @@ class LUCreateInstance(LogicalUnit): else: network_port = None - if constants.ENABLE_FILE_STORAGE: + if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE: # this is needed because os.path.join does not accept None arguments if self.op.file_storage_dir is None: string_file_storage_dir = "" @@ -6806,7 +6836,12 @@ class LUCreateInstance(LogicalUnit): string_file_storage_dir = self.op.file_storage_dir # build the full file storage dir path - file_storage_dir = utils.PathJoin(self.cfg.GetFileStorageDir(), + if self.op.disk_template == constants.DT_SHARED_FILE: + GetFileStorageDir = self.cfg.GetSharedFileStorageDir + else: + GetFileStorageDir = self.cfg.GetFileStorageDir + + file_storage_dir = utils.PathJoin(GetFileStorageDir(), string_file_storage_dir, instance) else: file_storage_dir = "" @@ -8027,7 +8062,7 @@ class LUGrowDisk(LogicalUnit): self.disk = instance.FindDisk(self.op.disk) - if instance.disk_template != constants.DT_FILE: + if instance.disk_template not in (constants.DT_FILE, constants.DT_SHARED_FILE): # TODO: check the free disk space for file, when that feature will be # supported _CheckNodesFreeDisk(self, nodenames, self.op.amount) @@ -8776,7 +8811,8 @@ class LUSetInstanceParams(LogicalUnit): result.append(("disk/%d" % device_idx, "remove")) elif disk_op == constants.DDM_ADD: # add a new disk - if instance.disk_template == constants.DT_FILE: + if instance.disk_template in (constants.DT_FILE, + constants.DT_SHARED_FILE): file_driver, file_path = instance.disks[0].logical_id file_path = os.path.dirname(file_path) else: diff --git a/lib/config.py b/lib/config.py index aa479c0..15a2acd 100644 --- a/lib/config.py +++ b/lib/config.py @@ -800,6 +800,13 @@ class ConfigWriter: return self._config_data.cluster.file_storage_dir @locking.ssynchronized(_config_lock, shared=1) + def GetSharedFileStorageDir(self): + """Get the shared file storage dir for this cluster. + + """ + return self._config_data.cluster.shared_file_storage_dir + + @locking.ssynchronized(_config_lock, shared=1) def GetHypervisorType(self): """Get the hypervisor type for this cluster. @@ -1384,6 +1391,7 @@ class ConfigWriter: constants.SS_CLUSTER_NAME: cluster.cluster_name, constants.SS_CLUSTER_TAGS: cluster_tags, constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir, + constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir, constants.SS_MASTER_CANDIDATES: mc_data, constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data, constants.SS_MASTER_IP: cluster.master_ip, diff --git a/lib/constants.py b/lib/constants.py index f2e978f..0a92cb5 100644 --- a/lib/constants.py +++ b/lib/constants.py @@ -123,7 +123,9 @@ QUEUE_DIR = DATA_DIR + "/queue" DAEMON_UTIL = _autoconf.PKGLIBDIR + "/daemon-util" ETC_HOSTS = "/etc/hosts" DEFAULT_FILE_STORAGE_DIR = _autoconf.FILE_STORAGE_DIR +DEFAULT_SHARED_FILE_STORAGE_DIR = _autoconf.SHARED_FILE_STORAGE_DIR ENABLE_FILE_STORAGE = _autoconf.ENABLE_FILE_STORAGE +ENABLE_SHARED_FILE_STORAGE = _autoconf.ENABLE_SHARED_FILE_STORAGE SYSCONFDIR = _autoconf.SYSCONFDIR TOOLSDIR = _autoconf.TOOLSDIR CONF_DIR = SYSCONFDIR + "/ganeti" @@ -314,15 +316,19 @@ DT_DISKLESS = "diskless" DT_PLAIN = "plain" DT_DRBD8 = "drbd" DT_FILE = "file" +DT_SHARED_FILE = "sharedfile" # the set of network-mirrored disk templates DTS_NET_MIRROR = frozenset([DT_DRBD8]) +# the set of externally mirrored disk templates +DTS_EXT_MIRROR = frozenset([DT_SHARED_FILE]) + # the set of non-lvm-based disk templates -DTS_NOT_LVM = frozenset([DT_DISKLESS, DT_FILE]) +DTS_NOT_LVM = frozenset([DT_DISKLESS, DT_FILE, DT_SHARED_FILE]) # the set of disk templates which can be grown -DTS_GROWABLE = frozenset([DT_PLAIN, DT_DRBD8, DT_FILE]) +DTS_GROWABLE = frozenset([DT_PLAIN, DT_DRBD8, DT_FILE, DT_SHARED_FILE]) # logical disk types LD_LV = "lvm" @@ -385,8 +391,8 @@ RIE_CERT_VALIDITY = 24 * 60 * 60 # Remote import/export connect timeout for socat RIE_CONNECT_TIMEOUT = 60 -DISK_TEMPLATES = frozenset([DT_DISKLESS, DT_PLAIN, - DT_DRBD8, DT_FILE]) +DISK_TEMPLATES = frozenset([DT_DISKLESS, DT_PLAIN, DT_DRBD8, + DT_FILE, DT_SHARED_FILE]) FILE_DRIVER = frozenset([FD_LOOP, FD_BLKTAP]) @@ -766,6 +772,7 @@ MAX_DISKS = 16 SS_CLUSTER_NAME = "cluster_name" SS_CLUSTER_TAGS = "cluster_tags" SS_FILE_STORAGE_DIR = "file_storage_dir" +SS_SHARED_FILE_STORAGE_DIR = "shared_file_storage_dir" SS_MASTER_CANDIDATES = "master_candidates" SS_MASTER_CANDIDATES_IPS = "master_candidates_ips" SS_MASTER_IP = "master_ip" diff --git a/lib/objects.py b/lib/objects.py index e640e30..0e1fbfe 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -445,7 +445,7 @@ class Disk(ConfigObject): devices needs to (or can) be assembled. """ - if self.dev_type in [constants.LD_LV, constants.LD_FILE]: + if self.dev_type in [constants.LD_LV, constants.LD_FILE] result = [node] elif self.dev_type in constants.LDS_DRBD: result = [self.logical_id[0], self.logical_id[1]] @@ -498,7 +498,7 @@ class Disk(ConfigObject): actual algorithms from bdev. """ - if self.dev_type == constants.LD_LV or self.dev_type == constants.LD_FILE: + if self.dev_type in (constants.LD_LV, constants.LD_FILE): self.size += amount elif self.dev_type == constants.LD_DRBD8: if self.children: @@ -857,6 +857,7 @@ class Cluster(TaggableObject): "master_netdev", "cluster_name", "file_storage_dir", + "shared_file_storage_dir", "enabled_hypervisors", "hvparams", "os_hvp", diff --git a/lib/ssconf.py b/lib/ssconf.py index 035a887..6ea01c9 100644 --- a/lib/ssconf.py +++ b/lib/ssconf.py @@ -153,6 +153,9 @@ class SimpleConfigReader(object): def GetFileStorageDir(self): return self._config_data["cluster"]["file_storage_dir"] + def GetSharedFileStorageDir(self): + return self._config_data["cluster"]["shared_file_storage_dir"] + def GetNodeList(self): return self._config_data["nodes"].keys() @@ -270,6 +273,7 @@ class SimpleStore(object): constants.SS_CLUSTER_NAME, constants.SS_CLUSTER_TAGS, constants.SS_FILE_STORAGE_DIR, + constants.SS_SHARED_FILE_STORAGE_DIR, constants.SS_MASTER_CANDIDATES, constants.SS_MASTER_CANDIDATES_IPS, constants.SS_MASTER_IP, @@ -363,6 +367,12 @@ class SimpleStore(object): """ return self._ReadFile(constants.SS_FILE_STORAGE_DIR) + def GetSharedFileStorageDir(self): + """Get the shared file storage dir. + + """ + return self._ReadFile(constants.SS_SHARED_FILE_STORAGE_DIR) + def GetMasterCandidates(self): """Return the list of master candidates. diff --git a/scripts/gnt-cluster b/scripts/gnt-cluster index bf160cd..fbe8e75 100755 --- a/scripts/gnt-cluster +++ b/scripts/gnt-cluster @@ -284,6 +284,7 @@ def ShowClusterConfig(opts, args): ToStdout(" - master netdev: %s", result["master_netdev"]) ToStdout(" - lvm volume group: %s", result["volume_group_name"]) ToStdout(" - file storage path: %s", result["file_storage_dir"]) + ToStdout(" - shared file storage path: %s", result["shared_file_storage_dir"]) ToStdout(" - maintenance of node health: %s", result["maintain_node_health"]) ToStdout(" - uid pool: %s", -- 1.7.1
