1 Commits

Author SHA1 Message Date
Jun Woo Shin
9b0f99c4dd make separate uCentral schema structure
Signed-off-by: Jun Woo Shin <jwoos@meta.com>
2022-10-21 14:37:08 -04:00
7 changed files with 272 additions and 159 deletions

View File

@@ -8,12 +8,14 @@
package com.facebook.openwifi.cloudsdk;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.facebook.openwifi.cloudsdk.models.ap.UCentralSchema;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
@@ -21,16 +23,16 @@ import com.google.gson.JsonObject;
*/
public class UCentralApConfiguration {
/** The raw configuration. */
private final JsonObject config;
private final UCentralSchema config;
/** Constructor from JSON string. */
public UCentralApConfiguration(String configJson) {
this.config = new Gson().fromJson(configJson, JsonObject.class);
this.config = new Gson().fromJson(configJson, UCentralSchema.class);
}
/** Constructor from JsonObject (makes deep copy). */
/** Constructor from JsonObject */
public UCentralApConfiguration(JsonObject config) {
this.config = config.deepCopy();
this.config = new Gson().fromJson(config, UCentralSchema.class);
}
@Override
@@ -45,22 +47,21 @@ public class UCentralApConfiguration {
/** Return the number of radios, or -1 if the field is missing/malformed. */
public int getRadioCount() {
if (!config.has("radios") || !config.get("radios").isJsonArray()) {
if (config.radios == null) {
return -1;
}
return config.getAsJsonArray("radios").size();
return config.radios.size();
}
/** Return all info in the radio config (or an empty array if none). */
public JsonArray getRadioConfigList() {
if (!config.has("radios") || !config.get("radios").isJsonArray()) {
return new JsonArray();
}
return config.getAsJsonArray("radios");
public List<UCentralSchema.Radio> getRadioConfigList() {
return config.radios;
}
/** Return all the operational bands of an AP (from the radio config) */
public Set<String> getRadioBandsSet(JsonArray radioConfigList) {
public Set<String> getRadioBandsSet(
List<UCentralSchema.Radio> radioConfigList
) {
Set<String> radioBandsSet = new HashSet<>();
if (radioConfigList == null) {
return radioBandsSet;
@@ -69,46 +70,39 @@ public class UCentralApConfiguration {
int radioIndex = 0; radioIndex < radioConfigList.size();
radioIndex++
) {
JsonElement e = radioConfigList.get(radioIndex);
if (!e.isJsonObject()) {
UCentralSchema.Radio radio = radioConfigList.get(radioIndex);
if (radio == null || radio.band == null) {
continue;
}
JsonObject radioObject = e.getAsJsonObject();
if (!radioObject.has("band")) {
continue;
}
radioBandsSet.add(radioObject.get("band").getAsString());
radioBandsSet.add(radio.band);
}
return radioBandsSet;
}
/** Return the radio config at the given index, or null if invalid. */
public JsonObject getRadioConfig(int index) {
public UCentralSchema.Radio getRadioConfig(int index) {
if (getRadioCount() < index) {
return null;
}
JsonArray radios = config.getAsJsonArray("radios");
List<UCentralSchema.Radio> radios = config.radios;
if (radios == null) {
return null;
}
JsonElement e = radios.get(index);
if (!e.isJsonObject()) {
return null;
}
return e.getAsJsonObject();
UCentralSchema.Radio radio = radios.get(index);
return radio;
}
/** Set radio config at the given index. Adds empty objects as needed. */
public void setRadioConfig(int index, JsonObject radioConfig) {
public void setRadioConfig(int index, UCentralSchema.Radio radioConfig) {
int radioCount = getRadioCount();
if (radioCount == -1) {
config.add("radios", new JsonArray());
config.radios = new ArrayList<UCentralSchema.Radio>();
radioCount = 0;
}
JsonArray radios = config.getAsJsonArray("radios");
List<UCentralSchema.Radio> radios = config.radios;
for (int i = radioCount; i <= index; i++) {
// insert empty objects as needed
radios.add(new JsonObject());
radios.add(new UCentralSchema.Radio());
}
radios.set(index, radioConfig);
}
@@ -119,11 +113,7 @@ public class UCentralApConfiguration {
*/
public int getStatisticsInterval() {
try {
return config
.getAsJsonObject("metrics")
.getAsJsonObject("statistics")
.get("interval")
.getAsInt();
return config.metrics.statistics.interval;
} catch (Exception e) {
return -1;
}
@@ -131,17 +121,14 @@ public class UCentralApConfiguration {
/** Set the statistics interval to the given value (in seconds). */
public void setStatisticsInterval(int intervalSec) {
if (!config.has("metrics") || !config.get("metrics").isJsonObject()) {
config.add("metrics", new JsonObject());
if (config.metrics == null) {
config.metrics = new UCentralSchema.Metrics();
}
JsonObject metrics = config.getAsJsonObject("metrics");
if (
!metrics.has("statistics") ||
!metrics.get("statistics").isJsonObject()
config.metrics.statistics == null
) {
metrics.add("statistics", new JsonObject());
config.metrics.statistics = new UCentralSchema.Metrics.Statistics();
}
JsonObject statistics = metrics.getAsJsonObject("statistics");
statistics.addProperty("interval", intervalSec);
config.metrics.statistics.interval = intervalSec;
}
}

View File

@@ -27,10 +27,12 @@ import com.facebook.openwifi.cloudsdk.ies.LocalPowerConstraint;
import com.facebook.openwifi.cloudsdk.ies.QbssLoad;
import com.facebook.openwifi.cloudsdk.ies.TxPwrInfo;
import com.facebook.openwifi.cloudsdk.models.ap.State;
import com.facebook.openwifi.cloudsdk.models.ap.UCentralSchema;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
/**
* uCentral utility methods/structures.
@@ -176,62 +178,110 @@ public class UCentralUtils {
entry.ieContainer = ieContainer;
}
/**
* Set all radios config channel of an AP to a given value.
*
* Returns true if changed, or false if unchanged for any reason.
*/
public static boolean setRadioConfigChannel(
String serialNumber,
UCentralApConfiguration config,
Map<String, Integer> newValueList
) {
return setRadioConfigField(
serialNumber,
config,
"channel",
newValueList
);
}
/**
* Set all radios config tx power of an AP to a given value.
*
* Returns true if changed, or false if unchanged for any reason.
*/
public static boolean setRadioConfigTxPower(
String serialNumber,
UCentralApConfiguration config,
Map<String, Integer> newValueList
) {
return setRadioConfigField(
serialNumber,
config,
"tx-power",
newValueList
);
}
/**
* Set all radios config of an AP to a given value.
*
* Returns true if changed, or false if unchanged for any reason.
*/
public static boolean setRadioConfigField(
private static boolean setRadioConfigField(
String serialNumber,
UCentralApConfiguration config,
String fieldName,
Map<String, Integer> newValueList
) {
boolean wasModified = false;
int radioCount = config.getRadioCount();
// Iterate all the radios of an AP to find the corresponding band
for (int radioIndex = 0; radioIndex < radioCount; radioIndex++) {
JsonObject radioConfig = config.getRadioConfig(radioIndex);
for (
int radioIndex = 0; radioIndex < config.getRadioCount();
radioIndex++
) {
UCentralSchema.Radio radioConfig =
config.getRadioConfig(radioIndex);
if (radioConfig == null) {
continue;
}
String operationalBand = radioConfig.get("band").getAsString();
String operationalBand = radioConfig.band;
if (!newValueList.containsKey(operationalBand)) {
continue;
}
// If the field doesn't exist in config, we generate the fieldName and
// If the fieldName doesn't exist in config, we generate the fieldName and
// assign the new value to it.
int newValue = newValueList.get(operationalBand);
if (!radioConfig.has(fieldName)) {
radioConfig.addProperty(fieldName, newValue);
config.setRadioConfig(radioIndex, radioConfig);
Integer currentValue = null;
switch (fieldName) {
case "channel":
if (
radioConfig.channel == null ||
radioConfig.channel.isString()
) {
wasModified = true;
} else {
currentValue = radioConfig.channel.getAsInt();
if (currentValue != newValue) {
wasModified = true;
}
}
radioConfig.channel = new JsonPrimitive(newValue);
break;
case "tx-power":
currentValue = radioConfig.txPower;
if (currentValue != newValue) {
radioConfig.txPower = newValue;
wasModified = true;
}
break;
}
if (wasModified) {
logger.info(
"Device {}: setting {} {} to {} (was empty)",
"Device {}: setting {} {} to {} (was {})",
serialNumber,
operationalBand,
fieldName,
newValue
);
wasModified = true;
continue;
}
// Compare vs. existing value.
// not all values are int so override those values
Integer currentValue = null;
JsonElement fieldValue = radioConfig.get(fieldName);
if (
fieldValue.isJsonPrimitive() &&
fieldValue.getAsJsonPrimitive().isNumber()
) {
currentValue = fieldValue.getAsInt();
} else {
logger.debug(
"Unable to get field '{}' as int, value was {}",
fieldName,
fieldValue.toString()
newValue,
currentValue
);
}
@@ -243,21 +293,9 @@ public class UCentralUtils {
fieldName,
newValue
);
} else {
// Update to new value
radioConfig.addProperty(fieldName, newValue);
config.setRadioConfig(radioIndex, radioConfig);
logger.info(
"Device {}: setting {} {} to {} (was {})",
serialNumber,
operationalBand,
fieldName,
newValue,
currentValue != null ? currentValue : fieldValue.toString()
);
wasModified = true;
}
}
return wasModified;
}
@@ -269,22 +307,18 @@ public class UCentralUtils {
* Returns the results map
*/
public static Map<String, List<String>> getBandsMap(
Map<String, JsonArray> deviceStatus
Map<String, List<UCentralSchema.Radio>> deviceStatus
) {
Map<String, List<String>> bandsMap = new HashMap<>();
for (String serialNumber : deviceStatus.keySet()) {
JsonArray radioList =
deviceStatus.get(serialNumber).getAsJsonArray();
List<UCentralSchema.Radio> radioList =
deviceStatus.get(serialNumber);
for (
int radioIndex = 0; radioIndex < radioList.size(); radioIndex++
) {
JsonElement e = radioList.get(radioIndex);
if (!e.isJsonObject()) {
return null;
}
JsonObject radioObject = e.getAsJsonObject();
String band = radioObject.get("band").getAsString();
UCentralSchema.Radio radio = radioList.get(radioIndex);
String band = radio.band;
bandsMap
.computeIfAbsent(band, k -> new ArrayList<>())
.add(serialNumber);
@@ -304,7 +338,7 @@ public class UCentralUtils {
* @return the results map of {band, {device, list of available channels}}
*/
public static Map<String, Map<String, List<Integer>>> getDeviceAvailableChannels(
Map<String, JsonArray> deviceStatus,
Map<String, List<UCentralSchema.Radio>> deviceStatus,
Map<String, Map<String, Capabilities.Phy>> deviceCapabilities,
Map<String, List<Integer>> defaultAvailableChannels
) {
@@ -312,18 +346,13 @@ public class UCentralUtils {
new HashMap<>();
for (String serialNumber : deviceStatus.keySet()) {
JsonArray radioList =
deviceStatus.get(serialNumber).getAsJsonArray();
List<UCentralSchema.Radio> radioList =
deviceStatus.get(serialNumber);
for (
int radioIndex = 0; radioIndex < radioList.size(); radioIndex++
) {
JsonElement e = radioList.get(radioIndex);
if (!e.isJsonObject()) {
return null;
}
JsonObject radioObject = e.getAsJsonObject();
String band = radioObject.get("band").getAsString();
UCentralSchema.Radio radio = radioList.get(radioIndex);
String band = radio.band;
Map<String, Capabilities.Phy> capabilitiesPhyMap =
deviceCapabilities.get(serialNumber);
List<Integer> availableChannels = new ArrayList<>();

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.openwifi.cloudsdk.models.ap;
import java.util.List;
import com.google.gson.JsonPrimitive;
import com.google.gson.annotations.SerializedName;
public class UCentralSchema {
public static class Radio {
public String band;
public int bandwidth;
// either "auto" or int
public JsonPrimitive channel;
@SerializedName("valid-channels") public int[] validChannels;
public String country;
@SerializedName("allow-dfs") public boolean allowDfs = true;
@SerializedName("channel-mode") public String channelMode = "HE";
@SerializedName("channel-wdith") public int channelWidth = 80;
@SerializedName("require-mode") public String requireMode;
public String mimo;
@SerializedName("tx-power") public int txPower;
@SerializedName("legacy-rates") public boolean legacyRates = false;
@SerializedName("beacon-interval") public int beaconInterval = 100;
@SerializedName("dtim-period") public int dtimPeriod = 2;
@SerializedName("maximum-clients") public int maximumClients;
public static class Rates {
public int beacon = 6000;
public int multicast = 24000;
}
public Rates rates;
public static class HESettings {
@SerializedName(
"multiple-bssid"
) public boolean multipleBssid = false;
public boolean ema = false;
@SerializedName("bss-color") public int bssColor = 64;
}
@SerializedName("he-settings") public HESettings heSettings;
@SerializedName("hostapd-iface-raw") public String[] hostapdIfaceRaw;
}
public List<Radio> radios;
public static class Metrics {
public static class Statistics {
public int interval;
public List<String> types;
}
public Statistics statistics;
public static class Health {
public int interval;
}
public Health health;
public static class WifiFrames {
public List<String> filters;
}
@SerializedName("wifi-frames") public WifiFrames wifiFrames;
public static class DhcpSnooping {
public List<String> filters;
}
@SerializedName("dhcp-snooping") public DhcpSnooping dhcpSnooping;
}
public Metrics metrics;
// metrics
// TODO the below fields are unused right now - include them as necessary
// unit
// globals
// definitions
// ethernet
// switch
// interfaces
// services
}

View File

@@ -35,10 +35,10 @@ public class UCentralUtilsTest {
"{\"interfaces\": [], \"radios\": [{\"band\": \"5G\", \"channel\": \"auto\"}]}"
);
boolean modified = UCentralUtils
.setRadioConfigField(serialNumber, config, "channel", newValueList);
.setRadioConfigChannel(serialNumber, config, newValueList);
assertTrue(modified);
assertEquals(
config.getRadioConfig(0).get("channel").getAsInt(),
config.getRadioConfig(0).channel.getAsInt(),
expectedChannel
);
@@ -47,10 +47,10 @@ public class UCentralUtilsTest {
"{\"interfaces\": [], \"radios\": [{\"band\": \"5G\"}]}"
);
modified = UCentralUtils
.setRadioConfigField(serialNumber, config, "channel", newValueList);
.setRadioConfigChannel(serialNumber, config, newValueList);
assertTrue(modified);
assertEquals(
config.getRadioConfig(0).get("channel").getAsInt(),
config.getRadioConfig(0).channel.getAsInt(),
expectedChannel
);
@@ -59,10 +59,10 @@ public class UCentralUtilsTest {
"{\"interfaces\": [], \"radios\": [{\"band\": \"5G\", \"channel\": 1}]}"
);
modified = UCentralUtils
.setRadioConfigField(serialNumber, config, "channel", newValueList);
.setRadioConfigChannel(serialNumber, config, newValueList);
assertFalse(modified);
assertEquals(
config.getRadioConfig(0).get("channel").getAsInt(),
config.getRadioConfig(0).channel.getAsInt(),
expectedChannel
);
@@ -71,10 +71,10 @@ public class UCentralUtilsTest {
"{\"interfaces\": [], \"radios\": [{\"band\": \"5G\", \"channel\": 15}]}"
);
modified = UCentralUtils
.setRadioConfigField(serialNumber, config, "channel", newValueList);
.setRadioConfigChannel(serialNumber, config, newValueList);
assertTrue(modified);
assertEquals(
config.getRadioConfig(0).get("channel").getAsInt(),
config.getRadioConfig(0).channel.getAsInt(),
expectedChannel
);
}

View File

@@ -329,10 +329,9 @@ public class ConfigManager implements Runnable {
channelList.putAll(deviceConfig.userChannels);
}
if (!channelList.isEmpty()) {
modified |= UCentralUtils.setRadioConfigField(
modified |= UCentralUtils.setRadioConfigChannel(
serialNumber,
config,
"channel",
channelList
);
}
@@ -346,10 +345,9 @@ public class ConfigManager implements Runnable {
txPowerList.putAll(deviceConfig.userTxPowers);
}
if (!txPowerList.isEmpty()) {
modified |= UCentralUtils.setRadioConfigField(
modified |= UCentralUtils.setRadioConfigTxPower(
serialNumber,
config,
"tx-power",
txPowerList
);
}

View File

@@ -28,6 +28,7 @@ import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer.KafkaRecord;
import com.facebook.openwifi.cloudsdk.models.ap.Capabilities;
import com.facebook.openwifi.cloudsdk.models.ap.State;
import com.facebook.openwifi.cloudsdk.models.ap.UCentralSchema;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceCapabilities;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus;
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent;
@@ -37,7 +38,6 @@ import com.facebook.openwifi.rrm.DeviceDataManager;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ModelerParams;
import com.facebook.openwifi.rrm.Utils;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
@@ -99,7 +99,7 @@ public class Modeler implements Runnable {
new ConcurrentHashMap<>();
/** List of radio info per device. */
public Map<String, JsonArray> latestDeviceStatusRadios =
public Map<String, List<UCentralSchema.Radio>> latestDeviceStatusRadios =
new ConcurrentHashMap<>();
/** List of capabilities per device. */
@@ -391,10 +391,11 @@ public class Modeler implements Runnable {
UCentralApConfiguration config
) {
// Get old vs new radios info and store the new radios info
JsonArray newRadioList = config.getRadioConfigList();
List<UCentralSchema.Radio> newRadioList = config.getRadioConfigList();
Set<String> newRadioBandsSet = config.getRadioBandsSet(newRadioList);
JsonArray oldRadioList = dataModel.latestDeviceStatusRadios
.put(serialNumber, newRadioList);
List<UCentralSchema.Radio> oldRadioList =
dataModel.latestDeviceStatusRadios
.put(serialNumber, newRadioList);
Set<String> oldRadioBandsSet = config.getRadioBandsSet(oldRadioList);
// Print info only when there are any updates

View File

@@ -24,10 +24,11 @@ import com.facebook.openwifi.cloudsdk.UCentralUtils;
import com.facebook.openwifi.cloudsdk.WifiScanEntry;
import com.facebook.openwifi.cloudsdk.models.ap.Capabilities;
import com.facebook.openwifi.cloudsdk.models.ap.State;
import com.facebook.openwifi.cloudsdk.models.ap.UCentralSchema;
import com.facebook.openwifi.rrm.DeviceTopology;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
public class TestUtils {
/** The Gson instance. */
@@ -104,34 +105,34 @@ public class TestUtils {
* @param band band (e.g., "2G")
* @param channel channel number
* @param channelWidth channel width in MHz
* @return a radio info object as a {@code JsonObject}
* @return a radio info object as a {@code UCentralSchema.Radio}
*/
private static JsonObject createDeviceStatusRadioObject(
private static UCentralSchema.Radio createDeviceStatusRadioObject(
String band,
int channel,
int channelWidth,
int txPower
) {
return gson.fromJson(
String.format(
"{\"band\": %s,\"channel\": %d,\"channel-mode\":\"HE\"," +
"\"channel-width\":%d,\"country\":\"CA\",\"tx-power\":%d}",
band,
channel,
channelWidth,
txPower
),
JsonObject.class
);
UCentralSchema.Radio radio = new UCentralSchema.Radio();
radio.band = band;
radio.channel = new JsonPrimitive(channel);
radio.channelMode = "HE";
radio.channelWidth = channelWidth;
radio.country = "CA";
radio.txPower = txPower;
return radio;
}
/**
* Create an array with one radio info entry with the given channel on a
* given band.
*/
public static JsonArray createDeviceStatus(String band, int channel) {
JsonArray jsonList = new JsonArray();
jsonList.add(
public static List<UCentralSchema.Radio> createDeviceStatus(
String band,
int channel
) {
List<UCentralSchema.Radio> radios = new ArrayList<>();
radios.add(
createDeviceStatusRadioObject(
band,
channel,
@@ -139,7 +140,7 @@ public class TestUtils {
DEFAULT_TX_POWER
)
);
return jsonList;
return radios;
}
/**
@@ -151,13 +152,13 @@ public class TestUtils {
* @return an array with one radio info entry with the given band, channel,
* and tx power
*/
public static JsonArray createDeviceStatus(
public static List<UCentralSchema.Radio> createDeviceStatus(
String band,
int channel,
int txPower
) {
JsonArray jsonList = new JsonArray();
jsonList.add(
List<UCentralSchema.Radio> radios = new ArrayList<>();
radios.add(
createDeviceStatusRadioObject(
band,
channel,
@@ -165,18 +166,20 @@ public class TestUtils {
txPower
)
);
return jsonList;
return radios;
}
/**
* Create an array with one radio info entry per given band (using the
* lowest channel).
*/
public static JsonArray createDeviceStatus(List<String> bands) {
JsonArray jsonList = new JsonArray();
public static List<UCentralSchema.Radio> createDeviceStatus(
List<String> bands
) {
List<UCentralSchema.Radio> radios = new ArrayList<>();
for (String band : bands) {
int channel = UCentralUtils.getLowerChannelLimit(band);
jsonList.add(
radios.add(
createDeviceStatusRadioObject(
band,
channel,
@@ -185,19 +188,19 @@ public class TestUtils {
)
);
}
return jsonList;
return radios;
}
/**
* Create an array with one radio info entry with the given tx power and
* channel.
*/
public static JsonArray createDeviceStatusSingleBand(
public static List<UCentralSchema.Radio> createDeviceStatusSingleBand(
int channel,
int txPower2G
) {
JsonArray jsonList = new JsonArray();
jsonList.add(
List<UCentralSchema.Radio> radios = new ArrayList<>();
radios.add(
createDeviceStatusRadioObject(
channelToLowestMatchingBand(channel),
channel,
@@ -205,21 +208,21 @@ public class TestUtils {
txPower2G
)
);
return jsonList;
return radios;
}
/**
* Create an array with two radio info entries (2G and 5G), with the given
* tx powers and channels.
*/
public static JsonArray createDeviceStatusDualBand(
public static List<UCentralSchema.Radio> createDeviceStatusDualBand(
int channel2G,
int txPower2G,
int channel5G,
int txPower5G
) {
JsonArray jsonList = new JsonArray();
jsonList.add(
List<UCentralSchema.Radio> radios = new ArrayList<>();
radios.add(
createDeviceStatusRadioObject(
UCentralConstants.BAND_2G,
channel2G,
@@ -227,7 +230,7 @@ public class TestUtils {
txPower2G
)
);
jsonList.add(
radios.add(
createDeviceStatusRadioObject(
UCentralConstants.BAND_5G,
channel5G,
@@ -235,7 +238,7 @@ public class TestUtils {
txPower5G
)
);
return jsonList;
return radios;
}
/** Create a wifi scan entry with the given channel. */