Feature/web gui (#12)

Basic GUI functions (start/stop).
This commit is contained in:
Michael N. Lipp 2023-10-21 22:16:10 +02:00 committed by GitHub
parent 6491742eb0
commit ae3941707a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
86 changed files with 12225 additions and 514 deletions

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<fileset-config file-format-version="1.2.0" simple-config="false" sync-formatter="false">
<local-check-config name="Project Checks" location="/VM-Operator/checkstyle.xml" type="project" description="">
<additional-data name="protect-config-file" value="false"/>
</local-check-config>
<fileset name="all" enabled="true" check-config-name="Project Checks" local="true">
<file-match-pattern match-pattern="." include-pattern="true"/>
</fileset>
</fileset-config>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<eclipse-pmd xmlns="http://acanda.ch/eclipse-pmd/0.8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://acanda.ch/eclipse-pmd/0.8 http://acanda.ch/eclipse-pmd/eclipse-pmd-0.8.xsd">
<analysis enabled="true" />
<rulesets>
<ruleset name="Custom Rules" ref="moodle-tools-console/ruleset.xml" refcontext="workspace" />
</rulesets>
</eclipse-pmd>

View file

@ -0,0 +1,4 @@
/bin/
/bin_test/
/generated/
/build/

View file

@ -0,0 +1,10 @@
build.commands=org.eclipse.jdt.core.javabuilder
connection.arguments=
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.java.home=null
connection.jvm.arguments=
connection.project.dir=..
derived.resources=.gradle,generated
eclipse.preferences.version=1
natures=org.eclipse.jdt.groovy.core.groovyNature,org.eclipse.jdt.core.javanature
project.path=\:org.jgrapes.osgi.conlets.services

View file

@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8

View file

@ -0,0 +1,2 @@
eclipse.preferences.version=1
line.separator=\n

View file

@ -0,0 +1,63 @@
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=_JGrapes
formatter_settings_version=13
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
sp_cleanup.add_missing_annotations=true
sp_cleanup.add_missing_deprecated_annotations=true
sp_cleanup.add_missing_methods=false
sp_cleanup.add_missing_nls_tags=false
sp_cleanup.add_missing_override_annotations=true
sp_cleanup.add_missing_override_annotations_interface_methods=true
sp_cleanup.add_serial_version_id=false
sp_cleanup.always_use_blocks=true
sp_cleanup.always_use_parentheses_in_expressions=false
sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
sp_cleanup.convert_functional_interfaces=false
sp_cleanup.convert_to_enhanced_for_loop=false
sp_cleanup.correct_indentation=false
sp_cleanup.format_source_code=true
sp_cleanup.format_source_code_changes_only=false
sp_cleanup.insert_inferred_type_arguments=false
sp_cleanup.make_local_variable_final=true
sp_cleanup.make_parameters_final=false
sp_cleanup.make_private_fields_final=true
sp_cleanup.make_type_abstract_if_missing_method=false
sp_cleanup.make_variable_declarations_final=false
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
sp_cleanup.on_save_use_additional_actions=false
sp_cleanup.organize_imports=false
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
sp_cleanup.remove_redundant_type_arguments=false
sp_cleanup.remove_trailing_whitespaces=false
sp_cleanup.remove_trailing_whitespaces_all=true
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
sp_cleanup.remove_unnecessary_casts=true
sp_cleanup.remove_unnecessary_nls_tags=false
sp_cleanup.remove_unused_imports=false
sp_cleanup.remove_unused_local_variables=false
sp_cleanup.remove_unused_private_fields=true
sp_cleanup.remove_unused_private_members=false
sp_cleanup.remove_unused_private_methods=true
sp_cleanup.remove_unused_private_types=true
sp_cleanup.sort_members=false
sp_cleanup.sort_members_all=false
sp_cleanup.use_anonymous_class_creation=false
sp_cleanup.use_blocks=false
sp_cleanup.use_blocks_only_for_return_and_throw=false
sp_cleanup.use_lambda=true
sp_cleanup.use_parentheses_in_expressions=false
sp_cleanup.use_this_for_non_static_field_access=false
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
sp_cleanup.use_this_for_non_static_method_access=false
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
sp_jautodoc.cleanup.add_header=false
sp_jautodoc.cleanup.replace_header=false

View file

@ -0,0 +1,54 @@
plugins {
id 'org.jdrupes.vmoperator.java-library-conventions'
}
dependencies {
implementation project(':org.jdrupes.vmoperator.manager.events')
implementation 'org.jgrapes:org.jgrapes.webconsole.base:[1.2.0,2)'
implementation 'org.jgrapes:org.jgrapes.webconsole.provider.vue:[1,2)'
implementation 'org.jgrapes:org.jgrapes.webconsole.provider.jgwcvuecomponents:[1,2)'
}
apply plugin: 'com.github.node-gradle.node'
node {
download = true
}
task extractDependencies(type: Copy) {
from configurations.compileClasspath
.findAll{ it.name.contains('.provider.')
|| it.name.contains('org.jgrapes.webconsole.base')
}
.collect{ zipTree (it) }
into 'build/unpacked'
duplicatesStrategy 'include'
}
task compileTs(type: NodeTask) {
dependsOn ':npmInstall'
dependsOn extractDependencies
inputs.dir project.file('src')
inputs.file project.file('tsconfig.json')
inputs.file project.file('rollup.config.mjs')
outputs.dir project.file('build/generated/resources')
script = file("${rootProject.rootDir}/node_modules/rollup/dist/bin/rollup")
args = ["-c"]
}
sourceSets {
main {
resources {
srcDir project.file('build/generated/resources')
}
}
}
processResources {
dependsOn compileTs
}
eclipse {
autoBuildTasks compileTs
}

View file

@ -0,0 +1 @@
{}

View file

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

View file

@ -0,0 +1,31 @@
/*
* Moodle Tools Console
* Copyright (C) 2022 Michael N. Lipp
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU 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 General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
"use strict";
const l10nBundles = new Map();
let entries = null;
// <#list supportedLanguages() as l>
entries = new Map();
l10nBundles.set("${l.locale.toLanguageTag()}", entries);
// <#list l.l10nBundle.keys as key>
entries.set("${key}", "${l.l10nBundle.getString(key)}");
// </#list>
// </#list>
export default l10nBundles;

View file

@ -0,0 +1,5 @@
<div class="jdrupes-vmoperator-vmconlet jdrupes-vmoperator-vmconlet-preview"
data-jgwc-on-load="orgJDrupesVmOperatorVmConlet.initPreview"
data-jgwc-on-unload="JGConsole.jgwc.unmountVueApps">
<div>Preview</div>
</div>

View file

@ -0,0 +1,84 @@
<div class="jdrupes-vmoperator-vmconlet jdrupes-vmoperator-vmconlet-view"
data-jgwc-on-load="orgJDrupesVmOperatorVmConlet.initView"
data-jgwc-on-unload="JGConsole.jgwc.unmountVueApps">
<div class="jdrupes-vmoperator-vmconlet-view-search">
<form>
<label class="form__label--horizontal">
<span>{{ localize("Filter") }}</span>
<input type="text" class="form__input-text--with-remove"
v-on:input="controller.updateFilter($event)">
<span role="button" tabindex="0" class="fa fa-remove"
v-on:click="controller.clearFilter($event)"></span>
</label>
</form>
</div>
<table
class="table--basic--striped jdrupes-vmoperator-vmconlet-view-table">
<thead>
<tr>
<th v-for="key in controller.keys"
class="sortable" v-on:click="controller.sortBy(key)">
{{ localize(controller.label(key)) }}<span v-if="controller.sortedByAsc(key)"
role="button" tabindex="0">&#x25B2;</span><span
v-if="controller.sortedByDesc(key)" role="button"
tabindex="0">&#x25BC;</span>
</th>
<th>
{{ localize("vmActions") }}
</th>
</tr>
</thead>
<tbody>
<template v-for="(entry, rowIndex) in filteredData">
<tr :class="[(rowIndex % 2) ? 'odd' : 'even']"
:aria-expanded="(entry.name in detailsByName) ? 'true' : 'false'">
<td v-for="key in controller.keys"
v-bind:title="key == 'name' ? entry['name']: false"
v-bind:rowspan="(key == 'name') && $aash.isDisclosed(scopedId(rowIndex)) ? 2 : false">
<aash-disclosure-button v-if="key === 'name'" :type="'div'"
:id-ref="scopedId(rowIndex)">
<span v-html="controller.breakBeforeDots(entry[key])"></span>
</aash-disclosure-button>
<span v-else-if="key === 'running'"
v-html="localize(entry[key] ? 'Yes' : 'No')"></span>
<span v-else-if="key === 'currentRam'"
v-html="formatMemory(BigInt(entry[key]))"></span>
<span v-else
v-html="controller.breakBeforeDots(entry[key])"></span>
</td>
<td class="jdrupes-vmoperator-vmconlet-view-action-list">
<span role="button" v-if="!entry['running']"
tabindex="0" class="fa fa-play" :title="localize('Start VM')"
v-on:click="vmAction(entry.name, 'start')"></span>
<span role="button" v-if="entry['running']"
tabindex="0" class="fa fa-stop" :title="localize('Stop VM')"
v-on:click="vmAction(entry.name, 'stop')"></span>
</td>
</tr>
<tr :id="scopedId(rowIndex)" v-if="$aash.isDisclosed(scopedId(rowIndex))"
:class="[(rowIndex % 2) ? 'odd' : 'even']">
<td colspan="4" class="details">
<table class="table--basic table--basic--autoStriped">
<tr>
<td>{{ localize("maximumCpus") }}</td>
<td>{{ entry.spec.vm.maximumCpus }}</td>
</tr>
<tr>
<td>{{ localize("requestedCpus") }}</td>
<td>{{ entry.spec.vm.maximumCpus }}</td>
</tr>
<tr>
<td>{{ localize("maximumRam") }}</td>
<td>{{ formatMemory(BigInt(entry.spec.vm.maximumRam)) }}</td>
</tr>
<tr>
<td>{{ localize("requestedRam") }}</td>
<td>{{ formatMemory(BigInt(entry.spec.vm.maximumRam)) }}</td>
</tr>
</table>
</td>
</tr>
</template>
</tbody>
</table>
</div>

View file

@ -0,0 +1,11 @@
conletName = VM Viewer
currentCpus = Current CPUs
currentRam = Current RAM
maximumCpus = Maximum CPUs
maximumRam = Maximum RAM
requestedCpus = Requested CPUs
requestedRam = Requested RAM
running = Running
vmActions = Actions
vmname = Name

View file

@ -0,0 +1,17 @@
conletName = VM Anzeige
running = Gestartet
currentCpus = Aktuelle CPUs
currentRam = Akuelles RAM
maximumCpus = Maximale CPUs
maximumRam = Maximales RAM
requestedCpus = Angeforderte CPUs
requestedRam = Angefordertes RAM
vmActions = Aktionen
vmname = Name
Start\ VM = VM Starten
Stop\ VM = VM Anhalten
Yes = Ja
No = Nein

View file

@ -0,0 +1,35 @@
import typescript from 'rollup-plugin-typescript2';
import postcss from 'rollup-plugin-postcss';
let packagePath = "org/jdrupes/vmoperator/vmconlet";
let baseName = "VmConlet"
let module = "build/generated/resources/" + packagePath
+ "/" + baseName + "-functions.js";
let pathsMap = {
"aash-plugin": "../../page-resource/aash-vue-components/lib/aash-vue-components.js",
"jgconsole": "../../console-base-resource/jgconsole.js",
"jgwc": "../../page-resource/jgwc-vue-components/jgwc-components.js",
"l10nBundles": "./" + baseName + "-l10nBundles.ftl.js",
"vue": "../../page-resource/vue/vue.esm-browser.js"
}
export default {
external: ['vue', 'aash-plugin', 'jgconsole', 'jgwc', 'l10nBundles'],
input: "src/" + packagePath + "/browser/" + baseName + "-functions.ts",
output: [
{
format: "esm",
file: module,
sourcemap: true,
sourcemapPathTransform: (relativeSourcePath, _sourcemapPath) => {
return relativeSourcePath.replace(/^([^/]*\/){12}/, "./");
},
paths: pathsMap
}
],
plugins: [
typescript(),
postcss()
]
};

View file

@ -0,0 +1,247 @@
/*
* VM-Operator
* Copyright (C) 2023 Michael N. Lipp
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.vmconlet;
import com.google.gson.JsonObject;
import freemarker.core.ParseException;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateNotFoundException;
import io.kubernetes.client.custom.Quantity;
import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.jdrupes.json.JsonBeanDecoder;
import org.jdrupes.json.JsonDecodeException;
import org.jdrupes.vmoperator.manager.events.StartVm;
import org.jdrupes.vmoperator.manager.events.StopVm;
import org.jdrupes.vmoperator.manager.events.VmChannel;
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
import org.jdrupes.vmoperator.manager.events.VmDefChanged.Type;
import org.jdrupes.vmoperator.util.GsonPtr;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Event;
import org.jgrapes.core.Manager;
import org.jgrapes.core.NamedChannel;
import org.jgrapes.core.annotation.Handler;
import org.jgrapes.webconsole.base.Conlet.RenderMode;
import org.jgrapes.webconsole.base.ConletBaseModel;
import org.jgrapes.webconsole.base.ConsoleConnection;
import org.jgrapes.webconsole.base.events.AddConletRequest;
import org.jgrapes.webconsole.base.events.AddConletType;
import org.jgrapes.webconsole.base.events.AddPageResources.ScriptResource;
import org.jgrapes.webconsole.base.events.ConsoleReady;
import org.jgrapes.webconsole.base.events.NotifyConletModel;
import org.jgrapes.webconsole.base.events.NotifyConletView;
import org.jgrapes.webconsole.base.events.RenderConlet;
import org.jgrapes.webconsole.base.events.RenderConletRequestBase;
import org.jgrapes.webconsole.base.events.SetLocale;
import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet;
/**
*/
@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
public class VmConlet extends FreeMarkerConlet<VmConlet.VmsModel> {
private static final Set<RenderMode> MODES = RenderMode.asSet(
RenderMode.Preview, RenderMode.View);
private final Map<String, DynamicKubernetesObject> vmInfos
= new ConcurrentHashMap<>();
/**
* Creates a new component with its channel set to the given channel.
*
* @param componentChannel the channel that the component's handlers listen
* on by default and that {@link Manager#fire(Event, Channel...)}
* sends the event to
*/
public VmConlet(Channel componentChannel) {
super(componentChannel);
}
/**
* On {@link ConsoleReady}, fire the {@link AddConletType}.
*
* @param event the event
* @param channel the channel
* @throws TemplateNotFoundException the template not found exception
* @throws MalformedTemplateNameException the malformed template name
* exception
* @throws ParseException the parse exception
* @throws IOException Signals that an I/O exception has occurred.
*/
@Handler
public void onConsoleReady(ConsoleReady event, ConsoleConnection channel)
throws TemplateNotFoundException, MalformedTemplateNameException,
ParseException, IOException {
// Add conlet resources to page
channel.respond(new AddConletType(type())
.setDisplayNames(
localizations(channel.supportedLocales(), "conletName"))
.addRenderMode(RenderMode.Preview)
.addScript(new ScriptResource().setScriptType("module")
.setScriptUri(event.renderSupport().conletResource(
type(), "VmConlet-functions.js"))));
}
@Override
protected Optional<VmsModel> createNewState(AddConletRequest event,
ConsoleConnection connection, String conletId) throws Exception {
return Optional.of(new VmsModel(conletId));
}
@Override
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
protected Set<RenderMode> doRenderConlet(RenderConletRequestBase<?> event,
ConsoleConnection channel, String conletId, VmsModel conletState)
throws Exception {
Set<RenderMode> renderedAs = new HashSet<>();
boolean sendData = false;
if (event.renderAs().contains(RenderMode.Preview)) {
Template tpl
= freemarkerConfig().getTemplate("VmConlet-preview.ftl.html");
channel.respond(new RenderConlet(type(), conletId,
processTemplate(event, tpl,
fmModel(event, channel, conletId, conletState)))
.setRenderAs(
RenderMode.Preview.addModifiers(event.renderAs()))
.setSupportedModes(MODES));
renderedAs.add(RenderMode.View);
sendData = true;
}
if (event.renderAs().contains(RenderMode.View)) {
Template tpl
= freemarkerConfig().getTemplate("VmConlet-view.ftl.html");
channel.respond(new RenderConlet(type(), conletId,
processTemplate(event, tpl,
fmModel(event, channel, conletId, conletState)))
.setRenderAs(
RenderMode.View.addModifiers(event.renderAs()))
.setSupportedModes(MODES));
renderedAs.add(RenderMode.View);
sendData = true;
}
if (sendData) {
for (var vmInfo : vmInfos.values()) {
var def = JsonBeanDecoder.create(vmInfo.getRaw().toString())
.readObject();
channel.respond(new NotifyConletView(type(),
conletId, "updateVm", def));
}
}
return renderedAs;
}
/**
* Track the VM definitions.
*
* @param event the event
* @param channel the channel
* @throws JsonDecodeException
*/
@Handler(namedChannels = "manager")
@SuppressWarnings({ "PMD.ConfusingTernary",
"PMD.AvoidInstantiatingObjectsInLoops" })
public void onVmDefChanged(VmDefChanged event, VmChannel channel)
throws JsonDecodeException {
if (event.type() == Type.DELETED) {
vmInfos.remove(event.vmDefinition().getMetadata().getName());
for (var entry : conletIdsByConsoleConnection().entrySet()) {
for (String conletId : entry.getValue()) {
entry.getKey().respond(new NotifyConletView(type(),
conletId, "removeVm"));
}
}
} else {
var vmDef = new DynamicKubernetesObject(
event.vmDefinition().getRaw().deepCopy());
GsonPtr.to(vmDef.getRaw()).to("metadata").get(JsonObject.class)
.remove("managedFields");
var vmSpec = GsonPtr.to(vmDef.getRaw()).to("spec", "vm");
vmSpec.set("maximumRam", Quantity.fromString(
vmSpec.getAsString("maximumRam").orElse("0")).getNumber()
.toBigInteger().toString());
vmSpec.set("currentRam", Quantity.fromString(
vmSpec.getAsString("currentRam").orElse("0")).getNumber()
.toBigInteger().toString());
var status = GsonPtr.to(vmDef.getRaw()).to("status");
status.set("ram", Quantity.fromString(
status.getAsString("ram").orElse("0")).getNumber()
.toBigInteger().toString());
String vmName = event.vmDefinition().getMetadata().getName();
vmInfos.put(vmName, vmDef);
// Extract running
var def = JsonBeanDecoder.create(vmDef.getRaw().toString())
.readObject();
for (var entry : conletIdsByConsoleConnection().entrySet()) {
for (String conletId : entry.getValue()) {
entry.getKey().respond(new NotifyConletView(type(),
conletId, "updateVm", def));
}
}
}
}
@Override
protected void doUpdateConletState(NotifyConletModel event,
ConsoleConnection channel, VmsModel conletState)
throws Exception {
event.stop();
switch (event.method()) {
case "start":
fire(new StartVm(event.params().asString(0),
new NamedChannel("manager")));
break;
case "stop":
fire(new StopVm(event.params().asString(0),
new NamedChannel("manager")));
break;
default:// ignore
break;
}
}
@Override
protected boolean doSetLocale(SetLocale event, ConsoleConnection channel,
String conletId) throws Exception {
return true;
}
/**
* The Class VmsModel.
*/
public class VmsModel extends ConletBaseModel {
/**
* Instantiates a new vms model.
*
* @param conletId the conlet id
*/
public VmsModel(String conletId) {
super(conletId);
}
}
}

View file

@ -0,0 +1,54 @@
/*
* VM-Operator
* Copyright (C) 2023 Michael N. Lipp
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.vmconlet;
import java.util.Map;
import java.util.Optional;
import org.jgrapes.core.Channel;
import org.jgrapes.core.ComponentType;
import org.jgrapes.webconsole.base.ConletComponentFactory;
/**
* The factory service for {@link VmConlet}s.
*/
public class VmConletFactory implements ConletComponentFactory {
/*
* (non-Javadoc)
*
* @see org.jgrapes.core.ComponentFactory#componentType()
*/
@Override
public Class<? extends ComponentType> componentType() {
return VmConlet.class;
}
/*
* (non-Javadoc)
*
* @see org.jgrapes.core.ComponentFactory#create(org.jgrapes.core.Channel,
* java.util.Map)
*/
@Override
public Optional<ComponentType> create(Channel componentChannel,
Map<?, ?> properties) {
return Optional.of(new VmConlet(componentChannel));
}
}

View file

@ -0,0 +1,148 @@
/*
* VM-Operator
* Copyright (C) 2023 Michael N. Lipp
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { reactive, ref, createApp, computed, onMounted } from "vue";
import JGConsole from "jgconsole";
import JgwcPlugin, { JGWC } from "jgwc";
import { provideApi, getApi } from "aash-plugin";
import l10nBundles from "l10nBundles";
import "./VmConlet-style.scss";
//
// Helpers
//
let unitMap = new Map<string, bigint>();
let unitMappings = new Array<{ key: string; value: bigint }>();
let memorySize = /^\\s*(\\d+(\\.\\d+)?)\\s*([A-Za-z]*)\\s*/;
// SI units and common abbreviations
let factor = BigInt("1");
unitMap.set("", factor);
let scale = BigInt("1000");
for (let unit of ["B", "kB", "MB", "GB", "TB", "PB", "EB"]) {
unitMap.set(unit, factor);
factor = factor * scale;
}
// Binary units
factor = BigInt("1024");
scale = BigInt("1024");
for (let unit of ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]) {
unitMap.set(unit, factor);
factor = factor * scale;
}
unitMap.forEach((value: bigint, key: string) => {
unitMappings.push({ key, value });
});
unitMappings.sort((a, b) => a.value < b.value ? 1 : a.value > b.value ? -1 : 0);
function formatMemory(size: bigint): string {
for (let mapping of unitMappings) {
if (size >= mapping.value
&& (size % mapping.value) === BigInt("0")) {
return (size / mapping.value + " " + mapping.key).trim();
}
}
return size.toString();
}
// For global access
declare global {
interface Window {
orgJDrupesVmOperatorVmConlet: any;
}
}
window.orgJDrupesVmOperatorVmConlet = {};
let vmInfos = reactive(new Map());
window.orgJDrupesVmOperatorVmConlet.initPreview
= (previewDom: HTMLElement, isUpdate: boolean) => {
const app = createApp({});
app.use(JgwcPlugin, []);
app.config.globalProperties.window = window;
app.mount(previewDom);
};
window.orgJDrupesVmOperatorVmConlet.initView = (viewDom: HTMLElement,
isUpdate: boolean) => {
const app = createApp({
setup(_props: any) {
const conletId: string
= (<HTMLElement>viewDom.parentNode!).dataset["conletId"]!;
const localize = (key: string) => {
return JGConsole.localize(
l10nBundles, JGWC.lang() || "en", key);
};
const controller = reactive(new JGConsole.TableController([
["name", "vmname"],
["running", "running"],
["currentCpus", "currentCpus"],
["currentRam", "currentRam"]
], {
sortKey: "name",
sortOrder: "up"
}));
let filteredData = computed(() => {
let infos = Array.from(vmInfos.values());
return controller.filter(infos);
});
const vmAction = (vmName: string, action: string) => {
JGConsole.notifyConletModel(conletId, action, vmName);
};
const idScope = JGWC.createIdScope();
const detailsByName = reactive(new Set());
return {
controller, vmInfos, filteredData, detailsByName,
localize, formatMemory, vmAction,
scopedId: (id: string) => { return idScope.scopedId(id); }
}
}
});
app.use(JgwcPlugin);
app.config.globalProperties.window = window;
app.mount(viewDom);
};
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmconlet.VmConlet",
"updateVm", function(conletId: String, vmDefinition: any) {
// Add some short-cuts for table controller
vmDefinition.name = vmDefinition.metadata.name;
vmDefinition.currentCpus = vmDefinition.status.cpus;
vmDefinition.currentRam = vmDefinition.status.ram;
for (let condition of vmDefinition.status.conditions) {
if (condition.type === "Running") {
vmDefinition.running = condition.status === "True";
break;
}
}
vmInfos.set(vmDefinition.name, vmDefinition);
});
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmconlet.VmConlet",
"removeVm", function(conletId: String, vmName: String) {
vmInfos.delete(vmName);
});

View file

@ -0,0 +1,51 @@
/*
* VM-Operator
* Copyright (C) 2023 Michael N. Lipp
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/*
* Conlet specific styles.
*/
.jdrupes-vmoperator-vmconlet-view-search {
display: flex;
justify-content: flex-end
}
.jdrupes-vmoperator-vmconlet-view-search form {
white-space: nowrap;
}
.jdrupes-vmoperator-vmconlet-view-action-list {
white-space: nowrap;
}
.jdrupes-vmoperator-vmconlet-view-action-list [role=button]:not(:last-child) {
margin-right: 0.5em;
}
.jdrupes-vmoperator-vmconlet-view td {
vertical-align: top;
}
.jdrupes-vmoperator-vmconlet-view td:not([colspan]):first-child {
white-space: nowrap;
}
.jdrupes-vmoperator-vmconlet-view table td.details {
padding-left: 1em;
}

View file

@ -0,0 +1 @@
export default new Map<string, Map<string, string>>();

View file

@ -0,0 +1,19 @@
/*
* VM-Operator
* Copyright (C) 2023 Michael N. Lipp
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jdrupes.vmoperator.vmconlet;

View file

@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "es2015",
"module": "es2015",
"sourceMap": true,
"inlineSources": true,
"declaration": true,
"importHelpers": true,
"strict": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"lib": ["DOM", "ES2020"],
"paths": {
"aash-plugin": ["./build/unpacked/org/jgrapes/webconsole/provider/jgwcvuecomponents/aash-vue-components/lib/AashPlugin"],
"jgconsole": ["./build/unpacked/org/jgrapes/webconsole/base/JGConsole"],
"jgwc": ["./build/unpacked/org/jgrapes/webconsole/provider/jgwcvuecomponents/jgwc-vue-components/jgwc-components"],
"l10nBundles": ["./src/org/jdrupes/vmoperator/vmconlet/browser/l10nBundles-stub"],
"vue": ["./build/unpacked/org/jgrapes/webconsole/provider/vue/vue/vue"]
}
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "l10nBundles-stub.ts"]
}