remove hypervisor vlan
remove VM support
This commit is contained in:
parent
0c882b7bcb
commit
d2f4fb1e86
8 changed files with 12 additions and 342 deletions
82
flake.lock
82
flake.lock
|
@ -175,24 +175,6 @@
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_5"
|
"systems": "systems_5"
|
||||||
},
|
},
|
||||||
"locked": {
|
|
||||||
"lastModified": 1705309234,
|
|
||||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils_4": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems_6"
|
|
||||||
},
|
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1701680307,
|
"lastModified": 1701680307,
|
||||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||||
|
@ -206,9 +188,9 @@
|
||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils_5": {
|
"flake-utils_4": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_7"
|
"systems": "systems_6"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1694529238,
|
"lastModified": 1694529238,
|
||||||
|
@ -269,28 +251,6 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"microvm": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": "flake-utils_3",
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"spectrum": "spectrum"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1710760349,
|
|
||||||
"narHash": "sha256-yqGeSnAB3Yeg02jtitzSGIuE21kq7+pWAdeGK3F9/cY=",
|
|
||||||
"owner": "astro",
|
|
||||||
"repo": "microvm.nix",
|
|
||||||
"rev": "0e2223d8fd459725850bd9aff7e9d68ec329769a",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "astro",
|
|
||||||
"repo": "microvm.nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nix-github-actions": {
|
"nix-github-actions": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
@ -330,7 +290,7 @@
|
||||||
},
|
},
|
||||||
"nixhelm": {
|
"nixhelm": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils_4",
|
"flake-utils": "flake-utils_3",
|
||||||
"nix-kube-generators": "nix-kube-generators",
|
"nix-kube-generators": "nix-kube-generators",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
|
@ -417,13 +377,13 @@
|
||||||
},
|
},
|
||||||
"poetry2nix": {
|
"poetry2nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils_5",
|
"flake-utils": "flake-utils_4",
|
||||||
"nix-github-actions": "nix-github-actions",
|
"nix-github-actions": "nix-github-actions",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixhelm",
|
"nixhelm",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"systems": "systems_8",
|
"systems": "systems_7",
|
||||||
"treefmt-nix": "treefmt-nix"
|
"treefmt-nix": "treefmt-nix"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
|
@ -448,29 +408,12 @@
|
||||||
"dns": "dns",
|
"dns": "dns",
|
||||||
"flake-utils": "flake-utils_2",
|
"flake-utils": "flake-utils_2",
|
||||||
"kubenix": "kubenix",
|
"kubenix": "kubenix",
|
||||||
"microvm": "microvm",
|
|
||||||
"nixhelm": "nixhelm",
|
"nixhelm": "nixhelm",
|
||||||
"nixos-hardware": "nixos-hardware",
|
"nixos-hardware": "nixos-hardware",
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"nixpkgs-unstable": "nixpkgs-unstable"
|
"nixpkgs-unstable": "nixpkgs-unstable"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"spectrum": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1708358594,
|
|
||||||
"narHash": "sha256-e71YOotu2FYA67HoC/voJDTFsiPpZNRwmiQb4f94OxQ=",
|
|
||||||
"ref": "refs/heads/main",
|
|
||||||
"rev": "6d0e73864d28794cdbd26ab7b37259ab0e1e044c",
|
|
||||||
"revCount": 614,
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://spectrum-os.org/git/spectrum"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://spectrum-os.org/git/spectrum"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
"systems": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681028828,
|
"lastModified": 1681028828,
|
||||||
|
@ -561,21 +504,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems_7": {
|
"systems_7": {
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems_8": {
|
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681028828,
|
"lastModified": 1681028828,
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
|
|
@ -23,11 +23,6 @@
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
microvm = {
|
|
||||||
url = "github:astro/microvm.nix";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
kubenix = {
|
kubenix = {
|
||||||
url = "github:pizzapim/kubenix/fix-protocol";
|
url = "github:pizzapim/kubenix/fix-protocol";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
|
|
@ -5,9 +5,8 @@
|
||||||
machine.nixosModule
|
machine.nixosModule
|
||||||
disko.nixosModules.disko
|
disko.nixosModules.disko
|
||||||
agenix.nixosModules.default
|
agenix.nixosModules.default
|
||||||
]
|
./physical.nix
|
||||||
++ lib.lists.optional machine.isPhysical ./physical.nix
|
];
|
||||||
++ lib.lists.optional machine.isVirtual ./virtual;
|
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
time.timeZone = "Europe/Amsterdam";
|
time.timeZone = "Europe/Amsterdam";
|
||||||
|
@ -59,17 +58,9 @@
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAX2IhgHNxC6JTvLu9cej+iWuG+uJFMXn4AiRro9533x";
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAX2IhgHNxC6JTvLu9cej+iWuG+uJFMXn4AiRro9533x";
|
||||||
certAuthority = true;
|
certAuthority = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
hypervisors = {
|
|
||||||
hostNames = [ "*.hyp" ];
|
|
||||||
publicKey =
|
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFzRkH3d/KVJQouswY/DMpenWbDFVOnI3Vut0xR0e1tb";
|
|
||||||
certAuthority = true;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
neovim = {
|
neovim = {
|
||||||
enable = true;
|
enable = true;
|
||||||
vimAlias = true;
|
vimAlias = true;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{ nixpkgs, nixpkgs-unstable, machines, physicalMachines, dns, microvm, disko, agenix, nixos-hardware, kubenix, ... }:
|
{ nixpkgs, nixpkgs-unstable, machines, physicalMachines, dns, agenix, nixos-hardware, kubenix, disko, ... }:
|
||||||
let
|
let
|
||||||
mkNixosSystems = systemDef:
|
mkNixosSystems = systemDef:
|
||||||
builtins.mapAttrs
|
builtins.mapAttrs
|
||||||
|
@ -11,7 +11,7 @@ in
|
||||||
nixosConfigurations = mkNixosSystems (name: machine: {
|
nixosConfigurations = mkNixosSystems (name: machine: {
|
||||||
system = machine.arch;
|
system = machine.arch;
|
||||||
|
|
||||||
specialArgs = { inherit nixpkgs-unstable machines machine dns microvm disko agenix nixos-hardware kubenix; };
|
specialArgs = { inherit nixpkgs-unstable machines machine dns agenix nixos-hardware kubenix disko; };
|
||||||
modules = [
|
modules = [
|
||||||
../.
|
../.
|
||||||
{ networking.hostName = name; }
|
{ networking.hostName = name; }
|
||||||
|
|
|
@ -46,7 +46,7 @@ in {
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
networking = {
|
networking = {
|
||||||
domain = if machine.isPhysical then "hyp" else "dmz";
|
domain = "dmz";
|
||||||
nftables.enable = true;
|
nftables.enable = true;
|
||||||
useDHCP = false;
|
useDHCP = false;
|
||||||
|
|
||||||
|
@ -59,96 +59,15 @@ in {
|
||||||
systemd.network = {
|
systemd.network = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
netdevs = lib.mkIf machine.isHypervisor {
|
|
||||||
"20-vlandmz" = {
|
|
||||||
vlanConfig.Id = 30;
|
|
||||||
|
|
||||||
netdevConfig = {
|
|
||||||
Kind = "vlan";
|
|
||||||
Name = "vlandmz";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
"20-bridgedmz" = {
|
|
||||||
netdevConfig = {
|
|
||||||
Kind = "bridge";
|
|
||||||
Name = cfg.dmz.bridgeName;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
networks = lib.attrsets.mergeAttrsList [
|
networks = lib.attrsets.mergeAttrsList [
|
||||||
(lib.optionalAttrs machine.isHypervisor {
|
(lib.optionalAttrs machine.isHypervisor {
|
||||||
"30-main-nic" = {
|
"30-main-nic" = {
|
||||||
matchConfig.Name = "en*";
|
matchConfig.Name = "en*";
|
||||||
vlan = [ "vlandmz" ];
|
|
||||||
|
|
||||||
networkConfig = {
|
networkConfig = {
|
||||||
DHCP = "yes";
|
DHCP = "yes";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
"40-vlandmz" = {
|
|
||||||
matchConfig.Name = "vlandmz";
|
|
||||||
linkConfig.RequiredForOnline = "enslaved";
|
|
||||||
|
|
||||||
networkConfig = {
|
|
||||||
IPv6AcceptRA = false;
|
|
||||||
LinkLocalAddressing = "no";
|
|
||||||
Bridge = cfg.dmz.bridgeName;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
"40-bridgedmz" = {
|
|
||||||
matchConfig.Name = cfg.dmz.bridgeName;
|
|
||||||
linkConfig.RequiredForOnline = "carrier";
|
|
||||||
|
|
||||||
networkConfig = {
|
|
||||||
IPv6AcceptRA = cfg.dmz.allowConnectivity;
|
|
||||||
LinkLocalAddressing = if cfg.dmz.allowConnectivity then "ipv6" else "no";
|
|
||||||
DHCP = "yes";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
"40-vms" = {
|
|
||||||
matchConfig.Name = "vm-*";
|
|
||||||
networkConfig.Bridge = cfg.dmz.bridgeName;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
(lib.optionalAttrs machine.isVirtual {
|
|
||||||
"30-main-nic" = {
|
|
||||||
matchConfig.Name = "en*";
|
|
||||||
|
|
||||||
networkConfig = {
|
|
||||||
IPv6AcceptRA = ! cfg.staticNetworking;
|
|
||||||
DHCP = lib.mkIf (! cfg.staticNetworking) "yes";
|
|
||||||
|
|
||||||
Address = lib.mkIf cfg.staticNetworking [
|
|
||||||
"${cfg.staticIPv4}/${cfg.dmz.ipv4.prefixLength}"
|
|
||||||
"${cfg.staticIPv6}/${cfg.dmz.ipv6.prefixLength}"
|
|
||||||
];
|
|
||||||
|
|
||||||
DNS = lib.mkIf cfg.staticNetworking [
|
|
||||||
cfg.dmz.ipv4.router
|
|
||||||
cfg.dmz.ipv6.router
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
routes = lib.mkIf cfg.staticNetworking [
|
|
||||||
{
|
|
||||||
routeConfig = {
|
|
||||||
Gateway = cfg.dmz.ipv4.router;
|
|
||||||
Destination = "0.0.0.0/0";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
{
|
|
||||||
routeConfig = {
|
|
||||||
Gateway = cfg.dmz.ipv6.router;
|
|
||||||
Destination = "::/0";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
(lib.optionalAttrs machine.isRaspberryPi {
|
(lib.optionalAttrs machine.isRaspberryPi {
|
||||||
"30-main-nic" = {
|
"30-main-nic" = {
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
{ pkgs, nixpkgs-unstable, config, lib, microvm, disko, agenix, machine, machines, dns, nixos-hardware, kubenix, ... }: {
|
{ pkgs, config, lib, machine, nixos-hardware, ... }: {
|
||||||
imports = [
|
imports = lib.lists.optional (machine.isRaspberryPi) nixos-hardware.nixosModules.raspberry-pi-4;
|
||||||
microvm.nixosModules.host
|
|
||||||
]
|
|
||||||
++ lib.lists.optional (machine.isRaspberryPi) nixos-hardware.nixosModules.raspberry-pi-4;
|
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
boot = lib.mkIf (machine.isHypervisor) {
|
boot = lib.mkIf (machine.isHypervisor) {
|
||||||
|
@ -54,32 +51,5 @@
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
microvm.vms =
|
|
||||||
let
|
|
||||||
vmsForHypervisor = lib.filterAttrs (n: v: v.isVirtual && v.hypervisorName == config.networking.hostName) machines;
|
|
||||||
in
|
|
||||||
builtins.mapAttrs
|
|
||||||
(name: vm:
|
|
||||||
{
|
|
||||||
# Do not restart virtual machines to apply configuration changes.
|
|
||||||
# While conceptually this seems useful, it could result in annoying situations.
|
|
||||||
# For example, changing the default VM configuration will restart ALL VMs simultaneously, causing a lot of stress on the servers.
|
|
||||||
# Downside of not restarting, is that we may need to do this manually now to apply changes.
|
|
||||||
restartIfChanged = false;
|
|
||||||
|
|
||||||
specialArgs = {
|
|
||||||
inherit agenix disko pkgs lib microvm dns nixpkgs-unstable kubenix;
|
|
||||||
machine = vm;
|
|
||||||
hypervisorConfig = config;
|
|
||||||
};
|
|
||||||
|
|
||||||
config.imports = [
|
|
||||||
./.
|
|
||||||
{ networking.hostName = name; }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
)
|
|
||||||
vmsForHypervisor;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
{ pkgs, lib, config, hypervisorConfig, ... }: {
|
|
||||||
imports = [ ./docker_swarm.nix ];
|
|
||||||
|
|
||||||
options.lab.vm = {
|
|
||||||
# TODO: make global.
|
|
||||||
baseMACAddress = lib.mkOption {
|
|
||||||
default = "BA:DB:EE:F0:00:00";
|
|
||||||
type = lib.types.str;
|
|
||||||
description = ''
|
|
||||||
Base MAC address for VMs in the DMZ.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
id = lib.mkOption {
|
|
||||||
type = lib.types.int;
|
|
||||||
description = ''
|
|
||||||
Unique identifier of this VM from wich the MAC address is derived.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
shares = lib.mkOption {
|
|
||||||
default = [ ];
|
|
||||||
description = ''
|
|
||||||
Directories mounted on the VM using VirtioFS.
|
|
||||||
'';
|
|
||||||
type = lib.types.listOf (lib.types.submodule ({ config, ... }: {
|
|
||||||
options = {
|
|
||||||
name = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = ''
|
|
||||||
The name of the directory share.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
mountPoint = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = ''
|
|
||||||
The mount point of the directory share inside the virtual machine.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
system.stateVersion = hypervisorConfig.system.stateVersion;
|
|
||||||
|
|
||||||
lab.vm.shares = [{
|
|
||||||
name = "host_keys";
|
|
||||||
mountPoint = "/etc/ssh/host_keys";
|
|
||||||
}];
|
|
||||||
|
|
||||||
services.openssh =
|
|
||||||
let
|
|
||||||
hostKeyPath = "/etc/ssh/host_keys/ssh_host_ed25519_key";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
hostKeys = [{
|
|
||||||
path = hostKeyPath;
|
|
||||||
type = "ed25519";
|
|
||||||
}];
|
|
||||||
|
|
||||||
extraConfig = ''
|
|
||||||
HostKey ${hostKeyPath}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
microvm = {
|
|
||||||
# TODO: make this dependent on the host CPU
|
|
||||||
vcpu = 4;
|
|
||||||
|
|
||||||
shares = [{
|
|
||||||
source = "/nix/store";
|
|
||||||
mountPoint = "/nix/.ro-store";
|
|
||||||
tag = "ro-store";
|
|
||||||
proto = "virtiofs";
|
|
||||||
}] ++ map
|
|
||||||
(share: {
|
|
||||||
source = "/var/lib/microvms/${config.networking.hostName}/shares/${share.name}";
|
|
||||||
mountPoint = share.mountPoint;
|
|
||||||
tag = share.name;
|
|
||||||
proto = "virtiofs";
|
|
||||||
})
|
|
||||||
config.lab.vm.shares;
|
|
||||||
|
|
||||||
interfaces = [{
|
|
||||||
type = "tap";
|
|
||||||
id = "vm-${config.networking.hostName}";
|
|
||||||
mac = pkgs.lib.net.mac.add config.lab.vm.id config.lab.vm.baseMACAddress;
|
|
||||||
}];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
{ pkgs, lib, config, machine, ... }:
|
|
||||||
let
|
|
||||||
cfg = config.lab.dockerSwarm;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
options.lab.dockerSwarm.enable = lib.mkOption {
|
|
||||||
default = false;
|
|
||||||
type = lib.types.bool;
|
|
||||||
description = ''
|
|
||||||
Whether to enable Docker Swarm on this host.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
|
||||||
lab.vm.shares = lib.mkIf machine.isVirtual [{
|
|
||||||
name = "docker";
|
|
||||||
mountPoint = "/var/lib/docker";
|
|
||||||
}];
|
|
||||||
|
|
||||||
networking = {
|
|
||||||
nftables.enable = lib.mkForce false;
|
|
||||||
firewall.enable = lib.mkForce false;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.docker = {
|
|
||||||
enable = true;
|
|
||||||
liveRestore = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.systemPackages = [
|
|
||||||
(pkgs.python311.withPackages (python-pkgs: with python-pkgs; [
|
|
||||||
docker
|
|
||||||
requests
|
|
||||||
jsondiff
|
|
||||||
pyyaml
|
|
||||||
]))
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
Loading…
Reference in a new issue