cleverca22
2/5/2016 - 4:01 PM

iscsi

iscsi

{ 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; };
    };
}
{ config, stdenv, pkgs, lib, ... }:

with lib;

{
  options = {
    services.iscsid = {
      enable = mkOption {
        type = types.bool;
        default = false;
        description = "enable iscsid running on startup";
      };
    };
  };
  config = mkIf config.services.iscsid.enable {
    systemd.services.iscsid = {
      description = "iscsid daemon";
      wantedBy = [ "basic.target" ];
      preStart = "${config.system.sbin.modprobe}/bin/modprobe iscsi_tcp";
      serviceConfig = {
        ExecStart = "${pkgs.openiscsi}/bin/iscsid -f -c ${pkgs.openiscsi}/etc/iscsi/iscsid.conf -i ${pkgs.openiscsi}/etc/iscsi/initiatorname.iscsi";
        KillMode = "process";
        Restart = "on-success";
      };
    };
  };
}
{ 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
}
{ ... }:
{
  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; };
      };
    };
  };
}
{ ... }:
{
  fileSystems = {
    "/" = {
      device = "UUID=d230cc98-8f33-457f-9fee-b65558165188";
      fsType = "ext4";
      iscsi = {
        enable = true;
        host = "192.168.2.61";
        lun = "iqn.2001-04.com.c2d-nix3";
      };
    };
    "/boot" = { device = "UUID=DCF1-5AC3"; };
  };
  networking = {
    firewall.enable = false;
    interfaces.eth0.ipAddress = "192.168.2.30";
    interfaces.eth0.prefixLength = 24;
    defaultGateway = "192.168.2.1";
    nameservers = [ "192.168.2.61" ];
    dhcpcd.persistent = true;
  };
}