1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.virtualisation.azure.agent;
8
9 waagent = with pkgs; stdenv.mkDerivation rec {
10 name = "waagent-2.0";
11 src = pkgs.fetchFromGitHub {
12 owner = "Azure";
13 repo = "WALinuxAgent";
14 rev = "1b3a8407a95344d9d12a2a377f64140975f1e8e4";
15 sha256 = "10byzvmpgrmr4d5mdn2kq04aapqb3sgr1admk13wjmy5cd6bwd2x";
16 };
17
18 patches = [ ./azure-agent-entropy.patch ];
19
20 buildInputs = [ makeWrapper python pythonPackages.wrapPython ];
21 runtimeDeps = [ findutils gnugrep gawk coreutils openssl openssh
22 nettools # for hostname
23 procps # for pidof
24 shadow # for useradd, usermod
25 utillinux # for (u)mount, fdisk, sfdisk, mkswap
26 parted
27 ];
28 pythonPath = [ pythonPackages.pyasn1 ];
29
30 configurePhase = false;
31 buildPhase = false;
32
33 installPhase = ''
34 substituteInPlace config/99-azure-product-uuid.rules \
35 --replace /bin/chmod "${coreutils}/bin/chmod"
36 mkdir -p $out/lib/udev/rules.d
37 cp config/*.rules $out/lib/udev/rules.d
38
39 mkdir -p $out/bin
40 cp waagent $out/bin/
41 chmod +x $out/bin/waagent
42
43 wrapProgram "$out/bin/waagent" \
44 --prefix PYTHONPATH : $PYTHONPATH \
45 --prefix PATH : "${makeBinPath runtimeDeps}"
46 '';
47 };
48
49 provisionedHook = pkgs.writeScript "provisioned-hook" ''
50 #!${pkgs.runtimeShell}
51 ${config.systemd.package}/bin/systemctl start provisioned.target
52 '';
53
54in
55
56{
57
58 ###### interface
59
60 options.virtualisation.azure.agent = {
61 enable = mkOption {
62 default = false;
63 description = "Whether to enable the Windows Azure Linux Agent.";
64 };
65 verboseLogging = mkOption {
66 default = false;
67 description = "Whether to enable verbose logging.";
68 };
69 mountResourceDisk = mkOption {
70 default = true;
71 description = "Whether the agent should format (ext4) and mount the resource disk to /mnt/resource.";
72 };
73 };
74
75 ###### implementation
76
77 config = mkIf cfg.enable {
78 assertions = [ {
79 assertion = pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64;
80 message = "Azure not currently supported on ${pkgs.stdenv.hostPlatform.system}";
81 } {
82 assertion = config.networking.networkmanager.enable == false;
83 message = "Windows Azure Linux Agent is not compatible with NetworkManager";
84 } ];
85
86 boot.initrd.kernelModules = [ "ata_piix" ];
87 networking.firewall.allowedUDPPorts = [ 68 ];
88
89
90 environment.etc."waagent.conf".text = ''
91 #
92 # Windows Azure Linux Agent Configuration
93 #
94
95 Role.StateConsumer=${provisionedHook}
96
97 # Enable instance creation
98 Provisioning.Enabled=y
99
100 # Password authentication for root account will be unavailable.
101 Provisioning.DeleteRootPassword=n
102
103 # Generate fresh host key pair.
104 Provisioning.RegenerateSshHostKeyPair=n
105
106 # Supported values are "rsa", "dsa" and "ecdsa".
107 Provisioning.SshHostKeyPairType=ed25519
108
109 # Monitor host name changes and publish changes via DHCP requests.
110 Provisioning.MonitorHostName=y
111
112 # Decode CustomData from Base64.
113 Provisioning.DecodeCustomData=n
114
115 # Execute CustomData after provisioning.
116 Provisioning.ExecuteCustomData=n
117
118 # Format if unformatted. If 'n', resource disk will not be mounted.
119 ResourceDisk.Format=${if cfg.mountResourceDisk then "y" else "n"}
120
121 # File system on the resource disk
122 # Typically ext3 or ext4. FreeBSD images should use 'ufs2' here.
123 ResourceDisk.Filesystem=ext4
124
125 # Mount point for the resource disk
126 ResourceDisk.MountPoint=/mnt/resource
127
128 # Respond to load balancer probes if requested by Windows Azure.
129 LBProbeResponder=y
130
131 # Enable logging to serial console (y|n)
132 # When stdout is not enough...
133 # 'y' if not set
134 Logs.Console=y
135
136 # Enable verbose logging (y|n)
137 Logs.Verbose=${if cfg.verboseLogging then "y" else "n"}
138
139 # Root device timeout in seconds.
140 OS.RootDeviceScsiTimeout=300
141 '';
142
143 services.udev.packages = [ waagent ];
144
145 networking.dhcpcd.persistent = true;
146
147 services.logrotate = {
148 enable = true;
149 config = ''
150 /var/log/waagent.log {
151 compress
152 monthly
153 rotate 6
154 notifempty
155 missingok
156 }
157 '';
158 };
159
160 systemd.targets.provisioned = {
161 description = "Services Requiring Azure VM provisioning to have finished";
162 };
163
164 systemd.services.consume-hypervisor-entropy =
165 { description = "Consume entropy in ACPI table provided by Hyper-V";
166
167 wantedBy = [ "sshd.service" "waagent.service" ];
168 before = [ "sshd.service" "waagent.service" ];
169 after = [ "local-fs.target" ];
170
171 path = [ pkgs.coreutils ];
172 script =
173 ''
174 echo "Fetching entropy..."
175 cat /sys/firmware/acpi/tables/OEM0 > /dev/random
176 '';
177 serviceConfig.Type = "oneshot";
178 serviceConfig.RemainAfterExit = true;
179 serviceConfig.StandardError = "journal+console";
180 serviceConfig.StandardOutput = "journal+console";
181 };
182
183 systemd.services.waagent = {
184 wantedBy = [ "multi-user.target" ];
185 after = [ "network-online.target" "sshd.service" ];
186 wants = [ "network-online.target" ];
187
188 path = [ pkgs.e2fsprogs pkgs.bash ];
189 description = "Windows Azure Agent Service";
190 unitConfig.ConditionPathExists = "/etc/waagent.conf";
191 serviceConfig = {
192 ExecStart = "${waagent}/bin/waagent -daemon";
193 Type = "simple";
194 };
195 };
196
197 };
198
199}