Merge branch 'main' into testing

This commit is contained in:
Michael Lipp 2025-03-06 15:07:12 +01:00
commit 5d3b19e827
20 changed files with 1209 additions and 131 deletions

View file

@ -1,4 +1,4 @@
# See https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml # See [rules](https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml)
# Default state for all rules # Default state for all rules
default: true default: true
@ -27,12 +27,4 @@ MD036: false
# MD043/required-headings : Required heading structure : # MD043/required-headings : Required heading structure :
# https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md043.md # https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md043.md
MD043: MD043: false
# List of headings
headings: [
"# Head",
"## Item",
"### Detail"
]
# Match case of headings
match_case: false

View file

@ -41,7 +41,7 @@ for number in $(seq 1 $count); do
if [ -z "$prefix" ]; then if [ -z "$prefix" ]; then
prefix=$(basename $template .tpl.yaml) prefix=$(basename $template .tpl.yaml)
fi fi
name="$prefix$number" name="$prefix$(printf %03d $number)"
index=$(($number - 1)) index=$(($number - 1))
esh -o $destination/$name.yaml $template number=$number index=$index esh -o $destination/$name.yaml $template number=$number index=$index
done done

View file

@ -62,7 +62,7 @@ public class K8sDynamicModelsBase<T extends K8sDynamicModel>
} catch (InstantiationException | IllegalAccessException } catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException exc) { | NoSuchMethodException | SecurityException exc) {
throw new IllegalArgumentException(exc); throw new IllegalArgumentException(exc); // NOPMD
} }
} }
} }

View file

@ -192,7 +192,7 @@ import org.jgrapes.util.events.WatchFile;
*/ */
@SuppressWarnings({ "PMD.ExcessiveImports", "PMD.AvoidPrintStackTrace", @SuppressWarnings({ "PMD.ExcessiveImports", "PMD.AvoidPrintStackTrace",
"PMD.DataflowAnomalyAnalysis", "PMD.TooManyMethods", "PMD.DataflowAnomalyAnalysis", "PMD.TooManyMethods",
"PMD.CouplingBetweenObjects" }) "PMD.CouplingBetweenObjects", "PMD.TooManyFields" })
public class Runner extends Component { public class Runner extends Component {
private static final String QEMU = "qemu"; private static final String QEMU = "qemu";

View file

@ -18,6 +18,7 @@
package org.jdrupes.vmoperator.util; package org.jdrupes.vmoperator.util;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
@ -157,11 +158,12 @@ public final class DataPath {
return (T) copy; return (T) copy;
} }
if (object.getClass().isArray()) { if (object.getClass().isArray()) {
var copy = new ArrayList<>(); var copy = Array.newInstance(object.getClass().getComponentType(),
for (var item : (Object[]) object) { Array.getLength(object));
copy.add(deepCopy(item)); for (int i = 0; i < Array.getLength(object); i++) {
Array.set(copy, i, deepCopy(Array.get(object, i)));
} }
return (T) copy.toArray(); return (T) copy;
} }
if (object instanceof Cloneable) { if (object instanceof Cloneable) {
try { try {

View file

@ -0,0 +1,17 @@
package org.jdrupes.vmoperator.util;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class DataPathTests {
@Test
void testArray() {
int[] orig
= { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3) };
var copy = DataPath.deepCopy(orig);
for (int i = 0; i < orig.length; i++) {
assertEquals(orig[i], copy[i]);
}
}
}

View file

@ -7,4 +7,4 @@ The VM-operator enables you to easily run Qemu based VMs as pods
in Kubernetes. It is built on the in Kubernetes. It is built on the
[JGrapes](https://mnlipp.github.io/jgrapes/) event driven framework. [JGrapes](https://mnlipp.github.io/jgrapes/) event driven framework.
See the project's [home page](https://jdrupes.org/vm-operator/) for details. See the project's [home page](https://vm-operator.jdrupes.org/) for details.

1003
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@
"documentation": "^14.0.1", "documentation": "^14.0.1",
"install": "^0.13.0", "install": "^0.13.0",
"jsdoc": "^4.0.2", "jsdoc": "^4.0.2",
"markdownlint": "^0.37.4",
"node-sass": "^9.0.0", "node-sass": "^9.0.0",
"npm": "^8.11.0", "npm": "^8.11.0",
"rollup": "^4.1.5", "rollup": "^4.1.5",
@ -34,5 +35,8 @@
}, },
"eslintIgnore": [ "eslintIgnore": [
"node_modules/**" "node_modules/**"
] ],
"dependencies": {
"markdownlint-cli": "^0.44.0"
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View file

@ -68,6 +68,11 @@
<li><p class="part-entry"><a href="admin-gui.html">For Admins</a></p></li> <li><p class="part-entry"><a href="admin-gui.html">For Admins</a></p></li>
<li><p class="part-entry"><a href="user-gui.html">For Users</a></p></li> <li><p class="part-entry"><a href="user-gui.html">For Users</a></p></li>
</ul> </ul>
<p class="part-list-title">Advanced</p>
<ul style="margin-bottom: 0;" class="no-bullets">
<li><p class="part-entry"><a href="auto-login.html">Auto Login</a></p></li>
<li><p class="part-entry"><a href="pools.html">Pools</a></p></li>
</ul>
<p class="part-list-title"><a href="upgrading.html">Upgrading</a></p> <p class="part-list-title"><a href="upgrading.html">Upgrading</a></p>
<p class="part-list-title"><a href="https://vm-operator.jdrupes.org/javadoc/index.html">Javadoc</a></p> <p class="part-list-title"><a href="https://vm-operator.jdrupes.org/javadoc/index.html">Javadoc</a></p>

View file

@ -19,4 +19,3 @@ the VMs and adjust the CPU and RAM usages (modifies the definition
in kubernetes). in kubernetes).
![VM-Operator GUI](VM-Operator-GUI-view.png) ![VM-Operator GUI](VM-Operator-GUI-view.png)

87
webpages/auto-login.md Normal file
View file

@ -0,0 +1,87 @@
---
title: "VM-Operator: Auto login — Login users automatically on the guest"
layout: vm-operator
---
# Auto Login
*Since 4.0.0*
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
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.
## Prepare the VM
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.
Sample configuration files for a VM-Operator agent are available
[here](https://github.com/mnlipp/VM-Operator/tree/main/dev-example/vmop-agent).
Copy
* `99-vmop-agent.rules``/usr/local/lib/udev/rules.d/99-vmop-agent.rules`,
* `vmop-agent``/usr/local/libexec/vmop-agent` and
* `vmop-agent.service``/usr/local/lib/systemd/system/vmop-agent.service`.
Some of these target directories may not exist by default and must be
created manually. If your system uses SELinux, run `restorecon` to apply
the correct security contexts.
Enable the agent:
```console
# systemctl daemon-reload
# systemctl enable vmop-agent
# udevadm control --reload-rules
# udevadm trigger
```
## The VM operator agent
Communication with the VM-Operator agent follows the pattern established by
protocols such as SMTP and FTP. The agent must handle the commands
"`login <username>`" and "`logout`" on its input. In response to
these commands, the agent sends back lines that start with a three
digit number. The first digit determines the type of message: "1" for
informational, "2" for success and "4" or "5" for errors. The second
digit provides information about the category that a response relates
to. The third digit is specific to the command.
While this describes the general pattern, the [runner](runner.html)
only evaluates the following codes:
| Code | Meaning |
| ---- | ------- |
| 220 | Sent by the agent on startup |
| 201 | Login command executed successfully |
| 202 | Logout command executed successfully |
The provided sample script is written for the gnome desktop environment.
It assumes that GDM is running as a service by default. When the agent
receives a login command, it stops GDM and starts a gnome-session for
the specified user. Upon receiving the logout command, it terminates
the session and starts GDM again.
No attempt has been made to make the script configurable. There are too
many possible options. The script should therefore be considered as a
starting point that you may need to adapt to your specific needs.
In addition to starting the desktop for the logged in user, the sample
script automatically creates user accounts if they do not already exist.
The idea behind this behavior is further explained in the
[section about pools](pools.html#vm-pools).
## Enable auto login for a VM
To enable auto login for a VM, specify the user to be logged in in the VM's
definition with "`spec.vm.display.loggedInUser: user-name`". If everything has been
set up correctly, you should be able to open the console and observe the
transition from GDM's login screen to the user's desktop when updating the
VM's spec.

View file

@ -27,6 +27,7 @@ If you just want to try out things, you can skip the remainder of this
page and proceed to "[the manager](manager.html)". page and proceed to "[the manager](manager.html)".
## Motivation ## Motivation
The project was triggered by a remark in the discussion about RedHat The project was triggered by a remark in the discussion about RedHat
[dropping SPICE support](https://bugzilla.redhat.com/show_bug.cgi?id=2030592) [dropping SPICE support](https://bugzilla.redhat.com/show_bug.cgi?id=2030592)
from the RHEL packages. Which means that you have to run Qemu in a from the RHEL packages. Which means that you have to run Qemu in a
@ -55,9 +56,11 @@ close to simply deploying the pod (you get the restart and some PVC
management "for free"). management "for free").
A second look, however, reveals that Kubernetes has more to offer. A second look, however, reveals that Kubernetes has more to offer.
* It has a well defined API for managing resources. * It has a well defined API for managing resources.
* It provides access to different kinds of managed storage for the VMs. * It provides access to different kinds of managed storage for the VMs.
* Its managing features *are* useful for running the component that * Its managing features *are* useful for running the component that
manages the pods with the VMs. manages the pods with the VMs.
And if you use Kubernetes anyway, well then the VMs within Kubernetes And if you use Kubernetes anyway, well then the VMs within Kubernetes

View file

@ -40,7 +40,8 @@ default files for creating these resources using the default namespace
can be found in the can be found in the
[deploy](https://github.com/mnlipp/VM-Operator/tree/main/deploy) [deploy](https://github.com/mnlipp/VM-Operator/tree/main/deploy)
directory. I recommend to use directory. I recommend to use
[kustomize](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/) to create your own configuration. [kustomize](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/)
to create your own configuration.
## Initial Configuration ## Initial Configuration

View file

@ -7,67 +7,105 @@ layout: vm-operator
*Since 4.0.0* *Since 4.0.0*
## Prepare the VM Not all VMs are defined as replacements for carefully maintained
individual PCs. In many workplaces, a standardardized VM configuration
can be used where all user-specific data is stored in each user's home
directory. By using a shared file system for home directories, users
can login on any VM and find themselves in their personal
environment.
If only a subset of users require access simultaneously, this makes it
possible to define a pool of standardardized VMs and dynamically assign
them to users as needed, eliminating the need to define a dedicated VM
for each user.
## Pool definitions
The VM-operator supports this use case with a CRD for pools.
```yaml
apiVersion: "vmoperator.jdrupes.org/v1"
kind: VmPool
metadata:
namespace: vmop-dev
name: test-vms
spec:
retention: "PT4h"
loginOnAssignment: true
permissions:
- user: admin
may:
- accessConsole
- start
- role: user
may:
- accessConsole
- start
```
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
[ISO 8601 duration](https://en.wikipedia.org/wiki/ISO_8601#Durations).
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.
VMs become members of one (or more) pools by adding the pool name to
the `spec.pools` array, as shown below:
```yaml
apiVersion: "vmoperator.jdrupes.org/v1"
kind: VirtualMachine
spec:
pools:
- test-vms
```
## Accessing a VM from the pool
Users can access a VM from a pool using the widget described in
[user view](user-gui.html). The widget must be configured to
provide access to a pool instead of to a specific VM.
![VM Access configuration](ConfigAccess-preview.png){: width="500"}
Assignment happens when the "Start" icon is clicked. If the assigned VM
is not already running, it will be started automatically. The assigned
VM's name apears in the widget above the action icons.
![VM Access via pool](PoolAccess-preview.png)
Apart from showing the assigned VM, the widget behaves in the same way
as when configured for accessing a specific VM.
## Guest OS Requirements
To ensure proper functionality when using VM pools, certain requirements
must be met on the guest OS.
### Shared file system ### Shared file system
Mount a shared file system as home file system on all VMs in the pool. All VMs in the pool must mount a shared file system as the home directory.
If you want to use the sample script for logging in a user, the filesystem When using the
must support POSIX file access control lists (ACLs). [sample agent](https://github.com/mnlipp/VM-Operator/tree/main/dev-example/vmop-agent),
the file system must support POSIX file access control lists (ACLs).
### Restrict access ### User management
The VMs should only be accessible via a desktop started by the VM-Operator. All VMs in the pool must map a given user name to the same user
id. This is typically accomplished by using a central user management,
such as LDAP. The drawback of such a solution is that it is rather
complicated to configure.
* Disable the display manager. As an alternative, the sample auto login agent provides a very simple
approach that uses the shared home directory for managing the user ids.
Simplified, the script searches for a home directory with the given user
name and derives the user id from it. It then checks if the user id is
known by the guest operating system. If not, the user is added.
```console Details can be found in the comments of the sample script.
# systemctl disable gdm
# systemctl stop gdm
```
* Disable `getty` on tty1.
```console
# systemctl mask getty@tty1
# systemctl stop getty@tty1
```
You can, of course, disable `getty` completely. If you do this, make sure
that you can still access your master VM through `ssh`, else you have
locked yourself out.
Strictly speaking, it is not necessary to disable these services, because
the sample script includes a `Conflicts=` directive in the systemd service
that starts the desktop for the user. However, this is mainly intended for
development purposes and not for production.
The following should actually be configured for any VM.
* Prevent suspend/hibernate, because it will lock the VM.
```console
# systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
```
### Install the VM-Operator agent
The VM-Operator agent runs as a systemd service. Sample configuration
files can be found
[here](https://github.com/mnlipp/VM-Operator/tree/main/dev-example/vmop-agent).
Copy
* `99-vmop-agent.rules` to `/usr/local/lib/udev/rules.d/99-vmop-agent.rules`,
* `vmop-agent` to `/usr/local/libexec/vmop-agent` and
* `vmop-agent.service` to `/usr/local/lib/systemd/system/vmop-agent.service`.
Note that some of the target directories do not exist by default and have to
be created first. Don't forget to run `restorecon` on systems with SELinux.
Enable everything:
```console
# udevadm control --reload-rules
# systemctl enable vmop-agent
# udevadm trigger
```

View file

@ -44,9 +44,9 @@ A sample configuration file with annotated options can be found
As the runner implementation uses the As the runner implementation uses the
[JGrapes](https://jgrapes.org/) framework, the file [JGrapes](https://jgrapes.org/) framework, the file
follows the framework's follows the framework's
[conventions](https://jgrapes.org/latest-release/javadoc/org/jgrapes/util/YamlConfigurationStore.html). The top level "`/Runner`" selects [conventions](https://jgrapes.org/latest-release/javadoc/org/jgrapes/util/YamlConfigurationStore.html).
the component to be configured. Nested within is the information The top level "`/Runner`" selects the component to be configured. Nested
to be applied to the component. within is the information to be applied to the component.
The main entries in the configuration file are the "template" and The main entries in the configuration file are the "template" and
the "vm" information. The runner processes the the "vm" information. The runner processes the
@ -58,8 +58,8 @@ defines a particular VM type, i.e. it contains the "nasty details"
that do not need to be modified for some given set of VM instances. that do not need to be modified for some given set of VM instances.
The templates provided with the runner can be found The templates provided with the runner can be found
[here](https://github.com/mnlipp/VM-Operator/tree/main/org.jdrupes.vmoperator.runner.qemu/templates). When details [here](https://github.com/mnlipp/VM-Operator/tree/main/org.jdrupes.vmoperator.runner.qemu/templates).
of the VM configuration need modification, a new VM type When details of the VM configuration need modification, a new VM type
(i.e. a new template) has to be defined. Authoring a new (i.e. a new template) has to be defined. Authoring a new
template requires some knowledge about the template requires some knowledge about the
[qemu invocation](https://www.qemu.org/docs/master/system/invocation.html). [qemu invocation](https://www.qemu.org/docs/master/system/invocation.html).
@ -92,7 +92,8 @@ which may be used in a stand-alone development configuration.
The runner supports adaption to changes of the RAM size (using the The runner supports adaption to changes of the RAM size (using the
balloon device) and to changes of the number of CPUs. Note that balloon device) and to changes of the number of CPUs. Note that
in order to get new CPUs online on Linux guests, you need a in order to get new CPUs online on Linux guests, you need a
[udev rule](https://docs.kernel.org/core-api/cpu_hotplug.html#user-space-notification) which is not installed by default[^simplest]. [udev rule](https://docs.kernel.org/core-api/cpu_hotplug.html#user-space-notification)
which is not installed by default[^simplest].
The runner also changes the images loaded in CDROM drives. If the The runner also changes the images loaded in CDROM drives. If the
drive is locked, i.e. if it doesn't respond to the "open tray" command drive is locked, i.e. if it doesn't respond to the "open tray" command
@ -101,7 +102,8 @@ the change will be suspended until the VM opens the tray.
Finally, `powerdownTimeout` can be changed while the qemu process runs. Finally, `powerdownTimeout` can be changed while the qemu process runs.
[^simplest]: The simplest form of the rule is probably: [^simplest]: The simplest form of the rule is probably:
```
```txt
ACTION=="add", SUBSYSTEM=="cpu", ATTR{online}="1" ACTION=="add", SUBSYSTEM=="cpu", ATTR{online}="1"
``` ```

View file

@ -10,7 +10,7 @@ layout: vm-operator
## To version 4.0.0 ## To version 4.0.0
* The VmViewer conlet has been renamed to VmAccess. This affects the * The VmViewer conlet has been renamed to VmAccess. This affects the
[configuration](https://jdrupes.org/vm-operator/user-gui.html). [configuration](https://vm-operator.jdrupes.org/user-gui.html).
Configuration information using the old path Configuration information using the old path
`/Manager/GuiHttpServer/ConsoleWeblet/WebConsole/ComponentCollector/VmViewer` `/Manager/GuiHttpServer/ConsoleWeblet/WebConsole/ComponentCollector/VmViewer`
is still accepted for backward compatibility until the next major version, is still accepted for backward compatibility until the next major version,

View file

@ -15,7 +15,7 @@ The idea of the user view is to provide an intuitive widget that
allows the users to access their own VMs and to optionally start allows the users to access their own VMs and to optionally start
and stop them. and stop them.
![VM-Viewer](VmAccess-preview.png) ![VM Access](VmAccess-preview.png)
The configuration options resulting from this seemingly simple The configuration options resulting from this seemingly simple
requirement are unexpectedly complex. requirement are unexpectedly complex.
@ -77,7 +77,8 @@ objects that either specify a role or a user.
## Console access ## Console access
Access to the VM's console is implemented by generating a Access to the VM's console is implemented by generating a
[connection file](https://manpages.debian.org/testing/virt-viewer/remote-viewer.1.en.html#CONNECTION_FILE) for virt-viewer when the user clicks on [connection file](https://manpages.debian.org/testing/virt-viewer/remote-viewer.1.en.html#CONNECTION_FILE)
for virt-viewer when the user clicks on
the console icon. If automatic open is enabled for this kind of the console icon. If automatic open is enabled for this kind of
files in the browser, the console opens without further user action. files in the browser, the console opens without further user action.