about summary refs log tree commit diff
path: root/nixpkgs/pkgs/tools/virtualization
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/tools/virtualization')
-rw-r--r--nixpkgs/pkgs/tools/virtualization/alpine-make-vm-image/default.nix37
-rw-r--r--nixpkgs/pkgs/tools/virtualization/amazon-ecs-cli/default.nix39
-rw-r--r--nixpkgs/pkgs/tools/virtualization/awsebcli/default.nix98
-rw-r--r--nixpkgs/pkgs/tools/virtualization/cloud-init/0001-add-nixos-support.patch127
-rw-r--r--nixpkgs/pkgs/tools/virtualization/cloud-init/default.nix139
-rw-r--r--nixpkgs/pkgs/tools/virtualization/cloudmonkey/default.nix24
-rw-r--r--nixpkgs/pkgs/tools/virtualization/cri-tools/default.nix47
-rw-r--r--nixpkgs/pkgs/tools/virtualization/distrobuilder/default.nix67
-rw-r--r--nixpkgs/pkgs/tools/virtualization/distrobuilder/generator.nix19
-rw-r--r--nixpkgs/pkgs/tools/virtualization/distrobuilder/nixos-generator.patch113
-rw-r--r--nixpkgs/pkgs/tools/virtualization/dockstarter/default.nix43
-rw-r--r--nixpkgs/pkgs/tools/virtualization/ec2-ami-tools/default.nix44
-rw-r--r--nixpkgs/pkgs/tools/virtualization/ec2-ami-tools/writable.patch23
-rw-r--r--nixpkgs/pkgs/tools/virtualization/ec2-api-tools/default.nix39
-rw-r--r--nixpkgs/pkgs/tools/virtualization/ec2instanceconnectcli/default.nix25
-rw-r--r--nixpkgs/pkgs/tools/virtualization/extra-container/default.nix40
-rw-r--r--nixpkgs/pkgs/tools/virtualization/google-compute-engine/default.nix63
-rw-r--r--nixpkgs/pkgs/tools/virtualization/google-guest-agent/default.nix66
-rw-r--r--nixpkgs/pkgs/tools/virtualization/google-guest-agent/disable-etc-mutation.patch54
-rw-r--r--nixpkgs/pkgs/tools/virtualization/google-guest-agent/fix-paths.patch43
-rw-r--r--nixpkgs/pkgs/tools/virtualization/google-guest-configs/default.nix52
-rw-r--r--nixpkgs/pkgs/tools/virtualization/google-guest-configs/fix-paths.patch61
-rw-r--r--nixpkgs/pkgs/tools/virtualization/google-guest-oslogin/default.nix57
-rw-r--r--nixpkgs/pkgs/tools/virtualization/govc/default.nix31
-rw-r--r--nixpkgs/pkgs/tools/virtualization/guestfs-tools/default.nix117
-rw-r--r--nixpkgs/pkgs/tools/virtualization/jumppad/default.nix30
-rw-r--r--nixpkgs/pkgs/tools/virtualization/kubevirt/default.nix54
-rw-r--r--nixpkgs/pkgs/tools/virtualization/linode-cli/default.nix86
-rw-r--r--nixpkgs/pkgs/tools/virtualization/linode-cli/remove-update-check.patch13
-rwxr-xr-xnixpkgs/pkgs/tools/virtualization/linode-cli/update.sh30
-rw-r--r--nixpkgs/pkgs/tools/virtualization/lxd-image-server/default.nix51
-rw-r--r--nixpkgs/pkgs/tools/virtualization/lxd-image-server/run.patch25
-rw-r--r--nixpkgs/pkgs/tools/virtualization/lxd-image-server/state.patch49
-rw-r--r--nixpkgs/pkgs/tools/virtualization/marathonctl/default.nix25
-rw-r--r--nixpkgs/pkgs/tools/virtualization/mininet/default.nix50
-rw-r--r--nixpkgs/pkgs/tools/virtualization/mkosi/default.nix134
-rw-r--r--nixpkgs/pkgs/tools/virtualization/multipass/cmake_no_fetch.patch32
-rw-r--r--nixpkgs/pkgs/tools/virtualization/multipass/cmake_warning.patch14
-rw-r--r--nixpkgs/pkgs/tools/virtualization/multipass/default.nix143
-rw-r--r--nixpkgs/pkgs/tools/virtualization/multipass/lxd_socket_path.patch13
-rw-r--r--nixpkgs/pkgs/tools/virtualization/nixos-container/default.nix38
-rw-r--r--nixpkgs/pkgs/tools/virtualization/nixos-container/nixos-container-completion.sh33
-rwxr-xr-xnixpkgs/pkgs/tools/virtualization/nixos-container/nixos-container.pl491
-rw-r--r--nixpkgs/pkgs/tools/virtualization/nixos-shell/default.nix31
-rw-r--r--nixpkgs/pkgs/tools/virtualization/onmetal-image/default.nix40
-rw-r--r--nixpkgs/pkgs/tools/virtualization/ovftool/default.nix150
-rw-r--r--nixpkgs/pkgs/tools/virtualization/ovftool/installCheckPhase.ovabin0 -> 75264 bytes
-rw-r--r--nixpkgs/pkgs/tools/virtualization/reg/default.nix42
-rw-r--r--nixpkgs/pkgs/tools/virtualization/rootlesskit/default.nix28
-rw-r--r--nixpkgs/pkgs/tools/virtualization/supermin/default.nix31
-rw-r--r--nixpkgs/pkgs/tools/virtualization/udocker/default.nix59
-rw-r--r--nixpkgs/pkgs/tools/virtualization/uefi-run/default.nix26
-rw-r--r--nixpkgs/pkgs/tools/virtualization/vpsfree-client/Gemfile5
-rw-r--r--nixpkgs/pkgs/tools/virtualization/vpsfree-client/Gemfile.lock58
-rw-r--r--nixpkgs/pkgs/tools/virtualization/vpsfree-client/default.nix18
-rw-r--r--nixpkgs/pkgs/tools/virtualization/vpsfree-client/gemset.nix233
-rw-r--r--nixpkgs/pkgs/tools/virtualization/xe-guest-utilities/default.nix49
-rw-r--r--nixpkgs/pkgs/tools/virtualization/xva-img/default.nix25
58 files changed, 3574 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/tools/virtualization/alpine-make-vm-image/default.nix b/nixpkgs/pkgs/tools/virtualization/alpine-make-vm-image/default.nix
new file mode 100644
index 000000000000..c06efa3617f0
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/alpine-make-vm-image/default.nix
@@ -0,0 +1,37 @@
+{ stdenv, lib, fetchFromGitHub, makeWrapper
+, apk-tools, coreutils, dosfstools, e2fsprogs, findutils, gnugrep, gnused, kmod, qemu-utils
+, rsync, util-linux
+}:
+
+stdenv.mkDerivation rec {
+  pname = "alpine-make-vm-image";
+  version = "0.12.0";
+
+  src = fetchFromGitHub {
+    owner = "alpinelinux";
+    repo = "alpine-make-vm-image";
+    rev = "v${version}";
+    sha256 = "sha256-IV/MC6dnvWMs5akM6Zw3TBzWPpsLL9FllK0sOV9MRGY=";
+  };
+
+  nativeBuildInputs = [ makeWrapper ];
+
+  dontBuild = true;
+  makeFlags = [ "PREFIX=$(out)" ];
+
+  postInstall = ''
+    wrapProgram $out/bin/alpine-make-vm-image --set PATH ${lib.makeBinPath [
+      apk-tools coreutils dosfstools e2fsprogs findutils gnugrep gnused kmod qemu-utils
+      rsync util-linux
+    ]}
+  '';
+
+  meta = with lib; {
+    homepage = "https://github.com/alpinelinux/alpine-make-vm-image";
+    description = "Make customized Alpine Linux disk image for virtual machines";
+    maintainers = with maintainers; [ qyliss ];
+    license = licenses.mit;
+    platforms = platforms.unix;
+    mainProgram = "alpine-make-vm-image";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/amazon-ecs-cli/default.nix b/nixpkgs/pkgs/tools/virtualization/amazon-ecs-cli/default.nix
new file mode 100644
index 000000000000..16b43012e260
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/amazon-ecs-cli/default.nix
@@ -0,0 +1,39 @@
+{ lib, stdenv, fetchurl}:
+
+stdenv.mkDerivation rec {
+  pname = "amazon-ecs-cli";
+  version = "1.21.0";
+
+  src =
+    if stdenv.hostPlatform.system == "x86_64-linux" then
+      fetchurl {
+        url = "https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-linux-amd64-v${version}";
+        sha256 = "sEHwhirU2EYwtBRegiIvN4yr7VKtmy7e6xx5gZOkuY0=";
+      }
+    else if stdenv.hostPlatform.system == "x86_64-darwin" then
+      fetchurl {
+        url = "https://s3.amazonaws.com/amazon-ecs-cli/ecs-cli-darwin-amd64-v${version}";
+        sha256 = "1viala49sifpcmgn3jw24h5bkrlm4ffadjiqagbxj3lr0r78i9nm";
+      }
+    else throw "Architecture not supported";
+
+  dontUnpack = true;
+
+  installPhase =
+    ''
+      mkdir -p $out/bin
+      cp $src $out/bin/ecs-cli
+      chmod +x $out/bin/ecs-cli
+    '';  # */
+
+  meta = with lib; {
+    homepage = "https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_CLI.html";
+    description = "The Amazon ECS command line interface";
+    longDescription = "The Amazon Elastic Container Service (Amazon ECS) command line interface (CLI) provides high-level commands to simplify creating, updating, and monitoring clusters and tasks from a local development environment.";
+    sourceProvenance = with sourceTypes; [ binaryNativeCode ];
+    license = licenses.asl20;
+    maintainers = with maintainers; [ Scriptkiddi ];
+    platforms = [ "x86_64-linux" "x86_64-darwin" ];
+    mainProgram = "ecs-cli";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/awsebcli/default.nix b/nixpkgs/pkgs/tools/virtualization/awsebcli/default.nix
new file mode 100644
index 000000000000..594b7a30b2ed
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/awsebcli/default.nix
@@ -0,0 +1,98 @@
+{ lib, python3, fetchFromGitHub, glibcLocales, git }:
+let
+  changeVersion = overrideFunc: version: hash: overrideFunc (oldAttrs: rec {
+    inherit version;
+    src = oldAttrs.src.override {
+      inherit version hash;
+    };
+  });
+
+  localPython = python3.override {
+    self = localPython;
+    packageOverrides = self: super: {
+      cement = changeVersion super.cement.overridePythonAttrs "2.8.2" "sha256-h2XtBSwGHXTk0Bia3cM9Jo3lRMohmyWdeXdB9yXkItI=";
+    };
+  };
+in
+with localPython.pkgs; buildPythonApplication rec {
+  pname = "awsebcli";
+  version = "3.20.10";
+  format = "setuptools";
+
+  src = fetchFromGitHub {
+    owner = "aws";
+    repo = "aws-elastic-beanstalk-cli";
+    rev = "refs/tags/${version}";
+    hash = "sha256-4JZx0iTMyrPHbuS3zlhpiWnenAQO5eSBJbPHUizLhYo=";
+  };
+
+  postPatch = ''
+    # https://github.com/aws/aws-elastic-beanstalk-cli/pull/469
+    substituteInPlace setup.py --replace "scripts=['bin/eb']," ""
+  '';
+
+  nativeBuildInputs = [
+    pythonRelaxDepsHook
+  ];
+
+  buildInputs = [
+    glibcLocales
+  ];
+
+  propagatedBuildInputs = [
+    blessed
+    botocore
+    cement
+    colorama
+    pathspec
+    pyyaml
+    future
+    requests
+    semantic-version
+    setuptools
+    tabulate
+    termcolor
+    websocket-client
+  ];
+
+  pythonRelaxDeps = [
+    "botocore"
+    "colorama"
+    "pathspec"
+    "PyYAML"
+    "six"
+    "termcolor"
+  ];
+
+  nativeCheckInputs = [
+    pytestCheckHook
+    pytest-socket
+    mock
+    git
+  ];
+
+  pytestFlagsArray = [
+    "tests/unit"
+  ];
+
+  disabledTests = [
+    # Needs docker installed to run.
+    "test_local_run"
+    "test_local_run__with_arguments"
+
+    # Needs access to the user's ~/.ssh directory.
+    "test_generate_and_upload_keypair__exit_code_0"
+    "test_generate_and_upload_keypair__exit_code_1"
+    "test_generate_and_upload_keypair__exit_code_is_other_than_1_and_0"
+    "test_generate_and_upload_keypair__ssh_keygen_not_present"
+  ];
+
+  meta = with lib; {
+    homepage = "https://aws.amazon.com/elasticbeanstalk/";
+    description = "A command line interface for Elastic Beanstalk";
+    changelog = "https://github.com/aws/aws-elastic-beanstalk-cli/blob/${version}/CHANGES.rst";
+    maintainers = with maintainers; [ eqyiel kirillrdy ];
+    license = licenses.asl20;
+    mainProgram = "eb";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/cloud-init/0001-add-nixos-support.patch b/nixpkgs/pkgs/tools/virtualization/cloud-init/0001-add-nixos-support.patch
new file mode 100644
index 000000000000..2e293321ac02
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/cloud-init/0001-add-nixos-support.patch
@@ -0,0 +1,127 @@
+diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
+index 7b83df8d..6d04de1a 100644
+--- a/cloudinit/distros/__init__.py
++++ b/cloudinit/distros/__init__.py
+@@ -75,6 +75,7 @@ OSFAMILIES = {
+     ],
+     "openeuler": ["openeuler"],
+     "OpenCloudOS": ["OpenCloudOS", "TencentOS"],
++    "nixos": ["nixos"],
+ }
+ 
+ LOG = logging.getLogger(__name__)
+diff --git a/cloudinit/distros/nixos.py b/cloudinit/distros/nixos.py
+new file mode 100644
+index 00000000..954e564b
+--- /dev/null
++++ b/cloudinit/distros/nixos.py
+@@ -0,0 +1,109 @@
++# vi: ts=4 expandtab
++#
++#    Copyright (C) 2012 Canonical Ltd.
++#    Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
++#    Copyright (C) 2012 Yahoo! Inc.
++#
++#    Author: Scott Moser <scott.moser@canonical.com>
++#    Author: Juerg Haefliger <juerg.haefliger@hp.com>
++#    Author: Joshua Harlow <harlowja@yahoo-inc.com>
++#
++#    This program is free software: you can redistribute it and/or modify
++#    it under the terms of the GNU General Public License version 3, as
++#    published by the Free Software Foundation.
++#
++#    This program is distributed in the hope that it will be useful,
++#    but WITHOUT ANY WARRANTY; without even the implied warranty of
++#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++#    GNU General Public License for more details.
++#
++#    You should have received a copy of the GNU General Public License
++#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++
++import os
++
++from cloudinit import distros
++from cloudinit import helpers
++from cloudinit import log as logging
++from cloudinit import util
++from cloudinit import atomic_helper
++
++from cloudinit.distros.parsers.hostname import HostnameConf
++from cloudinit.net import dhcp
++
++LOG = logging.getLogger(__name__)
++
++class Distro(distros.Distro):
++
++    def __init__(self, name, cfg, paths):
++        distros.Distro.__init__(self, name, cfg, paths)
++        # This will be used to restrict certain
++        # calls from repeatly happening (when they
++        # should only happen say once per instance...)
++        self._runner = helpers.Runners(paths)
++        self.usr_lib_exec = os.path.join(os.path.dirname(__file__),
++                                         "../../../../../libexec")
++        self.osfamily = 'nixos'
++        self.dhcp_client_priority = [
++            dhcp.Udhcpc,
++            dhcp.IscDhclient,
++            dhcp.Dhcpcd,
++        ]
++
++    def _select_hostname(self, hostname, fqdn):
++        # Prefer the short hostname over the long
++        # fully qualified domain name
++        if not hostname:
++            return fqdn
++        return hostname
++
++    def _write_hostname(self, your_hostname, out_fn):
++        conf = None
++        try:
++            # Try to update the previous one
++            # so lets see if we can read it first.
++            conf = self._read_hostname_conf(out_fn)
++        except IOError:
++            pass
++        if not conf:
++            conf = HostnameConf('')
++        conf.set_hostname(your_hostname)
++        atomic_helper.write_file(out_fn, str(conf).encode("utf-8"))
++
++    def _read_system_hostname(self):
++        sys_hostname = self._read_hostname(self.hostname_conf_fn)
++        return (self.hostname_conf_fn, sys_hostname)
++
++    def _read_hostname_conf(self, filename):
++        conf = HostnameConf(util.load_file(filename))
++        conf.parse()
++        return conf
++
++    def _read_hostname(self, filename, default=None):
++        hostname = None
++        try:
++            conf = self._read_hostname_conf(filename)
++            hostname = conf.hostname
++        except IOError:
++            pass
++        if not hostname:
++            return default
++        return hostname
++
++    def _write_network(self, settings):
++        raise NotImplementedError()
++
++    def apply_locale(self, locale, out_fn=None):
++        raise NotImplementedError()
++
++    def install_packages(self, pkglist):
++        raise NotImplementedError()
++
++    def package_command(self, command, args=None, pkgs=None):
++        pass
++
++    def set_timezone(self, tz):
++        pass
++
++    def update_package_sources(self):
++        pass
diff --git a/nixpkgs/pkgs/tools/virtualization/cloud-init/default.nix b/nixpkgs/pkgs/tools/virtualization/cloud-init/default.nix
new file mode 100644
index 000000000000..72a67045f56b
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/cloud-init/default.nix
@@ -0,0 +1,139 @@
+{ lib
+, nixosTests
+, buildPythonApplication
+, cloud-utils
+, dmidecode
+, fetchFromGitHub
+, iproute2
+, openssh
+, python3
+, shadow
+, systemd
+, coreutils
+, gitUpdater
+, busybox
+, procps
+}:
+
+python3.pkgs.buildPythonApplication rec {
+  pname = "cloud-init";
+  version = "23.3.3";
+  namePrefix = "";
+
+  src = fetchFromGitHub {
+    owner = "canonical";
+    repo = "cloud-init";
+    rev = "refs/tags/${version}";
+    hash = "sha256-49UvGrv40hyR3A2BndlQKwQqCC1ZaLm97IUKNW12sJo=";
+  };
+
+  patches = [
+    ./0001-add-nixos-support.patch
+  ];
+
+  prePatch = ''
+    substituteInPlace setup.py \
+      --replace /lib/systemd $out/lib/systemd
+
+    substituteInPlace cloudinit/net/networkd.py \
+      --replace '["/usr/sbin", "/bin"]' '["/usr/sbin", "/bin", "${iproute2}/bin", "${systemd}/bin"]'
+
+    substituteInPlace tests/unittests/test_net_activators.py \
+      --replace '["/usr/sbin", "/bin"]' \
+        '["/usr/sbin", "/bin", "${iproute2}/bin", "${systemd}/bin"]'
+
+    substituteInPlace tests/unittests/cmd/test_clean.py \
+      --replace "/bin/bash" "/bin/sh"
+  '';
+
+  postInstall = ''
+    install -D -m755 ./tools/write-ssh-key-fingerprints $out/libexec/write-ssh-key-fingerprints
+    for i in $out/libexec/*; do
+      wrapProgram $i --prefix PATH : "${lib.makeBinPath [ openssh ]}"
+    done
+  '';
+
+  propagatedBuildInputs = with python3.pkgs; [
+    configobj
+    jinja2
+    jsonpatch
+    jsonschema
+    netifaces
+    oauthlib
+    pyserial
+    pyyaml
+    requests
+  ];
+
+  nativeCheckInputs = with python3.pkgs; [
+    pytestCheckHook
+    httpretty
+    dmidecode
+    # needed for tests; at runtime we rather want the setuid wrapper
+    passlib
+    shadow
+    responses
+    pytest-mock
+    coreutils
+    procps
+  ];
+
+  makeWrapperArgs = [
+    "--prefix PATH : ${lib.makeBinPath [ dmidecode cloud-utils.guest busybox ]}/bin"
+  ];
+
+  disabledTests = [
+    # tries to create /var
+    "test_dhclient_run_with_tmpdir"
+    "test_dhcp_client_failover"
+    # clears path and fails because mkdir is not found
+    "test_path_env_gets_set_from_main"
+    # fails to find cat
+    "test_subp_combined_stderr_stdout"
+    # tries to read from /etc/ca-certificates.conf while inside the sandbox
+    "test_handler_ca_certs"
+    "TestRemoveDefaultCaCerts"
+    # Doesn't work in the sandbox
+    "TestEphemeralDhcpNoNetworkSetup"
+    "TestHasURLConnectivity"
+    "TestReadFileOrUrl"
+    "TestConsumeUserDataHttp"
+    # Chef Omnibus
+    "TestInstallChefOmnibus"
+    # https://github.com/canonical/cloud-init/pull/893
+    "TestGetPackageMirrorInfo"
+    # Disable failing VMware and PuppetAio tests
+    "test_get_data_iso9660_with_network_config"
+    "test_get_data_vmware_guestinfo_with_network_config"
+    "test_get_host_info"
+    "test_no_data_access_method"
+    "test_install_with_collection"
+    "test_install_with_custom_url"
+    "test_install_with_default_arguments"
+    "test_install_with_no_cleanup"
+    "test_install_with_version"
+  ];
+
+  preCheck = ''
+    # TestTempUtils.test_mkdtemp_default_non_root does not like TMPDIR=/build
+    export TMPDIR=/tmp
+  '';
+
+  pythonImportsCheck = [
+    "cloudinit"
+  ];
+
+  passthru = {
+    tests = { inherit (nixosTests) cloud-init cloud-init-hostname; };
+    updateScript = gitUpdater { ignoredVersions = ".ubuntu.*"; };
+  };
+
+  meta = with lib; {
+    homepage = "https://github.com/canonical/cloud-init";
+    description = "Provides configuration and customization of cloud instance";
+    changelog = "https://github.com/canonical/cloud-init/raw/${version}/ChangeLog";
+    license = with licenses; [ asl20 gpl3Plus ];
+    maintainers = with maintainers; [ illustris jfroche ];
+    platforms = platforms.all;
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/cloudmonkey/default.nix b/nixpkgs/pkgs/tools/virtualization/cloudmonkey/default.nix
new file mode 100644
index 000000000000..b7a9ec918cb9
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/cloudmonkey/default.nix
@@ -0,0 +1,24 @@
+{ buildGoModule, fetchFromGitHub, lib }:
+
+buildGoModule rec {
+  pname = "cloudmonkey";
+  version = "6.3.0";
+
+  src = fetchFromGitHub {
+    owner = "apache";
+    repo = "cloudstack-cloudmonkey";
+    rev = version;
+    sha256 = "sha256-FoouZ2udtZ68W5p32Svr8yAn0oBdWMupn1LEzqY04Oc=";
+  };
+
+  vendorHash = null;
+
+  meta = with lib; {
+    description = "CLI for Apache CloudStack";
+    homepage = "https://github.com/apache/cloudstack-cloudmonkey";
+    license = [ licenses.asl20 ];
+    maintainers = [ maintainers.womfoo ];
+    mainProgram = "cloudstack-cloudmonkey";
+  };
+
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/cri-tools/default.nix b/nixpkgs/pkgs/tools/virtualization/cri-tools/default.nix
new file mode 100644
index 000000000000..b57ce3abb168
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/cri-tools/default.nix
@@ -0,0 +1,47 @@
+{ lib
+, buildGoModule
+, fetchFromGitHub
+, installShellFiles
+}:
+
+buildGoModule rec {
+  pname = "cri-tools";
+  version = "1.28.0";
+
+  src = fetchFromGitHub {
+    owner = "kubernetes-sigs";
+    repo = pname;
+    rev = "v${version}";
+    sha256 = "sha256-inw4bPeObMlwtgFLR/8+tqRKTkcViZeEFZ1MOm0HYI4=";
+  };
+
+  vendorHash = null;
+
+  doCheck = false;
+
+  nativeBuildInputs = [ installShellFiles ];
+
+  buildPhase = ''
+    runHook preBuild
+    make binaries VERSION=${version}
+    runHook postBuild
+  '';
+
+  installPhase = ''
+    runHook preInstall
+    make install BINDIR=$out/bin
+
+    for shell in bash fish zsh; do
+      $out/bin/crictl completion $shell > crictl.$shell
+      installShellCompletion crictl.$shell
+    done
+    runHook postInstall
+  '';
+
+  meta = with lib; {
+    description = "CLI and validation tools for Kubelet Container Runtime Interface (CRI)";
+    homepage = "https://github.com/kubernetes-sigs/cri-tools";
+    license = licenses.asl20;
+    maintainers = with maintainers; [ ] ++ teams.podman.members;
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/distrobuilder/default.nix b/nixpkgs/pkgs/tools/virtualization/distrobuilder/default.nix
new file mode 100644
index 000000000000..e6b867139165
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/distrobuilder/default.nix
@@ -0,0 +1,67 @@
+{ lib
+, pkg-config
+, buildGoModule
+, fetchFromGitHub
+, makeWrapper
+, coreutils
+, gnupg
+, gnutar
+, squashfsTools
+, debootstrap
+, callPackage
+, nixosTests
+}:
+
+let
+  bins = [
+    coreutils
+    gnupg
+    gnutar
+    squashfsTools
+    debootstrap
+  ];
+in
+buildGoModule rec {
+  pname = "distrobuilder";
+  version = "3.0";
+
+  vendorHash = "sha256-pFrEkZnrcx0d3oM1klQrNHH+MiLvO4V1uFQdE0kXUqM=";
+
+  src = fetchFromGitHub {
+    owner = "lxc";
+    repo = "distrobuilder";
+    rev = "refs/tags/distrobuilder-${version}";
+    sha256 = "sha256-JfME9VaqaQnrhnzhSLGUy9uU+tki1hXdnwqBUD/5XH0=";
+    fetchSubmodules = false;
+  };
+
+  buildInputs = bins;
+
+
+  # tests require a local keyserver (mkg20001/nixpkgs branch distrobuilder-with-tests) but gpg is currently broken in tests
+  doCheck = false;
+
+  nativeBuildInputs = [
+    pkg-config
+    makeWrapper
+  ] ++ bins;
+
+  postInstall = ''
+    wrapProgram $out/bin/distrobuilder --prefix PATH ":" ${lib.makeBinPath bins}
+  '';
+
+  passthru = {
+    tests.incus = nixosTests.incus.container;
+
+    generator = callPackage ./generator.nix { inherit src version; };
+  };
+
+  meta = {
+    description = "System container image builder for LXC and LXD";
+    homepage = "https://github.com/lxc/distrobuilder";
+    license = lib.licenses.asl20;
+    maintainers = with lib.maintainers; [ megheaiulian adamcstephens ];
+    platforms = lib.platforms.linux;
+    mainProgram = "distrobuilder";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/distrobuilder/generator.nix b/nixpkgs/pkgs/tools/virtualization/distrobuilder/generator.nix
new file mode 100644
index 000000000000..e514a7df2e08
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/distrobuilder/generator.nix
@@ -0,0 +1,19 @@
+{ stdenvNoCC, lib, src, version, makeWrapper, coreutils, findutils, gnugrep, systemd }:
+
+stdenvNoCC.mkDerivation {
+  name = "distrobuilder-nixos-generator";
+
+  inherit src version;
+
+  patches = [
+    ./nixos-generator.patch
+  ];
+
+  dontBuild = true;
+  nativeBuildInputs = [ makeWrapper ];
+
+  installPhase = ''
+    install -D -m 0555 distrobuilder/lxc.generator $out/lib/systemd/system-generators/lxc
+    wrapProgram $out/lib/systemd/system-generators/lxc --prefix PATH : ${lib.makeBinPath [coreutils findutils gnugrep systemd]}:${systemd}/lib/systemd
+  '';
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/distrobuilder/nixos-generator.patch b/nixpkgs/pkgs/tools/virtualization/distrobuilder/nixos-generator.patch
new file mode 100644
index 000000000000..6194f33e1918
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/distrobuilder/nixos-generator.patch
@@ -0,0 +1,113 @@
+diff --git a/distrobuilder/lxc.generator b/distrobuilder/lxc.generator
+index 0ad81d1..69dbfe7 100644
+--- a/distrobuilder/lxc.generator
++++ b/distrobuilder/lxc.generator
+@@ -25,16 +25,6 @@ is_incus_vm() {
+ 	[ -e /dev/virtio-ports/org.linuxcontainers.incus ]
+ }
+ 
+-# is_in_path succeeds if the given file exists in on of the paths
+-is_in_path() {
+-	# Don't use $PATH as that may not include all relevant paths
+-	for path in /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin; do
+-		[ -e "${path}/$1" ] && return 0
+-	done
+-
+-	return 1
+-}
+-
+ ## Fix functions
+ # fix_ro_paths avoids udevd issues with /sys and /proc being writable
+ fix_ro_paths() {
+@@ -45,35 +35,6 @@ BindReadOnlyPaths=/sys /proc
+ EOF
+ }
+ 
+-# fix_nm_link_state forces the network interface to a DOWN state ahead of NetworkManager starting up
+-fix_nm_link_state() {
+-	[ -e "/sys/class/net/$1" ] || return 0
+-	ip_path=
+-	if [ -f /sbin/ip ]; then
+-		ip_path=/sbin/ip
+-	elif [ -f /bin/ip ]; then
+-		ip_path=/bin/ip
+-	else
+-		return 0
+-	fi
+-	cat <<-EOF > /run/systemd/system/network-device-down.service
+-[Unit]
+-Description=Turn off network device
+-Before=NetworkManager.service
+-Before=systemd-networkd.service
+-[Service]
+-# do not turn off if there is a default route to 169.254.0.1, i.e. the device is a routed nic
+-ExecCondition=/bin/sh -c '! /usr/bin/grep -qs 00000000.0100FEA9 /proc/net/route'
+-ExecStart=-${ip_path} link set $1 down
+-Type=oneshot
+-RemainAfterExit=true
+-[Install]
+-WantedBy=default.target
+-EOF
+-	mkdir -p /run/systemd/system/default.target.wants
+-	ln -sf /run/systemd/system/network-device-down.service /run/systemd/system/default.target.wants/network-device-down.service
+-}
+-
+ # fix_systemd_override_unit generates a unit specific override
+ fix_systemd_override_unit() {
+ 	dropin_dir="/run/systemd/${1}.d"
+@@ -112,16 +73,7 @@ fix_systemd_mask() {
+ # fix_systemd_udev_trigger overrides the systemd-udev-trigger.service to match the latest version
+ # of the file which uses "ExecStart=-" instead of "ExecStart=".
+ fix_systemd_udev_trigger() {
+-	cmd=
+-	if [ -f /usr/bin/udevadm ]; then
+-		cmd=/usr/bin/udevadm
+-	elif [ -f /sbin/udevadm ]; then
+-		cmd=/sbin/udevadm
+-	elif [ -f /bin/udevadm ]; then
+-		cmd=/bin/udevadm
+-	else
+-		return 0
+-	fi
++	cmd=udevadm
+ 
+ 	mkdir -p /run/systemd/system/systemd-udev-trigger.service.d
+ 	cat <<-EOF > /run/systemd/system/systemd-udev-trigger.service.d/zzz-lxc-override.conf
+@@ -145,24 +97,12 @@ EOF
+ }
+ 
+ ## Main logic
+-# Nothing to do in Incus VM but deployed in case it is later converted to a container
+-is_incus_vm || is_lxd_vm && exit 0
+ 
+ # Exit immediately if not an Incus/LXC container
+ is_lxc_container || exit 0
+ 
+-# Check for NetworkManager
+-nm_exists=0
+-
+-is_in_path NetworkManager && nm_exists=1
+-
+ # Determine systemd version
+-for path in /usr/lib/systemd/systemd /lib/systemd/systemd; do
+-	[ -x "${path}" ] || continue
+-
+-	systemd_version="$("${path}" --version | head -n1 | cut -d' ' -f2)"
+-	break
+-done
++systemd_version="$(systemd --version | head -n1 | cut -d' ' -f2)"
+ 
+ # Determine distro name and release
+ ID=""
+@@ -222,11 +162,6 @@ ACTION=="add|change|move", ENV{ID_NET_DRIVER}=="veth", ENV{INTERFACE}=="eth[0-9]
+ EOF
+ fi
+ 
+-# Workarounds for NetworkManager in containers
+-if [ "${nm_exists}" -eq 1 ]; then
+-	fix_nm_link_state eth0
+-fi
+-
+ # Allow masking units created by the lxc system-generator.
+ for d in /etc/systemd/system /usr/lib/systemd/system /lib/systemd/system; do
+ 	if ! [ -d "${d}" ]; then
diff --git a/nixpkgs/pkgs/tools/virtualization/dockstarter/default.nix b/nixpkgs/pkgs/tools/virtualization/dockstarter/default.nix
new file mode 100644
index 000000000000..321722d753a3
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/dockstarter/default.nix
@@ -0,0 +1,43 @@
+{ bash
+, coreutils
+, fetchFromGitHub
+, git
+, lib
+, makeWrapper
+, ncurses
+, stdenv
+}:
+
+stdenv.mkDerivation rec {
+  pname = "dockstarter";
+  version = "unstable-2022-10-26";
+
+  src = fetchFromGitHub {
+    owner = "ghostwriters";
+    repo = pname;
+    rev = "a1b6b6e29aa135c2a61ea67ca05e9e034856ca08";
+    hash = "sha256-G26DFme6YaizdE5oHBo/IqV+1quu07Bp+IykXtO/GgA=";
+  };
+
+  dontBuild = false;
+
+  nativeBuildInputs = [ makeWrapper ];
+
+  installPhase = ''
+    install -Dm755 main.sh $out/bin/ds
+    wrapProgram $out/bin/ds --prefix PATH : ${lib.makeBinPath [
+      bash
+      coreutils
+      git
+      ncurses
+    ]}
+  '';
+
+  meta = with lib; {
+    description = "DockSTARTer helps you get started with running apps in Docker.";
+    homepage = "https://dockstarter.com";
+    license = licenses.mit;
+    maintainers = with maintainers; [ urandom ];
+    mainProgram = "ds";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/ec2-ami-tools/default.nix b/nixpkgs/pkgs/tools/virtualization/ec2-ami-tools/default.nix
new file mode 100644
index 000000000000..8e6d7b06d170
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/ec2-ami-tools/default.nix
@@ -0,0 +1,44 @@
+{ lib, stdenv, fetchurl, unzip, ruby, openssl, makeWrapper }:
+
+stdenv.mkDerivation rec {
+  pname = "ec2-ami-tools";
+
+  version = "1.5.7";
+
+  nativeBuildInputs = [ makeWrapper unzip ];
+
+  src = fetchurl {
+    url = "https://s3.amazonaws.com/ec2-downloads/${pname}-${version}.zip";
+    sha256 = "17xj7xmdbcwdbzalhfs6yyiwa64978mk3li39l949qfjjgrxjias";
+  };
+
+  # Amazon EC2 requires that disk images are writable.  If they're
+  # not, the VM immediately terminates with a mysterious
+  # "Server.InternalError" message.  Since disk images generated in
+  # the Nix store are read-only, they must be made writable in the
+  # tarball uploaded to Amazon S3.  So add a `--mode=0755' flag to the
+  # tar invocation.
+  patches = [ ./writable.patch ];
+
+  installPhase =
+    ''
+      mkdir -p $out
+      mv * $out
+      rm $out/*.txt
+
+      for i in $out/bin/*; do
+          wrapProgram $i \
+            --set EC2_HOME $out \
+            --prefix PATH : ${lib.makeBinPath [ ruby openssl ]}
+      done
+
+      sed -i 's|/bin/bash|${stdenv.shell}|' $out/lib/ec2/platform/base/pipeline.rb
+    '';  # */
+
+  meta = {
+    homepage = "https://aws.amazon.com/developertools/Amazon-EC2/368";
+    description = "Command-line tools to create and manage Amazon EC2 virtual machine images";
+    license = lib.licenses.amazonsl;
+  };
+
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/ec2-ami-tools/writable.patch b/nixpkgs/pkgs/tools/virtualization/ec2-ami-tools/writable.patch
new file mode 100644
index 000000000000..54c2228911eb
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/ec2-ami-tools/writable.patch
@@ -0,0 +1,23 @@
+diff -ru ec2-ami-tools-1.4.0.5-orig/lib/ec2/amitools/bundle.rb ec2-ami-tools-1.4.0.5/lib/ec2/amitools/bundle.rb
+--- ec2-ami-tools-1.4.0.5-orig/lib/ec2/amitools/bundle.rb	2011-12-06 14:57:28.000000000 +0100
++++ ec2-ami-tools-1.4.0.5/lib/ec2/amitools/bundle.rb	2012-02-25 21:24:57.682427268 +0100
+@@ -80,7 +80,7 @@
+       # piped via several processes. The tee is used to allow a
+       # digest of the file to be calculated without having to re-read
+       # it from disk.
+-      tar = EC2::Platform::Current::Tar::Command.new.create.dereference.sparse
++      tar = EC2::Platform::Current::Tar::Command.new.create.dereference.sparse.writable
+       tar.owner(0).group(0)
+       tar.add(File::basename( image_file ), File::dirname( image_file ))
+       openssl = EC2::Platform::Current::Constants::Utility::OPENSSL
+diff -ru ec2-ami-tools-1.4.0.5-orig/lib/ec2/platform/linux/tar.rb ec2-ami-tools-1.4.0.5/lib/ec2/platform/linux/tar.rb
+--- ec2-ami-tools-1.4.0.5-orig/lib/ec2/platform/linux/tar.rb	2011-12-06 14:57:28.000000000 +0100
++++ ec2-ami-tools-1.4.0.5/lib/ec2/platform/linux/tar.rb	2012-02-25 21:23:36.342716403 +0100
+@@ -31,6 +31,7 @@
+           def update;         @options << '-u';          self; end
+           def sparse;         @options << '-S';          self; end
+           def dereference;    @options << '-h';          self; end
++          def writable;       @options << '--mode=0755'; self; end
+           
+           def archive(filename)
+             filename = '-' if filename.nil? 
diff --git a/nixpkgs/pkgs/tools/virtualization/ec2-api-tools/default.nix b/nixpkgs/pkgs/tools/virtualization/ec2-api-tools/default.nix
new file mode 100644
index 000000000000..97545204bfd4
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/ec2-api-tools/default.nix
@@ -0,0 +1,39 @@
+{ lib
+, stdenv
+, fetchurl
+, unzip
+, makeWrapper
+, jre
+}:
+
+stdenv.mkDerivation rec {
+  pname = "ec2-api-tools";
+  version = "1.7.5.1";
+
+  src = fetchurl {
+    url = "http://s3.amazonaws.com/ec2-downloads/${pname}-${version}.zip";
+    sha256 = "sha256-hRq+MEA+4chqPr3d9bS//X70tYcRBTD+rfAJVNmuLzo=";
+  };
+
+  nativeBuildInputs = [ makeWrapper unzip ];
+
+  installPhase = ''
+    d=$out/libexec/ec2-api-tools
+    mkdir -p $d
+    mv * $d
+    rm $d/bin/*.cmd # Windows stuff
+    for i in $d/bin/*; do
+      b=$(basename $i)
+      if [ $b = "ec2-cmd" ]; then continue; fi
+      makeWrapper $i $out/bin/$(basename $i) \
+        --set EC2_HOME $d \
+        --set JAVA_HOME ${jre}
+    done
+  '';
+
+  meta = {
+    homepage = "http://developer.amazonwebservices.com/connect/entry.jspa?externalID=351";
+    description = "Command-line tools to create and manage Amazon EC2 virtual machines";
+    license = lib.licenses.amazonsl;
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/ec2instanceconnectcli/default.nix b/nixpkgs/pkgs/tools/virtualization/ec2instanceconnectcli/default.nix
new file mode 100644
index 000000000000..27830f9645ca
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/ec2instanceconnectcli/default.nix
@@ -0,0 +1,25 @@
+{ lib, buildPythonPackage, fetchPypi, boto3, cryptography }:
+
+buildPythonPackage rec {
+  pname = "ec2instanceconnectcli";
+  version = "1.0.3";
+
+  src = fetchPypi {
+    inherit pname version;
+    sha256 = "sha256-/U59a6od0JI27VHX+Bvue/7tQy+iwU+g8yt9/GgdoH4=";
+  };
+
+  propagatedBuildInputs = [ boto3 cryptography ];
+
+  # has no tests
+  doCheck = false;
+
+  pythonImportsCheck = [ "ec2instanceconnectcli" ];
+
+  meta = with lib; {
+    description = "Command Line Interface for AWS EC2 Instance Connect";
+    homepage = "https://github.com/aws/aws-ec2-instance-connect-cli";
+    license = licenses.asl20;
+    maintainers = with maintainers; [ yurrriq ];
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/extra-container/default.nix b/nixpkgs/pkgs/tools/virtualization/extra-container/default.nix
new file mode 100644
index 000000000000..71ba276f4bd4
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/extra-container/default.nix
@@ -0,0 +1,40 @@
+{ stdenv, lib, nixos-container, openssh, glibcLocales, fetchFromGitHub }:
+
+stdenv.mkDerivation rec {
+  pname = "extra-container";
+  version = "0.12";
+
+  src = fetchFromGitHub {
+    owner = "erikarvstedt";
+    repo = pname;
+    rev = version;
+    hash = "sha256-/5wPv962ZHvZoZMOr4nMz7qcvbzlExRYS2nrnay/PU8=";
+  };
+
+  buildCommand = ''
+    install -D $src/extra-container $out/bin/extra-container
+    patchShebangs $out/bin
+    share=$out/share/extra-container
+    install $src/eval-config.nix -Dt $share
+
+    # Use existing PATH for systemctl and machinectl
+    scriptPath="export PATH=${lib.makeBinPath [ openssh ]}:\$PATH"
+
+    sed -i "
+      s|evalConfig=.*|evalConfig=$share/eval-config.nix|
+      s|LOCALE_ARCHIVE=.*|LOCALE_ARCHIVE=${glibcLocales}/lib/locale/locale-archive|
+      2i$scriptPath
+      2inixosContainer=${nixos-container}/bin
+    " $out/bin/extra-container
+  '';
+
+  meta = with lib; {
+    description = "Run declarative containers without full system rebuilds";
+    homepage = "https://github.com/erikarvstedt/extra-container";
+    changelog = "https://github.com/erikarvstedt/extra-container/blob/${version}/CHANGELOG.md";
+    license = licenses.mit;
+    platforms = platforms.linux;
+    maintainers = [ maintainers.erikarvstedt ];
+    mainProgram = "extra-container";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/google-compute-engine/default.nix b/nixpkgs/pkgs/tools/virtualization/google-compute-engine/default.nix
new file mode 100644
index 000000000000..c980a45bbef2
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/google-compute-engine/default.nix
@@ -0,0 +1,63 @@
+{ lib
+, fetchFromGitHub
+, buildPythonPackage
+, bash
+, bashInteractive
+, util-linux
+, boto
+, setuptools
+, distro
+}:
+
+buildPythonPackage rec {
+  pname = "google-compute-engine";
+  version = "20190124";
+
+  src = fetchFromGitHub {
+    owner = "GoogleCloudPlatform";
+    repo = "compute-image-packages";
+    rev = version;
+    sha256 = "08cy0jd463kng6hwbd3nfldsp4dpd2lknlvdm88cq795wy0kh4wp";
+  };
+
+  buildInputs = [ bash ];
+  propagatedBuildInputs = [ boto setuptools distro ];
+
+  postPatch = ''
+    for file in $(find google_compute_engine -type f); do
+      substituteInPlace "$file" \
+        --replace /bin/systemctl "/run/current-system/systemd/bin/systemctl" \
+        --replace /bin/bash "${bashInteractive}/bin/bash" \
+        --replace /sbin/hwclock "${util-linux}/bin/hwclock"
+      # SELinux tool ???  /sbin/restorecon
+    done
+
+    substituteInPlace google_config/udev/64-gce-disk-removal.rules \
+      --replace /bin/sh "${bash}/bin/sh" \
+      --replace /bin/umount "${util-linux}/bin/umount" \
+      --replace /usr/bin/logger "${util-linux}/bin/logger"
+  '';
+
+  postInstall = ''
+    # allows to install the package in `services.udev.packages` in NixOS
+    mkdir -p $out/lib/udev/rules.d
+    cp -r google_config/udev/*.rules $out/lib/udev/rules.d
+
+    # sysctl snippets will be used by google-compute-config.nix
+    mkdir -p $out/sysctl.d
+    cp google_config/sysctl/*.conf $out/sysctl.d
+
+    patchShebangs $out/bin/*
+  '';
+
+  doCheck = false;
+  pythonImportsCheck = [ "google_compute_engine" ];
+
+  meta = with lib; {
+    description = "Google Compute Engine tools and services";
+    homepage = "https://github.com/GoogleCloudPlatform/compute-image-packages";
+    license = licenses.asl20;
+    maintainers = with maintainers; [ zimbatm ];
+    platforms = platforms.linux;
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/google-guest-agent/default.nix b/nixpkgs/pkgs/tools/virtualization/google-guest-agent/default.nix
new file mode 100644
index 000000000000..423f43a02de0
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/google-guest-agent/default.nix
@@ -0,0 +1,66 @@
+{ lib
+, buildGoModule
+, fetchFromGitHub
+, coreutils
+, makeWrapper
+, google-guest-configs
+, google-guest-oslogin
+, iproute2
+, procps
+}:
+
+buildGoModule rec {
+  pname = "guest-agent";
+  version = "20230821.00";
+
+  src = fetchFromGitHub {
+    owner = "GoogleCloudPlatform";
+    repo = pname;
+    rev = "refs/tags/${version}";
+    hash = "sha256-DP15KDnD09edBxOQDwP0cjVIFxjMzE1hu1Sbu6Faj9Y=";
+  };
+
+  vendorHash = "sha256-PGvyDjhLwIKhR6NmwzbzjfkBK+FqsziAdsybQmCbtCc=";
+
+  patches = [ ./disable-etc-mutation.patch ];
+
+  nativeBuildInputs = [ makeWrapper ];
+
+  postPatch = ''
+    substitute ${./fix-paths.patch} fix-paths.patch \
+      --subst-var out \
+      --subst-var-by true "${coreutils}/bin/true"
+    patch -p1 < ./fix-paths.patch
+  '';
+
+  # We don't add `shadow` here; it's added to PATH if `mutableUsers` is enabled.
+  binPath = lib.makeBinPath [
+    google-guest-configs
+    google-guest-oslogin
+    iproute2
+    procps
+  ];
+
+  # Skip tests which require networking.
+  preCheck = ''
+    rm google_guest_agent/wsfc_test.go
+  '';
+
+  postInstall = ''
+    mkdir -p $out/etc/systemd/system
+    cp *.service $out/etc/systemd/system
+    install -Dm644 instance_configs.cfg $out/etc/default/instance_configs.cfg
+
+    wrapProgram $out/bin/google_guest_agent \
+      --prefix PATH ":" "$binPath"
+  '';
+
+  meta = with lib; {
+    description = "Guest Agent for Google Compute Engine";
+    homepage = "https://github.com/GoogleCloudPlatform/guest-agent";
+    changelog = "https://github.com/GoogleCloudPlatform/guest-agent/releases/tag/${version}";
+    license = licenses.asl20;
+    maintainers = with maintainers; [ abbradar ];
+    platforms = platforms.linux;
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/google-guest-agent/disable-etc-mutation.patch b/nixpkgs/pkgs/tools/virtualization/google-guest-agent/disable-etc-mutation.patch
new file mode 100644
index 000000000000..fe076baacd20
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/google-guest-agent/disable-etc-mutation.patch
@@ -0,0 +1,54 @@
+From 2e8060f9ade13ba18ae5930c0781227bfcce11a5 Mon Sep 17 00:00:00 2001
+From: Nikolay Amiantov <ab@fmap.me>
+Date: Mon, 10 Jan 2022 22:16:38 +0300
+Subject: [PATCH] Disable accounts setup phase and oslogin
+
+On NixOS we set up necessary groups and sudoers rules declaratively,
+and have most of `/etc` directory read-only. This creates (harmless)
+error messages when trying to create google-sudoers file.
+
+"oslogin" daemon sets up configuration necessary for OS Login to
+work, including PAM, NSS and sudoers. On NixOS we perform all this
+configuration declaratively and have most of /etc mounted read-only.
+This creates (harmless) error messages when running the daemon.
+---
+ google_guest_agent/non_windows_accounts.go | 9 ---------
+ google_guest_agent/oslogin.go              | 2 +-
+ 2 files changed, 1 insertion(+), 10 deletions(-)
+
+diff --git a/google_guest_agent/non_windows_accounts.go b/google_guest_agent/non_windows_accounts.go
+index 81013e3..05b830f 100644
+--- a/google_guest_agent/non_windows_accounts.go
++++ b/google_guest_agent/non_windows_accounts.go
+@@ -104,15 +104,6 @@ func (a *accountsMgr) set() error {
+ 		sshKeys = make(map[string][]string)
+ 	}
+ 
+-	logger.Debugf("create sudoers file if needed")
+-	if err := createSudoersFile(); err != nil {
+-		logger.Errorf("Error creating google-sudoers file: %v.", err)
+-	}
+-	logger.Debugf("create sudoers group if needed")
+-	if err := createSudoersGroup(); err != nil {
+-		logger.Errorf("Error creating google-sudoers group: %v.", err)
+-	}
+-
+ 	mdkeys := newMetadata.Instance.Attributes.SSHKeys
+ 	if !newMetadata.Instance.Attributes.BlockProjectKeys {
+ 		mdkeys = append(mdkeys, newMetadata.Project.Attributes.SSHKeys...)
+diff --git a/google_guest_agent/oslogin.go b/google_guest_agent/oslogin.go
+index d05f733..980e84c 100644
+--- a/google_guest_agent/oslogin.go
++++ b/google_guest_agent/oslogin.go
+@@ -76,7 +76,7 @@ func (o *osloginMgr) timeout() bool {
+ }
+ 
+ func (o *osloginMgr) disabled(os string) bool {
+-	return os == "windows"
++	return true
+ }
+ 
+ func (o *osloginMgr) set() error {
+-- 
+2.34.1
+
diff --git a/nixpkgs/pkgs/tools/virtualization/google-guest-agent/fix-paths.patch b/nixpkgs/pkgs/tools/virtualization/google-guest-agent/fix-paths.patch
new file mode 100644
index 000000000000..07bb112ae289
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/google-guest-agent/fix-paths.patch
@@ -0,0 +1,43 @@
+diff --git a/google-guest-agent.service b/google-guest-agent.service
+index 79b8e39..a39097b 100644
+--- a/google-guest-agent.service
++++ b/google-guest-agent.service
+@@ -14,7 +14,7 @@ PartOf=network.service networking.service NetworkManager.service systemd-network
+ 
+ [Service]
+ Type=notify
+-ExecStart=/usr/bin/google_guest_agent
++ExecStart=@out@/bin/google_guest_agent
+ OOMScoreAdjust=-999
+ Restart=always
+ 
+diff --git a/google-shutdown-scripts.service b/google-shutdown-scripts.service
+index 16bb9c2..ae02067 100644
+--- a/google-shutdown-scripts.service
++++ b/google-shutdown-scripts.service
+@@ -5,10 +5,10 @@ After=network-online.target rsyslog.service
+ 
+ [Service]
+ Type=oneshot
+-ExecStart=/bin/true
++ExecStart=@true@
+ RemainAfterExit=true
+ # This service does nothing on start, and runs shutdown scripts on stop.
+-ExecStop=/usr/bin/google_metadata_script_runner shutdown
++ExecStop=@out@/bin/google_metadata_script_runner shutdown
+ TimeoutStopSec=0
+ KillMode=process
+ 
+diff --git a/google-startup-scripts.service b/google-startup-scripts.service
+index dfc9838..2465265 100644
+--- a/google-startup-scripts.service
++++ b/google-startup-scripts.service
+@@ -6,7 +6,7 @@ Before=apt-daily.service
+ 
+ [Service]
+ Type=oneshot
+-ExecStart=/usr/bin/google_metadata_script_runner startup
++ExecStart=@out@/bin/google_metadata_script_runner startup
+ #TimeoutStartSec is ignored for Type=oneshot service units.
+ KillMode=process
+ 
diff --git a/nixpkgs/pkgs/tools/virtualization/google-guest-configs/default.nix b/nixpkgs/pkgs/tools/virtualization/google-guest-configs/default.nix
new file mode 100644
index 000000000000..73dd3c93d255
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/google-guest-configs/default.nix
@@ -0,0 +1,52 @@
+{ stdenv, lib, fetchFromGitHub, makeWrapper
+, ipcalc, iproute2, util-linux, coreutils, ethtool, gnugrep, gnused, nvme-cli }:
+
+stdenv.mkDerivation rec {
+  pname = "google-guest-configs";
+  version = "20211116.00";
+
+  src = fetchFromGitHub {
+    owner = "GoogleCloudPlatform";
+    repo = "guest-configs";
+    rev = version;
+    sha256 = "sha256-0SRu6p/DsHNNI20mkXJitt/Ee5S2ooiy5hNmD+ndecM=";
+  };
+
+  binDeps = lib.makeBinPath [ coreutils util-linux gnugrep gnused ethtool ipcalc iproute2 ];
+
+  nativeBuildInputs = [ makeWrapper ];
+
+  dontConfigure = true;
+  dontBuild = true;
+
+  postPatch = ''
+    substitute ${./fix-paths.patch} fix-paths.patch \
+      --subst-var out \
+      --subst-var-by nvme "${nvme-cli}/bin/nvme" \
+      --subst-var-by sh "${stdenv.shell}" \
+      --subst-var-by umount "${util-linux}/bin/umount" \
+      --subst-var-by logger "${util-linux}/bin/logger"
+    patch -p1 < ./fix-paths.patch
+  '';
+
+  installPhase = ''
+    mkdir -p $out/{bin,etc,lib}
+    cp -r src/etc/{modprobe.d,sysctl.d} $out/etc
+    cp -r src/lib/udev $out/lib
+    cp -r src/sbin/* $out/bin
+    cp -r src/usr/bin/* $out/bin
+
+    for i in $out/bin/* $out/lib/udev/google_nvme_id; do
+      wrapProgram "$i" \
+        --prefix "PATH" ":" "$binDeps"
+    done
+  '';
+
+  meta = with lib; {
+    homepage = "https://github.com/GoogleCloudPlatform/guest-configs";
+    description = "Linux Guest Environment for Google Compute Engine";
+    license = licenses.asl20;
+    platforms = platforms.linux;
+    maintainers = with maintainers; [ abbradar ];
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/google-guest-configs/fix-paths.patch b/nixpkgs/pkgs/tools/virtualization/google-guest-configs/fix-paths.patch
new file mode 100644
index 000000000000..7c9a86f10601
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/google-guest-configs/fix-paths.patch
@@ -0,0 +1,61 @@
+diff -ru3 source.old/src/lib/udev/google_nvme_id source.new/src/lib/udev/google_nvme_id
+--- source.old/src/lib/udev/google_nvme_id	1970-01-01 03:00:01.000000000 +0300
++++ source.new/src/lib/udev/google_nvme_id	2022-02-05 13:30:00.986242869 +0300
+@@ -17,7 +17,7 @@
+ # the metadata server
+ 
+ # Locations of the script's dependencies
+-readonly nvme_cli_bin=/usr/sbin/nvme
++readonly nvme_cli_bin=@nvme@
+ 
+ # Bash regex to parse device paths and controller identification
+ readonly NAMESPACE_NUMBER_REGEX="/dev/nvme[[:digit:]]+n([[:digit:]]+).*"
+diff -ru3 source.old/src/lib/udev/rules.d/64-gce-disk-removal.rules source.new/src/lib/udev/rules.d/64-gce-disk-removal.rules
+--- source.old/src/lib/udev/rules.d/64-gce-disk-removal.rules	1970-01-01 03:00:01.000000000 +0300
++++ source.new/src/lib/udev/rules.d/64-gce-disk-removal.rules	2022-02-05 13:27:42.635300567 +0300
+@@ -14,4 +14,4 @@
+ #
+ # When a disk is removed, unmount any remaining attached volumes.
+ 
+-ACTION=="remove", SUBSYSTEM=="block", KERNEL=="sd*|vd*|nvme*", RUN+="/bin/sh -c '/bin/umount -fl /dev/$name && /usr/bin/logger -p daemon.warn -s WARNING: hot-removed /dev/$name that was still mounted, data may have been corrupted'"
++ACTION=="remove", SUBSYSTEM=="block", KERNEL=="sd*|vd*|nvme*", RUN+="@sh@ -c '@umount@ -fl /dev/$name && @logger@ -p daemon.warn -s WARNING: hot-removed /dev/$name that was still mounted, data may have been corrupted'"
+diff -ru3 source.old/src/lib/udev/rules.d/65-gce-disk-naming.rules source.new/src/lib/udev/rules.d/65-gce-disk-naming.rules
+--- source.old/src/lib/udev/rules.d/65-gce-disk-naming.rules	1970-01-01 03:00:01.000000000 +0300
++++ source.new/src/lib/udev/rules.d/65-gce-disk-naming.rules	2022-02-05 13:27:05.053107964 +0300
+@@ -21,11 +21,11 @@
+ KERNEL=="sd*|vd*", IMPORT{program}="scsi_id --export --whitelisted -d $tempnode"
+ 
+ # NVME Local SSD naming
+-KERNEL=="nvme*n*", ATTRS{model}=="nvme_card", PROGRAM="/bin/sh -c 'nsid=$$(echo %k|sed -re s/nvme[0-9]+n\([0-9]+\).\*/\\1/); echo $$((nsid-1))'", ENV{ID_SERIAL_SHORT}="local-nvme-ssd-%c"
++KERNEL=="nvme*n*", ATTRS{model}=="nvme_card", PROGRAM="@sh@ -c 'nsid=$$(echo %k|sed -re s/nvme[0-9]+n\([0-9]+\).\*/\\1/); echo $$((nsid-1))'", ENV{ID_SERIAL_SHORT}="local-nvme-ssd-%c"
+ KERNEL=="nvme*", ATTRS{model}=="nvme_card", ENV{ID_SERIAL}="Google_EphemeralDisk_$env{ID_SERIAL_SHORT}"
+ 
+ # NVME Persistent Disk Naming
+-KERNEL=="nvme*n*", ATTRS{model}=="nvme_card-pd", IMPORT{program}="google_nvme_id -d $tempnode"
++KERNEL=="nvme*n*", ATTRS{model}=="nvme_card-pd", IMPORT{program}="@out@/lib/udev/google_nvme_id -d $tempnode"
+ 
+ # Symlinks
+ KERNEL=="sd*|vd*|nvme*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/google-$env{ID_SERIAL_SHORT}"
+diff -ru3 source.old/src/sbin/google-dhclient-script source.new/src/sbin/google-dhclient-script
+--- source.old/src/sbin/google-dhclient-script	1970-01-01 03:00:01.000000000 +0300
++++ source.new/src/sbin/google-dhclient-script	2022-02-05 13:29:37.430058984 +0300
+@@ -31,7 +31,6 @@
+ # This script is found in EL 7 and used to fix local routing in EL 6.
+ # ----------
+ 
+-PATH=/bin:/usr/bin:/sbin
+ # scripts in dhclient.d/ use $SAVEDIR (#833054)
+ SAVEDIR=/var/lib/dhclient
+ 
+@@ -58,9 +57,9 @@
+     if need_hostname; then
+         status=1
+         if [ -n "${new_ip_address}" ]; then
+-            eval $(/bin/ipcalc --silent --hostname ${new_ip_address} ; echo "status=$?")
++            eval $(ipcalc --silent --hostname ${new_ip_address} ; echo "status=$?")
+         elif [ -n "${new_ip6_address}" ]; then
+-            eval $(/bin/ipcalc --silent --hostname ${new_ip6_address} ; echo "status=$?")
++            eval $(ipcalc --silent --hostname ${new_ip6_address} ; echo "status=$?")
+         fi
+ 
+         if [ ${status} -eq 0 ]; then
diff --git a/nixpkgs/pkgs/tools/virtualization/google-guest-oslogin/default.nix b/nixpkgs/pkgs/tools/virtualization/google-guest-oslogin/default.nix
new file mode 100644
index 000000000000..7600952a45b6
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/google-guest-oslogin/default.nix
@@ -0,0 +1,57 @@
+{ lib
+, stdenv
+, curl
+, fetchFromGitHub
+, json_c
+, nixosTests
+, pam
+}:
+
+stdenv.mkDerivation rec {
+  pname = "google-guest-oslogin";
+  version = "20230831.00";
+
+  src = fetchFromGitHub {
+    owner = "GoogleCloudPlatform";
+    repo = "guest-oslogin";
+    rev = version;
+    sha256 = "sha256-9QCB94HVbeLjioJuSN1Aa+EqFncojPoWFxw5mS9bDGw=";
+  };
+
+  postPatch = ''
+    # change sudoers dir from /var/google-sudoers.d to /run/google-sudoers.d (managed through systemd-tmpfiles)
+    substituteInPlace src/pam/pam_oslogin_admin.cc --replace /var/google-sudoers.d /run/google-sudoers.d
+    # fix "User foo not allowed because shell /bin/bash does not exist"
+    substituteInPlace src/include/compat.h --replace /bin/bash /run/current-system/sw/bin/bash
+  '';
+
+  buildInputs = [ curl.dev pam json_c ];
+
+  env.NIX_CFLAGS_COMPILE = toString [ "-I${json_c.dev}/include/json-c" ];
+
+  makeFlags = [
+    "VERSION=${version}"
+    "PREFIX=$(out)"
+    "MANDIR=$(out)/share/man"
+    "SYSTEMDDIR=$(out)/etc/systemd/system"
+    "PRESETDIR=$(out)/etc/systemd/system-preset"
+  ];
+
+  postInstall = ''
+    sed -i "s,/usr/bin/,$out/bin/,g" $out/etc/systemd/system/google-oslogin-cache.service
+  '';
+
+  enableParallelBuilding = true;
+
+  passthru.tests = {
+    inherit (nixosTests) google-oslogin;
+  };
+
+  meta = with lib; {
+    homepage = "https://github.com/GoogleCloudPlatform/compute-image-packages";
+    description = "OS Login Guest Environment for Google Compute Engine";
+    license = licenses.asl20;
+    platforms = platforms.linux;
+    maintainers = with maintainers; [ flokli ];
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/govc/default.nix b/nixpkgs/pkgs/tools/virtualization/govc/default.nix
new file mode 100644
index 000000000000..18d5a83a591d
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/govc/default.nix
@@ -0,0 +1,31 @@
+{ lib, fetchFromGitHub, buildGoModule }:
+
+buildGoModule rec {
+  pname = "govc";
+  version = "0.33.1";
+
+  subPackages = [ "govc" ];
+
+  src = fetchFromGitHub {
+    rev = "v${version}";
+    owner = "vmware";
+    repo = "govmomi";
+    sha256 = "sha256-5zFyOWfVxQL/QveOlX4Xkg8FBwo8mZzR7ea2IacSrS4=";
+  };
+
+  vendorHash = "sha256-DBcovHOOfIy4dfi8U9zaCUzz5Zz8oIG44JCqMKtdxgg=";
+
+  ldflags = [
+    "-s"
+    "-w"
+    "-X github.com/vmware/govmomi/govc/flags.BuildVersion=${version}"
+  ];
+
+  meta = {
+    description = "A vSphere CLI built on top of govmomi";
+    homepage = "https://github.com/vmware/govmomi/tree/master/govc";
+    license = lib.licenses.asl20;
+    maintainers = with lib.maintainers; [ nicknovitski ];
+    mainProgram = "govc";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/guestfs-tools/default.nix b/nixpkgs/pkgs/tools/virtualization/guestfs-tools/default.nix
new file mode 100644
index 000000000000..2b7cfb79d870
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/guestfs-tools/default.nix
@@ -0,0 +1,117 @@
+{ lib
+, stdenv
+, fetchurl
+, bash-completion
+, bison
+, cdrkit
+, cpio
+, curl
+, flex
+, getopt
+, glib
+, gnupg
+, hivex
+, jansson
+, libguestfs-with-appliance
+, libosinfo
+, libvirt
+, libxml2
+, makeWrapper
+, ncurses
+, ocamlPackages
+, openssl
+, pcre2
+, perlPackages
+, pkg-config
+, qemu
+, xz
+}:
+
+stdenv.mkDerivation rec {
+  pname = "guestfs-tools";
+  version = "1.50.1";
+
+  src = fetchurl {
+    url = "https://download.libguestfs.org/guestfs-tools/${lib.versions.majorMinor version}-stable/guestfs-tools-${version}.tar.gz";
+    sha256 = "sha256-rH/MK9Xid+lb1bKnspCE3gATefBnHDZAQ3NRavhTvLA=";
+  };
+
+  nativeBuildInputs = [
+    bison
+    cdrkit
+    cpio
+    flex
+    getopt
+    makeWrapper
+    pkg-config
+    qemu
+  ] ++
+  (with perlPackages; [
+    GetoptLong
+    libintl-perl
+    ModuleBuild
+    perl
+    Po4a
+  ]) ++
+  (with ocamlPackages; [
+    findlib
+    gettext-stub
+    ocaml
+    ocaml_gettext
+    ounit2
+  ]);
+
+  buildInputs = [
+    bash-completion
+    glib
+    hivex
+    jansson
+    libguestfs-with-appliance
+    libosinfo
+    libvirt
+    libxml2
+    ncurses
+    openssl
+    pcre2
+    xz
+  ];
+
+  postPatch = ''
+    # If it uses the executable name, then there's nothing we can do
+    # when wrapping to stop it looking in
+    # $out/etc/.virt-builder-wrapped, which won't exist.
+    substituteInPlace common/mlstdutils/std_utils.ml \
+        --replace Sys.executable_name '(Array.get Sys.argv 0)'
+  '';
+
+  preConfigure = ''
+    patchShebangs ocaml-dep.sh.in ocaml-link.sh.in run.in
+  '';
+
+  makeFlags = [
+    "LIBGUESTFS_PATH=${libguestfs-with-appliance}/lib/guestfs"
+  ];
+
+  installFlags = [
+    "BASH_COMPLETIONS_DIR=${placeholder "out"}/share/bash-completion/completions"
+  ];
+
+  enableParallelBuilding = true;
+
+  postInstall = ''
+    wrapProgram $out/bin/virt-builder \
+      --argv0 virt-builder \
+      --prefix PATH : ${lib.makeBinPath [ curl gnupg ]}:$out/bin \
+      --suffix VIRT_BUILDER_DIRS : /etc:$out/etc
+    wrapProgram $out/bin/virt-win-reg \
+      --prefix PERL5LIB : ${with perlPackages; makeFullPerlPath [ hivex libintl-perl libguestfs-with-appliance ]}
+  '';
+
+  meta = with lib; {
+    description = "Extra tools for accessing and modifying virtual machine disk images";
+    license = with licenses; [ gpl2Plus lgpl21Plus ];
+    homepage = "https://libguestfs.org/";
+    maintainers = with maintainers; [ thiagokokada ];
+    platforms = platforms.linux;
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/jumppad/default.nix b/nixpkgs/pkgs/tools/virtualization/jumppad/default.nix
new file mode 100644
index 000000000000..c10b6887aeec
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/jumppad/default.nix
@@ -0,0 +1,30 @@
+{ lib, buildGoModule, fetchFromGitHub }:
+
+buildGoModule rec {
+  pname = "jumppad";
+  version = "0.5.53";
+
+  src = fetchFromGitHub {
+    owner = "jumppad-labs";
+    repo = pname;
+    rev = "v${version}";
+    hash = "sha256-93KTi7m+7zS6hSIF4dA995Z8jUdmE5u3O8ytCLsEqdE=";
+  };
+  vendorHash = "sha256-o3jA1gVKW6KUHzy5zZO4aaGVoCBFN96hbK0/usQ32fw=";
+
+  ldflags = [
+    "-s" "-w" "-X main.version=${version}"
+  ];
+
+  # Tests require a large variety of tools and resources to run including
+  # Kubernetes, Docker, and GCC.
+  doCheck = false;
+
+  meta = with lib; {
+    description = "A tool for building modern cloud native development environments";
+    homepage = "https://jumppad.dev";
+    license = licenses.mpl20;
+    maintainers = with maintainers; [ cpcloud ];
+    mainProgram = "jumppad";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/kubevirt/default.nix b/nixpkgs/pkgs/tools/virtualization/kubevirt/default.nix
new file mode 100644
index 000000000000..67027dc4bd28
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/kubevirt/default.nix
@@ -0,0 +1,54 @@
+{ buildGoModule
+, fetchFromGitHub
+, installShellFiles
+, lib
+, testers
+, kubevirt
+}:
+
+buildGoModule rec {
+  pname = "kubevirt";
+  version = "1.1.0";
+
+  src = fetchFromGitHub {
+    owner = "kubevirt";
+    repo = "kubevirt";
+    rev = "v${version}";
+    sha256 = "sha256-dW2rHW/37Jpk3vuu3O87nynK8Mp0IAqpkRvBDxT/++I=";
+  };
+
+  vendorHash = null;
+
+  subPackages = [ "cmd/virtctl" ];
+
+  tags = [ "selinux" ];
+
+  ldflags = [
+    "-X kubevirt.io/client-go/version.gitCommit=v${version}"
+    "-X kubevirt.io/client-go/version.gitTreeState=clean"
+    "-X kubevirt.io/client-go/version.gitVersion=v${version}"
+  ];
+
+  nativeBuildInputs = [ installShellFiles ];
+
+  postInstall = ''
+    installShellCompletion --cmd virtctl \
+      --bash <($out/bin/virtctl completion bash) \
+      --fish <($out/bin/virtctl completion fish) \
+      --zsh <($out/bin/virtctl completion zsh)
+  '';
+
+  passthru.tests.version = testers.testVersion {
+    package = kubevirt;
+    command = "virtctl version --client";
+    version = "v${version}";
+  };
+
+  meta = with lib; {
+    description = "Client tool to use advanced features such as console access";
+    homepage = "https://kubevirt.io/";
+    license = licenses.asl20;
+    maintainers = with maintainers; [ haslersn ];
+    mainProgram = "virtctl";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/linode-cli/default.nix b/nixpkgs/pkgs/tools/virtualization/linode-cli/default.nix
new file mode 100644
index 000000000000..a4f54ec86177
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/linode-cli/default.nix
@@ -0,0 +1,86 @@
+{ lib
+, fetchFromGitHub
+, fetchurl
+, buildPythonApplication
+, colorclass
+, installShellFiles
+, pyyaml
+, requests
+, setuptools
+, terminaltables
+, rich
+, openapi3
+, packaging
+}:
+
+let
+  hash = "sha256-J0L+FTVzYuAqTDOwpoH12lQr03UNo5dsQpd/iUKR40Q=";
+  # specVersion taken from: https://www.linode.com/docs/api/openapi.yaml at `info.version`.
+  specVersion = "4.166.0";
+  specHash = "sha256-rUwKQt3y/ALZUoW3eJiiIDJYLQpUHO7Abm0h09ra02g=";
+  spec = fetchurl {
+    url = "https://raw.githubusercontent.com/linode/linode-api-docs/v${specVersion}/openapi.yaml";
+    hash = specHash;
+  };
+
+in
+
+buildPythonApplication rec {
+  pname = "linode-cli";
+  version = "5.45.0";
+  pyproject = true;
+
+  src = fetchFromGitHub {
+    owner = "linode";
+    repo = pname;
+    rev = "v${version}";
+    inherit hash;
+  };
+
+  patches = [
+    ./remove-update-check.patch
+  ];
+
+  # remove need for git history
+  prePatch = ''
+    substituteInPlace setup.py \
+      --replace "version = get_version()" "version='${version}',"
+  '';
+
+  propagatedBuildInputs = [
+    colorclass
+    pyyaml
+    requests
+    setuptools
+    terminaltables
+    rich
+    openapi3
+    packaging
+  ];
+
+  postConfigure = ''
+    python3 -m linodecli bake ${spec} --skip-config
+    cp data-3 linodecli/
+    echo "${version}" > baked_version
+  '';
+
+  doInstallCheck = true;
+  installCheckPhase = ''
+    $out/bin/linode-cli --skip-config --version | grep ${version} > /dev/null
+  '';
+
+  nativeBuildInputs = [ installShellFiles ];
+  postInstall = ''
+    installShellCompletion --cmd linode-cli --bash <($out/bin/linode-cli --skip-config completion bash)
+  '';
+
+  passthru.updateScript = ./update.sh;
+
+  meta = with lib; {
+    mainProgram = "linode-cli";
+    description = "The Linode Command Line Interface";
+    homepage = "https://github.com/linode/linode-cli";
+    license = licenses.bsd3;
+    maintainers = with maintainers; [ ryantm techknowlogick ];
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/linode-cli/remove-update-check.patch b/nixpkgs/pkgs/tools/virtualization/linode-cli/remove-update-check.patch
new file mode 100644
index 000000000000..f26b30edf72d
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/linode-cli/remove-update-check.patch
@@ -0,0 +1,13 @@
+diff --git a/linodecli/api_request.py b/linodecli/api_request.py
+index 4273aa6..3ada5c2 100644
+--- a/linodecli/api_request.py
++++ b/linodecli/api_request.py
+@@ -305,7 +305,7 @@ def _attempt_warn_old_version(ctx, result):
+                 file=sys.stderr,
+             )
+
+-    if api_version_higher:
++    if False:
+         # check to see if there is, in fact, a version to upgrade to.  If not, don't
+         # suggest an upgrade (since there's no package anyway)
+         new_version_exists = False
\ No newline at end of file
diff --git a/nixpkgs/pkgs/tools/virtualization/linode-cli/update.sh b/nixpkgs/pkgs/tools/virtualization/linode-cli/update.sh
new file mode 100755
index 000000000000..8bd0bfe8602e
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/linode-cli/update.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -i bash -p curl gnugrep gnused jq yq-go
+
+set -x -eu -o pipefail
+
+cd $(dirname "${BASH_SOURCE[0]}")
+
+SPEC_VERSION=$(curl -s https://www.linode.com/docs/api/openapi.yaml | yq eval '.info.version' -)
+
+SPEC_SHA256=$(nix-prefetch-url --quiet https://raw.githubusercontent.com/linode/linode-api-docs/v${SPEC_VERSION}/openapi.yaml)
+
+VERSION=$(curl -s ${GITHUB_TOKEN:+-u ":$GITHUB_TOKEN"} \
+    -H "Accept: application/vnd.github.v3+json" \
+    "https://api.github.com/repos/linode/linode-cli/tags" \
+    | jq 'map(.name)' \
+    | grep '"' \
+    | sed 's/[ ",(^v)]//g' \
+    | grep -v -e rc -e list \
+    | cut -d '"' -f4 | sort -rV | head -n 1)
+
+SHA256=$(nix-prefetch-url --quiet --unpack https://github.com/linode/linode-cli/archive/refs/tags/v${VERSION}.tar.gz)
+
+setKV () {
+    sed -i "s|$1 = \".*\"|$1 = \"${2:-}\"|" ./default.nix
+}
+
+setKV specVersion ${SPEC_VERSION}
+setKV specHash ${SPEC_SHA256}
+setKV version ${VERSION}
+setKV hash ${SHA256}
diff --git a/nixpkgs/pkgs/tools/virtualization/lxd-image-server/default.nix b/nixpkgs/pkgs/tools/virtualization/lxd-image-server/default.nix
new file mode 100644
index 000000000000..43f46a8a72fc
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/lxd-image-server/default.nix
@@ -0,0 +1,51 @@
+{ lib
+, openssl
+, rsync
+, python3
+, fetchFromGitHub
+, nixosTests
+}:
+
+python3.pkgs.buildPythonApplication rec {
+  pname = "lxd-image-server";
+  version = "0.0.4";
+
+  src = fetchFromGitHub {
+    owner = "Avature";
+    repo = "lxd-image-server";
+    rev = version;
+    sha256 = "yx8aUmMfSzyWaM6M7+WcL6ouuWwOpqLzODWSdNgwCwo=";
+  };
+
+  patches = [
+    ./state.patch
+    ./run.patch
+  ];
+
+  propagatedBuildInputs = with python3.pkgs; [
+    setuptools
+    attrs
+    click
+    inotify
+    cryptography
+    confight
+    python-pidfile
+  ];
+
+  makeWrapperArgs = [
+    ''--prefix PATH ':' "${lib.makeBinPath [ openssl rsync ]}"''
+  ];
+
+  doCheck = false;
+
+  passthru.tests.lxd-image-server = nixosTests.lxd-image-server;
+
+  meta = with lib; {
+    description = "Creates and manages a simplestreams lxd image server on top of nginx";
+    homepage = "https://github.com/Avature/lxd-image-server";
+    license = licenses.asl20;
+    platforms = platforms.unix;
+    maintainers = with maintainers; [ mkg20001 ];
+    mainProgram = "lxd-image-server";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/lxd-image-server/run.patch b/nixpkgs/pkgs/tools/virtualization/lxd-image-server/run.patch
new file mode 100644
index 000000000000..bd1172c1f864
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/lxd-image-server/run.patch
@@ -0,0 +1,25 @@
+From df2ce9fb48a3790407646a388e0d220a75496c52 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Maciej=20Kr=C3=BCger?= <mkg20001@gmail.com>
+Date: Wed, 3 Nov 2021 14:23:38 +0100
+Subject: [PATCH] /var/run -> /run
+
+---
+ lxd_image_server/tools/config.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lxd_image_server/tools/config.py b/lxd_image_server/tools/config.py
+index 60e8973..23d392a 100644
+--- a/lxd_image_server/tools/config.py
++++ b/lxd_image_server/tools/config.py
+@@ -9,7 +9,7 @@ import confight
+ class Config():
+ 
+     _lock = Lock()
+-    pidfile = Path('/var/run/lxd-image-server/pidfile')
++    pidfile = Path('/run/lxd-image-server/pidfile')
+     data = {}
+ 
+     @classmethod
+-- 
+2.33.0
+
diff --git a/nixpkgs/pkgs/tools/virtualization/lxd-image-server/state.patch b/nixpkgs/pkgs/tools/virtualization/lxd-image-server/state.patch
new file mode 100644
index 000000000000..c6677ea48e9c
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/lxd-image-server/state.patch
@@ -0,0 +1,49 @@
+From 17a1e09eaf8957174425d05200be9ee3e77229f9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Maciej=20Kr=C3=BCger?= <mkg20001@gmail.com>
+Date: Thu, 21 Oct 2021 00:39:08 +0200
+Subject: [PATCH] Remove system-state changing code
+
+This is already done by the module on nixOS
+---
+ lxd_image_server/cli.py | 15 +--------------
+ 1 file changed, 1 insertion(+), 14 deletions(-)
+
+diff --git a/lxd_image_server/cli.py b/lxd_image_server/cli.py
+index d276e6d..f759bf2 100644
+--- a/lxd_image_server/cli.py
++++ b/lxd_image_server/cli.py
+@@ -140,30 +140,17 @@ def reload_config():
+ @cli.command()
+ @click.option('--root_dir', default='/var/www/simplestreams',
+               show_default=True)
+-@click.option('--ssl_dir', default='/etc/nginx/ssl', show_default=True,
+-              callback=lambda ctx, param, val: Path(val))
+ @click.pass_context
+-def init(ctx, root_dir, ssl_dir):
++def init(ctx, root_dir):
+     if not Path(root_dir).exists():
+         logger.error('Root directory does not exists')
+     else:
+-        if not ssl_dir.exists():
+-            os.makedirs(str(ssl_dir))
+-
+-        if not (ssl_dir / 'nginx.key').exists():
+-            generate_cert(str(ssl_dir))
+-
+         img_dir = str(Path(root_dir, 'images'))
+         streams_dir = str(Path(root_dir, 'streams/v1'))
+         if not Path(img_dir).exists():
+             os.makedirs(img_dir)
+         if not Path(streams_dir).exists():
+             os.makedirs(streams_dir)
+-        conf_path = Path('/etc/nginx/sites-enabled/simplestreams.conf')
+-        if not conf_path.exists():
+-            conf_path.symlink_to(
+-                '/etc/nginx/sites-available/simplestreams.conf')
+-            os.system('nginx -s reload')
+ 
+         if not Path(root_dir, 'streams', 'v1', 'images.json').exists():
+             ctx.invoke(update, img_dir=Path(root_dir, 'images'),
+-- 
+2.33.0
+
diff --git a/nixpkgs/pkgs/tools/virtualization/marathonctl/default.nix b/nixpkgs/pkgs/tools/virtualization/marathonctl/default.nix
new file mode 100644
index 000000000000..85576b4f6640
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/marathonctl/default.nix
@@ -0,0 +1,25 @@
+{ lib, buildGoModule, fetchFromGitHub }:
+
+buildGoModule rec {
+  pname = "marathonctl";
+  version = "0.0.7";
+
+  src = fetchFromGitHub {
+    owner = "shoenig";
+    repo = "marathonctl";
+    rev = "v${version}";
+    sha256 = "sha256-MigmvOwYa0uYPexchS4MP74I1Tp6QHYuQVSOh1+FrMg=";
+  };
+
+  vendorHash = "sha256-Oiol4KuPOyJq2Bfc5div+enX4kQqYn20itmwWBecuIg=";
+
+  ldflags = [ "-s" "-w" ];
+
+  meta = with lib; {
+    homepage = "https://github.com/shoenig/marathonctl";
+    description = "CLI tool for Marathon";
+    license = licenses.mit;
+    maintainers = with maintainers; [ manveru ];
+    mainProgram = "marathonctl";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/mininet/default.nix b/nixpkgs/pkgs/tools/virtualization/mininet/default.nix
new file mode 100644
index 000000000000..c33389861dd0
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/mininet/default.nix
@@ -0,0 +1,50 @@
+{ stdenv, lib, fetchFromGitHub
+, which
+, python3
+, help2man
+}:
+
+let
+  pyEnv = python3.withPackages(ps: [ ps.setuptools ]);
+in
+stdenv.mkDerivation rec {
+  pname = "mininet";
+  version = "2.3.1b4";
+
+  outputs = [ "out" "py" ];
+
+  src = fetchFromGitHub {
+    owner = "mininet";
+    repo = "mininet";
+    rev = version;
+    hash = "sha256-Z7Vbfu0EJ4+rCpckXrt3hgxeB9N2nnyPIXgPBnpV4uw=";
+  };
+
+  buildFlags = [ "mnexec" ];
+  makeFlags = [ "PREFIX=$(out)" ];
+
+  pythonPath = [ python3.pkgs.setuptools ];
+  nativeBuildInputs = [ help2man ];
+
+  propagatedBuildInputs = [ python3 which ];
+
+  installTargets = [ "install-mnexec" "install-manpages" ];
+
+  preInstall = ''
+    mkdir -p $out $py
+    # without --root, install fails
+    ${pyEnv.interpreter} setup.py install --root="/" --prefix=$py
+  '';
+
+  doCheck = false;
+
+
+  meta = with lib; {
+    description = "Emulator for rapid prototyping of Software Defined Networks";
+    license = licenses.bsd3;
+    platforms = platforms.linux;
+    homepage = "https://github.com/mininet/mininet";
+    maintainers = with maintainers; [ teto ];
+    mainProgram = "mnexec";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/mkosi/default.nix b/nixpkgs/pkgs/tools/virtualization/mkosi/default.nix
new file mode 100644
index 000000000000..38ffb1cd4838
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/mkosi/default.nix
@@ -0,0 +1,134 @@
+{ lib
+, fetchFromGitHub
+, fetchpatch
+, stdenv
+, python3
+, bubblewrap
+, systemd
+, pandoc
+
+  # Python packages
+, setuptools
+, setuptools-scm
+, wheel
+, buildPythonApplication
+, pytestCheckHook
+, pefile
+
+  # Optional dependencies
+, withQemu ? false
+, qemu
+, OVMF
+}:
+let
+  # For systemd features used by mkosi, see
+  # https://github.com/systemd/mkosi/blob/19bb5e274d9a9c23891905c4bcbb8f68955a701d/action.yaml#L64-L72
+  systemdForMkosi = (systemd.overrideAttrs (oldAttrs: {
+    patches = oldAttrs.patches ++ [
+      # Enable setting a deterministic verity seed for systemd-repart. Remove when upgrading to systemd 255.
+      (fetchpatch {
+        url = "https://github.com/systemd/systemd/commit/81e04781106e3db24e9cf63c1d5fdd8215dc3f42.patch";
+        hash = "sha256-KO3poIsvdeepPmXWQXNaJJCPpmBb4sVmO+ur4om9f5k=";
+      })
+      # Propagate SOURCE_DATE_EPOCH to mcopy. Remove when upgrading to systemd 255.
+      (fetchpatch {
+        url = "https://github.com/systemd/systemd/commit/4947de275a5553399854cc748f4f13e4ae2ba069.patch";
+        hash = "sha256-YIZZyc3f8pQO9fMAxiNhDdV8TtL4pXoh+hwHBzRWtfo=";
+      })
+      # repart: make sure rewinddir() is called before readdir() when performing rm -rf. Remove when upgrading to systemd 255.
+      (fetchpatch {
+        url = "https://github.com/systemd/systemd/commit/6bbb893b90e2dcb05fb310ba4608f9c9dc587845.patch";
+        hash = "sha256-A6cF2QAeYHGc0u0V1JMxIcV5shzf5x3Q6K+blZOWSn4=";
+      })
+      # Set timezone to UTC when invoking mcopy. Remove when upgrading to systemd 255.
+      (fetchpatch {
+        url = "https://github.com/systemd/systemd/commit/b2942c76adc5bb6a3e073aa5cee57834ee3a9813.patch";
+        hash = "sha256-phGln3Gs9p8CsEe+1laGrm9xcUJWVbNBW0W8oR9/7YU=";
+      })
+    ];
+  })).override {
+    withRepart = true;
+    withBootloader = true;
+    withSysusers = true;
+    withFirstboot = true;
+    withEfi = true;
+    withUkify = true;
+  };
+
+  python3pefile = python3.withPackages (ps: with ps; [
+    pefile
+  ]);
+in
+buildPythonApplication rec {
+  pname = "mkosi";
+  version = "19";
+  format = "pyproject";
+
+  outputs = [ "out" "man" ];
+
+  src = fetchFromGitHub {
+    owner = "systemd";
+    repo = "mkosi";
+    rev = "v${version}";
+    hash = "sha256-KjJM+KZCgUnsaEN2ZorhH0AR5nmiV2h3i7Vb3KdGFtI=";
+  };
+
+  # Fix ctypes finding library
+  # https://github.com/NixOS/nixpkgs/issues/7307
+  postPatch = lib.optionalString stdenv.isLinux ''
+    substituteInPlace mkosi/run.py \
+      --replace 'ctypes.util.find_library("c")' "'${stdenv.cc.libc}/lib/libc.so.6'"
+    substituteInPlace mkosi/__init__.py \
+      --replace '/usr/lib/systemd/ukify' "${systemdForMkosi}/lib/systemd/ukify"
+  '' + lib.optionalString withQemu ''
+    substituteInPlace mkosi/qemu.py \
+      --replace '/usr/share/ovmf/x64/OVMF_VARS.fd' "${OVMF.variables}" \
+      --replace '/usr/share/ovmf/x64/OVMF_CODE.fd' "${OVMF.firmware}"
+  '';
+
+  nativeBuildInputs = [
+    pandoc
+    setuptools
+    setuptools-scm
+    wheel
+  ];
+
+  propagatedBuildInputs = [
+    systemdForMkosi
+    bubblewrap
+  ] ++ lib.optional withQemu [
+    qemu
+  ];
+
+  postBuild = ''
+    ./tools/make-man-page.sh
+  '';
+
+  checkInputs = [
+    pytestCheckHook
+  ];
+
+  pythonImportsCheck = [
+    "mkosi"
+  ];
+
+  postInstall = ''
+    mkdir -p $out/share/man/man1
+    mv mkosi/resources/mkosi.1 $out/share/man/man1/
+  '';
+
+  makeWrapperArgs = [
+    "--set MKOSI_INTERPRETER ${python3pefile}/bin/python3"
+    "--prefix PYTHONPATH : \"$PYTHONPATH\""
+  ];
+
+  meta = with lib; {
+    description = "Build legacy-free OS images";
+    homepage = "https://github.com/systemd/mkosi";
+    changelog = "https://github.com/systemd/mkosi/releases/tag/v${version}";
+    license = licenses.lgpl21Only;
+    mainProgram = "mkosi";
+    maintainers = with maintainers; [ malt3 katexochen ];
+    platforms = platforms.linux;
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/multipass/cmake_no_fetch.patch b/nixpkgs/pkgs/tools/virtualization/multipass/cmake_no_fetch.patch
new file mode 100644
index 000000000000..bde1792d4002
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/multipass/cmake_no_fetch.patch
@@ -0,0 +1,32 @@
+diff --git a/3rd-party/CMakeLists.txt b/3rd-party/CMakeLists.txt
+index 188ebfc6..4a34a922 100644
+--- a/3rd-party/CMakeLists.txt
++++ b/3rd-party/CMakeLists.txt
+@@ -2,12 +2,8 @@ include(FetchContent)
+ set(FETCHCONTENT_QUIET FALSE)
+ 
+ FetchContent_Declare(gRPC
+-  GIT_REPOSITORY https://github.com/CanonicalLtd/grpc.git
+-  GIT_TAG e3acf245
+-  GIT_SHALLOW TRUE
+-  GIT_SUBMODULES "third_party/abseil-cpp third_party/cares/cares third_party/protobuf third_party/re2 third_party/zlib"
+-  GIT_SUBMODULES_RECURSE false
+-  GIT_PROGRESS TRUE
++  DOWNLOAD_COMMAND true
++  SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/grpc
+ )
+ set(gRPC_SSL_PROVIDER "package" CACHE STRING "Provider of ssl library")
+ 
+diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
+index 52bd407f..a1100112 100644
+--- a/tests/CMakeLists.txt
++++ b/tests/CMakeLists.txt
+@@ -28,7 +28,7 @@ FetchContent_Declare(googletest
+ )
+ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+ set(INSTALL_GTEST OFF CACHE BOOL "")
+-FetchContent_MakeAvailable(googletest)
++# FetchContent_MakeAvailable(googletest)
+ 
+ add_executable(multipass_tests
+   blueprint_test_lambdas.cpp
diff --git a/nixpkgs/pkgs/tools/virtualization/multipass/cmake_warning.patch b/nixpkgs/pkgs/tools/virtualization/multipass/cmake_warning.patch
new file mode 100644
index 000000000000..846cba0690fb
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/multipass/cmake_warning.patch
@@ -0,0 +1,14 @@
+diff --git a/src/cert/CMakeLists.txt b/src/cert/CMakeLists.txt
+index d44a0b09..de440f24 100644
+--- a/src/cert/CMakeLists.txt
++++ b/src/cert/CMakeLists.txt
+@@ -22,7 +22,7 @@ add_library(cert STATIC
+ target_include_directories(cert PRIVATE
+   ${OPENSSL_INCLUDE_DIR})
+ 
+-foreach(flag -Wno-nested-anon-types -Wno-gnu -Wno-pedantic -Wno-ignored-qualifiers)
++foreach(flag -Wno-nested-anon-types -Wno-gnu -Wno-pedantic -Wno-ignored-qualifiers -Wno-ignored-attributes)
+   check_cxx_compiler_flag(${flag} SUPPORTED)
+   if(SUPPORTED)
+     target_compile_options(cert PRIVATE ${flag})
+
diff --git a/nixpkgs/pkgs/tools/virtualization/multipass/default.nix b/nixpkgs/pkgs/tools/virtualization/multipass/default.nix
new file mode 100644
index 000000000000..8be9488c46e5
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/multipass/default.nix
@@ -0,0 +1,143 @@
+{ cmake
+, dnsmasq
+, fetchFromGitHub
+, git
+, gtest
+, iproute2
+, iptables
+, lib
+, libapparmor
+, libvirt
+, libxml2
+, nixosTests
+, openssl
+, OVMF
+, pkg-config
+, qemu
+, qemu-utils
+, qtbase
+, qtx11extras
+, slang
+, stdenv
+, wrapQtAppsHook
+, xterm
+}:
+
+let
+  pname = "multipass";
+  version = "1.12.2";
+
+  # This is done here because a CMakeLists.txt from one of it's submodules tries
+  # to modify a file, so we grab the source for the submodule here, copy it into
+  # the source of the Multipass project which allows the modification to happen.
+  grpc_src = fetchFromGitHub {
+    owner = "CanonicalLtd";
+    repo = "grpc";
+    rev = "e3acf245";
+    hash = "sha256-tDc2iGxIV68Yi4RL8ES4yglJNlu8yH6FlpVvZoWjoXk=";
+    fetchSubmodules = true;
+  };
+in
+stdenv.mkDerivation
+{
+  inherit pname version;
+
+  src = fetchFromGitHub {
+    owner = "canonical";
+    repo = "multipass";
+    rev = "refs/tags/v${version}";
+    hash = "sha256-1k0jbYMwfYuHmM/Cm76sbo3+mN6WypALMQBwlZ+9d+c=";
+    fetchSubmodules = true;
+    leaveDotGit = true;
+    postFetch = ''
+      # Workaround for https://github.com/NixOS/nixpkgs/issues/8567
+      cd $out
+      rm -rf .git
+    '';
+  };
+
+  patches = [
+    ./lxd_socket_path.patch
+    ./cmake_no_fetch.patch
+    ./cmake_warning.patch
+  ];
+
+  postPatch = ''
+    # Make sure the version is reported correctly in the compiled binary.
+    substituteInPlace ./CMakeLists.txt \
+      --replace "determine_version(MULTIPASS_VERSION)" "" \
+      --replace 'set(MULTIPASS_VERSION ''${MULTIPASS_VERSION})' 'set(MULTIPASS_VERSION "v${version}")'
+
+    # Patch the patch of the OVMF binaries to use paths from the nix store.
+    substituteInPlace ./src/platform/backends/qemu/linux/qemu_platform_detail_linux.cpp \
+      --replace "OVMF.fd" "${OVMF.fd}/FV/OVMF.fd" \
+      --replace "QEMU_EFI.fd" "${OVMF.fd}/FV/QEMU_EFI.fd"
+
+    # Copy the grpc submodule we fetched into the source code.
+    cp -r --no-preserve=mode ${grpc_src} 3rd-party/grpc
+
+    # Configure CMake to use gtest from the nix store since we disabled fetching from the internet.
+    cat >> tests/CMakeLists.txt <<'EOF'
+      add_library(gtest INTERFACE)
+      target_include_directories(gtest INTERFACE ${gtest.dev}/include)
+      target_link_libraries(gtest INTERFACE ${gtest}/lib/libgtest.so ''${CMAKE_THREAD_LIBS_INIT})
+      add_dependencies(gtest GMock)
+
+      add_library(gtest_main INTERFACE)
+      target_include_directories(gtest_main INTERFACE ${gtest.dev}/include)
+      target_link_libraries(gtest_main INTERFACE ${gtest}/lib/libgtest_main.so gtest)
+
+      add_library(gmock INTERFACE)
+      target_include_directories(gmock INTERFACE ${gtest.dev}/include)
+      target_link_libraries(gmock INTERFACE ${gtest}/lib/libgmock.so gtest)
+
+      add_library(gmock_main INTERFACE)
+      target_include_directories(gmock_main INTERFACE ${gtest.dev}/include)
+      target_link_libraries(gmock_main INTERFACE ${gtest}/lib/libgmock_main.so gmock gtest_main)
+    EOF
+  '';
+
+  buildInputs = [
+    gtest
+    libapparmor
+    libvirt
+    libxml2
+    openssl
+    qtbase
+    qtx11extras
+  ];
+
+  nativeBuildInputs = [
+    cmake
+    git
+    pkg-config
+    slang
+    wrapQtAppsHook
+  ];
+
+  nativeCheckInputs = [ gtest ];
+
+  postInstall = ''
+    wrapProgram $out/bin/multipassd --prefix PATH : ${lib.makeBinPath [
+      dnsmasq
+      iproute2
+      iptables
+      OVMF.fd
+      qemu
+      qemu-utils
+      xterm
+    ]}
+  '';
+
+  passthru.tests = {
+    multipass = nixosTests.multipass;
+  };
+
+  meta = with lib; {
+    description = "Ubuntu VMs on demand for any workstation.";
+    homepage = "https://multipass.run";
+    license = licenses.gpl3Plus;
+    maintainers = with maintainers; [ jnsgruk ];
+    platforms = [ "x86_64-linux" ];
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/multipass/lxd_socket_path.patch b/nixpkgs/pkgs/tools/virtualization/multipass/lxd_socket_path.patch
new file mode 100644
index 000000000000..e23e8f72dcd7
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/multipass/lxd_socket_path.patch
@@ -0,0 +1,13 @@
+diff --git a/src/platform/backends/lxd/lxd_request.h b/src/platform/backends/lxd/lxd_request.h
+index 4b5e8840..5e673ad7 100644
+--- a/src/platform/backends/lxd/lxd_request.h
++++ b/src/platform/backends/lxd/lxd_request.h
+@@ -27,7 +27,7 @@
+ 
+ namespace multipass
+ {
+-const QUrl lxd_socket_url{"unix:///var/snap/lxd/common/lxd/unix.socket@1.0"};
++const QUrl lxd_socket_url{"unix:///var/lib/lxd/unix.socket@1.0"};
+ const QString lxd_project_name{"multipass"};
+ 
+ class NetworkAccessManager;
diff --git a/nixpkgs/pkgs/tools/virtualization/nixos-container/default.nix b/nixpkgs/pkgs/tools/virtualization/nixos-container/default.nix
new file mode 100644
index 000000000000..0b44bde4719f
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/nixos-container/default.nix
@@ -0,0 +1,38 @@
+{ substituteAll
+, perl
+, shadow
+, util-linux
+, configurationDirectory ? "/etc/nixos-containers"
+, stateDirectory ? "/var/lib/nixos-containers"
+, nixosTests
+}:
+
+substituteAll {
+    name = "nixos-container";
+    dir = "bin";
+    isExecutable = true;
+    src = ./nixos-container.pl;
+    perl = perl.withPackages (p: [ p.FileSlurp ]);
+    su = "${shadow.su}/bin/su";
+    utillinux = util-linux;
+
+    inherit configurationDirectory stateDirectory;
+
+    passthru = {
+      tests = {
+        inherit (nixosTests)
+          containers-imperative
+          containers-ip
+          containers-tmpfs
+          containers-ephemeral
+          containers-unified-hierarchy
+          ;
+      };
+    };
+
+    postInstall = ''
+      t=$out/share/bash-completion/completions
+      mkdir -p $t
+      cp ${./nixos-container-completion.sh} $t/nixos-container
+    '';
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/nixos-container/nixos-container-completion.sh b/nixpkgs/pkgs/tools/virtualization/nixos-container/nixos-container-completion.sh
new file mode 100644
index 000000000000..5298b5b12038
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/nixos-container/nixos-container-completion.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+_nixos-container() {
+    local cur prev opts
+    COMPREPLY=()
+    cur="${COMP_WORDS[COMP_CWORD]}"
+    prev="${COMP_WORDS[COMP_CWORD-1]}"
+    opts="list create destroy restart start stop status update login root-login run show-ip show-host-key"
+    startstop_opts=$(nixos-container list)
+    update_opts="--config"
+
+    if [[ "$prev" == "nixos-container" ]]
+    then
+        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+        return 0
+    fi
+
+    if [[ $(echo "$opts" | grep "$prev") ]]
+    then
+        if [[ "$prev" == "start" || "$prev" == "stop" ]]
+        then
+            COMPREPLY=( $(compgen -W "${startstop_opts}" -- ${cur}) )
+            return 0
+        elif [[ "$prev" == "update" ]]
+        then
+            COMPREPLY=( $(compgen -W "${update_opts}" -- ${cur}) )
+            return 0
+        fi
+    fi
+}
+
+complete -F _nixos-container nixos-container
+
diff --git a/nixpkgs/pkgs/tools/virtualization/nixos-container/nixos-container.pl b/nixpkgs/pkgs/tools/virtualization/nixos-container/nixos-container.pl
new file mode 100755
index 000000000000..854557eb7237
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/nixos-container/nixos-container.pl
@@ -0,0 +1,491 @@
+#! @perl@/bin/perl
+
+use strict;
+use POSIX;
+use File::Path;
+use File::Slurp;
+use Fcntl ':flock';
+use Getopt::Long qw(:config gnu_getopt no_bundling);
+use Cwd 'abs_path';
+use Time::HiRes;
+
+my $nsenter = "@utillinux@/bin/nsenter";
+my $su = "@su@";
+
+my $configurationDirectory = "@configurationDirectory@";
+my $stateDirectory = "@stateDirectory@";
+
+# Ensure a consistent umask.
+umask 0022;
+
+# Ensure $NIXOS_CONFIG is not set.
+$ENV{"NIXOS_CONFIG"} = "";
+
+# Parse the command line.
+
+sub showHelp {
+    print <<EOF;
+Usage: nixos-container list
+       nixos-container create <container-name>
+         [--nixos-path <path>]
+         [--system-path <path>]
+         [--config <string>]
+         [--config-file <path>]
+         [--flake <flakeref>]
+         [--ensure-unique-name]
+         [--auto-start]
+         [--bridge <iface>]
+         [--port <port>]
+         [--host-address <string>]
+         [--local-address <string>]
+       nixos-container destroy <container-name>
+       nixos-container restart <container-name>
+       nixos-container start <container-name>
+       nixos-container stop <container-name>
+       nixos-container terminate <container-name>
+       nixos-container status <container-name>
+       nixos-container update <container-name>
+         [--config <string>]
+         [--config-file <path>]
+         [--flake <flakeref>]
+         [--nixos-path <path>]
+       nixos-container login <container-name>
+       nixos-container root-login <container-name>
+       nixos-container run <container-name> -- args...
+       nixos-container show-ip <container-name>
+       nixos-container show-host-key <container-name>
+EOF
+    exit 0;
+}
+
+my $systemPath;
+my $nixosPath;
+my $ensureUniqueName = 0;
+my $autoStart = 0;
+my $bridge;
+my $port;
+my $extraConfig;
+my $signal;
+my $configFile;
+my $hostAddress;
+my $localAddress;
+my $flake;
+my $flakeAttr = "container";
+
+# Nix passthru flags.
+my @nixFlags;
+my @nixFlags2;
+
+sub copyNixFlags0 { push @nixFlags, "--$_[0]"; }
+sub copyNixFlags1 { push @nixFlags, "--$_[0]", $_[1]; }
+
+# Ugly hack to handle flags that take two arguments, like --option.
+sub copyNixFlags2 {
+    if (scalar(@nixFlags2) % 3 == 0) {
+        push @nixFlags2, "--$_[0]", $_[1];
+    } else {
+        push @nixFlags2, $_[1];
+    }
+}
+
+GetOptions(
+    "help" => sub { showHelp() },
+    "ensure-unique-name" => \$ensureUniqueName,
+    "auto-start" => \$autoStart,
+    "bridge=s" => \$bridge,
+    "port=s" => \$port,
+    "system-path=s" => \$systemPath,
+    "signal=s" => \$signal,
+    "nixos-path=s" => \$nixosPath,
+    "config=s" => \$extraConfig,
+    "config-file=s" => \$configFile,
+    "host-address=s" => \$hostAddress,
+    "local-address=s" => \$localAddress,
+    "flake=s" => \$flake,
+    # Nix passthru options.
+    "log-format=s" => \&copyNixFlags1,
+    "option=s{2}" => \&copyNixFlags2,
+    "impure" => \&copyNixFlags0,
+    "update-input=s" => \&copyNixFlags1,
+    "override-input=s{2}" => \&copyNixFlags2,
+    "commit-lock-file" => \&copyNixFlags0,
+    "no-registries" => \&copyNixFlags0,
+    "no-update-lock-file" => \&copyNixFlags0,
+    "no-write-lock-file" => \&copyNixFlags0,
+    "no-allow-dirty" => \&copyNixFlags0,
+    "recreate-lock-file" => \&copyNixFlags0,
+    ) or exit 1;
+
+push @nixFlags, @nixFlags2;
+
+if (defined $hostAddress and !defined $localAddress or defined $localAddress and !defined $hostAddress) {
+    die "With --host-address set, --local-address is required as well!";
+}
+
+my $action = $ARGV[0] or die "$0: no action specified\n";
+
+if (defined $configFile and defined $extraConfig) {
+    die "--config and --config-file are mutually incompatible. " .
+        "Please define one or the other, but not both";
+}
+
+if (defined $flake && $flake =~ /^(.*)#([^#"]+)$/) {
+    $flake = $1;
+    $flakeAttr = $2;
+}
+
+# Execute the selected action.
+
+mkpath("$configurationDirectory", 0, 0755);
+mkpath("$stateDirectory", 0, 0700);
+
+
+if ($action eq "list") {
+    foreach my $confFile (glob "$configurationDirectory/*.conf") {
+        # Filter libpod configuration files
+        # From 22.05 and onwards this is not an issue any more as directories dont clash
+        if($confFile eq "/etc/containers/libpod.conf" || $confFile eq "/etc/containers/containers.conf" || $confFile eq "/etc/containers/registries.conf") {
+            next
+        }
+        $confFile =~ /\/([^\/]+).conf$/ or next;
+        print "$1\n";
+    }
+    exit 0;
+}
+
+my $containerName = $ARGV[1] or die "$0: no container name specified\n";
+$containerName =~ /^[a-zA-Z0-9_-]+$/ or die "$0: invalid container name\n";
+
+sub writeNixOSConfig {
+    my ($nixosConfigFile) = @_;
+
+    my $localExtraConfig = "";
+
+    if ($extraConfig) {
+        $localExtraConfig = $extraConfig
+    } elsif ($configFile) {
+        my $resolvedFile = abs_path($configFile);
+        $localExtraConfig = "imports = [ $resolvedFile ];"
+    }
+
+    my $nixosConfig = <<EOF;
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{ boot.isContainer = true;
+  networking.hostName = mkDefault "$containerName";
+  networking.useDHCP = false;
+  $localExtraConfig
+}
+EOF
+
+    write_file($nixosConfigFile, $nixosConfig);
+}
+
+sub buildFlake {
+    system("nix", "build", "-o", "$systemPath.tmp", @nixFlags, "--",
+           "$flake#nixosConfigurations.\"$flakeAttr\".config.system.build.toplevel") == 0
+        or die "$0: failed to build container from flake '$flake'\n";
+    $systemPath = readlink("$systemPath.tmp") or die;
+    unlink("$systemPath.tmp");
+}
+
+sub clearContainerState {
+    my ($profileDir, $gcRootsDir, $root, $configFile) = @_;
+
+    safeRemoveTree($profileDir) if -e $profileDir;
+    safeRemoveTree($gcRootsDir) if -e $gcRootsDir;
+    system("chattr", "-i", "$root/var/empty") if -e "$root/var/empty";
+    safeRemoveTree($root) if -e $root;
+    unlink($configFile) or die;
+}
+
+if ($action eq "create") {
+    # Acquire an exclusive lock to prevent races with other
+    # invocations of ‘nixos-container create’.
+    my $lockFN = "/run/lock/nixos-container";
+    open(my $lock, '>>', $lockFN) or die "$0: opening $lockFN: $!";
+    flock($lock, LOCK_EX) or die "$0: could not lock $lockFN: $!";
+
+    my $confFile = "$configurationDirectory/$containerName.conf";
+    my $root = "$stateDirectory/$containerName";
+
+    # Maybe generate a unique name.
+    if ($ensureUniqueName) {
+        my $base = $containerName;
+        for (my $nr = 0; ; $nr++) {
+            $confFile = "$configurationDirectory/$containerName.conf";
+            $root = "$stateDirectory/$containerName";
+            last unless -e $confFile || -e $root;
+            $containerName = "$base-$nr";
+        }
+    }
+
+    die "$0: container ‘$containerName’ already exists\n" if -e $confFile;
+
+    # Due to interface name length restrictions, container names must
+    # be restricted too.
+    die "$0: container name ‘$containerName’ is too long\n" if length $containerName > 11;
+
+    # Get an unused IP address.
+    my %usedIPs;
+    foreach my $confFile2 (glob "$configurationDirectory/*.conf") {
+        # Filter libpod configuration files
+        # From 22.05 and onwards this is not an issue any more as directories dont clash
+        if($confFile2 eq "/etc/containers/libpod.conf" || $confFile2 eq "/etc/containers/containers.conf" || $confFile2 eq "/etc/containers/registries.conf") {
+            next
+        }
+        my $s = read_file($confFile2) or die;
+        $usedIPs{$1} = 1 if $s =~ /^HOST_ADDRESS=([0-9\.]+)$/m;
+        $usedIPs{$1} = 1 if $s =~ /^LOCAL_ADDRESS=([0-9\.]+)$/m;
+    }
+
+    unless (defined $hostAddress) {
+        my $ipPrefix;
+        for (my $nr = 1; $nr < 255; $nr++) {
+            $ipPrefix = "10.233.$nr";
+            $hostAddress = "$ipPrefix.1";
+            $localAddress = "$ipPrefix.2";
+            last unless $usedIPs{$hostAddress} || $usedIPs{$localAddress};
+            $ipPrefix = undef;
+        }
+
+        die "$0: out of IP addresses\n" unless defined $ipPrefix;
+    }
+
+    my @conf;
+    push @conf, "PRIVATE_NETWORK=1\n";
+    push @conf, "HOST_ADDRESS=$hostAddress\n";
+    push @conf, "LOCAL_ADDRESS=$localAddress\n";
+    push @conf, "HOST_BRIDGE=$bridge\n";
+    push @conf, "HOST_PORT=$port\n";
+    push @conf, "AUTO_START=$autoStart\n";
+    push @conf, "FLAKE=$flake\n" if defined $flake;
+    write_file($confFile, \@conf);
+
+    close($lock);
+
+    print STDERR "host IP is $hostAddress, container IP is $localAddress\n";
+
+    # The per-container directory is restricted to prevent users on
+    # the host from messing with guest users who happen to have the
+    # same uid.
+    my $profileDir = "/nix/var/nix/profiles/per-container";
+    mkpath($profileDir, 0, 0700);
+    $profileDir = "$profileDir/$containerName";
+    mkpath($profileDir, 0, 0755);
+
+    # Build/set the initial configuration.
+    if (defined $flake) {
+        buildFlake();
+    }
+
+    if (defined $systemPath) {
+        system("nix-env", "-p", "$profileDir/system", "--set", $systemPath) == 0
+            or do {
+                clearContainerState($profileDir, "$profileDir/$containerName", $root, $confFile);
+                die "$0: failed to set initial container configuration\n";
+            };
+    } else {
+        mkpath("$root/etc/nixos", 0, 0755);
+
+        my $nixenvF = $nixosPath // "<nixpkgs/nixos>";
+        my $nixosConfigFile = "$root/etc/nixos/configuration.nix";
+        writeNixOSConfig $nixosConfigFile;
+
+        system("nix-env", "-p", "$profileDir/system",
+               "-I", "nixos-config=$nixosConfigFile", "-f", "$nixenvF",
+               "--set", "-A", "system", @nixFlags) == 0
+            or do {
+                clearContainerState($profileDir, "$profileDir/$containerName", $root, $confFile);
+                die "$0: failed to build initial container configuration\n"
+            };
+    }
+
+    print "$containerName\n" if $ensureUniqueName;
+    exit 0;
+}
+
+my $root = "$stateDirectory/$containerName";
+my $profileDir = "/nix/var/nix/profiles/per-container/$containerName";
+my $gcRootsDir = "/nix/var/nix/gcroots/per-container/$containerName";
+my $confFile = "$configurationDirectory/$containerName.conf";
+if (!-e $confFile) {
+    if ($action eq "destroy") {
+        exit 0;
+    } elsif ($action eq "status") {
+        print "gone\n";
+    }
+    die "$0: container ‘$containerName’ does not exist\n" ;
+}
+
+# Return the PID of the init process of the container.
+sub getLeader {
+    my $s = `machinectl show "$containerName" -p Leader`;
+    chomp $s;
+    $s =~ /^Leader=(\d+)$/ or die "unable to get container's main PID\n";
+    return int($1);
+}
+
+sub isContainerRunning {
+    my $status = `systemctl show 'container\@$containerName'`;
+    return $status =~ /ActiveState=active/;
+}
+
+sub terminateContainer {
+    my $leader = getLeader;
+    system("machinectl", "terminate", $containerName) == 0
+        or die "$0: failed to terminate container\n";
+    # Wait for the leader process to exit
+    # TODO: As for any use of PIDs for process control where the process is
+    #       not a direct child of ours, this can go wrong when the pid gets
+    #       recycled after a PID overflow.
+    #       Relying entirely on some form of UUID provided by machinectl
+    #       instead of PIDs would remove this risk.
+    #       See https://github.com/NixOS/nixpkgs/pull/32992#discussion_r158586048
+    while ( kill 0, $leader ) { Time::HiRes::sleep(0.1) }
+}
+
+sub startContainer {
+    system("systemctl", "start", "container\@$containerName") == 0
+        or die "$0: failed to start container\n";
+}
+
+sub stopContainer {
+    system("systemctl", "stop", "container\@$containerName") == 0
+        or die "$0: failed to stop container\n";
+}
+
+sub restartContainer {
+    stopContainer;
+    startContainer;
+}
+
+# Run a command in the container.
+sub runInContainer {
+    my @args = @_;
+    my $leader = getLeader;
+    exec($nsenter, "-t", $leader, "-m", "-u", "-i", "-n", "-p", "--", @args);
+    die "cannot run ‘nsenter’: $!\n";
+}
+
+# Remove a directory while recursively unmounting all mounted filesystems within
+# that directory and unmounting/removing that directory afterwards as well.
+#
+# NOTE: If the specified path is a mountpoint, its contents will be removed,
+#       only mountpoints underneath that path will be unmounted properly.
+sub safeRemoveTree {
+    my ($path) = @_;
+    system("find", $path, "-mindepth", "1", "-xdev",
+           "(", "-type", "d", "-exec", "mountpoint", "-q", "{}", ";", ")",
+           "-exec", "umount", "-fR", "{}", "+");
+    system("rm", "--one-file-system", "-rf", $path);
+    if (-e $path) {
+        system("umount", "-fR", $path);
+        system("rm", "--one-file-system", "-rf", $path);
+    }
+}
+
+if ($action eq "destroy") {
+    die "$0: cannot destroy declarative container (remove it from your configuration.nix instead)\n"
+        unless POSIX::access($confFile, &POSIX::W_OK);
+
+    terminateContainer if (isContainerRunning);
+
+    clearContainerState($profileDir, $gcRootsDir, $root, $confFile);
+}
+
+elsif ($action eq "restart") {
+    restartContainer;
+}
+
+elsif ($action eq "start") {
+    startContainer;
+}
+
+elsif ($action eq "stop") {
+    stopContainer;
+}
+
+elsif ($action eq "terminate") {
+    terminateContainer;
+}
+
+elsif ($action eq "status") {
+    print isContainerRunning() ? "up" : "down", "\n";
+}
+
+elsif ($action eq "update") {
+
+    # Unless overriden on the command line, rebuild the flake recorded
+    # in the container config file. FIXME: read the container config
+    # in a more sensible way.
+    if (!defined $flake && !defined $configFile && !defined $extraConfig) {
+        my $s = read_file($confFile);
+        $s =~ /^FLAKE=(.*)$/m;
+        $flake = $1;
+    }
+
+    if (defined $flake) {
+        buildFlake();
+        system("nix-env", "-p", "$profileDir/system", "--set", $systemPath) == 0
+            or die "$0: failed to set container configuration\n";
+    } else {
+
+        my $nixosConfigFile = "$root/etc/nixos/configuration.nix";
+
+        # FIXME: may want to be more careful about clobbering the existing
+        # configuration.nix.
+        if ((defined $extraConfig && $extraConfig ne "") ||
+            (defined $configFile && $configFile ne "")) {
+            writeNixOSConfig $nixosConfigFile;
+        }
+
+        my $nixenvF = $nixosPath // "<nixpkgs/nixos>";
+        system("nix-env", "-p", "$profileDir/system",
+               "-I", "nixos-config=$nixosConfigFile", "-f", $nixenvF,
+               "--set", "-A", "system", @nixFlags) == 0
+            or die "$0: failed to build container configuration\n";
+    }
+
+    if (isContainerRunning) {
+        print STDERR "reloading container...\n";
+        system("systemctl", "reload", "container\@$containerName") == 0
+            or die "$0: failed to reload container\n";
+    }
+}
+
+elsif ($action eq "login") {
+    exec("machinectl", "login", "--", $containerName);
+}
+
+elsif ($action eq "root-login") {
+    runInContainer("@su@", "root", "-l");
+}
+
+elsif ($action eq "run") {
+    shift @ARGV; shift @ARGV;
+    # Escape command.
+    my $s = join(' ', map { s/'/'\\''/g; "'$_'" } @ARGV);
+    runInContainer("@su@", "root", "-l", "-c", "exec " . $s);
+}
+
+elsif ($action eq "show-ip") {
+    my $s = read_file($confFile) or die;
+    $s =~ /^LOCAL_ADDRESS=([0-9\.]+)(\/[0-9]+)?$/m
+        or $s =~ /^LOCAL_ADDRESS6=([0-9a-f:]+)(\/[0-9]+)?$/m
+        or die "$0: cannot get IP address\n";
+    print "$1\n";
+}
+
+elsif ($action eq "show-host-key") {
+    my $fn = "$root/etc/ssh/ssh_host_ed25519_key.pub";
+    $fn = "$root/etc/ssh/ssh_host_ecdsa_key.pub" unless -e $fn;
+    exit 1 if ! -f $fn;
+    print read_file($fn);
+}
+
+else {
+    die "$0: unknown action ‘$action’\n";
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/nixos-shell/default.nix b/nixpkgs/pkgs/tools/virtualization/nixos-shell/default.nix
new file mode 100644
index 000000000000..6f7340835523
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/nixos-shell/default.nix
@@ -0,0 +1,31 @@
+{ lib, stdenv, nix, jq, fetchFromGitHub, makeWrapper }:
+
+stdenv.mkDerivation rec {
+  pname = "nixos-shell";
+  version = "1.1.0";
+
+  src = fetchFromGitHub {
+    owner = "Mic92";
+    repo = "nixos-shell";
+    rev = version;
+    sha256 = "sha256-5hHkokfSY3Z4VqjNm/j564l9Bgynw5H5McX0hTruGoI=";
+  };
+
+  nativeBuildInputs = [ makeWrapper ];
+
+  postInstall = ''
+    wrapProgram $out/bin/nixos-shell \
+      --prefix PATH : ${lib.makeBinPath [ nix jq ]}
+  '';
+
+  installFlags = [ "PREFIX=${placeholder "out"}" ];
+
+  meta = with lib; {
+    description = "Spawns lightweight nixos vms in a shell";
+    inherit (src.meta) homepage;
+    license = licenses.mit;
+    maintainers = with maintainers; [ mic92 ];
+    platforms = platforms.unix;
+    mainProgram = "nixos-shell";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/onmetal-image/default.nix b/nixpkgs/pkgs/tools/virtualization/onmetal-image/default.nix
new file mode 100644
index 000000000000..2b535dfb6d93
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/onmetal-image/default.nix
@@ -0,0 +1,40 @@
+{ lib
+, buildGoModule
+, fetchFromGitHub
+, installShellFiles
+}:
+
+buildGoModule rec {
+  pname = "onmetal-image";
+  version = "0.1.1";
+
+  src = fetchFromGitHub {
+    owner = "onmetal";
+    repo = "onmetal-image";
+    rev = "v${version}";
+    hash = "sha256-KvOBvAIE9V2bj5prdcc8G5ifHsvybHBCYWrI4fWtdvE=";
+  };
+
+  vendorHash = "sha256-aCL8hLcBnIs5BJM7opIwcOLvOS3uL9mYXs1vOAMlX/M=";
+
+  nativeBuildInputs = [
+    installShellFiles
+  ];
+
+  postInstall = ''
+    mv $out/bin/cmd $out/bin/onmetal-image
+
+    installShellCompletion --cmd onmetal-image \
+      --bash <($out/bin/onmetal-image completion bash) \
+      --fish <($out/bin/onmetal-image completion fish) \
+      --zsh <($out/bin/onmetal-image completion zsh)
+  '';
+
+  meta = with lib; {
+    description = "Onmetal OCI Image Specification, Library and Tooling";
+    homepage = "https://github.com/onmetal/onmetal-image";
+    license = licenses.asl20;
+    maintainers = with maintainers; [ ];
+    mainProgram = "onmetal-image";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/ovftool/default.nix b/nixpkgs/pkgs/tools/virtualization/ovftool/default.nix
new file mode 100644
index 000000000000..9bdf4429ff16
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/ovftool/default.nix
@@ -0,0 +1,150 @@
+{ lib, stdenv, fetchurl, system ? builtins.currentSystem, ovftoolBundles ? {}
+, autoPatchelfHook, makeWrapper, unzip
+, glibc, c-ares, libxcrypt-legacy, expat, icu60, xercesc, zlib
+}:
+
+let
+  version = "4.6.2-22220919";
+  version_i686 = "4.6.0-21452615";
+
+  ovftoolZipUnpackPhase = ''
+    runHook preUnpack
+    unzip ${ovftoolSource}
+    extracted=ovftool/
+    if [ -d "$extracted" ]; then
+      echo "ovftool extracted successfully" >&2
+    else
+      echo "Could not find $extracted - are you sure this is ovftool?" >&2
+      exit 1
+    fi
+    runHook postUnpack
+  '';
+
+  ovftoolSystems = let
+    baseUrl = "https://vdc-download.vmware.com/vmwb-repository/dcr-public";
+  in {
+    "i686-linux" = rec {
+      name = "VMware-ovftool-${version_i686}-lin.i386.zip";
+      url = "${baseUrl}/7254abb2-434d-4f5d-83e2-9311ced9752e/57e666a2-874c-48fe-b1d2-4b6381f7fe97/${name}";
+      hash = "sha256-qEOr/3SW643G5ZQQNJTelZbUxB8HmxPd5uD+Gqsoxz0=";
+      unpackPhase = ovftoolZipUnpackPhase;
+    };
+    "x86_64-linux" = rec {
+      name = "VMware-ovftool-${version}-lin.x86_64.zip";
+      url = "${baseUrl}/8a93ce23-4f88-4ae8-b067-ae174291e98f/c609234d-59f2-4758-a113-0ec5bbe4b120/${name}";
+      hash = "sha256-3B1cUDldoTqLsbSARj2abM65nv+Ot0z/Fa35/klJXEY=";
+      unpackPhase = ovftoolZipUnpackPhase;
+    };
+  };
+
+  ovftoolSystem = if builtins.hasAttr system ovftoolSystems then
+                    ovftoolSystems.${system}
+                  else throw "System '${system}' is unsupported by ovftool";
+
+  ovftoolSource = if builtins.hasAttr system ovftoolBundles then
+                    ovftoolBundles.${system}
+                  else
+                    fetchurl {
+                      inherit (ovftoolSystem) name url hash;
+                    };
+in
+stdenv.mkDerivation rec {
+  pname = "ovftool";
+  inherit version;
+
+  src = ovftoolSource;
+
+  buildInputs = [
+    glibc
+    libxcrypt-legacy
+    c-ares
+    expat
+    icu60
+    xercesc
+    zlib
+  ];
+
+  nativeBuildInputs = [ autoPatchelfHook makeWrapper unzip ];
+
+  preferLocalBuild = true;
+
+  sourceRoot = ".";
+
+  unpackPhase = ovftoolSystem.unpackPhase;
+
+  # Expects a directory named 'ovftool' containing the ovftool install.
+  # Based on https://aur.archlinux.org/packages/vmware-ovftool/
+  # with the addition of a libexec directory and a Nix-style binary wrapper.
+  installPhase = ''
+    runHook preInstall
+    if [ -d ovftool ]; then
+      # Ensure we're in the staging directory
+      cd ovftool
+    fi
+    # libraries
+    install -m 755 -d "$out/lib/${pname}"
+    # These all appear to be VMWare proprietary except for libgoogleurl and libcurl.
+    # The rest of the libraries that the installer extracts are omitted here,
+    # and provided in buildInputs. Since libcurl depends on VMWare's OpenSSL,
+    # we have to use both here too.
+    #
+    # FIXME: can we replace libgoogleurl? Possibly from Chromium?
+    # FIXME: tell VMware to use a modern version of OpenSSL.
+    #
+    install -m 644 -t "$out/lib/${pname}" \
+      libgoogleurl.so.59 \
+      libssoclient.so \
+      libvim-types.so libvmacore.so libvmomi.so \
+      libcurl.so.4 libcrypto.so.1.0.2 libssl.so.1.0.2
+    # libexec binaries
+    install -m 755 -d "$out/libexec/${pname}"
+    install -m 755 -t "$out/libexec/${pname}" ovftool.bin
+    install -m 644 -t "$out/libexec/${pname}" icudt44l.dat
+    # libexec resources
+    for subdir in "certs" "env" "env/en" "schemas/DMTF" "schemas/vmware"; do
+      install -m 755 -d "$out/libexec/${pname}/$subdir"
+      install -m 644 -t "$out/libexec/${pname}/$subdir" "$subdir"/*.*
+    done
+    # EULA/OSS files
+    install -m 755 -d "$out/share/licenses/${pname}"
+    install -m 644 -t "$out/share/licenses/${pname}" \
+      "vmware.eula" "vmware-eula.rtf" "open_source_licenses.txt"
+    # documentation files
+    install -m 755 -d "$out/share/doc/${pname}"
+    install -m 644 -t "$out/share/doc/${pname}" "README.txt"
+    # binary wrapper; note that LC_CTYPE is defaulted to en_US.UTF-8 by
+    # VMWare's wrapper script. We use C.UTF-8 instead.
+    install -m 755 -d "$out/bin"
+    makeWrapper "$out/libexec/${pname}/ovftool.bin" "$out/bin/ovftool" \
+      --set-default LC_CTYPE C.UTF-8 \
+      --prefix LD_LIBRARY_PATH : "$out/lib"
+    runHook postInstall
+  '';
+
+  preFixup = ''
+    addAutoPatchelfSearchPath "$out/lib"
+  '';
+
+  doInstallCheck = true;
+
+  installCheckPhase = ''
+    # This is a NixOS 22.11 image (doesn't actually matter) with a 1 MiB root disk that's all zero.
+    # Make sure that it converts properly.
+    mkdir -p ovftool-check
+    cd ovftool-check
+
+    $out/bin/ovftool ${./installCheckPhase.ova} nixos.ovf
+    if [ ! -f nixos.ovf ] || [ ! -f nixos.mf ] || [ ! -f nixos-disk1.vmdk ]; then
+      exit 1
+    fi
+  '';
+
+  meta = with lib; {
+    description = "VMWare tools for working with OVF, OVA, and VMX images";
+    sourceProvenance = with sourceTypes; [ binaryNativeCode ];
+    license = licenses.unfree;
+    maintainers = with maintainers; [ numinit wolfangaukang ];
+    platforms = builtins.attrNames ovftoolSystems;
+    mainProgram = "ovftool";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/ovftool/installCheckPhase.ova b/nixpkgs/pkgs/tools/virtualization/ovftool/installCheckPhase.ova
new file mode 100644
index 000000000000..3bb164029925
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/ovftool/installCheckPhase.ova
Binary files differdiff --git a/nixpkgs/pkgs/tools/virtualization/reg/default.nix b/nixpkgs/pkgs/tools/virtualization/reg/default.nix
new file mode 100644
index 000000000000..545b208035b3
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/reg/default.nix
@@ -0,0 +1,42 @@
+{ lib
+, fetchpatch
+, fetchFromGitHub
+, buildGoModule
+}:
+
+buildGoModule rec {
+  pname = "reg";
+  version = "0.16.1";
+
+  src = fetchFromGitHub {
+    owner = "genuinetools";
+    repo = "reg";
+    rev = "v${version}";
+    hash = "sha256-tfBetjoJkr84XLEEcfdRTtc0UZ4m/uRH1Fpr91lQn8o=";
+  };
+
+  patches = [
+    # https://github.com/genuinetools/reg/pull/218
+    (fetchpatch {
+      name = "update-x-sys-for-go-1.18-on-aarch64-darwin.patch";
+      url = "https://github.com/genuinetools/reg/commit/f37b04ad8be47f1e68ef9b2c5906324d7096231e.patch";
+      hash = "sha256-wmBjPdrpNpxx//I+d+k8DNR11TmyXxb84vXR/MrYHKA=";
+    })
+    (fetchpatch {
+      name = "update-vendored-dependencies.patch";
+      url = "https://github.com/genuinetools/reg/commit/8bb04bc3fd41c089716e65ee905c6feac8bda5a0.patch";
+      hash = "sha256-5nZaayy0xu7sOJHVCp6AotINiF7vXk2Jic8SR8ZrH/g=";
+    })
+  ];
+
+  vendorHash = null;
+  doCheck = false;
+
+  meta = with lib; {
+    description = "Docker registry v2 command line client and repo listing generator with security checks";
+    homepage = "https://github.com/genuinetools/reg";
+    license = licenses.mit;
+    maintainers = with maintainers; [ ereslibre ];
+    mainProgram = "reg";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/rootlesskit/default.nix b/nixpkgs/pkgs/tools/virtualization/rootlesskit/default.nix
new file mode 100644
index 000000000000..cfd89ee451e6
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/rootlesskit/default.nix
@@ -0,0 +1,28 @@
+{ lib, buildGoModule, fetchFromGitHub, nix-update-script, nixosTests }:
+
+buildGoModule rec {
+  pname = "rootlesskit";
+  version = "1.1.1";
+
+  src = fetchFromGitHub {
+    owner = "rootless-containers";
+    repo = "rootlesskit";
+    rev = "v${version}";
+    hash = "sha256-QjGjP7GiJiP2bJE707Oc4wZ9o/gRmSboK9xGbbyG5EM=";
+  };
+
+  vendorHash = "sha256-mNuj4/e1qH3P5MfbwPLddXWhc8aDcQuoSSHZ+S+zKWw=";
+
+  passthru = {
+    updateScript = nix-update-script { };
+    tests = nixosTests.docker-rootless;
+  };
+
+  meta = with lib; {
+    homepage = "https://github.com/rootless-containers/rootlesskit";
+    description = ''Kind of Linux-native "fake root" utility, made for mainly running Docker and Kubernetes as an unprivileged user'';
+    license = licenses.asl20;
+    maintainers = with maintainers; [ offline ];
+    platforms = platforms.linux;
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/supermin/default.nix b/nixpkgs/pkgs/tools/virtualization/supermin/default.nix
new file mode 100644
index 000000000000..b7600f6bb898
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/supermin/default.nix
@@ -0,0 +1,31 @@
+{ lib, stdenv, fetchurl
+, cpio, e2fsprogs, perl, pkg-config, ocamlPackages
+, glibc
+}:
+
+stdenv.mkDerivation (finalAttrs: {
+  pname = "supermin";
+  version = "5.2.2";
+
+  src = fetchurl {
+    url = "https://download.libguestfs.org/supermin/${lib.versions.majorMinor finalAttrs.version}-stable/supermin-${finalAttrs.version}.tar.gz";
+    sha256 = "zjkh02NcgWjPt8oMWoK51c71srJx+Et3bWO4u77sNY4=";
+  };
+
+  nativeBuildInputs = [ cpio e2fsprogs perl pkg-config ]
+    ++ (with ocamlPackages; [ findlib ocaml ]);
+  buildInputs = lib.optionals stdenv.hostPlatform.isGnu [ glibc glibc.static ];
+
+  postPatch = ''
+    patchShebangs src/bin2c.pl
+  '';
+
+  meta = with lib; {
+    homepage = "https://libguestfs.org/supermin.1.html";
+    description = "Tool for creating and building supermin appliances";
+    maintainers = with maintainers; [ qyliss ];
+    license = licenses.gpl2Plus;
+    platforms = platforms.linux;
+    mainProgram = "supermin";
+  };
+})
diff --git a/nixpkgs/pkgs/tools/virtualization/udocker/default.nix b/nixpkgs/pkgs/tools/virtualization/udocker/default.nix
new file mode 100644
index 000000000000..e13376211a89
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/udocker/default.nix
@@ -0,0 +1,59 @@
+{ lib
+, fetchFromGitHub
+, singularity
+, python3Packages
+, fetchpatch
+}:
+
+python3Packages.buildPythonApplication rec {
+  pname = "udocker";
+  version = "1.3.1";
+
+  src = fetchFromGitHub {
+    owner = "indigo-dc";
+    repo = "udocker";
+    rev = "v${version}";
+    sha256 = "0dfsjgidsnah8nrclrq10yz3ja859123z81kq4zdifbrhnrn5a2x";
+  };
+
+  # crun patchelf proot runc fakechroot
+  # are download statistically linked during runtime
+  buildInputs = [
+    singularity
+  ] ++ (with python3Packages; [
+    pytest-runner
+    pycurl
+  ]);
+
+  patches = [
+    (fetchpatch {
+      url = "https://github.com/indigo-dc/udocker/commit/9f7d6c5f9a3925bf87d000603c5b306d73bb0fa3.patch";
+      sha256 = "sha256-fiqvVqfdVIlILbSs6oDWmbWU9piZEI2oiAKUcmecx9Q=";
+    })
+  ];
+
+  nativeCheckInputs = with python3Packages; [
+    pytestCheckHook
+  ];
+
+  disabledTests = [
+    "test_02__load_structure"
+    "test_05__get_volume_bindings"
+  ];
+
+  disabledTestPaths = [
+    # Network
+    "tests/unit/test_curl.py"
+    "tests/unit/test_dockerioapi.py"
+  ];
+
+  meta = with lib; {
+    description = "basic user tool to execute simple docker containers in user space without root privileges";
+    homepage = "https://indigo-dc.gitbooks.io/udocker";
+    license = licenses.asl20;
+    maintainers = [ maintainers.bzizou ];
+    platforms = platforms.linux;
+    mainProgram = "udocker";
+  };
+
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/uefi-run/default.nix b/nixpkgs/pkgs/tools/virtualization/uefi-run/default.nix
new file mode 100644
index 000000000000..9d954d9ed03d
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/uefi-run/default.nix
@@ -0,0 +1,26 @@
+{ lib
+, fetchFromGitHub
+, rustPlatform
+}:
+
+rustPlatform.buildRustPackage rec {
+  pname = "uefi-run";
+  version = "0.6.1";
+
+  src = fetchFromGitHub {
+    owner = "Richard-W";
+    repo = pname;
+    rev = "v${version}";
+    hash = "sha256-tR547osqw18dCMHJLqJ8AQBelbv8yCl7rAqslu+vnDQ=";
+  };
+
+  cargoHash = "sha256-s1Kbc3JHoYy0UJwNfSunIdQ3xHjlQaut/Cb0JSYyB9g=";
+
+  meta = with lib; {
+    description = "Directly run UEFI applications in qemu";
+    homepage = "https://github.com/Richard-W/uefi-run";
+    license = licenses.mit;
+    maintainers = with maintainers; [ ];
+    mainProgram = "uefi-run";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/vpsfree-client/Gemfile b/nixpkgs/pkgs/tools/virtualization/vpsfree-client/Gemfile
new file mode 100644
index 000000000000..6bcacb5ee900
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/vpsfree-client/Gemfile
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gem "vpsfree-client", "0.17.1"
diff --git a/nixpkgs/pkgs/tools/virtualization/vpsfree-client/Gemfile.lock b/nixpkgs/pkgs/tools/virtualization/vpsfree-client/Gemfile.lock
new file mode 100644
index 000000000000..88d4f92a5b9a
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/vpsfree-client/Gemfile.lock
@@ -0,0 +1,58 @@
+GEM
+  remote: https://rubygems.org/
+  specs:
+    activesupport (7.0.5)
+      concurrent-ruby (~> 1.0, >= 1.0.2)
+      i18n (>= 1.6, < 2)
+      minitest (>= 5.1)
+      tzinfo (~> 2.0)
+    concurrent-ruby (1.2.2)
+    curses (1.4.4)
+    domain_name (0.5.20190701)
+      unf (>= 0.0.5, < 1.0.0)
+    haveapi-client (0.16.3)
+      activesupport (>= 4.0)
+      highline (~> 2.0.3)
+      json
+      require_all (~> 2.0.0)
+      rest-client (~> 2.1.0)
+      ruby-progressbar (~> 1.11.0)
+    highline (2.0.3)
+    http-accept (1.7.0)
+    http-cookie (1.0.5)
+      domain_name (~> 0.5)
+    i18n (1.14.1)
+      concurrent-ruby (~> 1.0)
+    json (2.6.3)
+    mime-types (3.4.1)
+      mime-types-data (~> 3.2015)
+    mime-types-data (3.2023.0218.1)
+    minitest (5.18.0)
+    netrc (0.11.0)
+    require_all (2.0.0)
+    rest-client (2.1.0)
+      http-accept (>= 1.7.0, < 2.0)
+      http-cookie (>= 1.0.2, < 2.0)
+      mime-types (>= 1.16, < 4.0)
+      netrc (~> 0.8)
+    ruby-progressbar (1.11.0)
+    tzinfo (2.0.6)
+      concurrent-ruby (~> 1.0)
+    unf (0.1.4)
+      unf_ext
+    unf_ext (0.0.8.2)
+    vpsadmin-client (3.0.0.master.20221118.pre.1.ac358990)
+      curses
+      haveapi-client (~> 0.16.1)
+      json
+    vpsfree-client (0.17.1)
+      vpsadmin-client (= 3.0.0.master.20221118.pre.1.ac358990)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  vpsfree-client (= 0.17.1)
+
+BUNDLED WITH
+   2.4.13
diff --git a/nixpkgs/pkgs/tools/virtualization/vpsfree-client/default.nix b/nixpkgs/pkgs/tools/virtualization/vpsfree-client/default.nix
new file mode 100644
index 000000000000..cffdc267e53c
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/vpsfree-client/default.nix
@@ -0,0 +1,18 @@
+{ lib, bundlerApp, bundlerUpdateScript }:
+
+bundlerApp {
+  pname = "vpsfree-client";
+  gemdir = ./.;
+  exes = [ "vpsfreectl" ];
+
+  passthru.updateScript = bundlerUpdateScript "vpsfree-client";
+
+  meta = with lib; {
+    description = "Ruby API and CLI for the vpsFree.cz API";
+    homepage = "https://github.com/vpsfreecz/vpsfree-client";
+    maintainers = with maintainers; [ aither64 zimbatm ];
+    license = licenses.gpl3;
+    platforms = platforms.unix;
+    mainProgram = "vpsfreectl";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/vpsfree-client/gemset.nix b/nixpkgs/pkgs/tools/virtualization/vpsfree-client/gemset.nix
new file mode 100644
index 000000000000..241fb30149d3
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/vpsfree-client/gemset.nix
@@ -0,0 +1,233 @@
+{
+  activesupport = {
+    dependencies = ["concurrent-ruby" "i18n" "minitest" "tzinfo"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "1c7k5i6531z5il4q1jnbrv7x7zcl3bgnxp5fzl71rzigk6zn53ym";
+      type = "gem";
+    };
+    version = "7.0.5";
+  };
+  concurrent-ruby = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0krcwb6mn0iklajwngwsg850nk8k9b35dhmc2qkbdqvmifdi2y9q";
+      type = "gem";
+    };
+    version = "1.2.2";
+  };
+  curses = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "00y9g79lzfffxarj3rmhnkblsnyx7izx91mh8c1sdcs9y2pdfq53";
+      type = "gem";
+    };
+    version = "1.4.4";
+  };
+  domain_name = {
+    dependencies = ["unf"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0lcqjsmixjp52bnlgzh4lg9ppsk52x9hpwdjd53k8jnbah2602h0";
+      type = "gem";
+    };
+    version = "0.5.20190701";
+  };
+  haveapi-client = {
+    dependencies = ["activesupport" "highline" "json" "require_all" "rest-client" "ruby-progressbar"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0iz0k9cwva8icc040k5m9ah0cz08jg6x51h6ahdccw6azy8h93i1";
+      type = "gem";
+    };
+    version = "0.16.3";
+  };
+  highline = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0yclf57n2j3cw8144ania99h1zinf8q3f5zrhqa754j6gl95rp9d";
+      type = "gem";
+    };
+    version = "2.0.3";
+  };
+  http-accept = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "09m1facypsdjynfwrcv19xcb1mqg8z6kk31g8r33pfxzh838c9n6";
+      type = "gem";
+    };
+    version = "1.7.0";
+  };
+  http-cookie = {
+    dependencies = ["domain_name"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "13rilvlv8kwbzqfb644qp6hrbsj82cbqmnzcvqip1p6vqx36sxbk";
+      type = "gem";
+    };
+    version = "1.0.5";
+  };
+  i18n = {
+    dependencies = ["concurrent-ruby"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0qaamqsh5f3szhcakkak8ikxlzxqnv49n2p7504hcz2l0f4nj0wx";
+      type = "gem";
+    };
+    version = "1.14.1";
+  };
+  json = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0nalhin1gda4v8ybk6lq8f407cgfrj6qzn234yra4ipkmlbfmal6";
+      type = "gem";
+    };
+    version = "2.6.3";
+  };
+  mime-types = {
+    dependencies = ["mime-types-data"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0ipw892jbksbxxcrlx9g5ljq60qx47pm24ywgfbyjskbcl78pkvb";
+      type = "gem";
+    };
+    version = "3.4.1";
+  };
+  mime-types-data = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "1pky3vzaxlgm9gw5wlqwwi7wsw3jrglrfflrppvvnsrlaiz043z9";
+      type = "gem";
+    };
+    version = "3.2023.0218.1";
+  };
+  minitest = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0ic7i5z88zcaqnpzprf7saimq2f6sad57g5mkkqsrqrcd6h3mx06";
+      type = "gem";
+    };
+    version = "5.18.0";
+  };
+  netrc = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0gzfmcywp1da8nzfqsql2zqi648mfnx6qwkig3cv36n9m0yy676y";
+      type = "gem";
+    };
+    version = "0.11.0";
+  };
+  require_all = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0sjf2vigdg4wq7z0xlw14zyhcz4992s05wgr2s58kjgin12bkmv8";
+      type = "gem";
+    };
+    version = "2.0.0";
+  };
+  rest-client = {
+    dependencies = ["http-accept" "http-cookie" "mime-types" "netrc"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "1qs74yzl58agzx9dgjhcpgmzfn61fqkk33k1js2y5yhlvc5l19im";
+      type = "gem";
+    };
+    version = "2.1.0";
+  };
+  ruby-progressbar = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "02nmaw7yx9kl7rbaan5pl8x5nn0y4j5954mzrkzi9i3dhsrps4nc";
+      type = "gem";
+    };
+    version = "1.11.0";
+  };
+  tzinfo = {
+    dependencies = ["concurrent-ruby"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "16w2g84dzaf3z13gxyzlzbf748kylk5bdgg3n1ipvkvvqy685bwd";
+      type = "gem";
+    };
+    version = "2.0.6";
+  };
+  unf = {
+    dependencies = ["unf_ext"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0bh2cf73i2ffh4fcpdn9ir4mhq8zi50ik0zqa1braahzadx536a9";
+      type = "gem";
+    };
+    version = "0.1.4";
+  };
+  unf_ext = {
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "1yj2nz2l101vr1x9w2k83a0fag1xgnmjwp8w8rw4ik2rwcz65fch";
+      type = "gem";
+    };
+    version = "0.0.8.2";
+  };
+  vpsadmin-client = {
+    dependencies = ["curses" "haveapi-client" "json"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "1rqxvfmcbpi8wcmgwdl34il3a4gg3q3zy8pyyj0kk0v8lly0wb6d";
+      type = "gem";
+    };
+    version = "3.0.0.master.20221118.pre.1.ac358990";
+  };
+  vpsfree-client = {
+    dependencies = ["vpsadmin-client"];
+    groups = ["default"];
+    platforms = [];
+    source = {
+      remotes = ["https://rubygems.org"];
+      sha256 = "0a4fmimzrysjcnvw2jz7f5hdslmy2aaipcgiisjkhqazw6nlbd8w";
+      type = "gem";
+    };
+    version = "0.17.1";
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/xe-guest-utilities/default.nix b/nixpkgs/pkgs/tools/virtualization/xe-guest-utilities/default.nix
new file mode 100644
index 000000000000..e78b36c9f84f
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/xe-guest-utilities/default.nix
@@ -0,0 +1,49 @@
+{ lib
+, buildGoModule
+, fetchFromGitHub
+, runtimeShell
+}:
+
+buildGoModule rec {
+  pname = "xe-guest-utilities";
+  version = "7.30.0";
+
+  src = fetchFromGitHub {
+    owner = "xenserver";
+    repo = "xe-guest-utilities";
+    rev = "v${version}";
+    hash = "sha256-gMb8QIUg8t0SiTtUzqeh4XM5hHgCXuf5KlV3OeoU0LI=";
+  };
+
+  vendorHash = "sha256-zhpDvo8iujE426/gxJY+Pqfv99vLNKHqyMQbbXIKodY=";
+
+  postPatch = ''
+    substituteInPlace mk/xen-vcpu-hotplug.rules \
+      --replace "/bin/sh" "${runtimeShell}"
+  '';
+
+  buildPhase = ''
+    runHook preBuild
+
+    make RELEASE=nixpkgs
+
+    runHook postBuild
+  '';
+
+  installPhase = ''
+    runHook preInstall
+
+    install -Dt "$out"/bin build/stage/usr/{,s}bin/*
+    install -Dt "$out"/etc/udev/rules.d build/stage/etc/udev/rules.d/*
+
+    runHook postInstall
+  '';
+
+  meta = {
+    description = "XenServer guest utilities";
+    homepage = "https://github.com/xenserver/xe-guest-utilities";
+    license = lib.licenses.bsd2;
+    maintainers = with lib.maintainers; [ ];
+    platforms = lib.platforms.linux;
+  };
+}
diff --git a/nixpkgs/pkgs/tools/virtualization/xva-img/default.nix b/nixpkgs/pkgs/tools/virtualization/xva-img/default.nix
new file mode 100644
index 000000000000..bb5c9d5a5992
--- /dev/null
+++ b/nixpkgs/pkgs/tools/virtualization/xva-img/default.nix
@@ -0,0 +1,25 @@
+{ stdenv, lib, cmake, fetchFromGitHub, openssl }:
+
+stdenv.mkDerivation rec {
+  pname = "xva-img";
+  version = "1.4.2";
+
+  src = fetchFromGitHub {
+    owner = "eriklax";
+    repo = "xva-img";
+    rev = version;
+    sha256 = "sha256-QHCKGsHSMT2P64No1IUCjenm1XZMSgEvsJGJOyHFZS8=";
+  };
+
+  nativeBuildInputs = [ cmake ];
+
+  buildInputs = [ openssl ];
+
+  meta = {
+    maintainers = with lib.maintainers; [ lheckemann willibutz ];
+    description = "Tool for converting Xen images to raw and back";
+    license = lib.licenses.gpl2;
+    platforms = lib.platforms.unix;
+    mainProgram = "xva-img";
+  };
+}