diff --git a/README.md b/README.md index 176437a..1895bbb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Java CI with Gradle](https://github.com/mnlipp/VM-Operator/actions/workflows/gradle.yml/badge.svg)](https://github.com/mnlipp/VM-Operator/actions/workflows/gradle.yml) +[![Java CI with Gradle](https://github.com/mnlipp/VM-Operator/actions/workflows/gradle.yml/badge.svg)](https://github.com/mnlipp/VM-Operator/actions/workflows/gradle.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/2277842dac894de4b663c6aa2779077e)](https://app.codacy.com/gh/mnlipp/VM-Operator/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) ![Latest Manager](https://img.shields.io/github/v/tag/mnlipp/vm-operator?filter=manager*&label=latest) ![Latest Runner](https://img.shields.io/github/v/tag/mnlipp/vm-operator?filter=runner-qemu*&label=latest) @@ -6,7 +6,8 @@ # Run Qemu in Kubernetes Pods The goal of this project is to provide the means for running Qemu -based VMs in Kubernetes pods. +based VMs in Kubernetes pods. See the [project's home page](https://mnlipp.github.io/VM-Operator/) for details. + diff --git a/deploy/crds/vms-crd.yaml b/deploy/crds/vms-crd.yaml index bfe3985..f441cbc 100644 --- a/deploy/crds/vms-crd.yaml +++ b/deploy/crds/vms-crd.yaml @@ -1012,12 +1012,7 @@ spec: type: array items: type: string - enum: - - start - - stop - - reset - - accessConsole - - "*" + enum: ["start", "stop", "accessConsole", "*"] default: [] vm: type: object diff --git a/dev-example/Readme.md b/dev-example/Readme.md index 516fb7e..dfcd3e8 100644 --- a/dev-example/Readme.md +++ b/dev-example/Readme.md @@ -1,16 +1,16 @@ # Example setup for development -The CRD must be deployed independently. Apart from that, the +The CRD must be deployed independently. Apart from that, the `kustomize.yaml` * creates a small cdrom image repository and - + * deploys the operator in namespace `vmop-dev` with a replica of 0. - + This allows you to run the manager in your IDE. The `kustomize.yaml` also changes the container image repository for -the operator to a private repository for development. You have to +the operator to a private repository for development. You have to adapt this to your own repository if you also want to test your development version in a container. diff --git a/example/local-path/Readme.md b/example/local-path/Readme.md index bdba8cc..7afb948 100644 --- a/example/local-path/Readme.md +++ b/example/local-path/Readme.md @@ -1,17 +1,17 @@ # Example setup -The CRD must be deployed independently. +The CRD must be deployed independently. ```sh kubectl apply -f https://github.com/mnlipp/VM-Operator/raw/main/deploy/crds/vms-crd.yaml ``` -Apart from that, the `kustomize.yaml` defines a namespace for the manager +Apart from that, the `kustomize.yaml` defines a namespace for the manager (and the VMs managed by it) and patches the repository PVC to create a small volume using local-path. -A second patch provides a new configuration file for the manager -that makes it use the local-path storage class when creating the +A second patch provides a new configuration file for the manager +that makes it use the local-path storage class when creating the small volume for a runner's data. The `kustomize.yaml` does not include the test VM. Before creating diff --git a/example/rook-ceph/Readme.md b/example/rook-ceph/Readme.md index 3756e93..1d2cfc6 100644 --- a/example/rook-ceph/Readme.md +++ b/example/rook-ceph/Readme.md @@ -1,12 +1,12 @@ # Example setup -The CRD must be deployed independently. +The CRD must be deployed independently. ```sh kubectl apply -f https://github.com/mnlipp/VM-Operator/raw/main/deploy/crds/vms-crd.yaml ``` -Apart from that, the `kustomize.yaml` defines a namespace for the manager +Apart from that, the `kustomize.yaml` defines a namespace for the manager (and the VMs managed by it) and applies patches to use `rook-cephfs` as storage class (instead of the default storage class). diff --git a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinitionModel.java b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinitionModel.java index 5e1ebb0..fa59c82 100644 --- a/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinitionModel.java +++ b/org.jdrupes.vmoperator.common/src/org/jdrupes/vmoperator/common/VmDefinitionModel.java @@ -41,8 +41,7 @@ public class VmDefinitionModel extends K8sDynamicModel { * Permissions for accessing and manipulating the VM. */ public enum Permission { - START("start"), STOP("stop"), RESET("reset"), - ACCESS_CONSOLE("accessConsole"); + START("start"), STOP("stop"), ACCESS_CONSOLE("accessConsole"); @SuppressWarnings("PMD.UseConcurrentHashMap") private static Map reprs = new HashMap<>(); diff --git a/org.jdrupes.vmoperator.manager.events/src/org/jdrupes/vmoperator/manager/events/ResetVm.java b/org.jdrupes.vmoperator.manager.events/src/org/jdrupes/vmoperator/manager/events/ResetVm.java deleted file mode 100644 index f3320c8..0000000 --- a/org.jdrupes.vmoperator.manager.events/src/org/jdrupes/vmoperator/manager/events/ResetVm.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 . - */ - -package org.jdrupes.vmoperator.manager.events; - -import org.jgrapes.core.Event; - -/** - * Triggers a reset of the VM. - */ -@SuppressWarnings("PMD.DataClass") -public class ResetVm extends Event { - - private final String vmName; - - /** - * Instantiates a new event. - * - * @param vmName the vm name - */ - public ResetVm(String vmName) { - this.vmName = vmName; - } - - /** - * Gets the vm name. - * - * @return the vm name - */ - public String vmName() { - return vmName; - } -} diff --git a/org.jdrupes.vmoperator.manager/build.gradle b/org.jdrupes.vmoperator.manager/build.gradle index 1887108..a8b67a0 100644 --- a/org.jdrupes.vmoperator.manager/build.gradle +++ b/org.jdrupes.vmoperator.manager/build.gradle @@ -18,10 +18,10 @@ dependencies { implementation 'org.jgrapes:org.jgrapes.http:[3.1.0,4)' implementation 'org.jgrapes:org.jgrapes.util:[1.34.0,2)' - implementation 'org.jgrapes:org.jgrapes.webconsole.base:[1.7.0,2)' + implementation 'org.jgrapes:org.jgrapes.webconsole.base:[1.5.0,2)' implementation 'org.jgrapes:org.jgrapes.webconsole.vuejs:[1.5.0,2)' implementation 'org.jgrapes:org.jgrapes.webconsole.rbac:[1.3.0,2)' - implementation 'org.jgrapes:org.jgrapes.webconlet.oidclogin:[1.4.0,2)' + implementation 'org.jgrapes:org.jgrapes.webconlet.oidclogin:[1.3.0,2)' implementation 'org.jgrapes:org.jgrapes.webconlet.markdowndisplay:[1.2.0,2)' runtimeOnly 'org.jgrapes:org.jgrapes.webconlet.sysinfo:[1.4.0,2)' diff --git a/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/ManagerIntro-Preview.md b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/ManagerIntro-Preview.md index b6b9efa..50a3024 100644 --- a/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/ManagerIntro-Preview.md +++ b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/ManagerIntro-Preview.md @@ -1,5 +1,5 @@ -You can use the "puzzle piece" icon on the top right corner of the +You can use the "puzzle piece" icon on the top right corner of the page to add display widgets (conlets) to the overview tab. Use the "full screen" icon on the top right corner of any -conlet (if available) to get a detailed view. +conlet (if available) to get a detailed view. \ No newline at end of file diff --git a/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/ManagerIntro-Preview_de.md b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/ManagerIntro-Preview_de.md index bec5f3e..e5e4d68 100644 --- a/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/ManagerIntro-Preview_de.md +++ b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/ManagerIntro-Preview_de.md @@ -1,4 +1,4 @@ -Verwenden Sie das "Puzzle"-Icon auf der rechten oberen Ecke +Verwenden Sie das "Puzzle"-Icon auf der rechten oberen Ecke der Seite, um Anzeige-Widgets (Conlets) hinzuzufügen. Wenn sich in der rechten oberen Ecke eines Conlets ein Vollbild-Icon diff --git a/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerConfig.ftl.yaml b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerConfig.ftl.yaml index 253f9b7..7679a68 100644 --- a/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerConfig.ftl.yaml +++ b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerConfig.ftl.yaml @@ -48,12 +48,6 @@ data: # Whether a shutdown initiated by the guest stops the pod deployment guestShutdownStops: ${ cr.spec.guestShutdownStops!false?c } - # When incremented, the VM is reset. The value has no default value, - # i.e. if you start the VM without a value for this property, and - # decide to trigger a reset later, you have to first set the value - # and then inrement it. - resetCounter: ${ cr.resetCount } - # Forward the cloud-init data if provided <#if cr.spec.cloudInit??> cloudInit: diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java index 4219e53..a882a79 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/ConfigMapReconciler.java @@ -36,6 +36,7 @@ import org.jdrupes.vmoperator.common.K8s; import static org.jdrupes.vmoperator.manager.Constants.APP_NAME; import static org.jdrupes.vmoperator.manager.Constants.VM_OP_NAME; import org.jdrupes.vmoperator.manager.events.VmChannel; +import org.jdrupes.vmoperator.manager.events.VmDefChanged; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; @@ -61,6 +62,7 @@ import org.yaml.snakeyaml.constructor.SafeConstructor; /** * Reconcile. * + * @param event the event * @param model the model * @param channel the channel * @return the dynamic kubernetes object @@ -68,8 +70,8 @@ import org.yaml.snakeyaml.constructor.SafeConstructor; * @throws TemplateException the template exception * @throws ApiException the api exception */ - public DynamicKubernetesObject reconcile(Map model, - VmChannel channel) + public DynamicKubernetesObject reconcile(VmDefChanged event, + Map model, VmChannel channel) throws IOException, TemplateException, ApiException { // Get API DynamicKubernetesApi cmApi = new DynamicKubernetesApi("", "v1", diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java index 86e3751..66c11a7 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Controller.java @@ -181,12 +181,13 @@ public class Controller extends Component { @Handler public void onModifyVm(ModifyVm event, VmChannel channel) throws ApiException, IOException { - patchVmDef(channel.client(), event.name(), "spec/vm/" + event.path(), + patchVmSpec(channel.client(), event.name(), event.path(), event.value()); } - private void patchVmDef(K8sClient client, String name, String path, - Object value) throws ApiException, IOException { + private void patchVmSpec(K8sClient client, String name, String path, + Object value) + throws ApiException, IOException { var vmStub = K8sDynamicStub.get(client, new GroupVersionKind(VM_OP_GROUP, "", VM_OP_KIND_VM), namespace, name); @@ -196,7 +197,7 @@ public class Controller extends Component { ? "\"" + value + "\"" : value.toString(); var res = vmStub.patch(V1Patch.PATCH_FORMAT_JSON_PATCH, - new V1Patch("[{\"op\": \"replace\", \"path\": \"/" + new V1Patch("[{\"op\": \"replace\", \"path\": \"/spec/vm/" + path + "\", \"value\": " + valueAsText + "}]"), client.defaultPatchOptions()); if (!res.isPresent()) { diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/DisplaySecretReconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/DisplaySecretReconciler.java index 17456aa..14b8890 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/DisplaySecretReconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/DisplaySecretReconciler.java @@ -64,7 +64,7 @@ import org.jose4j.base64url.Base64; var display = GsonPtr.to(event.vmDefinition().data()).to("spec", "vm", "display"); if (!display.get(JsonPrimitive.class, "spice", "generateSecret") - .map(JsonPrimitive::getAsBoolean).orElse(true)) { + .map(JsonPrimitive::getAsBoolean).orElse(false)) { return; } diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java index 437790b..5bbfe38 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/Reconciler.java @@ -51,7 +51,6 @@ import org.jdrupes.vmoperator.common.K8sDynamicModel; import org.jdrupes.vmoperator.common.K8sObserver; import org.jdrupes.vmoperator.common.K8sV1SecretStub; import static org.jdrupes.vmoperator.manager.Constants.COMP_DISPLAY_SECRET; -import org.jdrupes.vmoperator.manager.events.ResetVm; import org.jdrupes.vmoperator.manager.events.VmChannel; import org.jdrupes.vmoperator.manager.events.VmDefChanged; import org.jdrupes.vmoperator.util.ExtendedObjectWrapper; @@ -210,35 +209,13 @@ public class Reconciler extends Component { // Reconcile, use "augmented" vm definition for model Map model = prepareModel(channel.client(), patchCr(event.vmDefinition())); - var configMap = cmReconciler.reconcile(model, channel); + var configMap = cmReconciler.reconcile(event, model, channel); model.put("cm", configMap.getRaw()); dsReconciler.reconcile(event, model, channel); stsReconciler.reconcile(event, model, channel); lbReconciler.reconcile(event, model, channel); } - /** - * Reset the VM by incrementing the reset count and doing a - * partial reconcile (configmap only). - * - * @param event the event - * @param channel the channel - * @throws IOException - * @throws ApiException - * @throws TemplateException - */ - @Handler - public void onResetVm(ResetVm event, VmChannel channel) - throws ApiException, IOException, TemplateException { - var defRoot - = GsonPtr.to(channel.vmDefinition().data()).get(JsonObject.class); - defRoot.addProperty("resetCount", - defRoot.get("resetCount").getAsLong() + 1); - Map model - = prepareModel(channel.client(), patchCr(channel.vmDefinition())); - cmReconciler.reconcile(model, channel); - } - private DynamicKubernetesObject patchCr(K8sDynamicModel vmDef) { var json = vmDef.data().deepCopy(); // Adjust cdromImage path diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmMonitor.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmMonitor.java index e049b17..41f08ce 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmMonitor.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/VmMonitor.java @@ -25,13 +25,13 @@ import io.kubernetes.client.openapi.models.V1ObjectMeta; import io.kubernetes.client.util.Watch; import io.kubernetes.client.util.generic.options.ListOptions; import java.io.IOException; -import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.stream.Collectors; 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.K8sDynamicStub; import org.jdrupes.vmoperator.common.K8sObserver.ResponseType; import org.jdrupes.vmoperator.common.K8sV1ConfigMapStub; @@ -121,7 +121,7 @@ public class VmMonitor extends } if (vmDef.data() != null) { // New data, augment and save - addDynamicData(channel.client(), vmDef, channel.vmDefinition()); + addDynamicData(channel.client(), vmDef); channel.setVmDefinition(vmDef); } else { // Reuse cached @@ -151,16 +151,8 @@ public class VmMonitor extends } } - private void addDynamicData(K8sClient client, VmDefinitionModel vmState, - VmDefinitionModel prevState) { + private void addDynamicData(K8sClient client, K8sDynamicModel vmState) { var rootNode = GsonPtr.to(vmState.data()).get(JsonObject.class); - - // Maintain (or initialize) the resetCount - rootNode.addProperty("resetCount", Optional.ofNullable(prevState) - .map(ps -> GsonPtr.to(ps.data())) - .flatMap(d -> d.getAsLong("resetCount")).orElse(0L)); - - // Add defaults in case the VM is not running rootNode.addProperty("nodeName", ""); rootNode.addProperty("nodeAddress", ""); diff --git a/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml b/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml index e23a2ec..c365a12 100644 --- a/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml +++ b/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml @@ -45,12 +45,6 @@ # property in the CRD. # "guestShutdownStops": # false - - # When incremented, the VM is reset. The value has no default value, - # i.e. if you start the VM without a value for this property, and - # decide to trigger a reset later, you have to first set the value - # and then inrement it. - # "resetCounter": 1 # Define the VM (required) "vm": diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java index 4e89944..d6d5219 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Configuration.java @@ -82,9 +82,6 @@ public class Configuration implements Dto { /** If guest shutdown changes CRD .vm.state to "Stopped". */ public boolean guestShutdownStops; - /** Increments of the reset counter trigger a reset of the VM. */ - public Integer resetCounter; - /** The vm. */ @SuppressWarnings("PMD.ShortVariable") public Vm vm; diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/DisplayController.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/DisplayController.java index dc73cb2..304ea04 100644 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/DisplayController.java +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/DisplayController.java @@ -116,9 +116,8 @@ public class DisplayController extends Component { } if (Objects.equals(this.currentPassword, password)) { - return true; + return false; } - this.currentPassword = password; logger.fine(() -> "Updating display password"); fire(new MonitorCommand(new QmpSetDisplayPassword(protocol, password))); return true; 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 4d9f479..e0baa4f 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 @@ -55,7 +55,6 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import static org.jdrupes.vmoperator.common.Constants.APP_NAME; import org.jdrupes.vmoperator.runner.qemu.commands.QmpCont; -import org.jdrupes.vmoperator.runner.qemu.commands.QmpReset; import org.jdrupes.vmoperator.runner.qemu.events.ConfigureQemu; import org.jdrupes.vmoperator.runner.qemu.events.Exit; import org.jdrupes.vmoperator.runner.qemu.events.MonitorCommand; @@ -216,7 +215,6 @@ public class Runner extends Component { private CommandDefinition cloudInitImgDefinition; private CommandDefinition qemuDefinition; private final QemuMonitor qemuMonitor; - private Integer resetCounter; private State state = State.INITIALIZING; /** Preparatory actions for QEMU start */ @@ -617,7 +615,7 @@ public class Runner extends Component { * @param event the event */ @Handler(priority = -1000) - public void onConfigureQemuFinal(ConfigureQemu event) { + public void onConfigureQemu(ConfigureQemu event) { if (state == State.STARTING) { fire(new MonitorCommand(new QmpCont())); state = State.RUNNING; @@ -626,23 +624,6 @@ public class Runner extends Component { } } - /** - * On configure qemu. - * - * @param event the event - */ - @Handler - public void onConfigureQemu(ConfigureQemu event) { - if (state == State.RUNNING) { - if (resetCounter != null - && event.configuration().resetCounter != null - && event.configuration().resetCounter > resetCounter) { - fire(new MonitorCommand(new QmpReset())); - } - resetCounter = event.configuration().resetCounter; - } - } - /** * On process exited. * diff --git a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpReset.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpReset.java deleted file mode 100644 index 0bcffc4..0000000 --- a/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpReset.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 . - */ - -package org.jdrupes.vmoperator.runner.qemu.commands; - -import com.fasterxml.jackson.databind.JsonNode; - -/** - * A {@link QmpCommand} that send a system_reset to the VM. - */ -public class QmpReset extends QmpCommand { - - @SuppressWarnings({ "PMD.FieldNamingConventions", - "PMD.VariableNamingConventions" }) - private static final JsonNode jsonTemplate - = parseJson("{ \"execute\": \"system_reset\" }"); - - @Override - public JsonNode toJson() { - return jsonTemplate.deepCopy(); - } - - @Override - public String toString() { - return "QmpReset()"; - } - -} diff --git a/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/GsonPtr.java b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/GsonPtr.java index 8b84ed3..e3d9fcd 100644 --- a/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/GsonPtr.java +++ b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/GsonPtr.java @@ -265,18 +265,6 @@ public class GsonPtr { return set(selector, new JsonPrimitive(value)); } - /** - * Short for `set(selector, new JsonPrimitive(value))`. - * - * @param selector the selector - * @param value the value - * @return the gson ptr - * @see #set(Object, JsonElement) - */ - public GsonPtr set(Object selector, Long value) { - return set(selector, new JsonPrimitive(value)); - } - /** * Short for `set(selector, new JsonPrimitive(value))`. * diff --git a/org.jdrupes.vmoperator.vmconlet/resources/org/jdrupes/vmoperator/vmconlet/VmConlet-view.ftl.html b/org.jdrupes.vmoperator.vmconlet/resources/org/jdrupes/vmoperator/vmconlet/VmConlet-view.ftl.html index 708a1a3..913f45d 100644 --- a/org.jdrupes.vmoperator.vmconlet/resources/org/jdrupes/vmoperator/vmconlet/VmConlet-view.ftl.html +++ b/org.jdrupes.vmoperator.vmconlet/resources/org/jdrupes/vmoperator/vmconlet/VmConlet-view.ftl.html @@ -52,14 +52,12 @@ v-html="controller.breakBeforeDots(entry[key])"> - - -

${_("confirmResetMsg")}

-

- - - - - - -

- \ No newline at end of file diff --git a/org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-edit.ftl.html b/org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-edit.ftl.html index e86d9db..d4e86ca 100644 --- a/org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-edit.ftl.html +++ b/org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-edit.ftl.html @@ -1,8 +1,7 @@ -
+
{{ localize("Select VM") }} diff --git a/org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-preview.ftl.html b/org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-preview.ftl.html index c034504..1cd0392 100644 --- a/org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-preview.ftl.html +++ b/org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-preview.ftl.html @@ -1,5 +1,4 @@ -
- - - - - - diff --git a/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/VmViewer.java b/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/VmViewer.java index fe48d3b..c06cc1a 100644 --- a/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/VmViewer.java +++ b/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/VmViewer.java @@ -20,7 +20,6 @@ package org.jdrupes.vmoperator.vmviewer; import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.google.gson.JsonObject; @@ -55,7 +54,6 @@ import org.jdrupes.vmoperator.common.VmDefinitionModel.Permission; import org.jdrupes.vmoperator.manager.events.ChannelCache; import org.jdrupes.vmoperator.manager.events.GetDisplayPassword; import org.jdrupes.vmoperator.manager.events.ModifyVm; -import org.jdrupes.vmoperator.manager.events.ResetVm; import org.jdrupes.vmoperator.manager.events.VmChannel; import org.jdrupes.vmoperator.manager.events.VmDefChanged; import org.jdrupes.vmoperator.util.GsonPtr; @@ -92,10 +90,10 @@ import org.jgrapes.webconsole.base.events.UpdateConletType; import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet; /** - * The Class VmViewer. + * The Class VmConlet. */ @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.ExcessiveImports", - "PMD.CouplingBetweenObjects", "PMD.GodClass", "PMD.TooManyMethods" }) + "PMD.CouplingBetweenObjects", "PMD.GodClass" }) public class VmViewer extends FreeMarkerConlet { private static final String VM_NAME_PROPERTY = "vmName"; @@ -467,19 +465,12 @@ public class VmViewer extends FreeMarkerConlet { @Override @SuppressWarnings({ "PMD.AvoidDecimalLiteralsInBigDecimalConstructor", - "PMD.ConfusingArgumentToVarargsMethod", "PMD.NcssCount", - "PMD.AvoidLiteralsInIfCondition" }) + "PMD.ConfusingArgumentToVarargsMethod" }) protected void doUpdateConletState(NotifyConletModel event, ConsoleConnection channel, ViewerModel model) throws Exception { event.stop(); - if ("selectedVm".equals(event.method())) { - selectVm(event, channel, model); - return; - } - - // Handle command for selected VM - var both = Optional.ofNullable(model.vmName()) + var both = Optional.ofNullable(event.params().asString(0)) .flatMap(vm -> channelManager.both(vm)); if (both.isEmpty()) { return; @@ -488,8 +479,14 @@ public class VmViewer extends FreeMarkerConlet { var vmDef = both.get().associated; var vmName = vmDef.metadata().getName(); var perms = permissions(vmDef, channel.session()); - var resourceBundle = resourceBundle(channel.locale()); switch (event.method()) { + case "selectedVm": + model.setVmName(event.params().asString(0)); + String jsonState = objectMapper.writeValueAsString(model); + channel.respond(new KeyValueStoreUpdate().update(storagePath( + channel.session(), model.getConletId()), jsonState)); + updateConfig(channel, model); + break; case "start": if (perms.contains(Permission.START)) { fire(new ModifyVm(vmName, "state", "Running", vmChannel)); @@ -500,16 +497,6 @@ public class VmViewer extends FreeMarkerConlet { fire(new ModifyVm(vmName, "state", "Stopped", vmChannel)); } break; - case "reset": - if (perms.contains(Permission.RESET)) { - confirmReset(event, channel, model, resourceBundle); - } - break; - case "resetConfirmed": - if (perms.contains(Permission.RESET)) { - fire(new ResetVm(vmName), vmChannel); - } - break; case "openConsole": if (perms.contains(Permission.ACCESS_CONSOLE)) { var pwQuery = Event.onCompletion(new GetDisplayPassword(vmDef), @@ -523,15 +510,6 @@ public class VmViewer extends FreeMarkerConlet { } } - private void selectVm(NotifyConletModel event, ConsoleConnection channel, - ViewerModel model) throws JsonProcessingException { - model.setVmName(event.params().asString(0)); - String jsonState = objectMapper.writeValueAsString(model); - channel.respond(new KeyValueStoreUpdate().update(storagePath( - channel.session(), model.getConletId()), jsonState)); - updateConfig(channel, model); - } - private void openConsole(String vmName, ConsoleConnection connection, ViewerModel model, String password) { var vmDef = channelManager.associated(vmName).orElse(null); @@ -599,20 +577,6 @@ public class VmViewer extends FreeMarkerConlet { .findFirst().or(() -> addrs.stream().findFirst()); } - private void confirmReset(NotifyConletModel event, - ConsoleConnection channel, ViewerModel model, - ResourceBundle resourceBundle) throws TemplateNotFoundException, - MalformedTemplateNameException, ParseException, IOException { - Template tpl = freemarkerConfig() - .getTemplate("VmViewer-confirmReset.ftl.html"); - channel.respond(new OpenModalDialog(type(), model.getConletId(), - processTemplate(event, tpl, - fmModel(event, channel, model.getConletId(), model))) - .addOption("cancelable", true).addOption("closeLabel", "") - .addOption("title", - resourceBundle.getString("confirmResetTitle"))); - } - @Override protected boolean doSetLocale(SetLocale event, ConsoleConnection channel, String conletId) throws Exception { diff --git a/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/browser/VmViewer-functions.ts b/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/browser/VmViewer-functions.ts index a14e83c..ca3b246 100644 --- a/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/browser/VmViewer-functions.ts +++ b/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/browser/VmViewer-functions.ts @@ -31,9 +31,8 @@ declare global { interface Window { orgJDrupesVmOperatorVmViewer: { initPreview?: (previewDom: HTMLElement, isUpdate: boolean) => void, - initEdit?: (viewDom: HTMLElement, isUpdate: boolean) => void, - applyEdit?: (viewDom: HTMLElement, apply: boolean) => void, - confirmReset?: (conletType: string, conletId: string) => void + initEdit?: (viewDom: HTMLElement, isUpdate: boolean) => void + applyEdit?: (viewDom: HTMLElement, apply: boolean) => void } } } @@ -64,16 +63,7 @@ window.orgJDrupesVmOperatorVmViewer.initPreview = (previewDom: HTMLElement, vmName: "", vmDefinition: {} }); - const configured = computed(() => previewApi.vmDefinition.spec); - const startable = computed(() => previewApi.vmDefinition.spec && - previewApi.vmDefinition.spec.vm.state !== 'Running' - && !previewApi.vmDefinition.running); - const stoppable = computed(() => previewApi.vmDefinition.spec && - previewApi.vmDefinition.spec.vm.state !== 'Stopped' - && previewApi.vmDefinition.running); - const running = computed(() => previewApi.vmDefinition.running); - const permissions = computed(() => previewApi.vmDefinition.spec - ? previewApi.vmDefinition.userPermissions : []); + const vmDef = computed(() => previewApi.vmDefinition); watch(() => previewApi.vmName, (name: string) => { if (name !== "") { @@ -83,50 +73,40 @@ window.orgJDrupesVmOperatorVmViewer.initPreview = (previewDom: HTMLElement, provideApi(previewDom, previewApi); - const vmAction = (action: string) => { - JGConsole.notifyConletModel(conletId, action); + const vmAction = (vmName: string, action: string) => { + JGConsole.notifyConletModel(conletId, action, vmName); }; - return { localize, resourceBase, vmAction, configured, - startable, stoppable, running, permissions }; + return { localize, resourceBase, vmDef, vmAction }; }, template: ` - - + + - - -
- + - + + - - - - - - + v-on:click="vmAction(vmDef.name, 'stop')"> + +
` @@ -229,9 +209,3 @@ window.orgJDrupesVmOperatorVmViewer.applyEdit = const vmName = getApi>(dialogDom!)!.value; JGConsole.notifyConletModel(conletId, "selectedVm", vmName); } - -window.orgJDrupesVmOperatorVmViewer.confirmReset = - (conletType: string, conletId: string) => { - JGConsole.instance.closeModalDialog(conletType, conletId); - JGConsole.notifyConletModel(conletId, "resetConfirmed"); -} \ No newline at end of file diff --git a/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/browser/VmViewer-style.scss b/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/browser/VmViewer-style.scss index 6d0654f..8ef8b66 100644 --- a/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/browser/VmViewer-style.scss +++ b/org.jdrupes.vmoperator.vmviewer/src/org/jdrupes/vmoperator/vmviewer/browser/VmViewer-style.scss @@ -19,24 +19,7 @@ /* * Conlet specific styles. */ -.jdrupes-vmoperator-vmviewer { - - span[role="button"].svg-icon { - display: inline-block; - line-height: 1; - /* Align with forkawesome */ - font-size: 14px; - fill: var(--primary); - - &[aria-disabled="true"], &[aria-disabled=""] { - fill: var(--disabled); - } - - svg { - height: 2ex; - width: 1em; - } - } +.jdrupes-vmoperator-vmviewer-preview { [role=button] { padding: 0.25rem; @@ -45,10 +28,7 @@ box-shadow: var(--darkening); } } -} -.jdrupes-vmoperator-vmviewer.jdrupes-vmoperator-vmviewer-preview { - img { height: 3em; padding: 0.25rem; @@ -57,42 +37,14 @@ opacity: 0.4; } } - - .jdrupes-vmoperator-vmviewer-preview-action-list { - white-space: nowrap; - } - - span.busy::before { - font: normal normal normal 14px/1 ForkAwesome; - font-size: 1.125em; - content: "\f1ce"; - left: 1.45em; - top: 0.7em; - color: var(--info); - position: absolute; - animation: spin 2s linear infinite; - z-index: 100; - } } -.jdrupes-vmoperator-vmviewer.jdrupes-vmoperator-vmviewer-edit { +.jdrupes-vmoperator-vmviewer-preview-action-list { + white-space: nowrap; +} + +.jdrupes-vmoperator-vmviewer-edit { select { width: 15em; } -} - -.jdrupes-vmoperator-vmviewer.jdrupes-vmoperator-vmviewer-confirm-reset { - p { - text-align: center; - } - - span[role="button"].svg-icon { - fill: var(--danger); - - svg { - width: 2.5em; - height: 2.5em; - } - } - -} +} \ No newline at end of file