Pool configurable in GUI.

This commit is contained in:
Michael Lipp 2024-12-01 13:41:18 +01:00
parent 77cfcff2ed
commit 367aebeee5
6 changed files with 296 additions and 134 deletions

View file

@ -5,17 +5,33 @@
data-jgwc-on-unload="JGConsole.jgwc.unmountVueApps">
<form :id="formId" ref="formDom" onsubmit="return false;">
<section>
<span>{{ localize("Select VM") }}</span>
<p>
<label>
<span>{{ localize("VM") }}</span>
<select v-model="vmNameInput">
<#list vmNames as name>
<option value="${name}">${name}</option>
</#list>
</select>
</label>
</p>
<fieldset>
<legend>{{ localize("Select VM or pool") }}</legend>
<ul>
<li>
<label>
<input v-model="resource" type="radio" name="resource" value="vm">
<span>{{ localize("VM") }}</span>
<select v-model="vmNameInput" :disabled="resource !== 'vm'">
<#list vmNames as name>
<option value="${name}">${name}</option>
</#list>
</select>
</label>
</li>
<li>
<label>
<input v-model="resource" type="radio" name="resource" value="pool">
<span>{{ localize("Pool") }}</span>
<select v-model="poolNameInput" :disabled="resource !== 'pool'">
<#list poolNames as name>
<option value="${name}">${name}</option>
</#list>
</select>
</label>
</li>
</ul>
</fieldset>
</section>
</form>
</div>

View file

@ -1,7 +1,7 @@
conletName = VM-Zugriff
okayLabel = Anwenden und Schließen
Select\ VM = VM auswählen
Select\ VM\ or\ pool = VM oder Pool auswählen
Start\ VM = VM starten
Stop\ VM = VM anhalten

View file

@ -38,6 +38,7 @@ import java.time.Duration;
import java.util.Base64;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -49,13 +50,14 @@ import java.util.stream.Collectors;
import org.bouncycastle.util.Objects;
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.ChannelTracker;
import org.jdrupes.vmoperator.manager.events.GetDisplayPassword;
import org.jdrupes.vmoperator.manager.events.ModifyVm;
import org.jdrupes.vmoperator.manager.events.ResetVm;
import org.jdrupes.vmoperator.manager.events.VmChannel;
import org.jdrupes.vmoperator.manager.events.VmDefChanged;
import org.jdrupes.vmoperator.manager.events.VmPoolChanged;
import org.jgrapes.core.Channel;
import org.jgrapes.core.Components;
import org.jgrapes.core.Event;
@ -107,7 +109,7 @@ import org.jgrapes.webconsole.base.freemarker.FreeMarkerConlet;
*/
@SuppressWarnings({ "PMD.DataflowAnomalyAnalysis", "PMD.ExcessiveImports",
"PMD.CouplingBetweenObjects", "PMD.GodClass", "PMD.TooManyMethods" })
public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
public class VmAccess extends FreeMarkerConlet<VmAccess.ResourceModel> {
private static final String VM_NAME_PROPERTY = "vmName";
private static final String RENDERED
@ -126,6 +128,8 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
private Set<String> syncUsers = Collections.emptySet();
private Set<String> syncRoles = Collections.emptySet();
private boolean deleteConnectionFile = true;
@SuppressWarnings("PMD.UseConcurrentHashMap")
private final Map<String, VmPool> vmPools = new HashMap<>();
/**
* The periodically generated update event.
@ -247,20 +251,28 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
* @throws InterruptedException the interrupted exception
*/
@Handler
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
public void onConsoleConfigured(ConsoleConfigured event,
ConsoleConnection connection) throws InterruptedException,
IOException {
@SuppressWarnings("unchecked")
final var rendered = (Set<String>) connection.session().get(RENDERED);
final var rendered
= (Set<ResourceModel>) connection.session().get(RENDERED);
connection.session().remove(RENDERED);
if (!syncPreviews(connection.session())) {
return;
}
addMissingVms(event, connection, rendered);
}
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
private void addMissingVms(ConsoleConfigured event,
ConsoleConnection connection, final Set<ResourceModel> rendered) {
boolean foundMissing = false;
for (var vmName : accessibleVms(connection)) {
if (rendered.contains(vmName)) {
if (rendered.stream()
.anyMatch(r -> r.type() == ResourceModel.Type.VM
&& r.name().equals(vmName))) {
continue;
}
if (!foundMissing) {
@ -300,13 +312,12 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
}
@Override
protected Optional<ViewerModel> createNewState(AddConletRequest event,
protected Optional<ResourceModel> createNewState(AddConletRequest event,
ConsoleConnection connection, String conletId) throws Exception {
var model = new ViewerModel(conletId);
model.vmName = (String) event.properties().get(VM_NAME_PROPERTY);
if (model.vmName != null) {
model.setGenerated(true);
}
var model = new ResourceModel(conletId);
model.setType(ResourceModel.Type.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));
@ -314,9 +325,9 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
}
@Override
protected Optional<ViewerModel> createStateRepresentation(Event<?> event,
protected Optional<ResourceModel> createStateRepresentation(Event<?> event,
ConsoleConnection connection, String conletId) throws Exception {
var model = new ViewerModel(conletId);
var model = new ResourceModel(conletId);
String jsonState = objectMapper.writeValueAsString(model);
connection.respond(new KeyValueStoreUpdate().update(
storagePath(connection.session(), model.getConletId()), jsonState));
@ -325,7 +336,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
@Override
@SuppressWarnings("PMD.EmptyCatchBlock")
protected Optional<ViewerModel> recreateState(Event<?> event,
protected Optional<ResourceModel> recreateState(Event<?> event,
ConsoleConnection channel, String conletId) throws Exception {
KeyValueStoreQuery query = new KeyValueStoreQuery(
storagePath(channel.session(), conletId), channel);
@ -334,8 +345,8 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
if (!query.results().isEmpty()) {
var json = query.results().get(0).values().stream().findFirst()
.get();
ViewerModel model
= objectMapper.readValue(json, ViewerModel.class);
ResourceModel model
= objectMapper.readValue(json, ResourceModel.class);
return Optional.of(model);
}
} catch (InterruptedException e) {
@ -347,58 +358,23 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
}
@Override
@SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops", "unchecked" })
@SuppressWarnings({ "PMD.AvoidInstantiatingObjectsInLoops" })
protected Set<RenderMode> doRenderConlet(RenderConletRequestBase<?> event,
ConsoleConnection channel, String conletId, ViewerModel model)
ConsoleConnection channel, String conletId, ResourceModel model)
throws Exception {
if (event.renderAs().contains(RenderMode.Preview)) {
return renderPreview(event, channel, conletId, model);
}
// Render edit
ResourceBundle resourceBundle = resourceBundle(channel.locale());
Set<RenderMode> renderedAs = EnumSet.noneOf(RenderMode.class);
if (event.renderAs().contains(RenderMode.Preview)) {
channel.associated(PENDING, Event.class)
.ifPresent(e -> {
e.resumeHandling();
channel.setAssociated(PENDING, null);
});
// Remove conlet if definition has been removed
if (model.vmName() != null
&& !channelTracker.associated(model.vmName()).isPresent()) {
channel.respond(
new DeleteConlet(conletId, Collections.emptySet()));
return Collections.emptySet();
}
// Don't render if user has not at least one permission
if (model.vmName() != null
&& channelTracker.associated(model.vmName())
.map(d -> permissions(d, channel.session()).isEmpty())
.orElse(true)) {
return Collections.emptySet();
}
// Render
Template tpl
= freemarkerConfig().getTemplate("VmAccess-preview.ftl.html");
channel.respond(new RenderConlet(type(), conletId,
processTemplate(event, tpl,
fmModel(event, channel, conletId, model)))
.setRenderAs(
RenderMode.Preview.addModifiers(event.renderAs()))
.setSupportedModes(syncPreviews(channel.session())
? MODES_FOR_GENERATED
: MODES));
renderedAs.add(RenderMode.Preview);
if (!Strings.isNullOrEmpty(model.vmName())) {
Optional.ofNullable(channel.session().get(RENDERED))
.ifPresent(s -> ((Set<String>) s).add(model.vmName()));
updateConfig(channel, model);
}
}
if (event.renderAs().contains(RenderMode.Edit)) {
Template tpl = freemarkerConfig()
.getTemplate("VmAccess-edit.ftl.html");
var fmModel = fmModel(event, channel, conletId, model);
fmModel.put("vmNames", accessibleVms(channel));
fmModel.put("poolNames", accessiblePools(channel));
channel.respond(new OpenModalDialog(type(), conletId,
processTemplate(event, tpl, fmModel))
.addOption("cancelable", true)
@ -408,13 +384,69 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
return renderedAs;
}
@SuppressWarnings("unchecked")
private Set<RenderMode> renderPreview(RenderConletRequestBase<?> event,
ConsoleConnection channel, String conletId, ResourceModel model)
throws TemplateNotFoundException, MalformedTemplateNameException,
ParseException, IOException {
channel.associated(PENDING, Event.class)
.ifPresent(e -> {
e.resumeHandling();
channel.setAssociated(PENDING, null);
});
if (model.type() == ResourceModel.Type.VM && model.name() != null) {
// Remove conlet if VM definition has been removed
// or user has not at least one permission
Optional<VmDefinition> vmDef
= channelTracker.associated(model.name());
if (vmDef.isEmpty()
|| vmPermissions(vmDef.get(), channel.session()).isEmpty()) {
channel.respond(
new DeleteConlet(conletId, Collections.emptySet()));
return Collections.emptySet();
}
}
if (model.type() == ResourceModel.Type.POOL && model.name() != null) {
// Remove conlet if pool definition has been removed
// or user has not at least one permission
VmPool pool = vmPools.get(model.name());
if (pool == null
|| poolPermissions(pool, channel.session()).isEmpty()) {
channel.respond(
new DeleteConlet(conletId, Collections.emptySet()));
return Collections.emptySet();
}
}
// Render
Template tpl
= freemarkerConfig().getTemplate("VmAccess-preview.ftl.html");
channel.respond(new RenderConlet(type(), conletId,
processTemplate(event, tpl,
fmModel(event, channel, conletId, model)))
.setRenderAs(
RenderMode.Preview.addModifiers(event.renderAs()))
.setSupportedModes(syncPreviews(channel.session())
? MODES_FOR_GENERATED
: MODES));
if (!Strings.isNullOrEmpty(model.name())) {
Optional.ofNullable(channel.session().get(RENDERED))
.ifPresent(s -> ((Set<ResourceModel>) s).add(model));
updateConfig(channel, model);
}
return EnumSet.of(RenderMode.Preview);
}
private List<String> accessibleVms(ConsoleConnection channel) {
return channelTracker.associated().stream()
.filter(d -> !permissions(d, channel.session()).isEmpty())
.filter(d -> !vmPermissions(d, channel.session()).isEmpty())
.map(d -> d.getMetadata().getName()).sorted().toList();
}
private Set<Permission> permissions(VmDefinition vmDef, Session session) {
private Set<VmDefinition.Permission> vmPermissions(VmDefinition vmDef,
Session session) {
var user = WebConsoleUtils.userFromSession(session)
.map(ConsoleUser::getName).orElse(null);
var roles = WebConsoleUtils.rolesFromSession(session)
@ -422,17 +454,32 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
return vmDef.permissionsFor(user, roles);
}
private void updateConfig(ConsoleConnection channel, ViewerModel model) {
private List<String> accessiblePools(ConsoleConnection channel) {
return vmPools.values().stream()
.filter(d -> !poolPermissions(d, channel.session()).isEmpty())
.map(d -> d.name()).sorted().toList();
}
private Set<VmPool.Permission> poolPermissions(VmPool pool,
Session session) {
var user = WebConsoleUtils.userFromSession(session)
.map(ConsoleUser::getName).orElse(null);
var roles = WebConsoleUtils.rolesFromSession(session)
.stream().map(ConsoleRole::getName).toList();
return pool.permissionsFor(user, roles);
}
private void updateConfig(ConsoleConnection channel, ResourceModel model) {
channel.respond(new NotifyConletView(type(),
model.getConletId(), "updateConfig", model.vmName()));
model.getConletId(), "updateConfig", model.type(), model.name()));
updateVmDef(channel, model);
}
private void updateVmDef(ConsoleConnection channel, ViewerModel model) {
if (Strings.isNullOrEmpty(model.vmName())) {
private void updateVmDef(ConsoleConnection channel, ResourceModel model) {
if (Strings.isNullOrEmpty(model.name())) {
return;
}
channelTracker.value(model.vmName()).ifPresent(item -> {
channelTracker.value(model.name()).ifPresent(item -> {
try {
var vmDef = item.associated();
var data = Map.of("metadata",
@ -441,8 +488,8 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
"spec", vmDef.spec(),
"status", vmDef.getStatus(),
"userPermissions",
permissions(vmDef, channel.session()).stream()
.map(Permission::toString).toList());
vmPermissions(vmDef, channel.session()).stream()
.map(VmDefinition.Permission::toString).toList());
channel.respond(new NotifyConletView(type(),
model.getConletId(), "updateVmDefinition", data));
} catch (JsonSyntaxException e) {
@ -454,7 +501,8 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
@Override
protected void doConletDeleted(ConletDeleted event,
ConsoleConnection channel, String conletId, ViewerModel conletState)
ConsoleConnection channel, String conletId,
ResourceModel conletState)
throws Exception {
if (event.renderModes().isEmpty()) {
channel.respond(new KeyValueStoreUpdate().delete(
@ -487,7 +535,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
for (var conletId : entry.getValue()) {
var model = stateFromSession(connection.session(), conletId);
if (model.isEmpty()
|| !Objects.areEqual(model.get().vmName(), vmName)) {
|| !Objects.areEqual(model.get().name(), vmName)) {
continue;
}
if (event.type() == K8sObserver.ResponseType.DELETED) {
@ -500,21 +548,36 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
}
}
/**
* On vm pool changed.
*
* @param event the event
* @param channel the channel
*/
@Handler(namedChannels = "manager")
public void onVmPoolChanged(VmPoolChanged event) {
if (event.deleted()) {
vmPools.remove(event.vmPool().name());
return;
}
vmPools.put(event.vmPool().name(), event.vmPool());
}
@Override
@SuppressWarnings({ "PMD.AvoidDecimalLiteralsInBigDecimalConstructor",
"PMD.ConfusingArgumentToVarargsMethod", "PMD.NcssCount",
"PMD.AvoidLiteralsInIfCondition" })
protected void doUpdateConletState(NotifyConletModel event,
ConsoleConnection channel, ViewerModel model)
ConsoleConnection channel, ResourceModel model)
throws Exception {
event.stop();
if ("selectedVm".equals(event.method())) {
selectVm(event, channel, model);
if ("selectedResource".equals(event.method())) {
selectResource(event, channel, model);
return;
}
// Handle command for selected VM
var both = Optional.ofNullable(model.vmName())
var both = Optional.ofNullable(model.name())
.flatMap(vm -> channelTracker.value(vm));
if (both.isEmpty()) {
return;
@ -522,31 +585,31 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
var vmChannel = both.get().channel();
var vmDef = both.get().associated();
var vmName = vmDef.metadata().getName();
var perms = permissions(vmDef, channel.session());
var perms = vmPermissions(vmDef, channel.session());
var resourceBundle = resourceBundle(channel.locale());
switch (event.method()) {
case "start":
if (perms.contains(Permission.START)) {
if (perms.contains(VmDefinition.Permission.START)) {
fire(new ModifyVm(vmName, "state", "Running", vmChannel));
}
break;
case "stop":
if (perms.contains(Permission.STOP)) {
if (perms.contains(VmDefinition.Permission.STOP)) {
fire(new ModifyVm(vmName, "state", "Stopped", vmChannel));
}
break;
case "reset":
if (perms.contains(Permission.RESET)) {
if (perms.contains(VmDefinition.Permission.RESET)) {
confirmReset(event, channel, model, resourceBundle);
}
break;
case "resetConfirmed":
if (perms.contains(Permission.RESET)) {
if (perms.contains(VmDefinition.Permission.RESET)) {
fire(new ResetVm(vmName), vmChannel);
}
break;
case "openConsole":
if (perms.contains(Permission.ACCESS_CONSOLE)) {
if (perms.contains(VmDefinition.Permission.ACCESS_CONSOLE)) {
var user = WebConsoleUtils.userFromSession(channel.session())
.map(ConsoleUser::getName).orElse("");
var pwQuery
@ -561,17 +624,26 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
}
}
private void selectVm(NotifyConletModel event, ConsoleConnection channel,
ViewerModel model) throws JsonProcessingException {
model.setVmName(event.param(0));
String jsonState = objectMapper.writeValueAsString(model);
channel.respond(new KeyValueStoreUpdate().update(storagePath(
channel.session(), model.getConletId()), jsonState));
updateConfig(channel, model);
@SuppressWarnings({ "PMD.AvoidLiteralsInIfCondition",
"PMD.UseLocaleWithCaseConversions" })
private void selectResource(NotifyConletModel event,
ConsoleConnection channel, ResourceModel model)
throws JsonProcessingException {
try {
model.setType(ResourceModel.Type
.valueOf(event.<String> param(0).toUpperCase()));
model.setName(event.param(1));
String jsonState = objectMapper.writeValueAsString(model);
channel.respond(new KeyValueStoreUpdate().update(storagePath(
channel.session(), model.getConletId()), jsonState));
updateConfig(channel, model);
} catch (IllegalArgumentException e) {
logger.warning(() -> "Invalid resource type: " + e.getMessage());
}
}
private void openConsole(String vmName, ConsoleConnection connection,
ViewerModel model, String password) {
ResourceModel model, String password) {
var vmDef = channelTracker.associated(vmName).orElse(null);
if (vmDef == null) {
return;
@ -642,7 +714,7 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
}
private void confirmReset(NotifyConletModel event,
ConsoleConnection channel, ViewerModel model,
ConsoleConnection channel, ResourceModel model,
ResourceBundle resourceBundle) throws TemplateNotFoundException,
MalformedTemplateNameException, ParseException, IOException {
Template tpl = freemarkerConfig()
@ -662,58 +734,97 @@ public class VmAccess extends FreeMarkerConlet<VmAccess.ViewerModel> {
}
/**
* The Class VmsModel.
* The Class AccessModel.
*/
@SuppressWarnings("PMD.DataClass")
public static class ViewerModel extends ConletBaseModel {
private String vmName;
private boolean generated;
public static class ResourceModel extends ConletBaseModel {
/**
* Instantiates a new vms model.
* The Enum ResourceType.
*/
@SuppressWarnings("PMD.ShortVariable")
public enum Type {
VM, POOL
}
private Type type;
private String name;
/**
* Instantiates a new resource model.
*
* @param conletId the conlet id
*/
public ViewerModel(@JsonProperty("conletId") String conletId) {
public ResourceModel(@JsonProperty("conletId") String conletId) {
super(conletId);
}
/**
* Gets the vm name.
* Gets the resource name.
*
* @return the vmName
* @return the string
*/
@JsonGetter("vmName")
public String vmName() {
return vmName;
@JsonGetter("name")
public String name() {
return name;
}
/**
* Sets the vm name.
* Sets the name.
*
* @param vmName the vmName to set
* @param name the resource name to set
*/
public void setVmName(String vmName) {
this.vmName = vmName;
public void setName(String name) {
this.name = name;
}
/**
* Checks if is generated.
*
* @return the generated
* @return the resourceType
*/
public boolean isGenerated() {
return generated;
@JsonGetter("type")
public Type type() {
return type;
}
/**
* Sets the generated.
* Sets the type.
*
* @param generated the generated to set
* @param type the resource type to set
*/
public void setGenerated(boolean generated) {
this.generated = generated;
public void setType(Type type) {
this.type = type;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + java.util.Objects.hash(name, type);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ResourceModel other = (ResourceModel) obj;
return java.util.Objects.equals(name, other.name)
&& type == other.type;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(50);
builder.append("AccessModel [resourceType=").append(type)
.append(", resourceName=").append(name).append(']');
return builder.toString();
}
}

View file

@ -44,6 +44,7 @@ interface Api {
/* eslint-disable @typescript-eslint/no-explicit-any */
vmName: string;
vmDefinition: any;
poolName: string;
}
const localize = (key: string) => {
@ -62,7 +63,8 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
const previewApi: Api = reactive({
vmName: "",
vmDefinition: {}
vmDefinition: {},
poolName: ""
});
const configured = computed(() => previewApi.vmDefinition.spec);
const startable = computed(() => previewApi.vmDefinition.spec &&
@ -76,7 +78,8 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
const permissions = computed(() => previewApi.vmDefinition.spec
? previewApi.vmDefinition.userPermissions : []);
watch(() => previewApi.vmName, (name: string) => {
watch(previewApi, (api: Api) => {
const name = api.vmName || api.poolName;
if (name !== "") {
JGConsole.instance.updateConletTitle(conletId, name);
}
@ -139,14 +142,21 @@ window.orgJDrupesVmOperatorVmAccess.initPreview = (previewDom: HTMLElement,
};
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmaccess.VmAccess",
"updateConfig", function(conletId: string, vmName: string) {
"updateConfig",
function(conletId: string, type: string, resource: string) {
const conlet = JGConsole.findConletPreview(conletId);
if (!conlet) {
return;
}
const api = getApi<Api>(conlet.element().querySelector(
":scope .jdrupes-vmoperator-vmaccess-preview"))!;
api.vmName = vmName;
if (type === "VM") {
api.vmName = resource;
api.poolName = "";
} else {
api.poolName = resource;
api.vmName = "";
}
});
JGConsole.registerConletFunction("org.jdrupes.vmoperator.vmaccess.VmAccess",
@ -203,19 +213,36 @@ window.orgJDrupesVmOperatorVmAccess.initEdit = (dialogDom: HTMLElement,
l10nBundles, JGWC.lang()!, key);
};
const resource = ref<string>("vm");
const vmNameInput = ref<string>("");
const poolNameInput = ref<string>("");
watch(resource, (resource: string) => {
if (resource === "vm") {
poolNameInput.value = "";
}
if (resource === "pool")
vmNameInput.value = "";
});
const conletId = (<HTMLElement>dialogDom.closest(
"[data-conlet-id]")!).dataset["conletId"]!;
const conlet = JGConsole.findConletPreview(conletId);
if (conlet) {
const api = getApi<Api>(conlet.element().querySelector(
":scope .jdrupes-vmoperator-vmaccess-preview"))!;
if (api.poolName) {
resource.value = "pool";
}
vmNameInput.value = api.vmName;
poolNameInput.value = api.poolName;
}
provideApi(dialogDom, vmNameInput);
provideApi(dialogDom, { resource: () => resource.value,
name: () => resource.value === "vm"
? vmNameInput.value : poolNameInput.value });
return { formId, localize, vmNameInput };
return { formId, localize, resource, vmNameInput, poolNameInput };
}
});
app.use(JgwcPlugin);
@ -229,8 +256,9 @@ window.orgJDrupesVmOperatorVmAccess.applyEdit =
}
const conletId = (<HTMLElement>dialogDom.closest("[data-conlet-id]")!)
.dataset["conletId"]!;
const vmName = getApi<ref<string>>(dialogDom!)!.value;
JGConsole.notifyConletModel(conletId, "selectedVm", vmName);
const editApi = getApi<ref<string>>(dialogDom!)!;
JGConsole.notifyConletModel(conletId, "selectedResource", editApi.resource(),
editApi.name());
}
window.orgJDrupesVmOperatorVmAccess.confirmReset =

View file

@ -77,6 +77,11 @@
}
.jdrupes-vmoperator-vmaccess.jdrupes-vmoperator-vmaccess-edit {
fieldset ul li {
margin-top: 0.5em;
}
select {
width: 15em;
}