Delay console opening for pool VMs.
This commit is contained in:
parent
c582763fbf
commit
5ad052ffe4
6 changed files with 191 additions and 119 deletions
|
|
@ -1,74 +0,0 @@
|
||||||
/*
|
|
||||||
* VM-Operator
|
|
||||||
* Copyright (C) 2024 Michael N. Lipp
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jdrupes.vmoperator.manager.events;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import org.jdrupes.vmoperator.common.VmDefinition;
|
|
||||||
import org.jgrapes.core.Event;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the current display secret and optionally updates it.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("PMD.DataClass")
|
|
||||||
public class GetDisplayPassword extends Event<String> {
|
|
||||||
|
|
||||||
private final VmDefinition vmDef;
|
|
||||||
private final String user;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates a new request for the display secret.
|
|
||||||
*
|
|
||||||
* @param vmDef the vm name
|
|
||||||
* @param user the requesting user
|
|
||||||
*/
|
|
||||||
public GetDisplayPassword(VmDefinition vmDef, String user) {
|
|
||||||
this.vmDef = vmDef;
|
|
||||||
this.user = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the vm definition.
|
|
||||||
*
|
|
||||||
* @return the vm definition
|
|
||||||
*/
|
|
||||||
public VmDefinition vmDefinition() {
|
|
||||||
return vmDef;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the id of the user who has requested the password.
|
|
||||||
*
|
|
||||||
* @return the string
|
|
||||||
*/
|
|
||||||
public String user() {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the password. May only be called when the event is completed.
|
|
||||||
*
|
|
||||||
* @return the optional
|
|
||||||
*/
|
|
||||||
public Optional<String> password() {
|
|
||||||
if (!isDone()) {
|
|
||||||
throw new IllegalStateException("Event is not done.");
|
|
||||||
}
|
|
||||||
return currentResults().stream().findFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* VM-Operator
|
||||||
|
* Copyright (C) 2024 Michael N. Lipp
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jdrupes.vmoperator.manager.events;
|
||||||
|
|
||||||
|
import org.jdrupes.vmoperator.common.VmDefinition;
|
||||||
|
import org.jgrapes.core.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current display secret and optionally updates it.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.DataClass")
|
||||||
|
public class PrepareConsole extends Event<String> {
|
||||||
|
|
||||||
|
private final VmDefinition vmDef;
|
||||||
|
private final String user;
|
||||||
|
private final boolean loginUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new request for the display secret.
|
||||||
|
* After handling the event, a result of `null` means that
|
||||||
|
* no password is needed. No result means that the console
|
||||||
|
* is not accessible.
|
||||||
|
*
|
||||||
|
* @param vmDef the vm name
|
||||||
|
* @param user the requesting user
|
||||||
|
* @param loginUser login the user
|
||||||
|
*/
|
||||||
|
public PrepareConsole(VmDefinition vmDef, String user,
|
||||||
|
boolean loginUser) {
|
||||||
|
this.vmDef = vmDef;
|
||||||
|
this.user = user;
|
||||||
|
this.loginUser = loginUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiates a new request for the display secret.
|
||||||
|
* After handling the event, a result of `null` means that
|
||||||
|
* no password is needed. No result means that the console
|
||||||
|
* is not accessible.
|
||||||
|
*
|
||||||
|
* @param vmDef the vm name
|
||||||
|
* @param user the requesting user
|
||||||
|
*/
|
||||||
|
public PrepareConsole(VmDefinition vmDef, String user) {
|
||||||
|
this(vmDef, user, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the vm definition.
|
||||||
|
*
|
||||||
|
* @return the vm definition
|
||||||
|
*/
|
||||||
|
public VmDefinition vmDefinition() {
|
||||||
|
return vmDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the id of the user who has requested the password.
|
||||||
|
*
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
public String user() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the user should be logged in before allowing access.
|
||||||
|
*
|
||||||
|
* @return the loginUser
|
||||||
|
*/
|
||||||
|
public boolean loginUser() {
|
||||||
|
return loginUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns `true` if a password is available. May only be called
|
||||||
|
* when the event is completed. Note that the password returned
|
||||||
|
* by {@link #password()} may be `null`, indicating that no password
|
||||||
|
* is needed.
|
||||||
|
*
|
||||||
|
* @return true, if successful
|
||||||
|
*/
|
||||||
|
public boolean passwordAvailable() {
|
||||||
|
if (!isDone()) {
|
||||||
|
throw new IllegalStateException("Event is not done.");
|
||||||
|
}
|
||||||
|
return !currentResults().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the password. May only be called when the event has been
|
||||||
|
* completed with a valid result (see {@link #passwordAvailable()}).
|
||||||
|
*
|
||||||
|
* @return the password. A value of `null` means that no password
|
||||||
|
* is required.
|
||||||
|
*/
|
||||||
|
public String password() {
|
||||||
|
if (!isDone() || currentResults().isEmpty()) {
|
||||||
|
throw new IllegalStateException("Event is not done.");
|
||||||
|
}
|
||||||
|
return currentResults().get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ import static org.jdrupes.vmoperator.manager.Constants.COMP_DISPLAY_SECRET;
|
||||||
import static org.jdrupes.vmoperator.manager.Constants.DATA_DISPLAY_PASSWORD;
|
import static org.jdrupes.vmoperator.manager.Constants.DATA_DISPLAY_PASSWORD;
|
||||||
import static org.jdrupes.vmoperator.manager.Constants.DATA_PASSWORD_EXPIRY;
|
import static org.jdrupes.vmoperator.manager.Constants.DATA_PASSWORD_EXPIRY;
|
||||||
import org.jdrupes.vmoperator.manager.events.ChannelDictionary;
|
import org.jdrupes.vmoperator.manager.events.ChannelDictionary;
|
||||||
import org.jdrupes.vmoperator.manager.events.GetDisplayPassword;
|
import org.jdrupes.vmoperator.manager.events.PrepareConsole;
|
||||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||||
import org.jgrapes.core.Channel;
|
import org.jgrapes.core.Channel;
|
||||||
|
|
@ -72,7 +72,7 @@ public class DisplaySecretMonitor
|
||||||
extends AbstractMonitor<V1Secret, V1SecretList, VmChannel> {
|
extends AbstractMonitor<V1Secret, V1SecretList, VmChannel> {
|
||||||
|
|
||||||
private int passwordValidity = 10;
|
private int passwordValidity = 10;
|
||||||
private final List<PendingGet> pendingGets
|
private final List<PendingGet> pendingPrepares
|
||||||
= Collections.synchronizedList(new LinkedList<>());
|
= Collections.synchronizedList(new LinkedList<>());
|
||||||
private final ChannelDictionary<String, VmChannel, ?> channelDictionary;
|
private final ChannelDictionary<String, VmChannel, ?> channelDictionary;
|
||||||
|
|
||||||
|
|
@ -178,49 +178,59 @@ public class DisplaySecretMonitor
|
||||||
*/
|
*/
|
||||||
@Handler
|
@Handler
|
||||||
@SuppressWarnings("PMD.StringInstantiation")
|
@SuppressWarnings("PMD.StringInstantiation")
|
||||||
public void onGetDisplaySecrets(GetDisplayPassword event, VmChannel channel)
|
public void onPrepareConsole(PrepareConsole event, VmChannel channel)
|
||||||
throws ApiException {
|
throws ApiException {
|
||||||
// Update console user in status
|
// Update console user in status
|
||||||
var vmStub = VmDefinitionStub.get(client(),
|
var vmStub = VmDefinitionStub.get(client(),
|
||||||
new GroupVersionKind(VM_OP_GROUP, "", VM_OP_KIND_VM),
|
new GroupVersionKind(VM_OP_GROUP, "", VM_OP_KIND_VM),
|
||||||
event.vmDefinition().namespace(), event.vmDefinition().name());
|
event.vmDefinition().namespace(), event.vmDefinition().name());
|
||||||
vmStub.updateStatus(from -> {
|
var optVmDef = vmStub.updateStatus(from -> {
|
||||||
JsonObject status = from.statusJson();
|
JsonObject status = from.statusJson();
|
||||||
status.addProperty("consoleUser", event.user());
|
status.addProperty("consoleUser", event.user());
|
||||||
return status;
|
return status;
|
||||||
});
|
});
|
||||||
|
if (optVmDef.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var vmDef = optVmDef.get();
|
||||||
|
|
||||||
|
// Check if access is possible
|
||||||
|
if (event.loginUser()
|
||||||
|
? !vmDef.conditionStatus("Booted").orElse(false)
|
||||||
|
: !vmDef.conditionStatus("Running").orElse(false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Look for secret
|
// Look for secret
|
||||||
ListOptions options = new ListOptions();
|
ListOptions options = new ListOptions();
|
||||||
options.setLabelSelector("app.kubernetes.io/name=" + APP_NAME + ","
|
options.setLabelSelector("app.kubernetes.io/name=" + APP_NAME + ","
|
||||||
+ "app.kubernetes.io/component=" + COMP_DISPLAY_SECRET + ","
|
+ "app.kubernetes.io/component=" + COMP_DISPLAY_SECRET + ","
|
||||||
+ "app.kubernetes.io/instance="
|
+ "app.kubernetes.io/instance=" + vmDef.name());
|
||||||
+ event.vmDefinition().metadata().getName());
|
var stubs = K8sV1SecretStub.list(client(), vmDef.namespace(), options);
|
||||||
var stubs = K8sV1SecretStub.list(client(),
|
|
||||||
event.vmDefinition().namespace(), options);
|
|
||||||
if (stubs.isEmpty()) {
|
if (stubs.isEmpty()) {
|
||||||
// No secret means no password for this VM wanted
|
// No secret means no password for this VM wanted
|
||||||
|
event.setResult(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var stub = stubs.iterator().next();
|
var stub = stubs.iterator().next();
|
||||||
|
|
||||||
// Check validity
|
// Check validity
|
||||||
var model = stub.model().get();
|
var secret = stub.model().get();
|
||||||
@SuppressWarnings("PMD.StringInstantiation")
|
@SuppressWarnings("PMD.StringInstantiation")
|
||||||
var expiry = Optional.ofNullable(model.getData()
|
var expiry = Optional.ofNullable(secret.getData()
|
||||||
.get(DATA_PASSWORD_EXPIRY)).map(b -> new String(b)).orElse(null);
|
.get(DATA_PASSWORD_EXPIRY)).map(b -> new String(b)).orElse(null);
|
||||||
if (model.getData().get(DATA_DISPLAY_PASSWORD) != null
|
if (secret.getData().get(DATA_DISPLAY_PASSWORD) != null
|
||||||
&& stillValid(expiry)) {
|
&& stillValid(expiry)) {
|
||||||
// Fixed secret, don't touch
|
// Fixed secret, don't touch
|
||||||
event.setResult(
|
event.setResult(
|
||||||
new String(model.getData().get(DATA_DISPLAY_PASSWORD)));
|
new String(secret.getData().get(DATA_DISPLAY_PASSWORD)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updatePassword(stub, event);
|
updatePassword(stub, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("PMD.StringInstantiation")
|
@SuppressWarnings("PMD.StringInstantiation")
|
||||||
private void updatePassword(K8sV1SecretStub stub, GetDisplayPassword event)
|
private void updatePassword(K8sV1SecretStub stub, PrepareConsole event)
|
||||||
throws ApiException {
|
throws ApiException {
|
||||||
SecureRandom random = null;
|
SecureRandom random = null;
|
||||||
try {
|
try {
|
||||||
|
|
@ -242,9 +252,9 @@ public class DisplaySecretMonitor
|
||||||
var pending = new PendingGet(event,
|
var pending = new PendingGet(event,
|
||||||
event.vmDefinition().displayPasswordSerial().orElse(0L) + 1,
|
event.vmDefinition().displayPasswordSerial().orElse(0L) + 1,
|
||||||
new CompletionLock(event, 1500));
|
new CompletionLock(event, 1500));
|
||||||
pendingGets.add(pending);
|
pendingPrepares.add(pending);
|
||||||
Event.onCompletion(event, e -> {
|
Event.onCompletion(event, e -> {
|
||||||
pendingGets.remove(pending);
|
pendingPrepares.remove(pending);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update, will (eventually) trigger confirmation
|
// Update, will (eventually) trigger confirmation
|
||||||
|
|
@ -273,9 +283,9 @@ public class DisplaySecretMonitor
|
||||||
@Handler
|
@Handler
|
||||||
@SuppressWarnings("PMD.AvoidSynchronizedStatement")
|
@SuppressWarnings("PMD.AvoidSynchronizedStatement")
|
||||||
public void onVmDefChanged(VmDefChanged event, Channel channel) {
|
public void onVmDefChanged(VmDefChanged event, Channel channel) {
|
||||||
synchronized (pendingGets) {
|
synchronized (pendingPrepares) {
|
||||||
String vmName = event.vmDefinition().name();
|
String vmName = event.vmDefinition().name();
|
||||||
for (var pending : pendingGets) {
|
for (var pending : pendingPrepares) {
|
||||||
if (pending.event.vmDefinition().name().equals(vmName)
|
if (pending.event.vmDefinition().name().equals(vmName)
|
||||||
&& event.vmDefinition().displayPasswordSerial()
|
&& event.vmDefinition().displayPasswordSerial()
|
||||||
.map(s -> s >= pending.expectedSerial).orElse(false)) {
|
.map(s -> s >= pending.expectedSerial).orElse(false)) {
|
||||||
|
|
@ -293,7 +303,7 @@ public class DisplaySecretMonitor
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("PMD.DataClass")
|
@SuppressWarnings("PMD.DataClass")
|
||||||
private static class PendingGet {
|
private static class PendingGet {
|
||||||
public final GetDisplayPassword event;
|
public final PrepareConsole event;
|
||||||
public final long expectedSerial;
|
public final long expectedSerial;
|
||||||
public final CompletionLock lock;
|
public final CompletionLock lock;
|
||||||
|
|
||||||
|
|
@ -303,7 +313,7 @@ public class DisplaySecretMonitor
|
||||||
* @param event the event
|
* @param event the event
|
||||||
* @param expectedSerial the expected serial
|
* @param expectedSerial the expected serial
|
||||||
*/
|
*/
|
||||||
public PendingGet(GetDisplayPassword event, long expectedSerial,
|
public PendingGet(PrepareConsole event, long expectedSerial,
|
||||||
CompletionLock lock) {
|
CompletionLock lock) {
|
||||||
super();
|
super();
|
||||||
this.event = event;
|
this.event = event;
|
||||||
|
|
|
||||||
|
|
@ -49,11 +49,11 @@ import org.jdrupes.vmoperator.common.VmDefinition;
|
||||||
import org.jdrupes.vmoperator.common.VmDefinition.Permission;
|
import org.jdrupes.vmoperator.common.VmDefinition.Permission;
|
||||||
import org.jdrupes.vmoperator.common.VmPool;
|
import org.jdrupes.vmoperator.common.VmPool;
|
||||||
import org.jdrupes.vmoperator.manager.events.AssignVm;
|
import org.jdrupes.vmoperator.manager.events.AssignVm;
|
||||||
import org.jdrupes.vmoperator.manager.events.GetDisplayPassword;
|
|
||||||
import org.jdrupes.vmoperator.manager.events.GetPools;
|
import org.jdrupes.vmoperator.manager.events.GetPools;
|
||||||
import org.jdrupes.vmoperator.manager.events.GetVms;
|
import org.jdrupes.vmoperator.manager.events.GetVms;
|
||||||
import org.jdrupes.vmoperator.manager.events.GetVms.VmData;
|
import org.jdrupes.vmoperator.manager.events.GetVms.VmData;
|
||||||
import org.jdrupes.vmoperator.manager.events.ModifyVm;
|
import org.jdrupes.vmoperator.manager.events.ModifyVm;
|
||||||
|
import org.jdrupes.vmoperator.manager.events.PrepareConsole;
|
||||||
import org.jdrupes.vmoperator.manager.events.ResetVm;
|
import org.jdrupes.vmoperator.manager.events.ResetVm;
|
||||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||||
|
|
@ -808,18 +808,23 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
||||||
Map.of("autoClose", 5_000, "type", "Warning")));
|
Map.of("autoClose", 5_000, "type", "Warning")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var pwQuery = Event.onCompletion(new GetDisplayPassword(vmDef, user),
|
var pwQuery = Event.onCompletion(new PrepareConsole(vmDef, user,
|
||||||
e -> {
|
model.mode() == ResourceModel.Mode.POOL),
|
||||||
vmDef.extra()
|
e -> gotPassword(channel, model, vmDef, e));
|
||||||
.map(xtra -> xtra.connectionFile(e.password().orElse(null),
|
|
||||||
preferredIpVersion, deleteConnectionFile))
|
|
||||||
.ifPresent(
|
|
||||||
cf -> channel.respond(new NotifyConletView(type(),
|
|
||||||
model.getConletId(), "openConsole", cf)));
|
|
||||||
});
|
|
||||||
fire(pwQuery, vmChannel);
|
fire(pwQuery, vmChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void gotPassword(ConsoleConnection channel, ResourceModel model,
|
||||||
|
VmDefinition vmDef, PrepareConsole event) {
|
||||||
|
if (!event.passwordAvailable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vmDef.extra().map(xtra -> xtra.connectionFile(event.password(),
|
||||||
|
preferredIpVersion, deleteConnectionFile))
|
||||||
|
.ifPresent(cf -> channel.respond(new NotifyConletView(type(),
|
||||||
|
model.getConletId(), "openConsole", cf)));
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition",
|
@SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition",
|
||||||
"PMD.UseLocaleWithCaseConversions" })
|
"PMD.UseLocaleWithCaseConversions" })
|
||||||
private void selectResource(NotifyConletModel event,
|
private void selectResource(NotifyConletModel event,
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,9 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
|
||||||
const configured = computed(() => previewApi.vmDefinition.spec);
|
const configured = computed(() => previewApi.vmDefinition.spec);
|
||||||
const busy = computed(() => previewApi.vmDefinition.spec
|
const busy = computed(() => previewApi.vmDefinition.spec
|
||||||
&& (previewApi.vmDefinition.spec.vm.state === 'Running'
|
&& (previewApi.vmDefinition.spec.vm.state === 'Running'
|
||||||
&& !previewApi.vmDefinition.running
|
&& (previewApi.poolName
|
||||||
|
? !previewApi.vmDefinition.booted
|
||||||
|
: !previewApi.vmDefinition.running)
|
||||||
|| previewApi.vmDefinition.spec.vm.state === 'Stopped'
|
|| previewApi.vmDefinition.spec.vm.state === 'Stopped'
|
||||||
&& previewApi.vmDefinition.running));
|
&& previewApi.vmDefinition.running));
|
||||||
const startable = computed(() => previewApi.vmDefinition.spec
|
const startable = computed(() => previewApi.vmDefinition.spec
|
||||||
|
|
@ -85,6 +87,7 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
|
||||||
previewApi.vmDefinition.spec.vm.state !== 'Stopped'
|
previewApi.vmDefinition.spec.vm.state !== 'Stopped'
|
||||||
&& previewApi.vmDefinition.running);
|
&& previewApi.vmDefinition.running);
|
||||||
const running = computed(() => previewApi.vmDefinition.running);
|
const running = computed(() => previewApi.vmDefinition.running);
|
||||||
|
const booted = computed(() => previewApi.vmDefinition.booted);
|
||||||
const inUse = computed(() => previewApi.vmDefinition.usedBy != '');
|
const inUse = computed(() => previewApi.vmDefinition.usedBy != '');
|
||||||
const permissions = computed(() => previewApi.permissions);
|
const permissions = computed(() => previewApi.permissions);
|
||||||
const osicon = computed(() => {
|
const osicon = computed(() => {
|
||||||
|
|
@ -120,8 +123,8 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
|
||||||
};
|
};
|
||||||
|
|
||||||
return { localize, resourceBase, vmAction, poolName, vmName,
|
return { localize, resourceBase, vmAction, poolName, vmName,
|
||||||
configured, busy, startable, stoppable, running, inUse,
|
configured, busy, startable, stoppable, running, booted,
|
||||||
permissions, osicon };
|
inUse, permissions, osicon };
|
||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<table>
|
<table>
|
||||||
|
|
@ -129,7 +132,8 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
|
||||||
<tr>
|
<tr>
|
||||||
<td rowspan="2" style="position: relative"><span
|
<td rowspan="2" style="position: relative"><span
|
||||||
style="position: absolute;" :class="{ busy: busy }"
|
style="position: absolute;" :class="{ busy: busy }"
|
||||||
><img role=button :aria-disabled="!running
|
><img role=button :aria-disabled="(poolName
|
||||||
|
? !booted : !running)
|
||||||
|| !permissions.includes('accessConsole')"
|
|| !permissions.includes('accessConsole')"
|
||||||
v-on:click="vmAction('openConsole')"
|
v-on:click="vmAction('openConsole')"
|
||||||
:src="resourceBase + (running
|
:src="resourceBase + (running
|
||||||
|
|
@ -206,14 +210,17 @@ JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmaccess.VmAccess",
|
||||||
vmDefinition.currentCpus = vmDefinition.status.cpus;
|
vmDefinition.currentCpus = vmDefinition.status.cpus;
|
||||||
vmDefinition.currentRam = Number(vmDefinition.status.ram);
|
vmDefinition.currentRam = Number(vmDefinition.status.ram);
|
||||||
vmDefinition.usedBy = vmDefinition.status.consoleClient || "";
|
vmDefinition.usedBy = vmDefinition.status.consoleClient || "";
|
||||||
for (const condition of vmDefinition.status.conditions) {
|
vmDefinition.status.conditions.forEach((condition: any) => {
|
||||||
if (condition.type === "Running") {
|
if (condition.type === "Running") {
|
||||||
vmDefinition.running = condition.status === "True";
|
vmDefinition.running = condition.status === "True";
|
||||||
vmDefinition.runningConditionSince
|
vmDefinition.runningConditionSince
|
||||||
= new Date(condition.lastTransitionTime);
|
= new Date(condition.lastTransitionTime);
|
||||||
break;
|
} else if (condition.type === "Booted") {
|
||||||
}
|
vmDefinition.booted = condition.status === "True";
|
||||||
|
vmDefinition.bootedConditionSince
|
||||||
|
= new Date(condition.lastTransitionTime);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
vmDefinition = {};
|
vmDefinition = {};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,8 @@ import org.jdrupes.vmoperator.common.VmDefinition;
|
||||||
import org.jdrupes.vmoperator.common.VmDefinition.Permission;
|
import org.jdrupes.vmoperator.common.VmDefinition.Permission;
|
||||||
import org.jdrupes.vmoperator.common.VmExtraData;
|
import org.jdrupes.vmoperator.common.VmExtraData;
|
||||||
import org.jdrupes.vmoperator.manager.events.ChannelTracker;
|
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.ModifyVm;
|
||||||
|
import org.jdrupes.vmoperator.manager.events.PrepareConsole;
|
||||||
import org.jdrupes.vmoperator.manager.events.ResetVm;
|
import org.jdrupes.vmoperator.manager.events.ResetVm;
|
||||||
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
import org.jdrupes.vmoperator.manager.events.VmChannel;
|
||||||
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
|
||||||
|
|
@ -483,15 +483,20 @@ public class VmMgmt extends FreeMarkerConlet<VmMgmt.VmsModel> {
|
||||||
Map.of("autoClose", 5_000, "type", "Warning")));
|
Map.of("autoClose", 5_000, "type", "Warning")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var pwQuery = Event.onCompletion(new GetDisplayPassword(vmDef, user),
|
var pwQuery = Event.onCompletion(new PrepareConsole(vmDef, user),
|
||||||
e -> {
|
e -> gotPassword(channel, model, vmDef, e));
|
||||||
vmDef.extra().map(xtra -> xtra.connectionFile(
|
fire(pwQuery, vmChannel);
|
||||||
e.password().orElse(null), preferredIpVersion,
|
}
|
||||||
deleteConnectionFile)).ifPresent(
|
|
||||||
|
private void gotPassword(ConsoleConnection channel, VmsModel model,
|
||||||
|
VmDefinition vmDef, PrepareConsole event) {
|
||||||
|
if (!event.passwordAvailable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vmDef.extra().map(xtra -> xtra.connectionFile(event.password(),
|
||||||
|
preferredIpVersion, deleteConnectionFile)).ifPresent(
|
||||||
cf -> channel.respond(new NotifyConletView(type(),
|
cf -> channel.respond(new NotifyConletView(type(),
|
||||||
model.getConletId(), "openConsole", cf)));
|
model.getConletId(), "openConsole", cf)));
|
||||||
});
|
|
||||||
fire(pwQuery, vmChannel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue