cleverca22
4/25/2016 - 4:14 PM

iscsi-boot.nix

{ config, lib, pkgs, ... }:

with lib;

let
  targetOpts = { name, config, ... }: {
    options = {
      name = mkOption {
        type = types.str;
      };
      backingStore = mkOption {
        type = types.str;
      };
      index = mkOption {
        type = types.int;
        description = "the index of the target, must be unique within the server";
      };
    };
    config = {
      name = mkDefault name;
    };
  };
      makeService = target: {
        name = target.name;
        value = {
          description = target.name+" auto-starter";
          wantedBy = [ "basic.target" ];
          partOf = [ "tgtd.service" ];
          script = ''
            ${pkgs.tgt}/bin/tgtadm --lld iscsi --op new --mode target --tid ${builtins.toString target.index} -T ${target.name}
            ${pkgs.tgt}/bin/tgtadm --lld iscsi --op new --mode logicalunit --tid ${builtins.toString target.index} --lun 1 -b ${target.backingStore}
            ${pkgs.tgt}/bin/tgtadm --lld iscsi --op bind --mode target --tid ${builtins.toString target.index} -I ALL # gives everybody access
          '';
          serviceConfig = {
            Type = "oneshot";
            RemainAfterExit = true;
            ExecStop = "${pkgs.tgt}/bin/tgtadm --lld iscsi --op delete --mode target --tid ${builtins.toString target.index}";
          };
        };
      };
in
{
  options = {
    services.tgtd = {
      enable = mkOption {
        type = types.bool;
        default = false;
        description = "enable tgtd running on startup";
      };
      targets = mkOption {
        default = [];
        type = types.loaOf types.optionSet;
        options = targetOpts;
      };
    };
  };
  config = let
      LUNs = builtins.listToAttrs (map makeService (attrValues config.services.tgtd.targets));
      tgtd = {
        description = "tgtd daemon";
        wantedBy = [ "basic.target" ];
        serviceConfig = {
          ExecStart = "${pkgs.tgt}/bin/tgtd -f --iscsi nop_interval=30 --iscsi nop_count=10";
          ExecStop = "${pkgs.coreutils}/bin/sleep 30 ; ${pkgs.tgt}/bin/tgtadm --op delete --mode system";
          KillMode = "process";
          Restart = "on-success";
        };
      };
    in
     mkIf config.services.tgtd.enable {
      systemd.services = LUNs // { tgtd = tgtd; };
    };
}
[root@nas:~]# cat /etc/nixos/nixcfg/nas.nix 
{ ... }:

let
  keys = import ./keys.nix;
in
{
...
  services = {
    tgtd = {
      enable = true;
      targets = {
        "iqn.2016-01.laptop-root" = { backingStore = "/dev/naspool/laptop-root"; index = 1; };
        "iqn.2016-02.windows-extra" = { backingStore = "/dev/naspool/windows-extra"; index = 2; };
      };
    };
  };
}
[root@nas:~]# nix-repl '<nixos>'
Welcome to Nix version 1.11.2. Type :? for help.

Loading ‘<nixos>’...
Added 6 variables.

nix-repl> config.systemd.services."iqn.2016-01.laptop-root".description
"iqn.2016-01.laptop-root auto-starter"

nix-repl> config.systemd.services."iqn.2016-01.laptop-root".serviceConfig.ExecStart
"/nix/store/r5jddxlql2hkahaphl06yqclh0ahp5zi-unit-script/bin/iqn.2016-01.laptop-root-start "

nix-repl> config.systemd.services."iqn.2016-01.laptop-root".script
"/nix/store/vkz0bkpki3ha7p5h0b9wav35lnn4aj4v-tgt-1.0.60/bin/tgtadm --lld iscsi --op new --mode target --tid 1 -T iqn.2016-01.laptop-root\n/nix/store/vkz0bkpki3ha7p5h0b9wav35lnn4aj4v-tgt-1.0.60/bin/tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun 1 -b /dev/naspool/laptop-root\n/nix/store/vkz0bkpki3ha7p5h0b9wav35lnn4aj4v-tgt-1.0.60/bin/tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL # gives everybody access\n"

{ lib, config, pkgs, ... }:

with lib;

let
  cfg = config.services.motion;
  camera_list = builtins.attrValues cfg.cameras;
  final_config = concatStringsSep "\n" ([ cfg.config ] ++ (map (cam: "thread ${cam}") thread_config_files));
  config_file = pkgs.writeText "motion.conf" final_config;
  thread_configs = (listToAttrs (map (cam: {
    name = "motion/${cam.name}.conf";
    value = { text = cfg.camera_common_config + "\n" + cam.config;};
  }) camera_list));
  thread_config_files = map (cam: pkgs.writeText "thread-${cam.name}.conf" (cfg.camera_common_config + "\n" + cam.config)) camera_list;
  camera_options = { name, config, ... }:
  {
    options = {
      name = mkOption { type = types.str; };
      config = mkOption { type = types.str; };
    };
    config = {
      name = mkDefault name;
    };
  };
in
{
  options = {
    services.motion = {
      enable = mkEnableOption "motion";
      config = mkOption {
        type = types.str;
        default = "";
        description = "the raw contents of motion.conf";
      };
      cameras = mkOption {
        type = types.loaOf types.optionSet;
        options = camera_options;
        default = [];
      };
      camera_common_config = mkOption {
        type = types.str;
        default = "";
        description = "lines inserted into every thread file";
      };
    };
  };
  config = mkIf config.services.motion.enable {
    systemd.services.motion = {
      description = builtins.trace "reading description" "Motion daemon";
      wantedBy = [ "basic.target" ];
      serviceConfig = {
        ExecStart = "${pkgs.motion}/bin/motion -c ${config_file}";
        KillMode = "process";
        Restart = "always";
        User = "motion";
      };
    };
    environment.etc = {
      "motion.conf".text = final_config;
    } // thread_configs;
    users.users = {
      motion = {
        uid = 1012;
      };
    };
  };
}
{ config, lib, pkgs, ... }:

with lib;

let
  cfg = config.boot.initrd.iscsi;
  fileSystems = attrValues config.fileSystems ++ config.swapDevices;
  iscsiDevs = filter (dev: dev.iscsi.enable) fileSystems;
  anyiscsi = fold (j: v: v || j.iscsi.enable) false iscsiDevs;
  iscsiOptions = {
    iscsi = {
      enable = mkOption { default = false; type = types.bool; description = "The block device is backed by iscsi, adds this device as a initrd entry"; };
      host = mkOption { example = "192.168.2.61"; type = types.string; description = "the iscsi target"; };
      lun = mkOption { example = "iqn.2015-01.com.example:san.img"; type = types.string; description = "the LUN to connect"; };
    };
  };
in
{
  options = {
    fileSystems = mkOption {
      options = [ iscsiOptions ];
    };
    swapDevices = mkOption {
      options = [ iscsiOptions ];
    };
    boot.initrd.iscsi = {
      initiatorName = mkOption {
        example = "iqn.2015-09.com.example:3255a7223b2";
        type = types.string;
        description = "the initiator name used when connecting";
      };

      netDev = mkOption {
        example = "eth0";
        type = types.string;
        description = "the network device the initrd will setup for iscsi";
      };
    }; # iscsi
  }; # options
  config = mkIf anyiscsi (
    let
      ipAddress = config.networking.interfaces.${cfg.netDev}.ipAddress;
      prefixLength = config.networking.interfaces.${cfg.netDev}.prefixLength;
      defaultGateway = config.networking.defaultGateway;
    in
    {
      boot.initrd = {
        kernelModules = [ "iscsi_tcp" ];
        availableKernelModules = [ "crc32c" ];
        preLVMCommands = ''
          export PATH=$PATH:${pkgs.openiscsi.iscsistart}/bin/
          ip link set ${cfg.netDev} up
          ip addr add ${ipAddress}/${toString prefixLength} dev ${cfg.netDev}
          # todo, make this optional
          ip route add via ${defaultGateway} dev ${cfg.netDev}
        '' + concatMapStrings (dev: "iscsistart -t ${dev.iscsi.lun} -a ${dev.iscsi.host} -i ${config.boot.initrd.iscsi.initiatorName} -g 0\n") iscsiDevs;
      }; # initrd
  }); # config
}