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

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