Basically working.
This commit is contained in:
parent
d060a9334a
commit
6d5ba8829c
12 changed files with 493 additions and 148 deletions
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* VM-Operator
|
||||
* Copyright (C) 2023,2024 Michael N. Lipp
|
||||
* Copyright (C) 2023,2025 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
|
||||
|
|
@ -51,6 +51,7 @@ import org.jdrupes.vmoperator.common.K8sObserver;
|
|||
import org.jdrupes.vmoperator.common.VmDefinition;
|
||||
import org.jdrupes.vmoperator.common.VmDefinition.Permission;
|
||||
import org.jdrupes.vmoperator.common.VmPool;
|
||||
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.GetVms;
|
||||
|
|
@ -117,6 +118,7 @@ import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet;
|
|||
public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
||||
|
||||
private static final String VM_NAME_PROPERTY = "vmName";
|
||||
private static final String POOL_NAME_PROPERTY = "poolName";
|
||||
private static final String RENDERED
|
||||
= VmAccess.class.getName() + ".rendered";
|
||||
private static final String PENDING
|
||||
|
|
@ -281,33 +283,56 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
private void addMissingConlets(ConsoleConfigured event,
|
||||
ConsoleConnection connection, final Set<ResourceModel> rendered)
|
||||
throws InterruptedException {
|
||||
boolean foundMissing = false;
|
||||
var session = connection.session();
|
||||
for (var vmName : appPipeline.fire(new GetVms().accessibleFor(
|
||||
|
||||
// Evaluate missing VMs
|
||||
var missingVms = appPipeline.fire(new GetVms().accessibleFor(
|
||||
WebConsoleUtils.userFromSession(session)
|
||||
.map(ConsoleUser::getName).orElse(null),
|
||||
WebConsoleUtils.rolesFromSession(session).stream()
|
||||
.map(ConsoleRole::getName).toList()))
|
||||
.get().stream().map(d -> d.definition().name()).toList()) {
|
||||
if (rendered.stream()
|
||||
.anyMatch(r -> r.mode() == ResourceModel.Mode.VM
|
||||
&& r.name().equals(vmName))) {
|
||||
continue;
|
||||
}
|
||||
if (!foundMissing) {
|
||||
// Suspending to allow rendering of conlets to be noticed
|
||||
var failSafe = Components.schedule(t -> event.resumeHandling(),
|
||||
Duration.ofSeconds(1));
|
||||
event.suspendHandling(failSafe::cancel);
|
||||
connection.setAssociated(PENDING, event);
|
||||
foundMissing = true;
|
||||
}
|
||||
.get().stream().map(d -> d.definition().name())
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
missingVms.removeAll(rendered.stream()
|
||||
.filter(r -> r.mode() == ResourceModel.Mode.VM)
|
||||
.map(ResourceModel::name).toList());
|
||||
|
||||
// Evaluate missing pools
|
||||
var missingPools = appPipeline.fire(new GetPools().accessibleFor(
|
||||
WebConsoleUtils.userFromSession(session)
|
||||
.map(ConsoleUser::getName).orElse(null),
|
||||
WebConsoleUtils.rolesFromSession(session).stream()
|
||||
.map(ConsoleRole::getName).toList()))
|
||||
.get().stream().map(VmPool::name)
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
missingPools.removeAll(rendered.stream()
|
||||
.filter(r -> r.mode() == ResourceModel.Mode.POOL)
|
||||
.map(ResourceModel::name).toList());
|
||||
|
||||
// Nothing to do
|
||||
if (missingVms.isEmpty() && missingPools.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Suspending to allow rendering of conlets to be noticed
|
||||
var failSafe = Components.schedule(t -> event.resumeHandling(),
|
||||
Duration.ofSeconds(1));
|
||||
event.suspendHandling(failSafe::cancel);
|
||||
connection.setAssociated(PENDING, event);
|
||||
|
||||
// Create conlets for VMs and pools that haven't been rendered
|
||||
for (var vmName : missingVms) {
|
||||
fire(new AddConletRequest(event.event().event().renderSupport(),
|
||||
VmAccess.class.getName(),
|
||||
RenderMode.asSet(RenderMode.Preview))
|
||||
VmAccess.class.getName(), RenderMode.asSet(RenderMode.Preview))
|
||||
.addProperty(VM_NAME_PROPERTY, vmName),
|
||||
connection);
|
||||
}
|
||||
for (var poolName : missingPools) {
|
||||
fire(new AddConletRequest(event.event().event().renderSupport(),
|
||||
VmAccess.class.getName(), RenderMode.asSet(RenderMode.Preview))
|
||||
.addProperty(POOL_NAME_PROPERTY, poolName),
|
||||
connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -334,9 +359,14 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
protected Optional<ResourceModel> createNewState(AddConletRequest event,
|
||||
ConsoleConnection connection, String conletId) throws Exception {
|
||||
var model = new ResourceModel(conletId);
|
||||
model.setMode(ResourceModel.Mode.VM);
|
||||
model
|
||||
.setName((String) event.properties().get(VM_NAME_PROPERTY));
|
||||
var poolName = (String) event.properties().get(POOL_NAME_PROPERTY);
|
||||
if (poolName != null) {
|
||||
model.setMode(ResourceModel.Mode.POOL);
|
||||
model.setName(poolName);
|
||||
} else {
|
||||
model.setMode(ResourceModel.Mode.VM);
|
||||
model.setName((String) event.properties().get(VM_NAME_PROPERTY));
|
||||
}
|
||||
String jsonState = objectMapper.writeValueAsString(model);
|
||||
connection.respond(new KeyValueStoreUpdate().update(
|
||||
storagePath(connection.session(), model.getConletId()), jsonState));
|
||||
|
|
@ -428,18 +458,13 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
channel.setAssociated(PENDING, null);
|
||||
});
|
||||
|
||||
var session = channel.session();
|
||||
VmDefinition vmDef = null;
|
||||
if (model.mode() == ResourceModel.Mode.VM && model.name() != null) {
|
||||
// Remove conlet if VM definition has been removed
|
||||
// or user has not at least one permission
|
||||
Optional<VmData> vmData = appPipeline.fire(new GetVms()
|
||||
.withName(model.name()).accessibleFor(
|
||||
WebConsoleUtils.userFromSession(session)
|
||||
.map(ConsoleUser::getName).orElse(null),
|
||||
WebConsoleUtils.rolesFromSession(session).stream()
|
||||
.map(ConsoleRole::getName).toList()))
|
||||
.get().stream().findFirst();
|
||||
if (vmData.isEmpty()) {
|
||||
vmDef = getVmData(model, channel).map(VmData::definition)
|
||||
.orElse(null);
|
||||
if (vmDef == null) {
|
||||
channel.respond(
|
||||
new DeleteConlet(conletId, Collections.emptySet()));
|
||||
return Collections.emptySet();
|
||||
|
|
@ -453,11 +478,13 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
.fire(new GetPools().withName(model.name())).get()
|
||||
.stream().findFirst().orElse(null);
|
||||
if (pool == null
|
||||
|| poolPermissions(pool, channel.session()).isEmpty()) {
|
||||
|| permissions(pool, channel.session()).isEmpty()) {
|
||||
channel.respond(
|
||||
new DeleteConlet(conletId, Collections.emptySet()));
|
||||
return Collections.emptySet();
|
||||
}
|
||||
vmDef = getVmData(model, channel).map(VmData::definition)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
// Render
|
||||
|
|
@ -474,13 +501,32 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
if (!Strings.isNullOrEmpty(model.name())) {
|
||||
Optional.ofNullable(channel.session().get(RENDERED))
|
||||
.ifPresent(s -> ((Set<ResourceModel>) s).add(model));
|
||||
updateConfig(channel, model);
|
||||
updatePreview(channel, model, vmDef);
|
||||
}
|
||||
return EnumSet.of(RenderMode.Preview);
|
||||
}
|
||||
|
||||
private Set<Permission> vmPermissions(VmDefinition vmDef,
|
||||
Session session) {
|
||||
private Optional<VmData> getVmData(ResourceModel model,
|
||||
ConsoleConnection channel) throws InterruptedException {
|
||||
if (model.mode() == ResourceModel.Mode.VM) {
|
||||
// Get the VM data by name.
|
||||
var session = channel.session();
|
||||
return appPipeline.fire(new GetVms().withName(model.name())
|
||||
.accessibleFor(WebConsoleUtils.userFromSession(session)
|
||||
.map(ConsoleUser::getName).orElse(null),
|
||||
WebConsoleUtils.rolesFromSession(session).stream()
|
||||
.map(ConsoleRole::getName).toList()))
|
||||
.get().stream().findFirst();
|
||||
}
|
||||
|
||||
// Look for an (already) assigned VM
|
||||
var user = WebConsoleUtils.userFromSession(channel.session())
|
||||
.map(ConsoleUser::getName).orElse(null);
|
||||
return appPipeline.fire(new GetVms().assignedFrom(model.name())
|
||||
.assignedTo(user)).get().stream().findFirst();
|
||||
}
|
||||
|
||||
private Set<Permission> permissions(VmDefinition vmDef, Session session) {
|
||||
var user = WebConsoleUtils.userFromSession(session)
|
||||
.map(ConsoleUser::getName).orElse(null);
|
||||
var roles = WebConsoleUtils.rolesFromSession(session)
|
||||
|
|
@ -488,8 +534,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
return vmDef.permissionsFor(user, roles);
|
||||
}
|
||||
|
||||
private Set<Permission> poolPermissions(VmPool pool,
|
||||
Session session) {
|
||||
private Set<Permission> permissions(VmPool pool, Session session) {
|
||||
var user = WebConsoleUtils.userFromSession(session)
|
||||
.map(ConsoleUser::getName).orElse(null);
|
||||
var roles = WebConsoleUtils.rolesFromSession(session)
|
||||
|
|
@ -497,36 +542,60 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
return pool.permissionsFor(user, roles);
|
||||
}
|
||||
|
||||
private void updateConfig(ConsoleConnection channel, ResourceModel model)
|
||||
throws InterruptedException {
|
||||
channel.respond(new NotifyConletView(type(),
|
||||
model.getConletId(), "updateConfig", model.mode(), model.name()));
|
||||
updateVmDef(channel, model);
|
||||
private Set<Permission> permissions(ResourceModel model, Session session,
|
||||
VmPool pool, VmDefinition vmDef) throws InterruptedException {
|
||||
var user = WebConsoleUtils.userFromSession(session)
|
||||
.map(ConsoleUser::getName).orElse(null);
|
||||
var roles = WebConsoleUtils.rolesFromSession(session)
|
||||
.stream().map(ConsoleRole::getName).toList();
|
||||
Set<Permission> result = new HashSet<>();
|
||||
if (model.mode() == ResourceModel.Mode.POOL) {
|
||||
if (pool == null) {
|
||||
pool = appPipeline.fire(new GetPools()
|
||||
.withName(model.name())).get().stream().findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
if (pool != null) {
|
||||
result.addAll(pool.permissionsFor(user, roles));
|
||||
}
|
||||
}
|
||||
if (vmDef != null) {
|
||||
result.addAll(vmDef.permissionsFor(user, roles));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void updateVmDef(ConsoleConnection channel, ResourceModel model)
|
||||
throws InterruptedException {
|
||||
if (Strings.isNullOrEmpty(model.name())) {
|
||||
return;
|
||||
private void updatePreview(ConsoleConnection channel, ResourceModel model,
|
||||
VmDefinition vmDef) throws InterruptedException {
|
||||
channel.respond(new NotifyConletView(type(),
|
||||
model.getConletId(), "updateConfig", model.mode(), model.name()));
|
||||
updateVmDef(channel, model, vmDef);
|
||||
}
|
||||
|
||||
private void updateVmDef(ConsoleConnection channel, ResourceModel model,
|
||||
VmDefinition vmDef) throws InterruptedException {
|
||||
Map<String, Object> data = null;
|
||||
if (vmDef != null) {
|
||||
model.setAssignedVm(vmDef.name());
|
||||
try {
|
||||
data = Map.of("metadata",
|
||||
Map.of("namespace", vmDef.namespace(),
|
||||
"name", vmDef.name()),
|
||||
"spec", vmDef.spec(),
|
||||
"status", vmDef.getStatus(),
|
||||
"userPermissions",
|
||||
permissions(model, channel.session(), null, vmDef).stream()
|
||||
.map(VmDefinition.Permission::toString).toList());
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.log(Level.SEVERE, e,
|
||||
() -> "Failed to serialize VM definition");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
model.setAssignedVm(null);
|
||||
}
|
||||
appPipeline.fire(new GetVms().withName(model.name())).get().stream()
|
||||
.findFirst().map(d -> d.definition()).ifPresent(vmDef -> {
|
||||
try {
|
||||
var data = Map.of("metadata",
|
||||
Map.of("namespace", vmDef.namespace(),
|
||||
"name", vmDef.name()),
|
||||
"spec", vmDef.spec(),
|
||||
"status", vmDef.getStatus(),
|
||||
"userPermissions",
|
||||
vmPermissions(vmDef, channel.session()).stream()
|
||||
.map(VmDefinition.Permission::toString).toList());
|
||||
channel.respond(new NotifyConletView(type(),
|
||||
model.getConletId(), "updateVmDefinition", data));
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.log(Level.SEVERE, e,
|
||||
() -> "Failed to serialize VM definition");
|
||||
}
|
||||
});
|
||||
channel.respond(new NotifyConletView(type(),
|
||||
model.getConletId(), "updateVmDefinition", data));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -541,7 +610,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Track the VM definitions.
|
||||
* Track the VM definitions and update conlets.
|
||||
*
|
||||
* @param event the event
|
||||
* @param channel the channel
|
||||
|
|
@ -562,17 +631,43 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
for (var conletId : entry.getValue()) {
|
||||
var model = stateFromSession(connection.session(), conletId);
|
||||
if (model.isEmpty()
|
||||
|| model.get().mode() != ResourceModel.Mode.VM
|
||||
|| !Objects.areEqual(model.get().name(), vmDef.name())) {
|
||||
|| Strings.isNullOrEmpty(model.get().name())) {
|
||||
continue;
|
||||
}
|
||||
if (event.type() == K8sObserver.ResponseType.DELETED
|
||||
|| vmPermissions(vmDef, connection.session()).isEmpty()) {
|
||||
connection.respond(
|
||||
new DeleteConlet(conletId, Collections.emptySet()));
|
||||
if (model.get().mode() == ResourceModel.Mode.VM) {
|
||||
// Check if this VM is used by conlet
|
||||
if (!Objects.areEqual(model.get().name(), vmDef.name())) {
|
||||
continue;
|
||||
}
|
||||
if (event.type() == K8sObserver.ResponseType.DELETED
|
||||
|| permissions(vmDef, connection.session()).isEmpty()) {
|
||||
connection.respond(
|
||||
new DeleteConlet(conletId, Collections.emptySet()));
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
updateVmDef(connection, model.get());
|
||||
// Check if VM is used by pool conlet or to be assigned to
|
||||
// it
|
||||
var user
|
||||
= WebConsoleUtils.userFromSession(connection.session())
|
||||
.map(ConsoleUser::getName).orElse(null);
|
||||
var toBeUsedByConlet = vmDef.assignedFrom()
|
||||
.map(p -> p.equals(model.get().name())).orElse(false)
|
||||
&& vmDef.assignedTo().map(u -> u.equals(user))
|
||||
.orElse(false);
|
||||
if (!Objects.areEqual(model.get().assignedVm(),
|
||||
vmDef.name()) && !toBeUsedByConlet) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now unassigned if VM is deleted or no longer to be used
|
||||
if (event.type() == K8sObserver.ResponseType.DELETED
|
||||
|| !toBeUsedByConlet) {
|
||||
updateVmDef(connection, model.get(), null);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
updateVmDef(connection, model.get(), vmDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -598,7 +693,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
continue;
|
||||
}
|
||||
if (event.deleted()
|
||||
|| poolPermissions(event.vmPool(), connection.session())
|
||||
|| permissions(event.vmPool(), connection.session())
|
||||
.isEmpty()) {
|
||||
connection.respond(
|
||||
new DeleteConlet(conletId, Collections.emptySet()));
|
||||
|
|
@ -612,24 +707,36 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
"PMD.ConfusingArgumentToVarargsMethod", "PMD.NcssCount",
|
||||
"PMD.AvoidLiteralsInIfCondition" })
|
||||
protected void doUpdateConletState(NotifyConletModel event,
|
||||
ConsoleConnection channel, ResourceModel model)
|
||||
throws Exception {
|
||||
ConsoleConnection channel, ResourceModel model) throws Exception {
|
||||
event.stop();
|
||||
if ("selectedResource".equals(event.method())) {
|
||||
selectResource(event, channel, model);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle command for selected VM
|
||||
var vmData = appPipeline.fire(new GetVms().withName(model.name())).get()
|
||||
.stream().findFirst();
|
||||
Optional<VmData> vmData = getVmData(model, channel);
|
||||
if (vmData.isEmpty()) {
|
||||
return;
|
||||
if (model.mode() == ResourceModel.Mode.VM) {
|
||||
return;
|
||||
}
|
||||
if ("start".equals(event.method())) {
|
||||
// Assign a VM.
|
||||
var user = WebConsoleUtils.userFromSession(channel.session())
|
||||
.map(ConsoleUser::getName).orElse(null);
|
||||
vmData = Optional.ofNullable(appPipeline
|
||||
.fire(new AssignVm(model.name(), user)).get());
|
||||
if (vmData.isEmpty()) {
|
||||
// TODO message
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle command for selected VM
|
||||
var vmChannel = vmData.get().channel();
|
||||
var vmDef = vmData.get().definition();
|
||||
var vmName = vmDef.metadata().getName();
|
||||
var perms = vmPermissions(vmDef, channel.session());
|
||||
var perms = permissions(model, channel.session(), null, vmDef);
|
||||
var resourceBundle = resourceBundle(channel.locale());
|
||||
switch (event.method()) {
|
||||
case "start":
|
||||
|
|
@ -658,7 +765,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
.map(ConsoleUser::getName).orElse("");
|
||||
var pwQuery
|
||||
= Event.onCompletion(new GetDisplayPassword(vmDef, user),
|
||||
e -> openConsole(vmName, channel, model,
|
||||
e -> openConsole(vmDef, channel, model,
|
||||
e.password().orElse(null)));
|
||||
fire(pwQuery, vmChannel);
|
||||
}
|
||||
|
|
@ -680,33 +787,29 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
String jsonState = objectMapper.writeValueAsString(model);
|
||||
channel.respond(new KeyValueStoreUpdate().update(storagePath(
|
||||
channel.session(), model.getConletId()), jsonState));
|
||||
updateConfig(channel, model);
|
||||
updatePreview(channel, model,
|
||||
getVmData(model, channel).map(VmData::definition).orElse(null));
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.warning(() -> "Invalid resource type: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void openConsole(String vmName, ConsoleConnection connection,
|
||||
private void openConsole(VmDefinition vmDef, ConsoleConnection connection,
|
||||
ResourceModel model, String password) {
|
||||
VmDefinition vmDef;
|
||||
try {
|
||||
vmDef = appPipeline.fire(new GetVms().withName(model.name())).get()
|
||||
.stream().findFirst().map(VmData::definition).orElse(null);
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
if (vmDef == null) {
|
||||
return;
|
||||
}
|
||||
var addr = displayIp(vmDef);
|
||||
if (addr.isEmpty()) {
|
||||
logger.severe(() -> "Failed to find display IP for " + vmName);
|
||||
logger
|
||||
.severe(() -> "Failed to find display IP for " + vmDef.name());
|
||||
return;
|
||||
}
|
||||
var port = vmDef.<Number> fromVm("display", "spice", "port")
|
||||
.map(Number::longValue);
|
||||
if (port.isEmpty()) {
|
||||
logger.severe(() -> "No port defined for display of " + vmName);
|
||||
logger
|
||||
.severe(() -> "No port defined for display of " + vmDef.name());
|
||||
return;
|
||||
}
|
||||
StringBuffer data = new StringBuffer(100)
|
||||
|
|
@ -799,6 +902,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
|
||||
private Mode mode;
|
||||
private String name;
|
||||
private String assignedVm;
|
||||
|
||||
/**
|
||||
* Instantiates a new resource model.
|
||||
|
|
@ -809,6 +913,25 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
super(conletId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mode.
|
||||
*
|
||||
* @return the resourceType
|
||||
*/
|
||||
@JsonGetter("mode")
|
||||
public Mode mode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mode.
|
||||
*
|
||||
* @param mode the resource mode to set
|
||||
*/
|
||||
public void setMode(Mode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the resource name.
|
||||
*
|
||||
|
|
@ -829,29 +952,29 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the mode.
|
||||
* Gets the assigned vm.
|
||||
*
|
||||
* @return the resourceType
|
||||
* @return the string
|
||||
*/
|
||||
@JsonGetter("mode")
|
||||
public Mode mode() {
|
||||
return mode;
|
||||
@JsonGetter("assignedVm")
|
||||
public String assignedVm() {
|
||||
return assignedVm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mode.
|
||||
* Sets the assigned vm.
|
||||
*
|
||||
* @param mode the resource mode to set
|
||||
* @param name the assigned vm
|
||||
*/
|
||||
public void setMode(Mode mode) {
|
||||
this.mode = mode;
|
||||
public void setAssignedVm(String name) {
|
||||
this.assignedVm = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + java.util.Objects.hash(name, mode);
|
||||
result = prime * result + java.util.Objects.hash(mode, name);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -867,8 +990,8 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
return false;
|
||||
}
|
||||
ResourceModel other = (ResourceModel) obj;
|
||||
return java.util.Objects.equals(name, other.name)
|
||||
&& mode == other.mode;
|
||||
return mode == other.mode
|
||||
&& java.util.Objects.equals(name, other.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -878,6 +1001,5 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
|
|||
.append(", name=").append(name).append(']');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ interface Api {
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
vmName: string;
|
||||
vmDefinition: any;
|
||||
poolName: string;
|
||||
poolName: string | null;
|
||||
}
|
||||
|
||||
const localize = (key: string) => {
|
||||
|
|
@ -64,12 +64,21 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
|
|||
const previewApi: Api = reactive({
|
||||
vmName: "",
|
||||
vmDefinition: {},
|
||||
poolName: ""
|
||||
poolName: null
|
||||
});
|
||||
const poolName = computed(() => previewApi.poolName);
|
||||
const vmName = computed(() => previewApi.vmDefinition.name);
|
||||
const configured = computed(() => previewApi.vmDefinition.spec);
|
||||
const startable = computed(() => previewApi.vmDefinition.spec &&
|
||||
previewApi.vmDefinition.spec.vm.state !== 'Running'
|
||||
&& !previewApi.vmDefinition.running);
|
||||
const busy = computed(() => previewApi.vmDefinition.spec
|
||||
&& (previewApi.vmDefinition.spec.vm.state === 'Running'
|
||||
&& !previewApi.vmDefinition.running
|
||||
|| previewApi.vmDefinition.spec.vm.state === 'Stopped'
|
||||
&& previewApi.vmDefinition.running));
|
||||
const startable = computed(() => previewApi.vmDefinition.spec
|
||||
&& previewApi.vmDefinition.spec.vm.state !== 'Running'
|
||||
&& !previewApi.vmDefinition.running
|
||||
&& previewApi.vmDefinition.userPermissions.includes('start')
|
||||
|| previewApi.poolName !== null && !previewApi.vmDefinition.name);
|
||||
const stoppable = computed(() => previewApi.vmDefinition.spec &&
|
||||
previewApi.vmDefinition.spec.vm.state !== 'Stopped'
|
||||
&& previewApi.vmDefinition.running);
|
||||
|
|
@ -79,10 +88,8 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
|
|||
? previewApi.vmDefinition.userPermissions : []);
|
||||
|
||||
watch(previewApi, (api: Api) => {
|
||||
const name = api.vmName || api.poolName;
|
||||
if (name !== "") {
|
||||
JGConsole.instance.updateConletTitle(conletId, name);
|
||||
}
|
||||
JGConsole.instance.updateConletTitle(conletId,
|
||||
api.poolName || api.vmDefinition.name || "");
|
||||
});
|
||||
|
||||
provideApi(previewDom, previewApi);
|
||||
|
|
@ -91,16 +98,16 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
|
|||
JGConsole.notifyConletModel(conletId, action);
|
||||
};
|
||||
|
||||
return { localize, resourceBase, vmAction, configured,
|
||||
startable, stoppable, running, inUse, permissions };
|
||||
return { localize, resourceBase, vmAction, poolName, vmName,
|
||||
configured, busy, startable, stoppable, running, inUse,
|
||||
permissions };
|
||||
},
|
||||
template: `
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="2" style="position: relative"><span
|
||||
style="position: absolute;"
|
||||
:class="{ busy: configured && !startable && !stoppable }"
|
||||
style="position: absolute;" :class="{ busy: busy }"
|
||||
><img role=button :aria-disabled="!running
|
||||
|| !permissions.includes('accessConsole')"
|
||||
v-on:click="vmAction('openConsole')"
|
||||
|
|
@ -110,9 +117,12 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
|
|||
:title="localize('Open console')"></span><span
|
||||
style="visibility: hidden;"><img
|
||||
:src="resourceBase + 'computer.svg'"></span></td>
|
||||
<td v-if="!poolName" style="padding: 0;"></td>
|
||||
<td v-else>{{ vmName }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="jdrupes-vmoperator-vmaccess-preview-action-list">
|
||||
<span role="button"
|
||||
:aria-disabled="!startable || !permissions.includes('start')"
|
||||
<span role="button" :aria-disabled="!startable"
|
||||
tabindex="0" class="fa fa-play" :title="localize('Start VM')"
|
||||
v-on:click="vmAction('start')"></span>
|
||||
<span role="button"
|
||||
|
|
@ -130,9 +140,6 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
|
|||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`
|
||||
});
|
||||
|
|
@ -160,25 +167,29 @@ JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmaccess.VmAccess",
|
|||
});
|
||||
|
||||
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmaccess.VmAccess",
|
||||
"updateVmDefinition", function(conletId: string, vmDefinition: any) {
|
||||
"updateVmDefinition", function(conletId: string, vmDefinition: any | null) {
|
||||
const conlet = JGConsole.findConletPreview(conletId);
|
||||
if (!conlet) {
|
||||
return;
|
||||
}
|
||||
const api = getApi<Api>(conlet.element().querySelector(
|
||||
":scope .jdrupes-vmoperator-vmaccess-preview"))!;
|
||||
// Add some short-cuts for rendering
|
||||
vmDefinition.name = vmDefinition.metadata.name;
|
||||
vmDefinition.currentCpus = vmDefinition.status.cpus;
|
||||
vmDefinition.currentRam = Number(vmDefinition.status.ram);
|
||||
vmDefinition.usedBy = vmDefinition.status.consoleClient || "";
|
||||
for (const condition of vmDefinition.status.conditions) {
|
||||
if (condition.type === "Running") {
|
||||
vmDefinition.running = condition.status === "True";
|
||||
vmDefinition.runningConditionSince
|
||||
= new Date(condition.lastTransitionTime);
|
||||
break;
|
||||
if (vmDefinition) {
|
||||
// Add some short-cuts for rendering
|
||||
vmDefinition.name = vmDefinition.metadata.name;
|
||||
vmDefinition.currentCpus = vmDefinition.status.cpus;
|
||||
vmDefinition.currentRam = Number(vmDefinition.status.ram);
|
||||
vmDefinition.usedBy = vmDefinition.status.consoleClient || "";
|
||||
for (const condition of vmDefinition.status.conditions) {
|
||||
if (condition.type === "Running") {
|
||||
vmDefinition.running = condition.status === "True";
|
||||
vmDefinition.runningConditionSince
|
||||
= new Date(condition.lastTransitionTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vmDefinition = {};
|
||||
}
|
||||
api.vmDefinition = vmDefinition;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -49,7 +49,12 @@
|
|||
|
||||
.jdrupes-vmoperator-vmaccess.jdrupes-vmoperator-vmaccess-preview {
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
height: 3em;
|
||||
padding: 0.25rem;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue