1{
2 lib,
3 stdenv,
4 runCommand,
5 fetchurl,
6 fetchFromGitHub,
7 fetchPypi,
8 fetchpatch2,
9
10 # Build time
11 autoconf,
12 automake,
13 cmake,
14 ensureNewerSourcesHook,
15 fmt,
16 git,
17 libtool,
18 makeWrapper,
19 nasm,
20 pkg-config,
21 which,
22 openssl,
23
24 # Tests
25 nixosTests,
26
27 # Runtime dependencies
28 arrow-cpp,
29 babeltrace,
30 # Note when trying to upgrade boost:
31 # * When upgrading Ceph, it's recommended to check which boost version Ceph uses on Fedora,
32 # and default to that.
33 # * The version that Ceph downloads if `-DWITH_SYSTEM_BOOST:BOOL=ON` is not given
34 # is declared in `cmake/modules/BuildBoost.cmake` line `set(boost_version ...)`.
35 #
36 # If you want to upgrade to boost >= 1.86, you need a Ceph version that
37 # has this PR in:
38 # https://github.com/ceph/ceph/pull/61312
39 boost183,
40 bzip2,
41 cryptsetup,
42 cunit,
43 e2fsprogs,
44 doxygen,
45 gperf,
46 graphviz,
47 gnugrep,
48 gtest,
49 icu,
50 kmod,
51 libcap,
52 libcap_ng,
53 libnbd,
54 libnl,
55 libxml2,
56 lmdb,
57 lttng-ust,
58 lua,
59 lvm2,
60 lz4,
61 oath-toolkit,
62 openldap,
63 parted,
64 python311, # to get an idea which Python versions are supported by Ceph, see upstream `do_cmake.sh` (see `PYBUILD=` variable)
65 rdkafka,
66 rocksdb,
67 snappy,
68 openssh,
69 sqlite,
70 utf8proc,
71 xfsprogs,
72 zlib,
73 zstd,
74
75 # Dependencies of overridden Python dependencies, hopefully we can remove these soon.
76 rustPlatform,
77
78 # Optional Dependencies
79 curl ? null,
80 expat ? null,
81 fuse ? null,
82 libatomic_ops ? null,
83 libedit ? null,
84 libs3 ? null,
85 yasm ? null,
86
87 # Mallocs
88 gperftools ? null,
89 jemalloc ? null,
90
91 # Crypto Dependencies
92 cryptopp ? null,
93 nspr ? null,
94 nss ? null,
95
96 # Linux Only Dependencies
97 linuxHeaders,
98 util-linux,
99 libuuid,
100 udev,
101 keyutils,
102 rdma-core,
103 rabbitmq-c,
104 libaio ? null,
105 libxfs ? null,
106 liburing ? null,
107 zfs ? null,
108 ...
109}:
110
111# We must have one crypto library
112assert cryptopp != null || (nss != null && nspr != null);
113
114let
115 shouldUsePkg =
116 pkg: if pkg != null && lib.meta.availableOn stdenv.hostPlatform pkg then pkg else null;
117
118 optYasm = shouldUsePkg yasm;
119 optExpat = shouldUsePkg expat;
120 optCurl = shouldUsePkg curl;
121 optFuse = shouldUsePkg fuse;
122 optLibedit = shouldUsePkg libedit;
123 optLibatomic_ops = shouldUsePkg libatomic_ops;
124 optLibs3 = shouldUsePkg libs3;
125
126 optJemalloc = shouldUsePkg jemalloc;
127 optGperftools = shouldUsePkg gperftools;
128
129 optCryptopp = shouldUsePkg cryptopp;
130 optNss = shouldUsePkg nss;
131 optNspr = shouldUsePkg nspr;
132
133 optLibaio = shouldUsePkg libaio;
134 optLibxfs = shouldUsePkg libxfs;
135 optZfs = shouldUsePkg zfs;
136
137 # Downgrade rocksdb, 7.10 breaks ceph
138 rocksdb' = rocksdb.overrideAttrs {
139 version = "7.9.2";
140 src = fetchFromGitHub {
141 owner = "facebook";
142 repo = "rocksdb";
143 rev = "refs/tags/v7.9.2";
144 hash = "sha256-5P7IqJ14EZzDkbjaBvbix04ceGGdlWBuVFH/5dpD5VM=";
145 };
146 };
147
148 hasRadosgw = optExpat != null && optCurl != null && optLibedit != null;
149
150 # Malloc implementation (can be jemalloc, tcmalloc or null)
151 malloc = if optJemalloc != null then optJemalloc else optGperftools;
152
153 # We prefer nss over cryptopp
154 cryptoStr =
155 if optNss != null && optNspr != null then
156 "nss"
157 else if optCryptopp != null then
158 "cryptopp"
159 else
160 "none";
161
162 cryptoLibsMap = {
163 nss = [
164 optNss
165 optNspr
166 ];
167 cryptopp = [ optCryptopp ];
168 none = [ ];
169 };
170
171 getMeta = description: {
172 homepage = "https://ceph.io/en/";
173 inherit description;
174 license = with lib.licenses; [
175 lgpl21
176 gpl2Only
177 bsd3
178 mit
179 publicDomain
180 ];
181 maintainers = with lib.maintainers; [
182 adev
183 ak
184 johanot
185 krav
186 nh2
187 benaryorg
188 ];
189 platforms = [
190 "x86_64-linux"
191 "aarch64-linux"
192 ];
193 };
194
195 ceph-common =
196 with python.pkgs;
197 buildPythonPackage {
198 pname = "ceph-common";
199 format = "setuptools";
200 inherit src version;
201
202 sourceRoot = "ceph-${version}/src/python-common";
203
204 propagatedBuildInputs = [
205 pyyaml
206 ];
207
208 nativeCheckInputs = [
209 pytestCheckHook
210 ];
211
212 disabledTests = [
213 # requires network access
214 "test_valid_addr"
215 ];
216
217 meta = getMeta "Ceph common module for code shared by manager modules";
218 };
219
220 # Watch out for python <> boost compatibility
221 python = python311.override {
222 self = python;
223 packageOverrides =
224 self: super:
225 let
226 bcryptOverrideVersion = "4.0.1";
227 in
228 {
229 # Ceph does not support the following yet:
230 # * `bcrypt` > 4.0
231 # * `cryptography` > 40
232 # See:
233 # * https://github.com/NixOS/nixpkgs/pull/281858#issuecomment-1899358602
234 # * Upstream issue: https://tracker.ceph.com/issues/63529
235 # > Python Sub-Interpreter Model Used by ceph-mgr Incompatible With Python Modules Based on PyO3
236 # * Moved to issue: https://tracker.ceph.com/issues/64213
237 # > MGR modules incompatible with later PyO3 versions - PyO3 modules may only be initialized once per interpreter process
238
239 bcrypt = super.bcrypt.overridePythonAttrs (old: rec {
240 pname = "bcrypt";
241 version = bcryptOverrideVersion;
242 src = fetchPypi {
243 inherit pname version;
244 hash = "sha256-J9N1kDrIJhz+QEf2cJ0W99GNObHskqr3KvmJVSplDr0=";
245 };
246 cargoRoot = "src/_bcrypt";
247 cargoDeps = rustPlatform.fetchCargoVendor {
248 inherit
249 pname
250 version
251 src
252 cargoRoot
253 ;
254 hash = "sha256-8PyCgh/rUO8uynzGdgylAsb5k55dP9fCnf40UOTCR/M=";
255 };
256 });
257
258 # We pin the older `cryptography` 40 here;
259 # this also forces us to pin other packages, see below
260 cryptography = self.callPackage ./old-python-packages/cryptography.nix { };
261
262 # This is the most recent version of `pyopenssl` that's still compatible with `cryptography` 40.
263 # See https://github.com/NixOS/nixpkgs/pull/281858#issuecomment-1899358602
264 # and https://github.com/pyca/pyopenssl/blob/d9752e44127ba36041b045417af8a0bf16ec4f1e/CHANGELOG.rst#2320-2023-05-30
265 pyopenssl = super.pyopenssl.overridePythonAttrs (old: rec {
266 version = "23.1.1";
267 src = fetchPypi {
268 pname = "pyOpenSSL";
269 inherit version;
270 hash = "sha256-hBSYub7GFiOxtsR+u8AjZ8B9YODhlfGXkIF/EMyNsLc=";
271 };
272 disabledTests = old.disabledTests or [ ] ++ [
273 "test_export_md5_digest"
274 ];
275 disabledTestPaths = old.disabledTestPaths or [ ] ++ [
276 "tests/test_ssl.py"
277 ];
278 propagatedBuildInputs = old.propagatedBuildInputs or [ ] ++ [
279 self.flaky
280 ];
281 # hack: avoid building docs due to incompatibility with current sphinx
282 nativeBuildInputs = [ openssl ]; # old.nativeBuildInputs but without sphinx*
283 outputs = lib.filter (o: o != "doc") old.outputs;
284 });
285
286 # This is the most recent version of `trustme` that's still compatible with `cryptography` 40.
287 # See https://github.com/NixOS/nixpkgs/issues/359723
288 # and https://github.com/python-trio/trustme/commit/586f7759d5c27beb44da60615a71848eb2a5a490
289 trustme = self.callPackage ./old-python-packages/trustme.nix { };
290
291 fastapi = super.fastapi.overridePythonAttrs (old: {
292 # Flaky test:
293 # ResourceWarning: Unclosed <MemoryObjectSendStream>
294 # Unclear whether it's flaky in general or only in this overridden package set.
295 doCheck = false;
296 });
297
298 # Ceph does not support `kubernetes` >= 19, see:
299 # https://github.com/NixOS/nixpkgs/pull/281858#issuecomment-1900324090
300 kubernetes = super.kubernetes.overridePythonAttrs (old: rec {
301 version = "18.20.0";
302 src = fetchFromGitHub {
303 owner = "kubernetes-client";
304 repo = "python";
305 rev = "v${version}";
306 sha256 = "1sawp62j7h0yksmg9jlv4ik9b9i1a1w9syywc9mv8x89wibf5ql1";
307 fetchSubmodules = true;
308 };
309 });
310
311 };
312 };
313
314 boost' = boost183.override {
315 enablePython = true;
316 inherit python;
317 };
318
319 # TODO: split this off in build and runtime environment
320 ceph-python-env = python.withPackages (
321 ps: with ps; [
322 ceph-common
323
324 # build time
325 cython_0
326
327 # debian/control
328 bcrypt
329 cherrypy
330 influxdb
331 jinja2
332 kubernetes
333 natsort
334 numpy
335 pecan
336 prettytable
337 pyjwt
338 pyopenssl
339 python-dateutil
340 pyyaml
341 requests
342 routes
343 scikit-learn
344 scipy
345 setuptools
346 sphinx
347 virtualenv
348 werkzeug
349
350 # src/cephadm/zipapp-reqs.txt
351 markupsafe
352
353 # src/pybind/mgr/requirements-required.txt
354 cryptography
355 jsonpatch
356
357 # src/tools/cephfs/shell/setup.py
358 cmd2
359 colorama
360 ]
361 );
362 inherit (ceph-python-env.python) sitePackages;
363
364 version = "19.2.3";
365 src = fetchurl {
366 url = "https://download.ceph.com/tarballs/ceph-${version}.tar.gz";
367 hash = "sha256-zlgp28C81SZbaFJ4yvQk4ZgYz4K/aZqtcISTO8LscSU=";
368 };
369in
370rec {
371 ceph = stdenv.mkDerivation {
372 pname = "ceph";
373 inherit src version;
374
375 patches = [
376 ./boost-1.85.patch
377
378 (fetchpatch2 {
379 name = "ceph-boost-1.86-uuid.patch";
380 url = "https://github.com/ceph/ceph/commit/01306208eac492ee0e67bff143fc32d0551a2a6f.patch?full_index=1";
381 hash = "sha256-OnDrr72inzGXXYxPFQevsRZImSvI0uuqFHqtFU2dPQE=";
382 })
383
384 # See:
385 # * <https://github.com/boostorg/python/issues/394>
386 # * <https://aur.archlinux.org/cgit/aur.git/commit/?h=ceph&id=8c5cc7d8deec002f7596b6d0860859a0a718f12b>
387 # * <https://github.com/ceph/ceph/pull/60999>
388 ./boost-1.86-PyModule.patch
389 ];
390
391 nativeBuildInputs = [
392 autoconf # `autoreconf` is called, e.g. for `qatlib_ext`
393 automake # `aclocal` is called, e.g. for `qatlib_ext`
394 cmake
395 fmt
396 git
397 makeWrapper
398 libtool # used e.g. for `qatlib_ext`
399 nasm
400 pkg-config
401 python
402 python.pkgs.python # for the toPythonPath function
403 python.pkgs.wrapPython
404 which
405 (ensureNewerSourcesHook { year = "1980"; })
406 # for building docs/man-pages presumably
407 doxygen
408 graphviz
409 ];
410
411 buildInputs =
412 cryptoLibsMap.${cryptoStr}
413 ++ [
414 arrow-cpp
415 babeltrace
416 boost'
417 bzip2
418 # Adding `ceph-python-env` here adds the env's `site-packages` to `PYTHONPATH` during the build.
419 # This is important, otherwise the build system may not find the Python deps and then
420 # silently skip installing ceph-volume and other Ceph python tools.
421 ceph-python-env
422 cryptsetup
423 cunit
424 e2fsprogs # according to `debian/control` file, `ceph-volume` is supposed to use it
425 gperf
426 gtest
427 icu
428 libcap
429 libnbd
430 libnl
431 libxml2
432 lmdb
433 lttng-ust
434 lua
435 lvm2 # according to `debian/control` file, e.g. `pvs` command used by `src/ceph-volume/ceph_volume/api/lvm.py`
436 lz4
437 malloc
438 oath-toolkit
439 openldap
440 optLibatomic_ops
441 optLibs3
442 optYasm
443 parted # according to `debian/control` file, used by `src/ceph-volume/ceph_volume/util/disk.py`
444 rdkafka
445 rocksdb'
446 snappy
447 openssh # according to `debian/control` file, `ssh` command used by `cephadm`
448 sqlite
449 utf8proc
450 xfsprogs # according to `debian/control` file, `ceph-volume` is supposed to use it
451 zlib
452 zstd
453 ]
454 ++ lib.optionals stdenv.hostPlatform.isLinux [
455 keyutils
456 libcap_ng
457 liburing
458 libuuid
459 linuxHeaders
460 optLibaio
461 optLibxfs
462 optZfs
463 rabbitmq-c
464 rdma-core
465 udev
466 util-linux
467 ]
468 ++ lib.optionals hasRadosgw [
469 optCurl
470 optExpat
471 optFuse
472 optLibedit
473 ];
474
475 # Picked up, amongst others, by `wrapPythonPrograms`.
476 pythonPath = [
477 ceph-python-env
478 "${placeholder "out"}/${ceph-python-env.sitePackages}"
479 ];
480
481 # * `unset AS` because otherwise the Ceph CMake build errors with
482 # configure: error: No modern nasm or yasm found as required. Nasm should be v2.11.01 or later (v2.13 for AVX512) and yasm should be 1.2.0 or later.
483 # because the code at
484 # https://github.com/intel/isa-l/blob/633add1b569fe927bace3960d7c84ed9c1b38bb9/configure.ac#L99-L191
485 # doesn't even consider using `nasm` or `yasm` but instead uses `$AS`
486 # from `gcc-wrapper`.
487 # (Ceph's error message is extra confusing, because it says
488 # `No modern nasm or yasm found` when in fact it found e.g. `nasm`
489 # but then uses `$AS` instead.
490 # * replace /sbin and /bin based paths with direct nix store paths
491 # * increase the `command` buffer size since 2 nix store paths cannot fit within 128 characters
492 preConfigure = ''
493 unset AS
494
495 substituteInPlace src/common/module.c \
496 --replace "char command[128];" "char command[256];" \
497 --replace "/sbin/modinfo" "${kmod}/bin/modinfo" \
498 --replace "/sbin/modprobe" "${kmod}/bin/modprobe" \
499 --replace "/bin/grep" "${gnugrep}/bin/grep"
500
501 # Patch remount to use full path to mount(8), otherwise ceph-fuse fails when run
502 # from a systemd unit for example.
503 substituteInPlace src/client/fuse_ll.cc \
504 --replace-fail "mount -i -o remount" "${util-linux}/bin/mount -i -o remount"
505
506 # The install target needs to be in PYTHONPATH for "*.pth support" check to succeed
507 export PYTHONPATH=$PYTHONPATH:$lib/${sitePackages}:$out/${sitePackages}
508 patchShebangs src/
509 '';
510
511 cmakeFlags = [
512 "-DCMAKE_INSTALL_DATADIR=${placeholder "lib"}/lib"
513
514 "-DWITH_CEPHFS_SHELL:BOOL=ON"
515 "-DWITH_SYSTEMD:BOOL=OFF"
516 # `WITH_JAEGER` requires `thrift` as a depenedncy (fine), but the build fails with:
517 # CMake Error at src/opentelemetry-cpp-stamp/opentelemetry-cpp-build-Release.cmake:49 (message):
518 # Command failed: 2
519 #
520 # 'make' 'opentelemetry_trace' 'opentelemetry_exporter_jaeger_trace'
521 #
522 # See also
523 #
524 # /build/ceph-18.2.0/build/src/opentelemetry-cpp/src/opentelemetry-cpp-stamp/opentelemetry-cpp-build-*.log
525 # and that file contains:
526 # /build/ceph-18.2.0/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/TUDPTransport.cc: In member function 'virtual void opentelemetry::v1::exporter::jaeger::TUDPTransport::close()':
527 # /build/ceph-18.2.0/src/jaegertracing/opentelemetry-cpp/exporters/jaeger/src/TUDPTransport.cc:71:7: error: '::close' has not been declared; did you mean 'pclose'?
528 # 71 | ::THRIFT_CLOSESOCKET(socket_);
529 # | ^~~~~~~~~~~~~~~~~~
530 # Looks like `close()` is somehow not included.
531 # But the relevant code is already removed in `open-telemetry` 1.10: https://github.com/open-telemetry/opentelemetry-cpp/pull/2031
532 # So it's probably not worth trying to fix that for this Ceph version,
533 # and instead just disable Ceph's Jaeger support.
534 "-DWITH_JAEGER:BOOL=OFF"
535 "-DWITH_TESTS:BOOL=OFF"
536
537 # Use our own libraries, where possible
538 "-DWITH_SYSTEM_ARROW:BOOL=ON" # Only used if other options enable Arrow support.
539 "-DWITH_SYSTEM_BOOST:BOOL=ON"
540 "-DWITH_SYSTEM_GTEST:BOOL=ON"
541 "-DWITH_SYSTEM_ROCKSDB:BOOL=ON"
542 "-DWITH_SYSTEM_UTF8PROC:BOOL=ON"
543 "-DWITH_SYSTEM_ZSTD:BOOL=ON"
544
545 # Use our own python libraries too, see:
546 # https://github.com/NixOS/nixpkgs/pull/344993#issuecomment-2391046329
547 "-DCEPHADM_BUNDLED_DEPENDENCIES=none"
548
549 # TODO breaks with sandbox, tries to download stuff with npm
550 "-DWITH_MGR_DASHBOARD_FRONTEND:BOOL=OFF"
551 # WITH_XFS has been set default ON from Ceph 16, keeping it optional in nixpkgs for now
552 ''-DWITH_XFS=${if optLibxfs != null then "ON" else "OFF"}''
553 ]
554 ++ lib.optional stdenv.hostPlatform.isLinux "-DWITH_SYSTEM_LIBURING=ON";
555
556 preBuild =
557 # The legacy-option-headers target is not correctly empbedded in the build graph.
558 # It also contains some internal race conditions that we work around by building with `-j 1`.
559 # Upstream discussion for additional context at https://tracker.ceph.com/issues/63402.
560 ''
561 cmake --build . --target legacy-option-headers -j 1
562 '';
563
564 postFixup = ''
565 wrapPythonPrograms
566 wrapProgram $out/bin/ceph-mgr --prefix PYTHONPATH ":" "$(toPythonPath ${placeholder "out"}):$(toPythonPath ${ceph-python-env})"
567
568 # Test that ceph-volume exists since the build system has a tendency to
569 # silently drop it with misconfigurations.
570 test -f $out/bin/ceph-volume
571 '';
572
573 outputs = [
574 "out"
575 "lib"
576 "dev"
577 "doc"
578 "man"
579 ];
580
581 doCheck = false; # uses pip to install things from the internet
582
583 # Takes 7+h to build with 2 cores.
584 requiredSystemFeatures = [ "big-parallel" ];
585
586 meta = getMeta "Distributed storage system";
587
588 passthru = {
589 inherit version;
590 inherit python; # to be able to test our overridden packages above individually with `nix-build -A`
591 tests = {
592 inherit (nixosTests)
593 ceph-multi-node
594 ceph-single-node
595 ceph-single-node-bluestore
596 ceph-single-node-bluestore-dmcrypt
597 ;
598 };
599 };
600 };
601
602 ceph-client =
603 runCommand "ceph-client-${version}"
604 {
605 meta = getMeta "Tools needed to mount Ceph's RADOS Block Devices/Cephfs";
606 }
607 ''
608 mkdir -p $out/{bin,etc,${sitePackages},share/bash-completion/completions}
609 cp -r ${ceph}/bin/{ceph,.ceph-wrapped,rados,rbd,rbdmap} $out/bin
610 cp -r ${ceph}/bin/ceph-{authtool,conf,dencoder,rbdnamer,syn} $out/bin
611 cp -r ${ceph}/bin/rbd-replay* $out/bin
612 cp -r ${ceph}/sbin/mount.ceph $out/bin
613 cp -r ${ceph}/sbin/mount.fuse.ceph $out/bin
614 ln -s bin $out/sbin
615 cp -r ${ceph}/${sitePackages}/* $out/${sitePackages}
616 cp -r ${ceph}/etc/bash_completion.d $out/share/bash-completion/completions
617 # wrapPythonPrograms modifies .ceph-wrapped, so lets just update its paths
618 substituteInPlace $out/bin/ceph --replace ${ceph} $out
619 substituteInPlace $out/bin/.ceph-wrapped --replace ${ceph} $out
620 '';
621}