From a20602f67191a5cbd540015e555572ed2e0fba4f Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 1 Jun 2024 11:54:23 +0200 Subject: [PATCH 01/24] Plain tags for development branches. --- .../src/org.jdrupes.vmoperator.versioning-conventions.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle b/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle index 114db51..8623857 100644 --- a/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle +++ b/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle @@ -21,7 +21,8 @@ scmVersion { } var p = shortened.replace('.', '-') + "-" if (grgit.branch.current.name != "main" - && !grgit.branch.current.name.startsWith("release")) { + && !grgit.branch.current.name.startsWith("release") + && !grgit.branch.current.name.startsWith("develop")) { p = p + grgit.branch.current.name.replace('/', '-') + "-" } prefix = p From 837ee043aaf9f813057946a0fb4ecea2027c5cbd Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 1 Jun 2024 12:28:28 +0200 Subject: [PATCH 02/24] Checkout triggering version (is default). --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e6b3c9..3c59936 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,6 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 - ref: main - name: Install graphviz run: sudo apt-get install graphviz - name: Install podman From 2cb8c90357b6a52218e5e4e6fce5fe7c3989406c Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 1 Jun 2024 13:26:47 +0200 Subject: [PATCH 03/24] Try to fix version issue when building release. --- .../org.jdrupes.vmoperator.java-common-conventions.gradle | 8 +++++--- .../org.jdrupes.vmoperator.versioning-conventions.gradle | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/org.jdrupes.vmoperator.java-common-conventions.gradle b/buildSrc/src/org.jdrupes.vmoperator.java-common-conventions.gradle index e09814c..5185041 100644 --- a/buildSrc/src/org.jdrupes.vmoperator.java-common-conventions.gradle +++ b/buildSrc/src/org.jdrupes.vmoperator.java-common-conventions.gradle @@ -5,6 +5,11 @@ */ plugins { + // Apply the common versioning conventions. + // Put this at the start, because accessing project.version before + // this is applied makes things fail. + id 'org.jdrupes.vmoperator.versioning-conventions' + // Apply the java Plugin to add support for Java. id 'java' @@ -13,9 +18,6 @@ plugins { // Access to git information id 'org.ajoberstar.grgit' - - // Apply the common versioning conventions. - id 'org.jdrupes.vmoperator.versioning-conventions' } repositories { diff --git a/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle b/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle index 8623857..8993929 100644 --- a/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle +++ b/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle @@ -28,5 +28,5 @@ scmVersion { prefix = p } } -version = scmVersion.version +project.version = scmVersion.version ext.isSnapshot = version.endsWith('-SNAPSHOT') From c66e304de444970f390841d2731760d72903bf8c Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 1 Jun 2024 13:44:53 +0200 Subject: [PATCH 04/24] Fetch all tags to make axion release plugin work properly. --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3c59936..19f5323 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,9 +18,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 + fetch-tags: true - name: Install graphviz run: sudo apt-get install graphviz - name: Install podman From 2403f5db645e9a63f4852fb149145edb78f9922c Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 1 Jun 2024 13:57:21 +0200 Subject: [PATCH 05/24] Undo previous commit (is implied by fetch-depth: 0). --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 19f5323..e99ee03 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,6 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - fetch-tags: true - name: Install graphviz run: sudo apt-get install graphviz - name: Install podman From 6c250d46561537be61c5a4e243e84e19525b833b Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 1 Jun 2024 14:28:57 +0200 Subject: [PATCH 06/24] Fix release versions. --- .../src/org.jdrupes.vmoperator.versioning-conventions.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle b/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle index 8993929..40ced48 100644 --- a/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle +++ b/buildSrc/src/org.jdrupes.vmoperator.versioning-conventions.gradle @@ -21,6 +21,7 @@ scmVersion { } var p = shortened.replace('.', '-') + "-" if (grgit.branch.current.name != "main" + && grgit.branch.current.name != "HEAD" && !grgit.branch.current.name.startsWith("release") && !grgit.branch.current.name.startsWith("develop")) { p = p + grgit.branch.current.name.replace('/', '-') + "-" From 4617d8d3ba6ac5f4145f87ad7f73457ee4768885 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sat, 1 Jun 2024 14:59:08 +0200 Subject: [PATCH 07/24] Make byte[] to String conversion null-safe. --- .../org/jdrupes/vmoperator/manager/DisplaySecretMonitor.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/DisplaySecretMonitor.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/DisplaySecretMonitor.java index 8bc1db0..1ea766c 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/DisplaySecretMonitor.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/DisplaySecretMonitor.java @@ -33,6 +33,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Scanner; import java.util.logging.Level; import static org.jdrupes.vmoperator.common.Constants.APP_NAME; @@ -180,7 +181,8 @@ public class DisplaySecretMonitor // Check validity var model = stub.model().get(); @SuppressWarnings("PMD.StringInstantiation") - var expiry = new String(model.getData().get(DATA_PASSWORD_EXPIRY)); + var expiry = Optional.ofNullable(model.getData() + .get(DATA_PASSWORD_EXPIRY)).map(b -> new String(b)).orElse(null); if (model.getData().get(DATA_DISPLAY_PASSWORD) != null && stillValid(expiry)) { event.setResult( From a4bd129252a49397946d2603962edb1e74890a83 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Mon, 3 Jun 2024 23:10:35 +0200 Subject: [PATCH 08/24] Minor updates. --- misc/stylesheet.css | 54 ++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/misc/stylesheet.css b/misc/stylesheet.css index e21b9b2..2debce1 100644 --- a/misc/stylesheet.css +++ b/misc/stylesheet.css @@ -71,37 +71,31 @@ a[name] { color:#353833; } pre { - font-family: "DejaVu Sans Mono", monospace; + font-family:'DejaVu Sans Mono', monospace; } h1 { - font-family: "DejaVu Sans", sans; font-size:20px; } h2 { - font-family: "DejaVu Sans", sans; font-size:18px; } h3 { - font-family: "DejaVu Sans", sans; font-size:16px; } h4 { - font-family: "DejaVu Sans", sans; font-size:15px; } h5 { - font-family: "DejaVu Sans", sans; font-size:14px; } h6 { - font-family: "DejaVu Sans", sans; font-size:13px; } ul { list-style-type:disc; } code, tt { - font-family: "DejaVu Sans Mono", monospace; + font-family:'DejaVu Sans Mono', monospace; } :not(h1, h2, h3, h4, h5, h6) > code, :not(h1, h2, h3, h4, h5, h6) > tt { @@ -111,12 +105,12 @@ code, tt { line-height:1.4em; } dt code { - font-family: "DejaVu Sans Mono", monospace; + font-family:'DejaVu Sans Mono', monospace; font-size:14px; padding-top:4px; } .summary-table dt code { - font-family: "DejaVu Sans Mono", monospace; + font-family:'DejaVu Sans Mono', monospace; font-size:14px; vertical-align:top; padding-top:4px; @@ -124,7 +118,9 @@ dt code { sup { font-size:8px; } - +button { + font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif; +} /* * Styles for HTML generated by javadoc. * @@ -185,7 +181,6 @@ sup { min-height:2.8em; padding-top:10px; overflow:hidden; - font-family: "DejaVu Sans", sans; font-size:80%; } .sub-nav { @@ -193,7 +188,6 @@ sup { float:left; width:100%; overflow:hidden; - font-family: "DejaVu Sans", sans; font-size:80%; } .sub-nav div { @@ -311,13 +305,16 @@ main { position:relative; } dl.notes > dt { - font-family: "DejaVu Sans", sans; + font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif; + /* font-size:12px; */ font-weight:bold; margin:10px 0 0 0; color:#4E4E4E; } dl.notes > dd { - margin:5px 10px 10px 0; + margin:5px 10px 0 0; + /* font-size:14px; */ + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; } dl.name-value > dt { margin-left:1px; @@ -389,6 +386,11 @@ ul.see-list-long li:not(:last-child):after { border-bottom:1px solid #EEE; padding:0; } + +.summary-table .col-first { + font-family: "DejaVu Sans Mono", monospace; +} + .caption { position:relative; text-align:left; @@ -402,7 +404,6 @@ ul.see-list-long li:not(:last-child):after { padding-left:1px; margin:0; white-space:pre; - font-family: 'DejaVu Sans'; } .caption a:link, .caption a:visited { color:#1f389c; @@ -450,9 +451,6 @@ div.table-tabs > button.table-tab { display: grid; grid-template-columns: minmax(10%, max-content) minmax(15%, max-content) minmax(15%, auto); } -#method-summary-table .three-column-summary { - grid-template-columns: minmax(10%, 20%) minmax(15%, max-content) minmax(15%, auto); -} .four-column-summary { display: grid; grid-template-columns: minmax(10%, max-content) minmax(10%, max-content) minmax(10%, max-content) minmax(10%, auto); @@ -490,7 +488,6 @@ div.table-tabs > button.table-tab { } .table-header { background:#dee3e9; - font-family: 'DejaVu Sans'; font-weight: bold; } /* @@ -508,7 +505,6 @@ div.table-tabs > button.table-tab { .col-last { white-space:normal; } -/* .col-first a:link, .col-first a:visited, .col-second a:link, .col-second a:visited, .col-first a:link, .col-first a:visited, @@ -520,7 +516,6 @@ div.table-tabs > button.table-tab { .all-packages-container a:link, .all-packages-container a:visited { font-weight:bold; } -*/ .table-sub-heading-color { background-color:#EEEEFF; } @@ -537,12 +532,9 @@ div.table-tabs > button.table-tab { margin:0; padding:10px 0; } -/* div.block { - font-size:14px; font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; } -*/ .col-last div { padding-top:0; } @@ -553,8 +545,7 @@ div.block { .package-signature, .type-signature, .member-signature { - font-family: "DejaVu Sans Mono", monospace; - /* font-size:14px; */ + font-family:'DejaVu Sans Mono', monospace; margin:14px 0; white-space: pre-wrap; } @@ -593,13 +584,8 @@ h1.hidden { .deprecated-label, .descfrm-type-label, .implementation-label, .member-name-label, .member-name-link, .module-label-in-package, .module-label-in-type, .override-specify-label, .package-label-in-type, .package-hierarchy-label, .type-name-label, .type-name-link, .search-tag-link, .preview-label { - font-family: "DejaVu Sans", sans; font-weight:bold; } -.sub-title, .inheritance, .all-packages-table-tab1.col-first, - .summary-table .col-first { - font-family: "DejaVu Sans", sans; -} .deprecation-comment, .help-footnote, .preview-comment { font-style:italic; } @@ -658,6 +644,7 @@ main, nav, header, footer, section { ul.ui-autocomplete { position:fixed; z-index:999999; + background-color: #FFFFFF; } ul.ui-autocomplete li { float:left; @@ -667,6 +654,9 @@ ul.ui-autocomplete li { .result-highlight { font-weight:bold; } +.ui-autocomplete .result-item { + font-size: inherit; +} #search-input { background-image:url('resources/glass.png'); background-size:13px; From fefd2737f35a37186d087ba130be47728b919b2a Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Mon, 3 Jun 2024 23:12:22 +0200 Subject: [PATCH 09/24] Widen selection box. --- .../jdrupes/vmoperator/vmviewer/browser/VmViewer-style.scss | 5 +++++ 1 file changed, 5 insertions(+) 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 83e92a5..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 @@ -43,3 +43,8 @@ white-space: nowrap; } +.jdrupes-vmoperator-vmviewer-edit { + select { + width: 15em; + } +} \ No newline at end of file From ebead171933c870b6b8e8f96e19e7e12e66c97ae Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Tue, 4 Jun 2024 09:43:27 +0200 Subject: [PATCH 10/24] Minor update. --- misc/stylesheet.css | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/misc/stylesheet.css b/misc/stylesheet.css index 2debce1..625b5ae 100644 --- a/misc/stylesheet.css +++ b/misc/stylesheet.css @@ -45,7 +45,7 @@ body { background-color:#ffffff; color:#353833; - font: normal 16px/1.5 "DejaVu Serif", serif; + font: normal 16px/1.5 "DejaVu Sans", Arial, Helvetica, sans-serif; margin:0; padding:0; height:100%; @@ -80,10 +80,12 @@ h2 { font-size:18px; } h3 { - font-size:16px; + font-size:17px; } h4 { - font-size:15px; + font-size:16px; + margin-top: 1rem; + margin-bottom: 1rem; } h5 { font-size:14px; From 85d9278e2e5d6159fdb81cc0f41a90b09753053f Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Tue, 4 Jun 2024 11:20:12 +0200 Subject: [PATCH 11/24] Update diagrams. --- .../vmoperator/manager/package-info.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java index 54d4efe..cfec873 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java @@ -119,6 +119,7 @@ * [WebConsole] *-- [RoleConfigurator] * [WebConsole] *-- [RoleConletFilter] * [WebConsole] *-left- [LoginConlet] + * [WebConsole] *-right- [OidcClient] * * Component "ComponentCollector\nfor page resources" as cpr <> * [WebConsole] *-- [cpr] @@ -153,15 +154,26 @@ * () "guiHttp" as http * http .up. [GuiHttpServer] * - * [PreferencesStore] .right. http + * [PreferencesStore] .. http + * [OidcClient] .up. http + * [LanguageSelector] .left. http * [InMemorySessionManager] .up. http - * [LanguageSelector] .up. http * * package "Conceptual WebConsole" { - * [ConsoleWeblet] .left. http + * [ConsoleWeblet] .right. http * [ConsoleWeblet] *-down- [WebConsole] * } * + * [Controller] .down[hidden]. [ConsoleWeblet] + * + * () "console" as console + * console .. WebConsole + * + * [OidcClient] .. console + * [LoginConlet] .right. console + * + * note right of console: More conlets connect here + * * @enduml */ package org.jdrupes.vmoperator.manager; From bab0db48d1741e849a652dbb2f5bc69b253e5788 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Tue, 4 Jun 2024 11:25:21 +0200 Subject: [PATCH 12/24] Fix typo. --- .../src/org/jdrupes/vmoperator/manager/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java index cfec873..54e9cd1 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java @@ -57,7 +57,7 @@ * ``` * * Developers may also be interested in the usage of channels - * by the application's component: + * by the application's components: * * ![Main channels](app-channels.svg) * From 08f4a818da2d3b2789bad1460f9f3db2b1628bae Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Tue, 4 Jun 2024 21:57:15 +0200 Subject: [PATCH 13/24] Add HTTPConnector to diagrams. --- .../src/org/jdrupes/vmoperator/manager/package-info.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java index 54e9cd1..fc75b24 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java @@ -74,6 +74,8 @@ * * Component NioDispatcher as NioDispatcher <> * [Manager] *-up- [NioDispatcher] + * Component HttpConnector as HttpConnector <> + * [Manager] *-up- [HttpConnector] * Component FileSystemWatcher as FileSystemWatcher <> * [Manager] *-up- [FileSystemWatcher] * Component YamlConfigurationStore as YamlConfigurationStore <> @@ -148,11 +150,13 @@ * () "guiTransport" as hT * hT .up. [GuiSocketServer:8080] * hT .down. [GuiHttpServer] + * hT .right[hidden]. [HttpConnector] * * [YamlConfigurationStore] -right[hidden]- hT * * () "guiHttp" as http * http .up. [GuiHttpServer] + * http .up. [HttpConnector] * * [PreferencesStore] .. http * [OidcClient] .up. http From 9180323618cb65e657689bf7dd98cb7a0d5a1aeb Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Tue, 4 Jun 2024 22:09:54 +0200 Subject: [PATCH 14/24] Update. --- .../src/org/jdrupes/vmoperator/manager/package-info.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java index fc75b24..337b5e3 100644 --- a/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java +++ b/org.jdrupes.vmoperator.manager/src/org/jdrupes/vmoperator/manager/package-info.java @@ -157,6 +157,7 @@ * () "guiHttp" as http * http .up. [GuiHttpServer] * http .up. [HttpConnector] + * note top of [HttpConnector]: transport layer com-\nponents omitted * * [PreferencesStore] .. http * [OidcClient] .up. http @@ -176,7 +177,7 @@ * [OidcClient] .. console * [LoginConlet] .right. console * - * note right of console: More conlets connect here + * note right of console: More conlets\nconnect here * * @enduml */ From d6a0cc622000fe4194124c2b9043108e296f2760 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Wed, 5 Jun 2024 21:10:05 +0200 Subject: [PATCH 15/24] Improve state feedback. --- .../org/jdrupes/vmoperator/vmconlet/VmConlet-view.ftl.html | 6 ++++-- .../vmoperator/vmviewer/browser/VmViewer-functions.ts | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) 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 913f45d..708a1a3 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,12 +52,14 @@ v-html="controller.breakBeforeDots(entry[key])"> - - - - From b0b6d6723ed13b8c724cc0c84f764ca7b4a4853f Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 6 Jun 2024 12:03:00 +0200 Subject: [PATCH 16/24] Fix typo. --- .../src/org/jdrupes/vmoperator/vmviewer/VmViewer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c06cc1a..ab7238a 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 @@ -90,7 +90,7 @@ import org.jgrapes.webconsole.base.events.UpdateConletType; import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet; /** - * The Class VmConlet. + * The Class VmViewer. */ @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.ExcessiveImports", "PMD.CouplingBetweenObjects", "PMD.GodClass" }) From f20e61d57c969192e2ddd387b351afe27c2be364 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 6 Jun 2024 12:36:29 +0200 Subject: [PATCH 17/24] Default to generating secrets. --- .../org/jdrupes/vmoperator/manager/DisplaySecretReconciler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 14b8890..17456aa 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(false)) { + .map(JsonPrimitive::getAsBoolean).orElse(true)) { return; } From 92c9c6df9ca06f3cc01827ff2ecfdcc50565660a Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 6 Jun 2024 15:45:02 +0200 Subject: [PATCH 18/24] Implement reset. --- .../config-sample.yaml | 6 +++ .../vmoperator/runner/qemu/Configuration.java | 3 ++ .../vmoperator/runner/qemu/Runner.java | 21 ++++++++- .../runner/qemu/commands/QmpReset.java | 43 +++++++++++++++++++ 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpReset.java diff --git a/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml b/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml index c365a12..e23a2ec 100644 --- a/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml +++ b/org.jdrupes.vmoperator.runner.qemu/config-sample.yaml @@ -45,6 +45,12 @@ # 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 d6d5219..4e89944 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,6 +82,9 @@ 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/Runner.java b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/Runner.java index e0baa4f..4d9f479 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,6 +55,7 @@ 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; @@ -215,6 +216,7 @@ 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 */ @@ -615,7 +617,7 @@ public class Runner extends Component { * @param event the event */ @Handler(priority = -1000) - public void onConfigureQemu(ConfigureQemu event) { + public void onConfigureQemuFinal(ConfigureQemu event) { if (state == State.STARTING) { fire(new MonitorCommand(new QmpCont())); state = State.RUNNING; @@ -624,6 +626,23 @@ 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 new file mode 100644 index 0000000..0bcffc4 --- /dev/null +++ b/org.jdrupes.vmoperator.runner.qemu/src/org/jdrupes/vmoperator/runner/qemu/commands/QmpReset.java @@ -0,0 +1,43 @@ +/* + * 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()"; + } + +} From 0a1f89a27048bf8220c0e6c3d704b4493b634fa5 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Thu, 6 Jun 2024 15:58:22 +0200 Subject: [PATCH 19/24] Fix avoiding unnecessary password changes. --- .../org/jdrupes/vmoperator/runner/qemu/DisplayController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 304ea04..dc73cb2 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,8 +116,9 @@ public class DisplayController extends Component { } if (Objects.equals(this.currentPassword, password)) { - return false; + return true; } + this.currentPassword = password; logger.fine(() -> "Updating display password"); fire(new MonitorCommand(new QmpSetDisplayPassword(protocol, password))); return true; From 6a3f6c5e3ea4d17559bcf9010a53e10409522d0c Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sun, 9 Jun 2024 12:48:20 +0200 Subject: [PATCH 20/24] Add reset support. --- .../vmoperator/common/VmDefinitionModel.java | 3 +- .../vmoperator/manager/events/ResetVm.java | 48 ++++++++++++++++++ org.jdrupes.vmoperator.manager/build.gradle | 4 +- .../vmoperator/manager/runnerConfig.ftl.yaml | 6 +++ .../manager/ConfigMapReconciler.java | 6 +-- .../vmoperator/manager/Controller.java | 9 ++-- .../vmoperator/manager/Reconciler.java | 25 +++++++++- .../jdrupes/vmoperator/manager/VmMonitor.java | 14 ++++-- .../org/jdrupes/vmoperator/util/GsonPtr.java | 12 +++++ org.jdrupes.vmoperator.vmviewer/build.gradle | 2 +- .../vmviewer/VmViewer-confirmReset.ftl.html | 13 +++++ .../vmviewer/VmViewer-edit.ftl.html | 9 ++-- .../vmviewer/VmViewer-preview.ftl.html | 3 +- .../vmoperator/vmviewer/l10n.properties | 4 ++ .../vmoperator/vmviewer/l10n_de.properties | 5 ++ .../vmoperator/vmviewer/reset-icon.svg | 42 ++++++++++++++++ .../jdrupes/vmoperator/vmviewer/VmViewer.java | 32 ++++++++++-- .../vmviewer/browser/VmViewer-functions.ts | 24 +++++++-- .../vmviewer/browser/VmViewer-style.scss | 50 ++++++++++++++++--- 19 files changed, 275 insertions(+), 36 deletions(-) create mode 100644 org.jdrupes.vmoperator.manager.events/src/org/jdrupes/vmoperator/manager/events/ResetVm.java create mode 100644 org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-confirmReset.ftl.html create mode 100644 org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/reset-icon.svg 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 fa59c82..5e1ebb0 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,7 +41,8 @@ public class VmDefinitionModel extends K8sDynamicModel { * Permissions for accessing and manipulating the VM. */ public enum Permission { - START("start"), STOP("stop"), ACCESS_CONSOLE("accessConsole"); + START("start"), STOP("stop"), RESET("reset"), + 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 new file mode 100644 index 0000000..f3320c8 --- /dev/null +++ b/org.jdrupes.vmoperator.manager.events/src/org/jdrupes/vmoperator/manager/events/ResetVm.java @@ -0,0 +1,48 @@ +/* + * 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 a8b67a0..1887108 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.5.0,2)' + implementation 'org.jgrapes:org.jgrapes.webconsole.base:[1.7.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.3.0,2)' + implementation 'org.jgrapes:org.jgrapes.webconlet.oidclogin:[1.4.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/runnerConfig.ftl.yaml b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/runnerConfig.ftl.yaml index 7679a68..253f9b7 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,6 +48,12 @@ 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 a882a79..4219e53 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,7 +36,6 @@ 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; @@ -62,7 +61,6 @@ import org.yaml.snakeyaml.constructor.SafeConstructor; /** * Reconcile. * - * @param event the event * @param model the model * @param channel the channel * @return the dynamic kubernetes object @@ -70,8 +68,8 @@ import org.yaml.snakeyaml.constructor.SafeConstructor; * @throws TemplateException the template exception * @throws ApiException the api exception */ - public DynamicKubernetesObject reconcile(VmDefChanged event, - Map model, VmChannel channel) + public DynamicKubernetesObject reconcile(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 66c11a7..86e3751 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,13 +181,12 @@ public class Controller extends Component { @Handler public void onModifyVm(ModifyVm event, VmChannel channel) throws ApiException, IOException { - patchVmSpec(channel.client(), event.name(), event.path(), + patchVmDef(channel.client(), event.name(), "spec/vm/" + event.path(), event.value()); } - private void patchVmSpec(K8sClient client, String name, String path, - Object value) - throws ApiException, IOException { + private void patchVmDef(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); @@ -197,7 +196,7 @@ public class Controller extends Component { ? "\"" + value + "\"" : value.toString(); var res = vmStub.patch(V1Patch.PATCH_FORMAT_JSON_PATCH, - new V1Patch("[{\"op\": \"replace\", \"path\": \"/spec/vm/" + new V1Patch("[{\"op\": \"replace\", \"path\": \"/" + path + "\", \"value\": " + valueAsText + "}]"), client.defaultPatchOptions()); if (!res.isPresent()) { 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 5bbfe38..437790b 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,6 +51,7 @@ 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; @@ -209,13 +210,35 @@ public class Reconciler extends Component { // Reconcile, use "augmented" vm definition for model Map model = prepareModel(channel.client(), patchCr(event.vmDefinition())); - var configMap = cmReconciler.reconcile(event, model, channel); + var configMap = cmReconciler.reconcile(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 41f08ce..e049b17 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); + addDynamicData(channel.client(), vmDef, channel.vmDefinition()); channel.setVmDefinition(vmDef); } else { // Reuse cached @@ -151,8 +151,16 @@ public class VmMonitor extends } } - private void addDynamicData(K8sClient client, K8sDynamicModel vmState) { + private void addDynamicData(K8sClient client, VmDefinitionModel vmState, + VmDefinitionModel prevState) { 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.util/src/org/jdrupes/vmoperator/util/GsonPtr.java b/org.jdrupes.vmoperator.util/src/org/jdrupes/vmoperator/util/GsonPtr.java index e3d9fcd..8b84ed3 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,6 +265,18 @@ 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.vmviewer/build.gradle b/org.jdrupes.vmoperator.vmviewer/build.gradle index ab667f5..aca015b 100644 --- a/org.jdrupes.vmoperator.vmviewer/build.gradle +++ b/org.jdrupes.vmoperator.vmviewer/build.gradle @@ -5,7 +5,7 @@ plugins { dependencies { implementation project(':org.jdrupes.vmoperator.manager.events') - implementation 'org.jgrapes:org.jgrapes.webconsole.base:[1.3.0,2)' + implementation 'org.jgrapes:org.jgrapes.webconsole.base:[1.7.0,2)' implementation 'org.jgrapes:org.jgrapes.webconsole.provider.vue:[1,2)' implementation 'org.jgrapes:org.jgrapes.webconsole.provider.jgwcvuecomponents:[1.2,2)' implementation 'org.jgrapes:org.jgrapes.webconsole.provider.chartjs:[1.2,2)' diff --git a/org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-confirmReset.ftl.html b/org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-confirmReset.ftl.html new file mode 100644 index 0000000..f7e3840 --- /dev/null +++ b/org.jdrupes.vmoperator.vmviewer/resources/org/jdrupes/vmoperator/vmviewer/VmViewer-confirmReset.ftl.html @@ -0,0 +1,13 @@ +
+

${_("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 d4e86ca..e86d9db 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,7 +1,8 @@ -
+
{{ 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 1cd0392..c034504 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,4 +1,5 @@ -
+ + + + + + 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 ab7238a..1534f6b 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 @@ -54,6 +54,7 @@ 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; @@ -93,7 +94,7 @@ import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet; * The Class VmViewer. */ @SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.ExcessiveImports", - "PMD.CouplingBetweenObjects", "PMD.GodClass" }) + "PMD.CouplingBetweenObjects", "PMD.GodClass", "PMD.TooManyMethods" }) public class VmViewer extends FreeMarkerConlet { private static final String VM_NAME_PROPERTY = "vmName"; @@ -465,12 +466,12 @@ public class VmViewer extends FreeMarkerConlet { @Override @SuppressWarnings({ "PMD.AvoidDecimalLiteralsInBigDecimalConstructor", - "PMD.ConfusingArgumentToVarargsMethod" }) + "PMD.ConfusingArgumentToVarargsMethod", "PMD.NcssCount" }) protected void doUpdateConletState(NotifyConletModel event, ConsoleConnection channel, ViewerModel model) throws Exception { event.stop(); - var both = Optional.ofNullable(event.params().asString(0)) + var both = Optional.ofNullable(model.vmName()) .flatMap(vm -> channelManager.both(vm)); if (both.isEmpty()) { return; @@ -479,6 +480,7 @@ 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)); @@ -497,6 +499,16 @@ 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), @@ -577,6 +589,20 @@ 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 71bb0a4..1c20d66 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,8 +31,9 @@ declare global { interface Window { orgJDrupesVmOperatorVmViewer: { initPreview?: (previewDom: HTMLElement, isUpdate: boolean) => void, - initEdit?: (viewDom: HTMLElement, isUpdate: boolean) => void - applyEdit?: (viewDom: HTMLElement, apply: boolean) => void + initEdit?: (viewDom: HTMLElement, isUpdate: boolean) => void, + applyEdit?: (viewDom: HTMLElement, apply: boolean) => void, + confirmReset?: (conletType: string, conletId: string) => void } } } @@ -74,7 +75,7 @@ window.orgJDrupesVmOperatorVmViewer.initPreview = (previewDom: HTMLElement, provideApi(previewDom, previewApi); const vmAction = (vmName: string, action: string) => { - JGConsole.notifyConletModel(conletId, action, vmName); + JGConsole.notifyConletModel(conletId, action); }; return { localize, resourceBase, vmDef, vmAction }; @@ -83,7 +84,7 @@ window.orgJDrupesVmOperatorVmViewer.initPreview = (previewDom: HTMLElement, - @@ -211,3 +221,9 @@ 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 8ef8b66..c90a45f 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,7 +19,24 @@ /* * Conlet specific styles. */ -.jdrupes-vmoperator-vmviewer-preview { +.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; + } + } [role=button] { padding: 0.25rem; @@ -28,7 +45,10 @@ box-shadow: var(--darkening); } } +} +.jdrupes-vmoperator-vmviewer.jdrupes-vmoperator-vmviewer-preview { + img { height: 3em; padding: 0.25rem; @@ -37,14 +57,30 @@ opacity: 0.4; } } + + .jdrupes-vmoperator-vmviewer-preview-action-list { + white-space: nowrap; + } } -.jdrupes-vmoperator-vmviewer-preview-action-list { - white-space: nowrap; -} - -.jdrupes-vmoperator-vmviewer-edit { +.jdrupes-vmoperator-vmviewer.jdrupes-vmoperator-vmviewer-edit { select { width: 15em; } -} \ No newline at end of file +} + +.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; + } + } + +} From 074f0cf7dab5100e351b460cad7a100fab53eb43 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sun, 9 Jun 2024 13:25:10 +0200 Subject: [PATCH 21/24] Fix some minor codacy complaints. --- README.md | 5 ++--- dev-example/Readme.md | 8 ++++---- example/local-path/Readme.md | 8 ++++---- example/rook-ceph/Readme.md | 4 ++-- .../jdrupes/vmoperator/manager/ManagerIntro-Preview.md | 4 ++-- .../jdrupes/vmoperator/manager/ManagerIntro-Preview_de.md | 2 +- 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 1895bbb..176437a 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,8 +6,7 @@ # 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/dev-example/Readme.md b/dev-example/Readme.md index dfcd3e8..516fb7e 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 7afb948..bdba8cc 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 1d2cfc6..3756e93 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.manager/resources/org/jdrupes/vmoperator/manager/ManagerIntro-Preview.md b/org.jdrupes.vmoperator.manager/resources/org/jdrupes/vmoperator/manager/ManagerIntro-Preview.md index 50a3024..b6b9efa 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. \ No newline at end of file +conlet (if available) to get a detailed view. 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 e5e4d68..bec5f3e 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 From 1dc3c1cc89d3ae3ac49fe1d51d4dcaed0f0ff2b9 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sun, 9 Jun 2024 15:22:46 +0200 Subject: [PATCH 22/24] Add new permission. --- deploy/crds/vms-crd.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/deploy/crds/vms-crd.yaml b/deploy/crds/vms-crd.yaml index f441cbc..bfe3985 100644 --- a/deploy/crds/vms-crd.yaml +++ b/deploy/crds/vms-crd.yaml @@ -1012,7 +1012,12 @@ spec: type: array items: type: string - enum: ["start", "stop", "accessConsole", "*"] + enum: + - start + - stop + - reset + - accessConsole + - "*" default: [] vm: type: object From 4d5b6a58b70071958c02ae18ea7958c8054f4f0f Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sun, 9 Jun 2024 22:30:15 +0200 Subject: [PATCH 23/24] Fix VM selection. --- .../jdrupes/vmoperator/vmviewer/VmViewer.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) 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 1534f6b..fe48d3b 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,6 +20,7 @@ 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; @@ -466,11 +467,18 @@ public class VmViewer extends FreeMarkerConlet { @Override @SuppressWarnings({ "PMD.AvoidDecimalLiteralsInBigDecimalConstructor", - "PMD.ConfusingArgumentToVarargsMethod", "PMD.NcssCount" }) + "PMD.ConfusingArgumentToVarargsMethod", "PMD.NcssCount", + "PMD.AvoidLiteralsInIfCondition" }) 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()) .flatMap(vm -> channelManager.both(vm)); if (both.isEmpty()) { @@ -482,13 +490,6 @@ public class VmViewer extends FreeMarkerConlet { 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)); @@ -522,6 +523,15 @@ 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); From b2e218c616a2454d6ec23de6f3390c1899430584 Mon Sep 17 00:00:00 2001 From: "Michael N. Lipp" Date: Sun, 9 Jun 2024 22:40:03 +0200 Subject: [PATCH 24/24] Add busy spinner. --- .../vmviewer/browser/VmViewer-functions.ts | 60 +++++++++++-------- .../vmviewer/browser/VmViewer-style.scss | 12 ++++ 2 files changed, 46 insertions(+), 26 deletions(-) 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 1c20d66..a14e83c 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 @@ -64,7 +64,16 @@ window.orgJDrupesVmOperatorVmViewer.initPreview = (previewDom: HTMLElement, vmName: "", vmDefinition: {} }); - const vmDef = computed(() => previewApi.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 : []); watch(() => previewApi.vmName, (name: string) => { if (name !== "") { @@ -74,51 +83,50 @@ window.orgJDrupesVmOperatorVmViewer.initPreview = (previewDom: HTMLElement, provideApi(previewDom, previewApi); - const vmAction = (vmName: string, action: string) => { + const vmAction = (action: string) => { JGConsole.notifyConletModel(conletId, action); }; - return { localize, resourceBase, vmDef, vmAction }; + return { localize, resourceBase, vmAction, configured, + startable, stoppable, running, permissions }; }, template: `
+ + + + + +
- - + - + + +
+ - + v-on:click="vmAction('start')"> - + v-on:click="vmAction('stop')"> + v-on:click="vmAction('reset')"> -
` 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 c90a45f..6d0654f 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 @@ -61,6 +61,18 @@ .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 {