-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- System-wide service to configure shares for guest vms using virtiofs - Persistent fingerprint storage configured for gui-vm - Preparation for other services using this feature Signed-off-by: Manuel Bluhm <manuel@ssrc.tii.ae>
- Loading branch information
Showing
6 changed files
with
252 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,5 +4,6 @@ | |
imports = [ | ||
./dendrite-pinecone.nix | ||
./fprint.nix | ||
./storage.nix | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors | ||
# SPDX-License-Identifier: Apache-2.0 | ||
{ | ||
config, | ||
lib, | ||
... | ||
}: let | ||
inherit (lib) mkEnableOption mkOption types literalExpression; | ||
|
||
storageSubmodule = types.submodule { | ||
options = { | ||
tag = mkOption { | ||
type = types.nullOr types.str; | ||
description = '' | ||
Storage tag used as label for the microvm share. | ||
''; | ||
}; | ||
host-path = mkOption { | ||
type = types.str; | ||
description = '' | ||
Storage directory in the host. If it does not exist, it will be generated | ||
with the default owner 'microvm' and group 'kvm'. | ||
''; | ||
}; | ||
tmp-path = mkOption { | ||
type = types.nullOr types.str; | ||
default = null; | ||
description = '' | ||
(Optional) Temporary directory in the VM to mount the host directory. If this is set, | ||
the folder from host-path will be recursively copied from 'tmp-path' to 'vm-path' and owner, group, | ||
and permissions applied. | ||
If this parameter is null, the host-path is mounted to vm-path directly. | ||
''; | ||
}; | ||
vm-path = mkOption { | ||
type = types.str; | ||
description = '' | ||
Storage directory in the guest where the share is mounted to. | ||
''; | ||
}; | ||
target-vm = mkOption { | ||
type = types.str; | ||
description = '' | ||
Name of the VM the share is mounted into. The name must match the microvm | ||
name in 'config.microvm.vms': e.g., "gui-vm" (note the '-'). | ||
''; | ||
}; | ||
target-owner = mkOption { | ||
type = types.nullOr types.str; | ||
description = '' | ||
Ownership to be applied after copying from 'tmp-path' or passing the files to the vm-path. | ||
Note that the owner, group, and permissions are applied to the actual host files. This is | ||
reset on next boot by the host storage service to allow microvm to access the files. | ||
''; | ||
}; | ||
target-group = mkOption { | ||
type = types.nullOr types.str; | ||
description = '' | ||
Group to be applied after copying from 'tmp-path' or passing the files to the vm-path. | ||
Note that the owner, group, and permissions are applied to the actual host files. This is | ||
reset on next boot by the host storage service to allow microvm to access the files. | ||
''; | ||
}; | ||
target-permissions = mkOption { | ||
type = types.nullOr types.str; | ||
description = '' | ||
Permissions to be applied after copying from 'tmp-path' or passing the files to the vm-path. | ||
Format is an integer string (e.g., "755") as input for chmod. | ||
Note that the owner, group, and permissions are applied to the actual host files. This is | ||
reset on next boot by the host storage service to allow microvm to access the files. | ||
''; | ||
}; | ||
target-service = mkOption { | ||
type = types.str; | ||
default = "default.target"; | ||
description = '' | ||
Systemd unit that requires the share. If set, the share is processed in the VM before the service is started. | ||
Defaults to 'default.target'. | ||
''; | ||
}; | ||
}; | ||
}; | ||
in { | ||
options.ghaf.services.storage = { | ||
enable = mkEnableOption "Enable host-to-guest storage module"; | ||
shares = mkOption { | ||
type = types.listOf storageSubmodule; | ||
default = []; | ||
example = literalExpression '' | ||
[ | ||
{ | ||
tag = "fprint-store"; | ||
host-path = "/var/lib/fprint"; | ||
vm-path = "/var/lib/fprint"; | ||
target-vm = "gui-vm"; | ||
target-owner = "root"; | ||
target-group = "root"; | ||
target-permissions = "600"; | ||
target-service = "fprintd.service"; | ||
} | ||
{ | ||
tag = "test-store"; | ||
host-path = "/var/testfolder"; | ||
tmp-path = "/var/testfolder"; | ||
vm-path = "/run/testfolder"; | ||
target-vm = "admin-vm"; | ||
target-owner = "ghaf"; | ||
target-group = "ghaf"; | ||
target-permissions = "775"; | ||
} | ||
]; | ||
''; | ||
description = '' | ||
List of share configurations of type 'storageSubmodule'. | ||
''; | ||
}; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors | ||
# SPDX-License-Identifier: Apache-2.0 | ||
{ | ||
config, | ||
lib, | ||
pkgs, | ||
... | ||
}: let | ||
cfg = config.ghaf.services.storage; | ||
inherit (builtins) filter map listToAttrs; | ||
inherit (lib) mkIf optionals optionalAttrs optionalString forEach nameValuePair recursiveUpdate concatStringsSep unique foldl'; | ||
|
||
# Helper functions | ||
filterHostShares = filter (s: s.tag != null && s.host-path != null) cfg.shares; | ||
vmShares = filter (s: s.target-vm != null) cfg.shares; | ||
filterSharesByName = vm: filter (s: s.target-vm == vm) vmShares; | ||
sharePaths = map (s: s.host-path) filterHostShares; | ||
vmList = unique (map (s: s.target-vm) vmShares); | ||
mergeAttrSets = attrSets: foldl' (a: b: recursiveUpdate a b) {} attrSets; | ||
|
||
# Share configuration | ||
shareConfigs = vm: { | ||
microvm.shares = forEach (filterSharesByName vm) ( | ||
vmShare: { | ||
tag = "${vmShare.tag}"; | ||
source = "${vmShare.host-path}"; | ||
mountPoint = "${ | ||
if (vmShare.tmp-path != null) | ||
then vmShare.tmp-path | ||
else vmShare.vm-path | ||
}"; | ||
proto = "virtiofs"; | ||
} | ||
); | ||
}; | ||
|
||
# Service configuration | ||
serviceConfigs = vm: | ||
forEach (filterSharesByName vm) (vmShare: { | ||
environment.systemPackages = optionals (vmShare.tmp-path != null) [pkgs.umount]; | ||
systemd.services."storage-prep-${vmShare.tag}" = let | ||
# Service to set owner, permissions, and (optionally) copy files | ||
prepStorage = pkgs.writeShellScriptBin "storage_prep" '' | ||
set -xeuo pipefail | ||
if [ ! -d "${vmShare.vm-path}" ]; then | ||
mkdir -p ${vmShare.vm-path} | ||
fi | ||
${optionalString (vmShare.tmp-path != null) "cp -r ${vmShare.tmp-path}/* ${vmShare.vm-path}"} | ||
chown -R ${vmShare.target-owner}:${vmShare.target-group} ${vmShare.vm-path} | ||
chmod -R ${vmShare.target-permissions} ${vmShare.vm-path} | ||
${optionalString (vmShare.tmp-path != null) "${pkgs.umount}/bin/umount ${vmShare.tmp-path}"} | ||
''; | ||
in | ||
{ | ||
description = "Prepare host-shared files for ${vmShare.tag}"; | ||
enable = true; | ||
path = [prepStorage]; | ||
after = ["local-fs.target"]; | ||
requiredBy = ["${vmShare.target-service}"]; | ||
serviceConfig = { | ||
Type = "oneshot"; | ||
RemainAfterExit = true; | ||
Restart = "no"; | ||
StandardOutput = "journal"; | ||
StandardError = "journal"; | ||
ExecStart = "${prepStorage}/bin/storage_prep"; | ||
}; | ||
} | ||
// optionalAttrs (vmShare.tmp-path != null) { | ||
unitConfig.ConditionPathExists = "${vmShare.tmp-path}"; | ||
}; | ||
}); | ||
|
||
# Assemble VM config | ||
vmConfig = vm: { | ||
config = mergeAttrSets [ | ||
(shareConfigs vm) | ||
(mergeAttrSets (serviceConfigs vm)) | ||
]; | ||
}; | ||
in { | ||
config = mkIf cfg.enable { | ||
# Host directory generation | ||
systemd.services."storage-prep-host" = let | ||
prepStorage = pkgs.writeShellScriptBin "process_dirs" '' | ||
set -xeuo pipefail | ||
IFS=', ' read -r -a array <<< "${concatStringsSep " " sharePaths}" | ||
for path in "''${array[@]}"; do | ||
if [ ! -d "$path" ]; then | ||
mkdir -p $path | ||
fi | ||
chmod -R 770 $path | ||
done | ||
''; | ||
in { | ||
description = "Generate host storage directories"; | ||
enable = true; | ||
path = [prepStorage]; | ||
wantedBy = ["local-fs.target"]; | ||
serviceConfig = { | ||
Type = "oneshot"; | ||
RemainAfterExit = true; | ||
Restart = "no"; | ||
StandardOutput = "journal"; | ||
StandardError = "journal"; | ||
ExecStart = "${prepStorage}/bin/process_dirs"; | ||
}; | ||
}; | ||
|
||
# Extra VM config to be passed to the respective VMs | ||
microvm.vms = listToAttrs ( | ||
map (vm: nameValuePair "${vm}" (vmConfig vm)) vmList | ||
); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters