EMS: Applied changes from main repo
Change-Id: I91c7cd475c0a1139f73ff76b71b90ab3aae1445b
This commit is contained in:
parent
c773bc1328
commit
8212cd655d
@ -45,11 +45,12 @@
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.rauschig/jarchivelib -->
|
||||
<!--XXX: Commented a not used feature with dependency with vulnerability
|
||||
<dependency>
|
||||
<groupId>org.rauschig</groupId>
|
||||
<artifactId>jarchivelib</artifactId>
|
||||
<version>1.2.0</version>
|
||||
</dependency>
|
||||
</dependency>-->
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml -->
|
||||
<dependency>
|
||||
@ -61,6 +62,34 @@
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Fabric8 Kubernetes client -->
|
||||
<dependency>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>kubernetes-client</artifactId>
|
||||
<version>6.10.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.squareup.okio</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okio</groupId>
|
||||
<artifactId>okio</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -9,30 +9,32 @@
|
||||
|
||||
package gr.iccs.imu.ems.baguette.client.install;
|
||||
|
||||
import gr.iccs.imu.ems.baguette.client.install.installer.K8sClientInstaller;
|
||||
import gr.iccs.imu.ems.baguette.client.install.installer.SshClientInstaller;
|
||||
import gr.iccs.imu.ems.baguette.client.install.installer.SshJsClientInstaller;
|
||||
import gr.iccs.imu.ems.baguette.server.BaguetteServer;
|
||||
import gr.iccs.imu.ems.baguette.server.ClientShellCommand;
|
||||
import gr.iccs.imu.ems.baguette.server.NodeRegistryEntry;
|
||||
import gr.iccs.imu.ems.brokercep.BrokerCepService;
|
||||
import gr.iccs.imu.ems.common.plugin.PluginManager;
|
||||
import lombok.NoArgsConstructor;
|
||||
import gr.iccs.imu.ems.util.ConfigWriteService;
|
||||
import gr.iccs.imu.ems.util.PasswordUtil;
|
||||
import jakarta.jms.JMSException;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.jms.JMSException;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask.TASK_TYPE;
|
||||
|
||||
@ -41,18 +43,18 @@ import static gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask.TAS
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@NoArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public class ClientInstaller implements InitializingBean {
|
||||
private static ClientInstaller singleton;
|
||||
|
||||
@Autowired
|
||||
private ClientInstallationProperties properties;
|
||||
@Autowired
|
||||
private BrokerCepService brokerCepService;
|
||||
@Autowired
|
||||
private BaguetteServer baguetteServer;
|
||||
@Autowired
|
||||
private PluginManager pluginManager;
|
||||
private final ClientInstallationProperties properties;
|
||||
private final BrokerCepService brokerCepService;
|
||||
private final BaguetteServer baguetteServer;
|
||||
private final PluginManager pluginManager;
|
||||
|
||||
private final List<ClientInstallerPlugin> clientInstallerPluginList;
|
||||
private final ConfigWriteService configWriteService;
|
||||
private final PasswordUtil passwordUtil;
|
||||
|
||||
private final AtomicLong taskCounter = new AtomicLong();
|
||||
private ExecutorService executorService;
|
||||
@ -130,6 +132,12 @@ public class ClientInstaller implements InitializingBean {
|
||||
|
||||
return executeVmOrBaremetalTask(task, taskCounter);
|
||||
} else
|
||||
if ("K8S".equalsIgnoreCase(task.getType())) {
|
||||
if (baguetteServer.getNodeRegistry().getCoordinator()==null)
|
||||
throw new IllegalStateException("Baguette Server Coordinator has not yet been initialized");
|
||||
|
||||
return executeKubernetesTask(task, taskCounter);
|
||||
} else
|
||||
//if ("DIAGNOSTICS".equalsIgnoreCase(task.getType())) {
|
||||
if (task.getTaskType()==TASK_TYPE.DIAGNOSTICS) {
|
||||
return executeDiagnosticsTask(task, taskCounter);
|
||||
@ -139,7 +147,10 @@ public class ClientInstaller implements InitializingBean {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean executeVmOrBaremetalTask(ClientInstallationTask task, long taskCounter) {
|
||||
private boolean executeInstaller(ClientInstallationTask task, long taskCounter,
|
||||
BiFunction<ClientInstallationTask,Long,Boolean> condition,
|
||||
BiFunction<ClientInstallationTask,Long,Boolean> installerExecution)
|
||||
{
|
||||
NodeRegistryEntry entry = baguetteServer.getNodeRegistry().getNodeByAddress(task.getAddress());
|
||||
if (task.isNodeMustBeInRegistry() && entry==null)
|
||||
throw new IllegalStateException("Node entry has been removed from Node Registry before installation: Node IP address: "+ task.getAddress());
|
||||
@ -156,7 +167,7 @@ public class ClientInstaller implements InitializingBean {
|
||||
}
|
||||
|
||||
boolean success;
|
||||
if (! task.getInstructionSets().isEmpty()) {
|
||||
if (condition==null || condition.apply(task, taskCounter)) {
|
||||
// Starting installation
|
||||
entry.nodeInstalling(task);
|
||||
|
||||
@ -166,7 +177,7 @@ public class ClientInstaller implements InitializingBean {
|
||||
.forEach(plugin -> ((InstallationContextProcessorPlugin) plugin).processBeforeInstallation(task, taskCounter));
|
||||
|
||||
log.debug("ClientInstaller: INSTALLATION: Executing installation task: task-counter={}, task={}", taskCounter, task);
|
||||
success = executeVmTask(task, taskCounter);
|
||||
success = installerExecution.apply(task, taskCounter);
|
||||
log.debug("ClientInstaller: NODE_REGISTRY_ENTRY after installation execution: \n{}", task.getNodeRegistryEntry());
|
||||
|
||||
if (entry.getState() == NodeRegistryEntry.STATE.INSTALLING) {
|
||||
@ -179,7 +190,7 @@ public class ClientInstaller implements InitializingBean {
|
||||
pluginManager.getActivePlugins(InstallationContextProcessorPlugin.class)
|
||||
.forEach(plugin -> ((InstallationContextProcessorPlugin) plugin).processAfterInstallation(task, taskCounter, success));
|
||||
} else {
|
||||
log.debug("ClientInstaller: SKIP INSTALLATION: Task has no instructions sets. Skipping execution: Node IP address: "+ task.getAddress());
|
||||
log.debug("ClientInstaller: SKIP INSTALLATION: due to condition. Skipping execution: Node IP address: "+ task.getAddress());
|
||||
success = true;
|
||||
}
|
||||
|
||||
@ -203,6 +214,27 @@ public class ClientInstaller implements InitializingBean {
|
||||
return success;
|
||||
}
|
||||
|
||||
private boolean executeVmOrBaremetalTask(ClientInstallationTask task, long taskCounter) {
|
||||
return executeInstaller(task, taskCounter, (t,c) -> ! t.getInstructionSets().isEmpty(), this::executeVmTask);
|
||||
}
|
||||
|
||||
private boolean executeKubernetesTask(ClientInstallationTask task, long taskCounter) {
|
||||
return executeInstaller(task, taskCounter, (t,c) -> true, (t,c) -> {
|
||||
boolean result;
|
||||
log.info("ClientInstaller: Using K8sClientInstaller for task #{}", taskCounter);
|
||||
result = K8sClientInstaller.builder()
|
||||
.task(task)
|
||||
.taskCounter(taskCounter)
|
||||
.properties(properties)
|
||||
.configWriteService(configWriteService)
|
||||
.passwordUtil(passwordUtil)
|
||||
.build()
|
||||
.execute();
|
||||
log.info("ClientInstaller: Task execution result #{}: success={}", taskCounter, result);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private boolean executeDiagnosticsTask(ClientInstallationTask task, long taskCounter) {
|
||||
log.debug("ClientInstaller: DIAGNOSTICS: Executing diagnostics task: task-counter={}, task={}", taskCounter, task);
|
||||
boolean success = executeVmTask(task, taskCounter);
|
||||
|
@ -23,15 +23,14 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.text.StringSubstitutor;
|
||||
import org.apache.tomcat.util.net.SSLHostConfig;
|
||||
import org.apache.tomcat.util.net.SSLHostConfigCertificate;
|
||||
import org.rauschig.jarchivelib.Archiver;
|
||||
import org.rauschig.jarchivelib.ArchiverFactory;
|
||||
//import org.rauschig.jarchivelib.Archiver;
|
||||
//import org.rauschig.jarchivelib.ArchiverFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.context.WebServerInitializedEvent;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
@ -43,31 +42,24 @@ import java.util.stream.Collectors;
|
||||
* Baguette Client installation helper
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public abstract class AbstractInstallationHelper implements InitializingBean, ApplicationListener<WebServerInitializedEvent>, InstallationHelper {
|
||||
protected final static String LINUX_OS_FAMILY = "LINUX";
|
||||
protected final static String WINDOWS_OS_FAMILY = "WINDOWS";
|
||||
|
||||
protected static AbstractInstallationHelper instance = null;
|
||||
|
||||
@Autowired
|
||||
@Getter @Setter
|
||||
protected ClientInstallationProperties properties;
|
||||
@Autowired
|
||||
protected PasswordUtil passwordUtil;
|
||||
|
||||
protected String archiveBase64;
|
||||
//XXX: Commented a not used feature with dependency with vulnerability
|
||||
// protected String archiveBase64;
|
||||
protected boolean isServerSecure;
|
||||
protected String serverCert;
|
||||
|
||||
public synchronized static AbstractInstallationHelper getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
log.debug("AbstractInstallationHelper.afterPropertiesSet(): class={}: configuration: {}", getClass().getName(), properties);
|
||||
AbstractInstallationHelper.instance = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -115,7 +107,7 @@ public abstract class AbstractInstallationHelper implements InitializingBean, Ap
|
||||
String certPem = KeystoreUtil.exportCertificateAsPEM(c);
|
||||
log.debug("AbstractInstallationHelper.initServerCertificate(): SSL certificate[{}]: {}: \n{}", n, m, certPem);
|
||||
// Append PEM certificate to 'sb'
|
||||
sb.append(certPem).append(System.getProperty("line.separator"));
|
||||
sb.append(certPem).append(System.lineSeparator());
|
||||
m++;
|
||||
}
|
||||
// The first entry is used as the server certificate
|
||||
@ -182,6 +174,7 @@ public abstract class AbstractInstallationHelper implements InitializingBean, Ap
|
||||
}
|
||||
|
||||
// Create baguette client configuration archive
|
||||
/*XXX: Commented a not used feature with dependency with vulnerability
|
||||
Archiver archiver = ArchiverFactory.createArchiver(archiveFile);
|
||||
String tempFileName = "archive_" + System.currentTimeMillis();
|
||||
log.debug("AbstractInstallationHelper: Temp. archive name: {}", tempFileName);
|
||||
@ -198,6 +191,7 @@ public abstract class AbstractInstallationHelper implements InitializingBean, Ap
|
||||
byte[] archiveBytes = Files.readAllBytes(archiveFile.toPath());
|
||||
this.archiveBase64 = Base64.getEncoder().encodeToString(archiveBytes);
|
||||
log.debug("AbstractInstallationHelper: Archive Base64 encoded: {}", archiveBase64);
|
||||
*/
|
||||
}
|
||||
|
||||
private String getResourceAsString(String resourcePath) throws IOException {
|
||||
|
@ -39,8 +39,12 @@ public class InstallationHelperFactory implements InitializingBean {
|
||||
|
||||
public InstallationHelper createInstallationHelper(NodeRegistryEntry entry) {
|
||||
String nodeType = entry.getPreregistration().get("type");
|
||||
log.debug("InstallationHelperFactory: Node type: {}", nodeType);
|
||||
if ("VM".equalsIgnoreCase(nodeType) || "baremetal".equalsIgnoreCase(nodeType)) {
|
||||
return createVmInstallationHelper(entry);
|
||||
} else
|
||||
if ("K8S".equalsIgnoreCase(nodeType)) {
|
||||
return createK8sInstallationHelper(entry);
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported or missing Node type: "+nodeType);
|
||||
}
|
||||
@ -58,6 +62,12 @@ public class InstallationHelperFactory implements InitializingBean {
|
||||
}
|
||||
|
||||
private InstallationHelper createVmInstallationHelper(NodeRegistryEntry entry) {
|
||||
log.debug("InstallationHelperFactory: Returning VmInstallationHelper");
|
||||
return VmInstallationHelper.getInstance();
|
||||
}
|
||||
|
||||
private InstallationHelper createK8sInstallationHelper(NodeRegistryEntry entry) {
|
||||
log.debug("InstallationHelperFactory: Returning K8sInstallationHelper");
|
||||
return K8sInstallationHelper.getInstance();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
* Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package gr.iccs.imu.ems.baguette.client.install.helper;
|
||||
|
||||
import gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask;
|
||||
import gr.iccs.imu.ems.baguette.client.install.instruction.InstructionsSet;
|
||||
import gr.iccs.imu.ems.baguette.server.NodeRegistryEntry;
|
||||
import gr.iccs.imu.ems.translate.TranslationContext;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Baguette Client installation helper for Kubernetes
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class K8sInstallationHelper extends AbstractInstallationHelper {
|
||||
private static K8sInstallationHelper instance;
|
||||
|
||||
public static AbstractInstallationHelper getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
log.debug("K8sInstallationHelper.afterPropertiesSet(): configuration: {}", properties);
|
||||
instance = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientInstallationTask createClientInstallationTask(NodeRegistryEntry entry, TranslationContext translationContext) throws IOException {
|
||||
return createClientTask(ClientInstallationTask.TASK_TYPE.INSTALL, entry, translationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientInstallationTask createClientReinstallTask(NodeRegistryEntry entry, TranslationContext translationContext) throws IOException {
|
||||
return createClientTask(ClientInstallationTask.TASK_TYPE.REINSTALL, entry, translationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientInstallationTask createClientUninstallTask(NodeRegistryEntry entry, TranslationContext translationContext) throws Exception {
|
||||
return createClientTask(ClientInstallationTask.TASK_TYPE.UNINSTALL, entry, translationContext);
|
||||
}
|
||||
|
||||
private ClientInstallationTask createClientTask(@NonNull ClientInstallationTask.TASK_TYPE taskType,
|
||||
NodeRegistryEntry entry,
|
||||
TranslationContext translationContext)
|
||||
{
|
||||
Map<String, String> nodeMap = initializeNodeMap(entry);
|
||||
|
||||
String baseUrl = nodeMap.get("BASE_URL");
|
||||
String clientId = nodeMap.get("CLIENT_ID");
|
||||
String ipSetting = nodeMap.get("IP_SETTING");
|
||||
String requestId = nodeMap.get("requestId");
|
||||
|
||||
// Extract node identification and type information
|
||||
String nodeId = nodeMap.get("id");
|
||||
String nodeAddress = nodeMap.get("address");
|
||||
String nodeType = nodeMap.get("type");
|
||||
String nodeName = nodeMap.get("name");
|
||||
String nodeProvider = nodeMap.get("provider");
|
||||
|
||||
if (StringUtils.isBlank(nodeType)) nodeType = "K8S";
|
||||
|
||||
// Create Installation Task for VM node
|
||||
ClientInstallationTask task = ClientInstallationTask.builder()
|
||||
.id(clientId)
|
||||
.taskType(taskType)
|
||||
.nodeId(nodeId)
|
||||
.requestId(requestId)
|
||||
.name(nodeName)
|
||||
.address(nodeAddress)
|
||||
.type(nodeType)
|
||||
.provider(nodeProvider)
|
||||
.nodeRegistryEntry(entry)
|
||||
.translationContext(translationContext)
|
||||
.build();
|
||||
log.debug("K8sInstallationHelper.createClientTask(): Created client task: {}", task);
|
||||
return task;
|
||||
}
|
||||
|
||||
private Map<String, String> initializeNodeMap(NodeRegistryEntry entry) {
|
||||
return entry.getPreregistration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstructionsSet> prepareInstallationInstructionsForWin(NodeRegistryEntry entry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstructionsSet> prepareInstallationInstructionsForLinux(NodeRegistryEntry entry) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstructionsSet> prepareUninstallInstructionsForWin(NodeRegistryEntry entry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstructionsSet> prepareUninstallInstructionsForLinux(NodeRegistryEntry entry) throws IOException {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -23,11 +23,10 @@ import gr.iccs.imu.ems.translate.TranslationContext;
|
||||
import gr.iccs.imu.ems.util.CredentialsMap;
|
||||
import gr.iccs.imu.ems.util.NetUtil;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.text.StringSubstitutor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -44,6 +43,7 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class VmInstallationHelper extends AbstractInstallationHelper {
|
||||
private final static SimpleDateFormat tsW3C = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
|
||||
private final static SimpleDateFormat tsUTC = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
|
||||
@ -54,10 +54,19 @@ public class VmInstallationHelper extends AbstractInstallationHelper {
|
||||
tsFile.setTimeZone(TimeZone.getDefault());
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private ResourceLoader resourceLoader;
|
||||
@Autowired
|
||||
private ClientInstallationProperties clientInstallationProperties;
|
||||
private static VmInstallationHelper instance;
|
||||
|
||||
private final ClientInstallationProperties clientInstallationProperties;
|
||||
|
||||
public static AbstractInstallationHelper getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
log.debug("VmInstallationHelper.afterPropertiesSet(): configuration: {}", properties);
|
||||
instance = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientInstallationTask createClientInstallationTask(NodeRegistryEntry entry, TranslationContext translationContext) throws IOException {
|
||||
|
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
* Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package gr.iccs.imu.ems.baguette.client.install.installer;
|
||||
|
||||
import gr.iccs.imu.ems.baguette.client.install.ClientInstallationProperties;
|
||||
import gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask;
|
||||
import gr.iccs.imu.ems.baguette.client.install.ClientInstallerPlugin;
|
||||
import gr.iccs.imu.ems.util.ConfigWriteService;
|
||||
import gr.iccs.imu.ems.util.EmsConstant;
|
||||
import gr.iccs.imu.ems.util.EmsRelease;
|
||||
import gr.iccs.imu.ems.util.PasswordUtil;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMap;
|
||||
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
|
||||
import io.fabric8.kubernetes.api.model.apps.DaemonSet;
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.ConfigBuilder;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
|
||||
import io.fabric8.kubernetes.client.dsl.Resource;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.text.StringSubstitutor;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* EMS client installer on a Kubernetes cluster
|
||||
*/
|
||||
@Slf4j
|
||||
@Getter
|
||||
public class K8sClientInstaller implements ClientInstallerPlugin {
|
||||
private static final String K8S_SERVICE_ACCOUNT_SECRETS_PATH_DEFAULT = "/var/run/secrets/kubernetes.io/serviceaccount";
|
||||
private static final String APP_CONFIG_MAP_NAME_DEFAULT = "monitoring-configmap";
|
||||
private static final String EMS_CLIENT_CONFIG_MAP_NAME_DEFAULT = "ems-client-configmap";
|
||||
|
||||
private static final String EMS_CLIENT_DAEMONSET_SPECIFICATION_FILE_DEFAULT = "/ems-client-daemonset.yaml";
|
||||
private static final String EMS_CLIENT_DAEMONSET_NAME_DEFAULT = "ems-client-daemonset";
|
||||
private static final String EMS_CLIENT_DAEMONSET_IMAGE_REPOSITORY_DEFAULT = "registry.gitlab.com/nebulous-project/ems-main/ems-client";
|
||||
private static final String EMS_CLIENT_DAEMONSET_IMAGE_TAG_DEFAULT = EmsRelease.EMS_VERSION;
|
||||
private static final String EMS_CLIENT_DAEMONSET_IMAGE_PULL_POLICY_DEFAULT = "Always";
|
||||
|
||||
private final ClientInstallationTask task;
|
||||
private final long taskCounter;
|
||||
private final ClientInstallationProperties properties;
|
||||
private final ConfigWriteService configWriteService;
|
||||
private final PasswordUtil passwordUtil;
|
||||
|
||||
private String additionalCredentials; // Those specified in EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS, plus one generated
|
||||
private String brokerUsername;
|
||||
private String brokerPassword;
|
||||
|
||||
@Builder
|
||||
public K8sClientInstaller(ClientInstallationTask task, long taskCounter, ClientInstallationProperties properties,
|
||||
ConfigWriteService configWriteService, PasswordUtil passwordUtil)
|
||||
{
|
||||
this.task = task;
|
||||
this.taskCounter = taskCounter;
|
||||
this.properties = properties;
|
||||
this.configWriteService = configWriteService;
|
||||
this.passwordUtil = passwordUtil;
|
||||
|
||||
initializeAdditionalCredentials();
|
||||
}
|
||||
|
||||
private void initializeAdditionalCredentials() {
|
||||
brokerUsername = getConfig("EMS_CLIENT_BROKER_USERNAME", "user-" + RandomStringUtils.randomAlphanumeric(32));
|
||||
brokerPassword = getConfig("EMS_CLIENT_BROKER_PASSWORD", RandomStringUtils.randomAlphanumeric(32));
|
||||
|
||||
StringBuilder sb = new StringBuilder(getConfig("EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS", ""));
|
||||
if (StringUtils.isNotBlank(sb))
|
||||
sb.append(", ");
|
||||
sb.append(brokerUsername).append("/").append(brokerPassword);
|
||||
additionalCredentials = sb.toString();
|
||||
}
|
||||
|
||||
private String getConfig(@NonNull String key, String defaultValue) {
|
||||
String value = System.getenv(key);
|
||||
return value==null ? defaultValue : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeTask() {
|
||||
boolean success = true;
|
||||
try {
|
||||
deployOnCluster();
|
||||
} catch (Exception ex) {
|
||||
log.error("K8sClientInstaller: Failed executing installation instructions for task #{}, Exception: ", taskCounter, ex);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success) log.info("K8sClientInstaller: Task completed successfully #{}", taskCounter);
|
||||
else log.info("K8sClientInstaller: Error occurred while executing task #{}", taskCounter);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deployOnCluster() throws IOException {
|
||||
boolean dryRun = Boolean.parseBoolean( getConfig("EMS_CLIENT_DEPLOYMENT_DRY_RUN", "false") );
|
||||
if (dryRun)
|
||||
log.warn("K8sClientInstaller: NOTE: Dry Run set!! Will not make any changes to cluster");
|
||||
deployOnCluster(dryRun);
|
||||
}
|
||||
|
||||
private void deployOnCluster(boolean dryRun) throws IOException {
|
||||
String serviceAccountPath = getConfig("K8S_SERVICE_ACCOUNT_SECRETS_PATH", K8S_SERVICE_ACCOUNT_SECRETS_PATH_DEFAULT);
|
||||
String masterUrl = getConfig("KUBERNETES_SERVICE_HOST", null);
|
||||
String caCert = Files.readString(Paths.get(serviceAccountPath, "ca.crt"));
|
||||
String token = Files.readString(Paths.get(serviceAccountPath, "token"));
|
||||
String namespace = Files.readString(Paths.get(serviceAccountPath, "namespace"));
|
||||
log.debug("""
|
||||
K8sClientInstaller:
|
||||
Master URL: {}
|
||||
CA cert.:
|
||||
{}
|
||||
Token: {}
|
||||
Namespace: {}""",
|
||||
masterUrl, caCert.trim(), passwordUtil.encodePassword(token), namespace);
|
||||
|
||||
// Configure and start Kubernetes API client
|
||||
Config config = new ConfigBuilder()
|
||||
.withMasterUrl(masterUrl)
|
||||
.withCaCertData(caCert)
|
||||
.withOauthToken(token)
|
||||
.build();
|
||||
try (KubernetesClient client = new KubernetesClientBuilder().withConfig(config).build()) {
|
||||
// Prepare ems client config map
|
||||
createEmsClientConfigMap(dryRun, client, namespace);
|
||||
|
||||
// Prepare application config map
|
||||
createAppConfigMap(dryRun, client, namespace);
|
||||
|
||||
// Deploy ems client daemonset
|
||||
createEmsClientDaemonSet(dryRun, client, namespace);
|
||||
|
||||
task.getNodeRegistryEntry().nodeInstallationComplete(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void createEmsClientConfigMap(boolean dryRun, KubernetesClient client, String namespace) {
|
||||
log.debug("K8sClientInstaller.createEmsClientConfigMap: BEGIN: dry-run={}, namespace={}", dryRun, namespace);
|
||||
|
||||
// Get ems client configmap name
|
||||
String configMapName = getConfig("EMS_CLIENT_CONFIG_MAP_NAME", EMS_CLIENT_CONFIG_MAP_NAME_DEFAULT);
|
||||
log.debug("K8sClientInstaller: configMap: name: {}", configMapName);
|
||||
|
||||
// Get ems client configuration
|
||||
Map<String, String> configMapMap = configWriteService
|
||||
.getConfigFile(EmsConstant.EMS_CLIENT_K8S_CONFIG_MAP_FILE).getContentMap();
|
||||
log.debug("K8sClientInstaller: configMap: data:\n{}", configMapMap);
|
||||
|
||||
// Create ems client configmap
|
||||
Resource<ConfigMap> configMapResource = client.configMaps()
|
||||
.inNamespace(namespace)
|
||||
.resource(new ConfigMapBuilder()
|
||||
.withNewMetadata().withName(configMapName).endMetadata()
|
||||
.addToData("creationTimestamp", Long.toString(Instant.now().getEpochSecond()))
|
||||
.addToData(configMapMap)
|
||||
.build());
|
||||
log.trace("K8sClientInstaller: ConfigMap to create: {}", configMapResource);
|
||||
if (!dryRun) {
|
||||
ConfigMap configMap = configMapResource.serverSideApply();
|
||||
log.debug("K8sClientInstaller: ConfigMap created: {}", configMap);
|
||||
} else {
|
||||
log.warn("K8sClientInstaller: DRY-RUN: Didn't create ems client configmap");
|
||||
}
|
||||
}
|
||||
|
||||
private void createAppConfigMap(boolean dryRun, KubernetesClient client, String namespace) {
|
||||
log.debug("K8sClientInstaller.createAppConfigMap: BEGIN: dry-run={}, namespace={}", dryRun, namespace);
|
||||
|
||||
// Get ems client configmap name
|
||||
String configMapName = getConfig("APP_CONFIG_MAP_NAME", APP_CONFIG_MAP_NAME_DEFAULT);
|
||||
log.debug("K8sClientInstaller: App configMap: name: {}", configMapName);
|
||||
|
||||
// Get App ems-client-related configuration
|
||||
Map<String, String> configMapMap = new LinkedHashMap<>();
|
||||
configMapMap.put("BROKER_USERNAME", brokerUsername);
|
||||
configMapMap.put("BROKER_PASSWORD", brokerPassword);
|
||||
log.debug("K8sClientInstaller: App configMap: data:\n{}", configMapMap);
|
||||
|
||||
// Create ems client configmap
|
||||
Resource<ConfigMap> configMapResource = client.configMaps()
|
||||
.inNamespace(namespace)
|
||||
.resource(new ConfigMapBuilder()
|
||||
.withNewMetadata().withName(configMapName).endMetadata()
|
||||
.addToData("creationTimestamp", Long.toString(Instant.now().getEpochSecond()))
|
||||
.addToData(configMapMap)
|
||||
.build());
|
||||
log.trace("K8sClientInstaller: App ConfigMap to create: {}", configMapResource);
|
||||
if (!dryRun) {
|
||||
ConfigMap configMap = configMapResource.serverSideApply();
|
||||
log.debug("K8sClientInstaller: App ConfigMap created: {}", configMap);
|
||||
} else {
|
||||
log.warn("K8sClientInstaller: DRY-RUN: Didn't create App ems client configmap");
|
||||
}
|
||||
}
|
||||
|
||||
private void createEmsClientDaemonSet(boolean dryRun, KubernetesClient client, String namespace) throws IOException {
|
||||
log.debug("K8sClientInstaller.createEmsClientDaemonSet: BEGIN: dry-run={}, namespace={}", dryRun, namespace);
|
||||
|
||||
String resourceName = getConfig("EMS_CLIENT_DAEMONSET_SPECIFICATION_FILE", EMS_CLIENT_DAEMONSET_SPECIFICATION_FILE_DEFAULT);
|
||||
Map<String,String> values = Map.of(
|
||||
"EMS_CLIENT_DAEMONSET_NAME", getConfig("EMS_CLIENT_DAEMONSET_NAME", EMS_CLIENT_DAEMONSET_NAME_DEFAULT),
|
||||
"EMS_CLIENT_DAEMONSET_IMAGE_REPOSITORY", getConfig("EMS_CLIENT_DAEMONSET_IMAGE_REPOSITORY", EMS_CLIENT_DAEMONSET_IMAGE_REPOSITORY_DEFAULT),
|
||||
"EMS_CLIENT_DAEMONSET_IMAGE_TAG", getConfig("EMS_CLIENT_DAEMONSET_IMAGE_TAG", EMS_CLIENT_DAEMONSET_IMAGE_TAG_DEFAULT),
|
||||
"EMS_CLIENT_DAEMONSET_IMAGE_PULL_POLICY", getConfig("EMS_CLIENT_DAEMONSET_IMAGE_PULL_POLICY", EMS_CLIENT_DAEMONSET_IMAGE_PULL_POLICY_DEFAULT),
|
||||
"EMS_CLIENT_CONFIG_MAP_NAME", getConfig("EMS_CLIENT_CONFIG_MAP_NAME", EMS_CLIENT_CONFIG_MAP_NAME_DEFAULT),
|
||||
"EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS", additionalCredentials,
|
||||
"EMS_CLIENT_KEYSTORE_SECRET", getConfig("EMS_CLIENT_KEYSTORE_SECRET", ""),
|
||||
"EMS_CLIENT_TRUSTSTORE_SECRET", getConfig("EMS_CLIENT_TRUSTSTORE_SECRET", "")
|
||||
);
|
||||
log.debug("K8sClientInstaller: resourceName: {}", namespace);
|
||||
log.debug("K8sClientInstaller: values: {}", values);
|
||||
|
||||
String spec;
|
||||
try (InputStream inputStream = K8sClientInstaller.class.getResourceAsStream(resourceName)) {
|
||||
spec = StreamUtils.copyToString(inputStream, Charset.defaultCharset());
|
||||
log.trace("K8sClientInstaller: Ems client daemonset spec BEFORE:\n{}", spec);
|
||||
spec = StringSubstitutor.replace(spec, values);
|
||||
log.trace("K8sClientInstaller: Ems client daemonset spec AFTER :\n{}", spec);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(spec)) {
|
||||
try (InputStream stream = new ByteArrayInputStream(spec.getBytes(StandardCharsets.UTF_8))) {
|
||||
// Load a DaemonSet object
|
||||
DaemonSet daemonSet = client.apps().daemonSets().load(stream).item();
|
||||
log.debug("K8sClientInstaller: DaemonSet to create: {} :: {}", daemonSet.hashCode(), daemonSet.getMetadata().getName());
|
||||
|
||||
// Deploy the DaemonSet
|
||||
if (!dryRun) {
|
||||
DaemonSet ds = client.apps().daemonSets().inNamespace(namespace).resource(daemonSet).create();
|
||||
log.debug("K8sClientInstaller: DaemonSet created: {} :: {}", ds.hashCode(), ds.getMetadata().getName());
|
||||
} else {
|
||||
log.warn("K8sClientInstaller: DRY-RUN: Didn't create ems client daemonset");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("K8sClientInstaller: ERROR: Ems client daemonset spec is empty");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preProcessTask() {
|
||||
// Throw exception to prevent task exception, if task data have problem
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean postProcessTask() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -7,8 +7,9 @@
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package gr.iccs.imu.ems.baguette.client.install;
|
||||
package gr.iccs.imu.ems.baguette.client.install.installer;
|
||||
|
||||
import gr.iccs.imu.ems.baguette.client.install.*;
|
||||
import gr.iccs.imu.ems.baguette.client.install.instruction.INSTRUCTION_RESULT;
|
||||
import gr.iccs.imu.ems.baguette.client.install.instruction.Instruction;
|
||||
import gr.iccs.imu.ems.baguette.client.install.instruction.InstructionsService;
|
||||
@ -32,6 +33,9 @@ import org.apache.sshd.mina.MinaServiceFactoryFactory;
|
||||
import org.apache.sshd.scp.client.DefaultScpClientCreator;
|
||||
import org.apache.sshd.scp.client.ScpClient;
|
||||
import org.apache.sshd.scp.client.ScpClientCreator;
|
||||
import org.bouncycastle.openssl.PEMKeyPair;
|
||||
import org.bouncycastle.openssl.PEMParser;
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||
import org.bouncycastle.util.io.pem.PemObject;
|
||||
import org.bouncycastle.util.io.pem.PemReader;
|
||||
|
||||
@ -212,10 +216,12 @@ public class SshClientInstaller implements ClientInstallerPlugin {
|
||||
.verify(connectTimeout)
|
||||
.getSession();
|
||||
if (StringUtils.isNotBlank(privateKey)) {
|
||||
PrivateKey privKey = getPrivateKey(privateKey);
|
||||
/*PrivateKey privKey = getPrivateKey(privateKey);
|
||||
//PublicKey pubKey = getPublicKey(publicKeyStr);
|
||||
PublicKey pubKey = getPublicKey(privKey);
|
||||
KeyPair keyPair = new KeyPair(pubKey, privKey);
|
||||
*/
|
||||
KeyPair keyPair = getKeyPairFromPEM(privateKey);
|
||||
session.addPublicKeyIdentity(keyPair);
|
||||
}
|
||||
if (StringUtils.isNotBlank(password)) {
|
||||
@ -249,6 +255,28 @@ public class SshClientInstaller implements ClientInstallerPlugin {
|
||||
//PrivateKey privKey = kf.generatePrivate(keySpecPKCS8);
|
||||
}
|
||||
|
||||
private KeyPair getKeyPairFromPEM(String pemStr) throws IOException {
|
||||
// Load the PEM file containing the private key
|
||||
log.trace("SshClientInstaller: getKeyPairFromPEM: pemStr: {}", pemStr);
|
||||
pemStr = pemStr.replace("\\n", "\n");
|
||||
try (StringReader keyReader = new StringReader(pemStr); PEMParser pemParser = new PEMParser(keyReader)) {
|
||||
// Parse the PEM encoded private key
|
||||
Object pemObject = pemParser.readObject();
|
||||
log.trace("SshClientInstaller: getKeyPairFromPEM: pemObject: {}", pemObject);
|
||||
|
||||
// Convert the PEM object to a KeyPair
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
|
||||
if (pemObject instanceof PEMKeyPair pemKeyPair) {
|
||||
KeyPair keyPair = converter.getKeyPair(pemKeyPair);
|
||||
log.trace("SshClientInstaller: getKeyPairFromPEM: keyPair: {}", keyPair);
|
||||
return keyPair;
|
||||
} else {
|
||||
log.warn("SshClientInstaller: getKeyPairFromPEM: Failed to parse PEM private key");
|
||||
throw new RuntimeException("Failed to parse PEM private key");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PublicKey getPublicKey(PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
KeyFactory factory = KeyFactory.getInstance(privateKey.getAlgorithm());
|
||||
PKCS8EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
|
@ -7,8 +7,10 @@
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package gr.iccs.imu.ems.baguette.client.install;
|
||||
package gr.iccs.imu.ems.baguette.client.install.installer;
|
||||
|
||||
import gr.iccs.imu.ems.baguette.client.install.ClientInstallationProperties;
|
||||
import gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask;
|
||||
import gr.iccs.imu.ems.baguette.client.install.instruction.INSTRUCTION_RESULT;
|
||||
import gr.iccs.imu.ems.baguette.client.install.instruction.InstructionsSet;
|
||||
import lombok.Builder;
|
@ -7,7 +7,7 @@
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package gr.iccs.imu.ems.baguette.client.install;
|
||||
package gr.iccs.imu.ems.baguette.client.install.installer;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
@ -9,6 +9,9 @@
|
||||
|
||||
package gr.iccs.imu.ems.baguette.client.install.plugin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask;
|
||||
import gr.iccs.imu.ems.baguette.client.install.InstallationContextProcessorPlugin;
|
||||
import gr.iccs.imu.ems.translate.model.Monitor;
|
||||
@ -23,8 +26,8 @@ import java.util.*;
|
||||
|
||||
/**
|
||||
* Installation context processor plugin for generating 'allowed-topics' setting
|
||||
* used in baguette-client[.yml/.properties] config. file.
|
||||
* It set the 'COLLECTOR_ALLOWED_TOPICS' variable in pre-registration context.
|
||||
* used in baguette-client[.yml/.properties] configuration file.
|
||||
* It sets the 'COLLECTOR_ALLOWED_TOPICS' variable in pre-registration context.
|
||||
*/
|
||||
@Slf4j
|
||||
@Data
|
||||
@ -37,6 +40,7 @@ public class AllowedTopicsProcessorPlugin implements InstallationContextProcesso
|
||||
|
||||
StringBuilder sbAllowedTopics = new StringBuilder();
|
||||
Set<String> addedTopicsSet = new HashSet<>();
|
||||
Map<String, List<Object>> collectorConfigs = new LinkedHashMap<>();
|
||||
|
||||
boolean first = true;
|
||||
for (Monitor monitor : task.getTranslationContext().getMON()) {
|
||||
@ -53,7 +57,7 @@ public class AllowedTopicsProcessorPlugin implements InstallationContextProcesso
|
||||
}
|
||||
|
||||
// Get sensor configuration
|
||||
Map<String,Object> sensorConfig = monitor.getSensor().getConfiguration();;
|
||||
Map<String,Object> sensorConfig = monitor.getSensor().getConfiguration();
|
||||
|
||||
// Process Destination aliases, if specified in configuration
|
||||
if (sensorConfig!=null) {
|
||||
@ -72,6 +76,14 @@ public class AllowedTopicsProcessorPlugin implements InstallationContextProcesso
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (monitor.getSensor().isPullSensor()) {
|
||||
if (sensorConfig.get("type") instanceof String type && StringUtils.isNotBlank(type)) {
|
||||
collectorConfigs
|
||||
.computeIfAbsent(type, key->new LinkedList<>())
|
||||
.add(monitor.getSensor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.trace("AllowedTopicsProcessorPlugin: Task #{}: MONITOR: metric={}, allowed-topics={}",
|
||||
@ -86,7 +98,28 @@ public class AllowedTopicsProcessorPlugin implements InstallationContextProcesso
|
||||
String allowedTopics = sbAllowedTopics.toString();
|
||||
log.debug("AllowedTopicsProcessorPlugin: Task #{}: Allowed-Topics configuration for collectors: \n{}", taskCounter, allowedTopics);
|
||||
|
||||
String collectorConfigsStr = null;
|
||||
try {
|
||||
if (! collectorConfigs.isEmpty()) {
|
||||
log.debug("AllowedTopicsProcessorPlugin: Task #{}: Pull-Sensor collector configurations: \n{}", taskCounter, collectorConfigs);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
/*mapper.setFilterProvider(new SimpleFilterProvider().addFilter("customerFilter",
|
||||
SimpleBeanPropertyFilter.serializeAllExcept("@objectClass")));*/
|
||||
collectorConfigsStr = mapper
|
||||
.writerWithDefaultPrettyPrinter()
|
||||
.writeValueAsString(collectorConfigs);
|
||||
if (StringUtils.isBlank(collectorConfigsStr))
|
||||
collectorConfigsStr = null;
|
||||
}
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("AllowedTopicsProcessorPlugin: Task #{}: EXCEPTION while processing sensor configs. Skipping them.\n",
|
||||
taskCounter, e);
|
||||
}
|
||||
log.debug("AllowedTopicsProcessorPlugin: Task #{}: Pull-Sensor collector configurations String: \n{}", taskCounter, collectorConfigsStr);
|
||||
|
||||
task.getNodeRegistryEntry().getPreregistration().put(EmsConstant.COLLECTOR_ALLOWED_TOPICS_VAR, allowedTopics);
|
||||
task.getNodeRegistryEntry().getPreregistration().put(EmsConstant.COLLECTOR_CONFIGURATIONS_VAR, collectorConfigsStr);
|
||||
log.debug("AllowedTopicsProcessorPlugin: Task #{}: processBeforeInstallation: END", taskCounter);
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ public class PrometheusProcessorPlugin implements InstallationContextProcessorPl
|
||||
// Get monitor interval
|
||||
Interval interval = monitor.getSensor().pullSensor().getInterval();
|
||||
if (interval != null) {
|
||||
int period = interval.getPeriod();
|
||||
long period = interval.getPeriod();
|
||||
TimeUnit unit = TimeUnit.SECONDS;
|
||||
if (interval.getUnit() != null) {
|
||||
unit = TimeUnit.valueOf( interval.getUnit().name() );
|
||||
|
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
* Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package gr.iccs.imu.ems.baguette.client.install.watch;
|
||||
|
||||
import gr.iccs.imu.ems.baguette.server.NodeRegistry;
|
||||
import gr.iccs.imu.ems.baguette.server.NodeRegistryEntry;
|
||||
import gr.iccs.imu.ems.util.PasswordUtil;
|
||||
import io.fabric8.kubernetes.api.model.Node;
|
||||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
import io.fabric8.kubernetes.client.Config;
|
||||
import io.fabric8.kubernetes.client.ConfigBuilder;
|
||||
import io.fabric8.kubernetes.client.KubernetesClient;
|
||||
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
|
||||
import io.fabric8.kubernetes.client.dsl.Resource;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Kubernetes cluster pods watcher service
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class K8sPodWatcher implements InitializingBean {
|
||||
private static final String K8S_SERVICE_ACCOUNT_SECRETS_PATH_DEFAULT = "/var/run/secrets/kubernetes.io/serviceaccount";
|
||||
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final PasswordUtil passwordUtil;
|
||||
private final NodeRegistry nodeRegistry;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (Boolean.parseBoolean(getConfig("K8S_WATCHER_ENABLED", "true"))) {
|
||||
taskScheduler.scheduleWithFixedDelay(this::doWatch, Instant.now().plusSeconds(20), Duration.ofSeconds(10));
|
||||
} else {
|
||||
log.warn("K8sPodWatcher: Disabled (set K8S_WATCHER_ENABLED=true to enable)");
|
||||
}
|
||||
}
|
||||
|
||||
private String getConfig(@NonNull String key, String defaultValue) {
|
||||
String value = System.getenv(key);
|
||||
return value==null ? defaultValue : value;
|
||||
}
|
||||
|
||||
private void doWatch() {
|
||||
Map<String, NodeEntry> addressToNodeMap;
|
||||
Map<String, Set<PodEntry>> addressToPodMap;
|
||||
try {
|
||||
log.debug("K8sPodWatcher: BEGIN: doWatch");
|
||||
|
||||
String serviceAccountPath = getConfig("K8S_SERVICE_ACCOUNT_SECRETS_PATH", K8S_SERVICE_ACCOUNT_SECRETS_PATH_DEFAULT);
|
||||
String masterUrl = getConfig("KUBERNETES_SERVICE_HOST", null);
|
||||
String caCert = Files.readString(Paths.get(serviceAccountPath, "ca.crt"));
|
||||
String token = Files.readString(Paths.get(serviceAccountPath, "token"));
|
||||
String namespace = Files.readString(Paths.get(serviceAccountPath, "namespace"));
|
||||
log.trace("""
|
||||
K8sPodWatcher:
|
||||
Master URL: {}
|
||||
CA cert.:
|
||||
{}
|
||||
Token: {}
|
||||
Namespace: {}""",
|
||||
masterUrl, caCert.trim(), passwordUtil.encodePassword(token), namespace);
|
||||
|
||||
// Configure and start Kubernetes API client
|
||||
Config config = new ConfigBuilder()
|
||||
.withMasterUrl(masterUrl)
|
||||
.withCaCertData(caCert)
|
||||
.withOauthToken(token)
|
||||
.build();
|
||||
try (KubernetesClient client = new KubernetesClientBuilder().withConfig(config).build()) {
|
||||
log.debug("K8sPodWatcher: Retrieving active Kubernetes cluster nodes and pods");
|
||||
|
||||
// Get Kubernetes cluster nodes (Hosts)
|
||||
addressToNodeMap = new HashMap<>();
|
||||
Map<String, NodeEntry> uidToNodeMap = new HashMap<>();
|
||||
client.nodes()
|
||||
.resources()
|
||||
.map(Resource::item)
|
||||
.forEach(node -> {
|
||||
NodeEntry entry = uidToNodeMap.computeIfAbsent(
|
||||
node.getMetadata().getUid(), s -> new NodeEntry(node));
|
||||
node.getStatus().getAddresses().stream()
|
||||
.filter(address -> ! "Hostname".equalsIgnoreCase(address.getType()))
|
||||
.forEach(address -> addressToNodeMap.putIfAbsent(address.getAddress(), entry));
|
||||
});
|
||||
log.trace("K8sPodWatcher: Address-to-Nodes: {}", addressToNodeMap);
|
||||
|
||||
// Get Kubernetes cluster pods
|
||||
addressToPodMap = new HashMap<>();
|
||||
Map<String, PodEntry> uidToPodMap = new HashMap<>();
|
||||
client.pods()
|
||||
.inAnyNamespace()
|
||||
// .withLabel("nebulous.application")
|
||||
.resources()
|
||||
.map(Resource::item)
|
||||
.filter(pod-> "Running".equalsIgnoreCase(pod.getStatus().getPhase()))
|
||||
.forEach(pod -> {
|
||||
PodEntry entry = uidToPodMap.computeIfAbsent(
|
||||
pod.getMetadata().getUid(), s -> new PodEntry(pod));
|
||||
pod.getStatus().getPodIPs()
|
||||
.forEach(address ->
|
||||
addressToPodMap.computeIfAbsent(address.getIp(), s -> new HashSet<>()).add(entry)
|
||||
);
|
||||
});
|
||||
log.trace("K8sPodWatcher: Address-to-Pods: {}", addressToPodMap);
|
||||
|
||||
} // End of try-with-resources
|
||||
|
||||
// Update Node Registry
|
||||
log.debug("K8sPodWatcher: Updating Node Registry");
|
||||
Map<String, NodeRegistryEntry> addressToNodeEntryMap = nodeRegistry.getNodes().stream()
|
||||
.collect(Collectors.toMap(NodeRegistryEntry::getIpAddress, entry -> entry));
|
||||
|
||||
// New Pods
|
||||
HashMap<String, Set<PodEntry>> newPods = new HashMap<>(addressToPodMap);
|
||||
newPods.keySet().removeAll(addressToNodeEntryMap.keySet());
|
||||
if (! newPods.isEmpty()) {
|
||||
log.trace("K8sPodWatcher: New Pods found: {}", newPods);
|
||||
/*newPods.forEach((address, podSet) -> {
|
||||
if (podSet.size()==1)
|
||||
nodeRegistry.addNode(null, podSet.iterator().next().getPodUid());
|
||||
});*/
|
||||
} else {
|
||||
log.trace("K8sPodWatcher: No new Pods");
|
||||
}
|
||||
|
||||
// Node Entries to be removed
|
||||
HashMap<String, NodeRegistryEntry> oldEntries = new HashMap<>(addressToNodeEntryMap);
|
||||
oldEntries.keySet().removeAll(addressToPodMap.keySet());
|
||||
if (! oldEntries.isEmpty()) {
|
||||
log.trace("K8sPodWatcher: Node entries to be removed: {}", oldEntries);
|
||||
} else {
|
||||
log.trace("K8sPodWatcher: No node entries to remove");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("K8sPodWatcher: Error while running doWatch: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class NodeEntry {
|
||||
private final String nodeUid;
|
||||
private final String nodeName;
|
||||
|
||||
public NodeEntry(Node node) {
|
||||
nodeUid = node.getMetadata().getUid();
|
||||
nodeName = node.getMetadata().getName();
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class PodEntry {
|
||||
private final String podUid;
|
||||
private final String podIP;
|
||||
private final String podName;
|
||||
private final String hostIP;
|
||||
private final Map<String, String> labels;
|
||||
|
||||
public PodEntry(Pod pod) {
|
||||
podUid = pod.getMetadata().getUid();
|
||||
podIP = pod.getStatus().getPodIP();
|
||||
podName = pod.getMetadata().getName();
|
||||
hostIP = pod.getStatus().getHostIP();
|
||||
labels = Collections.unmodifiableMap(pod.getMetadata().getLabels());
|
||||
}
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ package gr.iccs.imu.ems.baguette.client.selfhealing;
|
||||
|
||||
import gr.iccs.imu.ems.baguette.client.install.ClientInstallationProperties;
|
||||
import gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask;
|
||||
import gr.iccs.imu.ems.baguette.client.install.SshClientInstaller;
|
||||
import gr.iccs.imu.ems.baguette.client.install.installer.SshClientInstaller;
|
||||
import gr.iccs.imu.ems.baguette.client.install.helper.InstallationHelperFactory;
|
||||
import gr.iccs.imu.ems.baguette.server.BaguetteServer;
|
||||
import gr.iccs.imu.ems.baguette.server.ClientShellCommand;
|
||||
|
@ -0,0 +1,156 @@
|
||||
#
|
||||
# Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
# Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
# If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
# https://www.mozilla.org/en-US/MPL/2.0/
|
||||
#
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: ${EMS_CLIENT_DAEMONSET_NAME}
|
||||
labels:
|
||||
app.kubernetes.io/name: ${EMS_CLIENT_DAEMONSET_NAME}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: ${EMS_CLIENT_DAEMONSET_NAME}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: ${EMS_CLIENT_DAEMONSET_NAME}
|
||||
spec:
|
||||
hostNetwork: true
|
||||
|
||||
terminationGracePeriodSeconds: 10
|
||||
containers:
|
||||
- name: "${EMS_CLIENT_DAEMONSET_NAME}"
|
||||
image: "${EMS_CLIENT_DAEMONSET_IMAGE_REPOSITORY}:${EMS_CLIENT_DAEMONSET_IMAGE_TAG}"
|
||||
imagePullPolicy: "${EMS_CLIENT_DAEMONSET_IMAGE_PULL_POLICY}"
|
||||
env:
|
||||
#
|
||||
# K8S cluster node info
|
||||
#
|
||||
- name: K8S_NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
- name: K8S_NODE_ADDRESS
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.hostIP
|
||||
#
|
||||
# Pod info
|
||||
#
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: POD_ADDRESS
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
#
|
||||
# EMS client settings
|
||||
#
|
||||
- name: IP_SETTING
|
||||
value: 'DEFAULT_IP'
|
||||
- name: BAGUETTE_CLIENT_BASE_DIR
|
||||
value: '/opt/baguette-client'
|
||||
- name: BAGUETTE_CLIENT_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.uid
|
||||
- name: NODE_CLIENT_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.uid
|
||||
- name: NODE_ADDRESS
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIP
|
||||
- name: SELF_HEALING_ENABLED
|
||||
value: "false"
|
||||
- name: COLLECTOR_NETDATA_ENABLE
|
||||
value: "true"
|
||||
- name: COLLECTOR_NETDATA_URL
|
||||
value: 'http://${K8S_NODE_ADDRESS}:19999/api/v1/allmetrics?format=json'
|
||||
- name: COLLECTOR_PROMETHEUS_ENABLE
|
||||
value: "false"
|
||||
- name: COLLECTOR_ALLOWED_TOPICS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: "${EMS_CLIENT_CONFIG_MAP_NAME}"
|
||||
key: COLLECTOR_ALLOWED_TOPICS
|
||||
optional: true
|
||||
- name: EMS_KEYSTORE_PASSWORD
|
||||
value: "${EMS_CLIENT_KEYSTORE_SECRET}"
|
||||
- name: EMS_TRUSTSTORE_PASSWORD
|
||||
value: "${EMS_CLIENT_TRUSTSTORE_SECRET}"
|
||||
#
|
||||
# EMS client Broker settings
|
||||
#
|
||||
- name: BROKER_URL_ADDRESS_INSECURE
|
||||
value: "0.0.0.0"
|
||||
- name: BROKERCEP_ADDITIONAL_BROKER_CREDENTIALS
|
||||
value: "${EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS}"
|
||||
#
|
||||
# Baguette Server connection info. Can be retrieved from EMS server:
|
||||
# https://ems-server:8111/baguette/connectionInfo
|
||||
#
|
||||
- name: BAGUETTE_SERVER_ADDRESS
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: "${EMS_CLIENT_CONFIG_MAP_NAME}"
|
||||
key: BAGUETTE_SERVER_ADDRESS
|
||||
- name: BAGUETTE_SERVER_PORT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: "${EMS_CLIENT_CONFIG_MAP_NAME}"
|
||||
key: BAGUETTE_SERVER_PORT
|
||||
- name: BAGUETTE_SERVER_PUBKEY
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: "${EMS_CLIENT_CONFIG_MAP_NAME}"
|
||||
key: BAGUETTE_SERVER_PUBKEY
|
||||
- name: BAGUETTE_SERVER_PUBKEY_FINGERPRINT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: "${EMS_CLIENT_CONFIG_MAP_NAME}"
|
||||
key: BAGUETTE_SERVER_PUBKEY_FINGERPRINT
|
||||
- name: BAGUETTE_SERVER_PUBKEY_FORMAT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: "${EMS_CLIENT_CONFIG_MAP_NAME}"
|
||||
key: BAGUETTE_SERVER_PUBKEY_FORMAT
|
||||
- name: BAGUETTE_SERVER_PUBKEY_ALGORITHM
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: "${EMS_CLIENT_CONFIG_MAP_NAME}"
|
||||
key: BAGUETTE_SERVER_PUBKEY_ALGORITHM
|
||||
- name: BAGUETTE_SERVER_USERNAME
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: "${EMS_CLIENT_CONFIG_MAP_NAME}"
|
||||
key: BAGUETTE_SERVER_USERNAME
|
||||
- name: BAGUETTE_SERVER_PASSWORD
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: "${EMS_CLIENT_CONFIG_MAP_NAME}"
|
||||
key: BAGUETTE_SERVER_PASSWORD
|
||||
ports:
|
||||
- name: openwire
|
||||
containerPort: 61616
|
||||
protocol: TCP
|
||||
- name: openwire-tls
|
||||
containerPort: 61617
|
||||
protocol: TCP
|
||||
- name: stomp
|
||||
containerPort: 61610
|
||||
protocol: TCP
|
47
nebulous/ems-core/baguette-client/Dockerfile
Normal file
47
nebulous/ems-core/baguette-client/Dockerfile
Normal file
@ -0,0 +1,47 @@
|
||||
#
|
||||
# Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
# Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
# If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
# https://www.mozilla.org/en-US/MPL/2.0/
|
||||
#
|
||||
|
||||
ARG RUN_IMAGE=eclipse-temurin
|
||||
ARG RUN_IMAGE_TAG=21.0.1_12-jre
|
||||
|
||||
# ----------------- Run image -----------------
|
||||
FROM $RUN_IMAGE:$RUN_IMAGE_TAG
|
||||
|
||||
# Install required and optional packages
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y vim iputils-ping \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Add an EMS user
|
||||
ARG EMS_USER=emsuser
|
||||
ARG EMS_HOME=/opt/baguette-client
|
||||
RUN mkdir -p ${EMS_HOME} && \
|
||||
addgroup ${EMS_USER} && \
|
||||
adduser --home ${EMS_HOME} --no-create-home --ingroup ${EMS_USER} --disabled-password ${EMS_USER} && \
|
||||
chown ${EMS_USER}:${EMS_USER} ${EMS_HOME}
|
||||
|
||||
USER ${EMS_USER}
|
||||
WORKDIR ${EMS_HOME}
|
||||
|
||||
# Setup environment
|
||||
ARG EMS_CONFIG_DIR=${EMS_HOME}/conf
|
||||
ARG JAVA_HOME=/opt/java/openjdk
|
||||
ARG PATH=$JAVA_HOME/bin:$PATH
|
||||
ARG INSTALLATION_PACKAGE=baguette-client-installation-package.tgz
|
||||
|
||||
# Copy Baguette Client files
|
||||
COPY --chown=${EMS_USER}:${EMS_USER} target/$INSTALLATION_PACKAGE /tmp
|
||||
RUN tar zxvf /tmp/$INSTALLATION_PACKAGE -C /opt && rm -f /tmp/$INSTALLATION_PACKAGE
|
||||
COPY --chown=${EMS_USER}:${EMS_USER} conf/* ${EMS_HOME}/conf/
|
||||
|
||||
EXPOSE 61610
|
||||
EXPOSE 61616
|
||||
EXPOSE 61617
|
||||
|
||||
ENTRYPOINT ["/bin/sh", "-c", "/opt/baguette-client/bin/run.sh && tail -f /opt/baguette-client/logs/output.txt"]
|
@ -41,9 +41,9 @@ date -Iseconds
|
||||
|
||||
# Common variables
|
||||
DOWNLOAD_URL=$BASE_URL/baguette-client.tgz
|
||||
DOWNLOAD_URL_MD5=$BASE_URL/baguette-client.tgz.md5
|
||||
DOWNLOAD_URL_SHA256=$BASE_URL/baguette-client.tgz.sha256
|
||||
INSTALL_PACKAGE=/opt/baguette-client/baguette-client.tgz
|
||||
INSTALL_PACKAGE_MD5=/opt/baguette-client/baguette-client.tgz.md5
|
||||
INSTALL_PACKAGE_SHA256=/opt/baguette-client/baguette-client.tgz.sha256
|
||||
INSTALL_DIR=/opt/
|
||||
STARTUP_SCRIPT=$BIN_DIRECTORY/baguette-client
|
||||
SERVICE_NAME=baguette-client
|
||||
@ -86,34 +86,34 @@ fi
|
||||
date -Iseconds
|
||||
echo "Download installation package...ok"
|
||||
|
||||
# Download installation package MD5 checksum
|
||||
# Download installation package SHA256 checksum
|
||||
echo ""
|
||||
echo "Download installation package MD5 checksum..."
|
||||
echo "Download installation package SHA256 checksum..."
|
||||
date -Iseconds
|
||||
wget $SERVER_CERT $DOWNLOAD_URL_MD5 -O $INSTALL_PACKAGE_MD5
|
||||
wget $SERVER_CERT $DOWNLOAD_URL_SHA256 -O $INSTALL_PACKAGE_SHA256
|
||||
if [ $? != 0 ]; then
|
||||
echo "Failed to download installation package ($?)"
|
||||
echo "Aborting installation..."
|
||||
date -Iseconds
|
||||
echo "ABORT: download MD5: `date -Iseconds`" >> $INSTALL_LOG
|
||||
echo "ABORT: download SHA256: `date -Iseconds`" >> $INSTALL_LOG
|
||||
exit 1
|
||||
fi
|
||||
date -Iseconds
|
||||
echo "Download installation package MD5 checksum...ok"
|
||||
echo "Download installation package SHA256 checksum...ok"
|
||||
|
||||
# Check MD5 checksum
|
||||
PACKAGE_MD5=`cat $INSTALL_PACKAGE_MD5`
|
||||
PACKAGE_CHECKSUM=`md5sum $INSTALL_PACKAGE |cut -d " " -f 1`
|
||||
# Check SHA256 checksum
|
||||
PACKAGE_SHA256=`cat $INSTALL_PACKAGE_SHA256`
|
||||
PACKAGE_CHECKSUM=`sha256sum $INSTALL_PACKAGE |cut -d " " -f 1`
|
||||
echo ""
|
||||
echo "Checksum MD5: $PACKAGE_MD5"
|
||||
echo "Checksum SHA256: $PACKAGE_SHA256"
|
||||
echo "Checksum calc: $PACKAGE_CHECKSUM"
|
||||
if [ $PACKAGE_CHECKSUM == $PACKAGE_MD5 ]; then
|
||||
if [ $PACKAGE_CHECKSUM == $PACKAGE_SHA256 ]; then
|
||||
echo "Checksum: ok"
|
||||
else
|
||||
echo "Checksum: wrong"
|
||||
echo "Aborting installation..."
|
||||
date -Iseconds
|
||||
echo "ABORT: wrong MD5: `date -Iseconds`" >> $INSTALL_LOG
|
||||
echo "ABORT: wrong SHA256: `date -Iseconds`" >> $INSTALL_LOG
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
24
nebulous/ems-core/baguette-client/bin/jre-install.sh
Normal file
24
nebulous/ems-core/baguette-client/bin/jre-install.sh
Normal file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
arch=$(uname -m)
|
||||
bits=$(getconf LONG_BIT)
|
||||
echo "Architecture: $arch, Bits: $bits"
|
||||
|
||||
[[ "$bits" == "32" ]] && [[ "${arch,,}" = arm* ]] && TARGET="ARM-32"
|
||||
[[ "$bits" == "64" ]] && [[ "${arch,,}" = arm* ]] && TARGET="ARM-64"
|
||||
[[ "$bits" == "64" ]] && [[ "${arch,,}" = amd* ]] && TARGET="x86-64"
|
||||
[[ "$bits" == "64" ]] && [[ "${arch,,}" = x86* ]] && TARGET="x86-64"
|
||||
|
||||
DOWNLOAD_URL="https://download.bell-sw.com/java/21.0.2+14"
|
||||
[[ "$TARGET" == "ARM-32" ]] && PACKAGE="bellsoft-jre21.0.2+14-linux-arm32-vfp-hflt-full.tar.gz"
|
||||
[[ "$TARGET" == "ARM-64" ]] && PACKAGE="bellsoft-jre21.0.2+14-linux-aarch64-full.tar.gz"
|
||||
[[ "$TARGET" == "x86-64" ]] && PACKAGE="bellsoft-jre21.0.2+14-linux-amd64-full.tar.gz"
|
||||
|
||||
if [[ "$TARGET" == "" ]]; then
|
||||
echo "Target device not supported"
|
||||
exit 1
|
||||
else
|
||||
echo "Target device: $TARGET"
|
||||
curl -k $DOWNLOAD_URL/$PACKAGE -o /tmp/jre.tar.gz
|
||||
ls -l
|
||||
fi
|
@ -12,32 +12,46 @@ setlocal
|
||||
set PWD=%~dp0
|
||||
cd %PWD%..
|
||||
set BASEDIR=%cd%
|
||||
|
||||
IF NOT DEFINED EMS_CONFIG_DIR set EMS_CONFIG_DIR=%BASEDIR%\conf
|
||||
IF NOT DEFINED PAASAGE_CONFIG_DIR set PAASAGE_CONFIG_DIR=%BASEDIR%\conf
|
||||
:: IF NOT DEFINED PAASAGE_CONFIG_DIR set PAASAGE_CONFIG_DIR=%BASEDIR%\conf
|
||||
IF NOT DEFINED EMS_CONFIG_LOCATION set EMS_CONFIG_LOCATION=optional:file:%EMS_CONFIG_DIR%\ems-client.yml,optional:file:%EMS_CONFIG_DIR%\ems-client.properties,optional:file:%EMS_CONFIG_DIR%\baguette-client.yml,optional:file:%EMS_CONFIG_DIR%\baguette-client.properties
|
||||
IF NOT DEFINED JASYPT_PASSWORD set JASYPT_PASSWORD=password
|
||||
set JAVA_HOME=%BASEDIR%/jre
|
||||
set LOG_FILE=%BASEDIR%/logs/output.txt
|
||||
:: IF NOT DEFINED JASYPT_PASSWORD set JASYPT_PASSWORD=password
|
||||
IF NOT DEFINED JAVA_HOME IF EXIST %BASEDIR%\jre\ set JAVA_HOME=%BASEDIR%/jre
|
||||
|
||||
:: Update path
|
||||
set PATH=%JAVA_HOME%\bin;%PATH%
|
||||
|
||||
:: Source external environment variables file
|
||||
if DEFINED EMS_EXTRA_ENV_VARS_FILE CALL %EMS_EXTRA_ENV_VARS_FILE%
|
||||
|
||||
:: Copy dependencies if missing
|
||||
if exist pom.xml (
|
||||
if not exist %BASEDIR%\target\dependency cmd /C "mvn dependency:copy-dependencies"
|
||||
)
|
||||
|
||||
:: Run Baguette Client
|
||||
set JAVA_OPTS= -Djavax.net.ssl.trustStore=%EMS_CONFIG_DIR%\client-broker-truststore.p12 ^
|
||||
-Djavax.net.ssl.trustStorePassword=melodic ^
|
||||
-Djavax.net.ssl.trustStoreType=pkcs12 ^
|
||||
-Djasypt.encryptor.password=%JASYPT_PASSWORD% ^
|
||||
--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED
|
||||
:: Set JAVA_OPTS
|
||||
::set JAVA_OPTS= -Djavax.net.ssl.trustStore=%EMS_CONFIG_DIR%\client-broker-truststore.p12 ^
|
||||
:: -Djavax.net.ssl.trustStorePassword=melodic ^
|
||||
:: -Djavax.net.ssl.trustStoreType=pkcs12 ^
|
||||
::set JAVA_OPTS=-Djavax.net.debug=all %JAVA_OPTS%
|
||||
::set JAVA_OPTS=-Dlogging.level.gr.iccs.imu.ems=TRACE %JAVA_OPTS%
|
||||
set JAVA_OPTS=%JAVA_OPTS% -Djasypt.encryptor.password=%JASYPT_PASSWORD%
|
||||
set JAVA_OPTS=%JAVA_OPTS% --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED
|
||||
|
||||
:: Print settings
|
||||
echo Starting baguette client...
|
||||
echo EMS_CONFIG_DIR=%EMS_CONFIG_DIR%
|
||||
echo EMS_CONFIG_LOCATION=%EMS_CONFIG_LOCATION%
|
||||
echo Starting baguette client...
|
||||
echo LOG_FILE=%LOG_FILE%
|
||||
|
||||
echo Starting baguette client... >> %LOG_FILE%
|
||||
echo EMS_CONFIG_DIR=%EMS_CONFIG_DIR% >> %LOG_FILE%
|
||||
echo EMS_CONFIG_LOCATION=%EMS_CONFIG_LOCATION% >> %LOG_FILE%
|
||||
echo LOG_FILE=%LOG_FILE% >> %LOG_FILE%
|
||||
|
||||
:: Run Baguette Client
|
||||
java %JAVA_OPTS% -classpath "%EMS_CONFIG_DIR%;%BASEDIR%\jars\*;%BASEDIR%\target\classes;%BASEDIR%\target\dependency\*" gr.iccs.imu.ems.baguette.client.BaguetteClient "--spring.config.location=%EMS_CONFIG_LOCATION%" "--logging.config=file:%EMS_CONFIG_DIR%\logback-spring.xml" %*
|
||||
|
||||
cd %PWD%
|
||||
|
@ -12,18 +12,28 @@
|
||||
PREVWORKDIR=`pwd`
|
||||
BASEDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )
|
||||
cd ${BASEDIR}
|
||||
|
||||
EMS_CONFIG_DIR=${BASEDIR}/conf
|
||||
PAASAGE_CONFIG_DIR=${BASEDIR}/conf
|
||||
#PAASAGE_CONFIG_DIR=${BASEDIR}/conf
|
||||
EMS_CONFIG_LOCATION=optional:file:$EMS_CONFIG_DIR/ems-client.yml,optional:file:$EMS_CONFIG_DIR/ems-client.properties,optional:file:$EMS_CONFIG_DIR/baguette-client.yml,optional:file:$EMS_CONFIG_DIR/baguette-client.properties
|
||||
LOG_FILE=${BASEDIR}/logs/output.txt
|
||||
TEE_FILE=${BASEDIR}/logs/tee.txt
|
||||
JASYPT_PASSWORD=password
|
||||
JAVA_HOME=${BASEDIR}/jre
|
||||
export EMS_CONFIG_DIR PAASAGE_CONFIG_DIR LOG_FILE JASYPT_PASSWORD JAVA_HOME
|
||||
#JASYPT_PASSWORD=password
|
||||
export HOST_IP=1.2.3.4
|
||||
|
||||
[ -z "${JAVA_HOME}" ] && [ -d "${BASEDIR}/jre" ] && JAVA_HOME=${BASEDIR}/jre
|
||||
#export EMS_CONFIG_DIR PAASAGE_CONFIG_DIR LOG_FILE JASYPT_PASSWORD JAVA_HOME
|
||||
export EMS_CONFIG_DIR LOG_FILE JAVA_HOME
|
||||
|
||||
# Update path
|
||||
PATH=${JAVA_HOME}/bin:$PATH
|
||||
|
||||
# Source external environment variables file
|
||||
if [ "$EMS_EXTRA_ENV_VARS_FILE" != "" ]; then
|
||||
echo "Sourcing $EMS_EXTRA_ENV_VARS_FILE..."
|
||||
source $EMS_EXTRA_ENV_VARS_FILE
|
||||
fi
|
||||
|
||||
# Check if baguette client is already running
|
||||
#PID=`jps | grep BaguetteClient | cut -d " " -f 1`
|
||||
PID=`ps -ef |grep java |grep BaguetteClient | cut -c 10-14`
|
||||
@ -40,14 +50,15 @@ if [ -f pom.xml ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run Baguette client
|
||||
JAVA_OPTS=-Djavax.net.ssl.trustStore=${EMS_CONFIG_DIR}/client-broker-truststore.p12
|
||||
JAVA_OPTS="${JAVA_OPTS} -Djavax.net.ssl.trustStorePassword=melodic -Djavax.net.ssl.trustStoreType=pkcs12"
|
||||
JAVA_OPTS="${JAVA_OPTS} -Djasypt.encryptor.password=$JASYPT_PASSWORD"
|
||||
# Set JAVA_OPTS
|
||||
#JAVA_OPTS=-Djavax.net.ssl.trustStore=${EMS_CONFIG_DIR}/client-broker-truststore.p12
|
||||
#JAVA_OPTS="${JAVA_OPTS} -Djavax.net.ssl.trustStorePassword=melodic -Djavax.net.ssl.trustStoreType=pkcs12"
|
||||
#JAVA_OPTS="-Djavax.net.debug=all ${JAVA_OPTS}"
|
||||
#JAVA_OPTS="-Dlogging.level.gr.iccs.imu.ems=TRACE ${JAVA_OPTS}"
|
||||
JAVA_OPTS="${JAVA_OPTS} -Djasypt.encryptor.password=$JASYPT_PASSWORD"
|
||||
JAVA_OPTS="${JAVA_OPTS} --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED"
|
||||
|
||||
# Print settings
|
||||
echo "Starting baguette client..."
|
||||
echo "EMS_CONFIG_DIR=${EMS_CONFIG_DIR}"
|
||||
echo "EMS_CONFIG_LOCATION=${EMS_CONFIG_LOCATION}"
|
||||
@ -58,14 +69,18 @@ echo "EMS_CONFIG_DIR=${EMS_CONFIG_DIR}" &>> ${LOG_FILE}
|
||||
echo "EMS_CONFIG_LOCATION=${EMS_CONFIG_LOCATION}" &>> ${LOG_FILE}
|
||||
echo "LOG_FILE=${LOG_FILE}" &>> ${LOG_FILE}
|
||||
|
||||
# Run Baguette Client
|
||||
if [ "$1" == "--i" ]; then
|
||||
echo "Baguette client running in Interactive mode"
|
||||
java ${JAVA_OPTS} -classpath "conf:jars/*:target/classes:target/dependency/*" gr.iccs.imu.ems.baguette.client.BaguetteClient "--spring.config.location=${EMS_CONFIG_LOCATION}" "--logging.config=file:${EMS_CONFIG_DIR}/logback-spring.xml" $* $* 2>&1 | tee ${TEE_FILE}
|
||||
java ${JAVA_OPTS} -classpath "conf:jars/*:target/classes:target/dependency/*" gr.iccs.imu.ems.baguette.client.BaguetteClient "--spring.config.location=${EMS_CONFIG_LOCATION}" "--logging.config=file:${EMS_CONFIG_DIR}/logback-spring.xml" $* 2>&1 | tee ${TEE_FILE}
|
||||
else
|
||||
java ${JAVA_OPTS} -classpath "conf:jars/*:target/classes:target/dependency/*" gr.iccs.imu.ems.baguette.client.BaguetteClient "--spring.config.location=${EMS_CONFIG_LOCATION}" "--logging.config=file:${EMS_CONFIG_DIR}/logback-spring.xml" $* &>> ${LOG_FILE} &
|
||||
PID=`jps | grep BaguetteClient | cut -d " " -f 1`
|
||||
PID=`ps -ef |grep java |grep BaguetteClient | cut -c 10-14`
|
||||
echo "Baguette client PID: $PID"
|
||||
if command -v jps
|
||||
then
|
||||
PID=`jps | grep BaguetteClient | cut -d " " -f 1`
|
||||
PID=`ps -ef |grep java |grep BaguetteClient | cut -c 10-14`
|
||||
echo "Baguette client PID: $PID"
|
||||
fi
|
||||
fi
|
||||
|
||||
cd $PREVWORKDIR
|
@ -11,12 +11,20 @@
|
||||
### EMS - Baguette Client properties ###
|
||||
################################################################################
|
||||
|
||||
#password-encoder-class = password.gr.iccs.imu.ems.util.AsterisksPasswordEncoder
|
||||
#password-encoder-class = password.gr.iccs.imu.ems.util.IdentityPasswordEncoder
|
||||
#password-encoder-class = password.gr.iccs.imu.ems.util.PresentPasswordEncoder
|
||||
#password-encoder-class = gr.iccs.imu.ems.util.password.AsterisksPasswordEncoder
|
||||
#password-encoder-class = gr.iccs.imu.ems.util.password.IdentityPasswordEncoder
|
||||
#password-encoder-class = gr.iccs.imu.ems.util.password.PresentPasswordEncoder
|
||||
|
||||
### Jasypt encryptor settings (using old settings until encrypted texts are updated)
|
||||
jasypt.encryptor.algorithm = PBEWithMD5AndDES
|
||||
jasypt.encryptor.ivGeneratorClassname = org.jasypt.iv.NoIvGenerator
|
||||
|
||||
# Baguette Client configuration
|
||||
|
||||
baseDir = ${BAGUETTE_CLIENT_BASE_DIR}
|
||||
connection-retry-enabled = true
|
||||
connection-retry-delay = 10000
|
||||
connection-retry-limit = -1
|
||||
auth-timeout = 60000
|
||||
exec-timeout = 120000
|
||||
#retry-period = 60000
|
||||
@ -38,7 +46,9 @@ client-id = ${BAGUETTE_CLIENT_ID}
|
||||
server-address = ${BAGUETTE_SERVER_ADDRESS}
|
||||
server-port = ${BAGUETTE_SERVER_PORT}
|
||||
server-pubkey = ${BAGUETTE_SERVER_PUBKEY}
|
||||
server-fingerprint = ${BAGUETTE_SERVER_PUBKEY_FINGERPRINT}
|
||||
server-pubkey-fingerprint = ${BAGUETTE_SERVER_PUBKEY_FINGERPRINT}
|
||||
server-pubkey-algorithm = ${BAGUETTE_SERVER_PUBKEY_ALGORITHM}
|
||||
server-pubkey-format = ${BAGUETTE_SERVER_PUBKEY_FORMAT}
|
||||
|
||||
server-username = ${BAGUETTE_SERVER_USERNAME}
|
||||
server-password = ${BAGUETTE_SERVER_PASSWORD}
|
||||
@ -58,7 +68,7 @@ server-password = ${BAGUETTE_SERVER_PASSWORD}
|
||||
# Collectors settings
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
#collector-classes = netdata.collector.gr.iccs.imu.ems.baguette.client.NetdataCollector
|
||||
#collector-classes = gr.iccs.imu.ems.baguette.client.collector.netdata.NetdataCollector
|
||||
|
||||
collector.netdata.enable = true
|
||||
collector.netdata.delay = 10000
|
||||
@ -141,8 +151,8 @@ brokercep.broker-protocol = ssl
|
||||
BROKER_URL_PROPERTIES = transport.daemon=true&transport.trace=false&transport.useKeepAlive=true&transport.useInactivityMonitor=false&transport.needClientAuth=${CLIENT_AUTH_REQUIRED}&transport.verifyHostName=true&transport.connectionTimeout=0&transport.keepAlive=true
|
||||
CLIENT_AUTH_REQUIRED = false
|
||||
brokercep.broker-url[0] = ${brokercep.broker-protocol}://0.0.0.0:${brokercep.broker-port}?${BROKER_URL_PROPERTIES}
|
||||
brokercep.broker-url[1] = tcp://127.0.0.1:61616?${BROKER_URL_PROPERTIES}
|
||||
brokercep.broker-url[2] =
|
||||
brokercep.broker-url[1] = tcp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61616?${BROKER_URL_PROPERTIES}
|
||||
brokercep.broker-url[2] = stomp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61610?${BROKER_URL_PROPERTIES}
|
||||
|
||||
CLIENT_URL_PROPERTIES=daemon=true&trace=false&useInactivityMonitor=false&connectionTimeout=0&keepAlive=true
|
||||
brokercep.broker-url-for-consumer = tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES}
|
||||
@ -153,12 +163,14 @@ brokercep.broker-url-for-clients = ${brokercep.broker-protocol}://${EMS_CLIENT_A
|
||||
brokercep.ssl.keystore-file = ${EMS_CONFIG_DIR}/client-broker-keystore.p12
|
||||
brokercep.ssl.keystore-type = PKCS12
|
||||
#brokercep.ssl.keystore-password = melodic
|
||||
brokercep.ssl.keystore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==)
|
||||
#brokercep.ssl.keystore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==)
|
||||
brokercep.ssl.keystore-password = ${EMS_KEYSTORE_PASSWORD}
|
||||
# Trust store
|
||||
brokercep.ssl.truststore-file = ${EMS_CONFIG_DIR}/client-broker-truststore.p12
|
||||
brokercep.ssl.truststore-type = PKCS12
|
||||
#brokercep.ssl.truststore-password = melodic
|
||||
brokercep.ssl.truststore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==)
|
||||
#brokercep.ssl.truststore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==)
|
||||
brokercep.ssl.truststore-password = ${EMS_TRUSTSTORE_PASSWORD}
|
||||
# Certificate
|
||||
brokercep.ssl.certificate-file = ${EMS_CONFIG_DIR}/client-broker.crt
|
||||
# Key-and-Cert data
|
||||
@ -170,7 +182,8 @@ brokercep.ssl.key-entry-ext-san = dns:localhost,ip:127.0.0.1,ip:${DEFAULT_IP},ip
|
||||
# Authentication and Authorization settings
|
||||
brokercep.authentication-enabled = true
|
||||
#brokercep.additional-broker-credentials = aaa/111, bbb/222, morphemic/morphemic
|
||||
brokercep.additional-broker-credentials = ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)
|
||||
#brokercep.additional-broker-credentials = ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)
|
||||
brokercep.additional-broker-credentials = ${EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS}
|
||||
brokercep.authorization-enabled = false
|
||||
|
||||
# Broker instance settings
|
||||
@ -184,14 +197,14 @@ brokercep.broker-using-shutdown-hook = false
|
||||
|
||||
# Message interceptors
|
||||
brokercep.message-interceptors[0].destination = >
|
||||
brokercep.message-interceptors[0].className = interceptor.broker.gr.iccs.imu.ems.brokercep.SequentialCompositeInterceptor
|
||||
brokercep.message-interceptors[0].className = gr.iccs.imu.ems.brokercep.broker.interceptor.SequentialCompositeInterceptor
|
||||
brokercep.message-interceptors[0].params[0] = #SourceAddressMessageUpdateInterceptor
|
||||
brokercep.message-interceptors[0].params[1] = #MessageForwarderInterceptor
|
||||
brokercep.message-interceptors[0].params[2] = #NodePropertiesMessageUpdateInterceptor
|
||||
|
||||
brokercep.message-interceptors-specs.SourceAddressMessageUpdateInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.SourceAddressMessageUpdateInterceptor
|
||||
brokercep.message-interceptors-specs.MessageForwarderInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.MessageForwarderInterceptor
|
||||
brokercep.message-interceptors-specs.NodePropertiesMessageUpdateInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.NodePropertiesMessageUpdateInterceptor
|
||||
brokercep.message-interceptors-specs.SourceAddressMessageUpdateInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.SourceAddressMessageUpdateInterceptor
|
||||
brokercep.message-interceptors-specs.MessageForwarderInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.MessageForwarderInterceptor
|
||||
brokercep.message-interceptors-specs.NodePropertiesMessageUpdateInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.NodePropertiesMessageUpdateInterceptor
|
||||
|
||||
# Message forward destinations (MessageForwarderInterceptor must be included in 'message-interceptors' property)
|
||||
#brokercep.message-forward-destinations[0].connection-string = tcp://localhost:51515
|
||||
|
@ -11,12 +11,22 @@
|
||||
### EMS - Baguette Client properties ###
|
||||
################################################################################
|
||||
|
||||
#password-encoder-class: password.gr.iccs.imu.ems.util.AsterisksPasswordEncoder
|
||||
#password-encoder-class: password.gr.iccs.imu.ems.util.IdentityPasswordEncoder
|
||||
#password-encoder-class: password.gr.iccs.imu.ems.util.PresentPasswordEncoder
|
||||
#password-encoder-class: gr.iccs.imu.ems.util.password.AsterisksPasswordEncoder
|
||||
#password-encoder-class: gr.iccs.imu.ems.util.password.IdentityPasswordEncoder
|
||||
#password-encoder-class: gr.iccs.imu.ems.util.password.PresentPasswordEncoder
|
||||
|
||||
### Jasypt encryptor settings (using old settings until encrypted texts are updated)
|
||||
jasypt:
|
||||
encryptor:
|
||||
algorithm: PBEWithMD5AndDES
|
||||
ivGeneratorClassname: org.jasypt.iv.NoIvGenerator
|
||||
|
||||
# Baguette Client configuration
|
||||
|
||||
baseDir: ${BAGUETTE_CLIENT_BASE_DIR}
|
||||
connection-retry-enabled: true
|
||||
connection-retry-delay: 10000
|
||||
connection-retry-limit: -1
|
||||
auth-timeout: 60000
|
||||
exec-timeout: 120000
|
||||
#retry-period: 60000
|
||||
@ -46,7 +56,9 @@ client-id: ${BAGUETTE_CLIENT_ID}
|
||||
server-address: ${BAGUETTE_SERVER_ADDRESS}
|
||||
server-port: ${BAGUETTE_SERVER_PORT}
|
||||
server-pubkey: ${BAGUETTE_SERVER_PUBKEY}
|
||||
server-fingerprint: ${BAGUETTE_SERVER_PUBKEY_FINGERPRINT}
|
||||
server-pubkey-fingerprint: ${BAGUETTE_SERVER_PUBKEY_FINGERPRINT}
|
||||
server-pubkey-algorithm: ${BAGUETTE_SERVER_PUBKEY_ALGORITHM}
|
||||
server-pubkey-format: ${BAGUETTE_SERVER_PUBKEY_FORMAT}
|
||||
|
||||
server-username: ${BAGUETTE_SERVER_USERNAME}
|
||||
server-password: ${BAGUETTE_SERVER_PASSWORD}
|
||||
@ -69,7 +81,9 @@ server-password: ${BAGUETTE_SERVER_PASSWORD}
|
||||
# Collectors settings
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
#collector-classes: netdata.collector.gr.iccs.imu.ems.baguette.client.NetdataCollector
|
||||
#collector-classes: gr.iccs.imu.ems.baguette.client.collector.netdata.NetdataCollector
|
||||
|
||||
collector-configurations: ${COLLECTOR_CONFIGURATIONS}
|
||||
|
||||
collector:
|
||||
netdata:
|
||||
@ -162,7 +176,8 @@ brokercep:
|
||||
# Broker connectors
|
||||
broker-url:
|
||||
- ${brokercep.broker-protocol}://0.0.0.0:${brokercep.broker-port}?${BROKER_URL_PROPERTIES}
|
||||
- tcp://127.0.0.1:61616?${BROKER_URL_PROPERTIES}
|
||||
- tcp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61616?${BROKER_URL_PROPERTIES}
|
||||
- stomp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61610?${BROKER_URL_PROPERTIES}
|
||||
|
||||
# Broker URLs for (EMS) consumer and clients
|
||||
broker-url-for-consumer: tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES}
|
||||
@ -173,12 +188,14 @@ brokercep:
|
||||
# Key store settings
|
||||
keystore-file: ${EMS_CONFIG_DIR}/client-broker-keystore.p12
|
||||
keystore-type: PKCS12
|
||||
keystore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic
|
||||
#keystore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic
|
||||
keystore-password: ${EMS_KEYSTORE_PASSWORD:${random.value}}
|
||||
|
||||
# Trust store settings
|
||||
truststore-file: ${EMS_CONFIG_DIR}/client-broker-truststore.p12
|
||||
truststore-type: PKCS12
|
||||
truststore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic
|
||||
#truststore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic
|
||||
truststore-password: ${EMS_TRUSTSTORE_PASSWORD:${random.value}}
|
||||
|
||||
# Certificate settings
|
||||
certificate-file: ${EMS_CONFIG_DIR}/client-broker.crt
|
||||
@ -192,7 +209,8 @@ brokercep:
|
||||
# Authentication and Authorization settings
|
||||
authentication-enabled: true
|
||||
#additional-broker-credentials: aaa/111, bbb/222, morphemic/morphemic
|
||||
additional-broker-credentials: 'ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)'
|
||||
#additional-broker-credentials: 'ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)'
|
||||
additional-broker-credentials: ${EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS}
|
||||
authorization-enabled: false
|
||||
|
||||
# Broker instance settings
|
||||
@ -207,7 +225,7 @@ brokercep:
|
||||
# Message interceptors
|
||||
message-interceptors:
|
||||
- destination: '>'
|
||||
className: 'interceptor.broker.gr.iccs.imu.ems.brokercep.SequentialCompositeInterceptor'
|
||||
className: 'gr.iccs.imu.ems.brokercep.broker.interceptor.SequentialCompositeInterceptor'
|
||||
params:
|
||||
- '#SourceAddressMessageUpdateInterceptor'
|
||||
- '#MessageForwarderInterceptor'
|
||||
@ -215,11 +233,11 @@ brokercep:
|
||||
|
||||
message-interceptors-specs:
|
||||
SourceAddressMessageUpdateInterceptor:
|
||||
className: interceptor.broker.gr.iccs.imu.ems.brokercep.SourceAddressMessageUpdateInterceptor
|
||||
className: gr.iccs.imu.ems.brokercep.broker.interceptor.SourceAddressMessageUpdateInterceptor
|
||||
MessageForwarderInterceptor:
|
||||
className: interceptor.broker.gr.iccs.imu.ems.brokercep.MessageForwarderInterceptor
|
||||
className: gr.iccs.imu.ems.brokercep.broker.interceptor.MessageForwarderInterceptor
|
||||
NodePropertiesMessageUpdateInterceptor:
|
||||
className: interceptor.broker.gr.iccs.imu.ems.brokercep.NodePropertiesMessageUpdateInterceptor
|
||||
className: gr.iccs.imu.ems.brokercep.broker.interceptor.NodePropertiesMessageUpdateInterceptor
|
||||
|
||||
# Message forward destinations (MessageForwarderInterceptor must be included in 'message-interceptors' property)
|
||||
#message-forward-destinations:
|
||||
|
56
nebulous/ems-core/baguette-client/docker-compose.yml
Normal file
56
nebulous/ems-core/baguette-client/docker-compose.yml
Normal file
@ -0,0 +1,56 @@
|
||||
#
|
||||
# Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
# If a copy of the MPL was not distributed with this file, You can obtain one at
|
||||
# https://www.mozilla.org/en-US/MPL/2.0/
|
||||
#
|
||||
|
||||
services:
|
||||
ems-client:
|
||||
image: ems-client:2024-feb-nebulous
|
||||
environment:
|
||||
# Logging settings
|
||||
# - LOGGING_LEVEL_GR_ICCS_IMU_EMS_BAGUETTE_CLIENT=TRACE
|
||||
|
||||
# Mode of operation - Common settings
|
||||
- IP_SETTING=DEFAULT_IP
|
||||
- SELF_HEALING_ENABLED=false
|
||||
#- JASYPT_PASSWORD=
|
||||
#- BAGUETTE_CLIENT_BASE_DIR=/opt/baguette-client
|
||||
|
||||
# This Node info
|
||||
- NODE_CLIENT_ID=localhost
|
||||
- BAGUETTE_CLIENT_ID=localhost
|
||||
|
||||
- EMS_CLIENT_ADDRESS=localhost
|
||||
- NODE_ADDRESS=localhost
|
||||
- NODE_ADDRESS_PUBLIC=localhost
|
||||
- NODE_ADDRESS_PRIVATE=localhost
|
||||
- zone-id=
|
||||
- provider=
|
||||
|
||||
# EMS server SSH info and credentials
|
||||
- BAGUETTE_SERVER_ADDRESS=host.docker.internal
|
||||
- BAGUETTE_SERVER_PORT=2222
|
||||
- BAGUETTE_SERVER_PUBKEY=-----BEGIN PUBLIC KEY-----\nMIICRjCCAbkGByqGSM49AgEwggGsAgEBME0GByqGSM49AQECQgH/////////////\n////////////////////////////////////////////////////////////////\n/////////zCBiARCAf//////////////////////////////////////////////\n///////////////////////////////////////8BEIAUZU+uWGOHJofkpohoLaF\nQO6i2nJbmbMV87i0iZGO8QnhVhk5Uex+k3sWUsC9O7G/BzVz34g9LDTx70Uf1GtQ\nPwAEgYUEAMaFjga3BATpzZ4+y2YjlbRCnGSBOQU/tSH4KK9ga009uqFLXnfv51ko\n/h3BJ6L/qN4zSLPBhWpCm/l+fjHC5b1mARg5KWp4mjvABFyKX7QsfRvZmPVESVeb\nRGgXr70XJz5mLJfucple9CZAxVC5AT+tB2E1PHCGonLCQIi+lHaf0WZQAkIB////\n///////////////////////////////////////6UYaHg78vlmt/zAFI9wml0Du1\nybiJnEeuu2+3HpE4ZAkCAQEDgYYABAHedQ6pZnbUFjw+/5B2MQGl/WQsUcRFS0Dy\n7RmSTOsfBlRnEaga7KWPT/lGaSwJfi3OdEMwZYdAN/I7A1HsvqemBgEYUuN96ENP\nGskWCX2qNmfDjxsDpPPXr7lsR9pzafXFpP7PLIvQSFb5UnlzI7edbF6UNVjfjkSY\nY8KnManEvzaMeQ\=\=\n-----END PUBLIC KEY-----
|
||||
- BAGUETTE_SERVER_PUBKEY_FINGERPRINT=SHA256\:GPn9rx9WWPr+JXlUw0cq8I8tvYLiyadVZswfCevzpN0
|
||||
- BAGUETTE_SERVER_PUBKEY_ALGORITHM=EC
|
||||
- BAGUETTE_SERVER_PUBKEY_FORMAT=X.509
|
||||
- BAGUETTE_SERVER_USERNAME=user-45cb8e46-c6bf-4a4b-8e7a-354f7b6d8fc1
|
||||
- BAGUETTE_SERVER_PASSWORD=tTUjicVJfrfCurbjecGfBOTxdshA9dOLLjIEo
|
||||
|
||||
# Collectors settings
|
||||
- COLLECTOR_NETDATA_ENABLE=false
|
||||
- COLLECTOR_PROMETHEUS_ENABLE=false
|
||||
- COLLECTOR_ALLOWED_TOPICS=
|
||||
|
||||
# AMQ broker settings
|
||||
- EMS_KEYSTORE_PASSWORD=
|
||||
- EMS_TRUSTSTORE_PASSWORD=
|
||||
- EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS=
|
||||
ports:
|
||||
- 11016:61616
|
||||
- 11017:61617
|
||||
- 11010:61610
|
||||
# - 1099:19999
|
@ -21,6 +21,12 @@
|
||||
|
||||
<properties>
|
||||
<atomix.version>3.1.12</atomix.version>
|
||||
|
||||
<!-- io.fabricat8 docker-maven-plugin properties -->
|
||||
<docker-maven-plugin.version>0.43.2</docker-maven-plugin.version>
|
||||
<docker.image.name>ems-client</docker.image.name>
|
||||
<docker.user>emsuser</docker.user>
|
||||
<docker.user.home>/opt/baguette-client</docker.user.home>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@ -74,6 +80,10 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@ -172,4 +182,120 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>dev-local-docker-image-build</id>
|
||||
<activation>
|
||||
<file>
|
||||
<exists>../.dev-local-docker-image-build</exists>
|
||||
</file>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Read docker plugin settings from properties file -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>properties-maven-plugin</artifactId>
|
||||
<version>1.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>read-docker-image-properties</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>read-project-properties</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<files>
|
||||
<file>../.dev-local-docker-image-build</file>
|
||||
</files>
|
||||
<outputFile/>
|
||||
<properties/>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>build-docker-image</id>
|
||||
<activation>
|
||||
<file><exists>.</exists></file>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Set docker image properties -->
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>set-docker-properties</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<exportAntProperties>true</exportAntProperties>
|
||||
<target unless="docker.image.tag">
|
||||
<property name="docker.image.tag" value="${project.version}"/>
|
||||
<property name="build.description" value=""/>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!--<execution>
|
||||
<id>print-docker-properties</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<echo>Print Docker image dev properties</echo>
|
||||
<echo>Image Tag: "${docker.image.tag}"</echo>
|
||||
<echo>Description: "${build.description}"</echo>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>-->
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Build docker image using docker-context folder -->
|
||||
<!--<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<version>${docker-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<verbose>true</verbose>
|
||||
<useColor>true</useColor>
|
||||
<images>
|
||||
<image>
|
||||
<name>${docker.image.name}:${docker.image.tag}</name>
|
||||
<build>
|
||||
<dockerFile>${project.basedir}/Dockerfile</dockerFile>
|
||||
<args>
|
||||
<EMS_USER>${docker.user}</EMS_USER>
|
||||
<EMS_HOME>${docker.user.home}</EMS_HOME>
|
||||
</args>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>docker-image-build</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>-->
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
@ -140,6 +140,13 @@ public class BaguetteClient implements ApplicationRunner {
|
||||
try {
|
||||
log.debug("BaguetteClient: Starting collector: {}...", collectorClass.getName());
|
||||
Collector collector = applicationContext.getBean(collectorClass);
|
||||
log.debug("BaguetteClient: Starting collector: {}: instance={}", collectorClass.getName(), collector);
|
||||
if (baguetteClientProperties.getCollectorConfigurations()!=null) {
|
||||
Object config = baguetteClientProperties.getCollectorConfigurations().get(collector.getName());
|
||||
log.debug("BaguetteClient: Starting collector: {}: collector-config={}", collectorClass.getName(), collector);
|
||||
if (config!=null)
|
||||
collector.setConfiguration(config);
|
||||
}
|
||||
collector.start();
|
||||
collectorsList.add(collector);
|
||||
log.debug("BaguetteClient: Starting collector: {}...ok", collectorClass.getName());
|
||||
|
@ -18,6 +18,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
@ -41,6 +42,7 @@ public class BaguetteClientProperties extends SshClientProperties {
|
||||
private int killDelay = 5;
|
||||
|
||||
private List<Class<? extends Collector>> collectorClasses;
|
||||
private Map<String,List<Map<String,Object>>> collectorConfigurations;
|
||||
|
||||
private String debugFakeIpAddress;
|
||||
|
||||
|
@ -12,5 +12,7 @@ package gr.iccs.imu.ems.baguette.client;
|
||||
import gr.iccs.imu.ems.util.Plugin;
|
||||
|
||||
public interface Collector extends Plugin {
|
||||
String getName();
|
||||
void setConfiguration(Object config);
|
||||
void activeGroupingChanged(String oldGrouping, String newGrouping);
|
||||
}
|
||||
|
@ -371,10 +371,14 @@ public class CommandExecutor {
|
||||
double upper = Double.parseDouble(args[4].trim());
|
||||
|
||||
if (eventGenerators.get(destination) == null) {
|
||||
/*
|
||||
EventGenerator generator = applicationContext.getBean(EventGenerator.class);
|
||||
generator.setBrokerUrl(brokerCepService.getBrokerCepProperties().getBrokerUrlForClients());
|
||||
generator.setBrokerUsername(brokerCepService.getBrokerUsername());
|
||||
generator.setBrokerPassword(brokerCepService.getBrokerPassword());
|
||||
*/
|
||||
EventGenerator generator = new EventGenerator((destinationName, event) ->
|
||||
sendEvent(null, destinationName, event)==CollectorContext.PUBLISH_RESULT.SENT);
|
||||
generator.setDestinationName(destination);
|
||||
generator.setLevel(1);
|
||||
generator.setInterval(interval);
|
||||
|
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
* Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package gr.iccs.imu.ems.baguette.client.collector.netdata;
|
||||
|
||||
import gr.iccs.imu.ems.baguette.client.Collector;
|
||||
import gr.iccs.imu.ems.baguette.client.collector.ClientCollectorContext;
|
||||
import gr.iccs.imu.ems.brokercep.event.EventMap;
|
||||
import gr.iccs.imu.ems.common.collector.CollectorContext;
|
||||
import gr.iccs.imu.ems.common.collector.netdata.NetdataCollectorProperties;
|
||||
import gr.iccs.imu.ems.util.EmsConstant;
|
||||
import gr.iccs.imu.ems.util.EventBus;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestClient;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Collects measurements from Netdata agents in a Kubernetes cluster
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class K8sNetdataCollector implements Collector, InitializingBean {
|
||||
protected final static Set<String> SENSOR_CONFIG_KEYS_EXCLUDED = Set.of("endpoint", "type", "_containerName");
|
||||
protected final static String NETDATA_DATA_API_V1_PATH = "/api/v1/data";
|
||||
protected final static String NETDATA_DATA_API_V2_PATH = "/api/v2/data";
|
||||
protected final static String DEFAULT_NETDATA_DATA_API_PATH = NETDATA_DATA_API_V2_PATH;
|
||||
|
||||
private final NetdataCollectorProperties properties;
|
||||
private final CollectorContext collectorContext;
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final EventBus<String, Object, Object> eventBus;
|
||||
private final RestClient restClient = RestClient.create();
|
||||
private final List<ScheduledFuture<?>> scheduledFuturesList = new LinkedList<>();
|
||||
private boolean started;
|
||||
private List<Map<String, Object>> configuration;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (!(collectorContext instanceof ClientCollectorContext))
|
||||
throw new IllegalArgumentException("Invalid CollectorContext provided. Expected: ClientCollectorContext, but got "+collectorContext.getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "netdata";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfiguration(Object config) {
|
||||
if (config instanceof List sensorConfigList) {
|
||||
configuration = sensorConfigList.stream()
|
||||
.filter(o -> o instanceof Map)
|
||||
.filter(map -> ((Map)map).keySet().stream().allMatch(k->k instanceof String))
|
||||
.toList();
|
||||
log.debug("K8sNetdataCollector: setConfiguration: {}", configuration);
|
||||
|
||||
// If configuration changes while collector running we need to restart it
|
||||
if (started) {
|
||||
log.debug("K8sNetdataCollector: setConfiguration: Restarting collector");
|
||||
stop();
|
||||
start();
|
||||
log.info("K8sNetdataCollector: setConfiguration: Restarted collector");
|
||||
}
|
||||
} else
|
||||
log.warn("K8sNetdataCollector: setConfiguration: Ignoring unsupported configuration object: {}", config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (started) return;
|
||||
if (configuration!=null)
|
||||
doStart();
|
||||
started = true;
|
||||
log.debug("K8sNetdataCollector: Started");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (!started) return;
|
||||
started = false;
|
||||
doStop();
|
||||
log.debug("K8sNetdataCollector: Stopped");
|
||||
}
|
||||
|
||||
private synchronized void doStart() {
|
||||
log.debug("K8sNetdataCollector: doStart(): BEGIN: configuration={}", configuration);
|
||||
log.trace("K8sNetdataCollector: doStart(): BEGIN: scheduledFuturesList={}", scheduledFuturesList);
|
||||
|
||||
// Get Netdata agent address and port from env. vars
|
||||
String netdataAddress = null;
|
||||
if (StringUtils.isBlank(netdataAddress)) netdataAddress = System.getenv("NETDATA_ADDRESS");
|
||||
if (StringUtils.isBlank(netdataAddress)) netdataAddress = System.getenv("NETDATA_IP");
|
||||
if (StringUtils.isBlank(netdataAddress)) netdataAddress = System.getenv("HOST_IP");
|
||||
if (StringUtils.isBlank(netdataAddress)) netdataAddress = "127.0.0.1";
|
||||
log.trace("K8sNetdataCollector: doStart(): netdataAddress={}", netdataAddress);
|
||||
|
||||
int netdataPort = Integer.parseInt(
|
||||
StringUtils.defaultIfBlank(System.getenv("NETDATA_PORT"), "19999").trim());
|
||||
final String baseUrl = String.format("http://%s:%d", netdataAddress.trim(), netdataPort);
|
||||
log.trace("K8sNetdataCollector: doStart(): baseUrl={}", baseUrl);
|
||||
|
||||
// Process each sensor configuration
|
||||
AtomicInteger sensorNum = new AtomicInteger(0);
|
||||
configuration.forEach(map -> {
|
||||
log.debug("K8sNetdataCollector: doStart(): Sensor-{}: map={}", sensorNum.incrementAndGet(), map);
|
||||
|
||||
// Check if it is a Pull sensor. (Push sensors are ignored)
|
||||
if ("true".equalsIgnoreCase( get(map, "pushSensor", "false") )) {
|
||||
log.debug("K8sNetdataCollector: doStart(): Sensor-{}: It is a Push sensor. Ignoring this sensor", sensorNum.get());
|
||||
return;
|
||||
}
|
||||
// else it is a Pull sensor
|
||||
|
||||
// Get destination (topic) and component name
|
||||
String destinationName = get(map, "name", null);
|
||||
log.trace("K8sNetdataCollector: doStart(): Sensor-{}: destination={}", sensorNum.get(), destinationName);
|
||||
if (StringUtils.isBlank(destinationName)) {
|
||||
log.warn("K8sNetdataCollector: doStart(): Sensor-{}: No destination found in sensor config: {}", sensorNum.get(), map);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get metric URL
|
||||
int apiVer;
|
||||
String url = null;
|
||||
String component = null;
|
||||
String context = null;
|
||||
String dimensions = "*";
|
||||
if (map.get("configuration") instanceof Map cfgMap) {
|
||||
log.trace("K8sNetdataCollector: doStart(): Sensor-{}: cfgMap={}", sensorNum.get(), cfgMap);
|
||||
|
||||
// Get component name
|
||||
component = get(cfgMap, "_containerName", null);
|
||||
log.trace("K8sNetdataCollector: doStart(): Sensor-{}: component={}", sensorNum.get(), component);
|
||||
|
||||
// Process 'configuration' map entries, to build metric URL
|
||||
Map<String, Object> sensorConfig = (Map<String, Object>) cfgMap;
|
||||
String endpoint = get(sensorConfig, "endpoint", DEFAULT_NETDATA_DATA_API_PATH);
|
||||
log.trace("K8sNetdataCollector: doStart(): Sensor-{}: endpoint={}", sensorNum.get(), endpoint);
|
||||
|
||||
if (NETDATA_DATA_API_V1_PATH.equalsIgnoreCase(endpoint)) {
|
||||
apiVer = 1;
|
||||
|
||||
// If expanded by a shorthand expression
|
||||
context = get(sensorConfig, EmsConstant.NETDATA_METRIC_KEY, null);
|
||||
if (StringUtils.isNotBlank(context))
|
||||
addEntryIfMissingOrBlank(sensorConfig, "context", context);
|
||||
|
||||
// Else check sensor config for 'context' key
|
||||
context = get(sensorConfig, "context", null);
|
||||
|
||||
addEntryIfMissingOrBlank(sensorConfig, "dimension", "*");
|
||||
addEntryIfMissingOrBlank(sensorConfig, "after", "-1");
|
||||
addEntryIfMissingOrBlank(sensorConfig, "group", "average");
|
||||
addEntryIfMissingOrBlank(sensorConfig, "format", "json2");
|
||||
} else
|
||||
if (NETDATA_DATA_API_V2_PATH.equalsIgnoreCase(endpoint)) {
|
||||
apiVer = 2;
|
||||
|
||||
// If expanded by a shorthand expression
|
||||
context = get(sensorConfig, EmsConstant.NETDATA_METRIC_KEY, null);
|
||||
if (StringUtils.isNotBlank(context))
|
||||
addEntryIfMissingOrBlank(sensorConfig, "scope_contexts", context);
|
||||
|
||||
// Else check sensor config for 'scope_contexts' or 'context' key
|
||||
context = get(sensorConfig, "scope_contexts", null);
|
||||
if (StringUtils.isBlank(context))
|
||||
context = get(sensorConfig, "context", null);
|
||||
|
||||
boolean isK8s = StringUtils.startsWithIgnoreCase(context, "k8s");
|
||||
if (isK8s) {
|
||||
addEntryIfMissingOrBlank(sensorConfig, "group_by", "label");
|
||||
addEntryIfMissingOrBlank(sensorConfig, "group_by_label", "k8s_pod_name");
|
||||
}
|
||||
addEntryIfMissingOrBlank(sensorConfig, "dimension", "*");
|
||||
addEntryIfMissingOrBlank(sensorConfig, "after", "-1");
|
||||
addEntryIfMissingOrBlank(sensorConfig, "time_group", "average");
|
||||
addEntryIfMissingOrBlank(sensorConfig, "format", "ssv");
|
||||
} else {
|
||||
log.warn("K8sNetdataCollector: doStart(): Sensor-{}: Invalid Netdata endpoint found in sensor config: {}", sensorNum.get(), map);
|
||||
return;
|
||||
}
|
||||
dimensions = get(sensorConfig, "dimension", dimensions);
|
||||
|
||||
StringBuilder sb = new StringBuilder(endpoint);
|
||||
final AtomicBoolean first = new AtomicBoolean(true);
|
||||
sensorConfig.forEach((key, value) -> {
|
||||
if (StringUtils.isNotBlank(key) && ! SENSOR_CONFIG_KEYS_EXCLUDED.contains(key)) {
|
||||
if (value instanceof String valueStr) {
|
||||
sb.append(first.get() ? "?" : "&").append(key).append("=").append(valueStr);
|
||||
first.set(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (StringUtils.isNotBlank(context)) {
|
||||
url = baseUrl + sb;
|
||||
} else {
|
||||
log.warn("K8sNetdataCollector: doStart(): Sensor-{}: No 'context' found in sensor configuration: {}", sensorNum.get(), map);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log.warn("K8sNetdataCollector: doStart(): Sensor-{}: No sensor configuration found is spec: {}", sensorNum.get(), map);
|
||||
return;
|
||||
}
|
||||
log.trace("K8sNetdataCollector: doStart(): Sensor-{}: Metric url={}", sensorNum.get(), url);
|
||||
|
||||
// Get interval and configure scheduler
|
||||
long period = 60;
|
||||
TimeUnit unit = TimeUnit.SECONDS;
|
||||
Duration duration = null;
|
||||
if (map.get("interval") instanceof Map intervalMap) {
|
||||
log.trace("K8sNetdataCollector: doStart(): Sensor-{}: intervalMap={}", sensorNum.get(), intervalMap);
|
||||
period = Long.parseLong(get(intervalMap, "period", Long.toString(period)));
|
||||
if (period>0) {
|
||||
String unitStr = get(intervalMap, "unit", unit.name());
|
||||
unit = StringUtils.isNotBlank(unitStr)
|
||||
? TimeUnit.valueOf(unitStr.toUpperCase().trim()) : TimeUnit.SECONDS;
|
||||
duration = Duration.of(period, unit.toChronoUnit());
|
||||
}
|
||||
}
|
||||
log.trace("K8sNetdataCollector: doStart(): Sensor-{}: duration-from-spec={}", sensorNum.get(), duration);
|
||||
if (duration==null) {
|
||||
duration = Duration.of(period, unit.toChronoUnit());
|
||||
}
|
||||
log.trace("K8sNetdataCollector: doStart(): Sensor-{}: duration={}", sensorNum.get(), duration);
|
||||
|
||||
final int apiVer1 = apiVer;
|
||||
final String url1 = url;
|
||||
final String component1 = component;
|
||||
scheduledFuturesList.add( taskScheduler.scheduleAtFixedRate(() -> {
|
||||
collectData(apiVer1, url1, destinationName, component1);
|
||||
}, duration) );
|
||||
log.debug("K8sNetdataCollector: doStart(): Sensor-{}: destination={}, component={}, interval={}, url={}",
|
||||
sensorNum.get(), destinationName, component, duration, url);
|
||||
log.info("K8sNetdataCollector: Collecting Netdata metric '{}.{}' into '{}', every {} {}",
|
||||
context, dimensions, destinationName, period, unit.name().toLowerCase());
|
||||
});
|
||||
log.trace("K8sNetdataCollector: doStart(): scheduledFuturesList={}", scheduledFuturesList);
|
||||
log.debug("K8sNetdataCollector: doStart(): END");
|
||||
}
|
||||
|
||||
private String get(Map<String,Object> map, String key, String defaultValue) {
|
||||
Object valObj;
|
||||
if (!map.containsKey(key) || (valObj = map.get(key))==null) return defaultValue;
|
||||
String value = valObj.toString();
|
||||
if (StringUtils.isBlank(value)) return defaultValue;
|
||||
return value;
|
||||
}
|
||||
|
||||
private void addEntryIfMissingOrBlank(Map<String, Object> map, String key, Object value) {
|
||||
if (map.containsKey(key) && map.get(key)!=null)
|
||||
if (map.get(key) instanceof String s && StringUtils.isNotBlank(s))
|
||||
return;
|
||||
map.put(key, value);
|
||||
}
|
||||
|
||||
private void collectData(int apiVer, String url, String destination, String component) {
|
||||
long startTm = System.currentTimeMillis();
|
||||
log.debug("K8sNetdataCollector: collectData(): BEGIN: apiVer={}, url={}, destination={}, component={}",
|
||||
apiVer, url, destination, component);
|
||||
|
||||
Map<String,Double> resultsMap = new HashMap<>();
|
||||
long timestamp = -1L;
|
||||
if (apiVer==1) {
|
||||
Map response = restClient.get()
|
||||
.uri(url)
|
||||
.retrieve()
|
||||
.body(Map.class);
|
||||
// Need to call for each pod separately
|
||||
// or get the total
|
||||
timestamp = Long.parseLong( response.get("before").toString() );
|
||||
List<String> chart_ids = (List) response.get("chart_ids");
|
||||
List<String> dimension_ids = (List) response.get("dimension_ids");
|
||||
List<String> latest_values = (List) response.get("view_latest_value");
|
||||
for (int i=0, n=chart_ids.size(); i<n; i++) {
|
||||
String id = chart_ids.get(i) + "|" + dimension_ids.get(i);
|
||||
try {
|
||||
// double v = values.get(i).doubleValue();
|
||||
double v = Double.parseDouble(latest_values.get(i));
|
||||
resultsMap.put(id, v);
|
||||
} catch (Exception e) {
|
||||
log.warn("K8sNetdataCollector: collectData(): ERROR at index #{}: id={}, value={}, Exception: ",
|
||||
i, id, latest_values.get(i), e);
|
||||
resultsMap.put(id, 0.0);
|
||||
}
|
||||
}
|
||||
} else
|
||||
if (apiVer==2) {
|
||||
log.warn("K8sNetdataCollector: collectData(): Calling Netdata: apiVer={}, url={}", apiVer, url);
|
||||
Map response = restClient.get()
|
||||
.uri(url)
|
||||
.retrieve()
|
||||
.body(Map.class);
|
||||
log.trace("K8sNetdataCollector: collectData(): apiVer={}, response={}", apiVer, response);
|
||||
|
||||
double result = Double.parseDouble( response.get("result").toString() );
|
||||
Map view = (Map) response.get("view");
|
||||
long after = Long.parseLong( view.get("after").toString() );
|
||||
long before = Long.parseLong( view.get("before").toString() );
|
||||
timestamp = before;
|
||||
log.trace("K8sNetdataCollector: collectData(): result={}, after={}, before={}", result, after, before);
|
||||
Map dimensions = (Map) view.get("dimensions");
|
||||
List<String> ids = (List<String>) dimensions.get("ids");
|
||||
List<String> units = (List<String>) dimensions.get("units");
|
||||
List<Number> values = (List<Number>) ((Map)dimensions.get("sts")).get("avg");
|
||||
log.trace("K8sNetdataCollector: collectData(): ids={}", ids);
|
||||
log.trace("K8sNetdataCollector: collectData(): units={}", units);
|
||||
log.trace("K8sNetdataCollector: collectData(): values={}", values);
|
||||
for (int i=0, n=ids.size(); i<n; i++) {
|
||||
try {
|
||||
double v = values.get(i).doubleValue();
|
||||
resultsMap.put(ids.get(i), v);
|
||||
} catch (Exception e) {
|
||||
log.warn("K8sNetdataCollector: collectData(): ERROR at index #{}: id={}, value={}, Exception: ",
|
||||
i, ids.get(i), values.get(i), e);
|
||||
resultsMap.put(ids.get(i), 0.0);
|
||||
}
|
||||
}
|
||||
//resultsMap.put("result", result);
|
||||
}
|
||||
|
||||
log.warn("K8sNetdataCollector: collectData(): Data collected: timestamp={}, results={}", timestamp, resultsMap);
|
||||
|
||||
// Publish collected data to destination
|
||||
final long timestamp1 = timestamp;
|
||||
Map<String, CollectorContext.PUBLISH_RESULT> publishResults = new LinkedHashMap<>();
|
||||
resultsMap.forEach((k,v) -> {
|
||||
publishResults.put( k+"="+v, publishMetricEvent(destination, k, v, timestamp1, null) );
|
||||
});
|
||||
log.debug("K8sNetdataCollector: collectData(): Events published: results={}", publishResults);
|
||||
|
||||
long endTm = System.currentTimeMillis();
|
||||
log.warn("K8sNetdataCollector: collectData(): END: duration={}ms", endTm-startTm);
|
||||
}
|
||||
|
||||
private synchronized void doStop() {
|
||||
log.debug("K8sNetdataCollector: doStop(): BEGIN");
|
||||
log.trace("K8sNetdataCollector: doStop(): BEGIN: scheduledFuturesList={}", scheduledFuturesList);
|
||||
// Cancel all task scheduler futures
|
||||
scheduledFuturesList.forEach(future -> future.cancel(true));
|
||||
scheduledFuturesList.clear();
|
||||
log.debug("K8sNetdataCollector: doStop(): END");
|
||||
}
|
||||
|
||||
public synchronized void activeGroupingChanged(String oldGrouping, String newGrouping) {
|
||||
log.debug("K8sNetdataCollector: activeGroupingChanged: {} --> {}", oldGrouping, newGrouping);
|
||||
}
|
||||
|
||||
protected CollectorContext.PUBLISH_RESULT publishMetricEvent(String metricName, String key, double metricValue, long timestamp, String nodeAddress) {
|
||||
EventMap event = new EventMap(metricValue, 1, timestamp);
|
||||
return sendEvent(metricName, metricName, key, event, null, true);
|
||||
}
|
||||
|
||||
private CollectorContext.PUBLISH_RESULT sendEvent(String metricName, String originalTopic, String key, EventMap event, String nodeAddress, boolean createDestination) {
|
||||
event.setEventProperty(EmsConstant.EVENT_PROPERTY_SOURCE_ADDRESS, nodeAddress);
|
||||
event.getEventProperties().put(EmsConstant.EVENT_PROPERTY_EFFECTIVE_DESTINATION, metricName);
|
||||
event.getEventProperties().put(EmsConstant.EVENT_PROPERTY_ORIGINAL_DESTINATION, originalTopic);
|
||||
event.getEventProperties().put(EmsConstant.EVENT_PROPERTY_KEY, key);
|
||||
log.debug("K8sNetdataCollector: Publishing metric: {}: {}", metricName, event.getMetricValue());
|
||||
CollectorContext.PUBLISH_RESULT result = collectorContext.sendEvent(null, metricName, event, createDestination);
|
||||
log.trace("K8sNetdataCollector: Publishing metric: {}: {} -> result: {}", metricName, event.getMetricValue(), result);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -21,11 +21,9 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Collects measurements from Netdata http server
|
||||
@ -33,6 +31,8 @@ import java.util.stream.Collectors;
|
||||
@Slf4j
|
||||
@Component
|
||||
public class NetdataCollector extends gr.iccs.imu.ems.common.collector.netdata.NetdataCollector implements Collector {
|
||||
private List<Map<String,Object>> configuration;
|
||||
|
||||
public NetdataCollector(@NonNull NetdataCollectorProperties properties,
|
||||
@NonNull CollectorContext collectorContext,
|
||||
@NonNull TaskScheduler taskScheduler,
|
||||
@ -43,6 +43,22 @@ public class NetdataCollector extends gr.iccs.imu.ems.common.collector.netdata.N
|
||||
throw new IllegalArgumentException("Invalid CollectorContext provided. Expected: ClientCollectorContext, but got "+collectorContext.getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "netdata";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfiguration(Object config) {
|
||||
if (config instanceof List sensorConfigList) {
|
||||
configuration = sensorConfigList.stream()
|
||||
.filter(o -> o instanceof Map)
|
||||
.filter(map -> ((Map)map).keySet().stream().allMatch(k->k instanceof String))
|
||||
.toList();
|
||||
log.info("Collectors::Netdata: setConfiguration: {}", configuration);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void activeGroupingChanged(String oldGrouping, String newGrouping) {
|
||||
HashSet<String> topics = new HashSet<>();
|
||||
for (String g : GROUPING.getNames()) {
|
||||
|
@ -21,11 +21,9 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Collects measurements from Prometheus exporter
|
||||
@ -33,6 +31,8 @@ import java.util.stream.Collectors;
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PrometheusCollector extends gr.iccs.imu.ems.common.collector.prometheus.PrometheusCollector implements Collector {
|
||||
private List<Map<String,Object>> configuration;
|
||||
|
||||
public PrometheusCollector(@NonNull PrometheusCollectorProperties properties,
|
||||
@NonNull CollectorContext collectorContext,
|
||||
@NonNull TaskScheduler taskScheduler,
|
||||
@ -43,6 +43,22 @@ public class PrometheusCollector extends gr.iccs.imu.ems.common.collector.promet
|
||||
throw new IllegalArgumentException("Invalid CollectorContext provided. Expected: ClientCollectorContext, but got "+collectorContext.getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "prometheus";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfiguration(Object config) {
|
||||
if (config instanceof List sensorConfigList) {
|
||||
configuration = sensorConfigList.stream()
|
||||
.filter(o -> o instanceof Map)
|
||||
.filter(map -> ((Map)map).keySet().stream().allMatch(k->k instanceof String))
|
||||
.toList();
|
||||
log.info("Collectors::Prometheus: setConfiguration: {}", configuration);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void activeGroupingChanged(String oldGrouping, String newGrouping) {
|
||||
HashSet<String> topics = new HashSet<>();
|
||||
for (String g : GROUPING.getNames()) {
|
||||
|
@ -63,6 +63,10 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -49,6 +49,7 @@ public class BaguetteServer implements InitializingBean, EventBus.EventConsumer<
|
||||
@Getter
|
||||
private final SelfHealingManager<NodeRegistryEntry> selfHealingManager;
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final ConfigWriteService configWriteService;
|
||||
|
||||
private Sshd server;
|
||||
|
||||
@ -147,6 +148,8 @@ public class BaguetteServer implements InitializingBean, EventBus.EventConsumer<
|
||||
|
||||
public BrokerCepService getBrokerCepService() { return brokerCepService; }
|
||||
|
||||
public Map<String, String> getServerConnectionInfo() { return server.getServerConnectionInfo(); }
|
||||
|
||||
public String getServerPubkey() { return server.getPublicKey(); }
|
||||
|
||||
public String getServerPubkeyFingerprint() { return server.getPublicKeyFingerprint(); }
|
||||
@ -162,13 +165,26 @@ public class BaguetteServer implements InitializingBean, EventBus.EventConsumer<
|
||||
if (server == null) {
|
||||
eventBus.subscribe(RecoveryConstant.SELF_HEALING_RECOVERY_GIVE_UP, this);
|
||||
|
||||
// Start SSH server
|
||||
log.info("BaguetteServer.startServer(): Starting SSH server...");
|
||||
nodeRegistry.setCoordinator(coordinator);
|
||||
Sshd server = new Sshd();
|
||||
server.start(config, coordinator, eventBus, nodeRegistry);
|
||||
server.setNodeRegistry(getNodeRegistry());
|
||||
//server.setNodeRegistry(getNodeRegistry());
|
||||
this.server = server;
|
||||
log.info("BaguetteServer.startServer(): Starting SSH server... done");
|
||||
|
||||
// Store Baguette Server connection info in a properties file
|
||||
try {
|
||||
configWriteService
|
||||
.getOrCreateConfigFile(
|
||||
EmsConstant.EMS_CLIENT_K8S_CONFIG_MAP_FILE,
|
||||
EmsConstant.EMS_CLIENT_K8S_CONFIG_MAP_FORMAT)
|
||||
.putAll(server.getServerConnectionInfo());
|
||||
} catch (Exception e) {
|
||||
log.error("BaguetteServer.startServer(): Failed to store connection info in ems-client-config-map: {}, Exception: ",
|
||||
EmsConstant.EMS_CLIENT_K8S_CONFIG_MAP_FILE, e);
|
||||
}
|
||||
} else {
|
||||
log.info("BaguetteServer.startServer(): SSH server is already running");
|
||||
}
|
||||
|
@ -164,9 +164,23 @@ public class ClientShellCommand implements Command, Runnable, ServerSessionAware
|
||||
if (entry!=null) {
|
||||
setNodeRegistryEntry(entry);
|
||||
} else {
|
||||
log.error("{}--> initNodeRegistryEntry: No node registry entry found for client: address={}", id, address);
|
||||
log.error("{}--> initNodeRegistryEntry: Marked client session for immediate close: address={}", id, address);
|
||||
setCloseConnection(true);
|
||||
log.info("{}--> initNodeRegistryEntry: No node registry entry found for client: address={}", id, address);
|
||||
if (coordinator.allowNotPreregisteredNode(this)) {
|
||||
try {
|
||||
log.info("{}--> initNodeRegistryEntry: Preregistering new client: address={}", id, address);
|
||||
HashMap<String, String> nodeInfo = new HashMap<>();
|
||||
nodeInfo.put("address", address);
|
||||
entry = coordinator.getServer().registerClient(nodeInfo);
|
||||
setNodeRegistryEntry(entry);
|
||||
} catch (Exception e) {
|
||||
log.error("{}--> initNodeRegistryEntry: Exception while registering new client: address={}, EXCEPTION: ", id, address, e);
|
||||
log.error("{}--> initNodeRegistryEntry: Marked client session for immediate close: address={}", id, address);
|
||||
setCloseConnection(true);
|
||||
}
|
||||
} else {
|
||||
log.error("{}--> initNodeRegistryEntry: Marked client session for immediate close: address={}", id, address);
|
||||
setCloseConnection(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,17 +38,22 @@ public class NodeRegistry {
|
||||
|
||||
// Get IP address from provided hostname or address
|
||||
Throwable errorObj = null;
|
||||
try {
|
||||
log.debug("NodeRegistry.addNode(): Resolving IP address from provided hostname/address: {}", hostnameOrAddress);
|
||||
InetAddress host = InetAddress.getByName(hostnameOrAddress);
|
||||
log.trace("NodeRegistry.addNode(): InetAddress for provided hostname/address: {}, InetAddress: {}", hostnameOrAddress, host);
|
||||
String resolvedIpAddress = host.getHostAddress();
|
||||
log.info("NodeRegistry.addNode(): Provided-Address={}, Resolved-IP-Address={}", hostnameOrAddress, resolvedIpAddress);
|
||||
ipAddress = resolvedIpAddress;
|
||||
} catch (UnknownHostException e) {
|
||||
log.error("NodeRegistry.addNode(): EXCEPTION while resolving IP address from provided hostname/address: {}\n", ipAddress, e);
|
||||
errorObj = e;
|
||||
//throw e;
|
||||
if (StringUtils.isNotBlank(ipAddress)) {
|
||||
try {
|
||||
log.debug("NodeRegistry.addNode(): Resolving IP address from provided hostname/address: {}", hostnameOrAddress);
|
||||
InetAddress host = InetAddress.getByName(hostnameOrAddress);
|
||||
log.trace("NodeRegistry.addNode(): InetAddress for provided hostname/address: {}, InetAddress: {}", hostnameOrAddress, host);
|
||||
String resolvedIpAddress = host.getHostAddress();
|
||||
log.info("NodeRegistry.addNode(): Provided-Address={}, Resolved-IP-Address={}", hostnameOrAddress, resolvedIpAddress);
|
||||
ipAddress = resolvedIpAddress;
|
||||
} catch (UnknownHostException e) {
|
||||
log.error("NodeRegistry.addNode(): EXCEPTION while resolving IP address from provided hostname/address: {}\n", ipAddress, e);
|
||||
errorObj = e;
|
||||
//throw e;
|
||||
}
|
||||
} else {
|
||||
ipAddress = "unknown-address-"+System.currentTimeMillis(); // We don't know POD address
|
||||
nodeInfo.put("address", ipAddress);
|
||||
}
|
||||
nodeInfo.put("original-address", hostnameOrAddress);
|
||||
nodeInfo.put("address", ipAddress);
|
||||
|
@ -142,6 +142,20 @@ public class Sshd {
|
||||
log.info("SSH server: Stopped");
|
||||
}
|
||||
|
||||
public Map<String,String> getServerConnectionInfo() {
|
||||
Map.Entry<String, String> credentials = configuration.getCredentials().getPreferredPair();
|
||||
return Map.of(
|
||||
"BAGUETTE_SERVER_ADDRESS", configuration.getServerAddress(),
|
||||
"BAGUETTE_SERVER_PORT", Integer.toString(configuration.getServerPort()),
|
||||
"BAGUETTE_SERVER_PUBKEY", StringEscapeUtils.unescapeJson(serverPubkey),
|
||||
"BAGUETTE_SERVER_PUBKEY_FINGERPRINT", serverPubkeyFingerprint,
|
||||
"BAGUETTE_SERVER_PUBKEY_ALGORITHM", serverPubkeyAlgorithm,
|
||||
"BAGUETTE_SERVER_PUBKEY_FORMAT", serverPubkeyFormat,
|
||||
"BAGUETTE_SERVER_USERNAME", credentials.getKey(),
|
||||
"BAGUETTE_SERVER_PASSWORD", credentials.getValue()
|
||||
);
|
||||
}
|
||||
|
||||
public void startHeartbeat(long period) {
|
||||
heartbeatOn = true;
|
||||
Thread heartbeat = new Thread(
|
||||
|
@ -97,7 +97,7 @@ public class NoopCoordinator implements ServerCoordinator {
|
||||
if (!checkStarted && started) {
|
||||
log.warn("{}: {}(): Coordinator is already running{}", className, methodName, str);
|
||||
} else {
|
||||
log.info("{}: {}(): Method invoked{}", className, methodName, str);
|
||||
log.debug("{}: {}(): Method invoked{}", className, methodName, str);
|
||||
}
|
||||
return started;
|
||||
}
|
||||
|
@ -27,6 +27,10 @@ public class ClusterSelfHealing {
|
||||
// Server-side self-healing methods
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public boolean isEnabled() {
|
||||
return selfHealingManager.isEnabled();
|
||||
}
|
||||
|
||||
List<NodeRegistryEntry> getAggregatorCapableNodesInZone(IClusterZone zone) {
|
||||
// Get the normal nodes in the zone that can be Aggregators (i.e. Aggregator and candidates)
|
||||
List<NodeRegistryEntry> aggregatorCapableNodes = zone.findAggregatorCapableNodes();
|
||||
|
@ -89,8 +89,12 @@ public class ClusteringCoordinator extends NoopCoordinator {
|
||||
topLevelGrouping, aggregatorGrouping, lastLevelGrouping);
|
||||
|
||||
// Configure Self-Healing manager
|
||||
clusterSelfHealing = new ClusterSelfHealing(server.getSelfHealingManager());
|
||||
server.getSelfHealingManager().setMode(SelfHealingManager.MODE.INCLUDED);
|
||||
if (server.getSelfHealingManager().isEnabled()) {
|
||||
clusterSelfHealing = new ClusterSelfHealing(server.getSelfHealingManager());
|
||||
server.getSelfHealingManager().setMode(SelfHealingManager.MODE.INCLUDED);
|
||||
} else {
|
||||
clusterSelfHealing = null;
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@ -336,9 +340,11 @@ public class ClusteringCoordinator extends NoopCoordinator {
|
||||
log.trace("addNodeInTopology: CSC is in zone: client={}, address={}, zone={}", csc.getId(), csc.getClientIpAddress(), zone.getId());
|
||||
|
||||
// Self-healing-related actions
|
||||
List<NodeRegistryEntry> aggregatorCapableNodes = clusterSelfHealing.getAggregatorCapableNodesInZone(zone);
|
||||
clusterSelfHealing.updateNodesSelfHealingMonitoring(zone, aggregatorCapableNodes);
|
||||
clusterSelfHealing.removeResourceLimitedNodeSelfHealingMonitoring(zone, aggregatorCapableNodes);
|
||||
if (clusterSelfHealing!=null && clusterSelfHealing.isEnabled()) {
|
||||
List<NodeRegistryEntry> aggregatorCapableNodes = clusterSelfHealing.getAggregatorCapableNodesInZone(zone);
|
||||
clusterSelfHealing.updateNodesSelfHealingMonitoring(zone, aggregatorCapableNodes);
|
||||
clusterSelfHealing.removeResourceLimitedNodeSelfHealingMonitoring(zone, aggregatorCapableNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,11 +377,13 @@ public class ClusteringCoordinator extends NoopCoordinator {
|
||||
}
|
||||
|
||||
// Self-healing-related actions
|
||||
List<NodeRegistryEntry> aggregatorCapableNodes = clusterSelfHealing.getAggregatorCapableNodesInZone(zone);
|
||||
clusterSelfHealing.updateNodesSelfHealingMonitoring(zone, aggregatorCapableNodes);
|
||||
if (aggregatorCapableNodes.isEmpty())
|
||||
; //XXX: TODO: ??Reconfigure non-candidate nodes to forward their events to EMS server??
|
||||
clusterSelfHealing.addResourceLimitedNodeSelfHealingMonitoring(zone, aggregatorCapableNodes);
|
||||
if (clusterSelfHealing!=null && clusterSelfHealing.isEnabled()) {
|
||||
List<NodeRegistryEntry> aggregatorCapableNodes = clusterSelfHealing.getAggregatorCapableNodesInZone(zone);
|
||||
clusterSelfHealing.updateNodesSelfHealingMonitoring(zone, aggregatorCapableNodes);
|
||||
if (aggregatorCapableNodes.isEmpty())
|
||||
; //XXX: TODO: ??Reconfigure non-candidate nodes to forward their events to EMS server??
|
||||
clusterSelfHealing.addResourceLimitedNodeSelfHealingMonitoring(zone, aggregatorCapableNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,8 @@ public class BaguetteServerProperties implements InitializingBean {
|
||||
private String keyFile = "hostkey.pem";
|
||||
public String getServerKeyFile() { return keyFile; }
|
||||
|
||||
private String connectionInfoFile = "baguette-server-connection-info.json";
|
||||
|
||||
private boolean heartbeatEnabled;
|
||||
@Min(-1)
|
||||
private long heartbeatPeriod = 60000;
|
||||
|
34
nebulous/ems-core/bin/k8s-helper.sh
Normal file
34
nebulous/ems-core/bin/k8s-helper.sh
Normal file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
# Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
# If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
# https://www.mozilla.org/en-US/MPL/2.0/
|
||||
#
|
||||
|
||||
EMS_CLIENT_K8S_CONFIG_MAP_NAME=ems-client-configmap
|
||||
EMS_CLIENT_K8S_CONFIG_MAP_FILE=ems-client-configmap.json
|
||||
K8S_OUTPUT_DIR=$EMS_CONFIG_DIR/k8s
|
||||
K8S_OUTPUT_FILE=$K8S_OUTPUT_DIR/cfgmap_output.json
|
||||
|
||||
# Check if baguette server connection info file exists
|
||||
[ ! -f $EMS_CLIENT_K8S_CONFIG_MAP_FILE ] && exit 1
|
||||
|
||||
# Read baguette server connection info file into a variable
|
||||
BSCI=$( tr -d "\r\n" < $EMS_CLIENT_K8S_CONFIG_MAP_FILE )
|
||||
#echo $BSCI
|
||||
|
||||
# Write baguette server connection info into ems-client-configmap
|
||||
mkdir -p $K8S_OUTPUT_DIR/
|
||||
echo "/* Date: $(date) */" > $K8S_OUTPUT_FILE
|
||||
sec=/var/run/secrets/kubernetes.io/serviceaccount
|
||||
curl -sS \
|
||||
-H "Authorization: Bearer $(cat $sec/token)" \
|
||||
-H "Content-Type: application/json-patch+json" \
|
||||
--cacert $sec/ca.crt \
|
||||
--request PATCH \
|
||||
--data "[{\"op\": \"replace\", \"path\": \"/data\", \"value\": $BSCI}]" \
|
||||
https://$KUBERNETES_SERVICE_HOST/api/v1/namespaces/$(cat $sec/namespace)/configmaps/$EMS_CLIENT_K8S_CONFIG_MAP_NAME \
|
||||
&>> $K8S_OUTPUT_FILE
|
@ -13,7 +13,7 @@ set PWD=%~dp0
|
||||
cd %PWD%..
|
||||
set BASEDIR=%cd%
|
||||
IF NOT DEFINED EMS_CONFIG_DIR set EMS_CONFIG_DIR=%BASEDIR%\config-files
|
||||
IF NOT DEFINED PAASAGE_CONFIG_DIR set PAASAGE_CONFIG_DIR=%BASEDIR%\config-files
|
||||
:: IF NOT DEFINED PAASAGE_CONFIG_DIR set PAASAGE_CONFIG_DIR=%BASEDIR%\config-files
|
||||
IF NOT DEFINED JARS_DIR set JARS_DIR=%BASEDIR%\control-service\target
|
||||
IF NOT DEFINED LOGS_DIR set LOGS_DIR=%BASEDIR%\logs
|
||||
IF NOT DEFINED PUBLIC_DIR set PUBLIC_DIR=%BASEDIR%\public_resources
|
||||
@ -31,7 +31,7 @@ if "%EMS_SECRETS_FILE%"=="" (
|
||||
set EMS_SECRETS_FILE=%EMS_CONFIG_DIR%\secrets.properties
|
||||
)
|
||||
if "%EMS_CONFIG_LOCATION%"=="" (
|
||||
set EMS_CONFIG_LOCATION=classpath:rule-templates.yml,optional:file:%EMS_CONFIG_DIR%\ems-server.yml,optional:file:%EMS_CONFIG_DIR%\ems-server.properties,optional:file:%EMS_CONFIG_DIR%\ems.yml,optional:file:%EMS_CONFIG_DIR%\ems.properties,optional:file:%EMS_SECRETS_FILE%
|
||||
set EMS_CONFIG_LOCATION=optional:classpath:rule-templates.yml,optional:file:%EMS_CONFIG_DIR%\ems-server.yml,optional:file:%EMS_CONFIG_DIR%\ems-server.properties,optional:file:%EMS_CONFIG_DIR%\ems.yml,optional:file:%EMS_CONFIG_DIR%\ems.properties,optional:file:%EMS_SECRETS_FILE%
|
||||
)
|
||||
|
||||
:: Check logger configuration
|
||||
@ -46,6 +46,9 @@ if "%LOG_FILE%"=="" (
|
||||
:: Set shell encoding to UTF-8 (in order to display banner correctly)
|
||||
chcp 65001
|
||||
|
||||
:: Create default models directory
|
||||
mkdir %BASEDIR%\models
|
||||
|
||||
:: Run EMS server
|
||||
rem Uncomment next line to set JAVA runtime options
|
||||
rem set JAVA_OPTS=-Djavax.net.debug=all
|
||||
|
@ -13,7 +13,7 @@ PREVWORKDIR=`pwd`
|
||||
BASEDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )
|
||||
cd ${BASEDIR}
|
||||
if [[ -z $EMS_CONFIG_DIR ]]; then EMS_CONFIG_DIR=$BASEDIR/config-files; export EMS_CONFIG_DIR; fi
|
||||
if [[ -z $PAASAGE_CONFIG_DIR ]]; then PAASAGE_CONFIG_DIR=$BASEDIR/config-files; export PAASAGE_CONFIG_DIR; fi
|
||||
#if [[ -z $PAASAGE_CONFIG_DIR ]]; then PAASAGE_CONFIG_DIR=$BASEDIR/config-files; export PAASAGE_CONFIG_DIR; fi
|
||||
if [[ -z $JARS_DIR ]]; then JARS_DIR=$BASEDIR/control-service/target; export JARS_DIR; fi
|
||||
if [[ -z $LOGS_DIR ]]; then LOGS_DIR=$BASEDIR/logs; export LOGS_DIR; fi
|
||||
if [[ -z $PUBLIC_DIR ]]; then PUBLIC_DIR=$BASEDIR/public_resources; export PUBLIC_DIR; fi
|
||||
@ -34,7 +34,7 @@ if [[ -z "$EMS_SECRETS_FILE" ]]; then
|
||||
EMS_SECRETS_FILE=$EMS_CONFIG_DIR/secrets.properties
|
||||
fi
|
||||
if [[ -z "$EMS_CONFIG_LOCATION" ]]; then
|
||||
EMS_CONFIG_LOCATION=classpath:rule-templates.yml,optional:file:$EMS_CONFIG_DIR/ems-server.yml,optional:file:$EMS_CONFIG_DIR/ems-server.properties,optional:file:$EMS_CONFIG_DIR/ems.yml,optional:file:$EMS_CONFIG_DIR/ems.properties,optional:file:$EMS_SECRETS_FILE
|
||||
EMS_CONFIG_LOCATION=optional:classpath:rule-templates.yml,optional:file:$EMS_CONFIG_DIR/ems-server.yml,optional:file:$EMS_CONFIG_DIR/ems-server.properties,optional:file:$EMS_CONFIG_DIR/ems.yml,optional:file:$EMS_CONFIG_DIR/ems.properties,optional:file:$EMS_SECRETS_FILE
|
||||
fi
|
||||
|
||||
# Check logger configuration
|
||||
@ -52,6 +52,9 @@ export LANG=C.UTF-8
|
||||
# Setup TERM & INT signal handler
|
||||
trap 'echo "Signaling EMS to exit"; kill -TERM "${emsPid}"; wait "${emsPid}"; ' SIGTERM SIGINT
|
||||
|
||||
# Create default models directory
|
||||
mkdir -p ${BASEDIR}/models
|
||||
|
||||
# Run EMS server
|
||||
# Uncomment next line to set JAVA runtime options
|
||||
#JAVA_OPTS=-Djavax.net.debug=all
|
||||
|
@ -14,6 +14,8 @@ import gr.iccs.imu.ems.brokercep.cep.CepService;
|
||||
import gr.iccs.imu.ems.brokercep.event.EventMap;
|
||||
import gr.iccs.imu.ems.brokercep.properties.BrokerCepProperties;
|
||||
import gr.iccs.imu.ems.util.StrUtil;
|
||||
import jakarta.jms.*;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.activemq.broker.BrokerService;
|
||||
@ -27,11 +29,12 @@ import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.jms.*;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
@ -59,6 +62,9 @@ public class BrokerCepConsumer implements MessageListener, InitializingBean, App
|
||||
|
||||
private final EventCache eventCache;
|
||||
|
||||
@Getter
|
||||
private final List<MessageListener> listeners = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
initialize();
|
||||
@ -123,36 +129,28 @@ public class BrokerCepConsumer implements MessageListener, InitializingBean, App
|
||||
}
|
||||
|
||||
public synchronized void addQueue(String queueName) {
|
||||
log.debug("BrokerCepConsumer.addQueue(): Adding queue: {}", queueName);
|
||||
if (addedDestinations.containsKey(queueName)) {
|
||||
log.debug("BrokerCepConsumer.addQueue(): Queue already added: {}", queueName);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Queue queue = session.createQueue(queueName);
|
||||
MessageConsumer consumer = session.createConsumer(queue);
|
||||
consumer.setMessageListener(this);
|
||||
addedDestinations.put(queueName, consumer);
|
||||
log.debug("BrokerCepConsumer.addQueue(): Added queue: {}", queueName);
|
||||
} catch (Exception ex) {
|
||||
log.error("BrokerCepConsumer.addQueue(): EXCEPTION: ", ex);
|
||||
}
|
||||
addDestinationAndListener(queueName, false, this);
|
||||
}
|
||||
|
||||
public synchronized void addTopic(String topicName) {
|
||||
log.debug("BrokerCepConsumer.addTopic(): Adding topic: {}", topicName);
|
||||
if (addedDestinations.containsKey(topicName)) {
|
||||
log.debug("BrokerCepConsumer.addTopic(): Topic already added: {}", topicName);
|
||||
addDestinationAndListener(topicName, true, this);
|
||||
}
|
||||
|
||||
private synchronized void addDestinationAndListener(String destinationName, boolean isTopic, MessageListener listener) {
|
||||
log.debug("BrokerCepConsumer.addDestinationAndListener(): Adding destination: {}", destinationName);
|
||||
if (addedDestinations.containsKey(destinationName)) {
|
||||
log.debug("BrokerCepConsumer.addDestinationAndListener(): Destination already added: {}", destinationName);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Topic topic = session.createTopic(topicName);
|
||||
MessageConsumer consumer = session.createConsumer(topic);
|
||||
consumer.setMessageListener(this);
|
||||
addedDestinations.put(topicName, consumer);
|
||||
log.debug("BrokerCepConsumer.addTopic(): Added topic: {}", topicName);
|
||||
Destination destination = isTopic
|
||||
? session.createTopic(destinationName) : session.createQueue(destinationName);
|
||||
MessageConsumer consumer = session.createConsumer(destination);
|
||||
consumer.setMessageListener(listener);
|
||||
addedDestinations.put(destinationName, consumer);
|
||||
log.debug("BrokerCepConsumer.addDestinationAndListener(): Added destination: {}", destinationName);
|
||||
} catch (Exception ex) {
|
||||
log.error("BrokerCepConsumer.addTopic(): EXCEPTION: ", ex);
|
||||
log.error("BrokerCepConsumer.addDestinationAndListener(): EXCEPTION: ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,6 +222,10 @@ public class BrokerCepConsumer implements MessageListener, InitializingBean, App
|
||||
log.warn("BrokerCepConsumer.onMessage(): Message ignored: type={}", message.getClass().getName());
|
||||
}
|
||||
eventCounter.incrementAndGet();
|
||||
|
||||
// Notify listeners
|
||||
listeners.forEach(l -> l.onMessage(message));
|
||||
|
||||
} catch (Exception ex) {
|
||||
log.error("BrokerCepConsumer.onMessage(): EXCEPTION: ", ex);
|
||||
eventFailuresCounter.incrementAndGet();
|
||||
|
@ -300,7 +300,7 @@ public class BrokerConfig implements InitializingBean {
|
||||
List<BrokerPlugin> plugins = new ArrayList<>();
|
||||
if (getBrokerAuthenticationPlugin()!=null) plugins.add(getBrokerAuthenticationPlugin());
|
||||
if (getBrokerAuthorizationPlugin()!=null) plugins.add(getBrokerAuthorizationPlugin());
|
||||
if (plugins.size() > 0) {
|
||||
if (!plugins.isEmpty()) {
|
||||
brokerService.setPlugins(plugins.toArray(new BrokerPlugin[0]));
|
||||
}
|
||||
|
||||
|
@ -46,8 +46,9 @@ public class SourceAddressMessageUpdateInterceptor extends AbstractMessageInterc
|
||||
boolean isLocal = StringUtils.isBlank(address) || NetUtil.isLocalAddress(address.trim());
|
||||
if (isLocal) {
|
||||
log.trace("SourceAddressMessageUpdateInterceptor: Producer host is local. Getting our public IP address");
|
||||
address = NetUtil.getPublicIpAddress();
|
||||
log.trace("SourceAddressMessageUpdateInterceptor: Producer host (public): {}", address);
|
||||
address = NetUtil.getIpSettingAddress();
|
||||
log.trace("SourceAddressMessageUpdateInterceptor: Producer host ({}): {}",
|
||||
NetUtil.isUsePublic() ? "public" : "default", address);
|
||||
} else {
|
||||
log.trace("SourceAddressMessageUpdateInterceptor: Producer host is not local. Ok");
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import java.util.stream.Collectors;
|
||||
public class MathUtil {
|
||||
static {
|
||||
License.iConfirmNonCommercialUse("EMS-"+ NetUtil.getIpAddress());
|
||||
mXparser.disableImpliedMultiplicationMode();
|
||||
}
|
||||
private static final Map<String, Function> functions = new HashMap<>();
|
||||
private static final Map<String, Constant> constants = new HashMap<>();
|
||||
@ -118,8 +119,20 @@ public class MathUtil {
|
||||
|
||||
// Get token names
|
||||
List<Token> initTokens = e.getCopyOfInitialTokens();
|
||||
log.debug("MathUtil: initial-tokens={}", initTokens);
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("MathUtil: initial-tokens={}", initTokens.stream()
|
||||
.map(token -> Map.of(
|
||||
"tokenId", token.tokenId,
|
||||
"tokenType", token.tokenTypeId,
|
||||
"tokenStr", token.tokenStr,
|
||||
"tokenValue", token.tokenValue,
|
||||
"looksLike", token.looksLike,
|
||||
"keyWord", token.keyWord,
|
||||
"tokenLevel", token.tokenLevel,
|
||||
"is-identifier", token.isIdentifier()
|
||||
)
|
||||
).toList()
|
||||
);
|
||||
mXparser.consolePrintTokens(initTokens);
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
package gr.iccs.imu.ems.brokercep.event;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import gr.iccs.imu.ems.util.StrUtil;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@ -29,7 +30,7 @@ import java.util.stream.Collectors;
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class EventMap extends LinkedHashMap<String, Object> implements Serializable {
|
||||
|
||||
private static Gson gson;
|
||||
private static Gson gson = new GsonBuilder().create();
|
||||
private static AtomicLong eventIdSequence = new AtomicLong(0);
|
||||
|
||||
// Standard/Known Event fields configuration
|
||||
|
@ -31,6 +31,7 @@ import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@ -98,9 +99,9 @@ public class BrokerClientApp {
|
||||
String url = processUrlArg( args[aa++] );
|
||||
String topic = args[aa++];
|
||||
String type = args[aa].startsWith("-T") ? args[aa++].substring(2) : "text";
|
||||
boolean processPlaceholders = !args[aa].startsWith("-PP") || Boolean.parseBoolean(args[aa++].substring(3));
|
||||
String payload = args[aa++];
|
||||
payload = payload
|
||||
.replaceAll("%TIMESTAMP%|%TS%", ""+System.currentTimeMillis());
|
||||
payload = getPayload(payload, processPlaceholders);
|
||||
EventMap event = gson.fromJson(payload, EventMap.class);
|
||||
sendEvent(url, username, password, topic, type, event, collectProperties(args, aa));
|
||||
} else
|
||||
@ -108,9 +109,9 @@ public class BrokerClientApp {
|
||||
String url = processUrlArg( args[aa++] );
|
||||
String topic = args[aa++];
|
||||
String type = args[aa].startsWith("-T") ? args[aa++].substring(2) : "text";
|
||||
boolean processPlaceholders = !args[aa].startsWith("-PP") || Boolean.parseBoolean(args[aa++].substring(3));
|
||||
String payload = args[aa++];
|
||||
payload = payload
|
||||
.replaceAll("%TIMESTAMP%|%TS%", ""+System.currentTimeMillis());
|
||||
payload = getPayload(payload, processPlaceholders);
|
||||
Map<String, String> properties = collectProperties(args, aa);
|
||||
if ("map".equalsIgnoreCase(type)) {
|
||||
EventMap event = gson.fromJson(payload, EventMap.class);
|
||||
@ -224,6 +225,24 @@ public class BrokerClientApp {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getPayload(String payload, boolean processPlaceholders) throws IOException {
|
||||
if (payload==null) return null;
|
||||
String payloadTrim = payload.trim();
|
||||
if (StringUtils.startsWith(payloadTrim, "@")) {
|
||||
payload = Files.readString(Paths.get(StringUtils.substring(payloadTrim, 1)));
|
||||
}
|
||||
if ("-".equals(payloadTrim)) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
|
||||
payload = reader.lines().collect(Collectors.joining("\n"));
|
||||
}
|
||||
if (processPlaceholders) {
|
||||
payload = payload
|
||||
.replaceAll("%TIMESTAMP%|%TS%", ""+System.currentTimeMillis());
|
||||
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
private static Map<String, String> collectProperties(String[] args, int aa) {
|
||||
return Arrays.stream(args, aa, args.length)
|
||||
.map(s->s.split("[=:]",2))
|
||||
@ -741,9 +760,11 @@ public class BrokerClientApp {
|
||||
log.info("BrokerClientApp: Usage: ");
|
||||
log.info("BrokerClientApp: client list [-U<USERNAME> [-P<PASSWORD]] <URL> ");
|
||||
log.info("BrokerClientApp: client publish [ -U<USERNAME> [-P<PASSWORD]] <URL> <TOPIC> [-T<MSG-TYPE>] <VALUE> <LEVEL> [<PROPERTY>]*");
|
||||
log.info("BrokerClientApp: client publish2 [-U<USERNAME> [-P<PASSWORD]] <URL> <TOPIC> [-T<MSG-TYPE>] <JSON-PAYLOAD> [<PROPERTY>]*");
|
||||
log.info("BrokerClientApp: client publish3 [-U<USERNAME> [-P<PASSWORD]] <URL> <TOPIC> [-T<MSG-TYPE>] <TEXT-PAYLOAD> [<PROPERTY>]*");
|
||||
log.info("BrokerClientApp: client publish2 [-U<USERNAME> [-P<PASSWORD]] <URL> <TOPIC> [-T<MSG-TYPE>] [-PP<true|false>] <JSON-PAYLOAD|-|@file> [<PROPERTY>]*");
|
||||
log.info("BrokerClientApp: client publish3 [-U<USERNAME> [-P<PASSWORD]] <URL> <TOPIC> [-T<MSG-TYPE>] [-PP<true|false>] <TEXT-PAYLOAD|-|@file> [<PROPERTY>]*");
|
||||
log.info("BrokerClientApp: <MSG-TYPE>: text, object, bytes, map");
|
||||
log.info("BrokerClientApp: -PP: Process placeholders (default 'true')");
|
||||
log.info("BrokerClientApp: '-' | '@file': Read payload from STDIN | Read payload from file 'file' ");
|
||||
log.info("BrokerClientApp: <PROPERTY>: <Property name>=<Property value> (use quotes if needed)");
|
||||
log.info("BrokerClientApp: client receive [-U<USERNAME> [-P<PASSWORD]] <URL> <TOPIC> ");
|
||||
log.info("BrokerClientApp: client subscribe [-U<USERNAME> [-P<PASSWORD]] <URL> <TOPIC> ");
|
||||
|
@ -12,12 +12,14 @@ package gr.iccs.imu.ems.brokerclient.event;
|
||||
import gr.iccs.imu.ems.brokerclient.BrokerClient;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@Slf4j
|
||||
@Data
|
||||
@ -25,6 +27,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
public class EventGenerator implements Runnable {
|
||||
private final static AtomicLong counter = new AtomicLong();
|
||||
private final BiFunction<String,EventMap,Boolean> eventPublisher;
|
||||
private final BrokerClient client;
|
||||
private String brokerUrl;
|
||||
private String brokerUsername;
|
||||
@ -38,6 +41,16 @@ public class EventGenerator implements Runnable {
|
||||
|
||||
private transient boolean keepRunning;
|
||||
|
||||
public EventGenerator(@NonNull BiFunction<String,EventMap,Boolean> eventPublisher) {
|
||||
this.eventPublisher = eventPublisher;
|
||||
this.client = null;
|
||||
}
|
||||
|
||||
public EventGenerator(@NonNull BrokerClient brokerClient) {
|
||||
this.eventPublisher = null;
|
||||
this.client = brokerClient;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void printCounter() {
|
||||
log.info("New EventGenerator with instance number: {}", counter.getAndIncrement());
|
||||
@ -65,7 +78,10 @@ public class EventGenerator implements Runnable {
|
||||
double newValue = Math.random() * valueRangeWidth + lowerValue;
|
||||
EventMap event = new EventMap(newValue, level, System.currentTimeMillis());
|
||||
log.info("EventGenerator.run(): Sending event #{}: {}", countSent + 1, event);
|
||||
client.publishEventWithCredentials(brokerUrl, brokerUsername, brokerPassword, destinationName, event);
|
||||
if (eventPublisher!=null)
|
||||
eventPublisher.apply(destinationName, event);
|
||||
else
|
||||
client.publishEventWithCredentials(brokerUrl, brokerUsername, brokerPassword, destinationName, event);
|
||||
countSent++;
|
||||
if (countSent == howMany) keepRunning = false;
|
||||
log.info("EventGenerator.run(): Event sent #{}: {}", countSent, event);
|
||||
|
@ -181,7 +181,7 @@ public abstract class AbstractEndpointCollector<T> implements InitializingBean,
|
||||
|
||||
// collect data from local node
|
||||
if (! properties.isSkipLocal()) {
|
||||
log.debug/*info*/("Collectors::{}: Collecting metrics from local node...", collectorId);
|
||||
log.debug("Collectors::{}: Collecting metrics from local node...", collectorId);
|
||||
collectAndPublishData("");
|
||||
} else {
|
||||
log.debug("Collectors::{}: Collection from local node is disabled", collectorId);
|
||||
@ -191,8 +191,8 @@ public abstract class AbstractEndpointCollector<T> implements InitializingBean,
|
||||
log.trace("Collectors::{}: Nodes without clients in Zone: {}", collectorId, collectorContext.getNodesWithoutClient());
|
||||
log.trace("Collectors::{}: Is Aggregator: {}", collectorId, collectorContext.isAggregator());
|
||||
if (collectorContext.isAggregator()) {
|
||||
if (collectorContext.getNodesWithoutClient().size()>0) {
|
||||
log.debug/*info*/("Collectors::{}: Collecting metrics from remote nodes (without EMS client): {}", collectorId,
|
||||
if (! collectorContext.getNodesWithoutClient().isEmpty()) {
|
||||
log.debug("Collectors::{}: Collecting metrics from remote nodes (without EMS client): {}", collectorId,
|
||||
collectorContext.getNodesWithoutClient());
|
||||
for (Object nodeAddress : collectorContext.getNodesWithoutClient()) {
|
||||
// collect data from remote node
|
||||
@ -253,7 +253,7 @@ public abstract class AbstractEndpointCollector<T> implements InitializingBean,
|
||||
|
||||
private COLLECTION_RESULT collectAndPublishData(@NonNull String nodeAddress) {
|
||||
if (ignoredNodes.containsKey(nodeAddress)) {
|
||||
log.debug/*info*/("Collectors::{}: Node is in ignore list: {}", collectorId, nodeAddress);
|
||||
log.debug("Collectors::{}: Node is in ignore list: {}", collectorId, nodeAddress);
|
||||
return COLLECTION_RESULT.IGNORED;
|
||||
}
|
||||
|
||||
@ -322,7 +322,7 @@ public abstract class AbstractEndpointCollector<T> implements InitializingBean,
|
||||
// Remote node data collection URL
|
||||
url = String.format(properties.getUrlOfNodesWithoutClient(), nodeAddress);
|
||||
}
|
||||
log.debug/*info*/("Collectors::{}: Collecting data from url: {}", collectorId, url);
|
||||
log.debug("Collectors::{}: Collecting data from url: {}", collectorId, url);
|
||||
|
||||
log.debug("Collectors::{}: Collecting data: {}...", collectorId, url);
|
||||
long startTm = System.currentTimeMillis();
|
||||
@ -342,8 +342,8 @@ public abstract class AbstractEndpointCollector<T> implements InitializingBean,
|
||||
log.debug("Collectors::{}: Collecting data...ok", collectorId);
|
||||
//log.info("Collectors::{}: Metrics: extracted={}, published={}, failed={}", collectorId,
|
||||
// stats.countSuccess + stats.countErrors, stats.countSuccess, stats.countErrors);
|
||||
if (log.isInfoEnabled())
|
||||
log.debug/*info*/("Collectors::{}: Publish statistics: {}", collectorId, stats);
|
||||
if (log.isDebugEnabled())
|
||||
log.debug("Collectors::{}: Publish statistics: {}", collectorId, stats);
|
||||
log.debug("Collectors::{}: Durations: rest-call={}, extract+publish={}, total={}", collectorId,
|
||||
callEndTm-startTm, endTm-callEndTm, endTm-startTm);
|
||||
} else {
|
||||
|
@ -53,17 +53,17 @@ instructions:
|
||||
executable: false
|
||||
exitCode: 0
|
||||
match: false
|
||||
- description: Upload installation package MD5 checksum
|
||||
- description: Upload installation package SHA256 checksum
|
||||
taskType: COPY
|
||||
fileName: /tmp/baguette-client.tgz.md5
|
||||
localFileName: '${EMS_PUBLIC_DIR}/resources/baguette-client.tgz.md5'
|
||||
fileName: /tmp/baguette-client.tgz.sha256
|
||||
localFileName: '${EMS_PUBLIC_DIR}/resources/baguette-client.tgz.sha256'
|
||||
executable: false
|
||||
exitCode: 0
|
||||
match: false
|
||||
- description: Check MD5 checksum of installation package
|
||||
- description: Check SHA256 checksum of installation package
|
||||
taskType: CHECK
|
||||
command: >-
|
||||
[[ `cat /tmp/baguette-client.tgz.md5` != `md5sum /tmp/baguette-client.tgz | cut -d ' ' -f 1 ` ]] && exit 99
|
||||
[[ `cat /tmp/baguette-client.tgz.sha256` != `sha256sum /tmp/baguette-client.tgz | cut -d ' ' -f 1 ` ]] && exit 99
|
||||
executable: false
|
||||
exitCode: 99
|
||||
match: true
|
||||
|
@ -48,18 +48,18 @@
|
||||
"match": false
|
||||
},
|
||||
{
|
||||
"description": "Upload installation package MD5 checksum",
|
||||
"description": "Upload installation package SHA256 checksum",
|
||||
"taskType": "COPY",
|
||||
"fileName": "/tmp/baguette-client.tgz.md5",
|
||||
"localFileName": "${EMS_PUBLIC_DIR}/resources/baguette-client.tgz.md5",
|
||||
"fileName": "/tmp/baguette-client.tgz.sha256",
|
||||
"localFileName": "${EMS_PUBLIC_DIR}/resources/baguette-client.tgz.sha256",
|
||||
"executable": false,
|
||||
"exitCode": 0,
|
||||
"match": false
|
||||
},
|
||||
{
|
||||
"description": "Check MD5 checksum of installation package",
|
||||
"description": "Check SHA256 checksum of installation package",
|
||||
"taskType": "CHECK",
|
||||
"command": "[[ `cat /tmp/baguette-client.tgz.md5` != `md5sum /tmp/baguette-client.tgz | cut -d ' ' -f 1 ` ]] && exit 99",
|
||||
"command": "[[ `cat /tmp/baguette-client.tgz.sha256` != `sha256sum /tmp/baguette-client.tgz | cut -d ' ' -f 1 ` ]] && exit 99",
|
||||
"executable": false,
|
||||
"exitCode": 99,
|
||||
"match": true
|
||||
|
@ -11,9 +11,9 @@
|
||||
### EMS - Baguette Client properties ###
|
||||
################################################################################
|
||||
|
||||
#password-encoder-class = password.gr.iccs.imu.ems.util.AsterisksPasswordEncoder
|
||||
#password-encoder-class = password.gr.iccs.imu.ems.util.IdentityPasswordEncoder
|
||||
#password-encoder-class = password.gr.iccs.imu.ems.util.PresentPasswordEncoder
|
||||
#password-encoder-class = gr.iccs.imu.ems.util.password.AsterisksPasswordEncoder
|
||||
#password-encoder-class = gr.iccs.imu.ems.util.password.IdentityPasswordEncoder
|
||||
#password-encoder-class = gr.iccs.imu.ems.util.password.PresentPasswordEncoder
|
||||
|
||||
### Jasypt encryptor settings (using old settings until encrypted texts are updated)
|
||||
jasypt.encryptor.algorithm = PBEWithMD5AndDES
|
||||
@ -68,7 +68,7 @@ server-password = ${BAGUETTE_SERVER_PASSWORD}
|
||||
# Collectors settings
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
#collector-classes = netdata.collector.gr.iccs.imu.ems.baguette.client.NetdataCollector
|
||||
#collector-classes = gr.iccs.imu.ems.baguette.client.collector.netdata.NetdataCollector
|
||||
|
||||
collector.netdata.enable = true
|
||||
collector.netdata.delay = 10000
|
||||
@ -80,7 +80,7 @@ collector.netdata.allowed-topics = ${COLLECTOR_ALLOWED_TOPICS}
|
||||
collector.netdata.error-limit = 3
|
||||
collector.netdata.pause-period = 60
|
||||
|
||||
collector.prometheus.enable = false
|
||||
collector.prometheus.enable = true
|
||||
collector.prometheus.delay = 10000
|
||||
collector.prometheus.url = http://127.0.0.1:9090/metrics
|
||||
collector.prometheus.urlOfNodesWithoutClient = http://%s:9090/metrics
|
||||
@ -151,8 +151,8 @@ brokercep.broker-protocol = ssl
|
||||
BROKER_URL_PROPERTIES = transport.daemon=true&transport.trace=false&transport.useKeepAlive=true&transport.useInactivityMonitor=false&transport.needClientAuth=${CLIENT_AUTH_REQUIRED}&transport.verifyHostName=true&transport.connectionTimeout=0&transport.keepAlive=true
|
||||
CLIENT_AUTH_REQUIRED = false
|
||||
brokercep.broker-url[0] = ${brokercep.broker-protocol}://0.0.0.0:${brokercep.broker-port}?${BROKER_URL_PROPERTIES}
|
||||
brokercep.broker-url[1] = tcp://127.0.0.1:61616?${BROKER_URL_PROPERTIES}
|
||||
brokercep.broker-url[2] =
|
||||
brokercep.broker-url[1] = tcp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61616?${BROKER_URL_PROPERTIES}
|
||||
brokercep.broker-url[2] = stomp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61610?${BROKER_URL_PROPERTIES}
|
||||
|
||||
CLIENT_URL_PROPERTIES=daemon=true&trace=false&useInactivityMonitor=false&connectionTimeout=0&keepAlive=true
|
||||
brokercep.broker-url-for-consumer = tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES}
|
||||
@ -163,16 +163,18 @@ brokercep.broker-url-for-clients = ${brokercep.broker-protocol}://${EMS_CLIENT_A
|
||||
brokercep.ssl.keystore-file = ${EMS_CONFIG_DIR}/client-broker-keystore.p12
|
||||
brokercep.ssl.keystore-type = PKCS12
|
||||
#brokercep.ssl.keystore-password = melodic
|
||||
brokercep.ssl.keystore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==)
|
||||
#brokercep.ssl.keystore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==)
|
||||
brokercep.ssl.keystore-password = ${EMS_KEYSTORE_PASSWORD}
|
||||
# Trust store
|
||||
brokercep.ssl.truststore-file = ${EMS_CONFIG_DIR}/client-broker-truststore.p12
|
||||
brokercep.ssl.truststore-type = PKCS12
|
||||
#brokercep.ssl.truststore-password = melodic
|
||||
brokercep.ssl.truststore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==)
|
||||
#brokercep.ssl.truststore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==)
|
||||
brokercep.ssl.truststore-password = ${EMS_TRUSTSTORE_PASSWORD}
|
||||
# Certificate
|
||||
brokercep.ssl.certificate-file = ${EMS_CONFIG_DIR}/client-broker.crt
|
||||
# Key-and-Cert data
|
||||
brokercep.ssl.key-entry-generate = IF-IP-CHANGED
|
||||
brokercep.ssl.key-entry-generate = ALWAYS
|
||||
brokercep.ssl.key-entry-name = ${EMS_CLIENT_ADDRESS}
|
||||
brokercep.ssl.key-entry-dname = CN=${EMS_CLIENT_ADDRESS},OU=Information Management Unit (IMU),O=Institute of Communication and Computer Systems (ICCS),L=Athens,ST=Attika,C=GR
|
||||
brokercep.ssl.key-entry-ext-san = dns:localhost,ip:127.0.0.1,ip:${DEFAULT_IP},ip:${PUBLIC_IP}
|
||||
@ -180,7 +182,8 @@ brokercep.ssl.key-entry-ext-san = dns:localhost,ip:127.0.0.1,ip:${DEFAULT_IP},ip
|
||||
# Authentication and Authorization settings
|
||||
brokercep.authentication-enabled = true
|
||||
#brokercep.additional-broker-credentials = aaa/111, bbb/222, morphemic/morphemic
|
||||
brokercep.additional-broker-credentials = ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)
|
||||
#brokercep.additional-broker-credentials = ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)
|
||||
brokercep.additional-broker-credentials = ${EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS}
|
||||
brokercep.authorization-enabled = false
|
||||
|
||||
# Broker instance settings
|
||||
@ -194,14 +197,14 @@ brokercep.broker-using-shutdown-hook = false
|
||||
|
||||
# Message interceptors
|
||||
brokercep.message-interceptors[0].destination = >
|
||||
brokercep.message-interceptors[0].className = interceptor.broker.gr.iccs.imu.ems.brokercep.SequentialCompositeInterceptor
|
||||
brokercep.message-interceptors[0].className = gr.iccs.imu.ems.brokercep.broker.interceptor.SequentialCompositeInterceptor
|
||||
brokercep.message-interceptors[0].params[0] = #SourceAddressMessageUpdateInterceptor
|
||||
brokercep.message-interceptors[0].params[1] = #MessageForwarderInterceptor
|
||||
brokercep.message-interceptors[0].params[2] = #NodePropertiesMessageUpdateInterceptor
|
||||
|
||||
brokercep.message-interceptors-specs.SourceAddressMessageUpdateInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.SourceAddressMessageUpdateInterceptor
|
||||
brokercep.message-interceptors-specs.MessageForwarderInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.MessageForwarderInterceptor
|
||||
brokercep.message-interceptors-specs.NodePropertiesMessageUpdateInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.NodePropertiesMessageUpdateInterceptor
|
||||
brokercep.message-interceptors-specs.SourceAddressMessageUpdateInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.SourceAddressMessageUpdateInterceptor
|
||||
brokercep.message-interceptors-specs.MessageForwarderInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.MessageForwarderInterceptor
|
||||
brokercep.message-interceptors-specs.NodePropertiesMessageUpdateInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.NodePropertiesMessageUpdateInterceptor
|
||||
|
||||
# Message forward destinations (MessageForwarderInterceptor must be included in 'message-interceptors' property)
|
||||
#brokercep.message-forward-destinations[0].connection-string = tcp://localhost:51515
|
||||
|
@ -83,6 +83,8 @@ server-password: ${BAGUETTE_SERVER_PASSWORD}
|
||||
|
||||
#collector-classes: gr.iccs.imu.ems.baguette.client.collector.netdata.NetdataCollector
|
||||
|
||||
collector-configurations: ${COLLECTOR_CONFIGURATIONS}
|
||||
|
||||
collector:
|
||||
netdata:
|
||||
enable: true
|
||||
@ -95,7 +97,7 @@ collector:
|
||||
error-limit: 3
|
||||
pause-period: 60
|
||||
prometheus:
|
||||
enable: false
|
||||
enable: true
|
||||
delay: 10000
|
||||
url: http://127.0.0.1:9090/metrics
|
||||
urlOfNodesWithoutClient: http://%s:9090/metrics
|
||||
@ -174,8 +176,8 @@ brokercep:
|
||||
# Broker connectors
|
||||
broker-url:
|
||||
- ${brokercep.broker-protocol}://0.0.0.0:${brokercep.broker-port}?${BROKER_URL_PROPERTIES}
|
||||
- tcp://127.0.0.1:61616?${BROKER_URL_PROPERTIES}
|
||||
- stomp://127.0.0.1:61610?${BROKER_URL_PROPERTIES}
|
||||
- tcp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61616?${BROKER_URL_PROPERTIES}
|
||||
- stomp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61610?${BROKER_URL_PROPERTIES}
|
||||
|
||||
# Broker URLs for (EMS) consumer and clients
|
||||
broker-url-for-consumer: tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES}
|
||||
@ -186,18 +188,20 @@ brokercep:
|
||||
# Key store settings
|
||||
keystore-file: ${EMS_CONFIG_DIR}/client-broker-keystore.p12
|
||||
keystore-type: PKCS12
|
||||
keystore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic
|
||||
#keystore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic
|
||||
keystore-password: ${EMS_KEYSTORE_PASSWORD:${random.value}}
|
||||
|
||||
# Trust store settings
|
||||
truststore-file: ${EMS_CONFIG_DIR}/client-broker-truststore.p12
|
||||
truststore-type: PKCS12
|
||||
truststore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic
|
||||
#truststore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic
|
||||
truststore-password: ${EMS_TRUSTSTORE_PASSWORD:${random.value}}
|
||||
|
||||
# Certificate settings
|
||||
certificate-file: ${EMS_CONFIG_DIR}/client-broker.crt
|
||||
|
||||
# key generation settings
|
||||
key-entry-generate: IF-IP-CHANGED
|
||||
key-entry-generate: ALWAYS
|
||||
key-entry-name: ${EMS_CLIENT_ADDRESS}
|
||||
key-entry-dname: 'CN=${EMS_CLIENT_ADDRESS},OU=Information Management Unit (IMU),O=Institute of Communication and Computer Systems (ICCS),L=Athens,ST=Attika,C=GR'
|
||||
key-entry-ext-san: 'dns:localhost,ip:127.0.0.1,ip:${DEFAULT_IP},ip:${PUBLIC_IP}'
|
||||
@ -205,7 +209,8 @@ brokercep:
|
||||
# Authentication and Authorization settings
|
||||
authentication-enabled: true
|
||||
#additional-broker-credentials: aaa/111, bbb/222, morphemic/morphemic
|
||||
additional-broker-credentials: 'ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)'
|
||||
#additional-broker-credentials: 'ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)'
|
||||
additional-broker-credentials: ${EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS}
|
||||
authorization-enabled: false
|
||||
|
||||
# Broker instance settings
|
||||
|
@ -11,8 +11,9 @@
|
||||
### Global settings
|
||||
################################################################################
|
||||
|
||||
### Don't touch the next line!!
|
||||
EMS_SERVER_ADDRESS=${${control.IP_SETTING}}
|
||||
### Don't touch the next lines!!
|
||||
EMS_IP_SETTING=${P_EMS_IP_SETTING:PUBLIC_IP}
|
||||
EMS_SERVER_ADDRESS=${${EMS_IP_SETTING}}
|
||||
DOLLAR=$
|
||||
|
||||
### Password Encoder settings
|
||||
@ -111,9 +112,9 @@ control.log-requests = ${EMS_LOG_REQUESTS:false}
|
||||
#control.skip-broker-cep = true
|
||||
#control.skip-baguette = true
|
||||
#control.skip-collectors = true
|
||||
#control.skip-metasolver = true
|
||||
#control.skip-notification = true
|
||||
control.upperware-grouping = GLOBAL
|
||||
control.skip-metasolver = true
|
||||
control.skip-notification = true
|
||||
#control.upperware-grouping = GLOBAL
|
||||
|
||||
### Debug settings - Load/Save translation results
|
||||
control.tc-load-file = ${EMS_TC_LOAD_FILE:${EMS_TC_FILE:${LOGS_DIR:${EMS_CONFIG_DIR}/../logs}/_TC.json}}
|
||||
@ -319,7 +320,7 @@ brokercep.broker-url[1] = tcp://0.0.0.0:61616?${BROKER_URL_PROPERTIES}
|
||||
brokercep.broker-url[2] = stomp://0.0.0.0:61610
|
||||
|
||||
# Broker URLs for (EMS) consumer and clients
|
||||
brokercep.broker-url-for-consumer = tcp://${EMS_SERVER_ADDRESS}:61616?${CLIENT_URL_PROPERTIES}
|
||||
brokercep.broker-url-for-consumer = tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES}
|
||||
brokercep.broker-url-for-clients = ${brokercep.broker-protocol}://${EMS_SERVER_ADDRESS}:${brokercep.broker-port}?${CLIENT_URL_PROPERTIES}
|
||||
# Must be a public IP address
|
||||
|
||||
@ -527,9 +528,9 @@ baguette.client.install.sessionRecordingDir = ${LOGS_DIR:${EMS_CONFIG_DIR}/../lo
|
||||
#baguette.client.install.parameters.SKIP_JRE_INSTALLATION=true
|
||||
#baguette.client.install.parameters.SKIP_START=true
|
||||
|
||||
baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_PROCESSORS=2
|
||||
baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_RAM=2*1024*1024
|
||||
baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_DISK_FREE=1024*1024
|
||||
#baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_PROCESSORS=2
|
||||
#baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_RAM=2*1024*1024
|
||||
#baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_DISK_FREE=1024*1024
|
||||
|
||||
### Settings for resolving Node state after baguette client installation
|
||||
#baguette.client.install.clientInstallVarName=__EMS_CLIENT_INSTALL__
|
||||
|
@ -11,8 +11,9 @@
|
||||
### Global settings
|
||||
################################################################################
|
||||
|
||||
### Don't touch the next line!!
|
||||
EMS_SERVER_ADDRESS: ${${control.IP_SETTING}}
|
||||
### Don't touch the next lines!!
|
||||
EMS_IP_SETTING: ${P_EMS_IP_SETTING:PUBLIC_IP}
|
||||
EMS_SERVER_ADDRESS: ${${EMS_IP_SETTING}}
|
||||
DOLLAR: '$'
|
||||
|
||||
### Password Encoder settings
|
||||
@ -114,9 +115,9 @@ control:
|
||||
#skip-broker-cep: true
|
||||
#skip-baguette: true
|
||||
#skip-collectors: true
|
||||
#skip-metasolver: true
|
||||
#skip-notification: true
|
||||
upperware-grouping: GLOBAL
|
||||
skip-metasolver: true
|
||||
skip-notification: true
|
||||
#upperware-grouping: GLOBAL
|
||||
|
||||
### Debug settings - Load/Save translation results
|
||||
tc-load-file: ${EMS_TC_LOAD_FILE:${EMS_TC_FILE:${LOGS_DIR:${EMS_CONFIG_DIR}/../logs}/_TC.json}}
|
||||
@ -329,7 +330,7 @@ brokercep:
|
||||
- stomp://0.0.0.0:61610
|
||||
|
||||
# Broker URLs for (EMS) consumer and clients
|
||||
broker-url-for-consumer: tcp://${EMS_SERVER_ADDRESS}:61616?${CLIENT_URL_PROPERTIES}
|
||||
broker-url-for-consumer: tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES}
|
||||
broker-url-for-clients: ${brokercep.broker-protocol}://${EMS_SERVER_ADDRESS}:${brokercep.broker-port}?${CLIENT_URL_PROPERTIES}
|
||||
# Must be a public IP address
|
||||
|
||||
@ -569,7 +570,7 @@ baguette.client.install:
|
||||
sessionRecordingDir: ${LOGS_DIR:${EMS_CONFIG_DIR}/../logs}
|
||||
|
||||
### Baguette and Netdata installation parameters (for condition checking)
|
||||
parameters:
|
||||
#parameters:
|
||||
|
||||
#SKIP_IGNORE_CHECK: true
|
||||
#SKIP_DETECTION: true
|
||||
@ -578,9 +579,9 @@ baguette.client.install:
|
||||
#SKIP_JRE_INSTALLATION: true
|
||||
#SKIP_START: true
|
||||
|
||||
BAGUETTE_INSTALLATION_MIN_PROCESSORS: 2
|
||||
BAGUETTE_INSTALLATION_MIN_RAM: 2*1024*1024
|
||||
BAGUETTE_INSTALLATION_MIN_DISK_FREE: 1024*1024
|
||||
#BAGUETTE_INSTALLATION_MIN_PROCESSORS: 2
|
||||
#BAGUETTE_INSTALLATION_MIN_RAM: 2*1024*1024
|
||||
#BAGUETTE_INSTALLATION_MIN_DISK_FREE: 1024*1024
|
||||
|
||||
### Settings for resolving Node state after baguette client installation
|
||||
#clientInstallVarName: '__EMS_CLIENT_INSTALL__'
|
||||
|
@ -21,8 +21,3 @@ control.ssl.truststore-password=ENC(ISMbn01HVPbtRPkqm2Lslg==)
|
||||
### Additional Baguette Server SSH username/passwords: aa/xx, bb/yy
|
||||
#baguette.server.credentials.aa=xx
|
||||
#baguette.server.credentials.bb=yy
|
||||
|
||||
### Other settings
|
||||
control.IP_SETTING=DEFAULT_IP
|
||||
control.esb-url=
|
||||
control.metasolver-configuration-url=
|
||||
|
@ -20,11 +20,11 @@
|
||||
<name>EMS - Control Service</name>
|
||||
|
||||
<properties>
|
||||
<!--<hawtio.version>3.0-M8</hawtio.version>-->
|
||||
<spring.boot.admin.version>3.1.3</spring.boot.admin.version>
|
||||
<micrometer.registry.prometheus.version>1.11.2</micrometer.registry.prometheus.version>
|
||||
<springdoc.version>2.1.0</springdoc.version>
|
||||
<jjwt.version>0.11.5</jjwt.version>
|
||||
<hawtio.version>4.0-M4</hawtio.version>
|
||||
<spring.boot.admin.version>3.2.2</spring.boot.admin.version>
|
||||
<micrometer.registry.prometheus.version>1.12.4</micrometer.registry.prometheus.version>
|
||||
<springdoc.version>2.3.0</springdoc.version>
|
||||
<jjwt.version>0.12.5</jjwt.version>
|
||||
<!-- Build timestamp -->
|
||||
<timestamp>${maven.build.timestamp}</timestamp>
|
||||
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
|
||||
@ -33,10 +33,12 @@
|
||||
<docker.esperJar>esper-${esper.version}.jar</docker.esperJar>
|
||||
|
||||
<!-- io.fabricat8 docker-maven-plugin properties -->
|
||||
<docker-maven-plugin.version>0.43.2</docker-maven-plugin.version>
|
||||
<docker-maven-plugin.version>0.44.0</docker-maven-plugin.version>
|
||||
<docker.image.name>ems-server</docker.image.name>
|
||||
<!--<docker.image.tag>${revision}</docker.image.tag>
|
||||
<build.description></build.description>-->
|
||||
<docker.user>emsuser</docker.user>
|
||||
<docker.user.home>/opt/ems-server</docker.user.home>
|
||||
|
||||
<!-- EMS server main class -->
|
||||
<start-class>gr.iccs.imu.ems.control.ControlServiceApplication</start-class>
|
||||
@ -176,13 +178,11 @@
|
||||
</dependency>-->
|
||||
|
||||
<!-- Hawtio console dependency (https://hawt.io/docs/get-started/) -->
|
||||
<!-- XXX: Causes 'BeanPostProcessorChecker' warnings -->
|
||||
<!--XXX: TODO: Temporarily disabled because Jolokia does not follow Spring Boot 3 changes (see https://adevait.com/java/spring-boot-3-0)
|
||||
<dependency>
|
||||
<groupId>io.hawt</groupId>
|
||||
<artifactId>hawtio-springboot</artifactId>
|
||||
<version>${hawtio.version}</version>
|
||||
</dependency>-->
|
||||
</dependency>
|
||||
|
||||
<!-- Springdoc: OpenAPI UI and Swagger -->
|
||||
<dependency>
|
||||
@ -208,43 +208,6 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Maven plugin dependencies -->
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/net.nicoulaj.maven.plugins/checksum-maven-plugin -->
|
||||
<dependency>
|
||||
<groupId>net.nicoulaj.maven.plugins</groupId>
|
||||
<artifactId>checksum-maven-plugin</artifactId>
|
||||
<version>1.11</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-utils</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.plexus</groupId>
|
||||
<artifactId>plexus-utils</artifactId>
|
||||
<version>4.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.15.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -291,7 +254,7 @@
|
||||
<plugin>
|
||||
<groupId>io.github.git-commit-id</groupId>
|
||||
<artifactId>git-commit-id-maven-plugin</artifactId>
|
||||
<version>6.0.0</version>
|
||||
<version>7.0.0</version>
|
||||
<!--<executions>
|
||||
<execution>
|
||||
<id>get-the-git-infos</id>
|
||||
@ -419,7 +382,7 @@
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<algorithms>
|
||||
<algorithm>MD5</algorithm>
|
||||
<algorithm>SHA256</algorithm>
|
||||
</algorithms>
|
||||
<individualFiles>true</individualFiles>
|
||||
<failOnError>true</failOnError>
|
||||
@ -477,13 +440,17 @@
|
||||
<destinationFile>../public_resources/resources/baguette-client.tgz</destinationFile>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<sourceFile>${project.build.directory}/baguette-client-installation-package.tgz.md5</sourceFile>
|
||||
<destinationFile>../public_resources/resources/baguette-client.tgz.md5</destinationFile>
|
||||
<sourceFile>${project.build.directory}/baguette-client-installation-package.tgz.sha256</sourceFile>
|
||||
<destinationFile>../public_resources/resources/baguette-client.tgz.sha256</destinationFile>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<sourceFile>../baguette-client/bin/install.sh</sourceFile>
|
||||
<destinationFile>../public_resources/resources/install.sh</destinationFile>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<sourceFile>../baguette-client/bin/jre-install.sh</sourceFile>
|
||||
<destinationFile>../public_resources/resources/jre-install.sh</destinationFile>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<ignoreFileNotFoundOnIncremental>true</ignoreFileNotFoundOnIncremental>
|
||||
<overWrite>true</overWrite>
|
||||
@ -700,6 +667,10 @@
|
||||
<name>${docker.image.name}:${docker.image.tag}</name>
|
||||
<build>
|
||||
<contextDir>${project.build.directory}/docker-context</contextDir>
|
||||
<args>
|
||||
<EMS_USER>${docker.user}</EMS_USER>
|
||||
<EMS_HOME>${docker.user.home}</EMS_HOME>
|
||||
</args>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
@ -739,12 +710,43 @@
|
||||
<name>docker.image.tag</name>
|
||||
<value>${docker.image.tag}</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>docker.user</name>
|
||||
<value>${docker.user}</value>
|
||||
</property>
|
||||
<property>
|
||||
<name>docker.user.home</name>
|
||||
<value>${docker.user.home}</value>
|
||||
</property>
|
||||
</properties>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>install-file</goal>
|
||||
</goals>
|
||||
<phase>install</phase>
|
||||
<configuration>
|
||||
<file>${project.build.directory}/docker-image.properties</file>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<version>${project.version}</version>
|
||||
<classifier>docker-image</classifier>
|
||||
<packaging>pom</packaging>
|
||||
<generatePom>false</generatePom>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
@ -22,16 +22,6 @@ RUN java -Djarmode=layertools -jar control-service.jar extract
|
||||
# ----------------- Run image -----------------
|
||||
FROM $RUN_IMAGE:$RUN_IMAGE_TAG
|
||||
|
||||
# Setup environment
|
||||
ENV BASEDIR /opt/ems-server
|
||||
ENV EMS_HOME ${BASEDIR}
|
||||
ENV EMS_CONFIG_DIR ${BASEDIR}/config
|
||||
|
||||
ENV BIN_DIR ${BASEDIR}/bin
|
||||
ENV CONFIG_DIR ${BASEDIR}/config
|
||||
ENV LOGS_DIR ${BASEDIR}/logs
|
||||
ENV PUBLIC_DIR ${BASEDIR}/public_resources
|
||||
|
||||
# Install required and optional packages
|
||||
RUN wget --progress=dot:giga -O /usr/local/bin/dumb-init \
|
||||
https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 && \
|
||||
@ -42,13 +32,23 @@ RUN wget --progress=dot:giga -O /usr/local/bin/dumb-init \
|
||||
|
||||
# Add an EMS user
|
||||
ARG EMS_USER=emsuser
|
||||
ARG EMS_HOME=/opt/ems-server
|
||||
RUN mkdir ${EMS_HOME} && \
|
||||
addgroup ${EMS_USER} && \
|
||||
adduser --home ${EMS_HOME} --no-create-home --ingroup ${EMS_USER} --disabled-password ${EMS_USER} && \
|
||||
chown ${EMS_USER}:${EMS_USER} ${EMS_HOME}
|
||||
|
||||
USER ${EMS_USER}
|
||||
WORKDIR ${BASEDIR}
|
||||
WORKDIR ${EMS_HOME}
|
||||
|
||||
# Setup environment
|
||||
ENV BASEDIR ${EMS_HOME}
|
||||
ENV EMS_CONFIG_DIR ${BASEDIR}/config
|
||||
|
||||
ENV BIN_DIR ${BASEDIR}/bin
|
||||
ENV CONFIG_DIR ${BASEDIR}/config
|
||||
ENV LOGS_DIR ${BASEDIR}/logs
|
||||
ENV PUBLIC_DIR ${BASEDIR}/public_resources
|
||||
|
||||
# Download a JRE suitable for running EMS clients, and
|
||||
# offer it for download
|
||||
@ -61,8 +61,10 @@ COPY --chown=${EMS_USER}:${EMS_USER} bin ${BIN_DIR}
|
||||
COPY --chown=${EMS_USER}:${EMS_USER} config ${CONFIG_DIR}
|
||||
COPY --chown=${EMS_USER}:${EMS_USER} public_resources ${PUBLIC_DIR}
|
||||
|
||||
# Create 'logs', and 'models' directories. Make bin/*.sh scripts executable
|
||||
RUN mkdir ${LOGS_DIR} && \
|
||||
chmod +rx ${BIN_DIR}/*.sh
|
||||
chmod +rx ${BIN_DIR}/*.sh && \
|
||||
mkdir -p ${EMS_HOME}/models
|
||||
|
||||
# Copy files from builder container
|
||||
COPY --chown=${EMS_USER}:${EMS_USER} --from=ems-server-builder /app/dependencies ${BASEDIR}
|
||||
|
@ -7,22 +7,42 @@
|
||||
# https://www.mozilla.org/en-US/MPL/2.0/
|
||||
#
|
||||
|
||||
ARG BUILDER_IMAGE=eclipse-temurin:21.0.1_12-jre-alpine
|
||||
ARG RUN_IMAGE=eclipse-temurin:21.0.1_12-jre-alpine
|
||||
ARG BUILDER_IMAGE=eclipse-temurin
|
||||
ARG BUILDER_IMAGE_TAG=21.0.1_12-jre
|
||||
ARG RUN_IMAGE=eclipse-temurin
|
||||
ARG RUN_IMAGE_TAG=21.0.1_12-jre
|
||||
|
||||
# ----------------- Builder image -----------------
|
||||
FROM $BUILDER_IMAGE as ems-server-builder
|
||||
FROM $BUILDER_IMAGE:$BUILDER_IMAGE_TAG as ems-server-builder
|
||||
#FROM vegardit/graalvm-maven:latest-java17
|
||||
WORKDIR /app
|
||||
COPY jars/control-service.jar .
|
||||
RUN java -Djarmode=layertools -jar control-service.jar extract
|
||||
|
||||
# ----------------- Run image -----------------
|
||||
FROM $RUN_IMAGE
|
||||
FROM $RUN_IMAGE:$RUN_IMAGE_TAG
|
||||
|
||||
# Install required and optional packages
|
||||
RUN wget --progress=dot:giga -O /usr/local/bin/dumb-init \
|
||||
https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 && \
|
||||
chmod +x /usr/local/bin/dumb-init
|
||||
#RUN apk update && \
|
||||
# apk add bash netcat-openbsd vim iputils-ping
|
||||
#
|
||||
|
||||
# Add an EMS user
|
||||
ARG EMS_USER=emsuser
|
||||
ARG EMS_HOME=/opt/ems-server
|
||||
RUN mkdir ${EMS_HOME} && \
|
||||
addgroup ${EMS_USER} && \
|
||||
adduser --home ${EMS_HOME} --no-create-home --ingroup ${EMS_USER} --disabled-password ${EMS_USER} && \
|
||||
chown ${EMS_USER}:${EMS_USER} ${EMS_HOME}
|
||||
|
||||
USER ${EMS_USER}
|
||||
WORKDIR ${EMS_HOME}
|
||||
|
||||
# Setup environment
|
||||
ENV BASEDIR /opt/ems-server
|
||||
ENV EMS_HOME ${BASEDIR}
|
||||
ENV BASEDIR ${EMS_HOME}
|
||||
ENV EMS_CONFIG_DIR ${BASEDIR}/config
|
||||
|
||||
ENV BIN_DIR ${BASEDIR}/bin
|
||||
@ -30,36 +50,21 @@ ENV CONFIG_DIR ${BASEDIR}/config
|
||||
ENV LOGS_DIR ${BASEDIR}/logs
|
||||
ENV PUBLIC_DIR ${BASEDIR}/public_resources
|
||||
|
||||
# Install required and optional packages
|
||||
RUN apk update && \
|
||||
apk add curl bash netcat-openbsd vim iputils-ping
|
||||
RUN wget -O /usr/local/bin/dumb-init \
|
||||
https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64 && \
|
||||
chmod +x /usr/local/bin/dumb-init
|
||||
|
||||
# Add an EMS user
|
||||
ARG EMS_USER=emsuser
|
||||
RUN mkdir ${EMS_HOME} && \
|
||||
addgroup ${EMS_USER} && \
|
||||
adduser --home ${EMS_HOME} --no-create-home --ingroup ${EMS_USER} --disabled-password ${EMS_USER} && \
|
||||
chown ${EMS_USER}:${EMS_USER} ${EMS_HOME}
|
||||
|
||||
USER ${EMS_USER}
|
||||
WORKDIR ${BASEDIR}
|
||||
|
||||
# Download a JRE suitable for running EMS clients, and
|
||||
# offer it for download
|
||||
ENV JRE_LINUX_PACKAGE zulu21.30.15-ca-jre21.0.1-linux_x64.tar.gz
|
||||
RUN mkdir -p ${PUBLIC_DIR}/resources && \
|
||||
curl https://cdn.azul.com/zulu/bin/${JRE_LINUX_PACKAGE} --output ${PUBLIC_DIR}/resources/${JRE_LINUX_PACKAGE}
|
||||
wget --progress=dot:giga -O ${PUBLIC_DIR}/resources/${JRE_LINUX_PACKAGE} https://cdn.azul.com/zulu/bin/${JRE_LINUX_PACKAGE}
|
||||
|
||||
# Copy resource files to image
|
||||
ADD --chown=${EMS_USER}:${EMS_USER} bin ${BIN_DIR}
|
||||
ADD --chown=${EMS_USER}:${EMS_USER} config ${CONFIG_DIR}
|
||||
ADD --chown=${EMS_USER}:${EMS_USER} public_resources ${PUBLIC_DIR}
|
||||
COPY --chown=${EMS_USER}:${EMS_USER} bin ${BIN_DIR}
|
||||
COPY --chown=${EMS_USER}:${EMS_USER} config ${CONFIG_DIR}
|
||||
COPY --chown=${EMS_USER}:${EMS_USER} public_resources ${PUBLIC_DIR}
|
||||
|
||||
RUN mkdir ${LOGS_DIR}
|
||||
RUN chmod +rx ${BIN_DIR}/*.sh
|
||||
# Create 'logs', and 'models' directories. Make bin/*.sh scripts executable
|
||||
RUN mkdir ${LOGS_DIR} && \
|
||||
chmod +rx ${BIN_DIR}/*.sh && \
|
||||
mkdir -p ${EMS_HOME}/models
|
||||
|
||||
# Copy files from builder container
|
||||
COPY --chown=${EMS_USER}:${EMS_USER} --from=ems-server-builder /app/dependencies ${BASEDIR}
|
||||
|
@ -12,10 +12,7 @@ package gr.iccs.imu.ems.control;
|
||||
import com.ulisesbocchio.jasyptspringboot.environment.StandardEncryptableEnvironment;
|
||||
import gr.iccs.imu.ems.control.controller.ControlServiceCoordinator;
|
||||
import gr.iccs.imu.ems.control.properties.ControlServiceProperties;
|
||||
import gr.iccs.imu.ems.util.EventBus;
|
||||
import gr.iccs.imu.ems.util.KeystoreUtil;
|
||||
import gr.iccs.imu.ems.util.PasswordUtil;
|
||||
import gr.iccs.imu.ems.util.StrUtil;
|
||||
import gr.iccs.imu.ems.util.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
@ -62,6 +59,8 @@ public class ControlServiceApplication {
|
||||
private final PasswordUtil passwordUtil;
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(EmsRelease.EMS_DESCRIPTION);
|
||||
|
||||
long initStartTime = System.currentTimeMillis();
|
||||
|
||||
// Start EMS server
|
||||
|
@ -69,8 +69,17 @@ public class ControlServiceController {
|
||||
throw new RestControllerException(400, "Request does not contain an application id");
|
||||
}
|
||||
|
||||
// Get applicationId (if provided)
|
||||
String applicationId = Optional.ofNullable(jObj.get("applicationId")).map(je -> stripQuotes(je.toString())).orElse(null);
|
||||
if (StringUtils.isBlank(appExecModelId)) {
|
||||
applicationId = appModelId;
|
||||
log.warn("ControlServiceController.newAppModel(): No 'applicationId' found. Using App model id instead: {}", applicationId);
|
||||
} else {
|
||||
log.info("ControlServiceController.newAppModel(): Found 'applicationId': {}", applicationId);
|
||||
}
|
||||
|
||||
// Start translation and component reconfiguration in a worker thread
|
||||
coordinator.processAppModel(appModelId, appExecModelId, ControlServiceRequestInfo.create(null, null, jwtToken));
|
||||
coordinator.processAppModel(appModelId, appExecModelId, ControlServiceRequestInfo.create(applicationId, null, null, jwtToken, null));
|
||||
log.debug("ControlServiceController.newAppModel(): Model translation dispatched to a worker thread");
|
||||
|
||||
return "OK";
|
||||
|
@ -13,10 +13,12 @@ import com.google.gson.GsonBuilder;
|
||||
import gr.iccs.imu.ems.baguette.server.BaguetteServer;
|
||||
import gr.iccs.imu.ems.baguette.server.NodeRegistry;
|
||||
import gr.iccs.imu.ems.baguette.server.ServerCoordinator;
|
||||
import gr.iccs.imu.ems.baguette.server.coordinator.NoopCoordinator;
|
||||
import gr.iccs.imu.ems.brokercep.BrokerCepService;
|
||||
import gr.iccs.imu.ems.brokercep.BrokerCepStatementSubscriber;
|
||||
import gr.iccs.imu.ems.brokercep.event.EventMap;
|
||||
import gr.iccs.imu.ems.control.collector.netdata.ServerNetdataCollector;
|
||||
import gr.iccs.imu.ems.control.plugin.AppModelPlugin;
|
||||
import gr.iccs.imu.ems.control.plugin.MetasolverPlugin;
|
||||
import gr.iccs.imu.ems.control.plugin.PostTranslationPlugin;
|
||||
import gr.iccs.imu.ems.control.plugin.TranslationContextPlugin;
|
||||
@ -73,6 +75,8 @@ public class ControlServiceCoordinator implements InitializingBean {
|
||||
private final PasswordUtil passwordUtil;
|
||||
private final EventBus<String,Object,Object> eventBus;
|
||||
|
||||
private final List<AppModelPlugin> appModelPluginList;
|
||||
|
||||
private final List<Translator> translatorImplementations;
|
||||
private Translator translator; // Will be populated in 'afterPropertiesSet()'
|
||||
private final List<PostTranslationPlugin> postTranslationPlugins;
|
||||
@ -106,8 +110,11 @@ public class ControlServiceCoordinator implements InitializingBean {
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
setCurrentEmsState(EMS_STATE.INITIALIZING, "Starting ControlServiceCoordinator...");
|
||||
|
||||
initTranslator();
|
||||
initMvvService();
|
||||
startBaguetteServer();
|
||||
|
||||
// Run configuration checks and throw exceptions early (before actually using EMS)
|
||||
if (properties.isSkipTranslation()) {
|
||||
@ -119,6 +126,8 @@ public class ControlServiceCoordinator implements InitializingBean {
|
||||
log.debug("ControlServiceCoordinator.afterPropertiesSet(): Post-translation plugins: {}", postTranslationPlugins);
|
||||
log.debug("ControlServiceCoordinator.afterPropertiesSet(): TranslationContext plugins: {}", translationContextPlugins);
|
||||
log.debug("ControlServiceCoordinator.afterPropertiesSet(): MetaSolver plugins: {}", metasolverPlugins);
|
||||
|
||||
setCurrentEmsState(EMS_STATE.IDLE, "ControlServiceCoordinator started");
|
||||
}
|
||||
|
||||
private void initMvvService() {
|
||||
@ -142,7 +151,7 @@ public class ControlServiceCoordinator implements InitializingBean {
|
||||
private void initTranslator() {
|
||||
log.debug("ControlServiceCoordinator.initTranslator(): Translator implementations: {}", translatorImplementations);
|
||||
if (translatorImplementations.size() == 1) {
|
||||
translator = translatorImplementations.get(0);
|
||||
translator = translatorImplementations.getFirst();
|
||||
} else if (translatorImplementations.isEmpty()) {
|
||||
throw new IllegalArgumentException("No Translator implementations found");
|
||||
} else {
|
||||
@ -156,6 +165,15 @@ public class ControlServiceCoordinator implements InitializingBean {
|
||||
log.info("ControlServiceCoordinator.initTranslator(): Effective translator: {}", translator.getClass().getName());
|
||||
}
|
||||
|
||||
private void startBaguetteServer() {
|
||||
log.debug("ControlServiceCoordinator.startBaguetteServer(): Starting Baguette Server...");
|
||||
try {
|
||||
baguetteServer.startServer(new NoopCoordinator());
|
||||
} catch (Exception ex) {
|
||||
log.error("ControlServiceCoordinator.startBaguetteServer(): EXCEPTION while starting Baguette server: ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
|
||||
public String getAppModelId() {
|
||||
@ -247,10 +265,17 @@ public class ControlServiceCoordinator implements InitializingBean {
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute callback after acquiring lock
|
||||
try {
|
||||
callback.run();
|
||||
} catch (Exception ex) {
|
||||
setCurrentEmsState(EMS_STATE.ERROR, ex.getMessage());
|
||||
StringBuilder sb = new StringBuilder(ex.getClass().getName()).append(": ").append(ex.getMessage());
|
||||
Throwable t = ex;
|
||||
while (t.getCause()!=null) {
|
||||
t = t.getCause();
|
||||
sb.append(", caused by: ").append(t.getClass().getName()).append(": ").append(t.getMessage());
|
||||
}
|
||||
setCurrentEmsState(EMS_STATE.ERROR, sb.toString());
|
||||
|
||||
String mesg = "ControlServiceCoordinator."+caller+": EXCEPTION: " + ex;
|
||||
log.error(mesg, ex);
|
||||
@ -263,6 +288,15 @@ public class ControlServiceCoordinator implements InitializingBean {
|
||||
// Release lock of this coordinator
|
||||
inUse.compareAndSet(true, false);
|
||||
}
|
||||
|
||||
// Invoke requestInfo callback if provided
|
||||
if (requestInfo.getCallback()!=null) {
|
||||
requestInfo.getCallback().accept(Map.of(
|
||||
"ems-state", StringUtils.defaultIfBlank(getCurrentEmsState().name(), "UNKNOWN"),
|
||||
"ems-state-message", StringUtils.defaultIfBlank(getCurrentEmsStateMessage(), ""),
|
||||
"ems-state-change-timestamp", getCurrentEmsStateChangeTimestamp()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
@ -270,17 +304,28 @@ public class ControlServiceCoordinator implements InitializingBean {
|
||||
protected void _processAppModels(String appModelId, String appExecModelId, ControlServiceRequestInfo requestInfo) {
|
||||
log.info("ControlServiceCoordinator._processAppModel(): BEGIN: app-model-id={}, app-exec-model-id={}, request-info={}", appModelId, appExecModelId, requestInfo);
|
||||
|
||||
// Run pre-processing plugins
|
||||
log.debug("ControlServiceCoordinator._processAppModel(): appModelPluginList: {}", appModelPluginList);
|
||||
if (appModelPluginList!=null) {
|
||||
for (AppModelPlugin plugin : appModelPluginList) {
|
||||
if (plugin!=null) {
|
||||
log.debug("ControlServiceCoordinator._processAppModel(): Calling preProcessingNewAppModel on plugin: {}", plugin);
|
||||
plugin.preProcessingNewAppModel(appModelId, requestInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Translate model into Translation Context (with EPL rules etc.)
|
||||
TranslationContext _TC;
|
||||
if (!properties.isSkipTranslation()) {
|
||||
_TC = translateAppModelAndStore(appModelId);
|
||||
_TC = translateAppModelAndStore(appModelId, requestInfo.getApplicationId());
|
||||
} else {
|
||||
log.warn("ControlServiceCoordinator._processAppModel(): Skipping translation due to configuration");
|
||||
_TC = loadStoredTranslationContext(appModelId);
|
||||
}
|
||||
|
||||
// Run TranslationContext plugins
|
||||
if (translationContextPlugins!=null && translationContextPlugins.size()>0) {
|
||||
if (translationContextPlugins!=null && !translationContextPlugins.isEmpty()) {
|
||||
log.info("ControlServiceCoordinator._processAppModel(): Running {} TranslationContext plugins", translationContextPlugins.size());
|
||||
translationContextPlugins.stream().filter(Objects::nonNull).forEach(plugin -> {
|
||||
log.debug("ControlServiceCoordinator._processAppModel(): Calling TranslationContext plugin: {}", plugin.getClass().getName());
|
||||
@ -356,6 +401,17 @@ public class ControlServiceCoordinator implements InitializingBean {
|
||||
log.warn("ControlServiceCoordinator._processAppModel(): Skipping notification due to configuration");
|
||||
}
|
||||
|
||||
// Run post-processing plugins
|
||||
log.debug("ControlServiceCoordinator._processAppModel(): appModelPluginList: {}", appModelPluginList);
|
||||
if (appModelPluginList!=null) {
|
||||
for (AppModelPlugin plugin : appModelPluginList) {
|
||||
if (plugin!=null) {
|
||||
log.debug("ControlServiceCoordinator._processAppModel(): Calling postProcessingNewAppModel on plugin: {}", plugin);
|
||||
plugin.postProcessingNewAppModel(appModelId, requestInfo, _TC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.currentTC = _TC;
|
||||
log.info("ControlServiceCoordinator._processAppModel(): END: app-model-id={}", appModelId);
|
||||
|
||||
@ -418,13 +474,13 @@ public class ControlServiceCoordinator implements InitializingBean {
|
||||
setCurrentEmsState(EMS_STATE.READY, null);
|
||||
}
|
||||
|
||||
private TranslationContext translateAppModelAndStore(String appModelId) {
|
||||
private TranslationContext translateAppModelAndStore(String appModelId, String applicationId) {
|
||||
final TranslationContext _TC;
|
||||
setCurrentEmsState(EMS_STATE.INITIALIZING, "Retrieving and translating model");
|
||||
|
||||
// Translate application model into a TranslationContext object
|
||||
log.info("ControlServiceCoordinator.translateAppModelAndStore(): Model translation: model-id={}", appModelId);
|
||||
_TC = translator.translate(appModelId);
|
||||
_TC = translator.translate(appModelId, applicationId);
|
||||
_TC.populateTopLevelMetricNames();
|
||||
log.debug("ControlServiceCoordinator.translateAppModelAndStore(): Model translation: RESULTS: {}", _TC);
|
||||
|
||||
@ -548,7 +604,7 @@ public class ControlServiceCoordinator implements InitializingBean {
|
||||
log.debug("ControlServiceCoordinator.configureBrokerCep(): Broker-CEP: Upperware grouping: {}", upperwareGrouping);
|
||||
Set<String> eventTypeNames = _TC.getG2T().get(upperwareGrouping);
|
||||
log.debug("ControlServiceCoordinator.configureBrokerCep(): Broker-CEP: Configuration of Event Types: {}", eventTypeNames);
|
||||
if (eventTypeNames == null || eventTypeNames.size() == 0)
|
||||
if (eventTypeNames == null || eventTypeNames.isEmpty())
|
||||
throw new RuntimeException("Broker-CEP: No event types for GLOBAL grouping");
|
||||
|
||||
// Clear any previous event types, statements or function definitions and register the new ones
|
||||
|
@ -25,11 +25,28 @@ public class ControlServiceRequestInfo {
|
||||
@ToString.Exclude
|
||||
private final String jwtToken;
|
||||
|
||||
private final String applicationId;
|
||||
private final java.util.function.Consumer<Object> callback;
|
||||
|
||||
public static ControlServiceRequestInfo create(String notificationUri, String requestUuid, String jwtToken) {
|
||||
return ControlServiceRequestInfo.builder()
|
||||
.notificationUri(notificationUri)
|
||||
.requestUuid(requestUuid)
|
||||
.jwtToken(jwtToken)
|
||||
.applicationId(null)
|
||||
.callback(null)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ControlServiceRequestInfo create(String applicationId, String notificationUri, String requestUuid,
|
||||
String jwtToken, java.util.function.Consumer<Object> callback)
|
||||
{
|
||||
return ControlServiceRequestInfo.builder()
|
||||
.notificationUri(notificationUri)
|
||||
.requestUuid(requestUuid)
|
||||
.jwtToken(jwtToken)
|
||||
.applicationId(applicationId)
|
||||
.callback(callback)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
@ -65,25 +65,45 @@ public class CredentialsController {
|
||||
response.put("password", brokerPassword);
|
||||
response.put("certificate", brokerCertificatePem);
|
||||
HttpEntity<Map> entity = coordinator.createHttpEntity(Map.class, response, jwtToken);
|
||||
log.info("CredentialsController.getBrokerCredentials(): Response: {}", response);
|
||||
log.debug("CredentialsController.getBrokerCredentials(): Response: {}",
|
||||
encodeMapFields(response, "password", "certificate"));
|
||||
|
||||
//return response;
|
||||
return entity;
|
||||
}
|
||||
|
||||
// @PreAuthorize(ROLES_ALLOWED_JWT_TOKEN_OR_API_KEY)
|
||||
@GetMapping(value = "/baguette/connectionInfo")
|
||||
public HttpEntity<Map> getBaguetteConnectionInfo(
|
||||
@RequestHeader(name = HttpHeaders.AUTHORIZATION, required = false) String jwtToken)
|
||||
{
|
||||
log.info("CredentialsController.getBaguetteConnectionInfo(): BEGIN");
|
||||
log.trace("CredentialsController.getBaguetteConnectionInfo(): JWT token: {}", jwtToken);
|
||||
|
||||
// Retrieve sensor information
|
||||
Map<String,String> response = coordinator.getBaguetteServer().getServerConnectionInfo();
|
||||
|
||||
// Prepare response
|
||||
HttpEntity<Map> entity = coordinator.createHttpEntity(Map.class, response, jwtToken);
|
||||
log.debug("CredentialsController.getBaguetteConnectionInfo(): Response: {}",
|
||||
encodeMapFields(response, "BAGUETTE_SERVER_PASSWORD", "BAGUETTE_SERVER_PUBKEY"));
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
// @PreAuthorize(ROLES_ALLOWED_JWT_TOKEN_OR_API_KEY)
|
||||
@GetMapping(value = "/baguette/ref/{ref}", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public HttpEntity<Map> getNodeCredentials(@PathVariable String optRef,
|
||||
public HttpEntity<Map> getNodeCredentials(@PathVariable String ref,
|
||||
@RequestHeader(name = HttpHeaders.AUTHORIZATION, required = false) String jwtToken)
|
||||
{
|
||||
log.info("CredentialsController.getNodeCredentials(): BEGIN: ref={}", optRef);
|
||||
log.info("CredentialsController.getNodeCredentials(): BEGIN: ref={}", ref);
|
||||
log.trace("CredentialsController.getNodeCredentials(): JWT token: {}", jwtToken);
|
||||
|
||||
if (StringUtils.isBlank(optRef))
|
||||
if (StringUtils.isBlank(ref))
|
||||
throw new IllegalArgumentException("The 'ref' parameter is mandatory");
|
||||
|
||||
// Check if it is EMS server ref
|
||||
if (credentialsCoordinator.getReference().equals(optRef)) {
|
||||
if (credentialsCoordinator.getReference().equals(ref)) {
|
||||
if (coordinator.getBaguetteServer()==null || !coordinator.getBaguetteServer().isServerRunning()) {
|
||||
log.warn("CredentialsController.getNodeCredentials(): Baguette Server is not started");
|
||||
return null;
|
||||
@ -101,7 +121,7 @@ public class CredentialsController {
|
||||
}
|
||||
String key = coordinator.getBaguetteServer().getServerPubkey();
|
||||
|
||||
log.debug("CredentialsController.getNodeCredentials(): Retrieved EMS server connection info by reference: ref={}", optRef);
|
||||
log.debug("CredentialsController.getNodeCredentials(): Retrieved EMS server connection info by reference: ref={}", ref);
|
||||
|
||||
// Prepare response
|
||||
Map<String,String> response = new HashMap<>();
|
||||
@ -117,11 +137,11 @@ public class CredentialsController {
|
||||
}
|
||||
|
||||
// Retrieve node credentials
|
||||
NodeRegistryEntry entry = coordinator.getBaguetteServer().getNodeRegistry().getNodeByReference(optRef);
|
||||
NodeRegistryEntry entry = coordinator.getBaguetteServer().getNodeRegistry().getNodeByReference(ref);
|
||||
if (entry==null) {
|
||||
throw new IllegalArgumentException("Not found Node with reference: "+optRef);
|
||||
throw new IllegalArgumentException("Not found Node with reference: "+ref);
|
||||
}
|
||||
log.debug("CredentialsController.getNodeCredentials(): Retrieved node by reference: ref={}", optRef);
|
||||
log.debug("CredentialsController.getNodeCredentials(): Retrieved node by reference: ref={}", ref);
|
||||
|
||||
// Prepare response
|
||||
Map<String,String> response = new HashMap<>();
|
||||
@ -131,11 +151,20 @@ public class CredentialsController {
|
||||
response.put("password", entry.getPreregistration().get("ssh.password"));
|
||||
response.put("private-key", entry.getPreregistration().get("ssh.key"));
|
||||
HttpEntity<Map> entity = coordinator.createHttpEntity(Map.class, response, jwtToken);
|
||||
log.debug("CredentialsController.getNodeCredentials(): Response: ** Not shown because it contains credentials **");
|
||||
//log.debug("CredentialsController.getNodeCredentials(): Response: ** Not shown because it contains credentials **");
|
||||
log.debug("CredentialsController.getNodeCredentials(): Response: {}", encodeMapFields(response, "password", "private-key"));
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
private HashMap<String, String> encodeMapFields(Map<String, String> response, String...keys) {
|
||||
HashMap<String, String> map = new HashMap<>(response);
|
||||
for (String k : keys) {
|
||||
map.put(k, passwordUtil.encodePassword(map.getOrDefault(k, "")));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
// EMS One-Time-Password (OTP) endpoints
|
||||
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
* Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package gr.iccs.imu.ems.control.plugin;
|
||||
|
||||
import gr.iccs.imu.ems.control.controller.ControlServiceRequestInfo;
|
||||
import gr.iccs.imu.ems.translate.TranslationContext;
|
||||
import gr.iccs.imu.ems.util.Plugin;
|
||||
|
||||
/**
|
||||
* Executed before/after Application Model processing by ControlServiceCoordinator
|
||||
*/
|
||||
public interface AppModelPlugin extends Plugin {
|
||||
default void preProcessingNewAppModel(String appModelId, ControlServiceRequestInfo requestInfo) { }
|
||||
default void postProcessingNewAppModel(String appModelId, ControlServiceRequestInfo requestInfo, TranslationContext translationContext) { }
|
||||
}
|
@ -16,5 +16,6 @@ import gr.iccs.imu.ems.util.Plugin;
|
||||
* TopicBeacon plugin
|
||||
*/
|
||||
public interface BeaconPlugin extends Plugin {
|
||||
default void init(TopicBeacon.BeaconContext context) { }
|
||||
void transmit(TopicBeacon.BeaconContext context);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
package gr.iccs.imu.ems.control.properties;
|
||||
|
||||
import gr.iccs.imu.ems.util.GROUPING;
|
||||
import gr.iccs.imu.ems.util.KeystoreAndCertificateProperties;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -48,7 +49,7 @@ public class ControlServiceProperties {
|
||||
private IpSetting ipSetting = IpSetting.PUBLIC_IP;
|
||||
private ExecutionWare executionware = ExecutionWare.PROACTIVE;
|
||||
|
||||
private String upperwareGrouping;
|
||||
private String upperwareGrouping = GROUPING.GLOBAL.name();
|
||||
private String metasolverConfigurationUrl;
|
||||
private String notificationUrl;
|
||||
|
||||
|
@ -66,6 +66,16 @@ public class TopicBeacon implements InitializingBean {
|
||||
// initialize a Gson instance
|
||||
initializeGson();
|
||||
|
||||
// initialize plugins
|
||||
beaconPlugins.stream().filter(Objects::nonNull).forEach(plugin -> {
|
||||
try {
|
||||
log.debug("Topic Beacon: initializing Beacon plugin: {}", plugin.getClass().getName());
|
||||
plugin.init(beaconContext);
|
||||
} catch (Throwable t) {
|
||||
log.error("Topic Beacon: EXCEPTION while initializing Beacon plugin: {}\n", plugin.getClass().getName(), t);
|
||||
}
|
||||
});
|
||||
|
||||
// configure and start scheduler
|
||||
Date startTime = new Date(System.currentTimeMillis() + properties.getInitialDelay());
|
||||
log.debug("Topic Beacon settings: init-delay={}, delay={}, heartbeat-topics={}, threshold-topics={}, instance-topics={}",
|
||||
@ -141,6 +151,8 @@ public class TopicBeacon implements InitializingBean {
|
||||
}
|
||||
|
||||
public String toJson(Object o) {
|
||||
if (gson==null)
|
||||
initializeGson();
|
||||
return gson.toJson(o);
|
||||
}
|
||||
|
||||
@ -183,7 +195,7 @@ public class TopicBeacon implements InitializingBean {
|
||||
coordinator.getTranslationContextOfAppModel(coordinator.getCurrentAppModelId())
|
||||
.getMetricConstraints()
|
||||
.forEach(c -> {
|
||||
String message = gson.toJson(c);
|
||||
String message = toJson(c);
|
||||
log.debug("Topic Beacon: Transmitting Metric Constraint threshold info: message={}, topics={}",
|
||||
message, properties.getThresholdTopics());
|
||||
try {
|
||||
@ -206,7 +218,7 @@ public class TopicBeacon implements InitializingBean {
|
||||
String nodeName = node.getPreregistration().getOrDefault("name", "");
|
||||
String nodeIp = node.getIpAddress();
|
||||
//String nodeIp = node.getPreregistration().getOrDefault("ip","");
|
||||
// String message = gson.toJson(node);
|
||||
// String message = toJson(node);
|
||||
log.debug("Topic Beacon: Transmitting Instance info for: instance={}, ip-address={}, message={}, topics={}",
|
||||
nodeName, nodeIp, node, properties.getInstanceTopics());
|
||||
sendEventToTopics(node, properties.getInstanceTopics());
|
||||
@ -271,7 +283,7 @@ public class TopicBeacon implements InitializingBean {
|
||||
private void sendEventToTopics(Object message, Set<String> topics) throws JMSException {
|
||||
EventMap event = new EventMap(-1);
|
||||
event.put("message", message);
|
||||
String s = gson.toJson(event);
|
||||
String s = toJson(event);
|
||||
log.trace("Topic Beacon: Converted event to JSON string: {}", s);
|
||||
sendMessageToTopics(s, topics);
|
||||
}
|
||||
@ -280,7 +292,7 @@ public class TopicBeacon implements InitializingBean {
|
||||
for (String topicName : topics) {
|
||||
log.trace("Topic Beacon: Sending event to topic: event={}, topic={}", event, topicName);
|
||||
brokerCepService.publishSerializable(
|
||||
brokerCepService.getBrokerCepProperties().getBrokerUrlForClients(),
|
||||
brokerCepService.getBrokerCepProperties().getBrokerUrlForConsumer(),
|
||||
brokerCepService.getBrokerUsername(),
|
||||
brokerCepService.getBrokerPassword(),
|
||||
topicName,
|
||||
|
@ -11,7 +11,6 @@ package gr.iccs.imu.ems.control.util.jwt;
|
||||
|
||||
import gr.iccs.imu.ems.util.PasswordUtil;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Header;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
@ -21,8 +20,12 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.security.Key;
|
||||
import java.util.*;
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.InvalidPropertiesFormatException;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@ -50,10 +53,10 @@ public class JwtTokenService {
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
protected Key getKeyFromProperties() {
|
||||
protected SecretKey getKeyFromProperties() {
|
||||
if (StringUtils.isBlank(jwtTokenProperties.getSecret()))
|
||||
throw new InvalidPropertiesFormatException("JWT token secret key is blank. Check 'jwt.secret' property.");
|
||||
Key key = Keys.hmacShaKeyFor(Base64.getDecoder().decode(jwtTokenProperties.getSecret()));
|
||||
SecretKey key = Keys.hmacShaKeyFor(Base64.getDecoder().decode(jwtTokenProperties.getSecret()));
|
||||
log.debug("JwtTokenService.getKeyFromProperties(): algorithm={}, format={}, key-size={}, base64-encoded-key={}",
|
||||
key.getAlgorithm(), key.getFormat(), key.getEncoded().length, passwordUtil.encodePassword(keyToString(key)));
|
||||
return key;
|
||||
@ -68,11 +71,11 @@ public class JwtTokenService {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public Claims parseToken(String token) {
|
||||
return Jwts.parserBuilder()
|
||||
.setSigningKey(getKeyFromProperties())
|
||||
return Jwts.parser()
|
||||
.verifyWith(getKeyFromProperties())
|
||||
.build()
|
||||
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
|
||||
.getBody();
|
||||
.parseSignedClaims(token.replace(TOKEN_PREFIX, ""))
|
||||
.getPayload();
|
||||
}
|
||||
|
||||
public String createToken(String userName) {
|
||||
@ -81,22 +84,20 @@ public class JwtTokenService {
|
||||
|
||||
public String createToken(String userName, Key key) {
|
||||
return Jwts.builder()
|
||||
.setSubject(userName)
|
||||
.setAudience(AUDIENCE_UPPERWARE)
|
||||
.setExpiration(new Date(System.currentTimeMillis() + jwtTokenProperties.getExpirationTime()))
|
||||
.subject(userName)
|
||||
.audience().add(AUDIENCE_UPPERWARE).and()
|
||||
.expiration(new Date(System.currentTimeMillis() + jwtTokenProperties.getExpirationTime()))
|
||||
.signWith(key)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public String createRefreshToken(String userName) {
|
||||
Map<String, Object> header = new HashMap<>();
|
||||
header.put(Header.CONTENT_TYPE, REFRESH_HEADER_STRING);
|
||||
return Jwts.builder()
|
||||
.setSubject(userName)
|
||||
.setHeader(header)
|
||||
.setAudience(AUDIENCE_JWT)
|
||||
.setId(UUID.randomUUID().toString())
|
||||
.setExpiration(new Date(System.currentTimeMillis() + jwtTokenProperties.getRefreshTokenExpirationTime()))
|
||||
.subject(userName)
|
||||
.header().contentType(REFRESH_HEADER_STRING).and()
|
||||
.audience().add(AUDIENCE_JWT).and()
|
||||
.id(UUID.randomUUID().toString())
|
||||
.expiration(new Date(System.currentTimeMillis() + jwtTokenProperties.getRefreshTokenExpirationTime()))
|
||||
.signWith(getKeyFromProperties())
|
||||
.compact();
|
||||
}
|
||||
|
@ -46,10 +46,7 @@ import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@Order(1)
|
||||
@ -400,11 +397,11 @@ public class WebSecurityConfig implements InitializingBean {
|
||||
log.debug("jwtAuthorizationFilter: Parsing Authorization header...");
|
||||
Claims claims = jwtTokenService.parseToken(jwtValue);
|
||||
String user = claims.getSubject();
|
||||
String audience = claims.getAudience();
|
||||
Set<String> audience = claims.getAudience();
|
||||
log.debug("jwtAuthorizationFilter: Authorization header --> user: {}", user);
|
||||
log.debug("jwtAuthorizationFilter: Authorization header --> audience: {}", audience);
|
||||
if (user!=null && audience!=null) {
|
||||
if (JwtTokenService.AUDIENCE_UPPERWARE.equals(audience)) {
|
||||
if (audience.contains(JwtTokenService.AUDIENCE_UPPERWARE)) {
|
||||
log.debug("jwtAuthorizationFilter: JWT token is valid");
|
||||
UsernamePasswordAuthenticationToken authentication =
|
||||
new UsernamePasswordAuthenticationToken(user, null,
|
||||
|
@ -12,6 +12,6 @@ ${AnsiColor.046} :: Spring Boot :: ${AnsiColor.87} ${spring
|
||||
${AnsiColor.046} :: Java (TM) :: ${AnsiColor.87} (${java.version})
|
||||
${AnsiColor.046} :: Build Num. :: ${AnsiColor.226}@buildNumber@
|
||||
${AnsiColor.046} :: Build Date :: ${AnsiColor.226}@timestamp@
|
||||
${AnsiColor.046} :: SCM Branch :: ${AnsiColor.226}@git.branch@
|
||||
${AnsiColor.046} :: SCM Branch :: ${AnsiColor.226}@git.branch@, at Repos.: @git.remote.origin.url@
|
||||
${AnsiColor.046} :: Image Tag :: ${AnsiColor.226}@docker.image.name@:@docker.image.tag@
|
||||
${AnsiColor.046} :: Description :: ${AnsiColor.226}@build.description@ ${AnsiColor.DEFAULT}${AnsiStyle.NORMAL}
|
@ -432,7 +432,7 @@
|
||||
<!--<li><a href="/resources/gr.iccs.imu.ems.brokerclient.properties">Broker Client properties</a></li>-->
|
||||
<br/>
|
||||
<li><a href="/resources/baguette-client.tgz">Baguette Client TGZ</a></li>
|
||||
<li><a href="/resources/baguette-client.tgz.md5">Baguette Client MD5</a></li>
|
||||
<li><a href="/resources/baguette-client.tgz.sha256">Baguette Client SHA256</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<version>3.2.3</version>
|
||||
<relativePath></relativePath>
|
||||
</parent>
|
||||
|
||||
@ -36,10 +36,10 @@
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
|
||||
<!-- Versions for common Maven plugins -->
|
||||
<source-plugin.version>2.4</source-plugin.version>
|
||||
<maven-compiler.version>3.11.0</maven-compiler.version>
|
||||
<javadoc-plugin.version>2.9.1</javadoc-plugin.version>
|
||||
<maven-assembly-plugin.version>2.5.3</maven-assembly-plugin.version>
|
||||
<source-plugin.version>3.3.0</source-plugin.version>
|
||||
<maven-compiler.version>3.12.1</maven-compiler.version>
|
||||
<javadoc-plugin.version>3.6.3</javadoc-plugin.version>
|
||||
<maven-assembly-plugin.version>3.7.0</maven-assembly-plugin.version>
|
||||
|
||||
<!-- Gson version -->
|
||||
<gson.version>2.10.1</gson.version>
|
||||
@ -58,18 +58,18 @@
|
||||
<!-- Jasypt version -->
|
||||
<jasypt.starter.version>3.0.5</jasypt.starter.version>
|
||||
<!-- Apache SSHD version -->
|
||||
<apache-sshd.version>2.11.0</apache-sshd.version>
|
||||
<apache-sshd.version>2.12.1</apache-sshd.version>
|
||||
<!-- Bouncy Castle version -->
|
||||
<bouncy-castle.version>1.77</bouncy-castle.version>
|
||||
<!-- Guava version -->
|
||||
<guava.version>32.1.3-jre</guava.version>
|
||||
<guava.version>33.0.0-jre</guava.version>
|
||||
<!-- Apache Commons-CSV -->
|
||||
<commons-csv.version>1.10.0</commons-csv.version>
|
||||
<!-- Cryptacular -->
|
||||
<cryptacular.version>1.2.6</cryptacular.version>
|
||||
|
||||
<!-- Jackson and Snakeyaml - Used in baguette-client-install -->
|
||||
<jackson.version>2.16.0</jackson.version>
|
||||
<jackson.version>2.16.2</jackson.version>
|
||||
<snakeyaml.version>2.2</snakeyaml.version>
|
||||
</properties>
|
||||
|
||||
|
@ -11,4 +11,7 @@ package gr.iccs.imu.ems.translate;
|
||||
|
||||
public interface Translator {
|
||||
TranslationContext translate(String modelPath);
|
||||
default TranslationContext translate(String modelPath, String applicationId) {
|
||||
return translate(modelPath);
|
||||
}
|
||||
}
|
@ -25,5 +25,5 @@ public class Interval extends AbstractInterfaceRootObject {
|
||||
public enum UnitType { DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS, MICROSECONDS, NANOSECONDS }
|
||||
|
||||
private UnitType unit;
|
||||
private int period;
|
||||
private long period;
|
||||
}
|
||||
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
* Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package gr.iccs.imu.ems.util;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ConfigWriteService {
|
||||
private final Map<String,Configuration> configurations = new HashMap<>();
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
public Configuration createConfigFile(@NonNull String fileName, String format) {
|
||||
if (configurations.containsKey(fileName))
|
||||
throw new IllegalArgumentException("Config. file already exists: "+fileName);
|
||||
return getOrCreateConfigFile(fileName, format);
|
||||
}
|
||||
|
||||
public Configuration getConfigFile(@NonNull String fileName) {
|
||||
return configurations.get(fileName);
|
||||
}
|
||||
|
||||
public Configuration getOrCreateConfigFile(@NonNull String fileName, String format) {
|
||||
if (StringUtils.isBlank(format) && StringUtils.endsWithIgnoreCase(fileName, ".json")) format = "json";
|
||||
final Format fmt = EnumUtils.getEnumIgnoreCase(Format.class, format, Format.PROPERTIES);
|
||||
return configurations.computeIfAbsent(fileName, s -> new Configuration(Paths.get(fileName), fmt));
|
||||
}
|
||||
|
||||
public boolean removeConfigFile(@NonNull String fileName, boolean alsoRemoveFile) {
|
||||
Configuration c = configurations.remove(fileName);
|
||||
if (c!=null) {
|
||||
if (! c.getConfigPath().toFile().delete()) {
|
||||
log.warn("removeConfigFile: Failed to remove config. file from the disk: {}", c.getConfigPath());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum Format { PROPERTIES, JSON }
|
||||
|
||||
@Data
|
||||
@RequiredArgsConstructor
|
||||
public class Configuration {
|
||||
@NonNull private final Path configPath;
|
||||
private final Format format;
|
||||
private final Map<String,String> contentMap = new LinkedHashMap<>();
|
||||
|
||||
public void put(@NonNull String key, String value) throws IOException {
|
||||
contentMap.put(key, value);
|
||||
write();
|
||||
}
|
||||
|
||||
public void putAll(@NonNull Map<String,String> map) throws IOException {
|
||||
contentMap.putAll(map);
|
||||
write();
|
||||
}
|
||||
|
||||
public void write() throws IOException {
|
||||
String content;
|
||||
if (format==Format.JSON) content = asJson();
|
||||
else content = asProperties();
|
||||
Files.writeString(configPath, content);
|
||||
}
|
||||
|
||||
private String asProperties() throws IOException {
|
||||
try (StringWriter writer = new StringWriter()) {
|
||||
Properties p = new Properties();
|
||||
p.putAll(contentMap);
|
||||
p.store(writer, null);
|
||||
return writer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private String asJson() {
|
||||
return gson.toJson(contentMap);
|
||||
}
|
||||
}
|
||||
}
|
@ -13,12 +13,18 @@ package gr.iccs.imu.ems.util;
|
||||
* EMS constant
|
||||
*/
|
||||
public class EmsConstant {
|
||||
public final static String EMS_PROPERTIES_PREFIX = ""; //""ems.";
|
||||
public final static String EMS_PROPERTIES_PREFIX = ""; //"ems.";
|
||||
public final static String EVENT_PROPERTY_SOURCE_ADDRESS = "producer-host";
|
||||
public final static String EVENT_PROPERTY_ORIGINAL_DESTINATION = "original-destination";
|
||||
public final static String EVENT_PROPERTY_EFFECTIVE_DESTINATION = "effective-destination";
|
||||
public final static String EVENT_PROPERTY_KEY = "destination-key";
|
||||
public final static String NETDATA_METRIC_KEY = "_netdata_metric";
|
||||
|
||||
public final static String COLLECTOR_DESTINATION_ALIASES = "destination-aliases";
|
||||
public final static String COLLECTOR_DESTINATION_ALIASES_DELIMITERS = "[,;: \t\r\n]+";
|
||||
public final static String COLLECTOR_ALLOWED_TOPICS_VAR = "COLLECTOR_ALLOWED_TOPICS";
|
||||
public static final String COLLECTOR_CONFIGURATIONS_VAR = "COLLECTOR_CONFIGURATIONS";
|
||||
|
||||
public final static String EMS_CLIENT_K8S_CONFIG_MAP_FILE = "ems-client-configmap.json";
|
||||
public final static String EMS_CLIENT_K8S_CONFIG_MAP_FORMAT = "json";
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
* Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package gr.iccs.imu.ems.util;
|
||||
|
||||
/**
|
||||
* EMS Release info
|
||||
*/
|
||||
public class EmsRelease {
|
||||
public final static String EMS_ID = "ems";
|
||||
public final static String EMS_NAME = "Event Management System";
|
||||
public final static String EMS_VERSION = EmsRelease.class.getPackage().getImplementationVersion();
|
||||
public final static String EMS_COPYRIGHT =
|
||||
"Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr)";
|
||||
public final static String EMS_LICENSE = "Mozilla Public License, v2.0";
|
||||
public final static String EMS_DESCRIPTION = String.format("\n%s (%s), v.%s, %s\n%s\n",
|
||||
EMS_NAME, EMS_ID, EMS_VERSION, EMS_LICENSE, EMS_COPYRIGHT);
|
||||
}
|
@ -30,6 +30,7 @@ import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.*;
|
||||
import java.security.cert.Certificate;
|
||||
@ -473,11 +474,26 @@ public class KeystoreUtil {
|
||||
log.debug(" Entry SAN: {}", properties.getKeyEntryExtSAN());
|
||||
log.debug(" Entry Gen.: {}", properties.getKeyEntryGenerate());
|
||||
|
||||
IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE keyGen = properties.getKeyEntryGenerate();
|
||||
boolean gen = (keyGen==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.YES || keyGen==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.ALWAYS);
|
||||
IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE keyGenerationStrategy = properties.getKeyEntryGenerate();
|
||||
boolean generateKey = (keyGenerationStrategy==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.YES
|
||||
|| keyGenerationStrategy==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.ALWAYS);
|
||||
|
||||
// If ALWAYS then remove previous keystore and truststore files
|
||||
if (generateKey) {
|
||||
Path oldKeystoreFile = Path.of(properties.getKeystoreFile());
|
||||
if (Files.exists(oldKeystoreFile))
|
||||
Files.deleteIfExists(oldKeystoreFile);
|
||||
Path oldTruststoreFile = Path.of(properties.getTruststoreFile());
|
||||
if (Files.exists(oldTruststoreFile))
|
||||
Files.deleteIfExists(oldTruststoreFile);
|
||||
Path oldCertificateFile = Path.of(properties.getCertificateFile());
|
||||
if (Files.exists(oldCertificateFile))
|
||||
Files.deleteIfExists(oldCertificateFile);
|
||||
log.debug(" Removed old Keystore, Truststore and Certificate files");
|
||||
}
|
||||
|
||||
// Check if key entry is missing
|
||||
if (keyGen==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.IF_MISSING) {
|
||||
if (keyGenerationStrategy==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.IF_MISSING) {
|
||||
// Check if keystore and truststore files exist (and create if they don't)
|
||||
KeystoreUtil
|
||||
.getKeystore(properties.getKeystoreFile(), properties.getKeystoreType(), properties.getKeystorePassword())
|
||||
@ -497,12 +513,12 @@ public class KeystoreUtil {
|
||||
log.debug(" Keystore already contains entry: {}", properties.getKeyEntryName());
|
||||
} else {
|
||||
log.debug(" Keystore does not contain entry: {}", properties.getKeyEntryName());
|
||||
gen = true;
|
||||
generateKey = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if IP address is in subject CN or SAN list
|
||||
if (keyGen==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.IF_IP_CHANGED) {
|
||||
if (keyGenerationStrategy==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.IF_IP_CHANGED) {
|
||||
// Check if keystore and truststore files exist (and create if they don't)
|
||||
KeystoreUtil
|
||||
.getKeystore(properties.getKeystoreFile(), properties.getKeystoreType(), properties.getKeystorePassword())
|
||||
@ -527,13 +543,13 @@ public class KeystoreUtil {
|
||||
// check if Default and Public IP addresses are contained in 'addrList'
|
||||
boolean defaultFound = addrList.stream().anyMatch(s -> s.equals(defaultIp));
|
||||
boolean publicFound = addrList.stream().anyMatch(s -> s.equals(publicIp));
|
||||
gen = !defaultFound || !publicFound;
|
||||
generateKey = !defaultFound || !publicFound;
|
||||
log.debug(" Address has changed: {} (default-ip-found={}, public-ip-found={})",
|
||||
gen, defaultFound, publicFound);
|
||||
generateKey, defaultFound, publicFound);
|
||||
}
|
||||
|
||||
// Generate new key pair and certificate, and update keystore and trust store
|
||||
if (gen) {
|
||||
if (generateKey) {
|
||||
log.debug(" Generating new Key pair and Certificate for: {}", properties.getKeyEntryName());
|
||||
|
||||
KeystoreUtil ksUtil = KeystoreUtil
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
package gr.iccs.imu.ems.util;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@ -30,6 +31,11 @@ public class NetUtil {
|
||||
|
||||
private final static String[][] PUBLIC_ADDRESS_DISCOVERY_SERVICES;
|
||||
|
||||
@Getter
|
||||
private final static boolean usePublic;
|
||||
@Getter
|
||||
private final static boolean useDefault;
|
||||
|
||||
static {
|
||||
// Configure Address Filters
|
||||
String filtersStr = System.getenv("NET_UTIL_ADDRESS_FILTERS");
|
||||
@ -69,6 +75,12 @@ public class NetUtil {
|
||||
servicesList.add(Arrays.asList("WhatIsMyIpAddress", "http://bot.whatismyipaddress.com/").toArray(new String[0]));
|
||||
}
|
||||
PUBLIC_ADDRESS_DISCOVERY_SERVICES = servicesList.toArray(new String[0][]);
|
||||
|
||||
// Configure IP address setting
|
||||
String s = System.getenv("IP_SETTING");
|
||||
s = s!=null ? s.trim() : "";
|
||||
useDefault = "DEFAULT_IP".equalsIgnoreCase(s);
|
||||
usePublic = ! useDefault;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@ -272,6 +284,12 @@ public class NetUtil {
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public static String getIpSettingAddress() {
|
||||
return usePublic ? getPublicIpAddress() : getDefaultIpAddress();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
public static boolean isLocalAddress(String addr) throws UnknownHostException {
|
||||
return isLocalAddress(InetAddress.getByName(addr));
|
||||
}
|
||||
|
@ -45,10 +45,11 @@ export const FORM_TYPE_OPTIONS = [
|
||||
'text': 'Credentials',
|
||||
'priority': 1001,
|
||||
'options': [
|
||||
{ 'id': 'get-cred', 'text': 'EMS server Broker credentials', 'url': '/broker/credentials', 'method': 'GET', 'form': '', 'priority': 1 },
|
||||
{ 'id': 'get-ref', 'text': 'VM credentials by Ref', 'url': '/baguette/ref/{ref}', 'method': 'GET', 'form': 'ref-form', 'priority': 2 },
|
||||
{ 'id': 'new-otp', 'text': 'New OTP', 'url': '/ems/otp/new', 'method': 'GET', 'form': '', 'priority': 3 },
|
||||
{ 'id': 'del-otp', 'text': 'Delete OTP', 'url': '/ems/otp/remove/{otp}', 'method': 'GET', 'form': 'otp-form', 'priority': 4 },
|
||||
{ 'id': 'get-bc-cred', 'text': 'EMS server Broker credentials', 'url': '/broker/credentials', 'method': 'GET', 'form': '', 'priority': 1 },
|
||||
{ 'id': 'get-bs-cred', 'text': 'Baguette Server connection info', 'url': '/baguette/connectionInfo', 'method': 'GET', 'form': '', 'priority': 2 },
|
||||
{ 'id': 'get-ref', 'text': 'VM credentials by Ref', 'url': '/baguette/ref/{ref}', 'method': 'GET', 'form': 'ref-form', 'priority': 3 },
|
||||
{ 'id': 'new-otp', 'text': 'New OTP', 'url': '/ems/otp/new', 'method': 'GET', 'form': '', 'priority': 4 },
|
||||
{ 'id': 'del-otp', 'text': 'Delete OTP', 'url': '/ems/otp/remove/{otp}', 'method': 'GET', 'form': 'otp-form', 'priority': 5 },
|
||||
]
|
||||
},
|
||||
|
||||
|
@ -54,7 +54,7 @@
|
||||
<a class="dropdown-item" href="/resources/client.sh" target="_new">Broker Client .sh</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="/resources/baguette-client.tgz" target="_new">Baguette Client</a>
|
||||
<a class="dropdown-item" href="/resources/baguette-client.tgz.md5" target="_new">Baguette Client MD5</a>
|
||||
<a class="dropdown-item" href="/resources/baguette-client.tgz.sha256" target="_new">Baguette Client SHA256</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item disabled" href="#">Details...</a>
|
||||
</div>
|
||||
|
@ -77,10 +77,10 @@
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/resources/baguette-client.tgz.md5" class="nav-link" target="_new">
|
||||
<a href="/resources/baguette-client.tgz.sha256" class="nav-link" target="_new">
|
||||
<i class="fas fa fa-font nav-icon"></i>
|
||||
<p>
|
||||
Baguette Client MD5
|
||||
Baguette Client SHA256
|
||||
<i class="fas right fa-arrow-alt-circle-down"></i>
|
||||
</p>
|
||||
</a>
|
||||
|
@ -15,8 +15,8 @@
|
||||
<artifactId>ems-nebulous-plugin</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>Nebulous-EMS plugin</name>
|
||||
<description>Nebulous-EMS plugin provides metric model translator and MVV service</description>
|
||||
<name>Nebulous EMS plugin</name>
|
||||
<description>Nebulous EMS plugin providing the metric model translator and MVV service</description>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
@ -28,20 +28,24 @@
|
||||
<ems.version>7.0.0-SNAPSHOT</ems.version>
|
||||
|
||||
<!-- Spring Boot versions -->
|
||||
<spring.version>6.1.2</spring.version>
|
||||
<spring-boot.version>3.2.1</spring-boot.version>
|
||||
<spring.version>6.1.4</spring.version>
|
||||
<spring-boot.version>3.2.3</spring-boot.version>
|
||||
<snakeyaml.version>2.2</snakeyaml.version>
|
||||
<lombok.version>1.18.30</lombok.version>
|
||||
|
||||
<!-- Nebulous-EMS extension dependency versions -->
|
||||
<jackson.version>2.16.0</jackson.version>
|
||||
<json-path.version>2.8.0</json-path.version>
|
||||
<jackson.version>2.16.2</jackson.version>
|
||||
<json-path.version>2.9.0</json-path.version>
|
||||
<thymeleaf.version>3.1.2.RELEASE</thymeleaf.version>
|
||||
<schematron.version>7.1.3</schematron.version>
|
||||
<schematron.version>8.0.0</schematron.version>
|
||||
|
||||
<!-- io.fabricat8 docker-maven-plugin properties -->
|
||||
<docker-image-properties-file>../ems-core/control-service/target/docker-image.properties</docker-image-properties-file>
|
||||
<docker-maven-plugin.version>0.43.2</docker-maven-plugin.version>
|
||||
|
||||
<!-- EMS Nebulous Docker image properties -->
|
||||
<docker-image-properties-file>docker-image.properties</docker-image-properties-file>
|
||||
<docker.image.tag-nebulous>${docker.image.tag}-nebulous</docker.image.tag-nebulous>
|
||||
<build.description>EMS Nebulous Docker Image is based on the EMS Core Docker Image</build.description>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@ -117,6 +121,12 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>eu.nebulouscloud</groupId>
|
||||
<artifactId>exn-connector-java</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Compile time dependencies - Specific to Nebulous EMS extension -->
|
||||
|
||||
@ -137,6 +147,15 @@
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ANTLR4 dependency -->
|
||||
<!--XXX: Using version 4.7 required by Esper 7.1.0 (used in Broker-CEP)
|
||||
XXX: Using latest ANTLR4 version results in conflict!
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>4.13.1</version>
|
||||
</dependency>-->
|
||||
|
||||
<!-- Thymeleaf dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
@ -168,8 +187,109 @@
|
||||
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>nexus-nebulous</id>
|
||||
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<targetPath>${project.build.directory}</targetPath>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>banner.txt</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
<includes>
|
||||
<include>*</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>banner.txt</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<!-- Plugins for getting Buildnumber and Git info -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>buildnumber-create</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>create</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>buildnumber-create-metadata</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>create-metadata</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
<!--<format>{0,number,integer}</format>-->
|
||||
<timestampFormat>yyyy-MM-dd HH:mm:ss.SSSZ</timestampFormat>
|
||||
<revisionOnScmFailure>${project.version}</revisionOnScmFailure>
|
||||
<!--<revisionOnScmFailure>unknownbuild</revisionOnScmFailure>-->
|
||||
<items>
|
||||
<item>buildNumber</item>
|
||||
</items>
|
||||
<doCheck>false</doCheck>
|
||||
<doUpdate>false</doUpdate>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>io.github.git-commit-id</groupId>
|
||||
<artifactId>git-commit-id-maven-plugin</artifactId>
|
||||
<version>7.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>get-git-info</id>
|
||||
<goals>
|
||||
<goal>revision</goal>
|
||||
</goals>
|
||||
<phase>initialize</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<generateGitPropertiesFile>true</generateGitPropertiesFile>
|
||||
<generateGitPropertiesFilename>${project.build.directory}/git.properties</generateGitPropertiesFilename>
|
||||
<commitIdGenerationMode>full</commitIdGenerationMode>
|
||||
<failOnNoGitDirectory>false</failOnNoGitDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-maven-plugin</artifactId>
|
||||
<!--<version>4.13.1</version>-->
|
||||
<version>4.7</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>antlr4</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<listener>true</listener>
|
||||
<visitor>true</visitor>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
@ -206,6 +326,10 @@
|
||||
tofile="${project.build.directory}/${project.artifactId}-${project.version}-jar-with-dependencies.jar"
|
||||
force="true"/>
|
||||
<delete file="${project.build.directory}/temp-jar-with-dependencies.jar" />
|
||||
<!--XXX:TODO: Used during development -->
|
||||
<!--<copy file="${project.build.directory}/${project.artifactId}-${project.version}-jar-with-dependencies.jar"
|
||||
todir="${project.basedir}/../../tests/plugins/"
|
||||
force="true"/>-->
|
||||
</target>
|
||||
</configuration>
|
||||
<goals>
|
||||
@ -215,6 +339,32 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>get-ems-core-docker-image-properties</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>gr.iccs.imu.ems</groupId>
|
||||
<artifactId>control-service</artifactId>
|
||||
<version>${ems.version}</version>
|
||||
<classifier>docker-image</classifier>
|
||||
<type>properties</type>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
<destFileName>${docker-image-properties-file}</destFileName>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Read docker image properties from file -->
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
@ -232,7 +382,7 @@
|
||||
<configuration>
|
||||
<!--<keyPrefix>dev-</keyPrefix>-->
|
||||
<files>
|
||||
<file>${docker-image-properties-file}</file>
|
||||
<file>${project.build.directory}/${docker-image-properties-file}</file>
|
||||
</files>
|
||||
<outputFile/>
|
||||
<properties/>
|
||||
@ -260,57 +410,14 @@
|
||||
</executions>
|
||||
</plugin>-->
|
||||
|
||||
<!-- Build docker image using docker-context folder -->
|
||||
<!--<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<version>${docker-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<verbose>true</verbose>
|
||||
<useColor>true</useColor>
|
||||
<images>
|
||||
<image>
|
||||
<name>${docker.image.name}:${docker.image.tag}-nebulous</name>
|
||||
<build>
|
||||
<from>${docker.image.name}:${docker.image.tag}</from>
|
||||
<labels>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<artifactId>${project.groupId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</labels>
|
||||
<env>
|
||||
<EXTRA_LOADER_PATHS>/plugins/*</EXTRA_LOADER_PATHS>
|
||||
<SCAN_PACKAGES>eu.nebulous.ems</SCAN_PACKAGES>
|
||||
</env>
|
||||
<assembly>
|
||||
<targetDir>/plugin</targetDir>
|
||||
<inline>
|
||||
<files>
|
||||
<file>
|
||||
<!– Path to the file you want to copy –>
|
||||
<source>${project.build.directory}/${project.artifactId}-${project.version}-jar-with-dependencies.jar</source>
|
||||
<!– Destination path within the container –>
|
||||
<outputDirectory></outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
</inline>
|
||||
</assembly>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>docker-image-build</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>-->
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:http://127.0.0.1/dummy</connection>
|
||||
<developerConnection>scm:git:https://127.0.0.1/dummy</developerConnection>
|
||||
<tag>HEAD</tag>
|
||||
<url>http://127.0.0.1/dummy</url>
|
||||
</scm>
|
||||
|
||||
</project>
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
grammar Constraints;
|
||||
|
||||
constraintExpression
|
||||
: orConstraint EOF ;
|
||||
|
||||
orConstraint
|
||||
: andConstraint ( OR andConstraint )*
|
||||
;
|
||||
|
||||
andConstraint
|
||||
: constraint ( AND constraint )*
|
||||
;
|
||||
|
||||
constraint
|
||||
: PARENTHESES_OPEN orConstraint PARENTHESES_CLOSE
|
||||
| metricConstraint
|
||||
| notConstraint
|
||||
| conditionalConstraint
|
||||
;
|
||||
|
||||
metricConstraint
|
||||
: ID comparisonOperator NUM
|
||||
| NUM comparisonOperator ID
|
||||
;
|
||||
|
||||
comparisonOperator
|
||||
: '=' | '==' | '<>'
|
||||
| '<' | '<=' | '=<'
|
||||
| '>' | '>=' | '=>'
|
||||
;
|
||||
|
||||
notConstraint
|
||||
: NOT constraint
|
||||
;
|
||||
|
||||
/*logicalOperator
|
||||
: AND | OR
|
||||
;*/
|
||||
|
||||
conditionalConstraint
|
||||
: IF orConstraint THEN orConstraint ( ELSE orConstraint )?
|
||||
;
|
||||
|
||||
PARENTHESES_OPEN: '(' ;
|
||||
PARENTHESES_CLOSE: ')' ;
|
||||
|
||||
NOT: N O T ;
|
||||
AND: A N D ;
|
||||
OR: O R ;
|
||||
|
||||
IF: I F ;
|
||||
THEN: T H E N ;
|
||||
ELSE: E L S E ;
|
||||
|
||||
fragment A: 'a' | 'A' ;
|
||||
fragment D: 'd' | 'D' ;
|
||||
fragment E: 'e' | 'E' ;
|
||||
fragment F: 'f' | 'F' ;
|
||||
fragment H: 'h' | 'H' ;
|
||||
fragment I: 'i' | 'I' ;
|
||||
fragment L: 'l' | 'L' ;
|
||||
fragment N: 'n' | 'N' ;
|
||||
fragment O: 'o' | 'O' ;
|
||||
fragment R: 'r' | 'R' ;
|
||||
fragment S: 's' | 'S' ;
|
||||
fragment T: 't' | 'T' ;
|
||||
|
||||
ID: [a-zA-Z] [a-zA-Z0-9_-]* ;
|
||||
|
||||
NUM: ('-' | '+')? [0-9]+ ('.' [0-9]+)?
|
||||
| ('-' | '+')? '.' [0-9]+
|
||||
;
|
||||
|
||||
WS: [ \r\n\t]+ -> skip ;
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
* Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package eu.nebulous.ems.controller;
|
||||
|
||||
import eu.nebulous.ems.translate.NebulousEmsTranslatorProperties;
|
||||
import gr.iccs.imu.ems.control.controller.ControlServiceCoordinator;
|
||||
import gr.iccs.imu.ems.control.controller.ControlServiceRequestInfo;
|
||||
import gr.iccs.imu.ems.control.controller.RestControllerException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class ModelController {
|
||||
|
||||
private final NebulousEmsTranslatorProperties translatorProperties;
|
||||
private final ControlServiceCoordinator coordinator;
|
||||
|
||||
@RequestMapping(value = "/loadAppModel", method = POST)
|
||||
public String loadAppModel(@RequestBody Map<String,String> request,
|
||||
@RequestHeader(name = HttpHeaders.AUTHORIZATION, required = false) String jwtToken) throws IOException {
|
||||
log.debug("ModelController.loadAppModel(): Received request: {}", request);
|
||||
log.trace("ModelController.loadAppModel(): JWT token: {}", jwtToken);
|
||||
|
||||
// Get information from request
|
||||
String applicationId = request.getOrDefault("application-id", "model-"+System.currentTimeMillis());
|
||||
String applicationName = request.getOrDefault("application-name", applicationId);
|
||||
String modelString = request.getOrDefault("model", null);
|
||||
if (StringUtils.isBlank(modelString))
|
||||
modelString = request.getOrDefault("body", null);
|
||||
String modelFile = request.getOrDefault("model-path", "").toString();
|
||||
log.info("ModelController.loadAppModel(): Request info: app-id={}, app-name={}, model-path={}, model={}",
|
||||
applicationId, applicationName, modelFile, modelString);
|
||||
|
||||
// Check parameters
|
||||
if (StringUtils.isBlank(applicationId)) {
|
||||
log.warn("ModelController.loadAppModel(): Request does not contain an application id");
|
||||
throw new RestControllerException(400, "Request does not contain an application id");
|
||||
}
|
||||
if (StringUtils.isBlank(modelString)) {
|
||||
log.warn("ModelController.loadAppModel(): Request does not contain a model");
|
||||
throw new RestControllerException(400, "Request does not contain a model");
|
||||
}
|
||||
|
||||
// Store model in the disk
|
||||
if (StringUtils.isNotBlank(modelString)) {
|
||||
modelFile = StringUtils.isBlank(modelFile) ? getModelFile(applicationId) : modelFile;
|
||||
storeModel(modelFile, modelString);
|
||||
}
|
||||
|
||||
// Start translation and reconfiguration in a worker thread
|
||||
coordinator.processAppModel(modelFile, null,
|
||||
ControlServiceRequestInfo.create(applicationId, null, null, jwtToken, null));
|
||||
log.debug("ModelController.loadAppModel(): Model translation dispatched to a worker thread");
|
||||
|
||||
return "OK";
|
||||
}
|
||||
|
||||
private String getModelFile(String appId) {
|
||||
return String.format("model-%s--%d.yml", appId, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private void storeModel(String fileName, String modelStr) throws IOException {
|
||||
Path path = Paths.get(translatorProperties.getModelsDir(), fileName);
|
||||
Files.writeString(path, modelStr);
|
||||
log.info("ModelController.storeModel(): Stored metric model in file: {}", path);
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
* Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package eu.nebulous.ems.plugins;
|
||||
|
||||
import gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask;
|
||||
import gr.iccs.imu.ems.baguette.client.install.plugin.AllowedTopicsProcessorPlugin;
|
||||
import gr.iccs.imu.ems.baguette.server.NodeRegistryEntry;
|
||||
import gr.iccs.imu.ems.control.controller.ControlServiceRequestInfo;
|
||||
import gr.iccs.imu.ems.control.plugin.AppModelPlugin;
|
||||
import gr.iccs.imu.ems.translate.TranslationContext;
|
||||
import gr.iccs.imu.ems.util.ConfigWriteService;
|
||||
import gr.iccs.imu.ems.util.EmsConstant;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class NebulousAppModelPlugin implements AppModelPlugin {
|
||||
private final AllowedTopicsProcessorPlugin allowedTopicsProcessorPlugin;
|
||||
private final ConfigWriteService configWriteService;
|
||||
|
||||
@Override
|
||||
public void preProcessingNewAppModel(String appModelId, ControlServiceRequestInfo requestInfo) {
|
||||
log.debug("NebulousAppModelPlugin: Nothing to do. Args: appModelId={}, requestInfo={}", appModelId, requestInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessingNewAppModel(String appModelId, ControlServiceRequestInfo requestInfo, TranslationContext translationContext) {
|
||||
log.debug("NebulousAppModelPlugin: BEGIN: appModelId={}, requestInfo={}", appModelId, requestInfo);
|
||||
|
||||
// Get collector allowed topics
|
||||
NodeRegistryEntry entry = new NodeRegistryEntry(null, null, null);
|
||||
ClientInstallationTask task = ClientInstallationTask.builder()
|
||||
.nodeRegistryEntry(entry)
|
||||
.translationContext(translationContext)
|
||||
.build();
|
||||
allowedTopicsProcessorPlugin.processBeforeInstallation(task, -1);
|
||||
String allowedTopics = task.getNodeRegistryEntry().getPreregistration().get(EmsConstant.COLLECTOR_ALLOWED_TOPICS_VAR);
|
||||
log.debug("NebulousAppModelPlugin: collector-allowed-topics: {}", allowedTopics);
|
||||
|
||||
if (StringUtils.isBlank(allowedTopics)) {
|
||||
log.debug("NebulousAppModelPlugin: END: No value for 'collector-allowed-topics' setting: appModelId={}, requestInfo={}", appModelId, requestInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
// Append collector-allowed-topics in ems-client-configmap file
|
||||
try {
|
||||
configWriteService
|
||||
.getOrCreateConfigFile(
|
||||
EmsConstant.EMS_CLIENT_K8S_CONFIG_MAP_FILE,
|
||||
EmsConstant.EMS_CLIENT_K8S_CONFIG_MAP_FORMAT)
|
||||
.put(EmsConstant.COLLECTOR_ALLOWED_TOPICS_VAR, allowedTopics);
|
||||
log.debug("NebulousAppModelPlugin: END: Updated ems-client-configmap file: {}", EmsConstant.EMS_CLIENT_K8S_CONFIG_MAP_FILE);
|
||||
} catch (IOException e) {
|
||||
log.error("NebulousAppModelPlugin: EXCEPTION while updating ems-client-configmap file, during post-processing of new App Model: appModelId={}, requestInfo={}\nException: ",
|
||||
appModelId, requestInfo, e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless
|
||||
* Esper library is used, in which case it is subject to the terms of General Public License v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package eu.nebulous.ems.plugins;
|
||||
|
||||
import gr.iccs.imu.ems.control.plugin.WebAdminPlugin;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class NebulousWebAdminPlugin implements WebAdminPlugin {
|
||||
public RestCallCommandGroup restCallCommands() {
|
||||
RestCallForm form = RestCallForm.builder()
|
||||
.id("load-app-model-form")
|
||||
.field( RestCallFormField.builder().name("application-id").text("Application Id").build() )
|
||||
.field( RestCallFormField.builder().name("application-name").text("Application Name").build() )
|
||||
.field( RestCallFormField.builder().name("model-path").text("Model Name").build() )
|
||||
.field( RestCallFormField.builder().name("model").text("Model").build() )
|
||||
.build();
|
||||
return RestCallCommandGroup.builder()
|
||||
.id("nebulous-group")
|
||||
.text("Nebulous-related API")
|
||||
.priority(0)
|
||||
.command(RestCallCommand.builder()
|
||||
.id("load-model").text("Load App. Model")
|
||||
.method("POST").url("/loadAppModel")
|
||||
.form(form)
|
||||
.priority(10).build())
|
||||
.build();
|
||||
}
|
||||
}
|
@ -9,25 +9,46 @@
|
||||
|
||||
package eu.nebulous.ems.plugins;
|
||||
|
||||
import eu.nebulous.ems.service.ExternalBrokerPublisherService;
|
||||
import eu.nebulous.ems.service.ExternalBrokerServiceProperties;
|
||||
import gr.iccs.imu.ems.control.plugin.BeaconPlugin;
|
||||
import gr.iccs.imu.ems.control.properties.TopicBeaconProperties;
|
||||
import gr.iccs.imu.ems.control.util.TopicBeacon;
|
||||
import gr.iccs.imu.ems.translate.TranslationContext;
|
||||
import jakarta.jms.JMSException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.jms.JMSException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PredictionInfoBeaconPlugin implements BeaconPlugin {
|
||||
private final ExternalBrokerServiceProperties properties;
|
||||
private final ExternalBrokerPublisherService externalBrokerPublisherService;
|
||||
private final String externalBrokerMetricsToPredictTopic = ExternalBrokerServiceProperties.BASE_TOPIC_PREFIX + "metric_list";
|
||||
private final String externalBrokerSloViolationDetectorTopics = ExternalBrokerServiceProperties.BASE_TOPIC_PREFIX + "slo.new";
|
||||
|
||||
public void init(TopicBeacon.BeaconContext context) {
|
||||
log.debug("PredictionInfoBeaconPlugin.init(): Invoked");
|
||||
if (properties.isEnabled()) {
|
||||
externalBrokerPublisherService.addAdditionalTopic(externalBrokerMetricsToPredictTopic, externalBrokerMetricsToPredictTopic);
|
||||
externalBrokerPublisherService.addAdditionalTopic(externalBrokerSloViolationDetectorTopics, externalBrokerSloViolationDetectorTopics);
|
||||
log.debug("PredictionInfoBeaconPlugin.init(): Initialized ExternalBrokerService");
|
||||
} else
|
||||
log.debug("PredictionInfoBeaconPlugin.init(): ExternalBrokerService is disabled due to configuration");
|
||||
}
|
||||
|
||||
public void transmit(TopicBeacon.BeaconContext context) {
|
||||
log.debug("PredictionInfoBeaconPlugin.transmit(): Invoked");
|
||||
transmitPredictionInfo(context);
|
||||
transmitSloViolatorInfo(context);
|
||||
log.trace("PredictionInfoBeaconPlugin.transmit(): Transmitted ");
|
||||
}
|
||||
|
||||
private <T>Set<T> emptyIfNull(Set<T> s) {
|
||||
@ -54,6 +75,8 @@ public class PredictionInfoBeaconPlugin implements BeaconPlugin {
|
||||
// Get metric contexts of top-level DAG nodes
|
||||
String metricContexts = translationContext.getAdditionalResultsAs(
|
||||
PredictionsPostTranslationPlugin.PREDICTION_TOP_LEVEL_NODES_METRICS, String.class);
|
||||
Map metricContextsMap = translationContext.getAdditionalResultsAs(
|
||||
PredictionsPostTranslationPlugin.PREDICTION_TOP_LEVEL_NODES_METRICS_MAP, Map.class);
|
||||
log.debug("Topic Beacon: transmitPredictionInfo: Metric Contexts for prediction: {}", metricContexts);
|
||||
|
||||
// Skip event sending if payload is empty
|
||||
@ -66,6 +89,8 @@ public class PredictionInfoBeaconPlugin implements BeaconPlugin {
|
||||
log.debug("Topic Beacon: Transmitting Prediction info: event={}, topics={}", metricContexts, properties.getPredictionTopics());
|
||||
try {
|
||||
context.sendMessageToTopics(metricContexts, properties.getPredictionTopics());
|
||||
if (properties.isEnabled())
|
||||
externalBrokerPublisherService.publishMessage(externalBrokerMetricsToPredictTopic, metricContextsMap);
|
||||
} catch (JMSException e) {
|
||||
log.error("Topic Beacon: EXCEPTION while transmitting Prediction info: event={}, topics={}, exception: ",
|
||||
metricContexts, properties.getPredictionTopics(), e);
|
||||
@ -88,6 +113,8 @@ public class PredictionInfoBeaconPlugin implements BeaconPlugin {
|
||||
// Get SLO metric decompositions (String) from TranslationContext
|
||||
String sloMetricDecompositions = translationContext.getAdditionalResultsAs(
|
||||
PredictionsPostTranslationPlugin.PREDICTION_SLO_METRIC_DECOMPOSITION, String.class);
|
||||
Map sloMetricDecompositionsMap = translationContext.getAdditionalResultsAs(
|
||||
PredictionsPostTranslationPlugin.PREDICTION_SLO_METRIC_DECOMPOSITION_MAP, Map.class);
|
||||
log.debug("Topic Beacon: transmitSloViolatorInfo: SLO metric decompositions: {}", sloMetricDecompositions);
|
||||
|
||||
if (StringUtils.isBlank(sloMetricDecompositions)) {
|
||||
@ -99,6 +126,8 @@ public class PredictionInfoBeaconPlugin implements BeaconPlugin {
|
||||
log.debug("Topic Beacon: Transmitting SLO Violator info: event={}, topics={}", sloMetricDecompositions, properties.getSloViolationDetectorTopics());
|
||||
try {
|
||||
context.sendMessageToTopics(sloMetricDecompositions, properties.getSloViolationDetectorTopics());
|
||||
if (properties.isEnabled())
|
||||
externalBrokerPublisherService.publishMessage(externalBrokerSloViolationDetectorTopics, sloMetricDecompositionsMap);
|
||||
} catch (JMSException e) {
|
||||
log.error("Topic Beacon: EXCEPTION while transmitting SLO Violator info: event={}, topics={}, exception: ",
|
||||
sloMetricDecompositions, properties.getSloViolationDetectorTopics(), e);
|
||||
|
@ -11,7 +11,6 @@ package eu.nebulous.ems.plugins;
|
||||
|
||||
import eu.nebulous.ems.translate.NebulousEmsTranslator;
|
||||
import gr.iccs.imu.ems.control.plugin.PostTranslationPlugin;
|
||||
import gr.iccs.imu.ems.control.properties.TopicBeaconProperties;
|
||||
import gr.iccs.imu.ems.control.util.TopicBeacon;
|
||||
import gr.iccs.imu.ems.translate.TranslationContext;
|
||||
import gr.iccs.imu.ems.translate.dag.DAGNode;
|
||||
@ -22,7 +21,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.time.temporal.ValueRange;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -30,7 +29,9 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PredictionsPostTranslationPlugin implements PostTranslationPlugin {
|
||||
public final static String PREDICTION_SLO_METRIC_DECOMPOSITION_MAP = "NEBULOUS_PREDICTION_SLO_METRIC_DECOMPOSITION_MAP";
|
||||
public final static String PREDICTION_SLO_METRIC_DECOMPOSITION = "NEBULOUS_PREDICTION_SLO_METRIC_DECOMPOSITION";
|
||||
public final static String PREDICTION_TOP_LEVEL_NODES_METRICS_MAP = "NEBULOUS_PREDICTION_TOP_LEVEL_NODES_METRICS_MAP";
|
||||
public final static String PREDICTION_TOP_LEVEL_NODES_METRICS = "NEBULOUS_PREDICTION_TOP_LEVEL_NODES_METRICS";
|
||||
|
||||
@PostConstruct
|
||||
@ -42,15 +43,27 @@ public class PredictionsPostTranslationPlugin implements PostTranslationPlugin {
|
||||
public void processTranslationResults(TranslationContext translationContext, TopicBeacon topicBeacon) {
|
||||
log.debug("PredictionsPostTranslationPlugin.processTranslationResults(): BEGIN");
|
||||
|
||||
String sloMetricDecompositions = getSLOMetricDecompositionPayload(translationContext, topicBeacon);
|
||||
translationContext.getAdditionalResults().put(PREDICTION_SLO_METRIC_DECOMPOSITION, sloMetricDecompositions);
|
||||
log.debug("PredictionsPostTranslationPlugin.processTranslationResults(): SLO metrics decompositions: model={}, decompositions={}",
|
||||
translationContext.getModelName(), sloMetricDecompositions);
|
||||
// PREDICTION_SLO_METRIC_DECOMPOSITION
|
||||
Map<String, Object> sloMetricDecompositionsMap = getSLOMetricDecompositionPayload(translationContext, topicBeacon);
|
||||
translationContext.getAdditionalResults().put(PREDICTION_SLO_METRIC_DECOMPOSITION_MAP, sloMetricDecompositionsMap);
|
||||
log.debug("PredictionsPostTranslationPlugin.processTranslationResults(): SLO metrics decompositions: model={}, decompositions-map={}",
|
||||
translationContext.getModelName(), sloMetricDecompositionsMap);
|
||||
|
||||
String metricsOfTopLevelNodes = getMetricsForPredictionPayload(translationContext, topicBeacon);
|
||||
translationContext.getAdditionalResults().put(PREDICTION_TOP_LEVEL_NODES_METRICS, metricsOfTopLevelNodes);
|
||||
String sloMetricDecompositionsStr = topicBeacon.toJson(sloMetricDecompositionsMap);
|
||||
translationContext.getAdditionalResults().put(PREDICTION_SLO_METRIC_DECOMPOSITION, sloMetricDecompositionsStr);
|
||||
log.debug("PredictionsPostTranslationPlugin.processTranslationResults(): SLO metrics decompositions: model={}, decompositions={}",
|
||||
translationContext.getModelName(), sloMetricDecompositionsStr);
|
||||
|
||||
// PREDICTION_TOP_LEVEL_NODES_METRICS
|
||||
HashMap<String, Object> metricsOfTopLevelNodesMap = getMetricsForPredictionPayload(translationContext, topicBeacon);
|
||||
translationContext.getAdditionalResults().put(PREDICTION_TOP_LEVEL_NODES_METRICS_MAP, metricsOfTopLevelNodesMap);
|
||||
log.debug("PredictionsPostTranslationPlugin.processTranslationResults(): Metrics of Top-Level nodes of model: model={}, metrics-map={}",
|
||||
translationContext.getModelName(), metricsOfTopLevelNodesMap);
|
||||
|
||||
String metricsOfTopLevelNodesStr = topicBeacon.toJson(metricsOfTopLevelNodesMap);
|
||||
translationContext.getAdditionalResults().put(PREDICTION_TOP_LEVEL_NODES_METRICS, metricsOfTopLevelNodesStr);
|
||||
log.debug("PredictionsPostTranslationPlugin.processTranslationResults(): Metrics of Top-Level nodes of model: model={}, metrics={}",
|
||||
translationContext.getModelName(), metricsOfTopLevelNodes);
|
||||
translationContext.getModelName(), metricsOfTopLevelNodesMap);
|
||||
|
||||
log.debug("PredictionsPostTranslationPlugin.processTranslationResults(): END");
|
||||
}
|
||||
@ -75,7 +88,7 @@ public class PredictionsPostTranslationPlugin implements PostTranslationPlugin {
|
||||
.collect(Collectors.toSet());
|
||||
}*/
|
||||
|
||||
public String getSLOMetricDecompositionPayload(TranslationContext translationContext, TopicBeacon topicBeacon) {
|
||||
public Map<String, Object> getSLOMetricDecompositionPayload(TranslationContext translationContext, TopicBeacon topicBeacon) {
|
||||
List<Object> slos = _getSLOMetricDecomposition(translationContext);
|
||||
if (slos.isEmpty()) {
|
||||
return null;
|
||||
@ -87,7 +100,7 @@ public class PredictionsPostTranslationPlugin implements PostTranslationPlugin {
|
||||
result.put("constraints", slos);
|
||||
result.put("version", topicBeacon.getModelVersion());
|
||||
|
||||
return topicBeacon.toJson(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private @NonNull List<Object> _getSLOMetricDecomposition(TranslationContext translationContext) {
|
||||
@ -133,7 +146,7 @@ public class PredictionsPostTranslationPlugin implements PostTranslationPlugin {
|
||||
MetricConstraint mc = mcMap.get(elementName);
|
||||
return Map.of(
|
||||
"name", NebulousEmsTranslator.nameNormalization.apply(mc.getName()),
|
||||
"comparisonOperator", mc.getComparisonOperator(),
|
||||
"operator", mc.getComparisonOperator().getOperator(),
|
||||
"threshold", mc.getThreshold());
|
||||
} else
|
||||
if (constraintNode instanceof LogicalConstraint) {
|
||||
@ -175,7 +188,7 @@ public class PredictionsPostTranslationPlugin implements PostTranslationPlugin {
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
private String getMetricsForPredictionPayload(@NonNull TranslationContext _TC, TopicBeacon topicBeacon) {
|
||||
private HashMap<String, Object> getMetricsForPredictionPayload(@NonNull TranslationContext _TC, TopicBeacon topicBeacon) {
|
||||
HashSet<MetricContext> metricsOfTopLevelNodes = getMetricsForPrediction(_TC);
|
||||
if (metricsOfTopLevelNodes.isEmpty()) {
|
||||
return null;
|
||||
@ -240,8 +253,7 @@ public class PredictionsPostTranslationPlugin implements PostTranslationPlugin {
|
||||
.filter(Objects::nonNull)
|
||||
.toList() );
|
||||
|
||||
// Serialize payload
|
||||
return topicBeacon.toJson(payload);
|
||||
return payload;
|
||||
}
|
||||
|
||||
private @NonNull HashSet<MetricContext> getMetricsForPrediction(@NonNull TranslationContext _TC) {
|
||||
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package eu.nebulous.ems.service;
|
||||
|
||||
import eu.nebulouscloud.exn.Connector;
|
||||
import eu.nebulouscloud.exn.core.Consumer;
|
||||
import eu.nebulouscloud.exn.core.Context;
|
||||
import eu.nebulouscloud.exn.core.Publisher;
|
||||
import eu.nebulouscloud.exn.handlers.ConnectorHandler;
|
||||
import eu.nebulouscloud.exn.settings.StaticExnConfig;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractExternalBrokerService {
|
||||
protected final ExternalBrokerServiceProperties properties;
|
||||
protected final TaskScheduler taskScheduler;
|
||||
|
||||
protected Connector connector;
|
||||
|
||||
protected AbstractExternalBrokerService(ExternalBrokerServiceProperties properties, TaskScheduler taskScheduler) {
|
||||
this.properties = properties;
|
||||
this.taskScheduler = taskScheduler;
|
||||
}
|
||||
|
||||
protected boolean checkProperties() {
|
||||
return properties!=null
|
||||
&& StringUtils.isNotBlank(properties.getBrokerAddress())
|
||||
&& (properties.getBrokerPort() > 0 && properties.getBrokerPort() <= 65535);
|
||||
}
|
||||
|
||||
protected void connectToBroker(List<Publisher> publishers, List<Consumer> consumers) {
|
||||
try {
|
||||
log.debug("AbstractExternalBrokerService: Trying to connect to broker: {}@{}:{}",
|
||||
properties.getBrokerUsername(), properties.getBrokerAddress(), properties.getBrokerPort());
|
||||
Connector connector = createConnector(publishers, consumers);
|
||||
connector.start();
|
||||
log.info("AbstractExternalBrokerService: Connected to broker");
|
||||
|
||||
Connector old_connector = this.connector;
|
||||
this.connector = connector;
|
||||
/*XXX:TODO: Report bug!!!
|
||||
if (old_connector!=null) {
|
||||
log.info("AbstractExternalBrokerService: Stopping old connector");
|
||||
old_connector.stop();
|
||||
}*/
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("AbstractExternalBrokerService: Could not connect to broker: ", e);
|
||||
this.connector = null;
|
||||
if (properties.getRetryDelay()>0) {
|
||||
log.error("AbstractExternalBrokerService: Next attempt to connect to broker in {}s", properties.getRetryDelay());
|
||||
taskScheduler.schedule(() -> connectToBroker(publishers, consumers), Instant.now().plusSeconds(properties.getRetryDelay()));
|
||||
} else {
|
||||
log.error("AbstractExternalBrokerService: Will not retry to connect to broker (delay={})", properties.getRetryDelay());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Connector createConnector(List<Publisher> publishers, List<Consumer> consumers) {
|
||||
return new Connector(
|
||||
ExternalBrokerServiceProperties.COMPONENT_NAME,
|
||||
new ConnectorHandler() {
|
||||
@Override
|
||||
public void onReady(Context context) {
|
||||
log.debug("AbstractExternalBrokerService: onReady: connected to: {}@{}:{}",
|
||||
properties.getBrokerUsername(), properties.getBrokerAddress(), properties.getBrokerPort());
|
||||
//((StatePublisher)context.getPublisher("state")).ready();
|
||||
// ALSO SET 'enableState=true' AT LINE 83: 'false, false,' --> 'true, false,'
|
||||
/*
|
||||
if(context.hasPublisher("state")){
|
||||
StatePublisher sp = (StatePublisher) context.getPublisher("state");
|
||||
sp.starting();
|
||||
sp.started();
|
||||
sp.custom("forecasting");
|
||||
sp.stopping();
|
||||
sp.stopped();
|
||||
}*/
|
||||
}
|
||||
},
|
||||
publishers, consumers,
|
||||
false, false,
|
||||
new StaticExnConfig(
|
||||
properties.getBrokerAddress(), properties.getBrokerPort(),
|
||||
properties.getBrokerUsername(), properties.getBrokerPassword(),
|
||||
properties.getHealthTimeout())
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package eu.nebulous.ems.service;
|
||||
|
||||
import eu.nebulouscloud.exn.core.Consumer;
|
||||
import eu.nebulouscloud.exn.core.Context;
|
||||
import eu.nebulouscloud.exn.core.Handler;
|
||||
import eu.nebulouscloud.exn.core.Publisher;
|
||||
import gr.iccs.imu.ems.util.NetUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.qpid.protonj2.client.Message;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class EmsBootInitializer extends AbstractExternalBrokerService implements ApplicationListener<ApplicationReadyEvent> {
|
||||
private final ExternalBrokerListenerService listener;
|
||||
private Consumer consumer;
|
||||
private Publisher publisher;
|
||||
|
||||
public EmsBootInitializer(ExternalBrokerServiceProperties properties,
|
||||
ExternalBrokerListenerService listener,
|
||||
TaskScheduler scheduler)
|
||||
{
|
||||
super(properties, scheduler);
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationReadyEvent event) {
|
||||
if (! properties.isEnabled()) {
|
||||
log.warn("===================> EMS is ready -- EMS Boot disabled due to configuration");
|
||||
return;
|
||||
}
|
||||
log.info("===================> EMS is ready -- Scheduling EMS Boot message");
|
||||
|
||||
// Start connector used for EMS Booting
|
||||
startConnector();
|
||||
|
||||
// Schedule sending EMS Boot message
|
||||
taskScheduler.schedule(this::sendEmsBootReadyEvent, Instant.now().plusSeconds(1));
|
||||
}
|
||||
|
||||
private void startConnector() {
|
||||
Handler messageHandler = new Handler() {
|
||||
@Override
|
||||
public void onMessage(String key, String address, Map body, Message message, Context context) {
|
||||
log.debug("EmsBootInitializer: Received a new EMS Boot Response message: key={}, address={}, body={}, message={}",
|
||||
key, address, body, message);
|
||||
processEmsBootResponseMessage(body);
|
||||
}
|
||||
};
|
||||
consumer = new Consumer(properties.getEmsBootResponseTopic(), properties.getEmsBootResponseTopic(), messageHandler, null, true, true);
|
||||
publisher = new Publisher(properties.getEmsBootTopic(), properties.getEmsBootTopic(), true, true);
|
||||
connectToBroker(List.of(publisher), List.of(consumer));
|
||||
}
|
||||
|
||||
protected void sendEmsBootReadyEvent() {
|
||||
//XXX:TODO: Work in PROGRESS...
|
||||
Map<String, String> message = Map.of(
|
||||
"internal-address", NetUtil.getDefaultIpAddress(),
|
||||
"public-address", NetUtil.getPublicIpAddress(),
|
||||
"address", NetUtil.getIpAddress()
|
||||
);
|
||||
log.debug("ExternalBrokerPublisherService: Sending message to EMS Boot: {}", message);
|
||||
publisher.send(message, null,true);
|
||||
log.debug("ExternalBrokerPublisherService: Sent message to EMS Boot");
|
||||
}
|
||||
|
||||
protected void processEmsBootResponseMessage(Map body) {
|
||||
try {
|
||||
// Process EMS Boot Response message
|
||||
String appId = body.get("application").toString();
|
||||
String modelStr = body.get("yaml").toString();
|
||||
log.info("EmsBootInitializer: Received a new EMS Boot Response: App-Id: {}, Model:\n{}", appId, modelStr);
|
||||
|
||||
try {
|
||||
listener.processMetricModel(appId, modelStr, null);
|
||||
} catch (Exception e) {
|
||||
log.warn("EmsBootInitializer: EXCEPTION while processing Metric Model for: app-id={} -- Exception: ", appId, e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("EmsBootInitializer: EXCEPTION while processing EMS Boot Response message: ", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package eu.nebulous.ems.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import eu.nebulous.ems.translate.NebulousEmsTranslatorProperties;
|
||||
import eu.nebulouscloud.exn.core.Consumer;
|
||||
import eu.nebulouscloud.exn.core.Context;
|
||||
import eu.nebulouscloud.exn.core.Handler;
|
||||
import eu.nebulouscloud.exn.core.Publisher;
|
||||
import gr.iccs.imu.ems.control.controller.ControlServiceCoordinator;
|
||||
import gr.iccs.imu.ems.control.controller.ControlServiceRequestInfo;
|
||||
import gr.iccs.imu.ems.control.controller.NodeRegistrationCoordinator;
|
||||
import gr.iccs.imu.ems.control.plugin.PostTranslationPlugin;
|
||||
import gr.iccs.imu.ems.control.util.TopicBeacon;
|
||||
import gr.iccs.imu.ems.translate.TranslationContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.qpid.protonj2.client.Message;
|
||||
import org.apache.qpid.protonj2.client.exceptions.ClientException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ExternalBrokerListenerService extends AbstractExternalBrokerService
|
||||
implements PostTranslationPlugin, InitializingBean
|
||||
{
|
||||
private final NebulousEmsTranslatorProperties translatorProperties;
|
||||
private final ApplicationContext applicationContext;
|
||||
private final ArrayBlockingQueue<Command> commandQueue = new ArrayBlockingQueue<>(100);
|
||||
private final ObjectMapper objectMapper;
|
||||
private List<Consumer> consumers;
|
||||
private Publisher commandsResponsePublisher;
|
||||
private Publisher modelsResponsePublisher;
|
||||
private String applicationId = System.getenv("APPLICATION_ID");
|
||||
|
||||
record Command(String key, String address, Map body, Message message, Context context) {
|
||||
}
|
||||
|
||||
public ExternalBrokerListenerService(ApplicationContext applicationContext,
|
||||
ExternalBrokerServiceProperties properties,
|
||||
TaskScheduler taskScheduler,
|
||||
ObjectMapper objectMapper,
|
||||
NebulousEmsTranslatorProperties translatorProperties)
|
||||
{
|
||||
super(properties, taskScheduler);
|
||||
this.applicationContext = applicationContext;
|
||||
this.objectMapper = objectMapper;
|
||||
this.translatorProperties = translatorProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (!properties.isEnabled()) {
|
||||
log.info("ExternalBrokerListenerService: Disabled due to configuration");
|
||||
return;
|
||||
}
|
||||
log.info("ExternalBrokerListenerService: Application Id (from Env.): {}", applicationId);
|
||||
if (checkProperties()) {
|
||||
initializeConsumers();
|
||||
initializePublishers();
|
||||
startCommandProcessor();
|
||||
connectToBroker(List.of(commandsResponsePublisher, modelsResponsePublisher), consumers);
|
||||
log.info("ExternalBrokerListenerService: Initialized listeners and publishers");
|
||||
} else {
|
||||
log.warn("ExternalBrokerListenerService: Not configured or misconfigured. Will not initialize");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processTranslationResults(TranslationContext translationContext, TopicBeacon topicBeacon) {
|
||||
if (!properties.isEnabled()) {
|
||||
log.info("ExternalBrokerListenerService: Disabled due to configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
this.applicationId = translationContext.getAppId();
|
||||
log.info("ExternalBrokerListenerService: Set applicationId to: {}", applicationId);
|
||||
|
||||
// Call control-service to deploy EMS clients
|
||||
if (properties.isDeployEmsClientsOnKubernetesEnabled()) {
|
||||
try {
|
||||
log.info("ExternalBrokerListenerService: Start deploying EMS clients...");
|
||||
String id = "dummy-" + System.currentTimeMillis();
|
||||
Map<String, Object> nodeInfo = new HashMap<>(Map.of(
|
||||
"id", id,
|
||||
"name", id,
|
||||
"type", "K8S",
|
||||
"provider", "Kubernetes",
|
||||
"zone-id", ""
|
||||
));
|
||||
applicationContext.getBean(NodeRegistrationCoordinator.class)
|
||||
.registerNode("", nodeInfo, translationContext);
|
||||
log.debug("ExternalBrokerListenerService: EMS clients deployment started");
|
||||
} catch (Exception e) {
|
||||
log.warn("ExternalBrokerListenerService: EXCEPTION while starting EMS client deployment: ", e);
|
||||
}
|
||||
} else
|
||||
log.info("ExternalBrokerListenerService: EMS clients deployment is disabled");
|
||||
}
|
||||
|
||||
private void initializeConsumers() {
|
||||
/*if (StringUtils.isBlank(applicationId)) {
|
||||
log.warn("ExternalBrokerListenerService: Call to initializeConsumers with blank applicationId. Will not change anything.");
|
||||
return;
|
||||
}*/
|
||||
|
||||
// Create message handler
|
||||
Handler messageHandler = new Handler() {
|
||||
@Override
|
||||
public void onMessage(String key, String address, Map body, Message message, Context context) {
|
||||
try {
|
||||
log.info("ExternalBrokerListenerService: messageHandler: Got new message: key={}, address={}, body={}, message={}",
|
||||
key, address, body, message);
|
||||
super.onMessage(key, address, body, message, context);
|
||||
commandQueue.add(new Command(key, address, body, message, context));
|
||||
} catch (IllegalStateException e) {
|
||||
log.warn("ExternalBrokerListenerService: Commands queue is full. Dropping command: queue-size={}", commandQueue.size());
|
||||
} catch (Exception e) {
|
||||
log.warn("ExternalBrokerListenerService: Error while processing message: ", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create consumers for each topic of interest
|
||||
consumers = List.of(
|
||||
new Consumer(properties.getCommandsTopic(), properties.getCommandsTopic(), messageHandler, null, true, true),
|
||||
new Consumer(properties.getModelsTopic(), properties.getModelsTopic(), messageHandler, null, true, true)
|
||||
);
|
||||
log.info("ExternalBrokerListenerService: created subscribers");
|
||||
}
|
||||
|
||||
private void initializePublishers() {
|
||||
commandsResponsePublisher = new Publisher(properties.getCommandsResponseTopic(), properties.getCommandsResponseTopic(), true, true);
|
||||
modelsResponsePublisher = new Publisher(properties.getModelsResponseTopic(), properties.getModelsResponseTopic(), true, true);
|
||||
log.info("ExternalBrokerListenerService: created publishers");
|
||||
}
|
||||
|
||||
private void startCommandProcessor() {
|
||||
taskScheduler.schedule(()->{
|
||||
while (true) {
|
||||
try {
|
||||
Command command = commandQueue.take();
|
||||
processMessage(command);
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("ExternalBrokerListenerService: Command processor interrupted. Exiting process loop");
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
log.warn("ExternalBrokerListenerService: Exception while processing command: {}\n", commandQueue, e);
|
||||
}
|
||||
}
|
||||
}, Instant.now());
|
||||
}
|
||||
|
||||
private void processMessage(Command command) throws ClientException, IOException {
|
||||
log.debug("ExternalBrokerListenerService: Command: {}", command);
|
||||
log.debug("ExternalBrokerListenerService: Command: message: {}", command.message);
|
||||
log.debug("ExternalBrokerListenerService: Command: body: {}", command.message.body());
|
||||
command.message.forEachProperty((s, o) ->
|
||||
log.debug("ExternalBrokerListenerService: Command: --- property: {} = {}", s, o));
|
||||
if (properties.getCommandsTopic().equals(command.address)) {
|
||||
// Process command
|
||||
log.info("ExternalBrokerListenerService: Received a command from external broker: {}", command.body);
|
||||
processCommandMessage(command);
|
||||
} else
|
||||
if (properties.getModelsTopic().equals(command.address)) {
|
||||
// Process metric model message
|
||||
log.info("ExternalBrokerListenerService: Received a new Metric Model message from external broker: {}", command.body);
|
||||
processMetricModelMessage(command);
|
||||
}
|
||||
}
|
||||
|
||||
private void processCommandMessage(Command command) throws ClientException {
|
||||
// Get application id
|
||||
String appId = getAppId(command, commandsResponsePublisher);
|
||||
if (appId == null) return;
|
||||
|
||||
// Get command string
|
||||
String commandStr = command.body.getOrDefault("command", "").toString();
|
||||
log.debug("ExternalBrokerListenerService: Command: {}", commandStr);
|
||||
|
||||
sendResponse(commandsResponsePublisher, appId, "ERROR: ---NOT YET IMPLEMENTED---: "+ command.body);
|
||||
}
|
||||
|
||||
private void processMetricModelMessage(Command command) throws ClientException, IOException {
|
||||
// Get application id
|
||||
String appId = getAppId(command, modelsResponsePublisher);
|
||||
if (appId == null) return;
|
||||
|
||||
// Get model string and/or model file
|
||||
Object modelObj = command.body.getOrDefault("model", null);
|
||||
if (modelObj==null) {
|
||||
modelObj = command.body.getOrDefault("yaml", null);
|
||||
}
|
||||
if (modelObj==null) {
|
||||
modelObj = command.body.getOrDefault("body", null);
|
||||
}
|
||||
String modelFile = command.body.getOrDefault("model-path", "").toString();
|
||||
|
||||
// Check if 'model' or 'model-path' is provided
|
||||
if (modelObj==null && StringUtils.isBlank(modelFile)) {
|
||||
log.warn("ExternalBrokerListenerService: No model found in Metric Model message: {}", command.body);
|
||||
sendResponse(modelsResponsePublisher, appId, "ERROR: No model found in Metric Model message: "+ command.body);
|
||||
return;
|
||||
}
|
||||
|
||||
String modelStr = null;
|
||||
if (modelObj!=null) {
|
||||
log.debug("ExternalBrokerListenerService: modelObj: class={}, object={}", modelObj.getClass(), modelObj);
|
||||
modelStr = (modelObj instanceof String) ? (String) modelObj : modelObj.toString();
|
||||
if (modelObj instanceof String) {
|
||||
modelStr = (String) modelObj;
|
||||
}
|
||||
if (modelObj instanceof Map) {
|
||||
modelStr = objectMapper.writeValueAsString(modelObj);
|
||||
}
|
||||
}
|
||||
|
||||
processMetricModel(appId, modelStr, modelFile);
|
||||
}
|
||||
|
||||
void processMetricModel(String appId, String modelStr, String modelFile) throws IOException {
|
||||
// If 'model' string is provided, store it in a file
|
||||
if (StringUtils.isNotBlank(modelStr)) {
|
||||
modelFile = StringUtils.isBlank(modelFile) ? getModelFile(appId) : modelFile;
|
||||
storeModel(modelFile, modelStr);
|
||||
} else if (StringUtils.isNotBlank(modelStr)) {
|
||||
log.warn("ExternalBrokerListenerService: Parameter 'modelStr' is blank. Trying modelFile: {}", modelFile);
|
||||
} else {
|
||||
log.warn("ExternalBrokerListenerService: Parameters 'modelStr' and 'modelFile' are both blank");
|
||||
throw new IllegalArgumentException("Parameters 'modelStr' and 'modelFile' are both blank");
|
||||
}
|
||||
|
||||
// Call control-service to process model, also pass a callback to get the result
|
||||
applicationContext.getBean(ControlServiceCoordinator.class).processAppModel(modelFile, null,
|
||||
ControlServiceRequestInfo.create(appId, null, null, null,
|
||||
(result) -> {
|
||||
// Send message with the processing result
|
||||
log.info("ExternalBrokerListenerService: Metric model processing result: {}", result);
|
||||
sendResponse(modelsResponsePublisher, appId, result);
|
||||
}));
|
||||
}
|
||||
|
||||
private String getAppId(Command command, Publisher publisher) throws ClientException {
|
||||
// Check if 'applicationId' is provided in message properties
|
||||
Object propApp = command.message.property(properties.getApplicationIdPropertyName());
|
||||
String appId = propApp != null ? propApp.toString() : null;
|
||||
if (StringUtils.isNotBlank(appId)) {
|
||||
log.debug("ExternalBrokerListenerService: Application Id found in message properties: {}", appId);
|
||||
return appId;
|
||||
}
|
||||
log.debug("ExternalBrokerListenerService: No Application Id found in message properties: {}", command.body);
|
||||
|
||||
// Check if 'applicationId' is provided in message body
|
||||
appId = command.body.getOrDefault("application", "").toString();
|
||||
if (StringUtils.isNotBlank(appId)) {
|
||||
log.debug("ExternalBrokerListenerService: Application Id found in message body: {}", appId);
|
||||
return appId;
|
||||
}
|
||||
log.debug("ExternalBrokerListenerService: No Application Id found in message body: {}", command.body);
|
||||
|
||||
// Not found 'applicationId'
|
||||
log.warn("ExternalBrokerListenerService: No Application Id found in message: {}", command.body);
|
||||
sendResponse(modelsResponsePublisher, null, "ERROR: No Application Id found in message: "+ command.body);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getModelFile(String appId) {
|
||||
return String.format("model-%s--%d.yml", appId, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private void storeModel(String fileName, String modelStr) throws IOException {
|
||||
Path path = Paths.get(translatorProperties.getModelsDir(), fileName);
|
||||
Files.writeString(path, modelStr);
|
||||
log.info("ExternalBrokerListenerService: Stored metric model in file: {}", path);
|
||||
}
|
||||
|
||||
private void sendResponse(Publisher publisher, String appId, Object response) {
|
||||
publisher.send(Map.of(
|
||||
"response", response
|
||||
), appId);
|
||||
}
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package eu.nebulous.ems.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import eu.nebulous.ems.translate.NebulousEmsTranslator;
|
||||
import eu.nebulouscloud.exn.core.Publisher;
|
||||
import gr.iccs.imu.ems.brokercep.BrokerCepService;
|
||||
import gr.iccs.imu.ems.brokercep.event.EventMap;
|
||||
import gr.iccs.imu.ems.control.plugin.PostTranslationPlugin;
|
||||
import gr.iccs.imu.ems.control.util.TopicBeacon;
|
||||
import gr.iccs.imu.ems.translate.Grouping;
|
||||
import gr.iccs.imu.ems.translate.TranslationContext;
|
||||
import jakarta.jms.JMSException;
|
||||
import jakarta.jms.Message;
|
||||
import jakarta.jms.MessageListener;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.activemq.command.ActiveMQMapMessage;
|
||||
import org.apache.activemq.command.ActiveMQMessage;
|
||||
import org.apache.activemq.command.ActiveMQObjectMessage;
|
||||
import org.apache.activemq.command.ActiveMQTextMessage;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ExternalBrokerPublisherService extends AbstractExternalBrokerService
|
||||
implements PostTranslationPlugin, MessageListener
|
||||
{
|
||||
private static final String COMBINED_SLO_PUBLISHER_KEY = "COMBINED_SLO_PUBLISHER_KEY_" + System.currentTimeMillis();
|
||||
private final BrokerCepService brokerCepService;
|
||||
private final Map<String, String> additionalTopicsMap = new HashMap<>();
|
||||
private final Gson gson = new Gson();
|
||||
private Map<String, Publisher> publishersMap;
|
||||
private String applicationId;
|
||||
private Set<String> sloSet;
|
||||
|
||||
protected ExternalBrokerPublisherService(ExternalBrokerServiceProperties properties,
|
||||
TaskScheduler taskScheduler, BrokerCepService brokerCepService)
|
||||
{
|
||||
super(properties, taskScheduler);
|
||||
this.brokerCepService = brokerCepService;
|
||||
}
|
||||
|
||||
public void addAdditionalTopic(@NonNull String topic, @NonNull String externalBrokerTopic) {
|
||||
if (StringUtils.isNotBlank(topic) && StringUtils.isNotBlank(externalBrokerTopic))
|
||||
additionalTopicsMap.put(topic.trim(), externalBrokerTopic.trim());
|
||||
else
|
||||
log.warn("ExternalBrokerPublisherService: Ignoring call to 'addAdditionalTopic' with blank argument(s): topic={}, externalBrokerTopic={}",
|
||||
topic, externalBrokerTopic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processTranslationResults(TranslationContext translationContext, TopicBeacon topicBeacon) {
|
||||
if (!properties.isEnabled()) {
|
||||
log.info("ExternalBrokerPublisherService: Disabled due to configuration");
|
||||
return;
|
||||
}
|
||||
log.debug("ExternalBrokerPublisherService: Initializing...");
|
||||
|
||||
// Get application id
|
||||
applicationId = translationContext.getAppId();
|
||||
|
||||
// Get Top-Level topics (i.e. those at GLOBAL grouping)
|
||||
Set<String> topLevelTopics = translationContext.getG2T().entrySet().stream()
|
||||
.filter(e -> e.getKey().equalsIgnoreCase(Grouping.GLOBAL.name()))
|
||||
.findAny().orElseThrow()
|
||||
.getValue();
|
||||
log.debug("ExternalBrokerPublisherService: Top-Level topics: {}", topLevelTopics);
|
||||
|
||||
if (topLevelTopics.isEmpty()) {
|
||||
log.warn("ExternalBrokerPublisherService: No top-level topics found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find top-level topics that correspond to SLOs (or other requirements)
|
||||
log.trace("ExternalBrokerPublisherService: SLOs-BEFORE: {}", translationContext.getSLO());
|
||||
sloSet = translationContext.getSLO().stream()
|
||||
.map(NebulousEmsTranslator.nameNormalization)
|
||||
.collect(Collectors.toSet());
|
||||
log.trace("ExternalBrokerPublisherService: SLOs-AFTER: {}", sloSet);
|
||||
|
||||
// Prepare publishers
|
||||
publishersMap = topLevelTopics.stream().collect(Collectors.toMap(
|
||||
t -> t, t -> {
|
||||
/*if (sloSet.contains(t)) {
|
||||
// Send SLO violations to combined SLO topic
|
||||
return new Publisher(t, properties.getCombinedSloTopic(), true, true);
|
||||
} else {
|
||||
// Send non-SLO events to their corresponding Nebulous topics
|
||||
return new Publisher(t, properties.getMetricsTopicPrefix() + t, true, true);
|
||||
}*/
|
||||
return new Publisher(t, properties.getMetricsTopicPrefix() + t, true, true);
|
||||
},
|
||||
(publisher, publisher2) -> publisher, HashMap::new
|
||||
));
|
||||
|
||||
// Also add publisher for Event Type VI messages
|
||||
publishersMap.put(COMBINED_SLO_PUBLISHER_KEY,
|
||||
new Publisher(COMBINED_SLO_PUBLISHER_KEY, properties.getCombinedSloTopic(), true, true));
|
||||
|
||||
// Also prepare additional publishers
|
||||
log.debug("ExternalBrokerPublisherService: additionalTopicsMap: {}", additionalTopicsMap);
|
||||
if (! additionalTopicsMap.isEmpty()) {
|
||||
additionalTopicsMap.forEach((topic, externalBrokerTopic) -> {
|
||||
log.trace("ExternalBrokerPublisherService: additionalTopicsMap: Additional publisher for: {} --> {}", topic, externalBrokerTopic);
|
||||
publishersMap.put(topic, new Publisher(topic, externalBrokerTopic, true, true));
|
||||
});
|
||||
}
|
||||
|
||||
// Create connector to external broker
|
||||
connectToBroker(publishersMap.values().stream().toList(), List.of());
|
||||
|
||||
// Register for EMS broker events
|
||||
brokerCepService.getBrokerCepBridge().getListeners().add(this);
|
||||
|
||||
log.info("ExternalBrokerPublisherService: initialized publishers");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(Message message) {
|
||||
if (message instanceof ActiveMQMessage amqMessage) {
|
||||
String topic = amqMessage.getDestination().getPhysicalName();
|
||||
Publisher publisher = publishersMap.get(topic);
|
||||
if (publisher!=null) {
|
||||
log.trace("ExternalBrokerPublisherService: Sending message to external broker: topic: {}, message: {}", topic, amqMessage);
|
||||
try {
|
||||
// Send metric event
|
||||
Map body = getMessageAsMap(amqMessage);
|
||||
if (body!=null) {
|
||||
publishMessage(publisher, body);
|
||||
log.trace("ExternalBrokerPublisherService: Sent message to external broker: topic: {}, message: {}", topic, body);
|
||||
|
||||
// If an SLO, also send an Event Type VI event to combined SLO topics
|
||||
if (sloSet.contains(topic)) {
|
||||
publishMessage(publishersMap.get(COMBINED_SLO_PUBLISHER_KEY), Map.of(
|
||||
"severity", 0.5,
|
||||
"predictionTime", Instant.now().toEpochMilli(),
|
||||
"probability", 1.0
|
||||
));
|
||||
log.trace("ExternalBrokerPublisherService: Also sent message to combined SLO topic: topic: {}, message: {}", topic, body);
|
||||
}
|
||||
} else
|
||||
log.warn("ExternalBrokerPublisherService: Could not get body from internal broker message: topic: {}, message: {}", topic, amqMessage);
|
||||
} catch (JMSException e) {
|
||||
log.warn("ExternalBrokerPublisherService: Error while sending message: {}, Exception: ", topic, e);
|
||||
}
|
||||
} else
|
||||
log.warn("ExternalBrokerPublisherService: No publisher found for topic: {}, message: {}", topic, message);
|
||||
} else
|
||||
log.trace("ExternalBrokerPublisherService: Ignoring non-ActiveMQ message from internal broker: {}", message);
|
||||
}
|
||||
|
||||
private Map getMessageAsMap(ActiveMQMessage amqMessage) throws JMSException {
|
||||
return switch (amqMessage) {
|
||||
case ActiveMQTextMessage textMessage -> EventMap.parseEventMap(textMessage.getText());
|
||||
case ActiveMQObjectMessage objectMessage -> EventMap.parseEventMap(objectMessage.getObject().toString());
|
||||
case ActiveMQMapMessage mapMessage -> mapMessage.getContentMap();
|
||||
case null, default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
public boolean publishMessage(String topic, String bodyStr) {
|
||||
if (! properties.isEnabled()) return false;
|
||||
Publisher publisher = publishersMap.get(topic);
|
||||
if (publisher!=null)
|
||||
publishMessage(publisher, gson.fromJson(bodyStr, Map.class));
|
||||
return publisher!=null;
|
||||
}
|
||||
|
||||
public boolean publishMessage(String topic, Map body) {
|
||||
if (! properties.isEnabled()) return false;
|
||||
Publisher publisher = publishersMap.get(topic);
|
||||
if (publisher!=null)
|
||||
publishMessage(publisher, body);
|
||||
return publisher!=null;
|
||||
}
|
||||
|
||||
private void publishMessage(Publisher publisher, Map body) {
|
||||
publisher.send(body, applicationId, Map.of(/*"prop1", "zz", "prop2", "cc"*/));
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 Institute of Communication and Computer Systems (imu.iccs.gr)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
* If a copy of the MPL was not distributed with this file, you can obtain one at
|
||||
* https://www.mozilla.org/en-US/MPL/2.0/
|
||||
*/
|
||||
|
||||
package eu.nebulous.ems.service;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import static gr.iccs.imu.ems.util.EmsConstant.EMS_PROPERTIES_PREFIX;
|
||||
|
||||
@Slf4j
|
||||
@Data
|
||||
@Validated
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = EMS_PROPERTIES_PREFIX + "external")
|
||||
public class ExternalBrokerServiceProperties implements InitializingBean {
|
||||
public final static String NEBULOUS_TOPIC_PREFIX = "eu.nebulouscloud.";
|
||||
public final static String COMPONENT_NAME = "monitoring";
|
||||
public final static String BASE_TOPIC_PREFIX = NEBULOUS_TOPIC_PREFIX + COMPONENT_NAME + ".";
|
||||
|
||||
private boolean enabled = true;
|
||||
private String brokerAddress;
|
||||
private int brokerPort;
|
||||
private String brokerUsername;
|
||||
@ToString.Exclude
|
||||
private String brokerPassword;
|
||||
private int healthTimeout = 60;
|
||||
private int retryDelay = 10;
|
||||
|
||||
private String applicationIdPropertyName = "application";
|
||||
|
||||
private String commandsTopic = BASE_TOPIC_PREFIX + "commands";
|
||||
private String commandsResponseTopic = BASE_TOPIC_PREFIX + "commands.reply";
|
||||
private String modelsTopic = NEBULOUS_TOPIC_PREFIX + "ui.dsl.metric_model";
|
||||
private String modelsResponseTopic = NEBULOUS_TOPIC_PREFIX + "ui.dsl.metric_model.reply";
|
||||
|
||||
private String metricsTopicPrefix = BASE_TOPIC_PREFIX + "realtime.";
|
||||
private String combinedSloTopic = BASE_TOPIC_PREFIX + "slo.severity_value";
|
||||
|
||||
private String emsBootTopic = NEBULOUS_TOPIC_PREFIX + "ems.boot";
|
||||
private String emsBootResponseTopic = NEBULOUS_TOPIC_PREFIX + "ems.boot.reply";
|
||||
|
||||
private boolean deployEmsClientsOnKubernetesEnabled = true;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
log.debug("ExternalBrokerServiceProperties: {}", this);
|
||||
}
|
||||
}
|
@ -53,6 +53,11 @@ public class NebulousEmsTranslator implements Translator, InitializingBean {
|
||||
|
||||
@Override
|
||||
public TranslationContext translate(String metricModelPath) {
|
||||
return translate(metricModelPath, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TranslationContext translate(String metricModelPath, String applicationId) {
|
||||
if (StringUtils.isBlank(metricModelPath)) {
|
||||
log.error("NebulousEmsTranslator: No metric model specified");
|
||||
throw new NebulousEmsTranslationException("No metric model specified");
|
||||
@ -72,7 +77,7 @@ public class NebulousEmsTranslator implements Translator, InitializingBean {
|
||||
|
||||
// -- Translate model ---------------------------------------------
|
||||
log.info("NebulousEmsTranslator: Translating metric model: {}", metricModelPath);
|
||||
TranslationContext _TC = translate(modelObj, metricModelPath);
|
||||
TranslationContext _TC = translate(modelObj, metricModelPath, applicationId);
|
||||
log.info("NebulousEmsTranslator: Translating metric model completed: {}", metricModelPath);
|
||||
|
||||
return _TC;
|
||||
@ -85,7 +90,7 @@ public class NebulousEmsTranslator implements Translator, InitializingBean {
|
||||
// ================================================================================================================
|
||||
// Private methods
|
||||
|
||||
private TranslationContext translate(Object modelObj, String modelFileName) throws Exception {
|
||||
private TranslationContext translate(Object modelObj, String modelFileName, String appId) throws Exception {
|
||||
log.debug("NebulousEmsTranslator.translate(): BEGIN: metric-model={}", modelObj);
|
||||
|
||||
// Get model name
|
||||
@ -93,6 +98,7 @@ public class NebulousEmsTranslator implements Translator, InitializingBean {
|
||||
|
||||
// Initialize data structures
|
||||
TranslationContext _TC = new TranslationContext(modelName, modelFileName);
|
||||
_TC.setAppId(appId);
|
||||
|
||||
// -- Expand shorthand expressions ------------------------------------
|
||||
log.debug("NebulousEmsTranslator.translate(): Expanding shorthand expressions: {}", modelName);
|
||||
|
@ -32,7 +32,7 @@ public class NebulousEmsTranslatorProperties implements InitializingBean {
|
||||
private boolean skipModelValidation;
|
||||
|
||||
// Translator parameters
|
||||
private String modelsDir = "/models";
|
||||
private String modelsDir = "models";
|
||||
|
||||
// Sensor processing parameters
|
||||
private long sensorMinInterval;
|
||||
|
@ -29,6 +29,7 @@ public class AnalysisUtils {
|
||||
List.of("<", "<=", "=<", ">", ">=", "=<", "=", "<>", "!=");
|
||||
final static List<String> LOGICAL_OPERATORS =
|
||||
List.of("and", "or");
|
||||
final static String NOT_OPERATOR = "not";
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Exceptions and Casting method
|
||||
@ -102,6 +103,31 @@ public class AnalysisUtils {
|
||||
return val;
|
||||
}
|
||||
|
||||
static Object getSpecFieldAsObject(Object o, String field) {
|
||||
return getSpecFieldAsObject(o, field, "Block '%s' is not String or Map: ");
|
||||
}
|
||||
|
||||
static Object getSpecFieldAsObject(Object o, String field, String exceptionMessage) {
|
||||
try {
|
||||
Map<String, Object> spec = asMap(o);
|
||||
Object oValue = spec.get(field);
|
||||
if (oValue == null)
|
||||
return null;
|
||||
if (oValue instanceof String || oValue instanceof Map)
|
||||
return oValue;
|
||||
throw createException(exceptionMessage.formatted(field) + spec);
|
||||
} catch (Exception e) {
|
||||
throw createException(exceptionMessage.formatted(field) + o, e);
|
||||
}
|
||||
}
|
||||
|
||||
static Object getMandatorySpecFieldAsObject(Object o, String field, String exceptionMessage) {
|
||||
Object val = getSpecFieldAsObject(o, field, exceptionMessage);
|
||||
if (val==null)
|
||||
throw createException(exceptionMessage.formatted(field) + o);
|
||||
return val;
|
||||
}
|
||||
|
||||
static String getSpecName(Object o) {
|
||||
return getSpecField(o, "name");
|
||||
}
|
||||
@ -219,4 +245,8 @@ public class AnalysisUtils {
|
||||
return LOGICAL_OPERATORS.contains(s);
|
||||
}
|
||||
|
||||
static boolean isNotOperator(String s) {
|
||||
return NOT_OPERATOR.equalsIgnoreCase(s);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user