Toggle diff (454 lines)
diff --git a/gnu/local.mk b/gnu/local.mk
index a277e63fa4..143aae2bea 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -944,6 +944,14 @@ dist_patch_DATA = \
%D%/packages/patches/fontconfig-hurd-path-max.patch \
%D%/packages/patches/freeimage-unbundle.patch \
%D%/packages/patches/fuse-overlapping-headers.patch \
+ %D%/packages/patches/ganeti-copy-hmac.patch \
+ %D%/packages/patches/ganeti-drbd-compat.patch \
+ %D%/packages/patches/ganeti-disable-version-symlinks.patch \
+ %D%/packages/patches/ganeti-haskell-pythondir.patch \
+ %D%/packages/patches/ganeti-openvswitch-may-exist.patch \
+ %D%/packages/patches/ganeti-preserve-PYTHONPATH.patch \
+ %D%/packages/patches/ganeti-shepherd-master-failover.patch \
+ %D%/packages/patches/ganeti-shepherd-support.patch \
%D%/packages/patches/gash-utils-ls-test.patch \
%D%/packages/patches/gawk-shell.patch \
%D%/packages/patches/gcc-arm-bug-71399.patch \
diff --git a/gnu/packages/patches/ganeti-copy-hmac.patch b/gnu/packages/patches/ganeti-copy-hmac.patch
new file mode 100644
index 0000000000..c1a758afe9
--- /dev/null
+++ b/gnu/packages/patches/ganeti-copy-hmac.patch
@@ -0,0 +1,83 @@
+This is a cherry-pick of three upstream commits that got lost in the
+transition when Ganeti was donated to the community.
+
+Submitted upstream: <https://github.com/ganeti/ganeti/pull/1494>.
+
+diff --git a/lib/bootstrap.py b/lib/bootstrap.py
+--- a/lib/bootstrap.py
++++ b/lib/bootstrap.py
+@@ -934,6 +934,8 @@ def SetupNodeDaemon(opts, cluster_name, node, ssh_port):
+ constants.NDS_CLUSTER_NAME: cluster_name,
+ constants.NDS_NODE_DAEMON_CERTIFICATE:
+ utils.ReadFile(pathutils.NODED_CERT_FILE),
++ constants.NDS_HMAC:
++ utils.ReadFile(pathutils.CONFD_HMAC_KEY),
+ constants.NDS_SSCONF: ssconf.SimpleStore().ReadAll(),
+ constants.NDS_START_NODE_DAEMON: True,
+ constants.NDS_NODE_NAME: node,
+diff --git a/lib/tools/common.py b/lib/tools/common.py
+--- a/lib/tools/common.py
++++ b/lib/tools/common.py
+@@ -184,6 +184,19 @@ def VerifyClusterName(data, error_fn, cluster_name_constant,
+ return name
+
+
++def VerifyHmac(data, error_fn):
++ """Verifies the presence of the hmac secret.
++
++ @type data: dict
++
++ """
++ hmac = data.get(constants.NDS_HMAC)
++ if not hmac:
++ raise error_fn("Hmac key must be provided")
++
++ return hmac
++
++
+ def LoadData(raw, data_check):
+ """Parses and verifies input data.
+
+diff --git a/lib/tools/node_daemon_setup.py b/lib/tools/node_daemon_setup.py
+--- a/lib/tools/node_daemon_setup.py
++++ b/lib/tools/node_daemon_setup.py
+@@ -51,6 +51,7 @@ from ganeti.tools import common
+ _DATA_CHECK = ht.TStrictDict(False, True, {
+ constants.NDS_CLUSTER_NAME: ht.TNonEmptyString,
+ constants.NDS_NODE_DAEMON_CERTIFICATE: ht.TNonEmptyString,
++ constants.NDS_HMAC: ht.TNonEmptyString,
+ constants.NDS_SSCONF: ht.TDictOf(ht.TNonEmptyString, ht.TString),
+ constants.NDS_START_NODE_DAEMON: ht.TBool,
+ constants.NDS_NODE_NAME: ht.TString,
+@@ -127,11 +128,18 @@ def Main():
+ cluster_name = common.VerifyClusterName(data, SetupError,
+ constants.NDS_CLUSTER_NAME)
+ cert_pem = common.VerifyCertificateStrong(data, SetupError)
++ hmac_key = common.VerifyHmac(data, SetupError)
+ ssdata = VerifySsconf(data, cluster_name)
+
+ logging.info("Writing ssconf files ...")
+ ssconf.WriteSsconfFiles(ssdata, dry_run=opts.dry_run)
+
++ logging.info("Writing hmac.key ...")
++ utils.WriteFile(pathutils.CONFD_HMAC_KEY, data=hmac_key,
++ mode=pathutils.NODED_CERT_MODE,
++ uid=getent.masterd_uid, gid=getent.masterd_gid,
++ dry_run=opts.dry_run)
++
+ logging.info("Writing node daemon certificate ...")
+ utils.WriteFile(pathutils.NODED_CERT_FILE, data=cert_pem,
+ mode=pathutils.NODED_CERT_MODE,
+diff --git a/src/Ganeti/Constants.hs b/src/Ganeti/Constants.hs
+--- a/src/Ganeti/Constants.hs
++++ b/src/Ganeti/Constants.hs
+@@ -4833,6 +4833,9 @@ ndsNodeDaemonCertificate = "node_daemon_certificate"
+ ndsSsconf :: String
+ ndsSsconf = "ssconf"
+
++ndsHmac :: String
++ndsHmac = "hmac_key"
++
+ ndsStartNodeDaemon :: String
+ ndsStartNodeDaemon = "start_node_daemon"
+
diff --git a/gnu/packages/patches/ganeti-disable-version-symlinks.patch b/gnu/packages/patches/ganeti-disable-version-symlinks.patch
new file mode 100644
index 0000000000..a5f347cfc6
--- /dev/null
+++ b/gnu/packages/patches/ganeti-disable-version-symlinks.patch
@@ -0,0 +1,136 @@
+This patch adds a new "--disable-version-links" configuration option
+that allows installing to the standard GNU installation directories
+instead of having to add symlinks in /etc/ganeti/{lib,share} that
+points to the right $ganeti/{lib,share}/$version. Mainly to reduce
+service complexity, and because Guix users can install as many versions
+of Ganeti they can muster without resorting to such hacks.
+
+diff --git a/Makefile.am b/Makefile.am
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -66,11 +66,16 @@ SHELL_ENV_INIT = autotools/shell-env-init
+ # so, if some currently architecture-independent executable is replaced by an
+ # architecture-dependent one (and hence has to go under $(versiondir)), add a link
+ # under $(versionedsharedir) but do not change the external links.
++#
++# As of Ganeti 3.0, it is possible to disable this behavior by passing
++# --disable-version-links, in which case the standard GNU installation
++# directories are used.
+ if USE_VERSION_FULL
+ DIRVERSION=$(VERSION_FULL)
+ else
+ DIRVERSION=$(VERSION_MAJOR).$(VERSION_MINOR)
+ endif
++if USE_VERSION_LINKS
+ versiondir = $(libdir)/ganeti/$(DIRVERSION)
+ defaultversiondir = $(libdir)/ganeti/default
+ versionedsharedir = $(prefix)/share/ganeti/$(DIRVERSION)
+@@ -90,6 +95,18 @@ gntpythondir = $(versionedsharedir)
+ pkgpython_bindir = $(versionedsharedir)
+ gnt_python_sbindir = $(versionedsharedir)
+ tools_pythondir = $(versionedsharedir)
++else
++myexeclibdir = $(pkglibdir)
++pkgpython_rpc_stubdir = $(pkgpythondir)/rpc/stub
++gntpythondir = $(sbindir)
++pkgpython_bindir = $(pkglibdir)
++gnt_python_sbindir = $(sbindir)
++tools_pythondir = $(pkglibdir)
++versionedsharedir = $(pkglibdir)
++# This is a hack but works because the only user does $(versiondir)$(datadir).
++versiondir =
++endif !USE_VERSION_LINKS
++
+
+ clientdir = $(pkgpythondir)/client
+ cmdlibdir = $(pkgpythondir)/cmdlib
+@@ -2356,6 +2373,7 @@ src/AutoConf.hs: Makefile src/AutoConf.hs.in $(PRINT_PY_CONSTANTS) \
+ -DVERSION_SUFFIX="$(VERSION_SUFFIX)" \
+ -DVERSION_FULL="$(VERSION_FULL)" \
+ -DDIRVERSION="$(DIRVERSION)" \
++ -DUSE_VERSION_LINKS="$(USE_VERSION_LINKS)" \
+ -DLOCALSTATEDIR="$(localstatedir)" \
+ -DSYSCONFDIR="$(sysconfdir)" \
+ -DSSH_CONFIG_DIR="$(SSH_CONFIG_DIR)" \
+@@ -2857,6 +2875,7 @@ install-exec-local:
+ @mkdir_p@ "$(DESTDIR)${localstatedir}/lib/ganeti" \
+ "$(DESTDIR)${localstatedir}/log/ganeti" \
+ "$(DESTDIR)${localstatedir}/run/ganeti"
++if USE_VERSION_LINKS
+ for dir in $(SYMLINK_TARGET_DIRS); do \
+ @mkdir_p@ $(DESTDIR)$$dir; \
+ done
+@@ -2892,7 +2911,8 @@ install-exec-local:
+ if INSTALL_SYMLINKS
+ $(LN_S) -f $(versionedsharedir) $(DESTDIR)$(sysconfdir)/ganeti/share
+ $(LN_S) -f $(versiondir) $(DESTDIR)$(sysconfdir)/ganeti/lib
+-endif
++endif INSTALL_SYMLINKS
++endif USE_VERSION_LINKS
+
+ .PHONY: apidoc
+ if WANT_HSAPIDOC
+diff --git a/configure.ac b/configure.ac
+--- a/configure.ac
++++ b/configure.ac
+@@ -29,6 +29,23 @@ AC_SUBST([BINDIR], $bindir)
+ AC_SUBST([SBINDIR], $sbindir)
+ AC_SUBST([MANDIR], $mandir)
+
++# --enable-version-links
++AC_ARG_ENABLE([version-links],
++ [AS_HELP_STRING([--enable-version-links],
++ m4_normalize([install ganeti to version-specific
++ subdirectories to allow installing multiple versions
++ in parallel (default: enabled)]))],
++ [[if test "$enableval" != no; then
++ USE_VERSION_LINKS=True
++ else
++ USE_VERSION_LINKS=False
++ fi
++ ]],
++ [USE_VERSION_LINKS=True
++ ])
++AC_SUBST(USE_VERSION_LINKS, $USE_VERSION_LINKS)
++AM_CONDITIONAL([USE_VERSION_LINKS], [test "$USE_VERSION_LINKS" = True])
++
+ # --enable-versionfull
+ AC_ARG_ENABLE([versionfull],
+ [AS_HELP_STRING([--enable-versionfull],
+diff --git a/lib/bootstrap.py b/lib/bootstrap.py
+--- a/lib/bootstrap.py
++++ b/lib/bootstrap.py
+@@ -944,7 +944,7 @@ def SetupNodeDaemon(opts, cluster_name, node, ssh_port):
+ debug=opts.debug, verbose=opts.verbose,
+ use_cluster_key=True, ask_key=opts.ssh_key_check,
+ strict_host_check=opts.ssh_key_check,
+- ensure_version=True)
++ ensure_version=constants.USE_VERSION_LINKS)
+
+ _WaitForSshDaemon(node, ssh_port)
+ _WaitForNodeDaemon(node)
+diff --git a/src/AutoConf.hs.in b/src/AutoConf.hs.in
+--- a/src/AutoConf.hs.in
++++ b/src/AutoConf.hs.in
+@@ -64,6 +64,9 @@ versionFull = "VERSION_FULL"
+ dirVersion :: String
+ dirVersion = "DIRVERSION"
+
++useVersionLinks :: Bool
++useVersionLinks = USE_VERSION_LINKS
++
+ localstatedir :: String
+ localstatedir = "LOCALSTATEDIR"
+
+diff --git a/src/Ganeti/Constants.hs b/src/Ganeti/Constants.hs
+--- a/src/Ganeti/Constants.hs
++++ b/src/Ganeti/Constants.hs
+@@ -164,5 +164,8 @@ versionRevision = AutoConf.versionRevision
+ dirVersion :: String
+ dirVersion = AutoConf.dirVersion
+
++useVersionLinks :: Bool
++useVersionLinks = AutoConf.useVersionLinks
++
+ osApiV10 :: Int
+ osApiV10 = 10
diff --git a/gnu/packages/patches/ganeti-drbd-compat.patch b/gnu/packages/patches/ganeti-drbd-compat.patch
new file mode 100644
index 0000000000..e06e04c5ef
--- /dev/null
+++ b/gnu/packages/patches/ganeti-drbd-compat.patch
@@ -0,0 +1,168 @@
+This patch adds support for newer versions of DRBD.
+
+Submitted upstream: <https://github.com/ganeti/ganeti/pull/1496>.
+
+diff --git a/lib/storage/drbd.py b/lib/storage/drbd.py
+--- a/lib/storage/drbd.py
++++ b/lib/storage/drbd.py
+@@ -315,6 +315,13 @@ class DRBD8Dev(base.BlockDev):
+ """
+ return self._show_info_cls.GetDevInfo(self._GetShowData(minor))
+
++ @staticmethod
++ def _NeedsLocalSyncerParams():
++ # For DRBD >= 8.4, syncer init must be done after local, not in net.
++ info = DRBD8.GetProcInfo()
++ version = info.GetVersion()
++ return version["k_minor"] >= 4
++
+ def _MatchesLocal(self, info):
+ """Test if our local config matches with an existing device.
+
+@@ -397,6 +404,22 @@ class DRBD8Dev(base.BlockDev):
+ base.ThrowError("drbd%d: can't attach local disk: %s",
+ minor, result.output)
+
++ def _WaitForMinorSyncParams():
++ """Call _SetMinorSyncParams and raise RetryAgain on errors.
++ """
++ if self._SetMinorSyncParams(minor, self.params):
++ raise utils.RetryAgain()
++ else:
++ return []
++
++ if self._NeedsLocalSyncerParams():
++ # Retry because disk config for DRBD resource may be still uninitialized.
++ try:
++ utils.Retry(_WaitForMinorSyncParams, 1.0, 5.0)
++ except utils.RetryTimeout:
++ base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
++ (minor, utils.CommaJoin(sync_errors)))
++
+ def _AssembleNet(self, minor, net_info, dual_pri=False, hmac=None,
+ secret=None):
+ """Configure the network part of the device.
+@@ -432,21 +455,24 @@ class DRBD8Dev(base.BlockDev):
+ # sync speed only after setting up both sides can race with DRBD
+ # connecting, hence we set it here before telling DRBD anything
+ # about its peer.
+- sync_errors = self._SetMinorSyncParams(minor, self.params)
+- if sync_errors:
+- base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
+- (minor, utils.CommaJoin(sync_errors)))
++
++ if not self._NeedsLocalSyncerParams():
++ sync_errors = self._SetMinorSyncParams(minor, self.params)
++ if sync_errors:
++ base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
++ (minor, utils.CommaJoin(sync_errors)))
+
+ family = self._GetNetFamily(minor, lhost, rhost)
+
+- cmd = self._cmd_gen.GenNetInitCmd(minor, family, lhost, lport,
++ cmds = self._cmd_gen.GenNetInitCmds(minor, family, lhost, lport,
+ rhost, rport, protocol,
+ dual_pri, hmac, secret, self.params)
+
+- result = utils.RunCmd(cmd)
+- if result.failed:
+- base.ThrowError("drbd%d: can't setup network: %s - %s",
+- minor, result.fail_reason, result.output)
++ for cmd in cmds:
++ result = utils.RunCmd(cmd)
++ if result.failed:
++ base.ThrowError("drbd%d: can't setup network: %s - %s",
++ minor, result.fail_reason, result.output)
+
+ def _CheckNetworkConfig():
+ info = self._GetShowInfo(minor)
+@@ -463,19 +489,20 @@ class DRBD8Dev(base.BlockDev):
+ base.ThrowError("drbd%d: timeout while configuring network", minor)
+
+ # Once the assembly is over, try to set the synchronization parameters
+- try:
+- # The minor may not have been set yet, requiring us to set it at least
+- # temporarily
+- old_minor = self.minor
+- self._SetFromMinor(minor)
+- sync_errors = self.SetSyncParams(self.params)
+- if sync_errors:
+- base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
+- (self.minor, utils.CommaJoin(sync_errors)))
+- finally:
+- # Undo the change, regardless of whether it will have to be done again
+- # soon
+- self._SetFromMinor(old_minor)
++ if not self._NeedsLocalSyncerParams():
++ try:
++ # The minor may not have been set yet, requiring us to set it at least
++ # temporarily
++ old_minor = self.minor
++ self._SetFromMinor(minor)
++ sync_errors = self.SetSyncParams(self.params)
++ if sync_errors:
++ base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
++ (self.minor, utils.CommaJoin(sync_errors)))
++ finally:
++ # Undo the change, regardless of whether it will have to be done again
++ # soon
++ self._SetFromMinor(old_minor)
+
+ @staticmethod
+ def _GetNetFamily(minor, lhost, rhost):
+diff --git a/lib/storage/drbd_cmdgen.py b/lib/storage/drbd_cmdgen.py
+--- a/lib/storage/drbd_cmdgen.py
++++ b/lib/storage/drbd_cmdgen.py
+@@ -56,7 +56,7 @@ class BaseDRBDCmdGenerator(object):
+ def GenLocalInitCmds(self, minor, data_dev, meta_dev, size_mb, params):
+ raise NotImplementedError
+
+- def GenNetInitCmd(self, minor, family, lhost, lport, rhost, rport, protocol,
++ def GenNetInitCmds(self, minor, family, lhost, lport, rhost, rport, protocol,
+ dual_pri, hmac, secret, params):
+ raise NotImplementedError
+
+@@ -138,7 +138,7 @@ class DRBD83CmdGenerator(BaseDRBDCmdGenerator):
+
+ return [args]
+
+- def GenNetInitCmd(self, minor, family, lhost, lport, rhost, rport, protocol,
++ def GenNetInitCmds(self, minor, family, lhost, lport, rhost, rport, protocol,
+ dual_pri, hmac, secret, params):
+ args = ["drbdsetup", self._DevPath(minor), "net",
+ "%s:%s:%s" % (family, lhost, lport),
+@@ -155,7 +155,7 @@ class DRBD83CmdGenerator(BaseDRBDCmdGenerator):
+ if params[constants.LDP_NET_CUSTOM]:
+ args.extend(shlex.split(params[constants.LDP_NET_CUSTOM]))
+
+- return args
++ return [args]
+
+ def GenSyncParamsCmd(self, minor, params):
+ args = ["drbdsetup", self._DevPath(minor), "syncer"]
+@@ -345,8 +345,14 @@ class DRBD84CmdGenerator(BaseDRBDCmdGenerator):
+
+ return cmds
+
+- def GenNetInitCmd(self, minor, family, lhost, lport, rhost, rport, protocol,
++ def GenNetInitCmds(self, minor, family, lhost, lport, rhost, rport, protocol,
+ dual_pri, hmac, secret, params):
++ cmds = []
++
++ cmds.append(["drbdsetup", "new-resource", self._GetResource(minor)])
++ cmds.append(["drbdsetup", "new-minor", self._GetResource(minor),
++ str(minor), "0"])
++
+ args = ["drbdsetup", "connect", self._GetResource(minor),
+ "%s:%s:%s" % (family, lhost, lport),
+ "%s:%s:%s" % (family, rhost, rport),
+@@ -362,7 +368,8 @@ class DRBD84CmdGenerator(BaseDRBDCmdGenerator):
+ if params[constants.LDP_NET_CUSTOM]:
+ args.extend(shlex.split(params[constants.LDP_NET_CUSTOM]))
+
+- return args
++ cmds.append(args)
++ return cmds
+
+ def GenSyncParamsCmd(self, minor, params):
+ args = ["drbdsetup", "disk-options", minor]
diff --git a/gnu/packages/patches/ganeti-haskell-pythondir.patch b/gnu/packages/patches/ganeti-haskell-pythondir.patch
new file mode 100644
index 0000000000..fa77771839
--- /dev/null
+++ b/gnu/packages/patches/ganeti-haskell-pythondir.patch
@@ -0,0 +1,66 @@
+This patch allows the Haskell daemons to locate Python libraries
+installed to a non-standard pythondir. It is necessary because Guix
+does not use versionedsharedir (see related patch that disables it).
+
+diff --git a/Makefile.am b/Makefile.am
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -83,6 +83,7 @@ myexeclibdir = $(pkglibdir)
+ bindir = $(versiondir)/$(BINDIR)
+ sbindir = $(versiondir)$(SBINDIR)
+ mandir = $(versionedsharedir)/root$(MANDIR)
++pythondir = $(versionedsharedir)
+ pkgpythondir = $(versionedsharedir)/ganeti
+ pkgpython_rpc_stubdir = $(versionedsharedir)/ganeti/rpc/stub
+ gntpythondir = $(versionedsharedir)
+@@ -2386,6 +2387,7 @@ src/AutoConf.hs: Makefile src/AutoConf.hs.in $(PRINT_PY_CONSTANTS) \
+ -DPKGLIBDIR="$(libdir)/ganeti" \
+ -DSHAREDIR="$(prefix)/share/ganeti" \
+ -DVERSIONEDSHAREDIR="$(versionedsharedir)" \
++ -DPYTHONDIR="$(pythondir)" \
+ -DDRBD_BARRIERS="$(DRBD_BARRIERS)" \
+ -DDRBD_NO_META_FLUSH="$(DRBD_NO_META_FLUSH)" \
+ -DSYSLOG_USAGE="$(SYSLOG_USAGE)" \
+diff --git a/src/AutoConf.hs.in b/s