1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
# Test printing via CUPS.
import ./make-test-python.nix (
{ pkgs
, socket ? true # whether to use socket activation
, listenTcp ? true # whether to open port 631 on client
, ...
}:
let
inherit (pkgs) lib;
in
{
name = "printing";
meta = with lib.maintainers; {
maintainers = [ domenkozar matthewbauer ];
};
nodes.server = { ... }: {
services.printing = {
enable = true;
stateless = true;
startWhenNeeded = socket;
listenAddresses = [ "*:631" ];
defaultShared = true;
openFirewall = true;
extraConf = ''
<Location />
Order allow,deny
Allow from all
</Location>
'';
};
# Add a HP Deskjet printer connected via USB to the server.
hardware.printers.ensurePrinters = [{
name = "DeskjetLocal";
deviceUri = "usb://foobar/printers/foobar";
model = "drv:///sample.drv/deskjet.ppd";
}];
};
nodes.client = { lib, ... }: {
services.printing.enable = true;
services.printing.startWhenNeeded = socket;
services.printing.listenAddresses = lib.mkIf (!listenTcp) [];
# Add printer to the client as well, via IPP.
hardware.printers.ensurePrinters = [{
name = "DeskjetRemote";
deviceUri = "ipp://server/printers/DeskjetLocal";
model = "drv:///sample.drv/deskjet.ppd";
}];
hardware.printers.ensureDefaultPrinter = "DeskjetRemote";
};
testScript = ''
import os
import re
start_all()
with subtest("Make sure that cups is up on both sides and printers are set up"):
server.wait_for_unit("ensure-printers.service")
client.wait_for_unit("ensure-printers.service")
assert "scheduler is running" in client.succeed("lpstat -r")
with subtest("UNIX socket is used for connections"):
assert "/var/run/cups/cups.sock" in client.succeed("lpstat -H")
with subtest("HTTP server is available too"):
${lib.optionalString listenTcp ''client.succeed("curl --fail http://localhost:631/")''}
client.succeed(f"curl --fail http://{server.name}:631/")
server.fail(f"curl --fail --connect-timeout 2 http://{client.name}:631/")
with subtest("LP status checks"):
assert "DeskjetRemote accepting requests" in client.succeed("lpstat -a")
assert "DeskjetLocal accepting requests" in client.succeed(
f"lpstat -h {server.name}:631 -a"
)
client.succeed("cupsdisable DeskjetRemote")
out = client.succeed("lpq")
print(out)
assert re.search(
"DeskjetRemote is not ready.*no entries",
client.succeed("lpq"),
flags=re.DOTALL,
)
client.succeed("cupsenable DeskjetRemote")
assert re.match(
"DeskjetRemote is ready.*no entries", client.succeed("lpq"), flags=re.DOTALL
)
# Test printing various file types.
for file in [
"${pkgs.groff.doc}/share/doc/*/examples/mom/penguin.pdf",
"${pkgs.groff.doc}/share/doc/*/meref.ps",
"${pkgs.cups.out}/share/doc/cups/images/cups.png",
"${pkgs.pcre.doc}/share/doc/pcre/pcre.txt",
]:
file_name = os.path.basename(file)
with subtest(f"print {file_name}"):
# Print the file on the client.
print(client.succeed("lpq"))
client.succeed(f"lp {file}")
client.wait_until_succeeds(
f"lpq; lpq | grep -q -E 'active.*root.*{file_name}'"
)
# Ensure that a raw PCL file appeared in the server's queue
# (showing that the right filters have been applied). Of
# course, since there is no actual USB printer attached, the
# file will stay in the queue forever.
server.wait_for_file("/var/spool/cups/d*-001")
server.wait_until_succeeds(f"lpq -a | grep -q -E '{file_name}'")
# Delete the job on the client. It should disappear on the
# server as well.
client.succeed("lprm")
client.wait_until_succeeds("lpq -a | grep -q -E 'no entries'")
retry(lambda _: "no entries" in server.succeed("lpq -a"))
# The queue is empty already, so this should be safe.
# Otherwise, pairs of "c*"-"d*-001" files might persist.
server.execute("rm /var/spool/cups/*")
'';
})
|