Merge branch 'main' into testing

This commit is contained in:
Michael Lipp 2024-11-24 16:59:10 +01:00
commit 4c600e7118
84 changed files with 859 additions and 139 deletions

View file

@ -0,0 +1,59 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: vmpools.vmoperator.jdrupes.org
spec:
group: vmoperator.jdrupes.org
# list of versions supported by this CustomResourceDefinition
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
permissions:
type: array
description: >-
Defines permissions for accessing and manipulating the Pool.
items:
type: object
description: >-
Permissions can be granted to a user or to a role.
oneOf:
- required:
- user
- required:
- role
properties:
user:
type: string
role:
type: string
may:
type: array
items:
type: string
enum:
- start
- stop
- reset
- accessConsole
- "*"
default: []
required:
- permissions
# either Namespaced or Cluster
scope: Namespaced
names:
# plural name to be used in the URL: /apis/<group>/<version>/<plural>
plural: vmpools
# singular name to be used as an alias on the CLI and for display
singular: vmpool
# kind is normally the CamelCased singular type. Your resource manifests use this.
kind: VmPool
listKind: VmPoolList

View file

@ -1019,6 +1019,13 @@ spec:
- accessConsole
- "*"
default: []
pools:
type: array
description: >-
List of pools to which this VM belongs.
items:
type: string
default: []
loggingProperties:
type: string
description: >-

View file

@ -1 +1,3 @@
/test-vm-ci.yaml
/kubeconfig.yaml
/crds/

View file

@ -7,8 +7,8 @@
"/Controller":
namespace: vmop-dev
"/Reconciler":
runnerData:
storageClassName: null
runnerDataPvc:
storageClassName: rook-cephfs
loadBalancerService:
labels:
label1: label1
@ -65,7 +65,7 @@
other:
- org.jgrapes.webconlet.oidclogin.LoginConlet
"/ComponentCollector":
"/VmViewer":
"/VmAccess":
displayResource:
preferredIpVersion: ipv4
syncPreviewsFor:

47
dev-example/gen-pool-vm-crds.sh Executable file
View file

@ -0,0 +1,47 @@
#!/bin/bash
function usage() {
cat >&2 <<EOF
Usage: $0 [OPTION]... [TEMPLATE]
Generate VM CRDs using TEMPLATE.
-c, --count Count of VMs to generate
-d, --destination DIR Generate into given directory (default: ".")
-h, --help Print this help
-p, --prefix PREFIX Prefix for generated file (default: basename of template)
EOF
exit 1
}
count=0
destination=.
template=""
prefix=""
while [ "$#" -gt 0 ]; do
case "$1" in
-c|--count) shift; count=$1;;
-d|--destination) shift; destination="$1";;
-h|--help) shift; usage;;
-p|--prefix) shift; prefix="$1";;
-*) echo >&2 "Unknown option: $1"; exit 1;;
*) template="$1";;
esac
shift
done
if [ -z "$template" ]; then
usage
fi
if [ "$count" = "0" ]; then
exit 0
fi
for number in $(seq 1 $count); do
if [ -z "$prefix" ]; then
prefix=$(basename $template .tpl.yaml)
fi
name="$prefix$number"
index=$(($number - 1))
esh -o $destination/$name.yaml $template number=$number index=$index
done

View file

@ -79,7 +79,7 @@ patches:
other:
- org.jgrapes.webconlet.locallogin.LoginConlet
"/ComponentCollector":
"/VmViewer":
"/VmAccess":
displayResource:
preferredIpVersion: ipv4
syncPreviewsFor:

View file

@ -0,0 +1,10 @@
apiVersion: "vmoperator.jdrupes.org/v1"
kind: VmPool
metadata:
namespace: vmop-dev
name: test-vms
spec:
permissions:
- user: admin
may:
- accessConsole

View file

@ -0,0 +1,10 @@
---
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
namespace: vmop-dev
name: test-vm-system-disk-snapshot
spec:
volumeSnapshotClassName: csi-rbdplugin-snapclass
source:
persistentVolumeClaimName: test-vm-system-disk

View file

@ -0,0 +1,70 @@
apiVersion: "vmoperator.jdrupes.org/v1"
kind: VirtualMachine
metadata:
namespace: vmop-dev
name: test-vm<%= ${number} %>
annotations:
argocd.argoproj.io/sync-wave: "20"
spec:
image:
# repository: docker-registry.lan.mnl.de
# path: vmoperator/org.jdrupes.vmoperator.runner.qemu-arch
# pullPolicy: Always
# repository: ghcr.io
# path: mnlipp/org.jdrupes.vmoperator.runner.qemu-alpine
# version: "3.0.0"
source: registry.mnl.de/org/jdrupes/vm-operator/org.jdrupes.vmoperator.runner.qemu-arch:testing
pullPolicy: Always
permissions:
- role: admin
may:
- "*"
- user: test
may:
- accessConsole
guestShutdownStops: true
cloudInit:
metaData: {}
pools:
- test-vms
vm:
# state: Running
bootMenu: true
maximumCpus: 4
currentCpus: 2
maximumRam: 4Gi
currentRam: 3Gi
networks:
# No bridge on TC1
# - tap: {}
- user: {}
disks:
- volumeClaimTemplate:
metadata:
name: system
spec:
storageClassName: ceph-rbd3slow
dataSource:
name: test-vm-system-disk-snapshot
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 40Gi
- cdrom:
image: ""
# image: https://download.fedoraproject.org/pub/fedora/linux/releases/38/Workstation/x86_64/iso/Fedora-Workstation-Live-x86_64-38-1.6.iso
display:
spice:
port: <%= $((5910 + number)) %>

View file

@ -38,4 +38,7 @@ public class Constants {
/** The Constant VM_OP_KIND_VM. */
public static final String VM_OP_KIND_VM = "VirtualMachine";
/** The Constant VM_OP_KIND_VM_POOL. */
public static final String VM_OP_KIND_VM_POOL = "VmPool";
}

View file

@ -0,0 +1,186 @@
/*
* VM-Operator
* Copyright (C) 2024 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
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.common;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.jdrupes.vmoperator.util.DataPath;
/**
* Represents a VM pool.
*/
@SuppressWarnings({ "PMD.DataClass" })
public class VmPool {
private String name;
private List<Grant> permissions = Collections.emptyList();
private final Set<String> vms
= Collections.synchronizedSet(new HashSet<>());
/**
* Returns the name.
*
* @return the name
*/
public String name() {
return name;
}
/**
* Sets the name.
*
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the permissions
*/
public List<Grant> permissions() {
return permissions;
}
/**
* Sets the permissions.
*
* @param permissions the permissions to set
*/
public void setPermissions(List<Grant> permissions) {
this.permissions = permissions;
}
/**
* Returns the VM names.
*
* @return the vms
*/
public Set<String> vms() {
return vms;
}
@Override
@SuppressWarnings("PMD.AvoidLiteralsInIfCondition")
public String toString() {
StringBuilder builder = new StringBuilder(50);
builder.append("VmPool [name=").append(name).append(", permissions=")
.append(permissions).append(", vms=");
if (vms.size() <= 3) {
builder.append(vms);
} else {
builder.append('[');
vms.stream().limit(3).map(s -> s + ",").forEach(builder::append);
builder.append("...]");
}
builder.append(']');
return builder.toString();
}
/**
* Collect all permissions for the given user with the given roles.
*
* @param user the user
* @param roles the roles
* @return the sets the
*/
public Set<Permission> permissionsFor(String user,
Collection<String> roles) {
return permissions.stream()
.filter(g -> DataPath.get(g, "user").map(u -> u.equals(user))
.orElse(false)
|| DataPath.get(g, "role").map(roles::contains).orElse(false))
.map(g -> DataPath.<Set<Permission>> get(g, "may")
.orElse(Collections.emptySet()).stream())
.flatMap(Function.identity()).collect(Collectors.toSet());
}
/**
* A permission grant to a user or role.
*
* @param user the user
* @param role the role
* @param may the may
*/
public record Grant(String user, String role, Set<Permission> may) {
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (user != null) {
builder.append("User ").append(user);
} else {
builder.append("Role ").append(role);
}
builder.append(" may=").append(may).append(']');
return builder.toString();
}
}
/**
* Permissions for accessing and manipulating the pool.
*/
public enum Permission {
START("start"), STOP("stop"), RESET("reset"),
ACCESS_CONSOLE("accessConsole");
@SuppressWarnings("PMD.UseConcurrentHashMap")
private static Map<String, Permission> reprs = new HashMap<>();
static {
for (var value : EnumSet.allOf(Permission.class)) {
reprs.put(value.repr, value);
}
}
private final String repr;
Permission(String repr) {
this.repr = repr;
}
/**
* Create permission from representation in CRD.
*
* @param value the value
* @return the permission
*/
@SuppressWarnings("PMD.AvoidLiteralsInIfCondition")
public static Set<Permission> parse(String value) {
if ("*".equals(value)) {
return EnumSet.allOf(Permission.class);
}
return Set.of(reprs.get(value));
}
@Override
public String toString() {
return repr;
}
}
}

View file

@ -0,0 +1,88 @@
/*
* VM-Operator
* Copyright (C) 2023 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
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.manager.events;
import org.jdrupes.vmoperator.common.VmPool;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Components;
import org.jgrapes.core.Event;
/**
* Indicates a change in a pool configuration.
*/
@SuppressWarnings("PMD.DataClass")
public class VmPoolChanged extends Event<Void> {
private final VmPool vmPool;
private final boolean deleted;
/**
* Instantiates a new VM changed event.
*
* @param pool the pool
* @param deleted true, if the pool was deleted
*/
public VmPoolChanged(VmPool pool, boolean deleted) {
vmPool = pool;
this.deleted = deleted;
}
/**
* Instantiates a new VM changed event for an existing pool.
*
* @param pool the pool
*/
public VmPoolChanged(VmPool pool) {
this(pool, false);
}
/**
* Returns the VM pool.
*
* @return the vm pool
*/
public VmPool vmPool() {
return vmPool;
}
/**
* Pool has been deleted.
*
* @return true, if successful
*/
public boolean deleted() {
return deleted;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(30);
builder.append(Components.objectName(this))
.append(" [");
if (deleted) {
builder.append("Deleted: ");
}
builder.append(vmPool);
if (channels() != null) {
builder.append(", channels=").append(Channel.toString(channels()));
}
builder.append(']');
return builder.toString();
}
}

View file

@ -31,8 +31,8 @@ dependencies {
runtimeOnly 'org.slf4j:slf4j-jdk14:[2.0.7,3)'
runtimeOnly 'org.apache.logging.log4j:log4j-to-jul:2.20.0'
runtimeOnly project(':org.jdrupes.vmoperator.vmconlet')
runtimeOnly project(':org.jdrupes.vmoperator.vmviewer')
runtimeOnly project(':org.jdrupes.vmoperator.vmmgmt')
runtimeOnly project(':org.jdrupes.vmoperator.vmaccess')
}
application {

View file

@ -246,7 +246,9 @@ public abstract class AbstractMonitor<O extends KubernetesObject,
}
/**
* Handle an observed change.
* Handle an observed change. The method is invoked by the observer
* thread(s). It is the responsibility of the implementing class to
* fire derived events on the appropriate event pipeline.
*
* @param client the client
* @param change the change

View file

@ -106,6 +106,7 @@ public class Controller extends Component {
// to access the VM's console. Might change in the future.
// attach(new ServiceMonitor(channel()).channelManager(chanMgr));
attach(new Reconciler(channel()));
attach(new PoolManager(channel()));
}
/**

View file

@ -0,0 +1,209 @@
/*
* VM-Operator
* Copyright (C) 2023,2024 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
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.manager;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.models.V1ObjectMeta;
import io.kubernetes.client.util.Watch;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import static org.jdrupes.vmoperator.common.Constants.VM_OP_GROUP;
import org.jdrupes.vmoperator.common.K8s;
import org.jdrupes.vmoperator.common.K8sClient;
import org.jdrupes.vmoperator.common.K8sDynamicModel;
import org.jdrupes.vmoperator.common.K8sDynamicModels;
import org.jdrupes.vmoperator.common.K8sDynamicStub;
import org.jdrupes.vmoperator.common.K8sObserver.ResponseType;
import org.jdrupes.vmoperator.common.VmPool;
import static org.jdrupes.vmoperator.manager.Constants.VM_OP_KIND_VM_POOL;
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
import org.jdrupes.vmoperator.manager.events.VmPoolChanged;
import org.jdrupes.vmoperator.util.GsonPtr;
import org.jgrapes.core.Channel;
import org.jgrapes.core.EventPipeline;
import org.jgrapes.core.annotation.Handler;
import org.jgrapes.core.events.Attached;
/**
* Watches for changes of VM pools. Reports the changes using
* {@link VmPoolChanged} events fired on a special pipeline to
* avoid concurrent change informations.
*/
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.ExcessiveImports" })
public class PoolManager extends
AbstractMonitor<K8sDynamicModel, K8sDynamicModels, Channel> {
private final ReentrantLock pendingLock = new ReentrantLock();
private final Map<String, Set<String>> pending = new ConcurrentHashMap<>();
private final Map<String, VmPool> pools = new ConcurrentHashMap<>();
private EventPipeline poolPipeline;
/**
* Instantiates a new VM pool manager.
*
* @param componentChannel the component channel
* @param channelManager the channel manager
*/
public PoolManager(Channel componentChannel) {
super(componentChannel, K8sDynamicModel.class,
K8sDynamicModels.class);
}
/**
* On attached.
*
* @param event the event
*/
@Handler
@SuppressWarnings("PMD.CompareObjectsWithEquals")
public void onAttached(Attached event) {
if (event.node() == this) {
poolPipeline = newEventPipeline();
}
}
@Override
protected void prepareMonitoring() throws IOException, ApiException {
client(new K8sClient());
// Get all our API versions
var ctx = K8s.context(client(), VM_OP_GROUP, "", VM_OP_KIND_VM_POOL);
if (ctx.isEmpty()) {
logger.severe(() -> "Cannot get CRD context.");
return;
}
context(ctx.get());
}
@Override
protected void handleChange(K8sClient client,
Watch.Response<K8sDynamicModel> response) {
var type = ResponseType.valueOf(response.type);
var poolName = response.object.metadata().getName();
// When pool is deleted, save VMs in pending
if (type == ResponseType.DELETED) {
try {
pendingLock.lock();
Optional.ofNullable(pools.get(poolName)).ifPresent(
p -> {
pending.computeIfAbsent(poolName, k -> Collections
.synchronizedSet(new HashSet<>())).addAll(p.vms());
pools.remove(poolName);
poolPipeline.fire(new VmPoolChanged(p, true));
});
} finally {
pendingLock.unlock();
}
return;
}
// Get full definition
var poolModel = response.object;
if (poolModel.data() == null) {
// ADDED event does not provide data, see
// https://github.com/kubernetes-client/java/issues/3215
try {
poolModel = K8sDynamicStub.get(client(), context(), namespace(),
poolModel.metadata().getName()).model().orElse(null);
} catch (ApiException e) {
return;
}
}
// Convert to VM pool
var vmPool = client().getJSON().getGson().fromJson(
GsonPtr.to(poolModel.data()).to("spec").get(),
VmPool.class);
V1ObjectMeta metadata = response.object.getMetadata();
vmPool.setName(metadata.getName());
// If modified, merge changes
if (type == ResponseType.MODIFIED && pools.containsKey(poolName)) {
pools.get(poolName).setPermissions(vmPool.permissions());
return;
}
// Add new pool
try {
pendingLock.lock();
Optional.ofNullable(pending.get(poolName)).ifPresent(s -> {
vmPool.vms().addAll(s);
});
pending.remove(poolName);
pools.put(poolName, vmPool);
poolPipeline.fire(new VmPoolChanged(vmPool));
} finally {
pendingLock.unlock();
}
}
/**
* Track VM definition changes.
*
* @param event the event
*/
@Handler
public void onVmDefChanged(VmDefChanged event) {
String vmName = event.vmDefinition().name();
switch (event.type()) {
case ADDED:
try {
pendingLock.lock();
event.vmDefinition().<List<String>> fromSpec("pools")
.orElse(Collections.emptyList()).stream().forEach(p -> {
if (pools.containsKey(p)) {
pools.get(p).vms().add(vmName);
} else {
pending.computeIfAbsent(p, k -> Collections
.synchronizedSet(new HashSet<>())).add(vmName);
}
poolPipeline.fire(new VmPoolChanged(pools.get(p)));
});
} finally {
pendingLock.unlock();
}
break;
case DELETED:
try {
pendingLock.lock();
pools.values().stream().forEach(p -> {
if (p.vms().remove(vmName)) {
poolPipeline.fire(new VmPoolChanged(p));
}
});
// Should not be necessary, but just in case
pending.values().stream().forEach(s -> s.remove(vmName));
} finally {
pendingLock.unlock();
}
break;
default:
break;
}
}
}

View file

@ -0,0 +1 @@
org.jdrupes.vmoperator.vmaccess.VmAccessFactory

View file

@ -1,9 +1,9 @@
<div
class="jdrupes-vmoperator-vmviewer jdrupes-vmoperator-vmviewer-confirm-reset">
class="jdrupes-vmoperator-vmaccess jdrupes-vmoperator-vmaccess-confirm-reset">
<p>${_("confirmResetMsg")}</p>
<p>
<span role="button" tabindex="0" class="svg-icon"
onclick="orgJDrupesVmOperatorVmViewer.confirmReset('${conletType}', '${conletId}')">
onclick="orgJDrupesVmOperatorVmAccess.confirmReset('${conletType}', '${conletId}')">
<svg viewBox="0 0 1541.33 1535.5083">
<path d="m 0,127.9968 v 448 c 0,35 29,64 64,64 h 448 c 35,0 64,-29 64,-64 0,-17 -6.92831,-33.07213 -19,-45 C 264.23058,241.7154 337.19508,314.89599 109,82.996795 c -11.999999,-12 -28,-19 -45,-19 -35,0 -64,29 -64,64.000005 z" />
<path d="m 772.97656,1535.5046 c 117.57061,0.3623 236.06134,-26.2848 345.77544,-81.4687 292.5708,-147.1572 459.8088,-465.37411 415.5214,-790.12504 C 1489.9861,339.15993 1243.597,77.463924 922.29883,14.342498 601.00067,-48.778928 274.05699,100.37563 110.62891,384.39133 c -34.855139,60.57216 -14.006492,137.9313 46.5664,172.78516 60.57172,34.85381 137.92941,14.00532 172.78321,-46.56641 109.97944,-191.12927 327.69604,-290.34657 543.53515,-247.94336 215.83913,42.40321 380.18953,216.77543 410.00973,435.44141 29.8203,218.66598 -81.8657,430.94957 -278.4863,529.84567 -196.6206,98.8962 -432.84043,61.8202 -589.90233,-92.6777 -24.91016,-24.5038 -85.48587,-83.3326 -119.02246,-52.9832 -24.01114,21.7292 -35.41741,29.5454 -59.9209,54.4559 -24.50381,24.9102 -35.33636,36.9034 -57.54543,60.4713 -38.1335,40.4667 34.10761,93.9685 59.01808,118.472 145.96311,143.5803 339.36149,219.2087 535.3125,219.8125 z"/>

View file

@ -1,7 +1,7 @@
<div title="${_("conletName")}"
class="jdrupes-vmoperator-vmviewer jdrupes-vmoperator-vmviewer-edit"
data-jgwc-on-load="orgJDrupesVmOperatorVmViewer.initEdit"
data-jgwc-on-action="orgJDrupesVmOperatorVmViewer.applyEdit"
class="jdrupes-vmoperator-vmaccess jdrupes-vmoperator-vmaccess-edit"
data-jgwc-on-load="orgJDrupesVmOperatorVmAccess.initEdit"
data-jgwc-on-action="orgJDrupesVmOperatorVmAccess.applyEdit"
data-jgwc-on-unload="JGConsole.jgwc.unmountVueApps">
<form :id="formId" ref="formDom" onsubmit="return false;">
<section>

View file

@ -1,7 +1,7 @@
<div
class="jdrupes-vmoperator-vmviewer jdrupes-vmoperator-vmviewer-preview"
class="jdrupes-vmoperator-vmaccess jdrupes-vmoperator-vmaccess-preview"
data-conlet-grid-rows="2" data-conlet-grid-columns="2"
data-jgwc-on-load="orgJDrupesVmOperatorVmViewer.initPreview"
data-jgwc-on-load="orgJDrupesVmOperatorVmAccess.initPreview"
data-jgwc-on-unload="JGConsole.jgwc.unmountVueApps"
data-conlet-resource-base="${conletResource('')}">
</div>

View file

@ -1,4 +1,4 @@
conletName = VM-Konsole
conletName = VM-Zugriff
okayLabel = Anwenden und Schließen
Select\ VM = VM auswählen

View file

@ -1,8 +1,8 @@
import typescript from 'rollup-plugin-typescript2';
import postcss from 'rollup-plugin-postcss';
let packagePath = "org/jdrupes/vmoperator/vmviewer";
let baseName = "VmViewer"
let packagePath = "org/jdrupes/vmoperator/vmaccess";
let baseName = "VmAccess"
let module = "build/generated/resources/" + packagePath
+ "/" + baseName + "-functions.js";

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.vmviewer;
package org.jdrupes.vmoperator.vmaccess;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonProperty;
@ -89,7 +89,7 @@ import org.jgrapes.webconsole.base.events.UpdateConletType;
import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet;
/**
* The Class VmViewer. The component supports the following
* The Class {@link VmAccess}. The component supports the following
* configuration properties:
*
* * `displayResource`: a map with the following entries:
@ -107,13 +107,13 @@ import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet;
*/
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.ExcessiveImports",
"PMD.CouplingBetweenObjects", "PMD.GodClass", "PMD.TooManyMethods" })
public class VmViewer extends FreeMarkerConlet<VmViewer.ViewerModel> {
public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
private static final String VM_NAME_PROPERTY = "vmName";
private static final String RENDERED
= VmViewer.class.getName() + ".rendered";
= VmAccess.class.getName() + ".rendered";
private static final String PENDING
= VmViewer.class.getName() + ".pending";
= VmAccess.class.getName() + ".pending";
private static final Set<RenderMode> MODES = RenderMode.asSet(
RenderMode.Preview, RenderMode.Edit);
private static final Set<RenderMode> MODES_FOR_GENERATED = RenderMode.asSet(
@ -140,7 +140,7 @@ public class VmViewer extends FreeMarkerConlet<VmViewer.ViewerModel> {
* on by default and that {@link Manager#fire(Event, Channel...)}
* sends the event to
*/
public VmViewer(Channel componentChannel) {
public VmAccess(Channel componentChannel) {
super(componentChannel);
}
@ -152,44 +152,57 @@ public class VmViewer extends FreeMarkerConlet<VmViewer.ViewerModel> {
@SuppressWarnings({ "unchecked", "PMD.AvoidDuplicateLiterals" })
@Handler
public void onConfigurationUpdate(ConfigurationUpdate event) {
event.structured(componentPath()).ifPresent(c -> {
try {
var dispRes = (Map<String, Object>) c
.getOrDefault("displayResource", Collections.emptyMap());
switch ((String) dispRes.getOrDefault("preferredIpVersion",
"")) {
case "ipv6":
preferredIpVersion = Inet6Address.class;
break;
case "ipv4":
default:
preferredIpVersion = Inet4Address.class;
break;
event.structured(componentPath())
.or(() -> {
var oldConfig = event.structured("/Manager/GuiHttpServer"
+ "/ConsoleWeblet/WebConsole/ComponentCollector/VmViewer");
if (oldConfig.isPresent()) {
logger.warning(() -> "Using configuration with old "
+ "component name \"VmViewer\", please update to "
+ "\"VmAccess\"");
}
return oldConfig;
})
.ifPresent(c -> {
try {
var dispRes = (Map<String, Object>) c
.getOrDefault("displayResource",
Collections.emptyMap());
switch ((String) dispRes.getOrDefault("preferredIpVersion",
"")) {
case "ipv6":
preferredIpVersion = Inet6Address.class;
break;
case "ipv4":
default:
preferredIpVersion = Inet4Address.class;
break;
}
// Delete connection file
deleteConnectionFile
= Optional.ofNullable(c.get("deleteConnectionFile"))
.filter(v -> v instanceof String).map(v -> (String) v)
.map(Boolean::parseBoolean).orElse(true);
// Delete connection file
deleteConnectionFile
= Optional.ofNullable(c.get("deleteConnectionFile"))
.filter(v -> v instanceof String)
.map(v -> (String) v)
.map(Boolean::parseBoolean).orElse(true);
// Users or roles for which previews should be synchronized
syncUsers = ((List<Map<String, String>>) c.getOrDefault(
"syncPreviewsFor", Collections.emptyList())).stream()
.map(m -> m.get("user"))
.filter(s -> s != null).collect(Collectors.toSet());
logger.finest(() -> "Syncing previews for users: "
+ syncUsers.toString());
syncRoles = ((List<Map<String, String>>) c.getOrDefault(
"syncPreviewsFor", Collections.emptyList())).stream()
.map(m -> m.get("role"))
.filter(s -> s != null).collect(Collectors.toSet());
logger.finest(() -> "Syncing previews for roles: "
+ syncRoles.toString());
} catch (ClassCastException e) {
logger.config("Malformed configuration: " + e.getMessage());
}
});
// Users or roles for which previews should be synchronized
syncUsers = ((List<Map<String, String>>) c.getOrDefault(
"syncPreviewsFor", Collections.emptyList())).stream()
.map(m -> m.get("user"))
.filter(s -> s != null).collect(Collectors.toSet());
logger.finest(() -> "Syncing previews for users: "
+ syncUsers.toString());
syncRoles = ((List<Map<String, String>>) c.getOrDefault(
"syncPreviewsFor", Collections.emptyList())).stream()
.map(m -> m.get("role"))
.filter(s -> s != null).collect(Collectors.toSet());
logger.finest(() -> "Syncing previews for roles: "
+ syncRoles.toString());
} catch (ClassCastException e) {
logger.config("Malformed configuration: " + e.getMessage());
}
});
}
private boolean syncPreviews(Session session) {
@ -222,7 +235,7 @@ public class VmViewer extends FreeMarkerConlet<VmViewer.ViewerModel> {
.addRenderMode(RenderMode.Preview)
.addScript(new ScriptResource().setScriptType("module")
.setScriptUri(event.renderSupport().conletResource(
type(), "VmViewer-functions.js"))));
type(), "VmAccess-functions.js"))));
channel.session().put(RENDERED, new HashSet<>());
}
@ -259,7 +272,7 @@ public class VmViewer extends FreeMarkerConlet<VmViewer.ViewerModel> {
foundMissing = true;
}
fire(new AddConletRequest(event.event().event().renderSupport(),
VmViewer.class.getName(),
VmAccess.class.getName(),
RenderMode.asSet(RenderMode.Preview))
.addProperty(VM_NAME_PROPERTY, vmName),
connection);
@ -283,7 +296,7 @@ public class VmViewer extends FreeMarkerConlet<VmViewer.ViewerModel> {
private String storagePath(Session session, String conletId) {
return "/" + WebConsoleUtils.userFromSession(session)
.map(ConsoleUser::getName).orElse("")
+ "/" + VmViewer.class.getName() + "/" + conletId;
+ "/" + VmAccess.class.getName() + "/" + conletId;
}
@Override
@ -365,7 +378,7 @@ public class VmViewer extends FreeMarkerConlet<VmViewer.ViewerModel> {
// Render
Template tpl
= freemarkerConfig().getTemplate("VmViewer-preview.ftl.html");
= freemarkerConfig().getTemplate("VmAccess-preview.ftl.html");
channel.respond(new RenderConlet(type(), conletId,
processTemplate(event, tpl,
fmModel(event, channel, conletId, model)))
@ -383,7 +396,7 @@ public class VmViewer extends FreeMarkerConlet<VmViewer.ViewerModel> {
}
if (event.renderAs().contains(RenderMode.Edit)) {
Template tpl = freemarkerConfig()
.getTemplate("VmViewer-edit.ftl.html");
.getTemplate("VmAccess-edit.ftl.html");
var fmModel = fmModel(event, channel, conletId, model);
fmModel.put("vmNames", accessibleVms(channel));
channel.respond(new OpenModalDialog(type(), conletId,
@ -633,7 +646,7 @@ public class VmViewer extends FreeMarkerConlet<VmViewer.ViewerModel> {
ResourceBundle resourceBundle) throws TemplateNotFoundException,
MalformedTemplateNameException, ParseException, IOException {
Template tpl = freemarkerConfig()
.getTemplate("VmViewer-confirmReset.ftl.html");
.getTemplate("VmAccess-confirmReset.ftl.html");
channel.respond(new OpenModalDialog(type(), model.getConletId(),
processTemplate(event, tpl,
fmModel(event, channel, model.getConletId(), model)))

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.vmviewer;
package org.jdrupes.vmoperator.vmaccess;
import java.util.Map;
import java.util.Optional;
@ -25,9 +25,9 @@ import org.jgrapes.core.ComponentType;
import org.jgrapes.webconsole.base.ConletComponentFactory;
/**
* The factory service for {@link VmViewer}s.
* The factory service for {@link VmAccess}s.
*/
public class VmViewerFactory implements ConletComponentFactory {
public class VmAccessFactory implements ConletComponentFactory {
/*
* (non-Javadoc)
@ -36,7 +36,7 @@ public class VmViewerFactory implements ConletComponentFactory {
*/
@Override
public Class<? extends ComponentType> componentType() {
return VmViewer.class;
return VmAccess.class;
}
/*
@ -48,7 +48,7 @@ public class VmViewerFactory implements ConletComponentFactory {
@Override
public Optional<ComponentType> create(Channel componentChannel,
Map<?, ?> properties) {
return Optional.of(new VmViewer(componentChannel));
return Optional.of(new VmAccess(componentChannel));
}
}

View file

@ -24,12 +24,12 @@ import JgwcPlugin, { JGWC } from "jgwc";
import { provideApi, getApi } from "aash-plugin";
import l10nBundles from "l10nBundles";
import "./VmViewer-style.scss";
import "./VmAccess-style.scss";
// For global access
declare global {
interface Window {
orgJDrupesVmOperatorVmViewer: {
orgJDrupesVmOperatorVmAccess: {
initPreview?: (previewDom: HTMLElement, isUpdate: boolean) => void,
initEdit?: (viewDom: HTMLElement, isUpdate: boolean) => void,
applyEdit?: (viewDom: HTMLElement, apply: boolean) => void,
@ -38,7 +38,7 @@ declare global {
}
}
window.orgJDrupesVmOperatorVmViewer = {};
window.orgJDrupesVmOperatorVmAccess = {};
interface Api {
/* eslint-disable @typescript-eslint/no-explicit-any */
@ -51,7 +51,7 @@ const localize = (key: string) => {
l10nBundles, JGWC.lang(), key);
};
window.orgJDrupesVmOperatorVmViewer.initPreview = (previewDom: HTMLElement,
window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
_isUpdate: boolean) => {
const app = createApp({
setup(_props: object) {
@ -107,7 +107,7 @@ window.orgJDrupesVmOperatorVmViewer.initPreview = (previewDom: HTMLElement,
:title="localize('Open console')"></span><span
style="visibility: hidden;"><img
:src="resourceBase + 'computer.svg'"></span></td>
<td class="jdrupes-vmoperator-vmviewer-preview-action-list">
<td class="jdrupes-vmoperator-vmaccess-preview-action-list">
<span role="button"
:aria-disabled="!startable || !permissions.includes('start')"
tabindex="0" class="fa fa-play" :title="localize('Start VM')"
@ -138,25 +138,25 @@ window.orgJDrupesVmOperatorVmViewer.initPreview = (previewDom: HTMLElement,
app.mount(previewDom);
};
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmviewer.VmViewer",
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmaccess.VmAccess",
"updateConfig", function(conletId: string, vmName: string) {
const conlet = JGConsole.findConletPreview(conletId);
if (!conlet) {
return;
}
const api = getApi<Api>(conlet.element().querySelector(
":scope .jdrupes-vmoperator-vmviewer-preview"))!;
":scope .jdrupes-vmoperator-vmaccess-preview"))!;
api.vmName = vmName;
});
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmviewer.VmViewer",
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmaccess.VmAccess",
"updateVmDefinition", function(conletId: string, vmDefinition: any) {
const conlet = JGConsole.findConletPreview(conletId);
if (!conlet) {
return;
}
const api = getApi<Api>(conlet.element().querySelector(
":scope .jdrupes-vmoperator-vmviewer-preview"))!;
":scope .jdrupes-vmoperator-vmaccess-preview"))!;
// Add some short-cuts for rendering
vmDefinition.name = vmDefinition.metadata.name;
vmDefinition.currentCpus = vmDefinition.status.cpus;
@ -173,13 +173,13 @@ JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmviewer.VmViewer",
api.vmDefinition = vmDefinition;
});
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmviewer.VmViewer",
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmaccess.VmAccess",
"openConsole", function(_conletId: string, mimeType: string, data: string) {
let target = document.getElementById(
"org.jdrupes.vmoperator.vmviewer.VmViewer.target");
"org.jdrupes.vmoperator.vmaccess.VmAccess.target");
if (!target) {
target = document.createElement("iframe");
target.id = "org.jdrupes.vmoperator.vmviewer.VmViewer.target";
target.id = "org.jdrupes.vmoperator.vmaccess.VmAccess.target";
target.setAttribute("name", target.id);
target.setAttribute("style", "display: none;");
document.querySelector("body")!.append(target);
@ -188,7 +188,7 @@ JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmviewer.VmViewer",
window.open(url, target.id);
});
window.orgJDrupesVmOperatorVmViewer.initEdit = (dialogDom: HTMLElement,
window.orgJDrupesVmOperatorVmAccess.initEdit = (dialogDom: HTMLElement,
isUpdate: boolean) => {
if (isUpdate) {
return;
@ -209,7 +209,7 @@ window.orgJDrupesVmOperatorVmViewer.initEdit = (dialogDom: HTMLElement,
const conlet = JGConsole.findConletPreview(conletId);
if (conlet) {
const api = getApi<Api>(conlet.element().querySelector(
":scope .jdrupes-vmoperator-vmviewer-preview"))!;
":scope .jdrupes-vmoperator-vmaccess-preview"))!;
vmNameInput.value = api.vmName;
}
@ -222,7 +222,7 @@ window.orgJDrupesVmOperatorVmViewer.initEdit = (dialogDom: HTMLElement,
app.mount(dialogDom);
}
window.orgJDrupesVmOperatorVmViewer.applyEdit =
window.orgJDrupesVmOperatorVmAccess.applyEdit =
(dialogDom: HTMLElement, apply: boolean) => {
if (!apply) {
return;
@ -233,7 +233,7 @@ window.orgJDrupesVmOperatorVmViewer.applyEdit =
JGConsole.notifyConletModel(conletId, "selectedVm", vmName);
}
window.orgJDrupesVmOperatorVmViewer.confirmReset =
window.orgJDrupesVmOperatorVmAccess.confirmReset =
(conletType: string, conletId: string) => {
JGConsole.instance.closeModalDialog(conletType, conletId);
JGConsole.notifyConletModel(conletId, "resetConfirmed");

View file

@ -19,7 +19,7 @@
/*
* Conlet specific styles.
*/
.jdrupes-vmoperator-vmviewer {
.jdrupes-vmoperator-vmaccess {
span[role="button"].svg-icon {
display: inline-block;
@ -47,7 +47,7 @@
}
}
.jdrupes-vmoperator-vmviewer.jdrupes-vmoperator-vmviewer-preview {
.jdrupes-vmoperator-vmaccess.jdrupes-vmoperator-vmaccess-preview {
img {
height: 3em;
@ -58,7 +58,7 @@
}
}
.jdrupes-vmoperator-vmviewer-preview-action-list {
.jdrupes-vmoperator-vmaccess-preview-action-list {
white-space: nowrap;
}
@ -76,13 +76,13 @@
}
}
.jdrupes-vmoperator-vmviewer.jdrupes-vmoperator-vmviewer-edit {
.jdrupes-vmoperator-vmaccess.jdrupes-vmoperator-vmaccess-edit {
select {
width: 15em;
}
}
.jdrupes-vmoperator-vmviewer.jdrupes-vmoperator-vmviewer-confirm-reset {
.jdrupes-vmoperator-vmaccess.jdrupes-vmoperator-vmaccess-confirm-reset {
p {
text-align: center;
}

View file

@ -1,6 +1,6 @@
/*
* VM-Operator
* Copyright (C) 2023 Michael N. Lipp
* Copyright (C) 2023, 2024 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
@ -16,4 +16,4 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.vmconlet;
package org.jdrupes.vmoperator.vmaccess;

View file

@ -14,7 +14,7 @@
"aash-plugin": ["./build/unpacked/org/jgrapes/webconsole/provider/jgwcvuecomponents/aash-vue-components/lib/AashPlugin"],
"jgconsole": ["./build/unpacked/org/jgrapes/webconsole/base/JGConsole"],
"jgwc": ["./build/unpacked/org/jgrapes/webconsole/provider/jgwcvuecomponents/jgwc-vue-components/jgwc-components"],
"l10nBundles": ["./src/org/jdrupes/vmoperator/vmviewer/browser/l10nBundles-stub"],
"l10nBundles": ["./src/org/jdrupes/vmoperator/vmaccess/browser/l10nBundles-stub"],
"vue": ["./build/unpacked/org/jgrapes/webconsole/provider/vue/vue/vue"]
}
},

View file

@ -1 +0,0 @@
org.jdrupes.vmoperator.vmconlet.VmConletFactory

View file

@ -0,0 +1 @@
org.jdrupes.vmoperator.vmmgmt.VmMgmtFactory

View file

@ -1,6 +1,6 @@
<div class="jdrupes-vmoperator-vmconlet jdrupes-vmoperator-vmconlet-preview"
<div class="jdrupes-vmoperator-vmmgmt jdrupes-vmoperator-vmmgmt-preview"
data-conlet-grid-rows="5"
data-jgwc-on-load="orgJDrupesVmOperatorVmConlet.initPreview"
data-jgwc-on-load="orgJDrupesVmOperatorVmMgmt.initPreview"
data-jgwc-on-unload="JGConsole.jgwc.unmountVueApps">
<form>

View file

@ -1,7 +1,7 @@
<div class="jdrupes-vmoperator-vmconlet jdrupes-vmoperator-vmconlet-view"
data-jgwc-on-load="orgJDrupesVmOperatorVmConlet.initView"
<div class="jdrupes-vmoperator-vmmgmt jdrupes-vmoperator-vmmgmt-view"
data-jgwc-on-load="orgJDrupesVmOperatorVmMgmt.initView"
data-jgwc-on-unload="JGConsole.jgwc.unmountVueApps">
<div class="jdrupes-vmoperator-vmconlet-view-search">
<div class="jdrupes-vmoperator-vmmgmt-view-search">
<form>
<label class="form__label--horizontal">
<span>{{ localize("Filter") }}</span>
@ -13,7 +13,7 @@
</form>
</div>
<table
class="table--basic--striped jdrupes-vmoperator-vmconlet-view-table">
class="table--basic--striped jdrupes-vmoperator-vmmgmt-view-table">
<thead>
<tr>
<th v-for="key in controller.keys"
@ -51,7 +51,7 @@
<span v-else
v-html="controller.breakBeforeDots(entry[key])"></span>
</td>
<td class="jdrupes-vmoperator-vmconlet-view-action-list">
<td class="jdrupes-vmoperator-vmmgmt-view-action-list">
<span role="button"
v-if="entry.spec.vm.state != 'Running' && !entry['running']"
tabindex="0" class="fa fa-play" :title="localize('Start VM')"

View file

@ -1,8 +1,8 @@
import typescript from 'rollup-plugin-typescript2';
import postcss from 'rollup-plugin-postcss';
let packagePath = "org/jdrupes/vmoperator/vmconlet";
let baseName = "VmConlet"
let packagePath = "org/jdrupes/vmoperator/vmmgmt";
let baseName = "VmMgmt"
let module = "build/generated/resources/" + packagePath
+ "/" + baseName + "-functions.js";

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.vmconlet;
package org.jdrupes.vmoperator.vmmgmt;
import java.time.Duration;
import java.time.Instant;

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.vmconlet;
package org.jdrupes.vmoperator.vmmgmt;
import freemarker.core.ParseException;
import freemarker.template.MalformedTemplateNameException;
@ -61,11 +61,11 @@ import org.jgrapes.webconsole.base.events.SetLocale;
import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet;
/**
* The Class VmConlet.
* The Class {@link VmMgmt}.
*/
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis",
"PMD.CouplingBetweenObjects" })
public class VmConlet extends FreeMarkerConlet<VmConlet.VmsModel> {
public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
private static final Set<RenderMode> MODES = RenderMode.asSet(
RenderMode.Preview, RenderMode.View);
@ -88,7 +88,7 @@ public class VmConlet extends FreeMarkerConlet<VmConlet.VmsModel> {
* sends the event to
*/
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
public VmConlet(Channel componentChannel) {
public VmMgmt(Channel componentChannel) {
super(componentChannel);
setPeriodicRefresh(Duration.ofMinutes(1), () -> new Update());
}
@ -115,7 +115,7 @@ public class VmConlet extends FreeMarkerConlet<VmConlet.VmsModel> {
.addRenderMode(RenderMode.Preview)
.addScript(new ScriptResource().setScriptType("module")
.setScriptUri(event.renderSupport().conletResource(
type(), "VmConlet-functions.js"))));
type(), "VmMgmt-functions.js"))));
}
@Override
@ -133,7 +133,7 @@ public class VmConlet extends FreeMarkerConlet<VmConlet.VmsModel> {
boolean sendVmInfos = false;
if (event.renderAs().contains(RenderMode.Preview)) {
Template tpl
= freemarkerConfig().getTemplate("VmConlet-preview.ftl.html");
= freemarkerConfig().getTemplate("VmMgmt-preview.ftl.html");
channel.respond(new RenderConlet(type(), conletId,
processTemplate(event, tpl,
fmModel(event, channel, conletId, conletState)))
@ -150,7 +150,7 @@ public class VmConlet extends FreeMarkerConlet<VmConlet.VmsModel> {
}
if (event.renderAs().contains(RenderMode.View)) {
Template tpl
= freemarkerConfig().getTemplate("VmConlet-view.ftl.html");
= freemarkerConfig().getTemplate("VmMgmt-view.ftl.html");
channel.respond(new RenderConlet(type(), conletId,
processTemplate(event, tpl,
fmModel(event, channel, conletId, conletState)))

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.vmconlet;
package org.jdrupes.vmoperator.vmmgmt;
import java.util.Map;
import java.util.Optional;
@ -25,9 +25,9 @@ import org.jgrapes.core.ComponentType;
import org.jgrapes.webconsole.base.ConletComponentFactory;
/**
* The factory service for {@link VmConlet}s.
* The factory service for {@link VmMgmt}s.
*/
public class VmConletFactory implements ConletComponentFactory {
public class VmMgmtFactory implements ConletComponentFactory {
/*
* (non-Javadoc)
@ -36,7 +36,7 @@ public class VmConletFactory implements ConletComponentFactory {
*/
@Override
public Class<? extends ComponentType> componentType() {
return VmConlet.class;
return VmMgmt.class;
}
/*
@ -48,7 +48,7 @@ public class VmConletFactory implements ConletComponentFactory {
@Override
public Optional<ComponentType> create(Channel componentChannel,
Map<?, ?> properties) {
return Optional.of(new VmConlet(componentChannel));
return Optional.of(new VmMgmt(componentChannel));
}
}

View file

@ -27,19 +27,19 @@ import { formatMemory, parseMemory } from "./MemorySize";
import CpuRamChart from "./CpuRamChart";
import ConditionlInputController from "./ConditionalInputController";
import "./VmConlet-style.scss";
import "./VmMgmt-style.scss";
// For global access
declare global {
interface Window {
orgJDrupesVmOperatorVmConlet: {
orgJDrupesVmOperatorVmMgmt: {
initPreview?: (previewDom: HTMLElement, isUpdate: boolean) => void,
initView?: (viewDom: HTMLElement, isUpdate: boolean) => void
}
}
}
window.orgJDrupesVmOperatorVmConlet = {};
window.orgJDrupesVmOperatorVmMgmt = {};
const vmInfos = reactive(new Map());
const vmSummary = reactive({
@ -64,7 +64,7 @@ const shortDateTime = (time: Date) => {
const chartData = new TimeSeries(2);
const chartDateUpdate = ref<Date>(null);
window.orgJDrupesVmOperatorVmConlet.initPreview = (previewDom: HTMLElement,
window.orgJDrupesVmOperatorVmMgmt.initPreview = (previewDom: HTMLElement,
_isUpdate: boolean) => {
const app = createApp({
setup(_props: object) {
@ -98,7 +98,7 @@ window.orgJDrupesVmOperatorVmConlet.initPreview = (previewDom: HTMLElement,
app.mount(previewDom);
};
window.orgJDrupesVmOperatorVmConlet.initView = (viewDom: HTMLElement,
window.orgJDrupesVmOperatorVmMgmt.initView = (viewDom: HTMLElement,
_isUpdate: boolean) => {
const app = createApp({
setup(_props: object) {
@ -174,7 +174,7 @@ window.orgJDrupesVmOperatorVmConlet.initView = (viewDom: HTMLElement,
app.mount(viewDom);
};
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmconlet.VmConlet",
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmmgmt.VmMgmt",
// eslint-disable-next-line @typescript-eslint/no-explicit-any
"updateVm", function(_conletId: string, vmDefinition: any) {
// Add some short-cuts for table controller
@ -194,12 +194,12 @@ JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmconlet.VmConlet",
vmInfos.set(vmDefinition.name, vmDefinition);
});
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmconlet.VmConlet",
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmmgmt.VmMgmt",
"removeVm", function(_conletId: string, vmName: string) {
vmInfos.delete(vmName);
});
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmconlet.VmConlet",
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmmgmt.VmMgmt",
// eslint-disable-next-line @typescript-eslint/no-explicit-any
"summarySeries", function(_conletId: string, series: any[]) {
chartData.clear();
@ -210,7 +210,7 @@ JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmconlet.VmConlet",
chartDateUpdate.value = new Date();
});
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmconlet.VmConlet",
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmmgmt.VmMgmt",
// eslint-disable-next-line @typescript-eslint/no-explicit-any
"updateSummary", function(_conletId: string, summary: any) {
chartData.push(new Date(), summary.usedCpus, Number(summary.usedRam));

View file

@ -20,7 +20,7 @@
* Conlet specific styles.
*/
.jdrupes-vmoperator-vmconlet-preview {
.jdrupes-vmoperator-vmmgmt-preview {
form {
float: right;
padding: 0.15em 0.3em;
@ -37,7 +37,7 @@
}
}
.jdrupes-vmoperator-vmconlet-view-search {
.jdrupes-vmoperator-vmmgmt-view-search {
display: flex;
justify-content: flex-end;
@ -46,7 +46,7 @@
}
}
.jdrupes-vmoperator-vmconlet-view-table {
.jdrupes-vmoperator-vmmgmt-view-table {
td {
vertical-align: top;
@ -94,7 +94,7 @@
}
}
.jdrupes-vmoperator-vmconlet-view-action-list {
.jdrupes-vmoperator-vmmgmt-view-action-list {
white-space: nowrap;
[role=button] {

View file

@ -16,4 +16,4 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.vmviewer;
package org.jdrupes.vmoperator.vmmgmt;

View file

@ -14,7 +14,7 @@
"aash-plugin": ["./build/unpacked/org/jgrapes/webconsole/provider/jgwcvuecomponents/aash-vue-components/lib/AashPlugin"],
"jgconsole": ["./build/unpacked/org/jgrapes/webconsole/base/JGConsole"],
"jgwc": ["./build/unpacked/org/jgrapes/webconsole/provider/jgwcvuecomponents/jgwc-vue-components/jgwc-components"],
"l10nBundles": ["./src/org/jdrupes/vmoperator/vmconlet/browser/l10nBundles-stub"],
"l10nBundles": ["./src/org/jdrupes/vmoperator/vmmgmt/browser/l10nBundles-stub"],
"vue": ["./build/unpacked/org/jgrapes/webconsole/provider/vue/vue/vue"],
"chartjs": ["./build/unpacked/org/jgrapes/webconsole/provider/chartjs/chart.js/auto/auto"]
}

View file

@ -1 +0,0 @@
org.jdrupes.vmoperator.vmviewer.VmViewerFactory

View file

@ -12,8 +12,8 @@ rootProject.name = 'VM-Operator'
include 'org.jdrupes.vmoperator.manager'
include 'org.jdrupes.vmoperator.manager.events'
include 'org.jdrupes.vmoperator.vmconlet'
include 'org.jdrupes.vmoperator.vmviewer'
include 'org.jdrupes.vmoperator.vmaccess'
include 'org.jdrupes.vmoperator.vmmgmt'
include 'org.jdrupes.vmoperator.runner.qemu'
include 'org.jdrupes.vmoperator.common'
include 'org.jdrupes.vmoperator.util'

View file

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before After
Before After

View file

@ -5,6 +5,19 @@ layout: vm-operator
# Upgrading
## To version 4.0.0
The VmViewer conlet has been renamed to VmAccess. This affects the
[configuration](https://jdrupes.org/vm-operator/user-gui.html). Configuration information using the old path
"/Manager/GuiHttpServer/ConsoleWeblet/WebConsole/ComponentCollector/VmViewer"
is still accepted for backward compatibility, but should be updated.
The change of name also causes conlets added to the overview page by
users to "disappear" from the GUI. They have to be re-added.
The latter behavior also applies to the VmConlet conlet which has been
renamed to VmMgmt.
## To version 3.4.0
Starting with this version, the VM-Operator no longer uses a stateful set

View file

@ -11,7 +11,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
and stop them.
![VM-Viewer](VmViewer-preview.png)
![VM-Viewer](VmAccess-preview.png)
The configuration options resulting from this seemingly simple
requirement are unexpectedly complex.
@ -62,7 +62,7 @@ objects that either specify a role or a user.
"/ConsoleWeblet":
"/WebConsole":
"/ComponentCollector":
"/VmViewer":
"/VmAccess":
syncPreviewsFor:
- role: user
- user: test