Commit 4b97f902 authored by Apollon Oikonomopoulos's avatar Apollon Oikonomopoulos Committed by Iustin Pop

Core shared file storage support

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-shared-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: default avatarApollon Oikonomopoulos <apollon@noc.grnet.gr>
[iustin@google.com: small style fixes]
Signed-off-by: default avatarIustin Pop <iustin@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent a997cec5
......@@ -712,6 +712,8 @@ lib/_autoconf.py: Makefile vcs-version | lib/.dir
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)'"; \
......
......@@ -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],
......
......@@ -2404,15 +2404,15 @@ def BlockdevRename(devlist):
_Fail("; ".join(msgs))
def _TransformFileStorageDir(file_storage_dir):
def _TransformFileStorageDir(fs_dir):
"""Checks whether given file_storage_dir is valid.
Checks wheter the given file_storage_dir is within the cluster-wide
default file_storage_dir stored in SimpleStore. Only paths under that
directory are allowed.
Checks wheter the given fs_dir is within the cluster-wide default
file_storage_dir or the shared_file_storage_dir, which are stored in
SimpleStore. Only paths under those directories are allowed.
@type file_storage_dir: str
@param file_storage_dir: the path to check
@type fs_dir: str
@param fs_dir: the path to check
@return: the normalized path if valid, None otherwise
......@@ -2420,13 +2420,15 @@ def _TransformFileStorageDir(file_storage_dir):
if not constants.ENABLE_FILE_STORAGE:
_Fail("File storage disabled at configure time")
cfg = _GetConfig()
file_storage_dir = os.path.normpath(file_storage_dir)
base_file_storage_dir = cfg.GetFileStorageDir()
if (os.path.commonprefix([file_storage_dir, base_file_storage_dir]) !=
base_file_storage_dir):
fs_dir = os.path.normpath(fs_dir)
base_fstore = cfg.GetFileStorageDir()
base_shared = cfg.GetSharedFileStorageDir()
if ((os.path.commonprefix([fs_dir, base_fstore]) != base_fstore) and
(os.path.commonprefix([fs_dir, base_shared]) != base_shared)):
_Fail("File storage directory '%s' is not under base file"
" storage directory '%s'", file_storage_dir, base_file_storage_dir)
return file_storage_dir
" storage directory '%s' or shared storage directory '%s'",
fs_dir, base_fstore, base_shared)
return fs_dir
def CreateFileStorageDir(file_storage_dir):
......
#
#
# Copyright (C) 2006, 2007, 2010 Google Inc.
# Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -2074,7 +2074,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
......
......@@ -410,6 +410,7 @@ def InitCluster(cluster_name, mac_prefix, # pylint: disable-msg=R0913
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},
......
......@@ -79,6 +79,7 @@ __all__ = [
"FORCE_VARIANT_OPT",
"GLOBAL_FILEDIR_OPT",
"HID_OS_OPT",
"GLOBAL_SHARED_FILEDIR_OPT",
"HVLIST_OPT",
"HVOPTS_OPT",
"HYPERVISOR_OPT",
......@@ -972,6 +973,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)
......
......@@ -352,6 +352,8 @@ def ShowClusterConfig(opts, args):
ToStdout(" - lvm reserved volumes: %s", reserved_lvs)
ToStdout(" - drbd usermode helper: %s", result["drbd_usermode_helper"])
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",
......
......@@ -1036,7 +1036,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 []
......@@ -4696,6 +4696,7 @@ class LUClusterQuery(NoHooksLU):
"volume_group_name": cluster.volume_group_name,
"drbd_usermode_helper": cluster.drbd_usermode_helper,
"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,
......@@ -5542,7 +5543,7 @@ class LUInstanceRename(LogicalUnit):
old_name = inst.name
rename_file_storage = False
if (inst.disk_template == constants.DT_FILE and
if (inst.disk_template in (constants.DT_FILE, constants.DT_SHARED_FILE) and
self.op.new_name != inst.name):
old_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1])
rename_file_storage = True
......@@ -6626,6 +6627,21 @@ def _GenerateDiskTemplate(lu, template_name,
opcodes.RequireFileStorage()
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)
elif template_name == constants.DT_SHARED_FILE:
if len(secondary_nodes) != 0:
raise errors.ProgrammerError("Wrong template configuration")
opcodes.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"],
......@@ -6744,7 +6760,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)
......@@ -6834,6 +6850,7 @@ def _ComputeDiskSizePerVG(disk_template, disks):
# 128 MB are added for drbd metadata for each disk
constants.DT_DRBD8: _compute(disks, 128),
constants.DT_FILE: {},
constants.DT_SHARED_FILE: {},
}
if disk_template not in req_size_dict:
......@@ -6854,6 +6871,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:
......@@ -7657,7 +7675,7 @@ class LUInstanceCreate(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 = ""
......@@ -7665,7 +7683,12 @@ class LUInstanceCreate(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:
get_fsd_fn = self.cfg.GetSharedFileStorageDir
else:
get_fsd_fn = self.cfg.GetFileStorageDir
file_storage_dir = utils.PathJoin(get_fsd_fn(),
string_file_storage_dir, instance)
else:
file_storage_dir = ""
......@@ -8811,9 +8834,10 @@ class LUInstanceGrowDisk(LogicalUnit):
self.disk = instance.FindDisk(self.op.disk)
if instance.disk_template != constants.DT_FILE:
# TODO: check the free disk space for file, when that feature
# will be supported
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
_CheckNodesFreeDiskPerVG(self, nodenames,
self.disk.ComputeGrowth(self.op.amount))
......@@ -9554,7 +9578,8 @@ class LUInstanceSetParams(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:
......
......@@ -877,6 +877,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.
......@@ -1738,6 +1745,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,
......
......@@ -144,7 +144,9 @@ SETUP_SSH = _autoconf.TOOLSDIR + "/setup-ssh"
KVM_IFUP = _autoconf.PKGLIBDIR + "/kvm-ifup"
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"
......@@ -360,15 +362,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])
# the set of disk templates that allow adoption
DTS_MAY_ADOPT = frozenset([DT_PLAIN])
......@@ -449,8 +455,8 @@ RIE_CONNECT_RETRIES = 10
#: Give child process up to 5 seconds to exit after sending a signal
CHILD_LINGER_TIMEOUT = 5.0
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])
......@@ -1077,6 +1083,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"
......
......@@ -558,7 +558,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:
......@@ -1066,6 +1066,7 @@ class Cluster(TaggableObject):
"master_netdev",
"cluster_name",
"file_storage_dir",
"shared_file_storage_dir",
"enabled_hypervisors",
"hvparams",
"os_hvp",
......
......@@ -162,6 +162,20 @@ def RequireFileStorage():
errors.ECODE_INVAL)
def RequireSharedFileStorage():
"""Checks that shared file storage is enabled.
While it doesn't really fit into this module, L{utils} was deemed too large
of a dependency to be imported for just one or two functions.
@raise errors.OpPrereqError: when shared file storage is disabled
"""
if not constants.ENABLE_SHARED_FILE_STORAGE:
raise errors.OpPrereqError("Shared file storage disabled at"
" configure time", errors.ECODE_INVAL)
@ht.WithDesc("CheckFileStorage")
def _CheckFileStorage(value):
"""Ensures file storage is enabled if used.
......@@ -169,6 +183,8 @@ def _CheckFileStorage(value):
"""
if value == constants.DT_FILE:
RequireFileStorage()
elif value == constants.DT_SHARED_FILE:
RequireSharedFileStorage()
return True
......
......@@ -155,6 +155,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()
......@@ -272,6 +275,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,
......@@ -369,6 +373,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.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment