1import ./make-test-python.nix (
2 { pkgs, ... }:
3 let
4 listenPort = 30123;
5 testString = "It works!";
6 mkCreateSmallFileService =
7 {
8 path,
9 loop ? false,
10 }:
11 {
12 script = ''
13 ${pkgs.coreutils}/bin/dd if=/dev/zero of=${path} bs=1K count=100
14 ${pkgs.lib.optionalString loop "${pkgs.util-linux}/bin/losetup --find ${path}"}
15 '';
16 serviceConfig = {
17 Type = "oneshot";
18 };
19 wantedBy = [ "multi-user.target" ];
20 before = [ "nbd-server.service" ];
21 };
22 in
23 {
24 name = "nbd";
25
26 nodes = {
27 server =
28 { config, pkgs, ... }:
29 {
30 # Create some small files of zeros to use as the ndb disks
31 ## `vault-pub.disk` is accessible from any IP
32 systemd.services.create-pub-file = mkCreateSmallFileService { path = "/vault-pub.disk"; };
33 ## `vault-priv.disk` is accessible only from localhost.
34 ## It's also a loopback device to test exporting /dev/...
35 systemd.services.create-priv-file = mkCreateSmallFileService {
36 path = "/vault-priv.disk";
37 loop = true;
38 };
39 ## `aaa.disk` is just here because "[aaa]" sorts before
40 ## "[generic]" lexicographically, and nbd-server breaks if
41 ## "[generic]" isn't the first section.
42 systemd.services.create-aaa-file = mkCreateSmallFileService { path = "/aaa.disk"; };
43
44 # Needed only for nbd-client used in the tests.
45 environment.systemPackages = [ pkgs.nbd ];
46
47 # Open the nbd port in the firewall
48 networking.firewall.allowedTCPPorts = [ listenPort ];
49
50 # Run the nbd server and expose the small file created above
51 services.nbd.server = {
52 enable = true;
53 exports = {
54 aaa = {
55 path = "/aaa.disk";
56 };
57 vault-pub = {
58 path = "/vault-pub.disk";
59 };
60 vault-priv = {
61 path = "/dev/loop0";
62 allowAddresses = [
63 "127.0.0.1"
64 "::1"
65 ];
66 };
67 };
68 listenAddress = "0.0.0.0";
69 listenPort = listenPort;
70 };
71 };
72
73 client =
74 { config, pkgs, ... }:
75 {
76 programs.nbd.enable = true;
77 };
78 };
79
80 testScript = ''
81 testString = "${testString}"
82
83 start_all()
84 server.wait_for_open_port(${toString listenPort})
85
86 # Client: Connect to the server, write a small string to the nbd disk, and cleanly disconnect
87 client.succeed("nbd-client server ${toString listenPort} /dev/nbd0 -name vault-pub -persist")
88 client.succeed(f"echo '{testString}' | dd of=/dev/nbd0 conv=notrunc")
89 client.succeed("nbd-client -d /dev/nbd0")
90
91 # Server: Check that the string written by the client is indeed in the file
92 foundString = server.succeed(f"dd status=none if=/vault-pub.disk count={len(testString)}")[:len(testString)]
93 if foundString != testString:
94 raise Exception(f"Read the wrong string from nbd disk. Expected: '{testString}'. Found: '{foundString}'")
95
96 # Client: Fail to connect to the private disk
97 client.fail("nbd-client server ${toString listenPort} /dev/nbd0 -name vault-priv -persist")
98
99 # Server: Successfully connect to the private disk
100 server.succeed("nbd-client localhost ${toString listenPort} /dev/nbd0 -name vault-priv -persist")
101 server.succeed(f"echo '{testString}' | dd of=/dev/nbd0 conv=notrunc")
102 foundString = server.succeed(f"dd status=none if=/dev/loop0 count={len(testString)}")[:len(testString)]
103 if foundString != testString:
104 raise Exception(f"Read the wrong string from nbd disk. Expected: '{testString}'. Found: '{foundString}'")
105 server.succeed("nbd-client -d /dev/nbd0")
106
107 # Server: Successfully connect to the aaa disk
108 server.succeed("nbd-client localhost ${toString listenPort} /dev/nbd0 -name aaa -persist")
109 server.succeed(f"echo '{testString}' | dd of=/dev/nbd0 conv=notrunc")
110 foundString = server.succeed(f"dd status=none if=/aaa.disk count={len(testString)}")[:len(testString)]
111 if foundString != testString:
112 raise Exception(f"Read the wrong string from nbd disk. Expected: '{testString}'. Found: '{foundString}'")
113 server.succeed("nbd-client -d /dev/nbd0")
114 '';
115 }
116)