From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp2 ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms11 with LMTPS id CIf/FkScBV/cOAAA0tVLHw (envelope-from ) for ; Wed, 08 Jul 2020 10:13:24 +0000 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp2 with LMTPS id wELREkScBV/QPgAAB5/wlQ (envelope-from ) for ; Wed, 08 Jul 2020 10:13:24 +0000 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 9E09F940656 for ; Wed, 8 Jul 2020 10:13:23 +0000 (UTC) Received: from localhost ([::1]:48268 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jt74s-0007GX-B4 for larch@yhetil.org; Wed, 08 Jul 2020 06:13:22 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:35524) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1jt74Y-00074z-5z for guix-patches@gnu.org; Wed, 08 Jul 2020 06:13:02 -0400 Received: from debbugs.gnu.org ([209.51.188.43]:54158) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1jt74X-0002Ul-Su for guix-patches@gnu.org; Wed, 08 Jul 2020 06:13:01 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1jt74X-00033m-Ns for guix-patches@gnu.org; Wed, 08 Jul 2020 06:13:01 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#42261] [PATCH 1/4] gnu: Add ganeti. References: <20200708100826.3114-1-marius@gnu.org> In-Reply-To: <20200708100826.3114-1-marius@gnu.org> Resent-From: Marius Bakke Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Wed, 08 Jul 2020 10:13:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 42261 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: 42261@debbugs.gnu.org Received: via spool by 42261-submit@debbugs.gnu.org id=B42261.159420313611695 (code B ref 42261); Wed, 08 Jul 2020 10:13:01 +0000 Received: (at 42261) by debbugs.gnu.org; 8 Jul 2020 10:12:16 +0000 Received: from localhost ([127.0.0.1]:37465 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1jt73c-00032B-3F for submit@debbugs.gnu.org; Wed, 08 Jul 2020 06:12:16 -0400 Received: from eggs.gnu.org ([209.51.188.92]:38804) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1jt73T-00030q-5c for 42261@debbugs.gnu.org; Wed, 08 Jul 2020 06:12:02 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]:33613) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1jt73N-0002Lk-V4 for 42261@debbugs.gnu.org; Wed, 08 Jul 2020 06:11:49 -0400 Received: from ti0006q161-3115.bb.online.no ([88.95.106.80]:53152 helo=localhost) by fencepost.gnu.org with esmtpsa (TLS1.2:RSA_AES_256_CBC_SHA1:256) (Exim 4.82) (envelope-from ) id 1jt73N-0001OV-5t for 42261@debbugs.gnu.org; Wed, 08 Jul 2020 06:11:49 -0400 From: Marius Bakke Date: Wed, 8 Jul 2020 12:11:14 +0200 Message-Id: <20200708101118.3579-1-marius@gnu.org> X-Mailer: git-send-email 2.27.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Score: 0.0 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-Spam-Score: -1.0 (-) X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+larch=yhetil.org@gnu.org Sender: "Guix-patches" X-Scanner: scn0 Authentication-Results: aspmx1.migadu.com; dkim=none; dmarc=none; spf=pass (aspmx1.migadu.com: domain of guix-patches-bounces@gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=guix-patches-bounces@gnu.org X-Spam-Score: 3.99 X-TUID: xB5aADdUUopd * gnu/packages/virtualization.scm (system->qemu-target, ganeti): New variables. * gnu/packages/patches/ganeti-copy-hmac.patch, gnu/packages/patches/ganeti-disable-version-symlinks.patch, gnu/packages/patches/ganeti-drbd-compat.patch, gnu/packages/patches/ganeti-haskell-pythondir.patch, gnu/packages/patches/ganeti-openvswitch-may-exist.patch, gnu/packages/patches/ganeti-preserve-PYTHONPATH.patch, gnu/packages/patches/ganeti-shepherd-master-failover.patch, gnu/packages/patches/ganeti-shepherd-support.patch: New files. * gnu/local.mk (dist_patch_DATA): Adjust accordingly. --- gnu/local.mk | 8 + gnu/packages/patches/ganeti-copy-hmac.patch | 83 ++++ .../ganeti-disable-version-symlinks.patch | 136 +++++++ gnu/packages/patches/ganeti-drbd-compat.patch | 168 ++++++++ .../patches/ganeti-haskell-pythondir.patch | 66 +++ .../ganeti-openvswitch-may-exist.patch | 25 ++ .../patches/ganeti-preserve-PYTHONPATH.patch | 21 + .../ganeti-shepherd-master-failover.patch | 26 ++ .../patches/ganeti-shepherd-support.patch | 87 ++++ gnu/packages/virtualization.scm | 378 ++++++++++++++++++ 10 files changed, 998 insertions(+) create mode 100644 gnu/packages/patches/ganeti-copy-hmac.patch create mode 100644 gnu/packages/patches/ganeti-disable-version-symlinks.patch create mode 100644 gnu/packages/patches/ganeti-drbd-compat.patch create mode 100644 gnu/packages/patches/ganeti-haskell-pythondir.patch create mode 100644 gnu/packages/patches/ganeti-openvswitch-may-exist.patch create mode 100644 gnu/packages/patches/ganeti-preserve-PYTHONPATH.patch create mode 100644 gnu/packages/patches/ganeti-shepherd-master-failover.patch create mode 100644 gnu/packages/patches/ganeti-shepherd-support.patch 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: . + +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: . + +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/src/AutoConf.hs.in +--- a/src/AutoConf.hs.in ++++ b/src/AutoConf.hs.in +@@ -157,6 +157,9 @@ sharedir = "SHAREDIR" + versionedsharedir :: String + versionedsharedir = "VERSIONEDSHAREDIR" + ++pythondir :: String ++pythondir = "PYTHONDIR" ++ + drbdBarriers :: String + drbdBarriers = "DRBD_BARRIERS" + +diff --git a/src/Ganeti/Path.hs b/src/Ganeti/Path.hs +--- a/src/Ganeti/Path.hs ++++ b/src/Ganeti/Path.hs +@@ -188,5 +188,5 @@ getInstReasonFilename instName = instanceReasonDir `pjoin` instName + + -- | The path to the Python executable for starting jobs. + jqueueExecutorPy :: IO FilePath +-jqueueExecutorPy = return $ versionedsharedir +- "ganeti" "jqueue" "exec.py" ++jqueueExecutorPy = return $ pythondir ++ "ganeti" "jqueue" "exec.py" +diff --git a/src/Ganeti/Query/Exec.hs b/src/Ganeti/Query/Exec.hs +--- a/src/Ganeti/Query/Exec.hs ++++ b/src/Ganeti/Query/Exec.hs +@@ -99,12 +99,12 @@ spawnJobProcess jid = withErrorLogAt CRITICAL (show jid) $ + do + use_debug <- isDebugMode + env_ <- (M.toList . M.insert "GNT_DEBUG" (if use_debug then "1" else "0") +- . M.insert "PYTHONPATH" AC.versionedsharedir ++ . M.insert "PYTHONPATH" AC.pythondir + . M.fromList) + `liftM` getEnvironment + execPy <- P.jqueueExecutorPy + logDebug $ "Executing " ++ AC.pythonPath ++ " " ++ execPy +- ++ " with PYTHONPATH=" ++ AC.versionedsharedir ++ ++ " with PYTHONPATH=" ++ AC.pythondir + + (master, child) <- pipeClient connectConfig + let (rh, wh) = clientToHandle child + diff --git a/gnu/packages/patches/ganeti-openvswitch-may-exist.patch b/gnu/packages/patches/ganeti-openvswitch-may-exist.patch new file mode 100644 index 0000000000..03543167f6 --- /dev/null +++ b/gnu/packages/patches/ganeti-openvswitch-may-exist.patch @@ -0,0 +1,25 @@ +It is possible that the switch is already configured by external tools. +Do not error out when that is the case. + +Submitted upstream: . + +diff --git a/lib/backend.py b/lib/backend.py +--- a/lib/backend.py ++++ b/lib/backend.py +@@ -5865,14 +5865,14 @@ def ConfigureOVS(ovs_name, ovs_link): + + """ + # Initialize the OpenvSwitch +- result = utils.RunCmd(["ovs-vsctl", "add-br", ovs_name]) ++ result = utils.RunCmd(["ovs-vsctl", "--may-exist", "add-br", ovs_name]) + if result.failed: + _Fail("Failed to create openvswitch. Script return value: %s, output: '%s'" + % (result.exit_code, result.output), log=True) + + # And connect it to a physical interface, if given + if ovs_link: +- result = utils.RunCmd(["ovs-vsctl", "add-port", ovs_name, ovs_link]) ++ result = utils.RunCmd(["ovs-vsctl", "--may-exist", "add-port", ovs_name, ovs_link]) + if result.failed: + _Fail("Failed to connect openvswitch to interface %s. Script return" + " value: %s, output: '%s'" % (ovs_link, result.exit_code, diff --git a/gnu/packages/patches/ganeti-preserve-PYTHONPATH.patch b/gnu/packages/patches/ganeti-preserve-PYTHONPATH.patch new file mode 100644 index 0000000000..1358e30633 --- /dev/null +++ b/gnu/packages/patches/ganeti-preserve-PYTHONPATH.patch @@ -0,0 +1,21 @@ +Do not override PYTHONPATH when calling Python code from the Haskell +daemons. This is necessary because the Python library dependencies are +only available through PYTHONPATH. + +diff --git a/src/Ganeti/Query/Exec.hs b/src/Ganeti/Query/Exec.hs +--- a/src/Ganeti/Query/Exec.hs ++++ b/src/Ganeti/Query/Exec.hs +@@ -99,12 +99,10 @@ spawnJobProcess jid = withErrorLogAt CRITICAL (show jid) $ + do + use_debug <- isDebugMode + env_ <- (M.toList . M.insert "GNT_DEBUG" (if use_debug then "1" else "0") +- . M.insert "PYTHONPATH" AC.pythondir + . M.fromList) + `liftM` getEnvironment + execPy <- P.jqueueExecutorPy + logDebug $ "Executing " ++ AC.pythonPath ++ " " ++ execPy +- ++ " with PYTHONPATH=" ++ AC.pythondir + + (master, child) <- pipeClient connectConfig + let (rh, wh) = clientToHandle child + diff --git a/gnu/packages/patches/ganeti-shepherd-master-failover.patch b/gnu/packages/patches/ganeti-shepherd-master-failover.patch new file mode 100644 index 0000000000..d255233fc1 --- /dev/null +++ b/gnu/packages/patches/ganeti-shepherd-master-failover.patch @@ -0,0 +1,26 @@ +By default, master-failover will call "herd start ganeti-wconfd" with +extra arguments such as --force-node. That does not work with the +Shepherd, so Guix has a "ganeti-wconfd-forced" service for this purpose. + +diff --git a/lib/bootstrap.py b/lib/bootstrap.py +--- a/lib/bootstrap.py ++++ b/lib/bootstrap.py +@@ -1010,8 +1010,7 @@ def MasterFailover(no_voting=False): + try: + # Forcefully start WConfd so that we can access the configuration + result = utils.RunCmd([pathutils.DAEMON_UTIL, +- "start", constants.WCONFD, "--force-node", +- "--no-voting", "--yes-do-it"]) ++ "start", "ganeti-wconfd-forced"]) + if result.failed: + raise errors.OpPrereqError("Could not start the configuration daemon," + " command %s had exitcode %s and error %s" % +@@ -1074,7 +1073,7 @@ def MasterFailover(no_voting=False): + return 1, warnings + finally: + # stop WConfd again: +- result = utils.RunCmd([pathutils.DAEMON_UTIL, "stop", constants.WCONFD]) ++ result = utils.RunCmd([pathutils.DAEMON_UTIL, "stop", "ganeti-wconfd-forced"]) + if result.failed: + warning = ("Could not stop the configuration daemon," + " command %s had exitcode %s and error %s" diff --git a/gnu/packages/patches/ganeti-shepherd-support.patch b/gnu/packages/patches/ganeti-shepherd-support.patch new file mode 100644 index 0000000000..f750604344 --- /dev/null +++ b/gnu/packages/patches/ganeti-shepherd-support.patch @@ -0,0 +1,87 @@ +Ganeti uses an internal tool to start/stop daemons during init and +upgrade. This patch makes the tool use native Shepherd facilities. + +diff --git a/daemons/daemon-util.in b/daemons/daemon-util.in +--- a/daemons/daemon-util.in ++++ b/daemons/daemon-util.in +@@ -184,6 +184,21 @@ use_systemctl() { + return 1 + } + ++# Checks if we should use the Shepherd to start/stop daemons ++use_shepherd() { ++ # Is Shepherd running as PID 1? ++ ps --no-headers -p 1 -o cmd | grep -q shepherd || return 1 ++ ++ type -p herd >/dev/null || return 1 ++ ++ # Does Shepherd know about Ganeti at all? ++ if herd status | grep -q ganeti; then ++ return 0 ++ fi ++ ++ return 1 ++} ++ + # Prints path to PID file for a daemon. + daemon_pidfile() { + if [[ "$#" -lt 1 ]]; then +@@ -261,6 +276,13 @@ check() { + else + return 1 + fi ++ elif use_shepherd; then ++ activestate="$(herd status ${name})" ++ if echo $activestate | grep -q Running ; then ++ return 0 ++ else ++ return 1 ++ fi + elif type -p start-stop-daemon >/dev/null; then + start-stop-daemon --stop --signal 0 --quiet \ + --pidfile $pidfile --name "$name" +@@ -291,6 +313,20 @@ start() { + return $? + fi + ++ if use_shepherd; then ++ if herd status "$name" | grep -q "disabled"; then ++ # The Shepherd will disable a service that has stopped, even if it exits ++ # gracefully. Thus, we must re-enable it in case of a master failover. ++ herd enable "${name}" ++ fi ++ # Note: unlike systemd, which happily starts a service and returns success ++ # even if the daemon immediately exits, the Shepherd actually waits for it ++ # to come up. Thus, ignore the exit status from 'herd start' in case of ++ # master daemons running on the wrong node, or ganeti-kvmd disabled, etc. ++ herd start "${name}" ++ return 0 ++ fi ++ + # Read $_ARGS and $EXTRA__ARGS + eval local args="\"\$${ucname}_ARGS \$EXTRA_${ucname}_ARGS\"" + +@@ -336,6 +372,13 @@ stop() { + + if use_systemctl; then + systemctl stop "${name}.service" ++ elif use_shepherd; then ++ if herd status | grep -q "$name"; then ++ herd stop "$name" ++ else ++ # Do not raise an error if the service has not been enabled. ++ return 0 ++ fi + elif type -p start-stop-daemon >/dev/null; then + start-stop-daemon --stop --quiet --oknodo --retry 30 \ + --pidfile $pidfile --name "$name" +@@ -352,6 +395,9 @@ check_and_start() { + if use_systemctl; then + echo "${name} supervised by systemd but not running, will not restart." + return 1 ++ elif use_shepherd; then ++ echo "${name} supervised by shepherd but not running, will not restart." ++ return 1 + fi + + start $name diff --git a/gnu/packages/virtualization.scm b/gnu/packages/virtualization.scm index 64cd815bb8..3ef9b8470a 100644 --- a/gnu/packages/virtualization.scm +++ b/gnu/packages/virtualization.scm @@ -38,6 +38,7 @@ #:use-module (gnu packages attr) #:use-module (gnu packages autotools) #:use-module (gnu packages backup) + #:use-module (gnu packages base) #:use-module (gnu packages bison) #:use-module (gnu packages check) #:use-module (gnu packages cmake) @@ -61,10 +62,17 @@ #:use-module (gnu packages gnupg) #:use-module (gnu packages golang) #:use-module (gnu packages gtk) + #:use-module (gnu packages haskell) + #:use-module (gnu packages haskell-apps) + #:use-module (gnu packages haskell-check) + #:use-module (gnu packages haskell-crypto) + #:use-module (gnu packages haskell-web) + #:use-module (gnu packages haskell-xyz) #:use-module (gnu packages image) #:use-module (gnu packages libbsd) #:use-module (gnu packages libusb) #:use-module (gnu packages linux) + #:use-module (gnu packages m4) #:use-module (gnu packages ncurses) #:use-module (gnu packages nettle) #:use-module (gnu packages networking) @@ -75,6 +83,7 @@ #:use-module (gnu packages polkit) #:use-module (gnu packages protobuf) #:use-module (gnu packages python) + #:use-module (gnu packages python-crypto) #:use-module (gnu packages python-web) #:use-module (gnu packages python-xyz) #:use-module (gnu packages pulseaudio) @@ -82,6 +91,7 @@ #:use-module (gnu packages sdl) #:use-module (gnu packages sphinx) #:use-module (gnu packages spice) + #:use-module (gnu packages ssh) #:use-module (gnu packages texinfo) #:use-module (gnu packages textutils) #:use-module (gnu packages tls) @@ -349,6 +359,374 @@ server and embedded PowerPC, and S390 guests.") "usbredir" "libdrm" "libepoxy" "pulseaudio" "vde2" "libcacard"))))) +(define (system->qemu-target system) + (cond + ((string-prefix? "i686" system) + "qemu-system-i386") + ((string-prefix? "arm" system) + "qemu-system-arm") + (else + (string-append "qemu-system-" (match (string-split system #\-) + ((arch kernel) arch) + (_ system)))))) + +(define-public ganeti + (package + (name "ganeti") + (version "3.0.0.beta1") + (source (origin + (method url-fetch) + (uri (string-append "https://github.com/ganeti/ganeti/releases" + "/download/v3.0.0beta1/ganeti-" + version ".tar.gz")) + (sha256 + (base32 "194mddbbcp8kvqhjxpx3sgvrzhlwfpdwk7m7brvdcrifrblbcd92")) + (patches (search-patches "ganeti-shepherd-support.patch" + "ganeti-shepherd-master-failover.patch" + "ganeti-copy-hmac.patch" + "ganeti-openvswitch-may-exist.patch" + "ganeti-drbd-compat.patch" + "ganeti-haskell-pythondir.patch" + "ganeti-disable-version-symlinks.patch" + "ganeti-preserve-PYTHONPATH.patch")))) + (build-system gnu-build-system) + (arguments + `(#:imported-modules ((guix build haskell-build-system) + (guix build python-build-system) + ,@%gnu-build-system-modules) + #:modules ((ice-9 rdelim) + ((guix build haskell-build-system) #:prefix haskell:) + ((guix build python-build-system) #:select (python-version)) + ,@%gnu-build-system-modules) + + ;; The default test target includes a lot of checks that are only really + ;; relevant for developers such as NEWS file checking, line lengths, etc. + ;; We are only interested in the "py-tests" and "hs-tests" targets: this + ;; is the closest we've got even though it includes a little more. + #:test-target "check-TESTS" + + #:configure-flags + (list "--localstatedir=/var" + "--sharedstatedir=/var" + "--sysconfdir=/etc" + "--enable-haskell-tests" + + ;; By default, the build system installs everything to versioned + ;; directories such as $libdir/3.0 and relies on a $libdir/default + ;; symlink pointed from /etc/ganeti/{lib,share} to actually function. + ;; This is done to accommodate installing multiple versions in + ;; parallel, but is of little use to us as Guix users can just + ;; roll back and forth. Thus, disable it for simplicity. + "--disable-version-links" + + ;; Ganeti can optionally take control over SSH keys and rotate + ;; them with 'gnt-cluster renew-crypto', and thus needs to know + ;; how to restart the SSH server. + "--with-sshd-restart-command='herd restart ssh-daemon'" + + ;; Look for OS definitions in this directory by default. It can + ;; be changed in the cluster configuration. + "--with-os-search-path=/run/current-system/profile/share/ganeti/os" + + ;; The default QEMU executable to use. We don't use the package + ;; here because this entry is stored in the cluster configuration. + (string-append "--with-kvm-path=/run/current-system/profile/bin/" + ,(system->qemu-target (%current-system)))) + #:phases + (modify-phases %standard-phases + (add-before 'bootstrap 'force-bootstrap + (lambda _ + (delete-file "configure") + #t)) + (add-after 'unpack 'adjust-quickcheck-requirement + (lambda _ + ;; The test suite wants QuickCheck 2.12 or lower, but works fine + ;; with 2.13. Just bump the requirement. + (substitute* '("ganeti.cabal" "cabal/ganeti.template.cabal") + (("&& < 2\\.13") + "&& < 2.14")) + #t)) + (add-after 'unpack 'patch-absolute-file-names + (lambda _ + (substitute* '("lib/utils/process.py" + "lib/utils/text.py" + "src/Ganeti/Constants.hs" + "src/Ganeti/HTools/CLI.hs" + "test/py/ganeti.config_unittest.py" + "test/py/ganeti.hooks_unittest.py" + "test/py/ganeti.utils.process_unittest.py" + "test/py/ganeti.utils.text_unittest.py" + "test/py/ganeti.utils.wrapper_unittest.py") + (("/bin/sh") (which "sh")) + (("/bin/bash") (which "bash")) + (("/usr/bin/env") (which "env")) + (("/bin/true") (which "true"))) + + ;; This script is called by the node daemon at startup to perform + ;; sanity checks on the cluster IP addresses, and it is also used + ;; in a master-failover scenario. Add absolute references to + ;; avoid propagating these executables. + (substitute* "tools/master-ip-setup" + (("arping") (which "arping")) + (("ndisc6") (which "ndisc6")) + (("fping") (which "fping")) + (("grep") (which "grep")) + (("ip addr") (string-append (which "ip") " addr"))) + #t)) + (add-after 'unpack 'override-builtin-PATH + (lambda _ + ;; Ganeti runs OS install scripts and similar with a built-in + ;; hard coded PATH. Patch so it works on Guix System. + (substitute* "src/Ganeti/Constants.hs" + (("/sbin:/bin:/usr/sbin:/usr/bin") + "/run/setuid-programs:/run/current-system/profile/sbin:\ +/run/current-system/profile/bin")) + #t)) + + ;; The build system invokes Cabal and GHC, which do not work with + ;; GHC_PACKAGE_PATH: . + ;; Tweak the build system to do roughly what haskell-build-system does. + (add-before 'configure 'configure-haskell + (assoc-ref haskell:%standard-phases 'setup-compiler)) + (add-after 'configure 'do-not-use-GHC_PACKAGE_PATH + (lambda _ + (unsetenv "GHC_PACKAGE_PATH") + (substitute* "Makefile" + (("\\$\\(CABAL\\)") + "$(CABAL) --package-db=../package.conf.d") + (("\\$\\(GHC\\)") + "$(GHC) -package-db=../package.conf.d")) + #t)) + + (add-after 'configure 'fix-installation-directories + (lambda _ + (substitute* "Makefile" + ;; Do not attempt to create /var during install. + (("\\$\\(DESTDIR\\)\\$\\{localstatedir\\}") + "$(DESTDIR)${prefix}${localstatedir}") + ;; Similarly, do not attempt to install the sample ifup scripts + ;; to /etc/ganeti. + (("\\$\\(DESTDIR\\)\\$\\(ifupdir\\)") + "$(DESTDIR)${prefix}$(ifupdir)")) + #t)) + (add-before 'build 'adjust-tests + (lambda _ + ;; Disable tests that can not run. Do it early to prevent + ;; touching the Makefile later and triggering a needless rebuild. + (substitute* "Makefile" + ;; These tests expect the presence of a 'root' user (via + ;; ganeti/runtime.py), which fails in the build environment. + (("test/py/ganeti\\.asyncnotifier_unittest\\.py") "") + (("test/py/ganeti\\.backend_unittest\\.py") "") + (("test/py/ganeti\\.daemon_unittest\\.py") "") + (("test/py/ganeti\\.tools\\.ensure_dirs_unittest\\.py") "") + (("test/py/ganeti\\.utils\\.io_unittest-runasroot\\.py") "") + ;; Disable the bash_completion test, as it requires the full + ;; bash instead of bash-minimal. + (("test/py/bash_completion\\.bash") + "") + ;; This test requires networking. + (("test/py/import-export_unittest\\.bash") + "")) + + ;; Many of the Makefile targets reset PYTHONPATH before running + ;; the Python interpreter, which does not work very well for us. + (substitute* "Makefile" + (("PYTHONPATH=") + (string-append "PYTHONPATH=" (getenv "PYTHONPATH") ":"))) + #t)) + (add-after 'build 'build-bash-completions + (lambda _ + (let ((orig-pythonpath (getenv "PYTHONPATH"))) + (setenv "PYTHONPATH" (string-append ".:" orig-pythonpath)) + (invoke "./autotools/build-bash-completion") + (setenv "PYTHONPATH" orig-pythonpath) + #t))) + (add-before 'check 'pre-check + (lambda* (#:key inputs #:allow-other-keys) + ;; Set TZDIR so that time zones are found. + (setenv "TZDIR" (string-append (assoc-ref inputs "tzdata") + "/share/zoneinfo")) + + ;; This test checks whether PYTHONPATH is untouched, and extends + ;; it to include test directories if so. Add an else branch for + ;; our modified PYTHONPATH, in order to prevent a confusing test + ;; failure where expired certificates are not cleaned because + ;; check-cert-expired is silently crashing. + (substitute* "test/py/ganeti-cleaner_unittest.bash" + (("then export PYTHONPATH=(.*)" all testpath) + (string-append all "else export PYTHONPATH=" + (getenv "PYTHONPATH") ":" testpath "\n"))) + + (substitute* "test/py/ganeti.utils.process_unittest.py" + ;; This test attempts to run an executable with + ;; RunCmd(..., reset_env=True), which fails because the default + ;; PATH from Constants.hs does not exist in the build container. + ((".*def testResetEnv.*" all) + (string-append " @unittest.skipIf(True, " + "\"cannot reset env in the build container\")\n" + all)) + + ;; XXX: Somehow this test fails in the build container, but + ;; works in 'guix environment -C', even without /bin/sh? + ((".*def testPidFile.*" all) + (string-append " @unittest.skipIf(True, " + "\"testPidFile fails in the build container\")\n" + all))) + + ;; XXX: Why are these links not added automatically. + (with-directory-excursion "test/hs" + (for-each (lambda (file) + (symlink "../../src/htools" file)) + '("hspace" "hscan" "hinfo" "hbal" "hroller" + "hcheck" "hail" "hsqueeze"))) + #t)) + (add-after 'install 'install-bash-completions + (lambda* (#:key outputs #:allow-other-keys) + (let* ((out (assoc-ref outputs "out")) + (compdir (string-append out "/etc/bash_completion.d"))) + (mkdir-p compdir) + (copy-file "doc/examples/bash_completion" + (string-append compdir "/ganeti")) + ;; The one file contains completions for many different + ;; executables. Create symlinks for found completions. + (with-directory-excursion compdir + (for-each + (lambda (prog) (symlink "ganeti" prog)) + (call-with-input-file "ganeti" + (lambda (port) + (let loop ((line (read-line port)) + (progs '())) + (if (eof-object? line) + progs + (if (string-prefix? "complete" line) + (loop (read-line port) + ;; Extract "prog" from lines of the form: + ;; "complete -F _prog -o filenames prog". + ;; Note that 'burnin' is listed with the + ;; absolute file name, which is why we + ;; run everything through 'basename'. + (cons (basename (car (reverse (string-split + line #\ )))) + progs)) + (loop (read-line port) progs)))))))) + #t))) + ;; Wrap all executables with PYTHONPATH. We can't borrow the phase + ;; from python-build-system because we also need to wrap the scripts + ;; in $out/lib/ganeti such as "node-daemon-setup". + (add-after 'install 'wrap + (lambda* (#:key inputs outputs #:allow-other-keys) + (let* ((out (assoc-ref outputs "out")) + (sbin (string-append out "/sbin")) + (lib (string-append out "/lib")) + (python (assoc-ref inputs "python")) + (major+minor (python-version python)) + (PYTHONPATH (string-append lib "/python" major+minor + "/site-packages:" + (getenv "PYTHONPATH")))) + (define (shell-script? file) + (call-with-ascii-input-file file + (lambda (port) + (let ((shebang (false-if-exception (read-line port)))) + (and shebang + (string-prefix? "#!" shebang) + (or (string-contains shebang "/bin/bash") + (string-contains shebang "/bin/sh"))))))) + + (define (wrap? file) + ;; Do not wrap shell scripts because some are meant to be + ;; sourced, which breaks if they are wrapped. We do wrap + ;; the Haskell executables because some call out to Python + ;; directly. + (and (executable-file? file) + (not (symbolic-link? file)) + (not (shell-script? file)))) + + (for-each (lambda (file) + (wrap-program file + `("PYTHONPATH" ":" prefix (,PYTHONPATH)))) + (filter wrap? + (append (find-files (string-append lib "/ganeti")) + (find-files sbin)))) + #t)))))) + (native-inputs + `(("haskell" ,ghc) + ("cabal" ,cabal-install) + ("m4" ,m4) + + ;; These inputs are necessary to bootstrap the package, because we + ;; have patched the build system. + ("autoconf" ,autoconf) + ("automake" ,automake) + + ;; Test dependencies. + ("fakeroot" ,fakeroot) + ("ghc-temporary" ,ghc-temporary) + ("ghc-test-framework" ,ghc-test-framework) + ("ghc-test-framework-hunit" ,ghc-test-framework-hunit) + ("ghc-test-framework-quickcheck2" ,ghc-test-framework-quickcheck2) + ("python-mock" ,python-mock) + ("python-pyyaml" ,python-pyyaml) + ("openssh" ,openssh) + ("procps" ,procps) + ("shelltestrunner" ,shelltestrunner) + ("tzdata" ,tzdata-for-tests))) + (inputs + `(("arping" ,iputils) ;must be the iputils version + ("curl" ,curl) + ("fping" ,fping) + ("iproute2" ,iproute) + ("ndisc6" ,ndisc6) + ("socat" ,socat) + ("qemu" ,qemu-minimal) ;for qemu-img + ("ghc-attoparsec" ,ghc-attoparsec) + ("ghc-base64-bytestring" ,ghc-base64-bytestring) + ("ghc-cryptonite" ,ghc-cryptonite) + ("ghc-curl" ,ghc-curl) + ("ghc-hinotify" ,ghc-hinotify) + ("ghc-hslogger" ,ghc-hslogger) + ("ghc-json" ,ghc-json) + ("ghc-lens" ,ghc-lens) + ("ghc-lifted-base" ,ghc-lifted-base) + ("ghc-network" ,ghc-network) + ("ghc-old-time" ,ghc-old-time) + ("ghc-psqueue" ,ghc-psqueue) + ("ghc-regex-pcre" ,ghc-regex-pcre) + ("ghc-utf8-string" ,ghc-utf8-string) + ("ghc-zlib" ,ghc-zlib) + + ;; For the optional metadata daemon. + ("ghc-snap-core" ,ghc-snap-core) + ("ghc-snap-server" ,ghc-snap-server) + + ("python" ,python) + ("python-pyopenssl" ,python-pyopenssl) + ("python-simplejson" ,python-simplejson) + ("python-pyparsing" ,python-pyparsing) + ("python-pyinotify" ,python-pyinotify) + ("python-pycurl" ,python-pycurl) + ("python-bitarray" ,python-bitarray) + ("python-paramiko" ,python-paramiko) + ("python-psutil" ,python-psutil))) + (home-page "http://www.ganeti.org/") + (synopsis "Cluster-based virtual machine management system") + (description + "Ganeti is a virtual machine management tool built on top of existing +virtualization technologies such as Xen or KVM. Once installed, Ganeti +assumes management of the virtual instances (Xen DomU). Ganeti controls: + +@itemize @bullet +@item Disk creation management; +@item Operating system installation for instances (in co-operation with +OS-specific install scripts); and +@item Startup, shutdown, and failover between physical systems. +@end itemize + +Ganeti is designed to facilitate cluster management of virtual servers and +to provide fast and simple recovery after physical failures, using +commodity hardware.") + (license license:bsd-2))) + (define-public libosinfo (package (name "libosinfo") -- 2.27.0