diff --git a/opensync-ext-cloud/.gitignore b/opensync-ext-cloud/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/opensync-ext-cloud/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/opensync-ext-cloud/pom.xml b/opensync-ext-cloud/pom.xml
new file mode 100644
index 0000000..ac367b9
--- /dev/null
+++ b/opensync-ext-cloud/pom.xml
@@ -0,0 +1,262 @@
+
+
+ 4.0.0
+
+ com.telecominfraproject.wlan
+ tip-wlan-cloud-root-pom
+ 0.0.1-SNAPSHOT
+ ../../wlan-cloud-root
+
+ opensync-ext-cloud
+ opensync-ext-cloud
+ Configuration interface that provides cloud config from the file
+
+
+ com.telecominfraproject.wlan
+ opensync-ext-interface
+ ${tip-wlan-cloud.release.version}
+
+
+ com.telecominfraproject.wlan
+ opensync-ext-interface
+ ${tip-wlan-cloud.release.version}
+
+
+ base-container
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+ base-client
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ webtoken-auth-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ portal-services
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ customer-service-local
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ location-service-local
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ equipment-service-local
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ profile-service-local
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ customer-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ customer-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ location-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ location-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ equipment-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ equipment-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ profile-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ profile-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ portal-user-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ portal-user-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ firmware-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ firmware-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ manufacturer-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ manufacturer-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ cloud-event-dispatcher-interface
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ cloud-metrics
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ single-process-streams
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ service-metric-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ service-metric-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ system-event-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ system-event-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ alarm-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ alarm-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ status-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ status-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ client-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ client-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+ routing-service
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
+ routing-datastore-inmemory
+ com.telecominfraproject.wlan
+ ${tip-wlan-cloud.release.version}
+
+
+
+
diff --git a/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/OpensyncExternalIntegrationCloud.java b/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/OpensyncExternalIntegrationCloud.java
new file mode 100644
index 0000000..07410e4
--- /dev/null
+++ b/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/OpensyncExternalIntegrationCloud.java
@@ -0,0 +1,750 @@
+package com.telecominfraproject.wlan.opensync.external.integration;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+
+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.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Component;
+
+import com.telecominfraproject.wlan.cloudeventdispatcher.CloudEventDispatcherInterface;
+import com.telecominfraproject.wlan.core.model.equipment.EquipmentType;
+import com.telecominfraproject.wlan.core.model.equipment.MacAddress;
+import com.telecominfraproject.wlan.core.model.equipment.RadioType;
+import com.telecominfraproject.wlan.customer.models.Customer;
+import com.telecominfraproject.wlan.customer.service.CustomerServiceInterface;
+import com.telecominfraproject.wlan.equipment.EquipmentServiceInterface;
+import com.telecominfraproject.wlan.equipment.models.Equipment;
+import com.telecominfraproject.wlan.equipment.models.EquipmentDetails;
+import com.telecominfraproject.wlan.location.service.LocationServiceInterface;
+import com.telecominfraproject.wlan.opensync.external.integration.controller.OpensyncCloudGatewayController;
+import com.telecominfraproject.wlan.opensync.external.integration.models.ConnectNodeInfo;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAPConfig;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAPInetState;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAPRadioConfig;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAPRadioState;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAPSsidConfig;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAPVIFState;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAWLANNode;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncWifiAssociatedClients;
+import com.telecominfraproject.wlan.profile.ProfileServiceInterface;
+import com.telecominfraproject.wlan.profile.models.ProfileDetails;
+import com.telecominfraproject.wlan.servicemetrics.models.ApClientMetrics;
+import com.telecominfraproject.wlan.servicemetrics.models.ClientMetrics;
+import com.telecominfraproject.wlan.servicemetrics.models.SingleMetricRecord;
+
+import sts.PlumeStats.Client;
+import sts.PlumeStats.ClientReport;
+import sts.PlumeStats.RadioBandType;
+import sts.PlumeStats.Report;
+import traffic.NetworkMetadata.FlowReport;
+import wc.stats.IpDnsTelemetry.WCStatsReport;
+
+@Profile("opensync_cloud_config")
+@Component
+public class OpensyncExternalIntegrationCloud implements OpensyncExternalIntegrationInterface {
+
+ private static final Logger LOG = LoggerFactory.getLogger(OpensyncExternalIntegrationCloud.class);
+
+ @Autowired
+ private CustomerServiceInterface customerServiceInterface;
+ @Autowired
+ private LocationServiceInterface locationServiceInterface;
+ @Autowired
+ private OvsdbSessionMapInterface ovsdbSessionMapInterface;
+ @Autowired
+ private CloudEventDispatcherInterface equipmentMetricsCollectorInterface;
+ @Autowired
+ private EquipmentServiceInterface equipmentServiceInterface;
+
+ @Autowired
+ private ProfileServiceInterface profileServiceInterface;
+
+ @Autowired
+ private CacheManager cacheManagerShortLived;
+
+ @Value("${connectus.ovsdb.autoProvisionedCustomerId:2}")
+ private int autoProvisionedCustomerId;
+ @Value("${connectus.ovsdb.autoProvisionedEquipmentId:0}")
+ private int autoProvisionedEquipmentId;
+ @Value("${connectus.ovsdb.autoProvisionedLocationId:5}")
+ private int autoProvisionedLocationId;
+ @Value("${connectus.ovsdb.autoProvisioned24G:1}")
+ private int autoProvisioned24G;
+ @Value("${connectus.ovsdb.autoProvisioned5LG:44}")
+ private int autoProvisioned5LG;
+ @Value("${connectus.ovsdb.autoProvisioned5UG:108}")
+ private int autoProvisioned5UG;
+ @Value("${connectus.ovsdb.autoProvisionedSsid:Connectus-cloud")
+ private String autoProvisionedSsid;
+
+ private Map opensyncNodeMap;
+ @Autowired
+ private OpensyncCloudGatewayController opensyncCloudGatewayController;
+
+ private Cache equipmentRecordCache;
+
+ @PostConstruct
+ private void postCreate() {
+ LOG.info("Using cloud integration");
+ equipmentRecordCache = cacheManagerShortLived.getCache("opensync_equipment_record_cache");
+ opensyncNodeMap = Collections.synchronizedMap(new HashMap());
+ }
+
+ public Equipment getEquipment(Long equipmentId) {
+ Equipment ce = null;
+ try {
+ ce = equipmentServiceInterface.getOrNull(equipmentId);
+ } catch (Exception e) {
+ // do nothing
+ }
+
+ return ce;
+ }
+
+ public void apConnected(String apId, ConnectNodeInfo connectNodeInfo) {
+ LOG.info("AP {} got connected to the gateway", apId);
+
+ Customer customer = customerServiceInterface.get(autoProvisionedCustomerId);
+
+ Equipment customerEquipment = equipmentServiceInterface.get(autoProvisionedEquipmentId);
+
+ LOG.debug("Customer {} Equipment {}", customer.toPrettyString(), customerEquipment.toPrettyString());
+
+ OvsdbSession ovsdbSession = ovsdbSessionMapInterface.getSession(apId);
+ ovsdbSession.setEquipmentId(customerEquipment.getId());
+ ovsdbSession.setCustomerId(customerEquipment.getCustomerId());
+
+ }
+
+ public void apDisconnected(String apId) {
+ LOG.info("AP {} got disconnected from the gateway", apId);
+ try {
+ // removed the 'in-memory' cached node
+ synchronized (opensyncNodeMap) {
+ opensyncNodeMap.remove(apId);
+ LOG.info("AP {} and table state data removed from memory cache", apId);
+ }
+
+ OvsdbSession ovsdbSession = ovsdbSessionMapInterface.getSession(apId);
+
+ if (ovsdbSession != null) {
+// routingServiceInterface.delete(ovsdbSession.getRoutingId());
+ } else {
+ LOG.warn("Cannot find ap {} in inventory", apId);
+ }
+ } catch (Exception e) {
+ LOG.error("Exception when registering ap routing {}", apId, e);
+ }
+
+ }
+
+ public OpensyncAPConfig getApConfig(String apId) {
+ LOG.info("Retrieving config for AP {} ", apId);
+ OpensyncAPConfig ret = null;
+
+ try {
+ OvsdbSession ovsdbSession = ovsdbSessionMapInterface.getSession(apId);
+ if (ovsdbSession == null) {
+ throw new IllegalStateException("AP is not connected " + apId);
+ }
+ long equipmentId = ovsdbSession.getEquipmentId();
+
+ RadioType rt;
+
+ ret = new OpensyncAPConfig();
+
+ // extract country, radio channels from resolvedEqCfg
+ String country = "CA";
+
+ int radioChannel24G = autoProvisioned24G;
+ int radioChannel5LG = autoProvisioned5LG;
+ int radioChannel5UG = autoProvisioned5UG;
+
+ OpensyncAPRadioConfig radioConfig = new OpensyncAPRadioConfig();
+ radioConfig.setCountry(country);
+ radioConfig.setRadioChannel24G(radioChannel24G);
+ radioConfig.setRadioChannel5LG(radioChannel5LG);
+ radioConfig.setRadioChannel5HG(108);
+
+ ret.setRadioConfig(radioConfig);
+
+ List ssidConfigs = new ArrayList<>();
+ ret.setSsidConfigs(ssidConfigs);
+
+ OpensyncAPSsidConfig osSsidCfg = new OpensyncAPSsidConfig();
+ osSsidCfg.setSsid(autoProvisionedSsid);
+ osSsidCfg.setRadioType(com.telecominfraproject.wlan.core.model.equipment.RadioType.is5GHz);
+ osSsidCfg.setBroadcast(true);
+
+ osSsidCfg.setEncryption("WPA-PSK");
+ osSsidCfg.setMode("2");
+
+ ssidConfigs.add(osSsidCfg);
+
+ // configure the same ssid on the second radio
+ osSsidCfg = osSsidCfg.clone();
+ osSsidCfg.setRadioType(com.telecominfraproject.wlan.core.model.equipment.RadioType.is2dot4GHz);
+ ssidConfigs.add(osSsidCfg);
+
+ } catch (Exception e) {
+ LOG.error("Cannot read config for AP {}", apId, e);
+ }
+
+ LOG.debug("Config content : {}", ret);
+
+ return ret;
+ }
+
+ /**
+ * @param topic
+ * @return apId extracted from the topic name, or null if it cannot be extracted
+ */
+ public static String extractApIdFromTopic(String topic) {
+ // Topic is formatted as
+ // "/ap/"+clientCn+"_"+ret.serialNumber+"/opensync"
+ if (topic == null) {
+ return null;
+ }
+
+ String[] parts = topic.split("/");
+ if (parts.length < 3) {
+ return null;
+ }
+
+ // apId is the third element in the topic
+ return parts[2];
+ }
+
+ /**
+ * @param topic
+ * @return customerId looked up from the topic name, or -1 if it cannot be
+ * extracted
+ */
+ public int extractCustomerIdFromTopic(String topic) {
+
+ String apId = extractApIdFromTopic(topic);
+ if (apId == null) {
+ return -1;
+ }
+
+ OvsdbSession ovsdbSession = ovsdbSessionMapInterface.getSession(apId);
+
+ if (ovsdbSession != null) {
+ return ovsdbSession.getCustomerId();
+ }
+
+ return -1;
+
+ }
+
+ public long extractEquipmentIdFromTopic(String topic) {
+
+ String apId = extractApIdFromTopic(topic);
+ if (apId == null) {
+ return -1;
+ }
+
+ OvsdbSession ovsdbSession = ovsdbSessionMapInterface.getSession(apId);
+
+ if (ovsdbSession != null) {
+ return ovsdbSession.getEquipmentId();
+ }
+
+ return -1;
+
+ }
+
+ public void processMqttMessage(String topic, Report report) {
+ LOG.info("Received report on topic {} for ap {}", topic, report.getNodeID());
+ int customerId = extractCustomerIdFromTopic(topic);
+ if (customerId > 0) {
+ opensyncCloudGatewayController.updateActiveCustomer(customerId);
+ }
+
+ long equipmentId = extractEquipmentIdFromTopic(topic);
+ if (equipmentId <= 0 || customerId <= 0) {
+ LOG.warn("Cannot determine equipment ids from topic {} - customerId {} equipmentId {}", topic, customerId,
+ equipmentId);
+ return;
+ }
+
+ List metricRecordList = new ArrayList<>();
+
+ populateApClientMetrics(metricRecordList, report, customerId, equipmentId);
+ // TODO: populateApNodeMetrics(metricRecordList, report, customerId,
+ // equipmentId, extractApIdFromTopic(topic));
+
+ if (!metricRecordList.isEmpty()) {
+ equipmentMetricsCollectorInterface.publishMetrics(metricRecordList);
+ }
+
+ }
+
+ private void populateApClientMetrics(List metricRecordList, Report report, int customerId,
+ long equipmentId) {
+ LOG.debug("populateApClientMetrics for Customer {} Equipment {}", customerId, equipmentId);
+
+ for (ClientReport clReport : report.getClientsList()) {
+ SingleMetricRecord smr = new SingleMetricRecord(customerId, equipmentId);
+ metricRecordList.add(smr);
+
+ ApClientMetrics apClientMetrics = new ApClientMetrics();
+ smr.setData(apClientMetrics);
+ smr.setCreatedTimestamp(clReport.getTimestampMs());
+
+ smr.setCustomerId(customerId);
+ smr.setEquipmentId(equipmentId);
+
+ Integer periodLengthSec = 60; // matches what's configured by
+ // OvsdbDao.configureStats(OvsdbClient)
+ apClientMetrics.setPeriodLengthSec(periodLengthSec);
+
+ List clientMetrics = new ArrayList<>();
+
+ for (Client cl : clReport.getClientListList()) {
+
+ // clReport.getChannel();
+ ClientMetrics cMetrics = new ClientMetrics();
+ clientMetrics.add(cMetrics);
+
+ cMetrics.setRadioType(
+ (clReport.getBand() == RadioBandType.BAND2G) ? RadioType.is2dot4GHz : RadioType.is5GHz);
+ cMetrics.setDeviceMacAddress(new MacAddress(cl.getMacAddress()));
+
+ if (cl.hasStats()) {
+ if (cl.getStats().hasRssi()) {
+ cMetrics.setRssi(cl.getStats().getRssi());
+ }
+
+ // we'll report each device as having a single (very long)
+ // session
+ cMetrics.setSessionId(cMetrics.getDeviceMacAddress().getAddressAsLong());
+
+ // populate Rx stats
+ if (cl.getStats().hasRxBytes()) {
+ cMetrics.setRxBytes(cl.getStats().getRxBytes());
+ }
+
+ if (cl.getStats().hasRxRate()) {
+ // cMetrics.setAverageRxRate(cl.getStats().getRxRate());
+ }
+
+ if (cl.getStats().hasRxErrors()) {
+ cMetrics.setNumRxNoFcsErr((int) cl.getStats().getRxErrors());
+ }
+
+ if (cl.getStats().hasRxFrames()) {
+ // cMetrics.setNumRxFramesReceived(cl.getStats().getRxFrames());
+ cMetrics.setNumRxPackets(cl.getStats().getRxFrames());
+ }
+
+ if (cl.getStats().hasRxRetries()) {
+ cMetrics.setNumRxRetry((int) cl.getStats().getRxRetries());
+ }
+
+ // populate Tx stats
+ if (cl.getStats().hasTxBytes()) {
+ cMetrics.setNumTxBytes(cl.getStats().getTxBytes());
+ }
+
+ if (cl.getStats().hasTxRate()) {
+ // cMetrics.setAverageTxRate(cl.getStats().getTxRate());
+ }
+
+ if (cl.getStats().hasTxRate() && cl.getStats().hasRxRate()) {
+ cMetrics.setRates(
+ new byte[] { (byte) (cl.getStats().getTxRate()), (byte) (cl.getStats().getRxRate()) });
+ }
+
+ if (cl.getStats().hasTxErrors()) {
+ cMetrics.setNumTxDropped((int) cl.getStats().getTxErrors());
+ }
+
+ if (cl.getStats().hasRxFrames()) {
+ // cMetrics.setNumTxFramesTransmitted(cl.getStats().getTxFrames());
+ cMetrics.setNumTxPackets(cl.getStats().getRxFrames());
+ }
+
+ if (cl.getStats().hasTxRetries()) {
+ cMetrics.setNumTxDataRetries((int) cl.getStats().getTxRetries());
+ }
+
+ }
+ }
+
+ if (clReport.getBand() == RadioBandType.BAND2G) {
+ apClientMetrics.setClientMetrics2g(clientMetrics.toArray(new ClientMetrics[0]));
+ } else {
+ apClientMetrics.setClientMetrics5g(clientMetrics.toArray(new ClientMetrics[0]));
+ }
+
+ }
+ }
+
+ public void processMqttMessage(String topic, FlowReport flowReport) {
+
+ LOG.info("Received report on topic {}", topic);
+ int customerId = extractCustomerIdFromTopic(topic);
+ if (customerId > 0) {
+ opensyncCloudGatewayController.updateActiveCustomer(customerId);
+ }
+
+ long equipmentId = extractEquipmentIdFromTopic(topic);
+ if (equipmentId <= 0 || customerId <= 0) {
+ LOG.warn("Cannot determine equipment ids from topic {} - customerId {} equipmentId {}", topic, customerId,
+ equipmentId);
+ return;
+ }
+
+ String apId = extractApIdFromTopic(topic);
+
+ if (apId == null) {
+ LOG.warn("Cannot determine AP id from topic {} - customerId {} equipmentId {} apId {}", topic, customerId,
+ equipmentId, apId);
+ return;
+ }
+
+ }
+
+ public void processMqttMessage(String topic, WCStatsReport wcStatsReport) {
+ LOG.debug("Received WCStatsReport {}", wcStatsReport.toString());
+
+ LOG.info("Received report on topic {}", topic);
+ int customerId = extractCustomerIdFromTopic(topic);
+ if (customerId > 0) {
+ opensyncCloudGatewayController.updateActiveCustomer(customerId);
+ }
+
+ long equipmentId = extractEquipmentIdFromTopic(topic);
+ if (equipmentId <= 0 || customerId <= 0) {
+ LOG.warn("Cannot determine equipment ids from topic {} - customerId {} equipmentId {}", topic, customerId,
+ equipmentId);
+ return;
+ }
+
+ String apId = extractApIdFromTopic(topic);
+
+ if (apId == null) {
+ LOG.warn("Cannot determine AP id from topic {} - customerId {} equipmentId {} apId {}", topic, customerId,
+ equipmentId, apId);
+ return;
+ }
+
+ }
+
+ @Override
+ public void wifiVIFStateDbTableUpdate(List vifStateTables, String apId) {
+ if (vifStateTables == null || vifStateTables.isEmpty() || apId == null)
+ return;
+ OpensyncNode osNode = null;
+
+ synchronized (opensyncNodeMap) {
+
+ if (opensyncNodeMap.containsKey(apId)) {
+ osNode = opensyncNodeMap.get(apId);
+ for (OpensyncAPVIFState vifState : vifStateTables) {
+ if (vifState.isEnabled())
+ osNode.updateVifState(vifState);
+ }
+ opensyncNodeMap.put(apId, osNode);
+ // LOG.debug("Updated VIF States for AP to NodeMap {}",
+ // opensyncNodeMap.get(apId).toPrettyString());
+ } else {
+ OvsdbSession session = ovsdbSessionMapInterface.getSession(apId);
+
+ if (session != null) {
+ int customerId = session.getCustomerId();
+ long equipmentId = session.getEquipmentId();
+ osNode = new OpensyncNode(apId, null, customerId, equipmentId);
+ for (OpensyncAPVIFState vifState : vifStateTables) {
+ if (vifState.isEnabled())
+ osNode.updateVifState(vifState);
+ }
+ opensyncNodeMap.put(apId, osNode);
+ }
+ }
+
+ osNode = opensyncNodeMap.get(apId);
+ List vifStates = osNode.getVifStates();
+ LOG.debug(
+ "BSSID SSID AUTH MODE RADIO DEVICES");
+
+ for (OpensyncAPVIFState vif : vifStates) {
+ String ssid = vif.getSsid();
+ int channel = vif.getChannel();
+ int devices = vif.getAssociatedClients().size();
+ String bssid = osNode.getRadioForChannel(channel).getMac();
+ String freqBand = osNode.getRadioForChannel(channel).getFreqBand();
+ String encryption = vif.getSecurity().get("encryption");
+
+ LOG.debug("{} {} {} {} {}", bssid, ssid, encryption, freqBand, devices);
+ }
+
+ }
+
+ }
+
+ @Override
+ public void wifiRadioStatusDbTableUpdate(List radioStateTables, String apId) {
+
+ OvsdbSession session = ovsdbSessionMapInterface.getSession(apId);
+ int customerId = session.getCustomerId();
+ long equipmentId = session.getEquipmentId();
+
+ if (customerId > 0) {
+ opensyncCloudGatewayController.updateActiveCustomer(customerId);
+ } else {
+ LOG.debug("Cannot get customerId {} for session {}", customerId);
+ return;
+ }
+
+ if (equipmentId < 1) {
+ LOG.debug("Cannot get equipmentId {} for session {}", equipmentId);
+ return;
+ }
+
+ if (radioStateTables == null || radioStateTables.isEmpty() || apId == null)
+ return;
+
+ // add to RadioStates States Map
+ OpensyncNode osNode = null;
+ synchronized (opensyncNodeMap) {
+
+ if (opensyncNodeMap.containsKey(apId)) {
+ osNode = opensyncNodeMap.get(apId);
+ for (OpensyncAPRadioState radioState : radioStateTables) {
+ if (radioState.isEnabled())
+ osNode.updateRadioState(radioState);
+ }
+ opensyncNodeMap.put(apId, osNode);
+ } else {
+ osNode = new OpensyncNode(apId, null, customerId, equipmentId);
+ for (OpensyncAPRadioState radioState : radioStateTables) {
+ if (radioState.isEnabled())
+ osNode.updateRadioState(radioState);
+ }
+ opensyncNodeMap.put(apId, osNode);
+
+ }
+ }
+ }
+
+ @Override
+ public void wifiInetStateDbTableUpdate(List inetStateTables, String apId) {
+ OvsdbSession session = ovsdbSessionMapInterface.getSession(apId);
+ int customerId = session.getCustomerId();
+ long equipmentId = session.getEquipmentId();
+
+ if (customerId > 0) {
+ opensyncCloudGatewayController.updateActiveCustomer(customerId);
+ } else {
+ LOG.debug("Cannot get customerId {} for session {}", customerId);
+ return;
+ }
+
+ if (equipmentId < 1) {
+ LOG.debug("Cannot get equipmentId {} for session {}", equipmentId);
+ return;
+ }
+
+ if (inetStateTables == null || inetStateTables.isEmpty() || apId == null)
+ return;
+
+ // add to Inet States Map
+ OpensyncNode osNode = null;
+ synchronized (opensyncNodeMap) {
+ if (opensyncNodeMap.containsKey(apId)) {
+ osNode = opensyncNodeMap.get(apId);
+ for (OpensyncAPInetState inetState : inetStateTables) {
+ if (inetState.isEnabled())
+ osNode.updateInetState(inetState);
+ }
+ opensyncNodeMap.put(apId, osNode);
+ } else {
+ osNode = new OpensyncNode(apId, null, customerId, equipmentId);
+ for (OpensyncAPInetState inetState : inetStateTables) {
+ if (inetState.isEnabled())
+ osNode.updateInetState(inetState);
+ }
+ opensyncNodeMap.put(apId, osNode);
+
+ }
+ }
+
+ }
+
+ @Override
+ public void wifiAssociatedClientsDbTableUpdate(List wifiAssociatedClients,
+ String apId) {
+ OvsdbSession session = ovsdbSessionMapInterface.getSession(apId);
+ int customerId = session.getCustomerId();
+ long equipmentId = session.getEquipmentId();
+
+ if (customerId > 0) {
+ opensyncCloudGatewayController.updateActiveCustomer(customerId);
+ } else {
+ LOG.debug("Cannot get customerId {} for session {}", customerId);
+ return;
+ }
+
+ if (equipmentId < 1) {
+ LOG.debug("Cannot get equipmentId {} for session {}", equipmentId);
+ return;
+ }
+
+ if (wifiAssociatedClients == null || wifiAssociatedClients.isEmpty() || apId == null)
+ return;
+
+ OpensyncNode osNode = null;
+ synchronized (opensyncNodeMap) {
+ if (opensyncNodeMap.containsKey(apId)) {
+ osNode = opensyncNodeMap.get(apId);
+ for (OpensyncWifiAssociatedClients wifiClient : wifiAssociatedClients) {
+ osNode.updateWifiClients(wifiClient);
+ }
+ opensyncNodeMap.put(apId, osNode);
+ } else {
+
+ osNode = new OpensyncNode(apId, null, customerId, equipmentId);
+ for (OpensyncWifiAssociatedClients wifiClient : wifiAssociatedClients) {
+ osNode.updateWifiClients(wifiClient);
+ }
+ opensyncNodeMap.put(apId, osNode);
+ }
+
+ }
+ }
+
+ @Override
+ public void awlanNodeDbTableUpdate(OpensyncAWLANNode opensyncAPState, String apId) {
+
+ OvsdbSession session = ovsdbSessionMapInterface.getSession(apId);
+ int customerId = session.getCustomerId();
+ long equipmentId = session.getEquipmentId();
+
+ if (customerId > 0) {
+ opensyncCloudGatewayController.updateActiveCustomer(customerId);
+ } else {
+ LOG.debug("Cannot get customerId {} for session {}", customerId);
+ return;
+ }
+
+ if (equipmentId < 1) {
+ LOG.debug("Cannot get equipmentId {} for session {}", equipmentId);
+ return;
+ }
+ if (opensyncAPState == null || apId == null)
+ return;
+
+ OpensyncNode osNode = null;
+ synchronized (opensyncNodeMap) {
+ if (opensyncNodeMap.containsKey(apId)) {
+ osNode = opensyncNodeMap.get(apId);
+ osNode.updateAWLANNode(opensyncAPState);
+ opensyncNodeMap.put(apId, osNode);
+ LOG.debug("Updated AWLAN_Node to map {}", osNode.toPrettyString());
+
+ } else {
+
+ osNode = new OpensyncNode(apId, opensyncAPState, customerId, equipmentId);
+ opensyncNodeMap.put(apId, osNode);
+ }
+
+ }
+
+ }
+
+ @Override
+ public void wifiVIFStateDbTableDelete(List vifStateTables, String apId) {
+ OvsdbSession session = ovsdbSessionMapInterface.getSession(apId);
+ int customerId = session.getCustomerId();
+ long equipmentId = session.getEquipmentId();
+
+ if (customerId > 0) {
+ opensyncCloudGatewayController.updateActiveCustomer(customerId);
+ } else {
+ LOG.debug("Cannot get customerId {} for session {}", customerId);
+ return;
+ }
+
+ if (equipmentId < 1) {
+ LOG.debug("Cannot get equipmentId {} for session {}", equipmentId);
+ return;
+ }
+
+ synchronized (opensyncNodeMap) {
+
+ if (opensyncNodeMap.containsKey(apId)) {
+ OpensyncNode osNode = opensyncNodeMap.get(apId);
+
+ for (OpensyncAPVIFState vifToDelete : vifStateTables) {
+ if (osNode.deleteVif(vifToDelete)) {
+ opensyncNodeMap.put(apId, osNode);
+ LOG.debug("Deleted VIF for interface {} ssid {} from AP {}", vifToDelete.getIfName(),
+ vifToDelete.getSsid(), apId);
+ } else {
+ LOG.debug("Cannot find VIF for interface {} ssid {} marked for deletion under AP {}",
+ vifToDelete.getIfName(), vifToDelete.getSsid(), apId);
+ }
+ }
+
+ } else {
+ LOG.debug("AP {} is not present in cache, cannot delete VIFs", apId);
+ }
+ }
+ }
+
+ @Override
+ public void wifiAssociatedClientsDbTableDelete(String deletedClientMac, String apId) {
+
+ OvsdbSession session = ovsdbSessionMapInterface.getSession(apId);
+ int customerId = session.getCustomerId();
+ long equipmentId = session.getEquipmentId();
+
+ if (customerId > 0) {
+ opensyncCloudGatewayController.updateActiveCustomer(customerId);
+ } else {
+ LOG.debug("Cannot get customerId {} for session {}", customerId);
+ return;
+ }
+
+ if (equipmentId < 1) {
+ LOG.debug("Cannot get equipmentId {} for session {}", equipmentId);
+ return;
+ }
+
+ synchronized (opensyncNodeMap) {
+ if (opensyncNodeMap.containsKey(apId)) {
+ OpensyncNode osNode = opensyncNodeMap.get(apId);
+ if (osNode.deleteWifiClient(deletedClientMac)) {
+ opensyncNodeMap.put(apId, osNode);
+ LOG.debug("Deleted WifiClient {} from AP {}", deletedClientMac, apId);
+ } else {
+ LOG.debug("Cannot find WifiClient {} marked for deletion under AP {}", deletedClientMac, apId);
+ }
+ } else {
+ LOG.debug("AP {} is not present in cache, cannot delete WifiClient {}", apId, deletedClientMac);
+ }
+ }
+
+ try {
+// LOG.debug("Deleted AssociatedClient {}",
+// clientServiceInterface.delete(new MacAddress(deletedClientMac).getAddressAsLong()));
+ } catch (Exception e) {
+ LOG.error("Error deleting AssociatedClient {}", deletedClientMac, e);
+ }
+
+ }
+
+}
diff --git a/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/OpensyncNode.java b/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/OpensyncNode.java
new file mode 100644
index 0000000..212db0a
--- /dev/null
+++ b/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/OpensyncNode.java
@@ -0,0 +1,283 @@
+package com.telecominfraproject.wlan.opensync.external.integration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.telecominfraproject.wlan.core.model.equipment.MacAddress;
+import com.telecominfraproject.wlan.core.model.equipment.RadioType;
+import com.telecominfraproject.wlan.core.model.json.BaseJsonModel;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAPInetState;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAPRadioState;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAPVIFState;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncAWLANNode;
+import com.telecominfraproject.wlan.opensync.external.integration.models.OpensyncWifiAssociatedClients;
+
+
+public class OpensyncNode extends BaseJsonModel {
+ private static final Logger LOG = LoggerFactory.getLogger(OpensyncNode.class);
+
+ private static final long serialVersionUID = 8388356663505464850L;
+
+ private String apId;
+ private OpensyncAWLANNode awlanNode = null;
+ private List radioStates = null;
+ private List inetStates = null;
+ private List vifStates = null;
+ private List wifiClients = null;
+ private int customerId;
+ private long equipmentId;
+
+ public OpensyncNode(String apId, OpensyncAWLANNode awlanNode, int customerId, long equipmentId) {
+ this.apId = apId;
+ this.awlanNode = awlanNode;
+ this.equipmentId = equipmentId;
+ this.customerId = customerId;
+ radioStates = new ArrayList();
+ inetStates = new ArrayList();
+ vifStates = new ArrayList();
+ wifiClients = new ArrayList();
+ }
+
+ public static long getSerialversionuid() {
+ return serialVersionUID;
+ }
+
+ public String getApId() {
+ return apId;
+ }
+
+ public int getCustomerId() {
+ return customerId;
+ }
+
+ public long getEquipmentId() {
+ return equipmentId;
+ }
+
+ public void updateAWLANNode(OpensyncAWLANNode awlanNode) {
+ this.awlanNode = awlanNode;
+ }
+
+ public OpensyncAWLANNode getAWLANNode() {
+ return awlanNode;
+ }
+
+ public void updateRadioState(OpensyncAPRadioState radioState) {
+
+ LOG.trace("Received Radio {}", radioState.toPrettyString());
+
+ if (radioState.getMac() == null || radioState.getIfName() == null || radioState.getFreqBand() == null)
+ return; // not ready
+ if (radioStates.isEmpty()) {
+ radioStates.add(radioState);
+ } else {
+ for (OpensyncAPRadioState radio : radioStates) {
+ if (radioState.getMac().equals(radio.getMac()) && radio.getFreqBand().equals(radioState.getFreqBand())
+ && radioState.getIfName().equals(radio.getIfName())) {
+ int index = radioStates.indexOf(radio);
+ radioStates.remove(index);
+ radioStates.add(radioState);
+ return;
+ }
+ }
+ // no matching radio present in table
+ radioStates.add(radioState);
+ }
+
+ }
+
+ public void updateVifState(OpensyncAPVIFState vifState) {
+
+ LOG.trace("Received {}", vifState.toPrettyString());
+ if (vifState.getChannel() == -1 || vifState.getSsid() == null || vifState.getIfName() == null)
+ return; // not ready
+
+ if (vifStates.isEmpty()) {
+ vifStates.add(vifState);
+ } else {
+ for (OpensyncAPVIFState vif : vifStates) {
+ if (vifState.getSsid().equals(vif.getSsid()) && vifState.getIfName().equals(vif.getIfName())) {
+ int index = vifStates.indexOf(vif);
+ vifStates.remove(index);
+ vifStates.add(vifState);
+ return;
+ }
+ }
+ // no matching radio present in table
+ vifStates.add(vifState);
+ }
+
+ }
+
+ public void updateInetState(OpensyncAPInetState inetState) {
+
+ if (inetState.getHwAddr() == null || inetState.getIfName() == null)
+ return;
+
+ if (inetStates.isEmpty()) {
+ inetStates.add(inetState);
+ } else {
+ for (OpensyncAPInetState inet : inetStates) {
+ if ((inetState.getHwAddr() != null && inet.getHwAddr().equals(inetState.getHwAddr()))
+ || inet.getIfName().equals(inetState.getIfName())) {
+ int index = inetStates.indexOf(inet);
+ inetStates.set(index, inetState);
+ return;
+ }
+ }
+ // no matching radio present in table
+ inetStates.add(inetState);
+
+ }
+ }
+
+ public void updateWifiClients(OpensyncWifiAssociatedClients wifiClient) {
+
+ if (wifiClient.getMac() == null || wifiClient.getState() == null)
+ return;
+
+ if (wifiClients.isEmpty()) {
+ wifiClients.add(wifiClient);
+ } else {
+ for (OpensyncWifiAssociatedClients client : wifiClients) {
+
+ if (wifiClient.getMac().equals(client.getMac())) {
+
+ int index = wifiClients.indexOf(client);
+ wifiClients.remove(index);
+ wifiClients.add(wifiClient);
+ return;
+
+ }
+
+ }
+
+ wifiClients.add(wifiClient);
+
+ }
+ }
+
+ public OpensyncAWLANNode getAwlanNode() {
+ return awlanNode;
+ }
+
+ public List getRadioStates() {
+ return radioStates;
+ }
+
+ public List getInetStates() {
+ return inetStates;
+ }
+
+ public List getVifStates() {
+ return vifStates;
+ }
+
+ public List getWifiClients() {
+ return wifiClients;
+ }
+
+ public OpensyncAPRadioState getRadioForMac(MacAddress mac) {
+ OpensyncAPRadioState ret = null;
+
+ for (OpensyncAPRadioState radio : radioStates) {
+ if (radio.getMac().equals(mac.toString())) {
+ ret = radio;
+ break;
+ }
+ }
+
+ return ret;
+ }
+
+ public OpensyncAPRadioState getRadioForChannel(int channel) {
+ OpensyncAPRadioState ret = null;
+
+ for (OpensyncAPRadioState radio : radioStates) {
+ if (radio.getChannel() == channel) {
+ ret = radio;
+ break;
+ }
+ }
+
+ return ret;
+ }
+
+ public OpensyncAPRadioState getRadioForBand(String freq_band) {
+ OpensyncAPRadioState ret = null;
+
+ for (OpensyncAPRadioState radio : radioStates) {
+ if (radio.getFreqBand().equals("5GL") && freq_band.equals(RadioType.is5GHz.toString())) {
+ ret = radio;
+ break;
+ } else if (radio.getFreqBand().equals("2.4G") && freq_band.equals(RadioType.is2dot4GHz.toString())) {
+ ret = radio;
+ break;
+ }
+ }
+ return ret;
+ }
+
+ public OpensyncAPVIFState getVIFForChannel(int channel) {
+ OpensyncAPVIFState ret = null;
+
+ for (OpensyncAPVIFState vif : vifStates) {
+ if (vif.getChannel() == channel) {
+ ret = vif;
+ break;
+ }
+ }
+
+ return ret;
+ }
+
+ public List getVIFsForSSID(String ssid) {
+ List ret = new ArrayList();
+
+ for (OpensyncAPVIFState vif : vifStates) {
+ if (vif.getSsid().equals(ssid)) {
+ ret.add(vif);
+ }
+ }
+
+ return ret;
+ }
+
+ public OpensyncAPInetState getInetForMac(MacAddress mac) {
+ OpensyncAPInetState ret = null;
+ for (OpensyncAPInetState inet : inetStates) {
+ if (inet.getHwAddr().equals(mac.toString())) {
+ ret = inet;
+ break;
+ }
+ }
+
+ return ret;
+ }
+
+ public boolean deleteWifiClient(String deletedClientMacAddress) {
+ for (OpensyncWifiAssociatedClients client : wifiClients) {
+ if (client.getMac().equals(deletedClientMacAddress)) {
+ wifiClients.remove(client);
+ return true;
+ }
+ }
+ // could not find this client
+ return false;
+ }
+
+ public boolean deleteVif(OpensyncAPVIFState toBeDeleted) {
+ for (OpensyncAPVIFState vif : vifStates) {
+ if (vif.getSsid().equals(toBeDeleted.getSsid()) && vif.getIfName().equals(toBeDeleted.getIfName())) {
+ vifStates.remove(vif);
+ return true;
+ }
+ }
+ // could not find this vif
+ return false;
+ }
+
+}
diff --git a/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/client/OpensyncCloudRestTemplatePostConfiguration.java b/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/client/OpensyncCloudRestTemplatePostConfiguration.java
new file mode 100644
index 0000000..2e47e17
--- /dev/null
+++ b/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/client/OpensyncCloudRestTemplatePostConfiguration.java
@@ -0,0 +1,55 @@
+package com.telecominfraproject.wlan.opensync.external.integration.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.client.AsyncRestTemplate;
+import org.springframework.web.client.RestTemplate;
+
+import com.telecominfraproject.wlan.core.model.json.BaseJsonModel;
+
+/**
+ * @author dtop
+ * Configure rest client to understand KDC classes that are children of KDC BaseJsonModel
+ */
+@Configuration
+@ConditionalOnClass(RestTemplate.class)
+public class OpensyncCloudRestTemplatePostConfiguration {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(OpensyncCloudRestTemplatePostConfiguration.class);
+
+
+ public OpensyncCloudRestTemplatePostConfiguration(@Autowired(required=false) AsyncRestTemplate asyncRestTemplate, @Autowired RestTemplate restTemplate) {
+ registerModulesWithObjectMappers(restTemplate);
+ if(asyncRestTemplate!=null) {
+ registerModulesWithObjectMappers(asyncRestTemplate);
+ }
+ }
+
+ public static void registerModulesWithObjectMappers(AsyncRestTemplate restT) {
+ //this is needed so that rest client can produce and consume JSON objects with (and without) _type property
+ for(@SuppressWarnings("rawtypes") HttpMessageConverter c: restT.getMessageConverters()){
+ if(c instanceof MappingJackson2HttpMessageConverter){
+ LOG.info("Configuring ObjectMapper on AsyncRestTemplate");
+ BaseJsonModel.registerAllSubtypes(((MappingJackson2HttpMessageConverter)c).getObjectMapper());
+ }
+ }
+ }
+
+ public static void registerModulesWithObjectMappers(RestTemplate restT) {
+ //this is needed so that rest client can produce and consume JSON objects with (and without) _type property
+ for(@SuppressWarnings("rawtypes") HttpMessageConverter c: restT.getMessageConverters()){
+ if(c instanceof MappingJackson2HttpMessageConverter){
+ LOG.info("Configuring ObjectMapper on RestTemplate");
+ BaseJsonModel.registerAllSubtypes(((MappingJackson2HttpMessageConverter)c).getObjectMapper());
+ }
+ }
+ }
+
+
+}
diff --git a/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/client/OpensyncCloudWebConfig.java b/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/client/OpensyncCloudWebConfig.java
new file mode 100644
index 0000000..97bcaff
--- /dev/null
+++ b/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/client/OpensyncCloudWebConfig.java
@@ -0,0 +1,50 @@
+package com.telecominfraproject.wlan.opensync.external.integration.client;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+import com.telecominfraproject.wlan.core.model.json.BaseJsonModel;
+
+
+/**
+ * @author dtoptygin
+ *
+ */
+@Configuration
+//@EnableWebMvc - DTOP: do not use this, it will break mapping for index.html file
+// see http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-spring-mvc-auto-configuration
+public class OpensyncCloudWebConfig extends WebMvcConfigurerAdapter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(OpensyncCloudWebConfig.class);
+
+ @Override
+ public void extendMessageConverters(List> converters) {
+ //this is needed so that servlets can consume and produce JSON objects with (and without) _type parameters
+ LOG.info("extending MessageConverters to understand KDC BaseJsonModel and its descendants");
+ for(HttpMessageConverter> c: converters){
+ if(c instanceof MappingJackson2HttpMessageConverter){
+ BaseJsonModel.registerAllSubtypes(((MappingJackson2HttpMessageConverter)c).getObjectMapper());
+ }
+ }
+ }
+
+ @Override
+ public void addFormatters(FormatterRegistry registry) {
+ // This is needed so that @RequestParam annotations in the servlet
+ // methods can be used with BaseJsonModel and its descendants and its
+ // collections.
+
+ //Use GenericlConverter here, simple one does not work
+ LOG.info("Adding custom converters to process KDC BaseJsonModel and its descendants");
+
+ registry.addConverter(new OpensyncCloudWebGenericConverter());
+ }
+
+}
diff --git a/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/client/OpensyncCloudWebGenericConverter.java b/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/client/OpensyncCloudWebGenericConverter.java
new file mode 100644
index 0000000..b54ab0a
--- /dev/null
+++ b/opensync-ext-cloud/src/main/java/com/telecominfraproject/wlan/opensync/external/integration/client/OpensyncCloudWebGenericConverter.java
@@ -0,0 +1,207 @@
+package com.telecominfraproject.wlan.opensync.external.integration.client;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.convert.TypeDescriptor;
+import org.springframework.core.convert.converter.GenericConverter;
+
+import com.telecominfraproject.wlan.core.model.json.BaseJsonModel;
+
+/**
+ * @author dtoptygin
+ *
+ */
+public class OpensyncCloudWebGenericConverter implements GenericConverter {
+ private static final Logger LOG = LoggerFactory.getLogger(OpensyncCloudWebGenericConverter.class);
+ private Set convertiblePairs;
+
+ /**
+ * Format the typeDescriptor for logging
+ * @param typeDescriptor
+ * @return
+ */
+ public static String getDataType(TypeDescriptor typeDescriptor) {
+ if (typeDescriptor == null) {
+ return "";
+ }
+ if (typeDescriptor.isCollection()) {
+ return typeDescriptor.getName() + "<" + getDataType(typeDescriptor.getElementTypeDescriptor()) + ">";
+ }
+ if (typeDescriptor.isArray()) {
+ return getDataType(typeDescriptor.getElementTypeDescriptor()) + "[]";
+ }
+ return typeDescriptor.getName();
+ }
+
+ /**
+ * Constructor
+ */
+ public OpensyncCloudWebGenericConverter() {
+ LOG.info("Registering Object Converter for the KDC models");
+ convertiblePairs = new HashSet<>();
+ convertiblePairs.add(new ConvertiblePair(String.class, com.telecominfraproject.wlan.core.model.json.BaseJsonModel.class));
+ convertiblePairs.add(new ConvertiblePair(String.class, List.class));
+ convertiblePairs.add(new ConvertiblePair(String.class, Set.class));
+ }
+
+ @Override
+ public Set getConvertibleTypes() {
+ return convertiblePairs;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Override
+ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
+ // dtop: this is ugly and needs to be generalized, but it works for
+ // List/Set of Integer/Long/BaseJsonModel/String
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("Attempting to convert '{}' from {} to {}", source, getDataType(sourceType),
+ getDataType(targetType));
+ }
+
+ if (targetType.isAssignableTo(TypeDescriptor.valueOf(String.class)) || targetType
+ .isAssignableTo(TypeDescriptor.collection(Collection.class, TypeDescriptor.valueOf(String.class)))) {
+ LOG.trace("Proceeding with conversion of String ... ");
+ Object ret = null;
+ if (targetType.isCollection()) {
+ if (targetType.getName().equals(List.class.getName())) {
+ ret = new ArrayList();
+ } else if (targetType.getName().equals(Set.class.getName())) {
+ ret = new HashSet();
+ } else {
+ throw new IllegalStateException("Unsupported collection type " + targetType.getName());
+ }
+
+ if (sourceType.isArray() || sourceType.isCollection()) {
+ for (Object obj : (Iterable