diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5e76326 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +#ignore eclipse workspace metadata folder +.metadata/** +eclipse_preferences.epf +StartingPoint/** +RemoteSystemsTempFiles/** +wc-templates/** +**/.settings/** +**/.recommenders/** +.project +.classpath +.DS_Store +#maven target folder +**/target/** +dependency-reduced-pom.xml +build.log +.sonarlint/** + +## IntelliJ Stuff +.idea +*.iml + +## Binaries +*.tar.gz + +*.pydevproject diff --git a/opensync_ext_interface/pom.xml b/opensync_ext_interface/pom.xml new file mode 100644 index 0000000..74fb292 --- /dev/null +++ b/opensync_ext_interface/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + ai.connectus + opensync_ext_interface + 0.0.1-SNAPSHOT + opensync_ext_interface + Interface that defines how opensync gateway gets the AP config + + + com.whizcontrol + base-models + 0.0.1-SNAPSHOT + + + ai.connectus + opensync_protobuf + 0.0.1-SNAPSHOT + + + \ No newline at end of file diff --git a/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/OpensyncExternalIntegrationInterface.java b/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/OpensyncExternalIntegrationInterface.java new file mode 100644 index 0000000..ff0ed6e --- /dev/null +++ b/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/OpensyncExternalIntegrationInterface.java @@ -0,0 +1,15 @@ +package ai.connectus.opensync.external.integration; + +import ai.connectus.opensync.external.integration.models.OpensyncAPConfig; +import sts.PlumeStats.Report; +import traffic.NetworkMetadata.FlowReport; +import wc.stats.IpDnsTelemetry.WCStatsReport; + +public interface OpensyncExternalIntegrationInterface { + void apConnected(String apId); + void apDisconnected(String apId); + OpensyncAPConfig getApConfig(String apId); + void processMqttMessage(String topic, Report report); + void processMqttMessage(String topic, FlowReport flowReport); + void processMqttMessage(String topic, WCStatsReport wcStatsReport); +} diff --git a/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/models/OpensyncAPConfig.java b/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/models/OpensyncAPConfig.java new file mode 100644 index 0000000..2991e44 --- /dev/null +++ b/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/models/OpensyncAPConfig.java @@ -0,0 +1,81 @@ +package ai.connectus.opensync.external.integration.models; + +import java.util.ArrayList; +import java.util.List; + +import com.whizcontrol.core.model.equipment.RadioType; +import com.whizcontrol.core.model.json.BaseJsonModel; + +public class OpensyncAPConfig extends BaseJsonModel { + + private static final long serialVersionUID = 3917975477206236668L; + + private OpensyncAPRadioConfig radioConfig; + private List ssidConfigs; + + + public OpensyncAPRadioConfig getRadioConfig() { + return radioConfig; + } + + + public void setRadioConfig(OpensyncAPRadioConfig radioConfig) { + this.radioConfig = radioConfig; + } + + + public List getSsidConfigs() { + return ssidConfigs; + } + + + public void setSsidConfigs(List ssidConfigs) { + this.ssidConfigs = ssidConfigs; + } + + + @Override + public OpensyncAPConfig clone() { + OpensyncAPConfig ret = (OpensyncAPConfig)super.clone(); + if(radioConfig!=null) { + ret.radioConfig = radioConfig.clone(); + } + + if(ssidConfigs!=null) { + ret.ssidConfigs = new ArrayList(); + for(OpensyncAPSsidConfig s: ssidConfigs) { + ret.ssidConfigs.add(s.clone()); + } + } + + return ret; + } + + public static void main(String[] args) { + OpensyncAPConfig cfg = new OpensyncAPConfig(); + cfg.radioConfig = new OpensyncAPRadioConfig(); + cfg.ssidConfigs = new ArrayList(); + + cfg.radioConfig.setRadioChannel24G(1); + cfg.radioConfig.setRadioChannel5LG(44); + cfg.radioConfig.setRadioChannel5HG(108); + + OpensyncAPSsidConfig ssidCfg = new OpensyncAPSsidConfig(); + ssidCfg.setRadioType(RadioType.is2dot4GHz); + ssidCfg.setSsid("Connectus-standalone"); + ssidCfg.setEncryption("WPA-PSK"); + ssidCfg.setKey("12345678"); + ssidCfg.setMode("2"); + cfg.ssidConfigs.add(ssidCfg); + + ssidCfg = new OpensyncAPSsidConfig(); + ssidCfg.setRadioType(RadioType.is5GHz); + ssidCfg.setSsid("Connectus-standalone-5"); + ssidCfg.setEncryption("WPA-PSK"); + ssidCfg.setKey("12345678"); + ssidCfg.setMode("2"); + cfg.ssidConfigs.add(ssidCfg); + + System.out.println(cfg.toPrettyString()); + } +} diff --git a/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/models/OpensyncAPRadioConfig.java b/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/models/OpensyncAPRadioConfig.java new file mode 100644 index 0000000..c7da12a --- /dev/null +++ b/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/models/OpensyncAPRadioConfig.java @@ -0,0 +1,50 @@ +package ai.connectus.opensync.external.integration.models; + +import com.whizcontrol.core.model.json.BaseJsonModel; + +public class OpensyncAPRadioConfig extends BaseJsonModel { + + private static final long serialVersionUID = 5683558403622855381L; + + private String country; + private int radioChannel24G; + private int radioChannel5LG; + private int radioChannel5HG; + + public int getRadioChannel24G() { + return radioChannel24G; + } + + public void setRadioChannel24G(int radioChannel24G) { + this.radioChannel24G = radioChannel24G; + } + + public int getRadioChannel5LG() { + return radioChannel5LG; + } + + public void setRadioChannel5LG(int radioChannel5LG) { + this.radioChannel5LG = radioChannel5LG; + } + + public int getRadioChannel5HG() { + return radioChannel5HG; + } + + public void setRadioChannel5HG(int radioChannel5HG) { + this.radioChannel5HG = radioChannel5HG; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + @Override + public OpensyncAPRadioConfig clone() { + return (OpensyncAPRadioConfig)super.clone(); + } +} diff --git a/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/models/OpensyncAPSsidConfig.java b/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/models/OpensyncAPSsidConfig.java new file mode 100644 index 0000000..f9f7606 --- /dev/null +++ b/opensync_ext_interface/src/main/java/ai/connectus/opensync/external/integration/models/OpensyncAPSsidConfig.java @@ -0,0 +1,70 @@ +package ai.connectus.opensync.external.integration.models; + +import com.whizcontrol.core.model.equipment.RadioType; +import com.whizcontrol.core.model.json.BaseJsonModel; + +public class OpensyncAPSsidConfig extends BaseJsonModel { + + private static final long serialVersionUID = -8540144450360788799L; + + private RadioType radioType; + private String ssid; + private String encryption; + private String key; + private String mode; + private boolean broadcast; + + public RadioType getRadioType() { + return radioType; + } + + public void setRadioType(RadioType radioType) { + this.radioType = radioType; + } + + public String getSsid() { + return ssid; + } + + public void setSsid(String ssid) { + this.ssid = ssid; + } + + public String getEncryption() { + return encryption; + } + + public void setEncryption(String encryption) { + this.encryption = encryption; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getMode() { + return mode; + } + + public void setMode(String mode) { + this.mode = mode; + } + + public boolean isBroadcast() { + return broadcast; + } + + public void setBroadcast(boolean broadcast) { + this.broadcast = broadcast; + } + + @Override + public OpensyncAPSsidConfig clone() { + return (OpensyncAPSsidConfig)super.clone(); + } + +} diff --git a/opensync_ext_static/pom.xml b/opensync_ext_static/pom.xml new file mode 100644 index 0000000..13b03a7 --- /dev/null +++ b/opensync_ext_static/pom.xml @@ -0,0 +1,15 @@ + + 4.0.0 + ai.connectus + opensync_ext_static + 0.0.1-SNAPSHOT + opensync_ext_static + Configuration interface that provides static config from the file + + + ai.connectus + opensync_ext_interface + 0.0.1-SNAPSHOT + + + \ No newline at end of file diff --git a/opensync_ext_static/src/main/java/ai/connectus/opensync/external/integration/OpensyncExternalIntegrationSimple.java b/opensync_ext_static/src/main/java/ai/connectus/opensync/external/integration/OpensyncExternalIntegrationSimple.java new file mode 100644 index 0000000..298326b --- /dev/null +++ b/opensync_ext_static/src/main/java/ai/connectus/opensync/external/integration/OpensyncExternalIntegrationSimple.java @@ -0,0 +1,59 @@ +package ai.connectus.opensync.external.integration; + +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import ai.connectus.opensync.external.integration.OpensyncExternalIntegrationInterface; +import ai.connectus.opensync.external.integration.models.OpensyncAPConfig; +import sts.PlumeStats.Report; +import traffic.NetworkMetadata.FlowReport; +import wc.stats.IpDnsTelemetry.WCStatsReport; + +@Component +public class OpensyncExternalIntegrationSimple implements OpensyncExternalIntegrationInterface { + + private static final Logger LOG = LoggerFactory.getLogger(OpensyncExternalIntegrationSimple.class); + + @Value("${connectus.ovsdb.configFileName:/Users/dtop/Documents/opensync_ext_static/src/main/resources/config_2_ssids.json}") + private String configFileName; + + public void apConnected(String apId) { + LOG.info("AP {} got connected to the gateway", apId); + } + + public void apDisconnected(String apId) { + LOG.info("AP {} got disconnected from the gateway", apId); + } + + public OpensyncAPConfig getApConfig(String apId) { + LOG.info("Retrieving config for AP {} from file {}", apId, configFileName); + OpensyncAPConfig ret = null; + + try { + ret = OpensyncAPConfig.fromFile(configFileName, OpensyncAPConfig.class); + } catch (IOException e) { + LOG.error("Cannot read config from {}", configFileName, e); + } + + LOG.debug("Config content : {}", ret); + + return ret; + } + + public void processMqttMessage(String topic, Report report) { + LOG.info("Received report on topic {} for ap {}", topic, report.getNodeID()); + } + + public void processMqttMessage(String topic, FlowReport flowReport) { + LOG.info("Received flowReport on topic {} for ap {}", topic, flowReport.getObservationPoint().getNodeId()); + } + + public void processMqttMessage(String topic, WCStatsReport wcStatsReport) { + LOG.info("Received wcStatsReport on topic {} for ap {}", topic, wcStatsReport.getObservationPoint().getNodeId()); + } + +} diff --git a/opensync_ext_static/src/main/resources/config_2_ssids.json b/opensync_ext_static/src/main/resources/config_2_ssids.json new file mode 100644 index 0000000..6486caf --- /dev/null +++ b/opensync_ext_static/src/main/resources/config_2_ssids.json @@ -0,0 +1,27 @@ +{ + "_type" : "OpensyncAPConfig", + "radioConfig" : { + "_type" : "OpensyncAPRadioConfig", + "country" : "CA", + "radioChannel24G" : 1, + "radioChannel5LG" : 44, + "radioChannel5HG" : 108 + }, + "ssidConfigs" : [ { + "_type" : "OpensyncAPSsidConfig", + "radioType" : "is2dot4GHz", + "ssid" : "Connectus-local", + "encryption" : "WPA-PSK", + "key" : "12345678", + "mode" : "2", + "broadcast" : true + }, { + "_type" : "OpensyncAPSsidConfig", + "radioType" : "is5GHz", + "ssid" : "Connectus-local-5", + "encryption" : "WPA-PSK", + "key" : "12345678", + "mode" : "2", + "broadcast" : true + } ] +} diff --git a/opensync_gateway/pom.xml b/opensync_gateway/pom.xml new file mode 100644 index 0000000..e22e003 --- /dev/null +++ b/opensync_gateway/pom.xml @@ -0,0 +1,78 @@ + + 4.0.0 + + com.whizcontrol + root-pom + 0.0.1-SNAPSHOT + + ai.connectus + opensync_experiment + opensync_experiment + Redirector and Controller for OpenSync + + + ai.connectus.opensync.experiment.OpenSyncProcess + + + + + base-container + com.whizcontrol + ${whizcontrol.release.version} + + + ai.connectus + opensync_ext_interface + 0.0.1-SNAPSHOT + + + ai.connectus + opensync_ext_static + 0.0.1-SNAPSHOT + + + + com.vmware.ovsdb + ovsdb-client + 1.0.1 + + + slf4j-log4j12 + org.slf4j + + + + + + + ai.connectus + opensync_protobuf + 0.0.1-SNAPSHOT + + + + com.google.protobuf + protobuf-java-util + 3.11.1 + + + + org.fusesource.mqtt-client + mqtt-client + 1.12 + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/experiment/OpenSyncExperimentController.java b/opensync_gateway/src/main/java/ai/connectus/opensync/experiment/OpenSyncExperimentController.java new file mode 100644 index 0000000..823fb5e --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/experiment/OpenSyncExperimentController.java @@ -0,0 +1,31 @@ +package ai.connectus.opensync.experiment; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import ai.connectus.opensync.ovsdb.ConnectusOvsdbClient; + +@RestController +public class OpenSyncExperimentController { + + private static final Logger LOG = LoggerFactory.getLogger(OpenSyncExperimentController.class); + + @Autowired + ConnectusOvsdbClient connectusOvsdbClient; + + @RequestMapping(value = "/connectedClients", method = RequestMethod.GET) + public List getConnectedClients() + { + List ret = new ArrayList(connectusOvsdbClient.getConnectedClientIds()); + LOG.info("Returning connected clients {}", ret); + return ret; + } + +} diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/experiment/OpenSyncProcess.java b/opensync_gateway/src/main/java/ai/connectus/opensync/experiment/OpenSyncProcess.java new file mode 100644 index 0000000..1dfcd98 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/experiment/OpenSyncProcess.java @@ -0,0 +1,22 @@ +package ai.connectus.opensync.experiment; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.ComponentScan; + +@ComponentScan(basePackages={"com.whizcontrol", "ai.connectus"}) +@EnableAutoConfiguration +public class OpenSyncProcess { + + /** + *
{@code java -Dssl.props=file:./ssl.properties -Dlogback.configurationFile=file:./logback.xml -jar ./opensync-experiment-0.0.1-SNAPSHOT.jar} + * + * @param args + */ + public static void main(String[] args) { + ConfigurableApplicationContext applicationContext = SpringApplication.run(OpenSyncProcess.class, args); + // signal start of the application context + applicationContext.start(); + } +} diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/mqtt/OpensyncMqttClient.java b/opensync_gateway/src/main/java/ai/connectus/opensync/mqtt/OpensyncMqttClient.java new file mode 100644 index 0000000..fef8729 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/mqtt/OpensyncMqttClient.java @@ -0,0 +1,208 @@ +package ai.connectus.opensync.mqtt; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.fusesource.mqtt.client.BlockingConnection; +import org.fusesource.mqtt.client.MQTT; +import org.fusesource.mqtt.client.Message; +import org.fusesource.mqtt.client.QoS; +import org.fusesource.mqtt.client.Topic; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.stereotype.Component; + +import com.google.protobuf.Descriptors; +import com.google.protobuf.MessageOrBuilder; +import com.google.protobuf.util.JsonFormat; +import com.google.protobuf.util.JsonFormat.TypeRegistry; + +import ai.connectus.opensync.external.integration.OpensyncExternalIntegrationInterface; +import ai.connectus.opensync.util.ZlibUtil; +import sts.PlumeStats; +import sts.PlumeStats.Report; +import traffic.NetworkMetadata; +import traffic.NetworkMetadata.FlowReport; +import wc.stats.IpDnsTelemetry; +import wc.stats.IpDnsTelemetry.WCStatsReport; + +@Component +public class OpensyncMqttClient implements ApplicationListener { + + private static final Logger LOG = LoggerFactory.getLogger(OpensyncMqttClient.class); + + private static final Logger MQTT_LOG = LoggerFactory.getLogger("MQTT_DATA"); + + public static Charset utf8 = Charset.forName("UTF-8"); + + @Autowired + private OpensyncExternalIntegrationInterface extIntegrationInterface; + + + // + // See https://github.com/fusesource/mqtt-client for the docs + // + + private boolean keepReconnecting = true; + private Thread mqttClientThread; + + public OpensyncMqttClient( + @Autowired io.netty.handler.ssl.SslContext sslContext, + @Value("${connectus.mqttBroker.address:testportal.123wlan.com}") + String mqttBrokerAddress, + @Value("${connectus.mqttBroker.listenPort:1883}") + int mqttBrokerListenPort + ){ + String username = "admin"; + String password = "admin"; + + Runnable mqttClientRunnable = () -> { + while(keepReconnecting) { + BlockingConnection connection = null; + try { + // Create a new MQTT connection to the broker. + /* + * Using SSL connections + * If you want to connect over SSL/TLS instead of + * TCP, use an "ssl://" or "tls://" URI prefix instead of "tcp://" for + * the host field. + * Supported protocol values are: + * + * ssl:// - Use the JVM default version of the SSL algorithm. + * sslv*:// - Use a specific SSL version where * is a version supported by your JVM. Example: sslv3 + * tls:// - Use the JVM default version of the TLS algorithm. + * tlsv*:// - Use a specific TLS version where * is a version supported by your JVM. Example: tlsv1.1 + * The client will use the + * default JVM SSLContext which is configured via JVM system properties + * unless you configure the MQTT instance using the setSslContext method. + * + * SSL connections perform blocking operations against internal thread + * pool unless you call the setBlockingExecutor method to configure that + * executor they will use instead. + * + */ + + MQTT mqtt = new MQTT(); + //mqtt.setHost("tcp://192.168.0.137:61616"); + mqtt.setHost("tls://"+mqttBrokerAddress+":"+mqttBrokerListenPort); + LOG.info("Connecting to Artemis using MQTT at {}", mqtt.getHost()); + mqtt.setClientId("opensync_mqtt"); + mqtt.setUserName(username); + mqtt.setPassword(password); + //Note: the following does not work with the serverContext, it has to be the clientContext + //mqtt.setSslContext(((JdkSslContext) sslContext).context()); + //For now we'll rely on regular SSLContext from the JDK + + //TODO: revisit this blocking connection, change it to futureConnection + connection = mqtt.blockingConnection(); + connection.connect(); + LOG.info("Connected to Artemis cluster at {}", mqtt.getHost()); + + // Subscribe to topics: + // + // new Topic("mqtt/example/publish", QoS.AT_LEAST_ONCE), + // new Topic("#", QoS.AT_LEAST_ONCE), + // new Topic("test/#", QoS.EXACTLY_ONCE), + // new Topic("foo/+/bar", QoS.AT_LEAST_ONCE) + Topic[] topics = { + new Topic("#", QoS.AT_LEAST_ONCE), + }; + + connection.subscribe(topics); + LOG.info("Subscribed to mqtt topics {}", Arrays.asList(topics)); + + //prepare a JSONPrinter to format protobuf messages as json + List protobufDescriptors = new ArrayList<>(); + protobufDescriptors.addAll(PlumeStats.getDescriptor().getMessageTypes()); + protobufDescriptors.addAll(IpDnsTelemetry.getDescriptor().getMessageTypes()); + protobufDescriptors.addAll(NetworkMetadata.getDescriptor().getMessageTypes()); + TypeRegistry oldRegistry = TypeRegistry.newBuilder().add(protobufDescriptors).build(); + JsonFormat.Printer jsonPrinter = JsonFormat.printer().includingDefaultValueFields().omittingInsignificantWhitespace().usingTypeRegistry(oldRegistry ); + + //main loop - receive messages + while(true) { + Message mqttMsg = connection.receive(5, TimeUnit.SECONDS); + + if(mqttMsg == null) { + continue; + } + + byte payload[] = mqttMsg.getPayload(); + //we acknowledge right after receive because: + // a. none of the stats messages are so important that we cannot skip one + // b. if there's some kind of problem with the message (decoding or processing) - we want to move on as quickly as possible and not let it get stuck in the queue + mqttMsg.ack(); + + LOG.trace("received message on topic {} size {}", mqttMsg.getTopic(), payload.length); + + if(payload[0]==0x78) { + //looks like zlib-compressed data, let's decompress it before deserializing + payload = ZlibUtil.decompress(payload); + } + + //attempt to parse the message as protobuf + MessageOrBuilder encodedMsg = null; + + try { + encodedMsg = Report.parseFrom(payload); + MQTT_LOG.debug("topic = {} Report = {}", mqttMsg.getTopic(), jsonPrinter.print(encodedMsg)); + extIntegrationInterface.processMqttMessage(mqttMsg.getTopic(), (Report) encodedMsg); + + }catch(Exception e) { + try { + //not a plume_stats report, attempt to deserialize as network_metadata + encodedMsg = FlowReport.parseFrom(payload); + MQTT_LOG.debug("topic = {} FlowReport = {}", mqttMsg.getTopic(), jsonPrinter.print(encodedMsg)); + extIntegrationInterface.processMqttMessage(mqttMsg.getTopic(), (FlowReport) encodedMsg); + }catch(Exception e1) { + + try { + //not a plume_stats report and not network_metadata report, attempt to deserialize as WCStatsReport + encodedMsg = WCStatsReport.parseFrom(payload); + MQTT_LOG.debug("topic = {} IpDnsTelemetry = {}", mqttMsg.getTopic(), jsonPrinter.print(encodedMsg)); + extIntegrationInterface.processMqttMessage(mqttMsg.getTopic(), (WCStatsReport) encodedMsg); + }catch(Exception e2) { + String msgStr = new String(mqttMsg.getPayload(), utf8); + MQTT_LOG.debug("topic = {} message = {}", mqttMsg.getTopic(), msgStr); + } + } + } + + } + + } catch(Exception e) { + LOG.error("Exception in MQTT receiver", e); + } finally { + try { + if(connection!=null) { + connection.disconnect(); + } + } catch(Exception e1) { + //do nothing + } + } + } + + }; + + Thread mqttClientThread = new Thread(mqttClientRunnable, "mqttClientThread"); + mqttClientThread.setDaemon(true); + mqttClientThread.start(); + + } + + + @Override + public void onApplicationEvent(ContextClosedEvent event) { + LOG.debug("Processing ContextClosedEvent event"); + keepReconnecting = false; + mqttClientThread.interrupt(); + } +} diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/ConnectusOvsdbClient.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/ConnectusOvsdbClient.java new file mode 100644 index 0000000..f075578 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/ConnectusOvsdbClient.java @@ -0,0 +1,162 @@ +package ai.connectus.opensync.ovsdb; + +import java.security.cert.X509Certificate; +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import javax.annotation.PostConstruct; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.vmware.ovsdb.callback.ConnectionCallback; +import com.vmware.ovsdb.service.OvsdbClient; +import com.vmware.ovsdb.service.OvsdbPassiveConnectionListener; + +import ai.connectus.opensync.external.integration.OpensyncExternalIntegrationInterface; +import ai.connectus.opensync.external.integration.models.OpensyncAPConfig; +import ai.connectus.opensync.ovsdb.dao.OvsdbDao; +import ai.connectus.opensync.ovsdb.dao.models.ConnectNodeInfo; +import ai.connectus.opensync.util.SslUtil; +import io.netty.handler.ssl.SslContext; + +@Component +public class ConnectusOvsdbClient { + + private static final Logger LOG = LoggerFactory.getLogger(ConnectusOvsdbClient.class); + + + @org.springframework.beans.factory.annotation.Value("${connectus.ovsdb.listenPort:6640}") + private int ovsdbListenPort; + + @org.springframework.beans.factory.annotation.Value("${connectus.manager.collectionIntervalSec.deviceStats:10}") + private long collectionIntervalSecDeviceStats; + + @Autowired + private SslContext sslContext; + + @Autowired + private OvsdbPassiveConnectionListener listener; + + @Autowired + private OvsdbDao ovsdbDao; + + @Autowired + private OpensyncExternalIntegrationInterface extIntegrationInterface; + + private final ConcurrentHashMap connectedClients = new ConcurrentHashMap<>(); + + @PostConstruct + private void postCreate() { + listenForConnections(); + } + + public void listenForConnections() { + + ConnectionCallback connectionCallback = new ConnectionCallback() { + public void connected(OvsdbClient ovsdbClient) { + String remoteHost = ovsdbClient.getConnectionInfo().getRemoteAddress().getHostAddress(); + int localPort = ovsdbClient.getConnectionInfo().getLocalPort(); + String subjectDn = null; + try { + subjectDn = ((X509Certificate) ovsdbClient.getConnectionInfo().getRemoteCertificate()).getSubjectDN().getName(); + + String clientCn = SslUtil.extractCN(subjectDn); + LOG.info("ovsdbClient connecting from {} on port {} clientCn {}", remoteHost, localPort, clientCn); + + ConnectNodeInfo connectNodeInfo = processConnectRequest(ovsdbClient, clientCn); + + //successfully connected - register it in our connectedClients table + //In Plume's environment clientCn is not unique that's why we are augmenting it with the serialNumber and using it as a key (equivalent of KDC unique qrCode) + String key = clientCn + "_" + connectNodeInfo.serialNumber; + ConnectusOvsdbClient.this.connectedClients.put(key, ovsdbClient); + extIntegrationInterface.apConnected(key); + + LOG.info("ovsdbClient connected from {} on port {} key {} ", remoteHost, localPort, key); + + LOG.info("ovsdbClient connectedClients = {}", ConnectusOvsdbClient.this.connectedClients.size()); + + } catch (Exception e) { + LOG.error("ovsdbClient error", e); + //something is wrong with the SSL + ovsdbClient.shutdown(); + return; + } + + } + public void disconnected(OvsdbClient ovsdbClient) { + String remoteHost = ovsdbClient.getConnectionInfo().getRemoteAddress().getHostAddress(); + int localPort = ovsdbClient.getConnectionInfo().getLocalPort(); + String subjectDn = null; + try { + subjectDn = ((X509Certificate) ovsdbClient.getConnectionInfo().getRemoteCertificate()).getSubjectDN().getName(); + } catch (Exception e) { + //do nothing + } + + String clientCn = SslUtil.extractCN(subjectDn); + + //disconnected - deregister ovsdbClient from our connectedClients table + //unfortunately we only know clientCn at this point, but in Plume's environment they are not unique + //so we are doing a reverse lookup here, and then if we find the key we will remove the entry from the connectedClients. + String key = ConnectusOvsdbClient.this.connectedClients.searchEntries(1, + (Entry t) -> { return t.getValue().equals(ovsdbClient) ? t.getKey() : null ;} + ); + + if(key!=null) { + ConnectusOvsdbClient.this.connectedClients.remove(key); + extIntegrationInterface.apDisconnected(key); + } + + ovsdbClient.shutdown(); + + LOG.info("ovsdbClient disconnected from {} on port {} clientCn {} key {} ", remoteHost, localPort, clientCn, key); + LOG.info("ovsdbClient connectedClients = {}", ConnectusOvsdbClient.this.connectedClients.size()); + } + }; + + listener.startListeningWithSsl(ovsdbListenPort, sslContext, connectionCallback).join(); + + LOG.debug("manager waiting for connection on port {}...", ovsdbListenPort); + } + + private ConnectNodeInfo processConnectRequest(OvsdbClient ovsdbClient, String clientCn) { + + LOG.debug("Starting Client connect"); + ConnectNodeInfo connectNodeInfo = ovsdbDao.getConnectNodeInfo(ovsdbClient); + connectNodeInfo = ovsdbDao.updateConnectNodeInfoOnConnect(ovsdbClient, connectNodeInfo); + + String apId = clientCn + "_" + connectNodeInfo.serialNumber; + OpensyncAPConfig opensyncAPConfig = extIntegrationInterface.getApConfig(apId); + + ovsdbDao.configureStats(ovsdbClient); + + //Check if device stats is configured in Wifi_Stats_Config table, provision it if needed + if(ovsdbDao.getDeviceStatsReportingInterval(ovsdbClient) != collectionIntervalSecDeviceStats) { + ovsdbDao.updateDeviceStatsReportingInterval(ovsdbClient, collectionIntervalSecDeviceStats); + } + + ovsdbDao.removeOnboardingSsids(ovsdbClient); + + if(opensyncAPConfig!=null) { + ovsdbDao.configureWifiRadios(ovsdbClient, opensyncAPConfig.getRadioConfig()); + ovsdbDao.configureSsids(ovsdbClient, opensyncAPConfig.getSsidConfigs()); + } + + ovsdbDao.provisionBridgePortInterface(ovsdbClient); + + ovsdbDao.configureWifiInet(ovsdbClient); + + LOG.debug("Client connect Done"); + return connectNodeInfo; + } + + public Set getConnectedClientIds(){ + return new HashSet<>(connectedClients.keySet()); + } + +} diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/ConnectusOvsdbRedirector.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/ConnectusOvsdbRedirector.java new file mode 100644 index 0000000..694ab74 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/ConnectusOvsdbRedirector.java @@ -0,0 +1,84 @@ +package ai.connectus.opensync.ovsdb; + +import java.security.cert.X509Certificate; + +import javax.annotation.PostConstruct; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.vmware.ovsdb.callback.ConnectionCallback; +import com.vmware.ovsdb.service.OvsdbClient; +import com.vmware.ovsdb.service.OvsdbPassiveConnectionListener; + +import ai.connectus.opensync.ovsdb.dao.OvsdbDao; +import ai.connectus.opensync.util.SslUtil; +import io.netty.handler.ssl.SslContext; + +@Component +public class ConnectusOvsdbRedirector { + + private static final Logger LOG = LoggerFactory.getLogger(ConnectusOvsdbRedirector.class); + + @org.springframework.beans.factory.annotation.Value("${connectus.ovsdb.redirector.listenPort:6643}") + private int ovsdbRedirectorListenPort; + + @Autowired + private SslContext sslContext; + + @Autowired + private OvsdbDao ovsdbDao; + + @Autowired + private OvsdbPassiveConnectionListener listener; + + @PostConstruct + private void postCreate() { + listenForConnections(); + } + + public void listenForConnections() { + + ConnectionCallback connectionCallback = new ConnectionCallback() { + public void connected(OvsdbClient ovsdbClient) { + String remoteHost = ovsdbClient.getConnectionInfo().getRemoteAddress().getHostAddress(); + int localPort = ovsdbClient.getConnectionInfo().getLocalPort(); + String subjectDn = null; + try { + subjectDn = ((X509Certificate) ovsdbClient.getConnectionInfo().getRemoteCertificate()).getSubjectDN().getName(); + + String clientCn = SslUtil.extractCN(subjectDn); + LOG.info("ovsdbClient redirector connected from {} on port {} clientCn {}", remoteHost, localPort, clientCn); + ovsdbDao.performRedirect(ovsdbClient, clientCn); + + } catch (Exception e) { + //something is wrong with the SSL or with the redirect + ovsdbClient.shutdown(); + return; + } + } + + public void disconnected(OvsdbClient ovsdbClient) { + String remoteHost = ovsdbClient.getConnectionInfo().getRemoteAddress().getHostAddress(); + int localPort = ovsdbClient.getConnectionInfo().getLocalPort(); + String subjectDn = null; + try { + subjectDn = ((X509Certificate) ovsdbClient.getConnectionInfo().getRemoteCertificate()).getSubjectDN().getName(); + } catch (Exception e) { + //do nothing + } + String clientCn = SslUtil.extractCN(subjectDn); + LOG.info("ovsdbClient redirector disconnected from {} on port {} clientCn {}", remoteHost, localPort, clientCn); + ovsdbClient.shutdown(); + } + }; + + listener.startListeningWithSsl(ovsdbRedirectorListenPort, sslContext, connectionCallback).join(); + + LOG.debug("redirector waiting for connection on port {} ...", ovsdbRedirectorListenPort); + + } + +} diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/NettySslContextConfig.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/NettySslContextConfig.java new file mode 100644 index 0000000..ea9f4e0 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/NettySslContextConfig.java @@ -0,0 +1,101 @@ +package ai.connectus.opensync.ovsdb; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.UnrecoverableEntryException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.net.ssl.TrustManagerFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; + +@Configuration +public class NettySslContextConfig { + + private static final Logger LOG = LoggerFactory.getLogger(NettySslContextConfig.class); + + @Bean(name="NettySslContextServer") + public SslContext nettySslContextServer( + @Value("${connectus.ovsdb.trustStore:/Users/dtop/Documents/certs_from_device/truststore.jks}") + String trustStoreFileName, + + @Value("${connectus.ovsdb.keyStore:/Users/dtop/Documents/certs_from_device/server.p12}") + String keyFileName, + + @Value("${connectus.ovsdb.keyStorePassword:mypassword}") + String keyStorePassword + ){ + File trustStoreFile = new File(trustStoreFileName); + File keyFile = new File(keyFileName); + + SslContext sslContext = null; + + try { + char[] pwd = keyStorePassword.toCharArray(); + + KeyStore ks = KeyStore.getInstance("PKCS12"); + try(InputStream is = new FileInputStream(keyFile)){ + ks.load(is, pwd); + } + + for(String alias: Collections.list(ks.aliases())) { + LOG.debug("Key Alias: {}", alias); + } + + PrivateKey key = (PrivateKey) ks.getKey("1", pwd); + Certificate[] chain = ks.getCertificateChain("1"); + X509Certificate[] keyCertChain = new X509Certificate[chain.length]; + int i=0; + for(Certificate cert : chain) { + keyCertChain[i] = (X509Certificate) cert; + i++; + } + + String keyPassword = null; + SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(key, keyPassword, keyCertChain ); + + KeyStore trustKs = KeyStore.getInstance("JKS"); + try(InputStream is = new FileInputStream(trustStoreFile)){ + trustKs.load(is, pwd); + } + + List trustChain = new ArrayList<>(); + + for(String alias: Collections.list(trustKs.aliases())) { + LOG.debug("Trust Alias: {}", alias); + X509Certificate cert = (X509Certificate) ks.getCertificate(alias); + trustChain.add(cert); + } + + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + tmf.init(trustKs); + sslContextBuilder.trustManager(tmf); + + sslContext = sslContextBuilder.build(); + LOG.debug("Built ssl context"); + } catch (KeyStoreException|CertificateException|NoSuchAlgorithmException|IOException|UnrecoverableEntryException e) { + throw new RuntimeException(e); + } + + return sslContext; + } +} diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/OvsdbListenerConfig.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/OvsdbListenerConfig.java new file mode 100644 index 0000000..482a94d --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/OvsdbListenerConfig.java @@ -0,0 +1,28 @@ +package ai.connectus.opensync.ovsdb; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.vmware.ovsdb.service.OvsdbPassiveConnectionListener; +import com.vmware.ovsdb.service.impl.OvsdbPassiveConnectionListenerImpl; + +@Configuration +public class OvsdbListenerConfig { + + private static final Logger LOG = LoggerFactory.getLogger(OvsdbListenerConfig.class); + + @Bean + public OvsdbPassiveConnectionListener ovsdbPassiveConnectionListener( + @org.springframework.beans.factory.annotation.Value("${connectus.ovsdb.listener.threadPoolSize:10}") + int threadPoolSize) { + LOG.debug("Configuring OvsdbPassiveConnectionListener with thread pool size {}", threadPoolSize); + ScheduledExecutorService executorService = Executors.newScheduledThreadPool(threadPoolSize); + OvsdbPassiveConnectionListener listener = new OvsdbPassiveConnectionListenerImpl(executorService); + return listener; + } +} diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/OvsdbDao.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/OvsdbDao.java new file mode 100644 index 0000000..4c0f137 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/OvsdbDao.java @@ -0,0 +1,1402 @@ +package ai.connectus.opensync.ovsdb.dao; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import com.vmware.ovsdb.exception.OvsdbClientException; +import com.vmware.ovsdb.protocol.operation.Delete; +import com.vmware.ovsdb.protocol.operation.Insert; +import com.vmware.ovsdb.protocol.operation.Operation; +import com.vmware.ovsdb.protocol.operation.Select; +import com.vmware.ovsdb.protocol.operation.Update; +import com.vmware.ovsdb.protocol.operation.notation.Atom; +import com.vmware.ovsdb.protocol.operation.notation.Condition; +import com.vmware.ovsdb.protocol.operation.notation.Function; +import com.vmware.ovsdb.protocol.operation.notation.Row; +import com.vmware.ovsdb.protocol.operation.notation.Uuid; +import com.vmware.ovsdb.protocol.operation.notation.Value; +import com.vmware.ovsdb.protocol.operation.result.InsertResult; +import com.vmware.ovsdb.protocol.operation.result.OperationResult; +import com.vmware.ovsdb.protocol.operation.result.SelectResult; +import com.vmware.ovsdb.service.OvsdbClient; +import com.whizcontrol.core.model.equipment.RadioType; + +import ai.connectus.opensync.external.integration.models.OpensyncAPRadioConfig; +import ai.connectus.opensync.external.integration.models.OpensyncAPSsidConfig; +import ai.connectus.opensync.ovsdb.dao.models.BridgeInfo; +import ai.connectus.opensync.ovsdb.dao.models.ConnectNodeInfo; +import ai.connectus.opensync.ovsdb.dao.models.InterfaceInfo; +import ai.connectus.opensync.ovsdb.dao.models.PortInfo; +import ai.connectus.opensync.ovsdb.dao.models.WifiInetConfigInfo; +import ai.connectus.opensync.ovsdb.dao.models.WifiRadioConfigInfo; +import ai.connectus.opensync.ovsdb.dao.models.WifiStatsConfigInfo; +import ai.connectus.opensync.ovsdb.dao.models.WifiVifConfigInfo; + +@Component +public class OvsdbDao { + private static final Logger LOG = LoggerFactory.getLogger(OvsdbDao.class); + + @org.springframework.beans.factory.annotation.Value("${connectus.ovsdb.managerAddr:3.88.149.10}") + private String managerIpAddr; + + @org.springframework.beans.factory.annotation.Value("${connectus.ovsdb.listenPort:6640}") + private int ovsdbListenPort; + + @org.springframework.beans.factory.annotation.Value("${connectus.mqttBroker.address:testportal.123wlan.com}") + private String mqttBrokerAddress; + + @org.springframework.beans.factory.annotation.Value("${connectus.mqttBroker.listenPort:1883}") + private int mqttBrokerListenPort; + + @org.springframework.beans.factory.annotation.Value("${connectus.ovsdb.timeoutSec:30}") + private int ovsdbTimeoutSec; + + public static final String ovsdbName = "Open_vSwitch"; + public static final String awlanNodeDbTable = "AWLAN_Node"; + public static final String wifiStatsConfigDbTable = "Wifi_Stats_Config"; + + public static final String interfaceDbTable = "Interface"; + public static final String portDbTable = "Port"; + public static final String bridgeDbTable = "Bridge"; + public static final String wifiRadioConfigDbTable = "Wifi_Radio_Config"; + public static final String wifiVifConfigDbTable = "Wifi_VIF_Config"; + public static final String wifiInetConfigDbTable = "Wifi_Inet_Config"; + + + + // + //Note: When talking to OVSDB always use future.get(X, TimeUnit.SECONDS); - to prevent DOS attacks with misbehaving clients + // + + public ConnectNodeInfo getConnectNodeInfo(OvsdbClient ovsdbClient) { + ConnectNodeInfo ret = new ConnectNodeInfo(); + + try { + List operations = new ArrayList<>(); + List conditions = new ArrayList<>(); + List columns = new ArrayList<>(); + columns.add("mqtt_settings"); + columns.add("redirector_addr"); + columns.add("manager_addr"); + columns.add("sku_number"); + columns.add("serial_number"); + + operations.add(new Select(awlanNodeDbTable, conditions , columns )); + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + if(LOG.isDebugEnabled()) { + LOG.debug("Select from {}:", awlanNodeDbTable); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + } + + Row row = null; + if (result != null && result.length > 0 && !((SelectResult) result[0]).getRows().isEmpty()) { + row = ((SelectResult)result[0]).getRows().iterator().next(); + } + + ret.mqttSettings = (row!=null)?row.getMapColumn("mqtt_settings"):null; + ret.redirectorAddr = (row!=null)?row.getStringColumn("redirector_addr"):null; + ret.managerAddr = (row!=null)?row.getStringColumn("manager_addr"):null; + + ret.skuNumber = getSingleValueFromSet(row, "sku_number"); + ret.serialNumber = getSingleValueFromSet(row, "serial_number"); + + } catch(OvsdbClientException | TimeoutException | ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } + + return ret; + } + + public ConnectNodeInfo updateConnectNodeInfoOnConnect(OvsdbClient ovsdbClient, ConnectNodeInfo incomingConnectNodeInfo) { + ConnectNodeInfo ret = incomingConnectNodeInfo.clone(); + + try { + List operations = new ArrayList<>(); + Map updateColumns = new HashMap<>(); + + //set device_mode = cloud - plume's APs do not use it + //updateColumns.put("device_mode", new Atom("cloud") ); + + //update sku_number if it was empty + if(ret.skuNumber == null || ret.skuNumber.isEmpty()) { + ret.skuNumber = "connectus.ai_"+ ret.serialNumber; + updateColumns.put("sku_number", new Atom(ret.skuNumber) ); + } + + //Configure the MQTT connection + //ovsh u AWLAN_Node mqtt_settings:ins:'["map",[["broker","testportal.123wlan.com"],["topics","/ap/dev-ap-0300/opensync"],["qos","0"],["port","1883"],["remote_log","1"]]]' + Map newMqttSettings = new HashMap<>(); + newMqttSettings.put("broker", mqttBrokerAddress); + newMqttSettings.put("topics", "/ap/"+ret.serialNumber+"/opensync"); + newMqttSettings.put("port", ""+mqttBrokerListenPort); + newMqttSettings.put("compress","zlib"); + newMqttSettings.put("qos", "0"); + newMqttSettings.put("remote_log", "1"); + + if(ret.mqttSettings == null || !ret.mqttSettings.equals(newMqttSettings)) { + @SuppressWarnings("unchecked") + com.vmware.ovsdb.protocol.operation.notation.Map mgttSettings = com.vmware.ovsdb.protocol.operation.notation.Map.of(newMqttSettings); + ret.mqttSettings = newMqttSettings; + updateColumns.put("mqtt_settings", mgttSettings); + } + + if(!updateColumns.isEmpty()) { + Row row = new Row(updateColumns ); + operations.add(new Update(awlanNodeDbTable, row )); + + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + if(LOG.isDebugEnabled()) { + LOG.debug("Updated {}:", awlanNodeDbTable); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + } + } + + } catch(OvsdbClientException | TimeoutException | ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } + + return ret; + } + + + /** + * @param ovsdbClient + * @return value of reporting_interval column for the stats_type=device from the Wifi_Stats_Config table. If value is not provisioned then return -1. + */ + public long getDeviceStatsReportingInterval(OvsdbClient ovsdbClient) { + long ret = -1; + try { + List operations = new ArrayList<>(); + List conditions = new ArrayList<>(); + List columns = new ArrayList<>(); + + columns.add("reporting_interval"); + columns.add("stats_type"); + columns.add("radio_type"); + + conditions.add(new Condition("stats_type", Function.EQUALS, new Atom<>("device") )); + + operations.add(new Select(wifiStatsConfigDbTable, conditions , columns )); + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + if(LOG.isDebugEnabled()) { + LOG.debug("Select from {}:", wifiStatsConfigDbTable); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + } + + Row row = null; + if (result != null && result.length > 0 && !((SelectResult) result[0]).getRows().isEmpty()) { + row = ((SelectResult)result[0]).getRows().iterator().next(); + ret = row.getIntegerColumn("reporting_interval"); + LOG.info("Stats collection for stats_type=device is already configured with reporting_interval = {}", ret); + } + + } catch(OvsdbClientException | TimeoutException | ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } + + return ret; + } + + /** + * @param ovsdbClient + * @param value of reporting_interval column for the stats_type=device from the Wifi_Stats_Config table. If value is not provisioned then return -1. + */ + public void updateDeviceStatsReportingInterval(OvsdbClient ovsdbClient, long newValue) { + try { + List operations = new ArrayList<>(); + Map updateColumns = new HashMap<>(); + + //turn on stats collection over MQTT: (reporting_interval is in seconds?) + //$ ovsh i Wifi_Stats_Config reporting_interval:=10 radio_type:="2.4G" stats_type:="device" + + updateColumns.put("reporting_interval", new Atom(10) ); + updateColumns.put("radio_type", new Atom("2.4G") ); + updateColumns.put("stats_type", new Atom("device") ); + + Row row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + if(LOG.isDebugEnabled()) { + LOG.debug("Updated {}:", wifiStatsConfigDbTable); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + } + + } catch(OvsdbClientException | TimeoutException | ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } + + } + + + public void performRedirect(OvsdbClient ovsdbClient, String clientCn) { + + List operations = new ArrayList<>(); + List conditions = new ArrayList<>(); + List columns = new ArrayList<>(); + columns.add("manager_addr"); + columns.add("sku_number"); + columns.add("serial_number"); + columns.add("model"); + columns.add("firmware_version"); + + try { + LOG.debug("Starting Redirect"); + + operations.add(new Select(awlanNodeDbTable, conditions , columns )); + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + LOG.debug("Select from AWLAN_Node:"); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + + String skuNumber = null; + String serialNumber = null; + String model = null; + String firmwareVersion = null; + + Row row = null; + if (result != null && result.length > 0 && !((SelectResult) result[0]).getRows().isEmpty()) { + row = ((SelectResult)result[0]).getRows().iterator().next(); + } + + firmwareVersion = (row!=null)?row.getStringColumn("firmware_version"):null; + + skuNumber = getSingleValueFromSet(row, "sku_number"); + serialNumber = getSingleValueFromSet(row, "serial_number"); + model = getSingleValueFromSet(row, "model"); + + LOG.info("Redirecting AP Node: clientCn {} serialNumber {} model {} firmwareVersion {} skuNumber {}", clientCn, serialNumber, model, firmwareVersion, skuNumber); + + //Update table AWLAN_Node - set manager_addr + operations.clear(); + Map updateColumns = new HashMap<>(); + + updateColumns.put("manager_addr", new Atom("ssl:" + managerIpAddr + ":" + ovsdbListenPort) ); + + row = new Row(updateColumns ); + operations.add(new Update(awlanNodeDbTable, row )); + + fResult = ovsdbClient.transact(ovsdbName, operations); + result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + LOG.debug("Updated AWLAN_Node:"); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + + LOG.debug("Redirect Done"); + } catch (ExecutionException | InterruptedException | OvsdbClientException | TimeoutException e) { + LOG.error("Error when redirecting AP Node", e); + throw new RuntimeException(e); + } + + } + + public T getSingleValueFromSet(Row row, String columnName){ + + Set set = (row!=null)?row.getSetColumn(columnName):null; + T ret = (set!=null && !set.isEmpty())? set.iterator().next(): null; + + return ret; + } + + public Map getProvisionedInterfaces(OvsdbClient ovsdbClient){ + Map ret = new HashMap<>(); + + List operations = new ArrayList<>(); + List conditions = new ArrayList<>(); + List columns = new ArrayList<>(); + columns.add("name"); + columns.add("_uuid"); + columns.add("ofport"); + columns.add("mtu"); + columns.add("ifindex"); + columns.add("link_state"); + columns.add("admin_state"); + + try { + LOG.debug("Retrieving Interfaces:"); + + operations.add(new Select(interfaceDbTable, conditions , columns )); + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + + for(Row row :((SelectResult)result[0]).getRows()){ + + InterfaceInfo interfaceInfo = new InterfaceInfo(); + interfaceInfo.name = row.getStringColumn("name"); + interfaceInfo.uuid = row.getUuidColumn("_uuid"); + interfaceInfo.ofport = row.getIntegerColumn("ofport").intValue(); + interfaceInfo.mtu = row.getIntegerColumn("mtu").intValue(); + interfaceInfo.ifIndex = row.getIntegerColumn("ifindex").intValue(); + interfaceInfo.linkState = row.getStringColumn("link_state"); + interfaceInfo.adminState = row.getStringColumn("admin_state"); + + ret.put(interfaceInfo.name, interfaceInfo); + } + + LOG.debug("Retrieved Interfaces: {}", ret); + + } catch (ExecutionException | InterruptedException | OvsdbClientException | TimeoutException e) { + LOG.error("Error in getProvisionedInterfaces", e); + + throw new RuntimeException(e); + } + + return ret; + } + + public Map getProvisionedPorts(OvsdbClient ovsdbClient){ + Map ret = new HashMap<>(); + + List operations = new ArrayList<>(); + List conditions = new ArrayList<>(); + List columns = new ArrayList<>(); + columns.add("name"); + columns.add("_uuid"); + columns.add("interfaces"); + + try { + LOG.debug("Retrieving Ports:"); + + operations.add(new Select(portDbTable, conditions , columns )); + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + + for(Row row :((SelectResult)result[0]).getRows()){ + + PortInfo portInfo = new PortInfo(); + portInfo.name = row.getStringColumn("name"); + portInfo.uuid = row.getUuidColumn("_uuid"); + portInfo.interfaceUuids = row.getSetColumn("interfaces"); + + ret.put(portInfo.name, portInfo); + } + + LOG.debug("Retrieved Ports: {}", ret); + + } catch (ExecutionException | InterruptedException | OvsdbClientException | TimeoutException e) { + LOG.error("Error in getProvisionedPorts", e); + throw new RuntimeException(e); + } + + return ret; + } + + public Map getProvisionedBridges(OvsdbClient ovsdbClient){ + Map ret = new HashMap<>(); + + List operations = new ArrayList<>(); + List conditions = new ArrayList<>(); + List columns = new ArrayList<>(); + columns.add("name"); + columns.add("_uuid"); + columns.add("ports"); + + try { + LOG.debug("Retrieving Bridges:"); + + operations.add(new Select(bridgeDbTable, conditions , columns )); + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + + for(Row row :((SelectResult)result[0]).getRows()){ + + BridgeInfo bridgeInfo = new BridgeInfo(); + bridgeInfo.name = row.getStringColumn("name"); + bridgeInfo.uuid = row.getUuidColumn("_uuid"); + bridgeInfo.portUuids = row.getSetColumn("ports"); + + ret.put(bridgeInfo.name, bridgeInfo); + } + + LOG.debug("Retrieved Bridges: {}", ret); + + } catch (ExecutionException | InterruptedException | OvsdbClientException | TimeoutException e) { + LOG.error("Error in getProvisionedBridges", e); + throw new RuntimeException(e); + } + + return ret; + } + + public Map getProvisionedWifiRadioConfigs(OvsdbClient ovsdbClient){ + Map ret = new HashMap<>(); + + List operations = new ArrayList<>(); + List conditions = new ArrayList<>(); + List columns = new ArrayList<>(); + + columns.add("_uuid"); + columns.add("if_name"); + + columns.add("channel"); + columns.add("channel_mode"); + columns.add("country"); + columns.add("enabled"); + columns.add("ht_mode"); + columns.add("tx_power"); + columns.add("vif_configs"); + columns.add("freq_band"); + columns.add("hw_config"); + + try { + LOG.debug("Retrieving WifiRadioConfig:"); + + operations.add(new Select(wifiRadioConfigDbTable, conditions , columns )); + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + + for(Row row :((SelectResult)result[0]).getRows()){ + + WifiRadioConfigInfo wifiRadioConfigInfo = new WifiRadioConfigInfo(); + wifiRadioConfigInfo.uuid = row.getUuidColumn("_uuid"); + wifiRadioConfigInfo.ifName = row.getStringColumn("if_name"); + Long channelTmp = getSingleValueFromSet(row, "channel"); + if (channelTmp==null) { + channelTmp = -1L; + } + wifiRadioConfigInfo.channel = channelTmp.intValue(); + wifiRadioConfigInfo.channelMode = row.getStringColumn("channel_mode"); + wifiRadioConfigInfo.country = getSingleValueFromSet(row, "country"); + Boolean tmp = getSingleValueFromSet(row, "enabled"); + wifiRadioConfigInfo.enabled = tmp!=null?tmp:false; + wifiRadioConfigInfo.htMode = getSingleValueFromSet(row, "ht_mode"); + wifiRadioConfigInfo.txPower = getSingleValueFromSet(row, "txPower"); + wifiRadioConfigInfo.vifConfigUuids = row.getSetColumn("vif_configs"); + wifiRadioConfigInfo.freqBand = row.getStringColumn("freq_band"); + wifiRadioConfigInfo.hwConfig = row.getMapColumn("hw_config"); + + + ret.put(wifiRadioConfigInfo.ifName, wifiRadioConfigInfo); + } + + LOG.debug("Retrieved WifiRadioConfig: {}", ret); + + } catch (ExecutionException | InterruptedException | OvsdbClientException | TimeoutException e) { + LOG.error("Error in getProvisionedWifiRadioConfigs", e); + throw new RuntimeException(e); + } + + return ret; + } + + public Map getProvisionedWifiVifConfigs(OvsdbClient ovsdbClient){ + Map ret = new HashMap<>(); + + List operations = new ArrayList<>(); + List conditions = new ArrayList<>(); + List columns = new ArrayList<>(); + columns.add("bridge"); + columns.add("_uuid"); + columns.add("btm"); + columns.add("enabled"); + columns.add("ft_psk"); + columns.add("group_rekey"); + columns.add("if_name"); + columns.add("mode"); + columns.add("rrm"); + columns.add("ssid"); + columns.add("ssid_broadcast"); + columns.add("uapsd_enable"); + columns.add("vif_radio_idx"); + columns.add("security"); + + try { + LOG.debug("Retrieving WifiVifConfig:"); + + operations.add(new Select(wifiVifConfigDbTable, conditions , columns )); + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + + for(Row row :((SelectResult)result[0]).getRows()){ + + WifiVifConfigInfo wifiVifConfigInfo = new WifiVifConfigInfo(); + wifiVifConfigInfo.bridge = row.getStringColumn("bridge"); + wifiVifConfigInfo.uuid = row.getUuidColumn("_uuid"); + wifiVifConfigInfo.btm = row.getIntegerColumn("btm").intValue(); + wifiVifConfigInfo.enabled = row.getBooleanColumn("enabled"); + wifiVifConfigInfo.ftPsk = row.getIntegerColumn("ft_psk").intValue(); + wifiVifConfigInfo.groupRekey = row.getIntegerColumn("group_rekey").intValue(); + wifiVifConfigInfo.ifName = row.getStringColumn("if_name"); + wifiVifConfigInfo.mode = row.getStringColumn("mode"); + wifiVifConfigInfo.rrm = row.getIntegerColumn("rrm").intValue(); + wifiVifConfigInfo.ssid = row.getStringColumn("ssid"); + wifiVifConfigInfo.ssidBroadcast = row.getStringColumn("ssid_broadcast"); + wifiVifConfigInfo.uapsdEnable = row.getBooleanColumn("uapsd_enable"); + wifiVifConfigInfo.vifRadioIdx = row.getIntegerColumn("vif_radio_idx").intValue(); + wifiVifConfigInfo.security = row.getMapColumn("security"); + + ret.put(wifiVifConfigInfo.ifName + '_' + wifiVifConfigInfo.ssid, wifiVifConfigInfo); + } + + LOG.debug("Retrieved WifiVifConfigs: {}", ret); + + } catch (ExecutionException | InterruptedException | OvsdbClientException | TimeoutException e) { + LOG.error("Error in getProvisionedWifiVifConfigs", e); + throw new RuntimeException(e); + } + + return ret; + } + + public Map getProvisionedWifiInetConfigs(OvsdbClient ovsdbClient){ + Map ret = new HashMap<>(); + + List operations = new ArrayList<>(); + List conditions = new ArrayList<>(); + List columns = new ArrayList<>(); + columns.add("NAT"); + columns.add("_uuid"); + columns.add("enabled"); + columns.add("if_name"); + columns.add("if_type"); + columns.add("ip_assign_scheme"); + columns.add("network"); + + try { + LOG.debug("Retrieving WifiInetConfig:"); + + operations.add(new Select(wifiInetConfigDbTable, conditions , columns )); + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + + for(Row row :((SelectResult)result[0]).getRows()){ + + WifiInetConfigInfo wifiInetConfigInfo = new WifiInetConfigInfo(); + Boolean natTmp = getSingleValueFromSet(row, "NAT"); + wifiInetConfigInfo.nat = natTmp!=null?natTmp:false; + + wifiInetConfigInfo.uuid = row.getUuidColumn("_uuid"); + wifiInetConfigInfo.enabled = row.getBooleanColumn("enabled"); + wifiInetConfigInfo.ifName = row.getStringColumn("if_name"); + wifiInetConfigInfo.ifType = row.getStringColumn("if_type"); + wifiInetConfigInfo.ipAssignScheme = row.getStringColumn("ip_assign_scheme"); + wifiInetConfigInfo.network = row.getBooleanColumn("network"); + + ret.put(wifiInetConfigInfo.ifName, wifiInetConfigInfo); + } + + LOG.debug("Retrieved WifiInetConfigs: {}", ret); + + } catch (ExecutionException | InterruptedException | OvsdbClientException | TimeoutException e) { + LOG.error("Error in getProvisionedWifiInetConfigs", e); + throw new RuntimeException(e); + } + + return ret; + } + + public Map getProvisionedWifiStatsConfigs(OvsdbClient ovsdbClient){ + Map ret = new HashMap<>(); + + List operations = new ArrayList<>(); + List conditions = new ArrayList<>(); + List columns = new ArrayList<>(); + columns.add("channel_list"); + columns.add("radio_type"); + columns.add("reporting_interval"); + columns.add("sampling_interval"); + columns.add("stats_type"); + columns.add("survey_interval_ms"); + columns.add("survey_type"); + columns.add("threshold"); + columns.add("_uuid"); + + try { + LOG.debug("Retrieving WifiStatsConfigs:"); + + operations.add(new Select(wifiStatsConfigDbTable, conditions , columns )); + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + + for(Row row :((SelectResult)result[0]).getRows()){ + + WifiStatsConfigInfo wifiStatsConfigInfo = new WifiStatsConfigInfo(); + + wifiStatsConfigInfo.channelList = row.getSetColumn("channel_list"); + wifiStatsConfigInfo.radioType = row.getStringColumn("radio_type"); + wifiStatsConfigInfo.reportingInterval = row.getIntegerColumn("reporting_interval").intValue(); + wifiStatsConfigInfo.samplingInterval = row.getIntegerColumn("sampling_interval").intValue(); + wifiStatsConfigInfo.statsType = row.getStringColumn("stats_type"); + wifiStatsConfigInfo.surveyType = getSingleValueFromSet(row, "survey_type"); + Long tmp = getSingleValueFromSet(row, "survey_interval_ms"); + wifiStatsConfigInfo.surveyIntervalMs = tmp!=null? tmp.intValue(): 0; + wifiStatsConfigInfo.threshold = row.getMapColumn("threshold"); + wifiStatsConfigInfo.uuid = row.getUuidColumn("_uuid"); + + ret.put(wifiStatsConfigInfo.radioType + "_" + wifiStatsConfigInfo.statsType + "_" + wifiStatsConfigInfo.surveyType, wifiStatsConfigInfo); + } + + LOG.debug("Retrieved WifiStatsConfigs: {}", ret); + + } catch (ExecutionException | InterruptedException | OvsdbClientException | TimeoutException e) { + LOG.error("Error in getProvisionedWifiStatsConfigs", e); + + throw new RuntimeException(e); + } + + return ret; + } + + public void provisionSingleBridgePortInterface(OvsdbClient ovsdbClient, String interfaceName, String bridgeName, int ifIndex, + int ofport, Map provisionedInterfaces, Map provisionedPorts, + Map provisionedBridges) + throws OvsdbClientException, TimeoutException, ExecutionException, InterruptedException { + + if(!provisionedInterfaces.containsKey(interfaceName)) { + //Create this interface and link it to the port and the bridge + List operations = new ArrayList<>(); + Map updateColumns = new HashMap<>(); + List conditions = new ArrayList<>(); + + + updateColumns.put("name", new Atom(interfaceName) ); + updateColumns.put("admin_state", new Atom("up") ); + updateColumns.put("link_state", new Atom("up") ); + updateColumns.put("ifindex", new Atom(ifIndex) ); + updateColumns.put("mtu", new Atom(1500) ); + updateColumns.put("ofport", new Atom(ofport) ); + Uuid interfaceUuid = null; + + Row row = new Row(updateColumns ); + operations.add(new Insert(interfaceDbTable, row )); + + { + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + LOG.debug("Provisioned Interface for {}", interfaceName); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + if(res instanceof InsertResult) { + interfaceUuid = ((InsertResult) res).getUuid(); + } + } + } + + if (interfaceUuid == null) { + throw new IllegalStateException("Interface entry was not created successfully"); + } + + Uuid portUuid = null; + operations = new ArrayList<>(); + //link the interface to the port, create port if necessary + if(!provisionedPorts.containsKey(interfaceName)) { + //need to create port + updateColumns = new HashMap<>(); + + //portUuid = new Uuid(new UUID(System.currentTimeMillis(), System.nanoTime())) ; + updateColumns.put("name", new Atom(interfaceName) ); + //updateColumns.put("_uuid", new Atom(portUuid)); + + Set portInterfacesSet = new HashSet<>(); + portInterfacesSet.add(interfaceUuid); + com.vmware.ovsdb.protocol.operation.notation.Set portInterfaces = com.vmware.ovsdb.protocol.operation.notation.Set.of(portInterfacesSet); + updateColumns.put("interfaces", portInterfaces ); + + row = new Row(updateColumns); + operations.add(new Insert(portDbTable, row )); + + { + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + LOG.debug("Provisioned Port for {}", interfaceName); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + if(res instanceof InsertResult) { + portUuid = ((InsertResult) res).getUuid(); + } + } + } + + } else { + //need to update port + PortInfo existingPort = provisionedPorts.get(interfaceName); + portUuid = existingPort.uuid; + + conditions = new ArrayList<>(); + updateColumns = new HashMap<>(); + + conditions.add(new Condition("name", Function.EQUALS, new Atom<>(interfaceName) )); + + Set portInterfacesSet = new HashSet<>(existingPort.interfaceUuids); + portInterfacesSet.add(interfaceUuid); + com.vmware.ovsdb.protocol.operation.notation.Set portInterfaces = com.vmware.ovsdb.protocol.operation.notation.Set.of(portInterfacesSet); + updateColumns.put("interfaces", portInterfaces ); + + row = new Row(updateColumns); + operations.add(new Update(portDbTable, row )); + + { + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + LOG.debug("Updated Port for {}", interfaceName); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + } + + } + + + if (portUuid == null) { + throw new IllegalStateException("Port entry was not created successfully"); + } + + + operations = new ArrayList<>(); + + //link the port to the bridge + if(provisionedBridges.containsKey(bridgeName)) { + BridgeInfo existingBridge = provisionedBridges.get(brHome); + + conditions = new ArrayList<>(); + updateColumns = new HashMap<>(); + + conditions.add(new Condition("name", Function.EQUALS, new Atom<>(bridgeName) )); + + Set bridgePortsSet = new HashSet<>(existingBridge.portUuids); + bridgePortsSet.add(portUuid); + com.vmware.ovsdb.protocol.operation.notation.Set bridgePorts = com.vmware.ovsdb.protocol.operation.notation.Set.of(bridgePortsSet); + updateColumns.put("ports", bridgePorts ); + + row = new Row(updateColumns); + operations.add(new Update(bridgeDbTable, row )); + + } else { + LOG.warn("provisionedBridges does not have bridge {} - {} - port will be dangling", bridgeName, provisionedBridges.keySet()); + } + + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + if(LOG.isDebugEnabled()) { + LOG.debug("Finished provisioning Interface/port/bridge for {} / {}", interfaceName, bridgeName); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + } + + } + } + + public static final String homeAp24 = "home-ap-24"; + public static final String homeApL50 = "home-ap-l50"; + public static final String brHome = "br-home"; + public static final String brWan = "br-wan"; + + public void provisionBridgePortInterface(OvsdbClient ovsdbClient) { + try { + Map provisionedInterfaces = getProvisionedInterfaces(ovsdbClient); + LOG.debug("Existing Interfaces: {}", provisionedInterfaces.keySet()); + + Map provisionedPorts = getProvisionedPorts(ovsdbClient); + LOG.debug("Existing Ports: {}", provisionedPorts.keySet()); + + Map provisionedBridges = getProvisionedBridges(ovsdbClient); + LOG.debug("Existing Bridges: {}", provisionedBridges.keySet()); + + provisionSingleBridgePortInterface(ovsdbClient, homeApL50, brHome, 30, 1, provisionedInterfaces, provisionedPorts, provisionedBridges); + provisionSingleBridgePortInterface(ovsdbClient, homeAp24, brHome, 31, 2, provisionedInterfaces, provisionedPorts, provisionedBridges); + + } catch(OvsdbClientException | TimeoutException | ExecutionException | InterruptedException e) { + LOG.error("Error in provisionBridgePortInterface", e); + throw new RuntimeException(e); + } + + } + + public void removeOnboardingSsids(OvsdbClient ovsdbClient) { + try { + List operations = new ArrayList<>(); + List conditions = new ArrayList<>(); + conditions.add(new Condition("ssid", Function.EQUALS, new Atom<>("opensync.onboard") )); + + operations.add(new Delete(wifiVifConfigDbTable, conditions )); + + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + if(LOG.isDebugEnabled()) { + LOG.debug("Removed onboarding SSIDs from {}:", wifiVifConfigDbTable); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + } + + } catch(OvsdbClientException | TimeoutException | ExecutionException | InterruptedException e) { + LOG.error("Error in removeOnboardingSsids", e); + throw new RuntimeException(e); + } + + } + + public void configureWifiRadios(OvsdbClient ovsdbClient, OpensyncAPRadioConfig opensyncAPRadioConfig) { + Map provisionedWifiRadios = getProvisionedWifiRadioConfigs(ovsdbClient); + LOG.debug("Existing WifiRadioConfigs: {}", provisionedWifiRadios.keySet()); + + try { + String country = opensyncAPRadioConfig.getCountry(); + String configName = "wifi0"; + int channel = opensyncAPRadioConfig.getRadioChannel24G(); + Map hwConfig = new HashMap<>(); + configureWifiRadios(ovsdbClient, configName, provisionedWifiRadios, channel, hwConfig, country); + + configName = "wifi1"; + channel = opensyncAPRadioConfig.getRadioChannel5LG(); + hwConfig = new HashMap<>(); + hwConfig.put("dfs_enable", "1"); + hwConfig.put("dfs_ignorecac", "0"); + hwConfig.put("dfs_usenol", "1"); + + configureWifiRadios(ovsdbClient, configName, provisionedWifiRadios, channel, hwConfig, country); + + configName = "wifi2"; + channel = opensyncAPRadioConfig.getRadioChannel5HG(); + hwConfig = new HashMap<>(); + hwConfig.put("dfs_enable", "1"); + hwConfig.put("dfs_ignorecac", "0"); + hwConfig.put("dfs_usenol", "1"); + + configureWifiRadios(ovsdbClient, configName, provisionedWifiRadios, channel, hwConfig, country); + + } catch(OvsdbClientException | TimeoutException | ExecutionException | InterruptedException e) { + LOG.error("Error in configureWifiRadios", e); + throw new RuntimeException(e); + } + + } + + public void configureWifiRadios(OvsdbClient ovsdbClient, String configName, + Map provisionedWifiRadios, int channel, Map hwConfig, String country ) + throws OvsdbClientException, TimeoutException, ExecutionException, InterruptedException { + + WifiRadioConfigInfo existingConfig = provisionedWifiRadios.get(configName); + + if(existingConfig==null) { + LOG.warn("There is no WifiRadioConfig {}", configName); + return; + } + + List operations = new ArrayList<>(); + Map updateColumns = new HashMap<>(); + List conditions = new ArrayList<>(); + conditions.add(new Condition("if_name", Function.EQUALS, new Atom<>(configName) )); + + updateColumns.put("channel", new Atom(channel) ); + updateColumns.put("country", new Atom<>(country) ); + @SuppressWarnings("unchecked") + com.vmware.ovsdb.protocol.operation.notation.Map hwConfigMap = com.vmware.ovsdb.protocol.operation.notation.Map.of(hwConfig); + updateColumns.put("hw_config", hwConfigMap); + + Row row = new Row(updateColumns ); + operations.add(new Update(wifiRadioConfigDbTable, conditions, row )); + + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + LOG.debug("Provisioned channel {} for {}", channel, configName); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + } + + public void configureSingleSsid(OvsdbClient ovsdbClient, String bridge, String ifName, String ssid, boolean ssidBroadcast, Map security, Map provisionedWifiRadioConfigs, String radioIfName) { + List operations = new ArrayList<>(); + Map updateColumns = new HashMap<>(); + + try { + ///usr/plume/tools/ovsh i Wifi_VIF_Config + // bridge:=br-home btm:=1 enabled:=true ft_psk:=0 group_rekey:=86400 + // if_name:=home-ap-24 mode:=ap rrm:=1 ssid:=ConnectUS-Plume ssid_broadcast:=enabled + // uapsd_enable:=true vif_radio_idx:=2 security:='["map",[["encryption","WPA-PSK"],["key","12345678"],["mode","2"]]]' + + updateColumns.put("bridge", new Atom<>(bridge) ); + updateColumns.put("btm", new Atom<>(1) ); + updateColumns.put("enabled", new Atom<>(true) ); + updateColumns.put("ft_psk", new Atom<>(0) ); + updateColumns.put("group_rekey", new Atom<>(86400) ); + updateColumns.put("if_name", new Atom<>(ifName) ); + updateColumns.put("mode", new Atom<>("ap") ); + updateColumns.put("rrm", new Atom<>(1) ); + updateColumns.put("ssid", new Atom<>(ssid) ); + updateColumns.put("ssid_broadcast", new Atom<>(ssidBroadcast?"enabled":"disabled") ); + updateColumns.put("uapsd_enable", new Atom<>(true) ); + updateColumns.put("vif_radio_idx", new Atom<>(2) ); + + @SuppressWarnings("unchecked") + com.vmware.ovsdb.protocol.operation.notation.Map securityMap = com.vmware.ovsdb.protocol.operation.notation.Map.of(security); + updateColumns.put("security", securityMap); + + Row row = new Row(updateColumns); + operations.add(new Insert(wifiVifConfigDbTable, row )); + + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + LOG.debug("Provisioned SSID {} on {}", ssid, ifName); + + Uuid vifConfigUuid = null; + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + if(res instanceof InsertResult) { + vifConfigUuid = ((InsertResult) res).getUuid(); + } + } + + if (vifConfigUuid == null) { + throw new IllegalStateException("Wifi_VIF_Config entry was not created successfully"); + } + + //update Wifi_Radio_Config here - add vifConfigUuid + ///usr/plume/tools/ovsh u Wifi_Radio_Config vif_configs:='["set",[["uuid","98e42897-b567-4186-84a6-4a4e38a51e9d"],["uuid","4314920e-c4e6-42a6-93e3-261142ed9adf"]]]' --where if_name==wifi0 + updateColumns.clear(); + operations.clear(); + + WifiRadioConfigInfo wifiRadioConfigInfo = provisionedWifiRadioConfigs.get(radioIfName); + if(wifiRadioConfigInfo == null) { + throw new IllegalStateException("missing Wifi_Radio_Config entry "+ radioIfName); + } + + Set vifConfigsSet = new HashSet<>(wifiRadioConfigInfo.vifConfigUuids); + vifConfigsSet.add(vifConfigUuid); + com.vmware.ovsdb.protocol.operation.notation.Set vifConfigs = com.vmware.ovsdb.protocol.operation.notation.Set.of(vifConfigsSet); + updateColumns.put("vif_configs", vifConfigs ); + + List conditions = new ArrayList<>(); + conditions.add(new Condition("if_name", Function.EQUALS, new Atom<>(radioIfName) )); + + row = new Row(updateColumns); + operations.add(new Update(wifiRadioConfigDbTable, conditions, row )); + + fResult = ovsdbClient.transact(ovsdbName, operations); + result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + if(LOG.isDebugEnabled()) { + LOG.debug("Updated WifiRadioConfig {} for SSID {}:", radioIfName, ssid); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + } + + LOG.info("Provisioned SSID {} on interface {} / {}", ssid, ifName, radioIfName); + + } catch(OvsdbClientException | TimeoutException | ExecutionException | InterruptedException e) { + LOG.error("Error in configureSingleSsid", e); + throw new RuntimeException(e); + } + } + + public void configureSsids(OvsdbClient ovsdbClient, List ssidConfigs) { + if(ssidConfigs==null || ssidConfigs.isEmpty()) { + LOG.debug("No SSIDs to configure"); + return; + } + + Map provisionedWifiVifConfigs = getProvisionedWifiVifConfigs(ovsdbClient); + Map provisionedWifiRadioConfigs = getProvisionedWifiRadioConfigs(ovsdbClient); + LOG.debug("Existing WifiVifConfigs: {}", provisionedWifiVifConfigs.keySet()); + + for(OpensyncAPSsidConfig ssidCfg: ssidConfigs) { + String bridge = brHome; + String ifName = (ssidCfg.getRadioType() == RadioType.is2dot4GHz)?homeAp24:homeApL50; + String radioIfName = (ssidCfg.getRadioType() == RadioType.is2dot4GHz)?"wifi0":"wifi1"; + String ssid = ssidCfg.getSsid(); + boolean ssidBroadcast = ssidCfg.isBroadcast(); + Map security = new HashMap<>(); + security.put("encryption", ssidCfg.getEncryption()); + security.put("key", ssidCfg.getKey()); + security.put("mode", ssidCfg.getMode()); + + if(!provisionedWifiVifConfigs.containsKey(ifName+"_"+ssid)){ + try { + configureSingleSsid(ovsdbClient, bridge, ifName, ssid, ssidBroadcast, security, provisionedWifiRadioConfigs, radioIfName); + } catch (IllegalStateException e) { + //could not provision this SSID, but still can go on + LOG.warn("could not provision SSID {} on {}", ssid, radioIfName); + } + } + + } + + } + + public void configureWifiInet(OvsdbClient ovsdbClient, Map provisionedWifiInetConfigs, String ifName) { + List operations = new ArrayList<>(); + Map updateColumns = new HashMap<>(); + + try { + ///usr/plume/tools/ovsh i Wifi_Inet_Config NAT:=false enabled:=true if_name:=home-ap-24 if_type:=vif ip_assign_scheme:=none network:=true + + updateColumns.put("NAT", new Atom<>(false) ); + updateColumns.put("enabled", new Atom<>(true) ); + updateColumns.put("if_name", new Atom<>(ifName) ); + updateColumns.put("if_type", new Atom<>("vif") ); + updateColumns.put("ip_assign_scheme", new Atom<>("none") ); + updateColumns.put("network", new Atom<>(true) ); + + Row row = new Row(updateColumns); + operations.add(new Insert(wifiInetConfigDbTable, row )); + + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + LOG.debug("Provisioned WifiInetConfig {}", ifName); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + + } catch(OvsdbClientException | TimeoutException | ExecutionException | InterruptedException e) { + LOG.error("Error in configureWifiInet", e); + throw new RuntimeException(e); + } + + } + + public void configureWifiInet(OvsdbClient ovsdbClient) { + Map provisionedWifiInetConfigs = getProvisionedWifiInetConfigs(ovsdbClient); + LOG.debug("Existing WifiInetConfigs: {}", provisionedWifiInetConfigs.keySet()); + + String ifName = homeAp24; + if(!provisionedWifiInetConfigs.containsKey(ifName)) { + configureWifiInet(ovsdbClient, provisionedWifiInetConfigs, ifName); + } + + ifName = homeApL50; + if(!provisionedWifiInetConfigs.containsKey(ifName)) { + configureWifiInet(ovsdbClient, provisionedWifiInetConfigs, ifName); + } + + } + + public void configureStats(OvsdbClient ovsdbClient) { + + Map provisionedWifiStatsConfigs = getProvisionedWifiStatsConfigs(ovsdbClient); + + try { + List operations = new ArrayList<>(); + Map updateColumns = new HashMap<>(); + Row row; + + Set channelSet = new HashSet<>(); + channelSet.add(1); + channelSet.add(6); + channelSet.add(11); + com.vmware.ovsdb.protocol.operation.notation.Set channels = com.vmware.ovsdb.protocol.operation.notation.Set.of(channelSet); + + Map thresholdMap = new HashMap<>(); + thresholdMap.put("max_delay", 600); + thresholdMap.put("util", 10); + + @SuppressWarnings("unchecked") + com.vmware.ovsdb.protocol.operation.notation.Map thresholds = com.vmware.ovsdb.protocol.operation.notation.Map.of(thresholdMap); + + if(!provisionedWifiStatsConfigs.containsKey("2.4G_device_null")) { + // + //updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("2.4G") ); + updateColumns.put("reporting_interval", new Atom<>(10) ); + updateColumns.put("sampling_interval", new Atom<>(0) ); + updateColumns.put("stats_type", new Atom<>("device") ); + //updateColumns.put("survey_interval_ms", new Atom<>(10) ); + //updateColumns.put("survey_type", new Atom<>("on-chan") ); + //updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!provisionedWifiStatsConfigs.containsKey("5GL_survey_on-chan")) { + // + updateColumns = new HashMap<>(); + //updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("5GL") ); + updateColumns.put("reporting_interval", new Atom<>(60) ); + updateColumns.put("sampling_interval", new Atom<>(10) ); + updateColumns.put("stats_type", new Atom<>("survey") ); + updateColumns.put("survey_interval_ms", new Atom<>(0) ); + updateColumns.put("survey_type", new Atom<>("on-chan") ); + //updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!provisionedWifiStatsConfigs.containsKey("2.4G_survey_off-chan")) { + // + updateColumns = new HashMap<>(); + updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("2.4G") ); + updateColumns.put("reporting_interval", new Atom<>(120) ); + updateColumns.put("sampling_interval", new Atom<>(10) ); + updateColumns.put("stats_type", new Atom<>("survey") ); + updateColumns.put("survey_interval_ms", new Atom<>(50) ); + updateColumns.put("survey_type", new Atom<>("off-chan") ); + updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!provisionedWifiStatsConfigs.containsKey("2.4G_neighbor_off-chan")) { + // + updateColumns = new HashMap<>(); + updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("2.4G") ); + updateColumns.put("reporting_interval", new Atom<>(120) ); + updateColumns.put("sampling_interval", new Atom<>(0) ); + updateColumns.put("stats_type", new Atom<>("neighbor") ); + updateColumns.put("survey_interval_ms", new Atom<>(0) ); + updateColumns.put("survey_type", new Atom<>("off-chan") ); + //updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!provisionedWifiStatsConfigs.containsKey("5GU_neighbor_on-chan")) { + // + updateColumns = new HashMap<>(); + //updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("5GU") ); + updateColumns.put("reporting_interval", new Atom<>(60) ); + updateColumns.put("sampling_interval", new Atom<>(0) ); + updateColumns.put("stats_type", new Atom<>("neighbor") ); + updateColumns.put("survey_interval_ms", new Atom<>(0) ); + updateColumns.put("survey_type", new Atom<>("on-chan") ); + //updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!provisionedWifiStatsConfigs.containsKey("5GL_client_null")) { + // + updateColumns = new HashMap<>(); + //updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("5GL") ); + updateColumns.put("reporting_interval", new Atom<>(60) ); + updateColumns.put("sampling_interval", new Atom<>(10) ); + updateColumns.put("stats_type", new Atom<>("client") ); + //updateColumns.put("survey_interval_ms", new Atom<>(0) ); + //updateColumns.put("survey_type", new Atom<>("on-chan") ); + //updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!provisionedWifiStatsConfigs.containsKey("5GU_client_null")) { + // + updateColumns = new HashMap<>(); + //updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("5GU") ); + updateColumns.put("reporting_interval", new Atom<>(60) ); + updateColumns.put("sampling_interval", new Atom<>(10) ); + updateColumns.put("stats_type", new Atom<>("client") ); + //updateColumns.put("survey_interval_ms", new Atom<>(0) ); + //updateColumns.put("survey_type", new Atom<>("on-chan") ); + //updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!provisionedWifiStatsConfigs.containsKey("2.4G_survey_on-chan")) { + // + updateColumns = new HashMap<>(); + //updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("2.4G") ); + updateColumns.put("reporting_interval", new Atom<>(60) ); + updateColumns.put("sampling_interval", new Atom<>(10) ); + updateColumns.put("stats_type", new Atom<>("survey") ); + updateColumns.put("survey_interval_ms", new Atom<>(0) ); + updateColumns.put("survey_type", new Atom<>("on-chan") ); + //updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!provisionedWifiStatsConfigs.containsKey("2.4G_client_null")) { + // + updateColumns = new HashMap<>(); + //updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("2.4G") ); + updateColumns.put("reporting_interval", new Atom<>(60) ); + updateColumns.put("sampling_interval", new Atom<>(10) ); + updateColumns.put("stats_type", new Atom<>("client") ); + //updateColumns.put("survey_interval_ms", new Atom<>(0) ); + //updateColumns.put("survey_type", new Atom<>("on-chan") ); + //updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!provisionedWifiStatsConfigs.containsKey("2.4G_neighbor_on-chan")) { + // + updateColumns = new HashMap<>(); + //updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("2.4G") ); + updateColumns.put("reporting_interval", new Atom<>(60) ); + updateColumns.put("sampling_interval", new Atom<>(0) ); + updateColumns.put("stats_type", new Atom<>("neighbor") ); + updateColumns.put("survey_interval_ms", new Atom<>(0) ); + updateColumns.put("survey_type", new Atom<>("on-chan") ); + //updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!provisionedWifiStatsConfigs.containsKey("5GU_survey_on-chan")) { + // + updateColumns = new HashMap<>(); + //updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("5GU") ); + updateColumns.put("reporting_interval", new Atom<>(60) ); + updateColumns.put("sampling_interval", new Atom<>(10) ); + updateColumns.put("stats_type", new Atom<>("survey") ); + updateColumns.put("survey_interval_ms", new Atom<>(0) ); + updateColumns.put("survey_type", new Atom<>("on-chan") ); + //updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!provisionedWifiStatsConfigs.containsKey("5GL_neighbor_on-chan")) { + // + updateColumns = new HashMap<>(); + //updateColumns.put("channel_list", channels ); + updateColumns.put("radio_type", new Atom<>("5GL") ); + updateColumns.put("reporting_interval", new Atom<>(60) ); + updateColumns.put("sampling_interval", new Atom<>(0) ); + updateColumns.put("stats_type", new Atom<>("neighbor") ); + updateColumns.put("survey_interval_ms", new Atom<>(0) ); + updateColumns.put("survey_type", new Atom<>("on-chan") ); + //updateColumns.put("threshold", thresholds ); + + + row = new Row(updateColumns ); + operations.add(new Insert(wifiStatsConfigDbTable, row )); + // + } + + if(!operations.isEmpty()) { + CompletableFuture fResult = ovsdbClient.transact(ovsdbName, operations); + OperationResult[] result = fResult.get(ovsdbTimeoutSec, TimeUnit.SECONDS); + + if(LOG.isDebugEnabled()) { + LOG.debug("Updated {}:", wifiStatsConfigDbTable); + + for(OperationResult res : result) { + LOG.debug("Op Result {}", res); + } + } + } + + } catch(OvsdbClientException | TimeoutException | ExecutionException | InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/BridgeInfo.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/BridgeInfo.java new file mode 100644 index 0000000..bb8d0db --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/BridgeInfo.java @@ -0,0 +1,33 @@ +package ai.connectus.opensync.ovsdb.dao.models; + +import java.util.HashSet; +import java.util.Set; + +import com.vmware.ovsdb.protocol.operation.notation.Uuid; + +public class BridgeInfo implements Cloneable{ + + public Set portUuids; + + public String name; + public Uuid uuid; + + @Override + public BridgeInfo clone() { + try { + BridgeInfo ret = (BridgeInfo)super.clone(); + if(portUuids!=null) { + ret.portUuids = new HashSet<>(this.portUuids); + } + return ret; + }catch(CloneNotSupportedException e) { + throw new IllegalStateException("Cannot clone ", e); + } + } + + @Override + public String toString() { + return String.format("BridgeInfo [portUuids=%s, name=%s, uuid=%s]", portUuids, name, uuid); + } + +} \ No newline at end of file diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/ConnectNodeInfo.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/ConnectNodeInfo.java new file mode 100644 index 0000000..8c89990 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/ConnectNodeInfo.java @@ -0,0 +1,33 @@ +package ai.connectus.opensync.ovsdb.dao.models; + +import java.util.HashMap; +import java.util.Map; + +public class ConnectNodeInfo implements Cloneable{ + public Map mqttSettings; + public String redirectorAddr; + public String managerAddr; + public String skuNumber; + public String serialNumber; + + @Override + public ConnectNodeInfo clone() { + try { + ConnectNodeInfo ret = (ConnectNodeInfo)super.clone(); + if (this.mqttSettings!=null) { + ret.mqttSettings = new HashMap<>(this.mqttSettings); + } + return ret; + }catch(CloneNotSupportedException e) { + throw new IllegalStateException("Cannot clone ", e); + } + } + + @Override + public String toString() { + return String.format( + "ConnectNodeInfo [mqttSettings=%s, redirectorAddr=%s, managerAddr=%s, skuNumber=%s, serialNumber=%s]", + mqttSettings, redirectorAddr, managerAddr, skuNumber, serialNumber); + } + +} \ No newline at end of file diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/InterfaceInfo.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/InterfaceInfo.java new file mode 100644 index 0000000..11e9149 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/InterfaceInfo.java @@ -0,0 +1,33 @@ +package ai.connectus.opensync.ovsdb.dao.models; + +import com.vmware.ovsdb.protocol.operation.notation.Uuid; + +public class InterfaceInfo implements Cloneable{ + + public String adminState; + public String linkState; + public int ifIndex; + public int mtu; + public int ofport; + + public String name; + public Uuid uuid; + + @Override + public InterfaceInfo clone() { + try { + InterfaceInfo ret = (InterfaceInfo)super.clone(); + return ret; + }catch(CloneNotSupportedException e) { + throw new IllegalStateException("Cannot clone ", e); + } + } + + @Override + public String toString() { + return String.format( + "InterfaceInfo [adminState=%s, linkState=%s, ifIndex=%s, mtu=%s, ofport=%s, name=%s, uuid=%s]", + adminState, linkState, ifIndex, mtu, ofport, name, uuid); + } + +} \ No newline at end of file diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/PortInfo.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/PortInfo.java new file mode 100644 index 0000000..3855e11 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/PortInfo.java @@ -0,0 +1,33 @@ +package ai.connectus.opensync.ovsdb.dao.models; + +import java.util.HashSet; +import java.util.Set; + +import com.vmware.ovsdb.protocol.operation.notation.Uuid; + +public class PortInfo implements Cloneable{ + + public Set interfaceUuids; + + public String name; + public Uuid uuid; + + @Override + public PortInfo clone() { + try { + PortInfo ret = (PortInfo)super.clone(); + if(interfaceUuids!=null) { + ret.interfaceUuids = new HashSet<>(this.interfaceUuids); + } + return ret; + }catch(CloneNotSupportedException e) { + throw new IllegalStateException("Cannot clone ", e); + } + } + + @Override + public String toString() { + return String.format("PortInfo [interfaceUuids=%s, name=%s, uuid=%s]", interfaceUuids, name, uuid); + } + +} \ No newline at end of file diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiInetConfigInfo.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiInetConfigInfo.java new file mode 100644 index 0000000..a74c16a --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiInetConfigInfo.java @@ -0,0 +1,33 @@ +package ai.connectus.opensync.ovsdb.dao.models; + +import com.vmware.ovsdb.protocol.operation.notation.Uuid; + +public class WifiInetConfigInfo implements Cloneable{ + + public boolean nat; + public boolean enabled; + public String ifName; + public String ifType; + public String ipAssignScheme; + public boolean network; + + public Uuid uuid; + + @Override + public WifiInetConfigInfo clone() { + try { + WifiInetConfigInfo ret = (WifiInetConfigInfo)super.clone(); + return ret; + }catch(CloneNotSupportedException e) { + throw new IllegalStateException("Cannot clone ", e); + } + } + + @Override + public String toString() { + return String.format( + "WifiInetConfigInfo [nat=%s, enabled=%s, ifName=%s, ifType=%s, ipAssignScheme=%s, network=%s, uuid=%s]", + nat, enabled, ifName, ifType, ipAssignScheme, network, uuid); + } + +} \ No newline at end of file diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiRadioConfigInfo.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiRadioConfigInfo.java new file mode 100644 index 0000000..63c7fe4 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiRadioConfigInfo.java @@ -0,0 +1,51 @@ +package ai.connectus.opensync.ovsdb.dao.models; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.vmware.ovsdb.protocol.operation.notation.Uuid; + +public class WifiRadioConfigInfo implements Cloneable{ + + public Set vifConfigUuids; + + public String freqBand; + public int channel; + public Integer txPower; + public String channelMode; + public boolean enabled; + public String htMode; + public Map hwConfig; + public String country; + + public String ifName; + public Uuid uuid; + + @Override + public WifiRadioConfigInfo clone() { + try { + WifiRadioConfigInfo ret = (WifiRadioConfigInfo)super.clone(); + if(vifConfigUuids!=null) { + ret.vifConfigUuids = new HashSet<>(this.vifConfigUuids); + } + + if(hwConfig!=null) { + ret.hwConfig = new HashMap<>(this.hwConfig); + } + return ret; + }catch(CloneNotSupportedException e) { + throw new IllegalStateException("Cannot clone ", e); + } + } + + @Override + public String toString() { + return String.format( + "WifiRadioConfigInfo [vifConfigUuids=%s, freqBand=%s, channel=%s, txPower=%s, channelMode=%s, enabled=%s, htMode=%s, hwConfig=%s, country=%s, ifName=%s, uuid=%s]", + vifConfigUuids, freqBand, channel, txPower, channelMode, enabled, htMode, hwConfig, country, ifName, + uuid); + } + +} \ No newline at end of file diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiStatsConfigInfo.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiStatsConfigInfo.java new file mode 100644 index 0000000..1f2cade --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiStatsConfigInfo.java @@ -0,0 +1,53 @@ +package ai.connectus.opensync.ovsdb.dao.models; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.vmware.ovsdb.protocol.operation.notation.Uuid; + +/** + * radio_type + stats_type + survey_type identifies a record + * + */ +public class WifiStatsConfigInfo implements Cloneable{ + + public Set channelList; + public String radioType; + public int reportingInterval; + public int samplingInterval; + public String statsType; + public int surveyIntervalMs; + public String surveyType; + public Map threshold; + + public Uuid uuid; + + @Override + public WifiStatsConfigInfo clone() { + try { + WifiStatsConfigInfo ret = (WifiStatsConfigInfo)super.clone(); + if(channelList!=null) { + ret.channelList = new HashSet<>(this.channelList); + } + if(threshold!=null) { + ret.threshold = new HashMap<>(this.threshold); + } + + return ret; + }catch(CloneNotSupportedException e) { + throw new IllegalStateException("Cannot clone ", e); + } + } + + @Override + public String toString() { + return String.format( + "WifiStatsConfigInfo [channelList=%s, radioType=%s, reportingInterval=%s, samplingInterval=%s, statsType=%s, surveyIntervalMs=%s, surveyType=%s, threshold=%s, uuid=%s]", + channelList, radioType, reportingInterval, samplingInterval, statsType, surveyIntervalMs, + surveyType, threshold, uuid); + } + + +} \ No newline at end of file diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiVifConfigInfo.java b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiVifConfigInfo.java new file mode 100644 index 0000000..c9c2d68 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/ovsdb/dao/models/WifiVifConfigInfo.java @@ -0,0 +1,48 @@ +package ai.connectus.opensync.ovsdb.dao.models; + +import java.util.HashMap; +import java.util.Map; + +import com.vmware.ovsdb.protocol.operation.notation.Uuid; + +public class WifiVifConfigInfo implements Cloneable{ + + public String bridge; + public int btm; + public boolean enabled; + public int ftPsk; + public int groupRekey; + public String ifName; + public String mode; + public int rrm; + public String ssid; + public String ssidBroadcast; + public boolean uapsdEnable; + public int vifRadioIdx; + public Map security; + + public Uuid uuid; + + @Override + public WifiVifConfigInfo clone() { + try { + WifiVifConfigInfo ret = (WifiVifConfigInfo)super.clone(); + + if(security!=null) { + ret.security = new HashMap<>(this.security); + } + return ret; + }catch(CloneNotSupportedException e) { + throw new IllegalStateException("Cannot clone ", e); + } + } + + @Override + public String toString() { + return String.format( + "WifiVifConfigInfo [bridge=%s, btm=%s, enabled=%s, ftPsk=%s, groupRekey=%s, ifName=%s, mode=%s, rrm=%s, ssid=%s, ssidBroadcast=%s, uapsdEnable=%s, vifRadioIdx=%s, security=%s, uuid=%s]", + bridge, btm, enabled, ftPsk, groupRekey, ifName, mode, rrm, ssid, ssidBroadcast, uapsdEnable, + vifRadioIdx, security, uuid); + } + +} \ No newline at end of file diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/util/SslUtil.java b/opensync_gateway/src/main/java/ai/connectus/opensync/util/SslUtil.java new file mode 100644 index 0000000..918fa31 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/util/SslUtil.java @@ -0,0 +1,36 @@ +package ai.connectus.opensync.util; + +public class SslUtil { + + /** + * Examples:
+ * + * subject=CN = PP302X-EX, C = US, O = Plume Design Inc., L = Palo Alto, ST = CA, emailAddress = support@plumewifi.com
+ * subject=C = CA, ST = Ontario, L = Ottawa, O = WhizControl Canada Inc, OU = Research and Develpoment, CN = dev-ap-0001, emailAddress = devadmin@123wlan.com + *
+ * @param subjectDn + * @return Value of the CN attribute of the supplied subject DN, or empty string if it cannot be extracted + */ + public static String extractCN(String subjectDn) { + if(subjectDn==null || subjectDn.isEmpty()) { + return ""; + } + String[] attrs = subjectDn.split(","); + String tmp; + int idx; + for(String attr : attrs) { + tmp = attr.trim(); + if(tmp.startsWith("CN")) { + idx = tmp.indexOf('='); + if(idx>0) { + return tmp.substring(idx + 1).trim(); + } else { + return ""; + } + } + } + + return ""; + + } +} diff --git a/opensync_gateway/src/main/java/ai/connectus/opensync/util/ZlibUtil.java b/opensync_gateway/src/main/java/ai/connectus/opensync/util/ZlibUtil.java new file mode 100644 index 0000000..06f1285 --- /dev/null +++ b/opensync_gateway/src/main/java/ai/connectus/opensync/util/ZlibUtil.java @@ -0,0 +1,105 @@ +package ai.connectus.opensync.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +public class ZlibUtil { + + public static final int MAX_BUFFER_SIZE = 8192; + + public static byte[] compress(byte[] bytesToCompress) { + if(bytesToCompress == null) { + return null; + } + + if(bytesToCompress.length==0) { + return new byte[0]; + } + + Deflater deflater = new Deflater(); + deflater.setInput(bytesToCompress); + deflater.finish(); + + byte[] bytesCompressed = new byte[bytesToCompress.length]; + + int numberOfBytesAfterCompression = deflater.deflate(bytesCompressed); + + if(numberOfBytesAfterCompression == 0) { + throw new IllegalStateException("Compressed size is greater than original?"); + } + + byte[] returnValues = new byte[numberOfBytesAfterCompression]; + + System.arraycopy(bytesCompressed, 0, returnValues, 0, numberOfBytesAfterCompression); + + return returnValues; + } + + private static class SizeAndBuffer{ + int size; + byte[] buffer; + + public SizeAndBuffer(int size, byte[] buffer) { + this.size = size; + this.buffer = buffer; + } + } + + public static byte[] decompress(byte[] bytesToDecompress) + { + if(bytesToDecompress == null) { + return null; + } + + if(bytesToDecompress.length == 0) { + return new byte[0]; + } + + byte[] returnValues = null; + + Inflater inflater = new Inflater(); + int numberOfBytesToDecompress = bytesToDecompress.length; + inflater.setInput(bytesToDecompress, 0, numberOfBytesToDecompress); + + int bufferSizeInBytes = numberOfBytesToDecompress; + + int numberOfBytesDecompressedSoFar = 0; + List bytesDecompressedSoFar = new ArrayList<>(); + + try + { + while (!inflater.needsInput()) + { + byte[] bytesDecompressedBuffer = new byte[bufferSizeInBytes]; + + int numberOfBytesDecompressedThisTime = inflater.inflate(bytesDecompressedBuffer); + numberOfBytesDecompressedSoFar += numberOfBytesDecompressedThisTime; + + bytesDecompressedSoFar.add(new SizeAndBuffer(numberOfBytesDecompressedThisTime, bytesDecompressedBuffer)); + } + + + //now stitch together all the buffers we've collected + returnValues = new byte[numberOfBytesDecompressedSoFar]; + int destPos = 0; + int length = 0; + + for(SizeAndBuffer partBuffer : bytesDecompressedSoFar) { + length = partBuffer.size; + System.arraycopy(partBuffer.buffer, 0, returnValues, destPos, length); + destPos += length; + } + + } catch (DataFormatException dfe) { + throw new IllegalStateException(dfe); + } + + inflater.end(); + + return returnValues; + } + +} diff --git a/opensync_gateway/src/main/resources/application.properties b/opensync_gateway/src/main/resources/application.properties new file mode 100644 index 0000000..bc43d6a --- /dev/null +++ b/opensync_gateway/src/main/resources/application.properties @@ -0,0 +1,122 @@ +# see http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#common-application-properties-security for details + +app.name=OpenSyncManager + +# +#Select spring profiles +# +# valid values for security profiles are: no_ssl, use_ssl +# valid values for auth profiles are: no_auth, form_based_auth, http_basic_auth, http_digest_auth, client_certificate_auth +# +# this property can be overridden by application.properties in the following locations: +# classpath root +# current directory +# classpath /config package +# /config subdir of the current directory. +# System property -Dspring.profiles.active=production +# or specified on the command line using the switch --spring.profiles.active=dev,hsqldb +# +# This does not work: spring.profiles.active=${whizcontrol.core.server.security},${whizcontrol.core.server.auth},Extra_${app.name} +# Substitution of ${} in application.properties works in general, but not for spring.profiles.active and not for spring.profiles.include properties +# +# *** Do not set spring.profiles.active property in this file, or be prepared for the World of Weird. +#spring.profiles.active=a_1,a_2,integration_test +# Example: with application.properties:spring.profiles.active=a_1,a_2,dev,integration_test and @ActiveProfiles(profiles = "test1") +# active profiles: [profile_it_1, profile_it_2, profile_dev_1, profile_dev_2, p_1, p_2, a_1, a_2, dev, integration_test, test1] +# What happens: +# RealActiveProfiles.addAll(application.properties:spring.profiles.active) +# RealActiveProfiles.addAll(application.properties:spring.profiles.include) +# Find all other files named application-${profileName}.properties based on content of RealActiveProfiles +# All application-${profileName}.properties:spring.profiles.include are read, and for each file: +# RealActiveProfiles.addAll(application-${profileName}.properties:spring.profiles.include) +# Recursively process other files named application-${profileName}.properties based on content of RealActiveProfiles +# +# Note that application-${profileName}.properties:spring.profiles.active values are NOT added to the RealActiveProfiles in this case. +# +# Another Example: with application.properties:spring.profiles.active NOT SET and @ActiveProfiles(profiles = "integration_test") +# active profiles: [profile_it_1, profile_it_2, active_it_1, active_it_2, p_1, p_2, integration_test] +# What happens: +# application.properties:spring.profiles.active is read and not found +# RealActiveProfiles.addAll(application.properties:spring.profiles.include) +# Find all other files named application-${profileName}.properties based on content of RealActiveProfiles +# Found application-integration_test.properties file +# RealActiveProfiles.addAll(application-integration_test.properties:spring.profiles.active) +# RealActiveProfiles.addAll(application-integration_test.properties:spring.profiles.include) +# Find all other files named application-${profileName}.properties based on content of RealActiveProfiles +# All application-${profileName}.properties:spring.profiles.include are read, and for each file: +# RealActiveProfiles.addAll(application-${profileName}.properties:spring.profiles.include) +# Recursively process other files named application-${profileName}.properties based on content of RealActiveProfiles +# +# Note that only application-integration_test.properties:spring.profiles.active is read, +# all other application-${profileName}.properties:spring.profiles.active values +# are NOT added to the RealActiveProfiles in this case. +# +# Summary: +# 1. Only the first available property application*.properties:spring.profiles.active is read and added to RealActiveProfiles +# 2. All properties application*.properties:spring.profiles.include are read, and their values are added to RealActiveProfiles +# 3. Many application*.properties can be read during initialization (i.e. one can include a profile name that is referring to another) +# +# *** +# +# Use spring.profiles.active property for unit/integration tests to select proper application-*.properties file +# - this can be done by placing annotation @ActiveProfiles(profiles = "integration_test") on the test classes +# +# Use spring.profiles.active property for specific deployments - staging/cloud to select proper application-*.properties file +# - this can be done by using SystemProperty -Dspring.profiles.active=cloud +# +# Deployment-specific properties can be configured in other property files, like persistence-${envTarget:dev}.properties +# where value for property envTarget can be set using any mechanism, including placing it in the application-*.properties +# +# +# Use spring.profiles.include property to specify static collection of profiles that are +# always present in this configuration, regardless of spring.profiles.active property +# Note: this property is additive, its value is always added to the list of active profiles +#spring.profiles.include=use_ssl,http_digest_auth,customer-credential-datastore-inmemory +#spring.profiles.include=no_ssl,no_auth +#spring.profiles.include=use_ssl,client_certificate_auth +spring.profiles.include=use_ssl_with_client_cert_and_basic_auth,client_certificate_and_basic_auth,rest-template-single-user-per-service-digest-auth,use_single_ds + +whizcontrol.equipmentAndNetworkManagementServiceBaseUrl=https://localhost:9094 + +#used by *-remote client classes when they authenticate their requests +whizcontrol.httpClientConfig=classpath:httpClientConfig.json + +#this user/password is used together with http_digest_auth and http_basic_auth spring profiles +whizcontrol.serviceUser=user +whizcontrol.servicePassword=password + +spring.main.show-banner=false +server.port=9096 + +#this port is used by secondary server connector, it is protected by digest authentication, while primary server.port is protected by client certificate auth +whizcontrol.secondaryPort=7071 + +#this server only supports REST requests, CSRF would get in the way +whizcontrol.csrf-enabled=false +whizcontrol.emailVerificationTokenExpiryMs=600 +whizcontrol.passwordResetTokenExpiryMs=3600 + +whizcontrol.customerAccountManagementServiceBaseUrl=https://localhost:9092 +whizcontrol.orderAndSubscriptionManagementServiceBaseUrl=https://localhost:9093 +whizcontrol.equipmentAndNetworkManagementServiceBaseUrl=https://localhost:9094 +whizcontrol.equipmentConfigurationManagerServiceBaseUrl=https://localhost:9082 +whizcontrol.equipmentStatusAndAlarmCollectorServiceBaseUrl=https://localhost:9083 +whizcontrol.equipmentMetricsCollectorServiceBaseUrl=https://localhost:9085 +whizcontrol.equipmentRoutingServiceBaseUrl=https://localhost:9081 +whizcontrol.firmwareManagementServiceBaseUrl=https://localhost:9072 +whizcontrol.equipmentEventCollectorServiceBaseUrl=https://localhost:9076 + +#server.session-timeout= # session timeout in seconds +#server.tomcat.max-threads = 0 # number of threads in protocol handler + +#server.context-path= # the context path, defaults to '/' +#server.servlet-path= # the servlet path, defaults to '/' +#server.tomcat.access-log-pattern= # log pattern of the access log +#server.tomcat.access-log-enabled=false # is access logging enabled + +# pretty print JSON +spring.jackson.serialization.INDENT_OUTPUT=TRUE +# sort keys +#http.mappers.json-sort-keys=false + +#spring.jmx.enabled=true # Expose MBeans from Spring diff --git a/opensync_gateway/src/main/resources/logback.xml b/opensync_gateway/src/main/resources/logback.xml new file mode 100644 index 0000000..e9e2846 --- /dev/null +++ b/opensync_gateway/src/main/resources/logback.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + %d{yyyy-MM-DD HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + /var/log/a2w/opensyncgw.log + true + + %date %level [%thread] %logger{36} [%file:%line] %msg%n + + + /var/log/a2w/opensyncgw.%i.log.gz + 1 + 3 + + + 20MB + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/opensync_gateway/src/main/resources/run.sh b/opensync_gateway/src/main/resources/run.sh new file mode 100644 index 0000000..70fbb77 --- /dev/null +++ b/opensync_gateway/src/main/resources/run.sh @@ -0,0 +1,30 @@ +SSL_PROPS=" " +SSL_PROPS+=" -Dssl.props=file:///home/ec2-user/opensync/ssl.properties" +SSL_PROPS+=" -Dwhizcontrol.httpClientConfig=file:///home/ec2-user/opensync/httpClientConfig.json" + +CLIENT_MQTT_SSL_PROPS=" " +CLIENT_MQTT_SSL_PROPS+=" -Djavax.net.ssl.keyStore=/home/ec2-user/opensync/client2Keystore.jks" +CLIENT_MQTT_SSL_PROPS+=" -Djavax.net.ssl.keyStorePassword=mypassword" +CLIENT_MQTT_SSL_PROPS+=" -Djavax.net.ssl.trustStore=/home/ec2-user/opensync/truststore.jks" +CLIENT_MQTT_SSL_PROPS+=" -Djavax.net.ssl.trustStorePassword=mypassword" + +OVSDB_PROPS=" " +OVSDB_PROPS+=" -Dconnectus.ovsdb.managerAddr=3.88.149.10" +OVSDB_PROPS+=" -Dconnectus.ovsdb.listenPort=6640 " +OVSDB_PROPS+=" -Dconnectus.ovsdb.redirector.listenPort=6643" +OVSDB_PROPS+=" -Dconnectus.ovsdb.timeoutSec=30" +OVSDB_PROPS+=" -Dconnectus.ovsdb.trustStore=/home/ec2-user/opensync/truststore.jks" +OVSDB_PROPS+=" -Dconnectus.ovsdb.keyStore=/home/ec2-user/opensync/server.p12" + +MQTT_PROPS=" " +MQTT_PROPS+=" -Dconnectus.mqttBroker.address=testportal.123wlan.com" +MQTT_PROPS+=" -Dconnectus.mqttBroker.listenPort=1883" + +LOGGING_PROPS=" -Dlogging.config=file:///home/ec2-user/opensync/logback.xml" + +RESTAPI_PROPS=" " +RESTAPI_PROPS+=" -Dserver.port=443" + +export ALL_PROPS="$SSL_PROPS $CLIENT_MQTT_SSL_PROPS $OVSDB_PROPS $MQTT_PROPS $LOGGING_PROPS $RESTAPI_PROPS" + +sudo java $ALL_PROPS -jar opensync_experiment-0.0.1-SNAPSHOT.jar > stdout.out 2>&1 &