Add console access to VM management.
This commit is contained in:
parent
5cd4edcec1
commit
af41c78c07
14 changed files with 561 additions and 124 deletions
|
|
@ -1,6 +1,7 @@
|
|||
<div class="jdrupes-vmoperator-vmmgmt jdrupes-vmoperator-vmmgmt-view"
|
||||
data-jgwc-on-load="orgJDrupesVmOperatorVmMgmt.initView"
|
||||
data-jgwc-on-unload="JGConsole.jgwc.unmountVueApps">
|
||||
data-jgwc-on-unload="JGConsole.jgwc.unmountVueApps"
|
||||
data-conlet-resource-base="${conletResource('')}">
|
||||
<div class="jdrupes-vmoperator-vmmgmt-view-search">
|
||||
<form>
|
||||
<label class="form__label--horizontal">
|
||||
|
|
@ -58,17 +59,26 @@
|
|||
</td>
|
||||
<td class="jdrupes-vmoperator-vmmgmt-view-action-list">
|
||||
<span role="button"
|
||||
v-if="entry.spec.vm.state != 'Running' && !entry['running']"
|
||||
v-if="entry.spec.vm.state != 'Running' && !entry['running']
|
||||
&& entry.permissions.includes('start')"
|
||||
tabindex="0" class="fa fa-play" :title="localize('Start VM')"
|
||||
v-on:click="vmAction(entry.name, 'start')"></span>
|
||||
<span role="button" v-else class="fa fa-play"
|
||||
aria-disabled="true" :title="localize('Start VM')"></span>
|
||||
<span role="button"
|
||||
v-if="entry.spec.vm.state != 'Stopped' && entry['running']"
|
||||
v-if="entry.spec.vm.state != 'Stopped' && entry['running']
|
||||
&& entry.permissions.includes('stop')"
|
||||
tabindex="0" class="fa fa-stop" :title="localize('Stop VM')"
|
||||
v-on:click="vmAction(entry.name, 'stop')"></span>
|
||||
<span role="button" v-else class="fa fa-stop"
|
||||
aria-disabled="true" :title="localize('Stop VM')"></span>
|
||||
<img role="button" :src="resourceBase + (!entry['running']
|
||||
? 'computer-off.svg' : (entry.usedFrom
|
||||
? 'computer-in-use.svg' : 'computer.svg'))"
|
||||
:title="localize('Open console')"
|
||||
:aria-disabled="!entry['running']
|
||||
|| !(entry.permissions.includes('accessConsole'))"
|
||||
v-on:click="vmAction(entry.name, 'openConsole')">
|
||||
</td>
|
||||
</tr>
|
||||
<tr :id="scopedId(rowIndex)" v-if="$aash.isDisclosed(scopedId(rowIndex))"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
fill="#000000"
|
||||
width="800"
|
||||
height="533.33331"
|
||||
viewBox="0 0 24 15.999999"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="computer-in-use.svg"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1">
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<linearGradient
|
||||
id="swatch3"
|
||||
inkscape:swatch="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="0"
|
||||
id="stop3" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.90509668"
|
||||
inkscape:cx="345.81941"
|
||||
inkscape:cy="376.2029"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1008"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="35"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
id="rect1"
|
||||
style="fill-opacity:0;stroke:#000000;stroke-width:1.97262;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0;paint-order:fill markers stroke"
|
||||
d="m 4.7729709,13.006705 -1.7691517,0 V 0.98808897 H 20.99618 V 13.006705 l -1.639132,0"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<path
|
||||
id="rect2"
|
||||
style="opacity:1;stroke-width:0.00145614;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;paint-order:fill markers stroke"
|
||||
d="m 0,13.998258 h 5.4336202 v 2.001741 H 0 Z"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
id="rect3"
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1.05373;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 5.6839082,10.94394 c 0.2963594,-0.907428 2.9522319,-1.683971 2.767387,-1.6392261 0,0 1.5028596,1.6181771 3.6459428,1.6129171 2.018383,-0.005 3.362681,-1.6125503 3.362681,-1.6125503 -0.171441,-0.061235 2.778887,0.7741493 2.977303,1.6787203 0.393054,1.791919 0.25928,4.489072 0.25928,4.489072 l -13.3818748,0.001 c 0,0 -0.181061,-2.844856 0.369281,-4.529957 z"
|
||||
sodipodi:nodetypes="sssssccs" />
|
||||
<ellipse
|
||||
style="fill:none;stroke:#000000;stroke-width:1.02152;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="path3"
|
||||
cx="11.964992"
|
||||
cy="6.3769712"
|
||||
rx="3.2413731"
|
||||
ry="3.225764" />
|
||||
<path
|
||||
id="rect2-2"
|
||||
style="stroke-width:0.00145614;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;paint-order:fill markers stroke"
|
||||
d="M 18.56638,13.998258 H 24 v 2.001741 h -5.43362 z"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
fill="#000000"
|
||||
width="800"
|
||||
height="533.33331"
|
||||
viewBox="0 0 24 15.999999"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="computer-off.svg"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1">
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<linearGradient
|
||||
id="swatch3"
|
||||
inkscape:swatch="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="0"
|
||||
id="stop3" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="1.3435029"
|
||||
inkscape:cx="377.74389"
|
||||
inkscape:cy="227.01849"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
id="rect1"
|
||||
style="fill-opacity:1;stroke:#000000;stroke-width:1.97262;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0;paint-order:fill markers stroke;fill:#545454"
|
||||
d="M 3.0038192,0.98808897 H 20.99618 V 13.006705 H 3.0038192 Z" />
|
||||
<rect
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.00306926;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="rect2"
|
||||
width="23.995173"
|
||||
height="2.0017407"
|
||||
x="0.0039473679"
|
||||
y="13.998839" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
|
||||
<svg
|
||||
fill="#000000"
|
||||
width="800"
|
||||
height="533.33331"
|
||||
viewBox="0 0 24 15.999999"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="computer.svg"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1">
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect4"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1"
|
||||
radius="0"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<linearGradient
|
||||
id="swatch3"
|
||||
inkscape:swatch="solid">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="0"
|
||||
id="stop3" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="1.3435029"
|
||||
inkscape:cx="377.74389"
|
||||
inkscape:cy="227.01849"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1011"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<path
|
||||
id="rect1"
|
||||
style="fill-opacity:0;stroke:#000000;stroke-width:1.97262;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0;paint-order:fill markers stroke"
|
||||
d="M 3.0038192,0.98808897 H 20.99618 V 13.006705 H 3.0038192 Z" />
|
||||
<rect
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.00306926;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
id="rect2"
|
||||
width="23.995173"
|
||||
height="2.0017407"
|
||||
x="0.0039473679"
|
||||
y="13.998839" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -17,3 +17,8 @@ usedBy = Used by
|
|||
usedFrom = Used from
|
||||
vmActions = Actions
|
||||
vmname = Name
|
||||
|
||||
confirmResetTitle = Confirm reset
|
||||
confirmResetMsg = Resetting the VM may cause loss of data. \
|
||||
Please confirm to continue.
|
||||
consoleTakenNotification = Console access is locked by another user.
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ vmname = Name
|
|||
Value\ is\ above\ maximum = Wert ist zu groß
|
||||
Illegal\ format = Ungültiges Format
|
||||
|
||||
confirmResetTitle = Zurücksetzen bestätigen
|
||||
confirmResetMsg = Zurücksetzen der VM kann zu Datenverlust führen. \
|
||||
Bitte bestätigen um fortzufahren.
|
||||
consoleTakenNotification = Die Konsole wird von einem anderen Benutzer verwendet.
|
||||
|
||||
Open\ console = Konsole anzeigen
|
||||
Start\ VM = VM Starten
|
||||
Stop\ VM = VM Anhalten
|
||||
|
||||
|
|
|
|||
|
|
@ -27,15 +27,22 @@ import io.kubernetes.client.custom.Quantity.Format;
|
|||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import org.jdrupes.vmoperator.common.K8sObserver;
|
||||
import org.jdrupes.vmoperator.common.VmDefinition;
|
||||
import org.jdrupes.vmoperator.common.VmDefinition.Permission;
|
||||
import org.jdrupes.vmoperator.manager.events.ChannelTracker;
|
||||
import org.jdrupes.vmoperator.manager.events.GetDisplayPassword;
|
||||
import org.jdrupes.vmoperator.manager.events.ModifyVm;
|
||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||
|
|
@ -44,13 +51,17 @@ import org.jgrapes.core.Channel;
|
|||
import org.jgrapes.core.Event;
|
||||
import org.jgrapes.core.Manager;
|
||||
import org.jgrapes.core.annotation.Handler;
|
||||
import org.jgrapes.util.events.ConfigurationUpdate;
|
||||
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.ConsoleRole;
|
||||
import org.jgrapes.webconsole.base.ConsoleUser;
|
||||
import org.jgrapes.webconsole.base.WebConsoleUtils;
|
||||
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.DisplayNotification;
|
||||
import org.jgrapes.webconsole.base.events.NotifyConletModel;
|
||||
import org.jgrapes.webconsole.base.events.NotifyConletView;
|
||||
import org.jgrapes.webconsole.base.events.RenderConlet;
|
||||
|
|
@ -61,10 +72,12 @@ import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet;
|
|||
/**
|
||||
* The Class {@link VmMgmt}.
|
||||
*/
|
||||
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis",
|
||||
"PMD.CouplingBetweenObjects" })
|
||||
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.CouplingBetweenObjects",
|
||||
"PMD.ExcessiveImports" })
|
||||
public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
|
||||
|
||||
private Class<?> preferredIpVersion = Inet4Address.class;
|
||||
private boolean deleteConnectionFile = true;
|
||||
private static final Set<RenderMode> MODES = RenderMode.asSet(
|
||||
RenderMode.Preview, RenderMode.View);
|
||||
private final ChannelTracker<String, VmChannel,
|
||||
|
|
@ -91,6 +104,44 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
|
|||
setPeriodicRefresh(Duration.ofMinutes(1), () -> new Update());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the component.
|
||||
*
|
||||
* @param event the event
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "PMD.AvoidDuplicateLiterals" })
|
||||
@Handler
|
||||
public void onConfigurationUpdate(ConfigurationUpdate event) {
|
||||
event.structured("/Manager/GuiHttpServer"
|
||||
+ "/ConsoleWeblet/WebConsole/ComponentCollector/VmAccess")
|
||||
.ifPresent(c -> {
|
||||
try {
|
||||
var dispRes = (Map<String, Object>) c
|
||||
.getOrDefault("displayResource",
|
||||
Collections.emptyMap());
|
||||
switch ((String) dispRes.getOrDefault("preferredIpVersion",
|
||||
"")) {
|
||||
case "ipv6":
|
||||
preferredIpVersion = Inet6Address.class;
|
||||
break;
|
||||
case "ipv4":
|
||||
default:
|
||||
preferredIpVersion = Inet4Address.class;
|
||||
break;
|
||||
}
|
||||
|
||||
// Delete connection file
|
||||
deleteConnectionFile
|
||||
= Optional.ofNullable(c.get("deleteConnectionFile"))
|
||||
.filter(v -> v instanceof String)
|
||||
.map(v -> (String) v)
|
||||
.map(Boolean::parseBoolean).orElse(true);
|
||||
} catch (ClassCastException e) {
|
||||
logger.config("Malformed configuration: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* On {@link ConsoleReady}, fire the {@link AddConletType}.
|
||||
*
|
||||
|
|
@ -117,7 +168,7 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Optional<VmsModel> createNewState(AddConletRequest event,
|
||||
protected Optional<VmsModel> createStateRepresentation(Event<?> event,
|
||||
ConsoleConnection connection, String conletId) throws Exception {
|
||||
return Optional.of(new VmsModel(conletId));
|
||||
}
|
||||
|
|
@ -160,17 +211,25 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
|
|||
}
|
||||
if (sendVmInfos) {
|
||||
for (var item : channelTracker.values()) {
|
||||
channel.respond(new NotifyConletView(type(),
|
||||
conletId, "updateVm",
|
||||
simplifiedVmDefinition(item.associated())));
|
||||
updateVm(channel, conletId, item.associated());
|
||||
}
|
||||
}
|
||||
|
||||
return renderedAs;
|
||||
}
|
||||
|
||||
private void updateVm(ConsoleConnection channel, String conletId,
|
||||
VmDefinition vmDef) {
|
||||
var user = WebConsoleUtils.userFromSession(channel.session())
|
||||
.map(ConsoleUser::getName).orElse(null);
|
||||
var roles = WebConsoleUtils.rolesFromSession(channel.session())
|
||||
.stream().map(ConsoleRole::getName).toList();
|
||||
channel.respond(new NotifyConletView(type(), conletId, "updateVm",
|
||||
simplifiedVmDefinition(vmDef, user, roles)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
|
||||
private Map<String, Object> simplifiedVmDefinition(VmDefinition vmDef) {
|
||||
private Map<String, Object> simplifiedVmDefinition(VmDefinition vmDef,
|
||||
String user, List<String> roles) {
|
||||
// Convert RAM sizes to unitless numbers
|
||||
var spec = DataPath.deepCopy(vmDef.spec());
|
||||
var vmSpec = DataPath.<Map<String, Object>> get(spec, "vm").get();
|
||||
|
|
@ -191,7 +250,9 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
|
|||
"name", vmDef.name()),
|
||||
"spec", spec,
|
||||
"status", status,
|
||||
"nodeName", vmDef.extra("nodeName"));
|
||||
"nodeName", vmDef.extra("nodeName"),
|
||||
"permissions", vmDef.permissionsFor(user, roles).stream()
|
||||
.map(VmDefinition.Permission::toString).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -221,8 +282,7 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
|
|||
channelTracker.put(vmName, channel, vmDef);
|
||||
for (var entry : conletIdsByConsoleConnection().entrySet()) {
|
||||
for (String conletId : entry.getValue()) {
|
||||
entry.getKey().respond(new NotifyConletView(type(),
|
||||
conletId, "updateVm", simplifiedVmDefinition(vmDef)));
|
||||
updateVm(entry.getKey(), conletId, vmDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -337,22 +397,35 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
|
|||
@Override
|
||||
@SuppressWarnings("PMD.AvoidDecimalLiteralsInBigDecimalConstructor")
|
||||
protected void doUpdateConletState(NotifyConletModel event,
|
||||
ConsoleConnection channel, VmsModel conletState)
|
||||
throws Exception {
|
||||
ConsoleConnection channel, VmsModel model) throws Exception {
|
||||
event.stop();
|
||||
String vmName = event.param(0);
|
||||
var vmChannel = channelTracker.channel(vmName).orElse(null);
|
||||
if (vmChannel == null) {
|
||||
var value = channelTracker.value(vmName);
|
||||
var vmChannel = value.map(v -> v.channel()).orElse(null);
|
||||
var vmDef = value.map(v -> v.associated()).orElse(null);
|
||||
if (vmDef == null) {
|
||||
return;
|
||||
}
|
||||
var user = WebConsoleUtils.userFromSession(channel.session())
|
||||
.map(ConsoleUser::getName).orElse("");
|
||||
var roles = WebConsoleUtils.rolesFromSession(channel.session())
|
||||
.stream().map(ConsoleRole::getName).toList();
|
||||
var perms = vmDef.permissionsFor(user, roles);
|
||||
switch (event.method()) {
|
||||
case "start":
|
||||
fire(new ModifyVm(vmName, "state", "Running", vmChannel));
|
||||
if (perms.contains(VmDefinition.Permission.START)) {
|
||||
fire(new ModifyVm(vmName, "state", "Running", vmChannel));
|
||||
}
|
||||
break;
|
||||
case "stop":
|
||||
fire(new ModifyVm(vmName, "state", "Stopped", vmChannel));
|
||||
if (perms.contains(VmDefinition.Permission.STOP)) {
|
||||
fire(new ModifyVm(vmName, "state", "Stopped", vmChannel));
|
||||
}
|
||||
break;
|
||||
case "openConsole":
|
||||
if (perms.contains(VmDefinition.Permission.ACCESS_CONSOLE)) {
|
||||
openConsole(channel, model, vmChannel, vmDef, user, perms);
|
||||
}
|
||||
break;
|
||||
case "cpus":
|
||||
fire(new ModifyVm(vmName, "currentCpus",
|
||||
|
|
@ -370,6 +443,29 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
|
|||
}
|
||||
}
|
||||
|
||||
private void openConsole(ConsoleConnection channel, VmsModel model,
|
||||
VmChannel vmChannel, VmDefinition vmDef, String user,
|
||||
Set<Permission> perms) {
|
||||
ResourceBundle resourceBundle = resourceBundle(channel.locale());
|
||||
if (!vmDef.consoleAccessible(user, perms)) {
|
||||
channel.respond(new DisplayNotification(
|
||||
resourceBundle.getString("consoleTakenNotification"),
|
||||
Map.of("autoClose", 5_000, "type", "Warning")));
|
||||
return;
|
||||
}
|
||||
var pwQuery = Event.onCompletion(new GetDisplayPassword(vmDef, user),
|
||||
e -> {
|
||||
var data = vmDef.connectionFile(e.password().orElse(null),
|
||||
preferredIpVersion, deleteConnectionFile);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
channel.respond(new NotifyConletView(type(),
|
||||
model.getConletId(), "openConsole", data));
|
||||
});
|
||||
fire(pwQuery, vmChannel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doSetLocale(SetLocale event, ConsoleConnection channel,
|
||||
String conletId) throws Exception {
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ window.orgJDrupesVmOperatorVmMgmt.initView = (viewDom: HTMLElement,
|
|||
setup(_props: object) {
|
||||
const conletId: string
|
||||
= (<HTMLElement>viewDom.parentNode!).dataset["conletId"]!;
|
||||
const resourceBase = (<HTMLElement>viewDom).dataset.conletResourceBase;
|
||||
|
||||
const controller = reactive(new JGConsole.TableController([
|
||||
["name", "vmname"],
|
||||
|
|
@ -162,9 +163,9 @@ window.orgJDrupesVmOperatorVmMgmt.initView = (viewDom: HTMLElement,
|
|||
}
|
||||
|
||||
return {
|
||||
controller, vmInfos, filteredData, detailsByName, localize,
|
||||
shortDateTime, formatMemory, vmAction, cic, parseMemory,
|
||||
maximumCpus,
|
||||
controller, vmInfos, filteredData, detailsByName,
|
||||
resourceBase, localize, shortDateTime, formatMemory,
|
||||
vmAction, cic, parseMemory, maximumCpus,
|
||||
scopedId: (id: string) => { return idScope.scopedId(id); }
|
||||
};
|
||||
}
|
||||
|
|
@ -219,3 +220,20 @@ JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmmgmt.VmMgmt",
|
|||
Object.assign(vmSummary, summary);
|
||||
});
|
||||
|
||||
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmmgmt.VmMgmt",
|
||||
"openConsole", function(_conletId: string, data: string) {
|
||||
let target = document.getElementById(
|
||||
"org.jdrupes.vmoperator.vmmgt.VmMgmt.target");
|
||||
if (!target) {
|
||||
target = document.createElement("iframe");
|
||||
target.id = "org.jdrupes.vmoperator.vmmgt.VmMgmt.target";
|
||||
target.setAttribute("name", target.id);
|
||||
target.setAttribute("style", "display: none;");
|
||||
document.querySelector("body")!.append(target);
|
||||
}
|
||||
const url = "data:application/x-virt-viewer;base64,"
|
||||
+ window.btoa(data);
|
||||
window.open(url, target.id);
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -103,6 +103,10 @@
|
|||
.jdrupes-vmoperator-vmmgmt-view-action-list {
|
||||
white-space: nowrap;
|
||||
|
||||
& > * + * {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
padding: 0.25rem;
|
||||
|
||||
|
|
@ -110,4 +114,14 @@
|
|||
box-shadow: var(--darkening);
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
display: inline;
|
||||
height: 1.5em;
|
||||
vertical-align: top;
|
||||
|
||||
&[aria-disabled=''], &[aria-disabled='true'] {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue