diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java index 337b5e3..1d05ec9 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java @@ -1,6 +1,6 @@ /* * VM-Operator - * Copyright (C) 2023 Michael N. Lipp + * Copyright (C) 2023,2025 Michael N. Lipp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -83,8 +83,18 @@ * [YamlConfigurationStore] *-right[hidden]- [Controller] * * [Manager] *-- [Controller] - * [Controller] *-- [VmWatcher] - * [Controller] *-- [Reconciler] + * Component VmMonitor as VmMonitor <> + * [Controller] *-- [VmMonitor] + * [VmMonitor] -right[hidden]- [PoolMonitor] + * Component PoolMonitor as PoolMonitor <> + * [Controller] *-- [PoolMonitor] + * Component PodMonitor as PodMonitor <> + * [Controller] *-- [PodMonitor] + * [PodMonitor] -up[hidden]- VmMonitor + * Component DisplaySecretMonitor as DisplaySecretMonitor <> + * [Controller] *-- [DisplaySecretMonitor] + * [DisplaySecretMonitor] -up[hidden]- VmMonitor + * [Controller] *-left- [Reconciler] * [Controller] -right[hidden]- [GuiHttpServer] * * [Manager] *-down- [GuiSocketServer:8080] diff --git a/org.jdrupes.vmoperator.manager/test-resources/basic-vm.yaml b/org.jdrupes.vmoperator.manager/test-resources/basic-vm.yaml index 54ea110..36054a2 100644 --- a/org.jdrupes.vmoperator.manager/test-resources/basic-vm.yaml +++ b/org.jdrupes.vmoperator.manager/test-resources/basic-vm.yaml @@ -1,8 +1,8 @@ apiVersion: "vmoperator.jdrupes.org/v1" kind: VirtualMachine metadata: - namespace: vmop-dev - name: unittest-vm + namespace: vmop-test + name: test-vm spec: image: repository: docker-registry.lan.mnl.de diff --git a/org.jdrupes.vmoperator.manager/test-resources/kustomization.yaml b/org.jdrupes.vmoperator.manager/test-resources/kustomization.yaml new file mode 100644 index 0000000..3a8451e --- /dev/null +++ b/org.jdrupes.vmoperator.manager/test-resources/kustomization.yaml @@ -0,0 +1,111 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../../deploy + +namespace: vmop-test + +patches: +- patch: |- + kind: PersistentVolumeClaim + apiVersion: v1 + metadata: + name: vmop-image-repository + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: local-path + +- patch: |- + kind: ConfigMap + apiVersion: v1 + metadata: + name: vm-operator + data: + # Keep in sync with config.yaml + config.yaml: | + "/Manager": + # clusterName: "test" + "/Controller": + "/Reconciler": + runnerData: + storageClassName: null + loadBalancerService: + labels: + label1: label1 + label2: toBeReplaced + annotations: + metallb.universe.tf/loadBalancerIPs: 192.168.168.1 + metallb.universe.tf/ip-allocated-from-pool: single-common + metallb.universe.tf/allow-shared-ip: single-common + "/GuiSocketServer": + port: 8888 + "/GuiHttpServer": + # This configures the GUI + "/ConsoleWeblet": + "/WebConsole": + "/LoginConlet": + users: + - name: admin + fullName: Administrator + password: "$2b$05$NiBd74ZGdplLC63ePZf1f.UtjMKkbQ23cQoO2OKOFalDBHWAOy21." + - name: test1 + fullName: Test Account + password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2" + - name: test2 + fullName: Test Account + password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2" + - name: test3 + fullName: Test Account + password: "$2b$05$hZaI/jToXf/d3BctZdT38Or7H7h6Pn2W3WiB49p5AyhDHFkkYCvo2" + "/RoleConfigurator": + rolesByUser: + # User admin has role admin + admin: + - admin + test1: + - user + test2: + - user + test3: + - user + # All users have role other + "*": + - other + replace: false + "/RoleConletFilter": + conletTypesByRole: + # Admins can use all conlets + admin: + - "*" + user: + - org.jdrupes.vmoperator.vmviewer.VmViewer + # Others cannot use any conlet (except login conlet to log out) + other: + - org.jgrapes.webconlet.locallogin.LoginConlet + "/ComponentCollector": + "/VmAccess": + displayResource: + preferredIpVersion: ipv4 + syncPreviewsFor: + - role: user +- target: + group: apps + version: v1 + kind: Deployment + name: vm-operator + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: docker-registry.lan.mnl.de/vmoperator/org.jdrupes.vmoperator.manager:test + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + - op: replace + path: /spec/replicas + value: 0 + \ No newline at end of file diff --git a/org.jdrupes.vmoperator.manager/test/org/jdrupes/vmoperator/manager/BasicTests.java b/org.jdrupes.vmoperator.manager/test/org/jdrupes/vmoperator/manager/BasicTests.java index 03db0d2..d600d3c 100644 --- a/org.jdrupes.vmoperator.manager/test/org/jdrupes/vmoperator/manager/BasicTests.java +++ b/org.jdrupes.vmoperator.manager/test/org/jdrupes/vmoperator/manager/BasicTests.java @@ -41,7 +41,7 @@ class BasicTests { private static APIResource vmsContext; private static K8sV1DeploymentStub mgrDeployment; private static K8sDynamicStub vmStub; - private static final String VM_NAME = "unittest-vm"; + private static final String VM_NAME = "test-vm"; private static final Object EXISTS = new Object(); @BeforeAll @@ -54,7 +54,7 @@ class BasicTests { // Update manager pod by scaling deployment mgrDeployment - = K8sV1DeploymentStub.get(client, "vmop-dev", "vm-operator"); + = K8sV1DeploymentStub.get(client, "vmop-test", "vm-operator"); mgrDeployment.scale(0); mgrDeployment.scale(1); waitForManager(); @@ -65,13 +65,13 @@ class BasicTests { vmsContext = apiRes.get(); // Cleanup existing VM - K8sDynamicStub.get(client, vmsContext, "vmop-dev", VM_NAME) + K8sDynamicStub.get(client, vmsContext, "vmop-test", VM_NAME) .delete(); ListOptions listOpts = new ListOptions(); listOpts.setLabelSelector("app.kubernetes.io/name=" + APP_NAME + "," + "app.kubernetes.io/instance=" + VM_NAME + "," + "app.kubernetes.io/component=" + DisplaySecret.NAME); - var secrets = K8sV1SecretStub.list(client, "vmop-dev", listOpts); + var secrets = K8sV1SecretStub.list(client, "vmop-test", listOpts); for (var secret : secrets) { secret.delete(); } @@ -103,7 +103,7 @@ class BasicTests { "app.kubernetes.io/managed-by=" + VM_OP_NAME + "," + "app.kubernetes.io/name=" + APP_NAME + "," + "app.kubernetes.io/instance=" + VM_NAME); - var knownPvcs = K8sV1PvcStub.list(client, "vmop-dev", listOpts); + var knownPvcs = K8sV1PvcStub.list(client, "vmop-test", listOpts); for (var pvc : knownPvcs) { pvc.delete(); } @@ -112,7 +112,7 @@ class BasicTests { @AfterAll static void tearDownAfterClass() throws Exception { // Cleanup - K8sDynamicStub.get(client, vmsContext, "vmop-dev", VM_NAME) + K8sDynamicStub.get(client, vmsContext, "vmop-test", VM_NAME) .delete(); deletePvcs(); @@ -124,7 +124,7 @@ class BasicTests { void testConfigMap() throws IOException, InterruptedException, ApiException { K8sV1ConfigMapStub stub - = K8sV1ConfigMapStub.get(client, "vmop-dev", VM_NAME); + = K8sV1ConfigMapStub.get(client, "vmop-test", VM_NAME); for (int i = 0; i < 10; i++) { if (stub.model().isPresent()) { break; @@ -134,7 +134,7 @@ class BasicTests { // Check config map var config = stub.model().get(); Map, Object> toCheck = Map.of( - List.of("namespace"), "vmop-dev", + List.of("namespace"), "vmop-test", List.of("name"), VM_NAME, List.of("labels", "app.kubernetes.io/name"), Constants.APP_NAME, List.of("labels", "app.kubernetes.io/instance"), VM_NAME, @@ -191,7 +191,7 @@ class BasicTests { + "app.kubernetes.io/component=" + DisplaySecret.NAME); Collection secrets = null; for (int i = 0; i < 10; i++) { - secrets = K8sV1SecretStub.list(client, "vmop-dev", listOpts); + secrets = K8sV1SecretStub.list(client, "vmop-test", listOpts); if (secrets.size() > 0) { break; } @@ -207,7 +207,7 @@ class BasicTests { @Test void testRunnerPvc() throws ApiException, InterruptedException { var stub - = K8sV1PvcStub.get(client, "vmop-dev", VM_NAME + "-runner-data"); + = K8sV1PvcStub.get(client, "vmop-test", VM_NAME + "-runner-data"); for (int i = 0; i < 10; i++) { if (stub.model().isPresent()) { break; @@ -227,7 +227,7 @@ class BasicTests { @Test void testSystemDiskPvc() throws ApiException, InterruptedException { var stub - = K8sV1PvcStub.get(client, "vmop-dev", VM_NAME + "-system-disk"); + = K8sV1PvcStub.get(client, "vmop-test", VM_NAME + "-system-disk"); for (int i = 0; i < 10; i++) { if (stub.model().isPresent()) { break; @@ -248,7 +248,7 @@ class BasicTests { @Test void testDisk1Pvc() throws ApiException, InterruptedException { var stub - = K8sV1PvcStub.get(client, "vmop-dev", VM_NAME + "-disk-1"); + = K8sV1PvcStub.get(client, "vmop-test", VM_NAME + "-disk-1"); for (int i = 0; i < 10; i++) { if (stub.model().isPresent()) { break; @@ -274,7 +274,7 @@ class BasicTests { new V1Patch("[{\"op\": \"replace\", \"path\": \"/spec/vm/state" + "\", \"value\": \"Running\"}]"), client.defaultPatchOptions()).isPresent()); - var stub = K8sV1PodStub.get(client, "vmop-dev", VM_NAME); + var stub = K8sV1PodStub.get(client, "vmop-test", VM_NAME); for (int i = 0; i < 20; i++) { if (stub.model().isPresent()) { break; @@ -303,7 +303,7 @@ class BasicTests { @Test public void testLoadBalancer() throws ApiException, InterruptedException { - var stub = K8sV1ServiceStub.get(client, "vmop-dev", VM_NAME); + var stub = K8sV1ServiceStub.get(client, "vmop-test", VM_NAME); for (int i = 0; i < 10; i++) { if (stub.model().isPresent()) { break; diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java index d8ac5d8..5c0da21 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java @@ -157,6 +157,15 @@ import org.jgrapes.util.events.WatchFile; * * success --> Running * + * state Running { + * state Booting + * state Booted + * + * [*] -right-> Booting + * Booting -down-> Booting: VserportChanged[guest agent connected]/fire GetOsinfo + * Booting --> Booted: Osinfo + * } + * * state Terminating { * state terminate <> * state qemuRunning <> diff --git a/webpages/VM-Operator-GUI-view.png b/webpages/VM-Operator-GUI-view.png index 0463cc5..dbda800 100644 Binary files a/webpages/VM-Operator-GUI-view.png and b/webpages/VM-Operator-GUI-view.png differ diff --git a/webpages/VmAccess-preview.png b/webpages/VmAccess-preview.png index d13387f..a97f7e1 100644 Binary files a/webpages/VmAccess-preview.png and b/webpages/VmAccess-preview.png differ diff --git a/webpages/admin-gui.md b/webpages/admin-gui.md index b968a74..325c227 100644 --- a/webpages/admin-gui.md +++ b/webpages/admin-gui.md @@ -12,10 +12,10 @@ layout: vm-operator An overview display shows the current CPU and RAM usage and a graph with recent changes. -![VM-Operator GUI](VM-Operator-GUI-preview.png) +![VM-Operator admin GUI preview](VM-Operator-GUI-preview.png) The detail display lists all VMs. From here you can start and stop the VMs and adjust the CPU and RAM usages (modifies the definition in kubernetes). -![VM-Operator GUI](VM-Operator-GUI-view.png) +![VM-Operator admin GUI view](VM-Operator-GUI-view.png) diff --git a/webpages/auto-login.md b/webpages/auto-login.md index 59856b2..66f0edf 100644 --- a/webpages/auto-login.md +++ b/webpages/auto-login.md @@ -9,7 +9,7 @@ layout: vm-operator When users log into the web GUI, they have already authenticated with the VM-Operator. In some environments, requiring an additional login on the -guest OS can be cumbersome. To enhance the user experience, the VM-Operator +guest OS can be annoying. To enhance the user experience, the VM-Operator supports automatic login on the guest operating system, thus eliminating the need for multiple logins. However, this feature requires specific support from the guest OS. @@ -18,9 +18,9 @@ support from the guest OS. Automatic login requires an agent running inside the guest OS. Similar to QEMU's standard guest agent, the VM-Operator agent communicates with -the host via a tty device (`/dev/virtio-ports/org.jdrupes.vmop_agent.0`). On -modern Linux systems, `udev` can detect this device and trigger the start -of an associated systemd service. +the host via a tty device (provided in the guest as +`/dev/virtio-ports/org.jdrupes.vmop_agent.0`). On modern Linux systems, `udev` can +detect this device and trigger the start of an associated systemd service. Sample configuration files for a VM-Operator agent are available [here](https://github.com/mnlipp/VM-Operator/tree/main/dev-example/vmop-agent). diff --git a/webpages/index-pic.svg b/webpages/index-pic.svg index e912900..d6b0ef9 100644 --- a/webpages/index-pic.svg +++ b/webpages/index-pic.svg @@ -8,7 +8,7 @@ version="1.1" id="svg1" xml:space="preserve" - inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)" + inkscape:version="1.4 (e7c3feb100, 2024-10-09)" sodipodi:docname="index-pic.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" @@ -24,13 +24,13 @@ inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="mm" - inkscape:zoom="0.67704826" - inkscape:cx="757.70079" - inkscape:cy="438.66888" + inkscape:zoom="0.95749083" + inkscape:cx="689.30164" + inkscape:cy="285.64242" inkscape:window-width="1920" - inkscape:window-height="1011" + inkscape:window-height="1008" inkscape:window-x="0" - inkscape:window-y="32" + inkscape:window-y="35" inkscape:window-maximized="1" inkscape:current-layer="g1"> + xlink:href="data:image/png;base64,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 SUVORK5CYII= " + id="image1-35" + x="395.12466" + y="387.88336" /> diff --git a/webpages/index.md b/webpages/index.md index 34a3c99..6dc3c10 100644 --- a/webpages/index.md +++ b/webpages/index.md @@ -9,7 +9,7 @@ layout: vm-operator # Welcome to VM-Operator -![Overview picture](index-pic.svg) +![VM-Operator summary picture](index-pic.svg) This project provides an easy to use and flexible solution for running QEMU/KVM based virtual machines (VMs) in Kubernetes pods. diff --git a/webpages/pools.md b/webpages/pools.md index 41e26ef..c84264f 100644 --- a/webpages/pools.md +++ b/webpages/pools.md @@ -46,16 +46,20 @@ spec: The `retention` specifies how long the assignment of a VM from the pool to a user remains valid after the user closes the console. This ensures that a user can resume work within this timeframe without the risk of another -user taking over the VM. The time is specified as +user taking over the VM. The time is specified as an [ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). +Specifying an ISO 8601 time is also supported, but if you consider +using an absolute time, check again whether a dedicated VM for the user +isn't the more appropriate choice. -Setting `loginOnAssignment` to `true` triggers automatic login of the -user (as described in [section auto login](auto-login.html)) when -the VM is assigned. The `permissions` property specifies the actions -that users or roles can perform on assigned VMs. +Setting `loginOnAssignment` to `true` (defaults to `false`) triggers automatic +login of the user (as described in [section auto login](auto-login.html)) +when the VM is assigned. The `permissions` property specifies the actions +that users or roles can perform on assigned VMs. The `may` property defaults +to `[accessConsole]` if not specified. VMs become members of one (or more) pools by adding the pool name to -the `spec.pools` array, as shown below: +the `spec.pools` array in the VM definition, as shown below: ```yaml apiVersion: "vmoperator.jdrupes.org/v1"