at 25.11-pre 11 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9let 10 cfg = config.services.waagent; 11 12 # Format for waagent.conf 13 settingsFormat = { 14 type = 15 with types; 16 let 17 singleAtom = 18 (nullOr (oneOf [ 19 bool 20 str 21 int 22 float 23 ])) 24 // { 25 description = "atom (bool, string, int or float) or null"; 26 }; 27 atom = either singleAtom (listOf singleAtom) // { 28 description = singleAtom.description + " or a list of them"; 29 }; 30 in 31 attrsOf ( 32 either atom (attrsOf atom) 33 // { 34 description = atom.description + " or an attribute set of them"; 35 } 36 ); 37 generate = 38 name: value: 39 let 40 # Transform non-attribute values 41 transform = 42 x: 43 # Transform bool to "y" or "n" 44 if (isBool x) then 45 (if x then "y" else "n") 46 # Concatenate list items with comma 47 else if (isList x) then 48 concatStringsSep "," (map transform x) 49 else 50 toString x; 51 52 # Convert to format of waagent.conf 53 recurse = 54 path: value: 55 if builtins.isAttrs value then 56 pipe value [ 57 (mapAttrsToList (k: v: recurse (path ++ [ k ]) v)) 58 concatLists 59 ] 60 else 61 [ 62 { 63 name = concatStringsSep "." path; 64 inherit value; 65 } 66 ]; 67 convert = 68 attrs: 69 pipe (recurse [ ] attrs) [ 70 # Filter out null values and emoty lists 71 (filter (kv: kv.value != null && kv.value != [ ])) 72 # Transform to Key=Value form, then concatenate 73 (map (kv: "${kv.name}=${transform kv.value}")) 74 (concatStringsSep "\n") 75 ]; 76 in 77 pkgs.writeText name (convert value); 78 }; 79 80 settingsType = types.submodule { 81 freeformType = settingsFormat.type; 82 options = { 83 Provisioning = { 84 Enable = mkOption { 85 type = types.bool; 86 default = !config.services.cloud-init.enable; 87 defaultText = literalExpression "!config.services.cloud-init.enable"; 88 description = '' 89 Whether to enable provisioning functionality in the agent. 90 91 If provisioning is disabled, SSH host and user keys in the image are preserved 92 and configuration in the Azure provisioning API is ignored. 93 94 Set to `false` if cloud-init is used for provisioning tasks. 95 ''; 96 }; 97 98 Agent = mkOption { 99 type = types.enum [ 100 "auto" 101 "waagent" 102 "cloud-init" 103 "disabled" 104 ]; 105 default = "auto"; 106 description = '' 107 Which provisioning agent to use. 108 ''; 109 }; 110 }; 111 112 ResourceDisk = { 113 Format = mkOption { 114 type = types.bool; 115 default = false; 116 description = '' 117 If set to `true`, waagent formats and mounts the resource disk that the platform provides, 118 unless the file system type in `ResourceDisk.FileSystem` is set to `ntfs`. 119 The agent makes a single Linux partition (ID 83) available on the disk. 120 This partition isn't formatted if it can be successfully mounted. 121 122 This configuration has no effect if resource disk is managed by cloud-init. 123 ''; 124 }; 125 126 FileSystem = mkOption { 127 type = types.str; 128 default = "ext4"; 129 description = '' 130 The file system type for the resource disk. 131 If the string is `X`, then `mkfs.X` should be present in the environment. 132 You can add additional filesystem packages using `services.waagent.extraPackages`. 133 134 This configuration has no effect if resource disk is managed by cloud-init. 135 ''; 136 }; 137 138 MountPoint = mkOption { 139 type = types.str; 140 default = "/mnt/resource"; 141 description = '' 142 This option specifies the path at which the resource disk is mounted. 143 The resource disk is a temporary disk and might be emptied when the VM is deprovisioned. 144 145 This configuration has no effect if resource disk is managed by cloud-init. 146 ''; 147 }; 148 149 MountOptions = mkOption { 150 type = with types; listOf str; 151 default = [ ]; 152 example = [ 153 "nodev" 154 "nosuid" 155 ]; 156 description = '' 157 This option specifies disk mount options to be passed to the `mount -o` command. 158 For more information, see the {manpage}`mount(8)` manual page. 159 ''; 160 }; 161 162 EnableSwap = mkOption { 163 type = types.bool; 164 default = false; 165 description = '' 166 If enabled, the agent creates a swap file (`/swapfile`) on the resource disk 167 and adds it to the system swap space. 168 169 This configuration has no effect if resource disk is managed by cloud-init. 170 ''; 171 }; 172 173 SwapSizeMB = mkOption { 174 type = types.int; 175 default = 0; 176 description = '' 177 Specifies the size of the swap file in megabytes. 178 179 This configuration has no effect if resource disk is managed by cloud-init. 180 ''; 181 }; 182 }; 183 184 Logs.Verbose = lib.mkOption { 185 type = types.bool; 186 default = false; 187 description = '' 188 If you set this option, log verbosity is boosted. 189 Waagent logs to `/var/log/waagent.log` and uses the system logrotate functionality to rotate logs. 190 ''; 191 }; 192 193 OS = { 194 EnableRDMA = lib.mkOption { 195 type = types.bool; 196 default = false; 197 description = '' 198 If enabled, the agent attempts to install and then load an RDMA kernel driver 199 that matches the version of the firmware on the underlying hardware. 200 ''; 201 }; 202 203 RootDeviceScsiTimeout = lib.mkOption { 204 type = types.nullOr types.int; 205 default = 300; 206 description = '' 207 Configures the SCSI timeout in seconds on the OS disk and data drives. 208 If set to `null`, the system defaults are used. 209 ''; 210 }; 211 }; 212 213 HttpProxy = { 214 Host = lib.mkOption { 215 type = types.nullOr types.str; 216 default = null; 217 description = '' 218 If you set http proxy, waagent will use is proxy to access the Internet. 219 ''; 220 }; 221 222 Port = lib.mkOption { 223 type = types.nullOr types.int; 224 default = null; 225 description = '' 226 If you set http proxy, waagent will use this proxy to access the Internet. 227 ''; 228 }; 229 }; 230 231 AutoUpdate.UpdateToLatestVersion = lib.mkOption { 232 type = types.bool; 233 default = false; 234 description = '' 235 Whether or not to enable auto-update of the Extension Handler. 236 ''; 237 }; 238 }; 239 }; 240in 241{ 242 options.services.waagent = { 243 enable = lib.mkEnableOption "Windows Azure Linux Agent"; 244 245 package = lib.mkPackageOption pkgs "waagent" { }; 246 247 extraPackages = lib.mkOption { 248 default = [ ]; 249 description = '' 250 Additional packages to add to the waagent {env}`PATH`. 251 ''; 252 example = lib.literalExpression "[ pkgs.powershell ]"; 253 type = lib.types.listOf lib.types.package; 254 }; 255 256 settings = lib.mkOption { 257 type = settingsType; 258 default = { }; 259 description = '' 260 The waagent.conf configuration, see https://learn.microsoft.com/en-us/azure/virtual-machines/extensions/agent-linux for documentation. 261 ''; 262 }; 263 }; 264 265 config = lib.mkIf cfg.enable { 266 assertions = [ 267 { 268 assertion = (cfg.settings.HttpProxy.Host != null) -> (cfg.settings.HttpProxy.Port != null); 269 message = "Option services.waagent.settings.HttpProxy.Port must be set if services.waagent.settings.HttpProxy.Host is set."; 270 } 271 ]; 272 273 boot.initrd.kernelModules = [ "ata_piix" ]; 274 networking.firewall.allowedUDPPorts = [ 68 ]; 275 276 services.udev.packages = with pkgs; [ waagent ]; 277 278 boot.initrd.services.udev = with pkgs; { 279 # Provide waagent-shipped udev rules in initrd too. 280 packages = [ waagent ]; 281 # udev rules shell out to chmod, cut and readlink, which are all 282 # provided by pkgs.coreutils, which is in services.udev.path, but not 283 # boot.initrd.services.udev.binPackages. 284 binPackages = [ coreutils ]; 285 }; 286 287 networking.dhcpcd.persistent = true; 288 289 services.logrotate = { 290 enable = true; 291 settings."/var/log/waagent.log" = { 292 compress = true; 293 frequency = "monthly"; 294 rotate = 6; 295 }; 296 }; 297 298 # Write settings to /etc/waagent.conf 299 environment.etc."waagent.conf".source = settingsFormat.generate "waagent.conf" cfg.settings; 300 301 systemd.targets.provisioned = { 302 description = "Services Requiring Azure VM provisioning to have finished"; 303 }; 304 305 systemd.services.consume-hypervisor-entropy = { 306 description = "Consume entropy in ACPI table provided by Hyper-V"; 307 308 wantedBy = [ 309 "sshd.service" 310 "waagent.service" 311 ]; 312 before = [ 313 "sshd.service" 314 "waagent.service" 315 ]; 316 317 path = [ pkgs.coreutils ]; 318 script = '' 319 echo "Fetching entropy..." 320 cat /sys/firmware/acpi/tables/OEM0 > /dev/random 321 ''; 322 serviceConfig.Type = "oneshot"; 323 serviceConfig.RemainAfterExit = true; 324 serviceConfig.StandardError = "journal+console"; 325 serviceConfig.StandardOutput = "journal+console"; 326 }; 327 328 systemd.services.waagent = { 329 wantedBy = [ "multi-user.target" ]; 330 after = [ 331 "network-online.target" 332 ] ++ lib.optionals config.services.cloud-init.enable [ "cloud-init.service" ]; 333 wants = [ 334 "network-online.target" 335 "sshd.service" 336 "sshd-keygen.service" 337 ]; 338 339 path = 340 with pkgs; 341 [ 342 e2fsprogs 343 bash 344 findutils 345 gnugrep 346 gnused 347 iproute2 348 iptables 349 openssh 350 openssl 351 parted 352 353 # for hostname 354 nettools 355 # for pidof 356 procps 357 # for useradd, usermod 358 shadow 359 360 util-linux # for (u)mount, fdisk, sfdisk, mkswap 361 # waagent's Microsoft.CPlat.Core.RunCommandLinux needs lsof 362 lsof 363 ] 364 ++ cfg.extraPackages; 365 description = "Windows Azure Agent Service"; 366 unitConfig.ConditionPathExists = "/etc/waagent.conf"; 367 serviceConfig = { 368 ExecStart = "${lib.getExe cfg.package} -daemon"; 369 Type = "simple"; 370 Restart = "always"; 371 Slice = "azure.slice"; 372 CPUAccounting = true; 373 MemoryAccounting = true; 374 }; 375 }; 376 377 # waagent will generate files under /etc/sudoers.d during provisioning 378 security.sudo.extraConfig = '' 379 #includedir /etc/sudoers.d 380 ''; 381 }; 382}