6 Commits

Author SHA1 Message Date
zhiqiand
6fb30ce7e4 add lastAccess and created
Signed-off-by: zhiqiand <zhiqian@fb.com>
2022-10-05 20:30:25 -07:00
zhiqiand
350a45b616 fix refresh token logic and sychronizing
Signed-off-by: zhiqiand <zhiqian@fb.com>
2022-10-04 19:00:24 -07:00
zhiqiand
52dae760d8 fix some comments
Signed-off-by: zhiqiand <zhiqian@fb.com>
2022-10-04 16:08:03 -07:00
zhiqiand
343fc7b6ee fix comments on thread-safe and getting refreshAccessToken called
Signed-off-by: zhiqiand <zhiqian@fb.com>
2022-10-04 16:08:03 -07:00
zhiqiand
2a952f56a9 fix some comments
Signed-off-by: zhiqiand <zhiqian@fb.com>
2022-10-04 16:08:03 -07:00
zhiqiand
52a2258c2d initial commit
Signed-off-by: zhiqiand <zhiqian@fb.com>
2022-10-04 16:08:03 -07:00
140 changed files with 1427 additions and 3461 deletions

View File

@@ -18,7 +18,7 @@ jobs:
distribution: 'adopt' distribution: 'adopt'
cache: maven cache: maven
- name: Build with Maven - name: Build with Maven
run: mvn javadoc:aggregate run: mvn javadoc:javadoc
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3 uses: peaceiris/actions-gh-pages@v3
with: with:

11
.gitignore vendored
View File

@@ -1,9 +1,12 @@
/target/ /target
*/target/ /*.log*
/device_config.json
/settings.json
/topology.json
# Eclipse # Eclipse
.settings/ /.settings/
bin/ /bin/
.metadata .metadata
.classpath .classpath
.project .project

View File

@@ -1,7 +1,7 @@
FROM maven:3-eclipse-temurin-11 as build FROM maven:3-jdk-11 as build
WORKDIR /usr/src/java WORKDIR /usr/src/java
COPY . . COPY . .
RUN mvn clean package -pl owrrm -am -DappendVersionString="$(./scripts/get_build_version.sh)" RUN mvn clean package -DappendVersionString="$(./scripts/get_build_version.sh)"
FROM adoptopenjdk/openjdk11-openj9:latest FROM adoptopenjdk/openjdk11-openj9:latest
RUN apt-get update && apt-get install -y gettext-base wget RUN apt-get update && apt-get install -y gettext-base wget
@@ -10,8 +10,8 @@ RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentr
RUN mkdir /owrrm-data RUN mkdir /owrrm-data
WORKDIR /usr/src/java WORKDIR /usr/src/java
COPY docker-entrypoint.sh / COPY docker-entrypoint.sh /
COPY --from=build /usr/src/java/owrrm/target/openwifi-rrm.jar /usr/local/bin/ COPY --from=build /usr/src/java/target/openwifi-rrm.jar /usr/local/bin/
EXPOSE 16789 16790 EXPOSE 16789
ENTRYPOINT ["/docker-entrypoint.sh"] ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["java", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", \ CMD ["java", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", \
"-jar", "/usr/local/bin/openwifi-rrm.jar", \ "-jar", "/usr/local/bin/openwifi-rrm.jar", \

View File

@@ -1,10 +1,12 @@
# OpenWiFi RRM Service # OpenWiFi RRM Service
[See here](owrrm/README.md) for details. OpenWiFi uCentral-based radio resource management (RRM) service, providing a
cloud-based Wi-Fi RRM layer for APs running the OpenWiFi SDK.
## Project Structure This service collects data from OpenWiFi APs (e.g. Wi-Fi scans, stats,
This is an [Apache Maven] project with the following modules: capabilities) via the uCentral Gateway and Kafka, and integrates with the
* `lib-cloudsdk` - OpenWiFi CloudSDK Java Library OpenWiFi Provisioning service to perform optimization across configured
* `owrrm` - OpenWiFi RRM Service "venues". It pushes new device configuration parameters to APs after RRM
algorithms are run (manually or periodically).
## Requirements ## Requirements
* **Running:** JRE 11. * **Running:** JRE 11.
@@ -14,7 +16,7 @@ This is an [Apache Maven] project with the following modules:
``` ```
$ mvn package [-DskipTests] $ mvn package [-DskipTests]
``` ```
This will build a runnable JAR located at `owrrm/target/openwifi-rrm.jar`. This will build a runnable JAR located at `target/openwifi-rrm.jar`.
Alternatively, Docker builds can be launched using the provided Alternatively, Docker builds can be launched using the provided
[Dockerfile](Dockerfile). [Dockerfile](Dockerfile).
@@ -25,7 +27,34 @@ $ mvn test
``` ```
Unit tests are written using [JUnit 5]. Unit tests are written using [JUnit 5].
## Code Style ## Usage
```
$ java -jar openwifi-rrm.jar [-h]
```
To start the service, use the `run` command while providing configuration via
either environment variables (`--config-env`) or a static JSON file
(`--config-file`, default `settings.json`). The following data is *required*:
* Service configuration
* Env: `SERVICECONFIG_PRIVATEENDPOINT`, `SERVICECONFIG_PUBLICENDPOINT`
* JSON: `serviceConfig` structure
* Kafka broker URL
* Env: `KAFKACONFIG_BOOTSTRAPSERVER`
* JSON: `kafkaConfig` structure
* MySQL database credentials
* Env: `DATABASECONFIG_SERVER`, `DATABASECONFIG_USER`, `DATABASECONFIG_PASSWORD`
* JSON: `databaseConfig` structure
## OpenAPI
This service provides an OpenAPI HTTP interface on the port specified in the
service configuration (`moduleConfig.apiServerParams`). An auto-generated
OpenAPI 3.0 document is hosted at the endpoints `/openapi.{yaml,json}` and is
written to [openapi.yaml](openapi.yaml) during the Maven "compile" phase.
## For Developers
See [IMPLEMENTATION.md](IMPLEMENTATION.md) for service architecture details and
[ALGORITHMS.md](ALGORITHMS.md) for descriptions of the RRM algorithms.
Code is auto-formatted using [Spotless] with a custom Eclipse style config (see Code is auto-formatted using [Spotless] with a custom Eclipse style config (see
[spotless/eclipse-java-formatter.xml](spotless/eclipse-java-formatter.xml)). [spotless/eclipse-java-formatter.xml](spotless/eclipse-java-formatter.xml)).
This can be applied via Maven (but is *not* enforced at build time): This can be applied via Maven (but is *not* enforced at build time):

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -29,8 +29,8 @@ services:
targetPort: 16789 targetPort: 16789
protocol: TCP protocol: TCP
restapiinternal: restapiinternal:
servicePort: 16790 servicePort: 17007
targetPort: 16790 targetPort: 17007
protocol: TCP protocol: TCP
checks: checks:

View File

@@ -1,3 +0,0 @@
# OpenWiFi CloudSDK Java Library
A Java library providing clients and models for the OpenWiFi uCentral-based
CloudSDK.

View File

@@ -1,80 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>openwifi-cloudsdk</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>com.facebook</groupId>
<artifactId>openwifi-base</artifactId>
<version>2.7.0</version>
</parent>
<properties>
<!-- Hack for static files located in root project -->
<myproject.root>${project.basedir}/..</myproject.root>
</properties>
<build>
<finalName>openwifi-cloudsdk</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.konghq</groupId>
<artifactId>unirest-java</artifactId>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,201 +0,0 @@
/*
* 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;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.facebook.openwifi.cloudsdk.models.ap.State.Interface.SSID.Association;
import com.facebook.openwifi.cloudsdk.models.ap.State.Interface.SSID.Association.Rate;
/**
* Aggregation model for State aggregation. Only contains info useful for
* analysis.
*/
public class AggregatedState {
/** Rate information with aggregated fields. */
public static class AggregatedRate {
/**
* This is the common bitRate for all the aggregated fields.
*/
public long bitRate;
/**
* This is the common channel width for all the aggregated fields.
*/
public int chWidth;
/**
* Aggregated fields mcs
*/
public List<Integer> mcs = new ArrayList<>();
/** Constructor with no args */
private AggregatedRate() {}
/** Add a Rate to the AggregatedRate */
private void add(Rate rate) {
if (rate == null) {
return;
}
if (mcs.isEmpty()) {
bitRate = rate.bitrate;
chWidth = rate.chwidth;
}
mcs.add(rate.mcs);
}
/**
* Add an AggregatedRate with the same channel_width to the
* AggregatedRate
*/
private void add(AggregatedRate rate) {
if (rate == null || rate.chWidth != chWidth) {
return;
}
if (mcs.isEmpty()) {
bitRate = rate.bitRate;
chWidth = rate.chWidth;
}
mcs.addAll(rate.mcs);
}
}
/**
* Radio information with channel, channel_width and tx_power.
*/
public static class Radio {
public int channel;
public int channelWidth;
public int txPower;
private Radio() {}
public Radio(int channel, int channelWidth, int txPower) {
this.channel = channel;
this.channelWidth = channelWidth;
this.txPower = txPower;
}
private Radio(Map<String, Integer> radioInfo) {
channel = radioInfo.getOrDefault("channel", -1);
channelWidth = radioInfo.getOrDefault("channel_width", -1);
txPower = radioInfo.getOrDefault("tx_power", -1);
}
@Override
public int hashCode() {
return Objects.hash(channel, channelWidth, txPower);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Radio other = (Radio) obj;
return channel == other.channel &&
channelWidth == other.channelWidth && txPower == other.txPower;
}
}
public String bssid;
public String station;
public long connected;
public long inactive;
public List<Integer> rssi;
public long rxBytes;
public long rxPackets;
public AggregatedRate rxRate;
public long txBytes;
public long txDuration;
public long txFailed;
public long txPackets;
public AggregatedRate txRate;
public long txRetries;
public int ackSignal;
public int ackSignalAvg;
public Radio radio;
/** Constructor with no args */
public AggregatedState() {
this.rxRate = new AggregatedRate();
this.txRate = new AggregatedRate();
this.rssi = new ArrayList<>();
this.radio = new Radio();
}
/** Construct from Association and radio */
public AggregatedState(
Association association,
Map<String, Integer> radioInfo
) {
this.rxRate = new AggregatedRate();
this.txRate = new AggregatedRate();
this.rssi = new ArrayList<>();
this.bssid = association.bssid;
this.station = association.station;
this.connected = association.connected;
this.inactive = association.inactive;
this.rssi.add(association.rssi);
this.rxBytes = association.rx_bytes;
this.rxPackets = association.rx_packets;
this.rxRate.add(association.rx_rate);
this.txBytes = association.tx_bytes;
this.txDuration = association.tx_duration;
this.txFailed = association.tx_failed;
this.txPackets = association.tx_packets;
this.txRate.add(association.tx_rate);
this.txRetries = association.tx_retries;
this.ackSignal = association.ack_signal;
this.ackSignalAvg = association.ack_signal_avg;
this.radio = new Radio(radioInfo);
}
/**
* Check whether the passed-in AggregatedState and this one match for aggregation.
* If the two match in bssid, station and radio. Then they could be aggregated.
*
* @param state the reference AggregatedState with which to check with.
* @return boolean return true if the two matches for aggregation.
*/
public boolean matchesForAggregation(AggregatedState state) {
return bssid == state.bssid && station == state.station &&
Objects.equals(radio, state.radio);
}
/**
* Add an AggregatedState to this AggregatedState. Succeed only when the two
* match for aggregation.
*
* @param state input AggregatedState
* @return boolean true if the two match in bssid, station, channel,
* channel_width and tx_power
*/
public boolean add(AggregatedState state) {
if (matchesForAggregation(state)) {
this.rssi.addAll(state.rssi);
this.rxRate.add(state.rxRate);
this.txRate.add(state.txRate);
return true;
}
return false;
}
}

View File

@@ -1,6 +0,0 @@
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd'T'HH:mm:ss.SSS} %-5p [%c{1}:%L] - %m%n

4
owrrm/.gitignore vendored
View File

@@ -1,4 +0,0 @@
/*.log*
/device_config.json
/settings.json
/topology.json

View File

@@ -1,39 +0,0 @@
# OpenWiFi RRM Service
OpenWiFi uCentral-based radio resource management (RRM) service, providing a
cloud-based Wi-Fi RRM layer for APs running the OpenWiFi SDK.
This service collects data from OpenWiFi APs (e.g. Wi-Fi scans, stats,
capabilities) via the uCentral Gateway and Kafka, and integrates with the
OpenWiFi Provisioning service to perform optimization across configured
"venues". It pushes new device configuration parameters to APs after RRM
algorithms are run (manually or periodically).
See [IMPLEMENTATION.md](IMPLEMENTATION.md) for service architecture details and
[ALGORITHMS.md](ALGORITHMS.md) for descriptions of the RRM algorithms.
## Usage
```
$ java -jar openwifi-rrm.jar [-h]
```
To start the service, use the `run` command while providing configuration via
either environment variables (`--config-env`) or a static JSON file
(`--config-file`, default `settings.json`). The following data is *required*:
* Service configuration
* Env: `SERVICECONFIG_PRIVATEENDPOINT`, `SERVICECONFIG_PUBLICENDPOINT`
* JSON: `serviceConfig` structure
* Kafka broker URL
* Env: `KAFKACONFIG_BOOTSTRAPSERVER`
* JSON: `kafkaConfig` structure
* MySQL database credentials
* Env: `DATABASECONFIG_SERVER`, `DATABASECONFIG_USER`, `DATABASECONFIG_PASSWORD`
* JSON: `databaseConfig` structure
## OpenAPI
This service provides an OpenAPI HTTP interface on the port specified in the
service configuration (`moduleConfig.apiServerParams`). An auto-generated
OpenAPI 3.0 document is hosted at the endpoints `/openapi.{yaml,json}` and is
written to [openapi.yaml](openapi.yaml) during the Maven "compile" phase.
[JUnit 5]: https://junit.org/junit5/

View File

@@ -1,143 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>openwifi-rrm</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>com.facebook</groupId>
<artifactId>openwifi-base</artifactId>
<version>2.7.0</version>
</parent>
<properties>
<!-- Hack for static files located in root project -->
<myproject.root>${project.basedir}/..</myproject.root>
<mainClassName>com.facebook.openwifi.rrm.Launcher</mainClassName>
<appendVersionString></appendVersionString>
</properties>
<build>
<finalName>openwifi-rrm</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
<resource>
<directory>src/main/resources-filtered</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${mainClassName}</Main-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.facebook</groupId>
<artifactId>openwifi-cloudsdk</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.konghq</groupId>
<artifactId>unirest-java</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,160 +0,0 @@
/*
* 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.rrm;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ThreadPool;
import spark.embeddedserver.jetty.JettyServerFactory;
import spark.utils.Assert;
/**
* Creates Jetty Server instances. Majority of the logic is taken from
* JettyServerFactory. The additional feature is that this class will actually
* set two connectors (original class doesn't set any connectors at all and
* leaves it up to the serivce start logic). Since we set two connectors here on
* the server, Spark uses the existing conectors instead of trying to spin up
* its own connectors. The other difference is that it uses a different
* ServerConnector constructor to avoid allocating additional threads that
* aren't necessary ({@link #makeConnector})
*
* @see spark.embeddedserver.jetty.EmbeddedJettyFactory
*/
public class CustomJettyServerFactory implements JettyServerFactory {
// normally this is set in EmbeddedJettyServer but since we create our own connectors here,
// we need the value here
private boolean trustForwardHeaders = true; // true by default
private final int internalPort;
private final int externalPort;
public CustomJettyServerFactory(int internalPort, int externalPort) {
this.internalPort = internalPort;
this.externalPort = externalPort;
}
public void setTrustForwardHeaders(boolean trustForwardHeaders) {
this.trustForwardHeaders = trustForwardHeaders;
}
/**
* This is basically
* spark.embeddedserver.jetty.SocketConnectorFactory.createSocketConnector,
* the only difference being that we use a different constructor for the
* Connector and that the private methods called are just inlined.
*/
public Connector makeConnector(
Server server,
String host,
int port,
boolean trustForwardHeaders
) {
Assert.notNull(server, "'server' must not be null");
Assert.notNull(host, "'host' must not be null");
// spark.embeddedserver.jetty.SocketConnectorFactory.createHttpConnectionFactory
HttpConfiguration httpConfig = new HttpConfiguration();
httpConfig.setSecureScheme("https");
if (trustForwardHeaders) {
httpConfig.addCustomizer(new ForwardedRequestCustomizer());
}
HttpConnectionFactory httpConnectionFactory =
new HttpConnectionFactory(httpConfig);
ServerConnector connector = new ServerConnector(
server,
0, // acceptors, don't allocate separate threads for acceptor
0, // selectors, use default number
httpConnectionFactory
);
// spark.embeddedserver.jetty.SocketConnectorFactory.initializeConnector
connector.setIdleTimeout(TimeUnit.HOURS.toMillis(1));
connector.setHost(host);
connector.setPort(port);
return connector;
}
/**
* Creates a Jetty server.
*
* @param maxThreads maxThreads
* @param minThreads minThreads
* @param threadTimeoutMillis threadTimeoutMillis
* @return a new jetty server instance
*/
@Override
public Server create(
int maxThreads,
int minThreads,
int threadTimeoutMillis
) {
Server server;
if (maxThreads > 0) {
int max = maxThreads;
int min = (minThreads > 0) ? minThreads : 8;
int idleTimeout =
(threadTimeoutMillis > 0) ? threadTimeoutMillis : 60000;
server = new Server(new QueuedThreadPool(max, min, idleTimeout));
} else {
server = new Server();
}
Connector internalConnector = null;
if (internalPort != -1) {
internalConnector = makeConnector(
server,
"0.0.0.0",
internalPort,
trustForwardHeaders
);
}
Connector externalConnector = null;
if (externalPort != -1) {
externalConnector = makeConnector(
server,
"0.0.0.0",
externalPort,
trustForwardHeaders
);
}
if (internalConnector == null) {
server.setConnectors(new Connector[] { externalConnector });
} else if (externalConnector == null) {
server.setConnectors(new Connector[] { internalConnector });
} else {
server.setConnectors(
new Connector[] { internalConnector, externalConnector }
);
}
return server;
}
/**
* Creates a Jetty server with supplied thread pool
* @param threadPool thread pool
* @return a new jetty server instance
*/
@Override
public Server create(ThreadPool threadPool) {
return threadPool != null ? new Server(threadPool) : new Server();
}
}

View File

@@ -1,121 +0,0 @@
/*
* 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.rrm.services;
import java.util.Map;
import java.util.HashMap;
import java.time.Instant;
import com.facebook.openwifi.cloudsdk.models.gw.TokenValidationResult;
import com.facebook.openwifi.cloudsdk.models.gw.UserInfo;
import com.facebook.openwifi.cloudsdk.models.gw.WebTokenResult;
import com.google.gson.Gson;
import spark.Service;
import spark.Request;
import spark.Response;
import spark.Route;
/**
* This is a mock OW Security service meant to be used in tests.
*
* @see <a href="https://github.com/Telecominfraproject/wlan-cloud-ucentralsec">owsec</a>
*/
public class MockOWSecService {
private class TokenInfo {
long expiry;
long created;
}
private final Gson gson = new Gson();
/** A mapping of valid tokens to their expiry time in seconds since epoch */
private Map<String, TokenInfo> validTokens;
/** The Spark service */
private Service service;
public MockOWSecService(int port) {
validTokens = new HashMap<>();
service = Service.ignite();
service.port(port);
service.get("/api/v1/validateToken", new ValidateTokenEndpoint());
service.get("/api/v1/oauth2", new ValidateTokenEndpoint());
service.get("/api/v1/systemEndpoints", new SystemEndpoint());
service.awaitInitialization();
}
public void stop() {
service.stop();
service.awaitStop();
}
public void addToken(String token, long expiresInSec) {
TokenInfo time = new TokenInfo();
time.created = Instant.now().getEpochSecond();
time.expiry = expiresInSec;
validTokens.put(token, time);
}
public void removeToken(String token) {
validTokens.remove(token);
}
public int getPort() { return service.port(); }
public class Oauth2Endpoint implements Route {
@Override
public String handle(Request request, Response response) {
response.status(501);
return "Not Implemented";
}
}
public class SystemEndpoint implements Route {
@Override
public String handle(Request request, Response response) {
response.status(501);
return "Not Implemented";
}
}
public class ValidateTokenEndpoint implements Route {
@Override
public String handle(Request request, Response response) {
String token = request.queryParams("token");
if (token == null) {
response.status(403);
return "Forbidden";
}
TokenInfo info = validTokens.get(token);
if (info == null) {
response.status(403);
return "Forbidden";
}
if (info.created + info.expiry < Instant.now().getEpochSecond()) {
response.status(403);
return "Forbidden";
}
TokenValidationResult result = new TokenValidationResult();
result.userInfo = new UserInfo();
result.tokenInfo = new WebTokenResult();
result.tokenInfo.access_token = token;
result.tokenInfo.created = info.created;
result.tokenInfo.expires_in = info.expiry;
return gson.toJson(result);
}
}
}

50
pom.xml
View File

@@ -2,25 +2,35 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.facebook</groupId> <groupId>com.facebook</groupId>
<artifactId>openwifi-base</artifactId> <artifactId>openwifi-rrm</artifactId>
<version>2.7.0</version> <version>2.7.0</version>
<packaging>pom</packaging>
<modules>
<module>lib-cloudsdk</module>
<module>owrrm</module>
</modules>
<properties> <properties>
<!-- Hack for static files located in root project -->
<myproject.root>${project.basedir}</myproject.root>
<java.version>11</java.version> <java.version>11</java.version>
<slf4j.version>1.7.32</slf4j.version> <slf4j.version>1.7.32</slf4j.version>
<junit.version>5.7.2</junit.version> <junit.version>5.7.2</junit.version>
<swagger.version>2.1.10</swagger.version> <swagger.version>2.1.10</swagger.version>
<mainClassName>com.facebook.openwifirrm.Launcher</mainClassName>
<appendVersionString></appendVersionString>
<!-- do not abort builds on autoformatter errors --> <!-- do not abort builds on autoformatter errors -->
<spotless.check.skip>true</spotless.check.skip> <spotless.check.skip>true</spotless.check.skip>
</properties> </properties>
<build> <build>
<pluginManagement> <finalName>openwifi-rrm</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
<resource>
<directory>src/main/resources-filtered</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
@@ -38,7 +48,6 @@
<version>2.22.2</version> <version>2.22.2</version>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version> <version>3.2.1</version>
<configuration> <configuration>
@@ -59,6 +68,13 @@
</excludes> </excludes>
</filter> </filter>
</filters> </filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${mainClassName}</Main-Class>
</manifestEntries>
</transformer>
</transformers>
<createDependencyReducedPom>false</createDependencyReducedPom> <createDependencyReducedPom>false</createDependencyReducedPom>
</configuration> </configuration>
<executions> <executions>
@@ -107,13 +123,13 @@
<configuration> <configuration>
<java> <java>
<eclipse> <eclipse>
<file>${myproject.root}/spotless/eclipse-java-formatter.xml</file> <file>${project.basedir}/spotless/eclipse-java-formatter.xml</file>
<version>4.12.0</version> <version>4.12.0</version>
</eclipse> </eclipse>
<trimTrailingWhitespace /> <trimTrailingWhitespace />
<removeUnusedImports/> <removeUnusedImports/>
<licenseHeader> <licenseHeader>
<file>${myproject.root}/spotless/license-header.txt</file> <file>${project.basedir}/spotless/license-header.txt</file>
</licenseHeader> </licenseHeader>
</java> </java>
</configuration> </configuration>
@@ -127,9 +143,7 @@
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</pluginManagement>
</build> </build>
<dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
@@ -145,11 +159,13 @@
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version> <version>${junit.version}</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId> <artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version> <version>${junit.version}</version>
<scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>info.picocli</groupId> <groupId>info.picocli</groupId>
@@ -211,11 +227,5 @@
<artifactId>quartz</artifactId> <artifactId>quartz</artifactId>
<version>2.3.2</version> <version>2.3.2</version>
</dependency> </dependency>
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement>
</project> </project>

View File

@@ -2,9 +2,7 @@ import http from 'k6/http';
import { sleep } from 'k6'; import { sleep } from 'k6';
const INTERNAL_BASE_URL = 'http://localhost:16790/api/v1'; const BASE_URL = 'http://localhost:16789/api/v1';
const EXTERNAL_BASE_URL = __ENV.SEPARATE_INTERNAL_EXTERNAL_PORTS ? 'http://localhost:16789/api/v1' : INTERNAL_BASE_URL;
export default function () { export default function () {
const endpoints = [ const endpoints = [
@@ -14,19 +12,13 @@ export default function () {
'getToplogy', 'getToplogy',
'currentModel', 'currentModel',
]; ];
const internalRequests = endpoints.map(endpoint => { const requests = endpoints.map(endpoint => {
return { return {
method: 'GET', method: 'GET',
url: `${INTERNAL_BASE_URL}/${endpoint}`, url: `${BASE_URL}/${endpoint}`,
};
});
const externalRequests = endpoints.map(endpoint => {
return {
method: 'GET',
url: `${EXTERNAL_BASE_URL}/${endpoint}`,
}; };
}); });
let responses = http.batch([...internalRequests, ...externalRequests]); let responses = http.batch(requests);
sleep(0.1); sleep(0.1);
} }

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm; package com.facebook.openwifirrm;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.List; import java.util.List;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm; package com.facebook.openwifirrm;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@@ -47,17 +47,17 @@ public class DeviceDataManager {
private final ReadWriteLock topologyLock = new ReentrantReadWriteLock(); private final ReadWriteLock topologyLock = new ReentrantReadWriteLock();
/** Lock on {@link #deviceLayeredConfig}. */ /** Lock on {@link #deviceLayeredConfig}. */
public final ReadWriteLock deviceLayeredConfigLock = private final ReadWriteLock deviceLayeredConfigLock =
new ReentrantReadWriteLock(); new ReentrantReadWriteLock();
/** The current device topology. */ /** The current device topology. */
public DeviceTopology topology; private DeviceTopology topology;
/** The current layered device config. */ /** The current layered device config. */
public DeviceLayeredConfig deviceLayeredConfig; private DeviceLayeredConfig deviceLayeredConfig;
/** The cached device configs (map of serial number to computed config). */ /** The cached device configs (map of serial number to computed config). */
public Map<String, DeviceConfig> cachedDeviceConfigs = private Map<String, DeviceConfig> cachedDeviceConfigs =
new ConcurrentHashMap<>(); new ConcurrentHashMap<>();
/** Empty constructor without backing files (ex. for unit tests). */ /** Empty constructor without backing files (ex. for unit tests). */

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm; package com.facebook.openwifirrm;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm; package com.facebook.openwifirrm;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm; package com.facebook.openwifirrm;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
@@ -18,10 +18,11 @@ import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralClient; import com.facebook.openwifirrm.mysql.DatabaseManager;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer; import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaProducer; import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer;
import com.facebook.openwifi.rrm.mysql.DatabaseManager; import com.facebook.openwifirrm.ucentral.UCentralKafkaProducer;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
@@ -154,7 +155,8 @@ public class Launcher implements Callable<Integer> {
: DEFAULT_DEVICE_LAYERED_CONFIG_FILE : DEFAULT_DEVICE_LAYERED_CONFIG_FILE
); );
String serviceKey = Utils.generateServiceKey(config.serviceConfig); String serviceKey =
UCentralUtils.generateServiceKey(config.serviceConfig);
// Instantiate clients // Instantiate clients
UCentralClient.verifySsl(config.uCentralConfig.verifySsl); UCentralClient.verifySsl(config.uCentralConfig.verifySsl);
@@ -164,9 +166,7 @@ public class Launcher implements Callable<Integer> {
config.uCentralConfig.uCentralSecPublicEndpoint, config.uCentralConfig.uCentralSecPublicEndpoint,
config.uCentralConfig.username, config.uCentralConfig.username,
config.uCentralConfig.password, config.uCentralConfig.password,
config.uCentralConfig.uCentralSocketParams.connectTimeoutMs, config.uCentralConfig.uCentralSocketParams
config.uCentralConfig.uCentralSocketParams.socketTimeoutMs,
config.uCentralConfig.uCentralSocketParams.wifiScanTimeoutMs
); );
UCentralKafkaConsumer consumer; UCentralKafkaConsumer consumer;
UCentralKafkaProducer producer; UCentralKafkaProducer producer;
@@ -265,7 +265,7 @@ public class Launcher implements Callable<Integer> {
.setPrettyPrinting() .setPrettyPrinting()
.serializeNulls() // for here only!! .serializeNulls() // for here only!!
.create(); .create();
System.out.println(gson.toJson(DeviceConfig.createDefault())); logger.info(gson.toJson(DeviceConfig.createDefault()));
return 0; return 0;
} }

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm; package com.facebook.openwifirrm;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -18,18 +18,18 @@ import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralClient; import com.facebook.openwifirrm.modules.ApiServer;
import com.facebook.openwifi.cloudsdk.kafka.KafkaRunner; import com.facebook.openwifirrm.modules.ConfigManager;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer; import com.facebook.openwifirrm.modules.DataCollector;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaProducer; import com.facebook.openwifirrm.modules.Modeler;
import com.facebook.openwifi.cloudsdk.models.gw.SystemInfoResults; import com.facebook.openwifirrm.modules.ProvMonitor;
import com.facebook.openwifi.rrm.modules.ApiServer; import com.facebook.openwifirrm.modules.RRMScheduler;
import com.facebook.openwifi.rrm.modules.ConfigManager; import com.facebook.openwifirrm.mysql.DatabaseManager;
import com.facebook.openwifi.rrm.modules.DataCollector; import com.facebook.openwifirrm.ucentral.KafkaRunner;
import com.facebook.openwifi.rrm.modules.Modeler; import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifi.rrm.modules.ProvMonitor; import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer;
import com.facebook.openwifi.rrm.modules.RRMScheduler; import com.facebook.openwifirrm.ucentral.UCentralKafkaProducer;
import com.facebook.openwifi.rrm.mysql.DatabaseManager; import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults;
/** /**
* RRM service runner. * RRM service runner.

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm; package com.facebook.openwifirrm;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -14,17 +14,17 @@ import java.util.Map;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.rrm.modules.ConfigManager; import com.facebook.openwifirrm.modules.ConfigManager;
import com.facebook.openwifi.rrm.modules.Modeler; import com.facebook.openwifirrm.modules.Modeler;
import com.facebook.openwifi.rrm.optimizers.channel.ChannelOptimizer; import com.facebook.openwifirrm.optimizers.channel.ChannelOptimizer;
import com.facebook.openwifi.rrm.optimizers.channel.LeastUsedChannelOptimizer; import com.facebook.openwifirrm.optimizers.channel.LeastUsedChannelOptimizer;
import com.facebook.openwifi.rrm.optimizers.channel.RandomChannelInitializer; import com.facebook.openwifirrm.optimizers.channel.RandomChannelInitializer;
import com.facebook.openwifi.rrm.optimizers.channel.UnmanagedApAwareChannelOptimizer; import com.facebook.openwifirrm.optimizers.channel.UnmanagedApAwareChannelOptimizer;
import com.facebook.openwifi.rrm.optimizers.tpc.LocationBasedOptimalTPC; import com.facebook.openwifirrm.optimizers.tpc.LocationBasedOptimalTPC;
import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApApTPC; import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApApTPC;
import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApClientTPC; import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApClientTPC;
import com.facebook.openwifi.rrm.optimizers.tpc.RandomTxPowerInitializer; import com.facebook.openwifirrm.optimizers.tpc.RandomTxPowerInitializer;
import com.facebook.openwifi.rrm.optimizers.tpc.TPC; import com.facebook.openwifirrm.optimizers.tpc.TPC;
/** /**
* RRM algorithm model and utility methods. * RRM algorithm model and utility methods.

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm; package com.facebook.openwifirrm;
import java.util.Map; import java.util.Map;
@@ -34,7 +34,7 @@ public class RRMConfig {
* Private endpoint for the RRM service * Private endpoint for the RRM service
* ({@code SERVICECONFIG_PRIVATEENDPOINT}) * ({@code SERVICECONFIG_PRIVATEENDPOINT})
*/ */
public String privateEndpoint = "http://owrrm.wlan.local:16790"; // see ApiServerParams.internalHttpPort public String privateEndpoint = "http://owrrm.wlan.local:16789"; // see ApiServerParams.httpPort
/** /**
* Public endpoint for the RRM service * Public endpoint for the RRM service
@@ -60,7 +60,7 @@ public class RRMConfig {
* ({@code SERVICECONFIG_VENDORREFERENCEURL}) * ({@code SERVICECONFIG_VENDORREFERENCEURL})
*/ */
public String vendorReferenceUrl = public String vendorReferenceUrl =
"https://github.com/Telecominfraproject/wlan-cloud-rrm/blob/main/owrrm/ALGORITHMS.md"; "https://github.com/Telecominfraproject/wlan-cloud-rrm/blob/main/ALGORITHMS.md";
} }
/** Service configuration. */ /** Service configuration. */
@@ -309,12 +309,6 @@ public class RRMConfig {
* ({@code MODELERPARAMS_WIFISCANBUFFERSIZE}) * ({@code MODELERPARAMS_WIFISCANBUFFERSIZE})
*/ */
public int wifiScanBufferSize = 10; public int wifiScanBufferSize = 10;
/**
* Maximum rounds of States to store per device
* ({@code MODELERPARAMS_STATEBUFFERSIZE})
*/
public int stateBufferSize = 10;
} }
/** Modeler parameters. */ /** Modeler parameters. */
@@ -325,16 +319,10 @@ public class RRMConfig {
*/ */
public class ApiServerParams { public class ApiServerParams {
/** /**
* The HTTP port to listen on for internal traffic, or -1 to disable * The HTTP port to listen on, or -1 to disable
* ({@code APISERVERPARAMS_INTERNALHTTPPORT}) * ({@code APISERVERPARAMS_HTTPPORT})
*/ */
public int internalHttpPort = 16790; public int httpPort = 16789;
/**
* The HTTP port to listen on for external traffic, or -1 to disable
* ({@code APISERVERPARAMS_EXTERNALHTTPPORT})
*/
public int externalHttpPort = 16789;
/** /**
* Comma-separated list of all allowed CORS domains (exact match * Comma-separated list of all allowed CORS domains (exact match
@@ -544,16 +532,10 @@ public class RRMConfig {
if ((v = env.get("MODELERPARAMS_WIFISCANBUFFERSIZE")) != null) { if ((v = env.get("MODELERPARAMS_WIFISCANBUFFERSIZE")) != null) {
modelerParams.wifiScanBufferSize = Integer.parseInt(v); modelerParams.wifiScanBufferSize = Integer.parseInt(v);
} }
if ((v = env.get("MODELERPARAMS_STATEBUFFERSIZE")) != null) {
modelerParams.stateBufferSize = Integer.parseInt(v);
}
ModuleConfig.ApiServerParams apiServerParams = ModuleConfig.ApiServerParams apiServerParams =
config.moduleConfig.apiServerParams; config.moduleConfig.apiServerParams;
if ((v = env.get("APISERVERPARAMS_INTERNALHTTPPORT")) != null) { if ((v = env.get("APISERVERPARAMS_HTTPPORT")) != null) {
apiServerParams.internalHttpPort = Integer.parseInt(v); apiServerParams.httpPort = Integer.parseInt(v);
}
if ((v = env.get("APISERVERPARAMS_EXTERNALHTTPPORT")) != null) {
apiServerParams.externalHttpPort = Integer.parseInt(v);
} }
if ((v = env.get("APISERVERPARAMS_CORSDOMAINLIST")) != null) { if ((v = env.get("APISERVERPARAMS_CORSDOMAINLIST")) != null) {
apiServerParams.corsDomainList = v; apiServerParams.corsDomainList = v;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm; package com.facebook.openwifirrm;
import java.util.List; import java.util.List;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm; package com.facebook.openwifirrm;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@@ -16,8 +16,6 @@ import java.io.InputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Scanner; import java.util.Scanner;
@@ -25,8 +23,6 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.json.JSONObject; import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
@@ -35,8 +31,6 @@ import com.google.gson.GsonBuilder;
* Generic utility methods. * Generic utility methods.
*/ */
public class Utils { public class Utils {
private static final Logger logger = LoggerFactory.getLogger(Utils.class);
/** Hex value array for use in {@link #longToMac(long)}. */ /** Hex value array for use in {@link #longToMac(long)}. */
private static final char[] HEX_VALUES = "0123456789abcdef".toCharArray(); private static final char[] HEX_VALUES = "0123456789abcdef".toCharArray();
@@ -199,19 +193,4 @@ public class Utils {
public static <T> T deepCopy(T obj, Class<T> classOfT) { public static <T> T deepCopy(T obj, Class<T> classOfT) {
return gson.fromJson(gson.toJson(obj), classOfT); return gson.fromJson(gson.toJson(obj), classOfT);
} }
/** Generate the RRM service key. */
public static String generateServiceKey(
RRMConfig.ServiceConfig serviceConfig
) {
try {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.update(serviceConfig.publicEndpoint.getBytes());
sha256.update(serviceConfig.privateEndpoint.getBytes());
return Utils.bytesToHex(sha256.digest());
} catch (NoSuchAlgorithmException e) {
logger.error("Unable to generate service key", e);
return "";
}
}
} }

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm; package com.facebook.openwifirrm;
import picocli.CommandLine.IVersionProvider; import picocli.CommandLine.IVersionProvider;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.aggregators; package com.facebook.openwifirrm.aggregators;
/** /**
* Aggregates added values into one "aggregate" measure. * Aggregates added values into one "aggregate" measure.

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.aggregators; package com.facebook.openwifirrm.aggregators;
/** /**
* Tracks the mean of all added values. If no values are added, the mean is 0. * Tracks the mean of all added values. If no values are added, the mean is 0.

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.modules; package com.facebook.openwifirrm.modules;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.URI; import java.net.URI;
@@ -35,33 +35,31 @@ import org.reflections.util.ConfigurationBuilder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralClient; import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifi.cloudsdk.models.gw.SystemInfoResults; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.cloudsdk.models.gw.TokenValidationResult; import com.facebook.openwifirrm.DeviceLayeredConfig;
import com.facebook.openwifi.cloudsdk.models.prov.rrm.Algorithm; import com.facebook.openwifirrm.DeviceTopology;
import com.facebook.openwifi.cloudsdk.models.prov.rrm.Provider; import com.facebook.openwifirrm.RRMAlgorithm;
import com.facebook.openwifi.rrm.CustomJettyServerFactory; import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ApiServerParams;
import com.facebook.openwifi.rrm.DeviceConfig; import com.facebook.openwifirrm.RRMConfig.ServiceConfig;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.Utils.LruCache;
import com.facebook.openwifi.rrm.DeviceLayeredConfig; import com.facebook.openwifirrm.VersionProvider;
import com.facebook.openwifi.rrm.DeviceTopology; import com.facebook.openwifirrm.optimizers.channel.LeastUsedChannelOptimizer;
import com.facebook.openwifi.rrm.RRMAlgorithm; import com.facebook.openwifirrm.optimizers.channel.RandomChannelInitializer;
import com.facebook.openwifi.rrm.Utils; import com.facebook.openwifirrm.optimizers.channel.UnmanagedApAwareChannelOptimizer;
import com.facebook.openwifi.rrm.VersionProvider; import com.facebook.openwifirrm.optimizers.tpc.LocationBasedOptimalTPC;
import com.facebook.openwifi.rrm.RRMConfig.ServiceConfig; import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApApTPC;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ApiServerParams; import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApClientTPC;
import com.facebook.openwifi.rrm.Utils.LruCache; import com.facebook.openwifirrm.optimizers.tpc.RandomTxPowerInitializer;
import com.facebook.openwifi.rrm.optimizers.channel.LeastUsedChannelOptimizer; import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifi.rrm.optimizers.channel.RandomChannelInitializer; import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifi.rrm.optimizers.channel.UnmanagedApAwareChannelOptimizer; import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults;
import com.facebook.openwifi.rrm.optimizers.tpc.LocationBasedOptimalTPC; import com.facebook.openwifirrm.ucentral.gw.models.TokenValidationResult;
import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApApTPC; import com.facebook.openwifirrm.ucentral.prov.rrm.models.Algorithm;
import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApClientTPC; import com.facebook.openwifirrm.ucentral.prov.rrm.models.Provider;
import com.facebook.openwifi.rrm.optimizers.tpc.RandomTxPowerInitializer;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import org.openjdk.jol.info.GraphLayout;
import io.swagger.v3.core.util.Json; import io.swagger.v3.core.util.Json;
import io.swagger.v3.core.util.Yaml; import io.swagger.v3.core.util.Yaml;
import io.swagger.v3.jaxrs2.Reader; import io.swagger.v3.jaxrs2.Reader;
@@ -83,9 +81,7 @@ import io.swagger.v3.oas.models.OpenAPI;
import spark.Request; import spark.Request;
import spark.Response; import spark.Response;
import spark.Route; import spark.Route;
import spark.Service; import spark.Spark;
import spark.embeddedserver.EmbeddedServers;
import spark.embeddedserver.jetty.EmbeddedJettyFactory;
/** /**
* HTTP API server. * HTTP API server.
@@ -114,27 +110,6 @@ public class ApiServer implements Runnable {
private static final Logger logger = private static final Logger logger =
LoggerFactory.getLogger(ApiServer.class); LoggerFactory.getLogger(ApiServer.class);
/**
* This is the identifier for the server factory that Spark should use. This
* particular identifier points to the custom factory that we register to
* enable running multiple ports on one service.
*
* @see #run()
*/
private static final String SPARK_EMBEDDED_SERVER_IDENTIFIER =
ApiServer.class.getName();
/**
* The Spark service instance. Normally, you would use the static methods on
* Spark, but since we need to spin up multiple instances of Spark for testing,
* we choose to go with instantiating the service ourselves. There is really no
* difference except with the static method, Spark calls ignite and holds a
* singleton instance for us.
*
* @see Spark
*/
private final Service service;
/** The module parameters. */ /** The module parameters. */
private final ApiServerParams params; private final ApiServerParams params;
@@ -189,10 +164,9 @@ public class ApiServer implements Runnable {
UCentralClient client, UCentralClient client,
RRMScheduler scheduler RRMScheduler scheduler
) { ) {
this.service = Service.ignite();
this.params = params; this.params = params;
this.serviceConfig = serviceConfig; this.serviceConfig = serviceConfig;
this.serviceKey = Utils.generateServiceKey(serviceConfig); this.serviceKey = UCentralUtils.generateServiceKey(serviceConfig);
this.deviceDataManager = deviceDataManager; this.deviceDataManager = deviceDataManager;
this.configManager = configManager; this.configManager = configManager;
this.modeler = modeler; this.modeler = modeler;
@@ -220,117 +194,64 @@ public class ApiServer implements Runnable {
return ret; return ret;
} }
/**
* Block until initialization finishes. Just calls the method on the
* underlying service.
*/
public void awaitInitialization() {
service.awaitInitialization();
}
@Override @Override
public void run() { public void run() {
this.startTimeMs = System.currentTimeMillis(); this.startTimeMs = System.currentTimeMillis();
if (params.internalHttpPort == -1 && params.externalHttpPort == -1) { if (params.httpPort == -1) {
logger.info("API server is disabled."); logger.info("API server is disabled.");
return; return;
} else if (params.internalHttpPort == -1) {
logger.info("Internal API server is disabled");
} else if (params.externalHttpPort == -1) {
logger.info("External API server is disabled");
} }
if (params.internalHttpPort == params.externalHttpPort) { Spark.port(params.httpPort);
logger.error(
"Internal and external port cannot be the same - not starting API server"
);
return;
}
EmbeddedServers.add(
SPARK_EMBEDDED_SERVER_IDENTIFIER,
new EmbeddedJettyFactory(
new CustomJettyServerFactory(
params.internalHttpPort,
params.externalHttpPort
)
)
);
// use the embedded server factory added above, this is required so that we
// don't mess up the default factory which can and will be used for
// additional Spark services in testing
service.embeddedServerIdentifier(SPARK_EMBEDDED_SERVER_IDENTIFIER);
// Usually you would call this with an actual port and Spark would spin up a
// port on it. However, since we're putting our own connectors in so that we
// can use two ports and Spark has logic to use connectors that already exist
// so it doesn't matter what port we pass in here as long as it's not one of
// the actual ports we're using (Spark has some weird logic where it still
// tries to bind to the port).
// @see EmbeddedJettyServer
service.port(0);
// Configure API docs hosting // Configure API docs hosting
service.staticFiles.location("/public"); Spark.staticFiles.location("/public");
service.get("/openapi.yaml", this::getOpenApiYaml); Spark.get("/openapi.yaml", this::getOpenApiYaml);
service.get("/openapi.json", this::getOpenApiJson); Spark.get("/openapi.json", this::getOpenApiJson);
// Install routes // Install routes
service.before(this::beforeFilter); Spark.before(this::beforeFilter);
service.after(this::afterFilter); Spark.after(this::afterFilter);
service.options("/*", this::options); Spark.options("/*", this::options);
service.get("/api/v1/system", new SystemEndpoint()); Spark.get("/api/v1/system", new SystemEndpoint());
service.post("/api/v1/system", new SetSystemEndpoint()); Spark.post("/api/v1/system", new SetSystemEndpoint());
service.get("/api/v1/provider", new ProviderEndpoint()); Spark.get("/api/v1/provider", new ProviderEndpoint());
service.get("/api/v1/algorithms", new AlgorithmsEndpoint()); Spark.get("/api/v1/algorithms", new AlgorithmsEndpoint());
service.put("/api/v1/runRRM", new RunRRMEndpoint()); Spark.put("/api/v1/runRRM", new RunRRMEndpoint());
service.get("/api/v1/getTopology", new GetTopologyEndpoint()); Spark.get("/api/v1/getTopology", new GetTopologyEndpoint());
service.post("/api/v1/setTopology", new SetTopologyEndpoint()); Spark.post("/api/v1/setTopology", new SetTopologyEndpoint());
service.get( Spark.get(
"/api/v1/getDeviceLayeredConfig", "/api/v1/getDeviceLayeredConfig",
new GetDeviceLayeredConfigEndpoint() new GetDeviceLayeredConfigEndpoint()
); );
service.get("/api/v1/getDeviceConfig", new GetDeviceConfigEndpoint()); Spark.get("/api/v1/getDeviceConfig", new GetDeviceConfigEndpoint());
service.post( Spark.post(
"/api/v1/setDeviceNetworkConfig", "/api/v1/setDeviceNetworkConfig",
new SetDeviceNetworkConfigEndpoint() new SetDeviceNetworkConfigEndpoint()
); );
service.post( Spark.post(
"/api/v1/setDeviceZoneConfig", "/api/v1/setDeviceZoneConfig",
new SetDeviceZoneConfigEndpoint() new SetDeviceZoneConfigEndpoint()
); );
service.post( Spark.post(
"/api/v1/setDeviceApConfig", "/api/v1/setDeviceApConfig",
new SetDeviceApConfigEndpoint() new SetDeviceApConfigEndpoint()
); );
service.post( Spark.post(
"/api/v1/modifyDeviceApConfig", "/api/v1/modifyDeviceApConfig",
new ModifyDeviceApConfigEndpoint() new ModifyDeviceApConfigEndpoint()
); );
service.get("/api/v1/currentModel", new GetCurrentModelEndpoint()); Spark.get("/api/v1/currentModel", new GetCurrentModelEndpoint());
service.get("/api/v1/optimizeChannel", new OptimizeChannelEndpoint()); Spark.get("/api/v1/optimizeChannel", new OptimizeChannelEndpoint());
service.get("/api/v1/optimizeTxPower", new OptimizeTxPowerEndpoint()); Spark.get("/api/v1/optimizeTxPower", new OptimizeTxPowerEndpoint());
service.get("/api/v1/memory", new MemoryEndpoint(this));
logger.info( logger.info("API server listening on HTTP port {}", params.httpPort);
"API server listening for HTTP internal on port {} and external on port {}",
params.internalHttpPort,
params.externalHttpPort
);
} }
/** Stop the server. */ /** Stop the server. */
public void shutdown() { public void shutdown() {
service.stop(); Spark.stop();
}
/**
* Block until stop finishes. Just calls the method on the underlying service.
*/
public void awaitStop() {
service.awaitStop();
} }
/** Reconstructs a URL. */ /** Reconstructs a URL. */
@@ -348,18 +269,16 @@ public class ApiServer implements Runnable {
* HTTP 403 response and return false. * HTTP 403 response and return false.
*/ */
private boolean performOpenWifiAuth(Request request, Response response) { private boolean performOpenWifiAuth(Request request, Response response) {
int port = request.port(); // TODO check if request came from internal endpoint
boolean internal = port > 0 && port == params.internalHttpPort; boolean internal = true;
if (internal) {
String internalName = request.headers("X-INTERNAL-NAME"); String internalName = request.headers("X-INTERNAL-NAME");
if (internalName != null) { if (internal && internalName != null) {
// Internal request, validate "X-API-KEY" // Internal request, validate "X-API-KEY"
String apiKey = request.headers("X-API-KEY"); String apiKey = request.headers("X-API-KEY");
if (apiKey != null && apiKey.equals(serviceKey)) { if (apiKey != null && apiKey.equals(serviceKey)) {
// auth success // auth success
return true; return true;
} }
}
} else { } else {
// External request, validate token: // External request, validate token:
// Authorization: Bearer <token> // Authorization: Bearer <token>
@@ -378,7 +297,7 @@ public class ApiServer implements Runnable {
} }
// auth failure // auth failure
service.halt(403, "Forbidden"); Spark.halt(403, "Forbidden");
return false; return false;
} }
@@ -406,11 +325,10 @@ public class ApiServer implements Runnable {
private void beforeFilter(Request request, Response response) { private void beforeFilter(Request request, Response response) {
// Log requests // Log requests
logger.debug( logger.debug(
"[{}] {} {} on port {}", "[{}] {} {}",
request.ip(), request.ip(),
request.requestMethod(), request.requestMethod(),
getFullUrl(request.pathInfo(), request.queryString()), getFullUrl(request.pathInfo(), request.queryString())
request.port()
); );
// Remove "Server: Jetty" header // Remove "Server: Jetty" header
@@ -1362,91 +1280,6 @@ public class ApiServer implements Runnable {
} }
} }
@Path("/api/v1/memory")
public class MemoryEndpoint implements Route {
private final ApiServer apiServer;
MemoryEndpoint(ApiServer server) {
this.apiServer = server;
}
@Override
public String handle(Request request, Response response) {
String type = request.queryParamOrDefault("type", "");
String view = request.queryParamOrDefault("view", "footprint");
java.util.function.Function<GraphLayout, String> fn = (GraphLayout graph) -> {
return view.equals("footprint") ? graph.toFootprint() : graph.toPrintable();
};
String result;
switch (type) {
case "modeler.dataModel":
result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.dataModel));
break;
case "modeler.dataModel.latestWifiScans":
result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.dataModel.latestWifiScans));
break;
case "modeler.dataModel.latestStates":
result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.dataModel.latestStates));
break;
case "modeler.dataModel.latestDeviceStatusRadios":
result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.dataModel.latestDeviceStatusRadios));
break;
case "modeler.deviceDataManager":
result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.deviceDataManager));
break;
case "modeler.deviceDataManager.topology":
result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.deviceDataManager.topology));
break;
case "modeler.deviceDataManager.deviceLayeredConfig":
result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.deviceDataManager.deviceLayeredConfig));
break;
case "modeler.deviceDataManager.cachedDeviceConfigs":
result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.deviceDataManager.cachedDeviceConfigs));
break;
case "modeler.dataModel.latestDeviceCapabilities":
result = fn.apply(GraphLayout.parseInstance(apiServer.modeler.dataModel.latestDeviceCapabilities));
case "modeler":
result = fn.apply(GraphLayout.parseInstance(apiServer.modeler));
break;
case "configManager.deviceDataMap":
result = fn.apply(GraphLayout.parseInstance(apiServer.configManager.deviceDataMap));
break;
case "configManager":
result = fn.apply(GraphLayout.parseInstance(apiServer.configManager));
break;
case "scheduler":
result = fn.apply(GraphLayout.parseInstance(apiServer.scheduler));
break;
case "deviceDataManager":
result = fn.apply(GraphLayout.parseInstance(apiServer.deviceDataManager));
break;
case "":
default:
result = GraphLayout.parseInstance(apiServer).toFootprint();
break;
}
logger.info("MEMORY RESPONSE: \n{}", result);
return result;
}
}
@Path("/api/v1/optimizeTxPower") @Path("/api/v1/optimizeTxPower")
public class OptimizeTxPowerEndpoint implements Route { public class OptimizeTxPowerEndpoint implements Route {
// Hack for use in @ApiResponse -> @Content -> @Schema // Hack for use in @ApiResponse -> @Content -> @Schema

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.modules; package com.facebook.openwifirrm.modules;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -21,13 +21,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralApConfiguration; import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifi.cloudsdk.UCentralClient; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.cloudsdk.UCentralUtils; import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ConfigManagerParams;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus; import com.facebook.openwifirrm.ucentral.UCentralApConfiguration;
import com.facebook.openwifi.rrm.DeviceConfig; import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ConfigManagerParams; import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus;
/** /**
* Device configuration manager module. * Device configuration manager module.
@@ -46,7 +46,7 @@ public class ConfigManager implements Runnable {
private final UCentralClient client; private final UCentralClient client;
/** Runtime per-device data. */ /** Runtime per-device data. */
public class DeviceData { private class DeviceData {
/** Last received device config. */ /** Last received device config. */
public UCentralApConfiguration config; public UCentralApConfiguration config;
@@ -55,7 +55,7 @@ public class ConfigManager implements Runnable {
} }
/** Map from device serial number to runtime data. */ /** Map from device serial number to runtime data. */
public Map<String, DeviceData> deviceDataMap = new TreeMap<>(); private Map<String, DeviceData> deviceDataMap = new TreeMap<>();
/** The main thread reference (i.e. where {@link #run()} is invoked). */ /** The main thread reference (i.e. where {@link #run()} is invoked). */
private Thread mainThread; private Thread mainThread;
@@ -171,6 +171,7 @@ public class ConfigManager implements Runnable {
return; return;
} }
} }
client.refreshAccessToken();
// Fetch device list // Fetch device list
List<DeviceWithStatus> devices = client.getDevices(); List<DeviceWithStatus> devices = client.getDevices();

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.modules; package com.facebook.openwifirrm.modules;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -23,22 +23,22 @@ import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralApConfiguration; import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifi.cloudsdk.UCentralClient; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.cloudsdk.UCentralUtils; import com.facebook.openwifirrm.RRMConfig.ModuleConfig.DataCollectorParams;
import com.facebook.openwifi.cloudsdk.WifiScanEntry; import com.facebook.openwifirrm.Utils;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer; import com.facebook.openwifirrm.mysql.DatabaseManager;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer.KafkaRecord; import com.facebook.openwifirrm.mysql.StateRecord;
import com.facebook.openwifi.cloudsdk.models.gw.CommandInfo; import com.facebook.openwifirrm.ucentral.UCentralApConfiguration;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceCapabilities; import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus; import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer;
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer.KafkaRecord;
import com.facebook.openwifi.rrm.DeviceConfig; import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifi.rrm.Utils; import com.facebook.openwifirrm.ucentral.gw.models.CommandInfo;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.DataCollectorParams; import com.facebook.openwifirrm.ucentral.gw.models.DeviceCapabilities;
import com.facebook.openwifi.rrm.mysql.DatabaseManager; import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus;
import com.facebook.openwifi.rrm.mysql.StateRecord; import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
@@ -218,6 +218,7 @@ public class DataCollector implements Runnable {
return; return;
} }
} }
client.refreshAccessToken();
// Fetch device list // Fetch device list
List<DeviceWithStatus> devices = client.getDevices(); List<DeviceWithStatus> devices = client.getDevices();

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.modules; package com.facebook.openwifirrm.modules;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@@ -20,21 +20,21 @@ import java.util.concurrent.LinkedBlockingQueue;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralApConfiguration; import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifi.cloudsdk.UCentralClient; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.cloudsdk.UCentralUtils; import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ModelerParams;
import com.facebook.openwifi.cloudsdk.WifiScanEntry; import com.facebook.openwifirrm.Utils;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer; import com.facebook.openwifirrm.ucentral.UCentralApConfiguration;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer.KafkaRecord; import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifi.cloudsdk.models.ap.State; import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceCapabilities; import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer.KafkaRecord;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus; import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifi.cloudsdk.models.gw.StatisticsRecords; import com.facebook.openwifirrm.ucentral.gw.models.DeviceCapabilities;
import com.facebook.openwifi.rrm.DeviceConfig; import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ModelerParams; import com.facebook.openwifirrm.ucentral.gw.models.StatisticsRecords;
import com.facebook.openwifi.rrm.Utils; import com.facebook.openwifirrm.ucentral.models.State;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@@ -50,7 +50,7 @@ public class Modeler implements Runnable {
private final ModelerParams params; private final ModelerParams params;
/** The device data manager. */ /** The device data manager. */
public final DeviceDataManager deviceDataManager; private final DeviceDataManager deviceDataManager;
/** The uCentral client instance. */ /** The uCentral client instance. */
private final UCentralClient client; private final UCentralClient client;
@@ -74,7 +74,7 @@ public class Modeler implements Runnable {
} }
/** The blocking data queue. */ /** The blocking data queue. */
public final BlockingQueue<InputData> dataQueue = private final BlockingQueue<InputData> dataQueue =
new LinkedBlockingQueue<>(); new LinkedBlockingQueue<>();
/** Data model representation. */ /** Data model representation. */
@@ -93,9 +93,8 @@ public class Modeler implements Runnable {
public Map<String, List<List<WifiScanEntry>>> latestWifiScans = public Map<String, List<List<WifiScanEntry>>> latestWifiScans =
new ConcurrentHashMap<>(); new ConcurrentHashMap<>();
/** List of latest states per device. */ /** List of latest state per device. */
public Map<String, List<State>> latestStates = public Map<String, State> latestState = new ConcurrentHashMap<>();
new ConcurrentHashMap<>();
/** List of radio info per device. */ /** List of radio info per device. */
public Map<String, JsonArray> latestDeviceStatusRadios = public Map<String, JsonArray> latestDeviceStatusRadios =
@@ -239,6 +238,7 @@ public class Modeler implements Runnable {
return; return;
} }
} }
client.refreshAccessToken();
// TODO: backfill data from database? // TODO: backfill data from database?
@@ -268,10 +268,7 @@ public class Modeler implements Runnable {
if (state != null) { if (state != null) {
try { try {
State stateModel = gson.fromJson(state, State.class); State stateModel = gson.fromJson(state, State.class);
dataModel.latestStates.computeIfAbsent( dataModel.latestState.put(device.serialNumber, stateModel);
device.serialNumber,
k -> new LinkedList<>()
).add(stateModel);
logger.debug( logger.debug(
"Device {}: added initial state from uCentralGw", "Device {}: added initial state from uCentralGw",
device.serialNumber device.serialNumber
@@ -303,17 +300,8 @@ public class Modeler implements Runnable {
if (state != null) { if (state != null) {
try { try {
State stateModel = gson.fromJson(state, State.class); State stateModel = gson.fromJson(state, State.class);
List<State> latestStatesList = dataModel.latestStates dataModel.latestState
.computeIfAbsent( .put(record.serialNumber, stateModel);
record.serialNumber,
k -> new LinkedList<>()
);
while (
latestStatesList.size() >= params.stateBufferSize
) {
latestStatesList.remove(0);
}
latestStatesList.add(stateModel);
stateUpdates.add(record.serialNumber); stateUpdates.add(record.serialNumber);
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
logger.error( logger.error(
@@ -436,7 +424,7 @@ public class Modeler implements Runnable {
logger.debug("Removed some wifi scan entries from data model"); logger.debug("Removed some wifi scan entries from data model");
} }
if ( if (
dataModel.latestStates.entrySet() dataModel.latestState.entrySet()
.removeIf(e -> !isRRMEnabled(e.getKey())) .removeIf(e -> !isRRMEnabled(e.getKey()))
) { ) {
logger.debug("Removed some state entries from data model"); logger.debug("Removed some state entries from data model");

View File

@@ -6,33 +6,24 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.modules; package com.facebook.openwifirrm.modules;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.AggregatedState; import com.facebook.openwifirrm.aggregators.Aggregator;
import com.facebook.openwifi.cloudsdk.WifiScanEntry; import com.facebook.openwifirrm.aggregators.MeanAggregator;
import com.facebook.openwifi.cloudsdk.ies.HTOperation; import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifi.cloudsdk.ies.VHTOperation; import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifi.cloudsdk.models.ap.State; import com.facebook.openwifirrm.ucentral.informationelement.HTOperation;
import com.facebook.openwifi.cloudsdk.models.ap.State.Interface; import com.facebook.openwifirrm.ucentral.informationelement.VHTOperation;
import com.facebook.openwifi.cloudsdk.models.ap.State.Interface.SSID;
import com.facebook.openwifi.cloudsdk.models.ap.State.Interface.SSID.Association;
import com.facebook.openwifi.rrm.aggregators.Aggregator;
import com.facebook.openwifi.rrm.aggregators.MeanAggregator;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/** /**
* Modeler utilities. * Modeler utilities.
@@ -300,7 +291,7 @@ public class ModelerUtils {
/** /**
* Compute aggregated wifiscans using a given reference time. * Compute aggregated wifiscans using a given reference time.
* *
* @see #getAggregatedWifiScans(com.facebook.openwifi.rrm.modules.Modeler.DataModel, * @see #getAggregatedWifiScans(com.facebook.openwifirrm.modules.Modeler.DataModel,
* long, Aggregator) * long, Aggregator)
*/ */
public static Map<String, Map<String, WifiScanEntry>> getAggregatedWifiScans( public static Map<String, Map<String, WifiScanEntry>> getAggregatedWifiScans(
@@ -390,196 +381,4 @@ public class ModelerUtils {
} }
return aggregatedWifiScans; return aggregatedWifiScans;
} }
/**
* This method converts the input State info to an AggregatedState
* and adds it to the bssidToAggregatedStates map. If the bssid/station
* of the input State does not exist in the map, create a new
* AggregatedState list. If the bssid/station of the input State exists,
* then convert State to AggregatedState and check if there exits an
* AggregatedState of the same radio. If there does, append the value
* of aggregation field to the existing AggregatedState, if not, create
* a new AggregatedState and add it to the list.
*
* @param bssidToAggregatedStates map from bssid/station to a list of AggregatedState
* @param state the state that is to be added
*/
static void addStateToAggregation(
Map<String, List<AggregatedState>> bssidToAggregatedStates,
State state
) {
for (Interface stateInterface : state.interfaces) {
if (stateInterface.ssids == null) {
continue;
}
for (SSID ssid : stateInterface.ssids) {
Map<String, Integer> radioInfo = new HashMap<>();
radioInfo.put("channel", ssid.radio.get("channel").getAsInt());
radioInfo.put(
"channel_width",
ssid.radio.get("channel_width").getAsInt()
);
radioInfo
.put("tx_power", ssid.radio.get("tx_power").getAsInt());
for (Association association : ssid.associations) {
if (association == null) {
continue;
}
String key = getBssidStationKeyPair(
association.bssid,
association.station
);
List<AggregatedState> aggregatedStates =
bssidToAggregatedStates
.computeIfAbsent(key, k -> new ArrayList<>());
AggregatedState aggState =
new AggregatedState(association, radioInfo);
/**
* Indicate if the aggState can be merged into some old AggregatedState.
* If true, it will be merged by appending its mcs/rssi field to the old one.
* If false, it will be added to the list aggregatedStates.
*/
boolean canBeMergedToOldAggregatedState = false;
for (
AggregatedState oldAggregatedState : aggregatedStates
) {
if (oldAggregatedState.add(aggState)) {
canBeMergedToOldAggregatedState = true;
break;
}
}
if (!canBeMergedToOldAggregatedState) {
aggregatedStates.add(aggState);
}
bssidToAggregatedStates.put(key, aggregatedStates);
}
}
}
}
/**
* This method aggregates States by bssid/station key pair and radio info.
* if two States of the same bssid/station match in channel, channel width and tx_power
* need to be aggregated to one {@code AggregatedState}. Currently only mcs and
* rssi fields are being aggregated. They are of {@code List<Integer>} type in AggregatedState,
* which list all the values over the time.
*
* @param dataModel the data model which includes the latest recorded States
* @param obsoletionPeriodMs the maximum amount of time (in milliseconds) it
* is worth aggregating over, starting from the
* most recent States and working backwards in time.
* A State exactly {@code obsoletionPeriodMs} ms earlier
* than the most recent State is considered non-obsolete
* (i.e., the "non-obsolete" window is inclusive).
* Must be non-negative.
* @param refTimeMs the reference time were passed to make testing easier
* @return map from serial number to a map from bssid_station String pair to a list of AggregatedState
*/
public static Map<String, Map<String, List<AggregatedState>>> getAggregatedStates(
Modeler.DataModel dataModel,
long obsoletionPeriodMs,
long refTimeMs
) {
if (obsoletionPeriodMs < 0) {
throw new IllegalArgumentException(
"obsoletionPeriodMs must be non-negative."
);
}
Map<String, Map<String, List<AggregatedState>>> aggregatedStates =
new HashMap<>();
for (
Map.Entry<String, List<State>> deviceToStateList : dataModel.latestStates
.entrySet()
) {
String serialNumber = deviceToStateList.getKey();
List<State> states = deviceToStateList.getValue();
if (states.isEmpty()) {
continue;
}
/**
* Sort in reverse chronological order. Sorting is done just in case the
* States in the original list are not chronological already - although
* they are inserted chronologically, perhaps latency, synchronization, etc.
*/
states.sort(
(state1, state2) -> -Long.compare(state1.unit.localtime, state2.unit.localtime)
);
Map<String, List<AggregatedState>> bssidToAggregatedStates =
aggregatedStates
.computeIfAbsent(serialNumber, k -> new HashMap<>());
for (State state : states) {
if (refTimeMs - state.unit.localtime > obsoletionPeriodMs) {
// discard obsolete entries
break;
}
addStateToAggregation(bssidToAggregatedStates, state);
}
}
return aggregatedStates;
}
/**
* This method gets the most recent State from latestStates per device.
*
* @param latestStates list of latest States per device
* @return map from device String to latest State
*/
public static Map<String, State> getLatestState(
Map<String, List<State>> latestStates
) {
Map<String, State> latestState = new ConcurrentHashMap<>();
for (
Map.Entry<String, List<State>> stateEntry : latestStates.entrySet()
) {
String key = stateEntry.getKey();
List<State> value = stateEntry.getValue();
if (value.isEmpty()) {
latestState.put(key, null);
} else {
latestState.put(key, value.get(value.size() - 1));
}
}
return latestState;
}
/** Create a key pair consisted of bssid and station string */
public static String getBssidStationKeyPair(String bssid, String station) {
return String.format(
"bssid: %s, station: %s",
bssid,
station
);
}
/** Return the radio's band, or null if band cannot be found */
public static String getBand(
State.Radio radio,
JsonObject deviceCapability
) {
if (radio.phy == null) {
return null;
}
JsonElement radioCapabilityElement = deviceCapability.get(radio.phy);
if (radioCapabilityElement == null) {
return null;
}
JsonObject radioCapability = radioCapabilityElement.getAsJsonObject();
JsonElement bandsElement = radioCapability.get("band");
if (bandsElement == null) {
return null;
}
JsonArray bands = bandsElement.getAsJsonArray();
if (bands.isEmpty()) {
return null;
}
return bands.get(0).getAsString();
}
} }

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.modules; package com.facebook.openwifirrm.modules;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@@ -18,19 +18,19 @@ import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralClient; import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifi.cloudsdk.models.prov.InventoryTag; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.cloudsdk.models.prov.InventoryTagList; import com.facebook.openwifirrm.DeviceTopology;
import com.facebook.openwifi.cloudsdk.models.prov.RRMDetails; import com.facebook.openwifirrm.RRMAlgorithm;
import com.facebook.openwifi.cloudsdk.models.prov.SerialNumberList; import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ProvMonitorParams;
import com.facebook.openwifi.cloudsdk.models.prov.Venue; import com.facebook.openwifirrm.RRMSchedule;
import com.facebook.openwifi.cloudsdk.models.prov.VenueList; import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifi.rrm.DeviceConfig; import com.facebook.openwifirrm.ucentral.prov.models.InventoryTag;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.ucentral.prov.models.InventoryTagList;
import com.facebook.openwifi.rrm.DeviceTopology; import com.facebook.openwifirrm.ucentral.prov.models.RRMDetails;
import com.facebook.openwifi.rrm.RRMAlgorithm; import com.facebook.openwifirrm.ucentral.prov.models.SerialNumberList;
import com.facebook.openwifi.rrm.RRMSchedule; import com.facebook.openwifirrm.ucentral.prov.models.Venue;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ProvMonitorParams; import com.facebook.openwifirrm.ucentral.prov.models.VenueList;
/** /**
* owprov monitor module. * owprov monitor module.
@@ -103,6 +103,7 @@ public class ProvMonitor implements Runnable {
return; return;
} }
} }
client.refreshAccessToken();
// Fetch data from owprov // Fetch data from owprov
// TODO: this may change later - for now, we only fetch inventory and // TODO: this may change later - for now, we only fetch inventory and

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.modules; package com.facebook.openwifirrm.modules;
import java.text.ParseException; import java.text.ParseException;
import java.util.Arrays; import java.util.Arrays;
@@ -32,11 +32,11 @@ import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.rrm.DeviceConfig; import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.rrm.RRMAlgorithm; import com.facebook.openwifirrm.RRMAlgorithm;
import com.facebook.openwifi.rrm.RRMSchedule; import com.facebook.openwifirrm.RRMSchedule;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.RRMSchedulerParams; import com.facebook.openwifirrm.RRMConfig.ModuleConfig.RRMSchedulerParams;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.mysql; package com.facebook.openwifirrm.mysql;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
@@ -26,9 +26,9 @@ import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.WifiScanEntry; import com.facebook.openwifirrm.Utils;
import com.facebook.openwifi.cloudsdk.models.ap.State; import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifi.rrm.Utils; import com.facebook.openwifirrm.ucentral.models.State;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.mysql; package com.facebook.openwifirrm.mysql;
/** /**
* Representation of a record in the "state" table. * Representation of a record in the "state" table.

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.optimizers.channel; package com.facebook.openwifirrm.optimizers.channel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -18,18 +18,16 @@ import java.util.Map;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralConstants; import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifi.cloudsdk.UCentralUtils; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.cloudsdk.WifiScanEntry; import com.facebook.openwifirrm.modules.ConfigManager;
import com.facebook.openwifi.cloudsdk.ies.HTOperation; import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifi.cloudsdk.ies.VHTOperation; import com.facebook.openwifirrm.ucentral.UCentralConstants;
import com.facebook.openwifi.cloudsdk.models.ap.State; import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifi.rrm.DeviceConfig; import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.ucentral.informationelement.HTOperation;
import com.facebook.openwifi.rrm.modules.ConfigManager; import com.facebook.openwifirrm.ucentral.informationelement.VHTOperation;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel; import com.facebook.openwifirrm.ucentral.models.State;
import com.facebook.openwifi.rrm.modules.ModelerUtils;
import com.google.gson.JsonObject;
/** /**
* Channel optimizer base class. * Channel optimizer base class.
@@ -41,6 +39,24 @@ public abstract class ChannelOptimizer {
/** Minimum supported channel width (MHz), inclusive. */ /** Minimum supported channel width (MHz), inclusive. */
public static final int MIN_CHANNEL_WIDTH = 20; public static final int MIN_CHANNEL_WIDTH = 20;
/** List of available channels per band for use. */
public static final Map<String, List<Integer>> AVAILABLE_CHANNELS_BAND =
new HashMap<>();
static {
AVAILABLE_CHANNELS_BAND.put(
UCentralConstants.BAND_5G,
Collections.unmodifiableList(
Arrays.asList(36, 40, 44, 48, 149, 153, 157, 161, 165)
)
);
AVAILABLE_CHANNELS_BAND.put(
UCentralConstants.BAND_2G,
Collections.unmodifiableList(
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
)
);
}
/** Map of channel width (MHz) to available (primary) channels */ /** Map of channel width (MHz) to available (primary) channels */
protected static final Map<Integer, List<Integer>> AVAILABLE_CHANNELS_WIDTH = protected static final Map<Integer, List<Integer>> AVAILABLE_CHANNELS_WIDTH =
new HashMap<>(); new HashMap<>();
@@ -138,7 +154,7 @@ public abstract class ChannelOptimizer {
// Remove model entries not in the given zone // Remove model entries not in the given zone
this.model.latestWifiScans.keySet() this.model.latestWifiScans.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); .removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber));
this.model.latestStates.keySet() this.model.latestState.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); .removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber));
this.model.latestDeviceStatusRadios.keySet() this.model.latestDeviceStatusRadios.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); .removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber));
@@ -181,7 +197,7 @@ public abstract class ChannelOptimizer {
String vhtOper String vhtOper
) { ) {
if ( if (
UCentralUtils.AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G) AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G)
.contains(channel) .contains(channel)
) { ) {
// 2.4G, it only supports 20 MHz // 2.4G, it only supports 20 MHz
@@ -300,11 +316,7 @@ public abstract class ChannelOptimizer {
List<WifiScanEntry> scanRespsFiltered = List<WifiScanEntry> scanRespsFiltered =
new ArrayList<WifiScanEntry>(); new ArrayList<WifiScanEntry>();
for (WifiScanEntry entry : scanResps) { for (WifiScanEntry entry : scanResps) {
final String entryBand = UCentralUtils if (UCentralUtils.isChannelInBand(entry.channel, band)) {
.freqToBand(entry.frequency);
if (entryBand == null || !entryBand.equals(band)) {
continue;
}
int channelWidth = getChannelWidthFromWiFiScan( int channelWidth = getChannelWidthFromWiFiScan(
entry.channel, entry.channel,
entry.ht_oper, entry.ht_oper,
@@ -324,6 +336,7 @@ public abstract class ChannelOptimizer {
scanRespsFiltered.add(newEntry); scanRespsFiltered.add(newEntry);
} }
} }
}
if (scanRespsFiltered.size() == 0) { if (scanRespsFiltered.size() == 0) {
// 3. Filter out APs with empty scan results (on a particular band) // 3. Filter out APs with empty scan results (on a particular band)
@@ -349,7 +362,6 @@ public abstract class ChannelOptimizer {
* @param band the operational band (e.g., "2G") * @param band the operational band (e.g., "2G")
* @param serialNumber the device's serial number * @param serialNumber the device's serial number
* @param state the latest state of all the devices * @param state the latest state of all the devices
* @param latestDeviceCapabilities latest device capabilities
* @return the current channel and channel width (MHz) of the device in the * @return the current channel and channel width (MHz) of the device in the
* given band; returns a current channel of 0 if no channel in the given * given band; returns a current channel of 0 if no channel in the given
* band is found. * band is found.
@@ -357,8 +369,7 @@ public abstract class ChannelOptimizer {
protected static int[] getCurrentChannel( protected static int[] getCurrentChannel(
String band, String band,
String serialNumber, String serialNumber,
State state, State state
Map<String, JsonObject> latestDeviceCapabilities
) { ) {
int currentChannel = 0; int currentChannel = 0;
int currentChannelWidth = MIN_CHANNEL_WIDTH; int currentChannelWidth = MIN_CHANNEL_WIDTH;
@@ -368,28 +379,14 @@ public abstract class ChannelOptimizer {
radioIndex < state.radios.length; radioIndex < state.radios.length;
radioIndex++ radioIndex++
) { ) {
State.Radio radio = state.radios[radioIndex]; int tempChannel = state.radios[radioIndex].channel;
// check if radio is in band of interest if (UCentralUtils.isChannelInBand(tempChannel, band)) {
JsonObject deviceCapability =
latestDeviceCapabilities.get(serialNumber);
if (deviceCapability == null) {
continue;
}
final String radioBand = ModelerUtils.getBand(
radio,
deviceCapability
);
if (radioBand == null || !radioBand.equals(band)) {
continue;
}
int tempChannel = radio.channel;
currentChannel = tempChannel; currentChannel = tempChannel;
// treat as two separate 80MHz channel and only assign to one // treat as two separate 80MHz channel and only assign to one
// TODO: support 80p80 properly // TODO: support 80p80 properly
Integer parsedChannelWidth = UCentralUtils Integer parsedChannelWidth = UCentralUtils
.parseChannelWidth( .parseChannelWidth(
radio.channel_width, state.radios[radioIndex].channel_width,
true true
); );
if (parsedChannelWidth != null) { if (parsedChannelWidth != null) {
@@ -399,10 +396,11 @@ public abstract class ChannelOptimizer {
logger.error( logger.error(
"Invalid channel width {}", "Invalid channel width {}",
radio.channel_width state.radios[radioIndex].channel_width
); );
continue; continue;
} }
}
return new int[] { currentChannel, currentChannelWidth }; return new int[] { currentChannel, currentChannelWidth };
} }

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.optimizers.channel; package com.facebook.openwifirrm.optimizers.channel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
@@ -20,13 +20,12 @@ import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralConstants; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.cloudsdk.UCentralUtils; import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifi.cloudsdk.WifiScanEntry; import com.facebook.openwifirrm.ucentral.UCentralConstants;
import com.facebook.openwifi.cloudsdk.models.ap.State; import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel; import com.facebook.openwifirrm.ucentral.models.State;
import com.facebook.openwifi.rrm.modules.ModelerUtils;
/** /**
* Least used channel optimizer. * Least used channel optimizer.
@@ -90,13 +89,13 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
protected static Map<Integer, Integer> getOccupiedOverlapChannels( protected static Map<Integer, Integer> getOccupiedOverlapChannels(
Map<Integer, Integer> occupiedChannels Map<Integer, Integer> occupiedChannels
) { ) {
final int maxChannel = int maxChannel =
UCentralUtils.getUpperChannelLimit(UCentralConstants.BAND_2G); UCentralUtils.UPPER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G);
final int minChannel = int minChannel =
UCentralUtils.getLowerChannelLimit(UCentralConstants.BAND_2G); UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G);
Map<Integer, Integer> occupiedOverlapChannels = new TreeMap<>(); Map<Integer, Integer> occupiedOverlapChannels = new TreeMap<>();
for ( for (
int overlapChannel : UCentralUtils.AVAILABLE_CHANNELS_BAND int overlapChannel : AVAILABLE_CHANNELS_BAND
.get(UCentralConstants.BAND_2G) .get(UCentralConstants.BAND_2G)
) { ) {
int occupancy = 0; int occupancy = 0;
@@ -338,13 +337,11 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
UCentralUtils.getDeviceAvailableChannels( UCentralUtils.getDeviceAvailableChannels(
model.latestDeviceStatusRadios, model.latestDeviceStatusRadios,
model.latestDeviceCapabilities, model.latestDeviceCapabilities,
UCentralUtils.AVAILABLE_CHANNELS_BAND AVAILABLE_CHANNELS_BAND
); );
Map<String, State> latestState =
ModelerUtils.getLatestState(model.latestStates);
Map<String, String> bssidsMap = Map<String, String> bssidsMap =
UCentralUtils.getBssidsMap(latestState); UCentralUtils.getBssidsMap(model.latestState);
for (String band : bandsMap.keySet()) { for (String band : bandsMap.keySet()) {
// Performance metrics // Performance metrics
@@ -372,12 +369,11 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
availableChannelsList == null || availableChannelsList == null ||
availableChannelsList.isEmpty() availableChannelsList.isEmpty()
) { ) {
availableChannelsList = availableChannelsList = AVAILABLE_CHANNELS_BAND.get(band);
UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band);
} }
// Get current channel of the device // Get current channel of the device
State state = latestState.get(serialNumber); State state = model.latestState.get(serialNumber);
if (state == null) { if (state == null) {
logger.debug( logger.debug(
"Device {}: No state found, skipping...", "Device {}: No state found, skipping...",
@@ -393,12 +389,7 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer {
continue; continue;
} }
int[] currentChannelInfo = int[] currentChannelInfo =
getCurrentChannel( getCurrentChannel(band, serialNumber, state);
band,
serialNumber,
state,
model.latestDeviceCapabilities
);
int currentChannel = currentChannelInfo[0]; int currentChannel = currentChannelInfo[0];
// Filter out APs if the radios in the state do not contain a // Filter out APs if the radios in the state do not contain a
// channel in a band given by the state. This can happen when // channel in a band given by the state. This can happen when

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.optimizers.channel; package com.facebook.openwifirrm.optimizers.channel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -19,12 +19,11 @@ import java.util.TreeSet;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralUtils; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.cloudsdk.WifiScanEntry; import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifi.cloudsdk.models.ap.State; import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel; import com.facebook.openwifirrm.ucentral.models.State;
import com.facebook.openwifi.rrm.modules.ModelerUtils;
/** /**
* Random channel initializer. * Random channel initializer.
@@ -126,13 +125,11 @@ public class RandomChannelInitializer extends ChannelOptimizer {
UCentralUtils.getDeviceAvailableChannels( UCentralUtils.getDeviceAvailableChannels(
model.latestDeviceStatusRadios, model.latestDeviceStatusRadios,
model.latestDeviceCapabilities, model.latestDeviceCapabilities,
UCentralUtils.AVAILABLE_CHANNELS_BAND AVAILABLE_CHANNELS_BAND
); );
Map<String, State> latestState =
ModelerUtils.getLatestState(model.latestStates);
Map<String, String> bssidsMap = Map<String, String> bssidsMap =
UCentralUtils.getBssidsMap(latestState); UCentralUtils.getBssidsMap(model.latestState);
for (Map.Entry<String, List<String>> entry : bandsMap.entrySet()) { for (Map.Entry<String, List<String>> entry : bandsMap.entrySet()) {
// Performance metrics // Performance metrics
@@ -152,7 +149,7 @@ public class RandomChannelInitializer extends ChannelOptimizer {
// to get the valid result for single channel assignment // to get the valid result for single channel assignment
// If the intersection is empty, then turn back to the default channels list // If the intersection is empty, then turn back to the default channels list
List<Integer> availableChannelsList = new ArrayList<>( List<Integer> availableChannelsList = new ArrayList<>(
UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band) AVAILABLE_CHANNELS_BAND.get(band)
); );
for (String serialNumber : entry.getValue()) { for (String serialNumber : entry.getValue()) {
List<Integer> deviceChannelsList = deviceAvailableChannels List<Integer> deviceChannelsList = deviceAvailableChannels
@@ -161,16 +158,14 @@ public class RandomChannelInitializer extends ChannelOptimizer {
if ( if (
deviceChannelsList == null || deviceChannelsList.isEmpty() deviceChannelsList == null || deviceChannelsList.isEmpty()
) { ) {
deviceChannelsList = deviceChannelsList = AVAILABLE_CHANNELS_BAND.get(band);
UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band);
} }
availableChannelsList.retainAll(deviceChannelsList); availableChannelsList.retainAll(deviceChannelsList);
} }
if ( if (
availableChannelsList == null || availableChannelsList.isEmpty() availableChannelsList == null || availableChannelsList.isEmpty()
) { ) {
availableChannelsList = availableChannelsList = AVAILABLE_CHANNELS_BAND.get(band);
UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band);
logger.debug( logger.debug(
"The intersection of the device channels lists is empty!!! " + "The intersection of the device channels lists is empty!!! " +
"Fall back to the default channels list" "Fall back to the default channels list"
@@ -188,7 +183,7 @@ public class RandomChannelInitializer extends ChannelOptimizer {
? rng.nextInt(availableChannelsList.size()) : defaultChannelIndex ? rng.nextInt(availableChannelsList.size()) : defaultChannelIndex
); );
State state = latestState.get(serialNumber); State state = model.latestState.get(serialNumber);
if (state == null) { if (state == null) {
logger.debug( logger.debug(
"Device {}: No state found, skipping...", "Device {}: No state found, skipping...",
@@ -204,12 +199,7 @@ public class RandomChannelInitializer extends ChannelOptimizer {
continue; continue;
} }
int[] currentChannelInfo = int[] currentChannelInfo =
getCurrentChannel( getCurrentChannel(band, serialNumber, state);
band,
serialNumber,
state,
model.latestDeviceCapabilities
);
int currentChannel = currentChannelInfo[0]; int currentChannel = currentChannelInfo[0];
int currentChannelWidth = currentChannelInfo[1]; int currentChannelWidth = currentChannelInfo[1];
if (currentChannel == 0) { if (currentChannel == 0) {

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.optimizers.channel; package com.facebook.openwifirrm.optimizers.channel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -17,10 +17,10 @@ import java.util.TreeMap;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralConstants; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.cloudsdk.WifiScanEntry; import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.ucentral.UCentralConstants;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel; import com.facebook.openwifirrm.ucentral.WifiScanEntry;
/** /**
* Unmanaged AP aware least used channel optimizer. * Unmanaged AP aware least used channel optimizer.

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.optimizers.tpc; package com.facebook.openwifirrm.optimizers.tpc;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@@ -18,11 +18,12 @@ import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.models.ap.State; import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifi.rrm.DeviceConfig; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel; import com.facebook.openwifirrm.modules.ModelerUtils;
import com.facebook.openwifi.rrm.modules.ModelerUtils; import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifirrm.ucentral.models.State;
/** /**
* Location-based optimal TPC algorithm. * Location-based optimal TPC algorithm.
@@ -152,7 +153,6 @@ public class LocationBasedOptimalTPC extends TPC {
/** /**
* Calculate new tx powers for the given band. * Calculate new tx powers for the given band.
* *
* @param band band (e.g., "2G")
* @param channel channel * @param channel channel
* @param serialNumbers The serial numbers of the APs with the channel * @param serialNumbers The serial numbers of the APs with the channel
* @param txPowerMap this map from serial number to band to new tx power * @param txPowerMap this map from serial number to band to new tx power
@@ -160,13 +160,13 @@ public class LocationBasedOptimalTPC extends TPC {
* this method with the new tx powers. * this method with the new tx powers.
*/ */
private void buildTxPowerMapForChannel( private void buildTxPowerMapForChannel(
String band,
int channel, int channel,
List<String> serialNumbers, List<String> serialNumbers,
Map<String, Map<String, Integer>> txPowerMap Map<String, Map<String, Integer>> txPowerMap
) { ) {
int numOfAPs = 0; int numOfAPs = 0;
int boundary = 100; int boundary = 100;
String band = UCentralUtils.getBandFromChannel(channel);
Map<String, Integer> validAPs = new TreeMap<>(); Map<String, Integer> validAPs = new TreeMap<>();
List<Double> apLocX = new ArrayList<>(); List<Double> apLocX = new ArrayList<>();
List<Double> apLocY = new ArrayList<>(); List<Double> apLocY = new ArrayList<>();
@@ -175,8 +175,7 @@ public class LocationBasedOptimalTPC extends TPC {
// Filter out the invalid APs (e.g., no radio, no location data) // Filter out the invalid APs (e.g., no radio, no location data)
// Update txPowerChoices, boundary, apLocX, apLocY for the optimization // Update txPowerChoices, boundary, apLocX, apLocY for the optimization
for (String serialNumber : serialNumbers) { for (String serialNumber : serialNumbers) {
List<State> states = model.latestStates.get(serialNumber); State state = model.latestState.get(serialNumber);
State state = states.get(states.size() - 1);
// Ignore the device if its radio is not active // Ignore the device if its radio is not active
if (state.radios == null || state.radios.length == 0) { if (state.radios == null || state.radios.length == 0) {
@@ -283,27 +282,9 @@ public class LocationBasedOptimalTPC extends TPC {
@Override @Override
public Map<String, Map<String, Integer>> computeTxPowerMap() { public Map<String, Map<String, Integer>> computeTxPowerMap() {
Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>();
Map<String, Map<Integer, List<String>>> bandToChannelToAps = Map<Integer, List<String>> apsPerChannel = getApsPerChannel();
getApsPerChannel(); for (Map.Entry<Integer, List<String>> e : apsPerChannel.entrySet()) {
for ( buildTxPowerMapForChannel(e.getKey(), e.getValue(), txPowerMap);
Map.Entry<String, Map<Integer, List<String>>> bandEntry : bandToChannelToAps
.entrySet()
) {
final String band = bandEntry.getKey();
Map<Integer, List<String>> channelToAps = bandEntry.getValue();
for (
Map.Entry<Integer, List<String>> channelEntry : channelToAps
.entrySet()
) {
final int channel = channelEntry.getKey();
List<String> serialNumbers = channelEntry.getValue();
buildTxPowerMapForChannel(
band,
channel,
serialNumbers,
txPowerMap
);
}
} }
return txPowerMap; return txPowerMap;
} }

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.optimizers.tpc; package com.facebook.openwifirrm.optimizers.tpc;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@@ -20,13 +20,11 @@ import java.util.TreeMap;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralUtils; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.cloudsdk.WifiScanEntry; import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifi.cloudsdk.models.ap.State; import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel; import com.facebook.openwifirrm.ucentral.models.State;
import com.facebook.openwifi.rrm.modules.ModelerUtils;
import com.google.gson.JsonObject;
/** /**
* Measurement-based AP-AP TPC algorithm. * Measurement-based AP-AP TPC algorithm.
@@ -156,9 +154,8 @@ public class MeasurementBasedApApTPC extends TPC {
*/ */
protected static Set<String> getManagedBSSIDs(DataModel model) { protected static Set<String> getManagedBSSIDs(DataModel model) {
Set<String> managedBSSIDs = new HashSet<>(); Set<String> managedBSSIDs = new HashSet<>();
for (Map.Entry<String, List<State>> e : model.latestStates.entrySet()) { for (Map.Entry<String, State> e : model.latestState.entrySet()) {
List<State> states = e.getValue(); State state = e.getValue();
State state = states.get(states.size() - 1);
if (state.interfaces == null) { if (state.interfaces == null) {
continue; continue;
} }
@@ -219,14 +216,7 @@ public class MeasurementBasedApApTPC extends TPC {
// At a given AP, if we receive a signal from ap_2, then it gets added to the rssi list for ap_2 // At a given AP, if we receive a signal from ap_2, then it gets added to the rssi list for ap_2
latestScan.stream() latestScan.stream()
.filter((entry) -> { .filter(entry -> (managedBSSIDs.contains(entry.bssid) && UCentralUtils.isChannelInBand(entry.channel, band)))
if (!managedBSSIDs.contains(entry.bssid)) {
return false;
}
String entryBand = UCentralUtils
.freqToBand(entry.frequency);
return entryBand != null && entryBand.equals(band);
})
.forEach( .forEach(
entry -> { entry -> {
bssidToRssiValues.get(entry.bssid).add(entry.signal); bssidToRssiValues.get(entry.bssid).add(entry.signal);
@@ -305,25 +295,23 @@ public class MeasurementBasedApApTPC extends TPC {
/** /**
* Calculate new tx powers for the given channel on the given APs . * Calculate new tx powers for the given channel on the given APs .
* *
* @param band band (e.g., "2G")
* @param channel channel * @param channel channel
* @param serialNumbers the serial numbers of the APs with the channel * @param serialNumbers the serial numbers of the APs with the channel
* @param txPowerMap this maps from serial number to band to new tx power (dBm) * @param txPowerMap this maps from serial number to band to new tx power (dBm)
* and is updated by this method with the new tx powers. * and is updated by this method with the new tx powers.
*/ */
protected void buildTxPowerMapForChannel( protected void buildTxPowerMapForChannel(
String band,
int channel, int channel,
List<String> serialNumbers, List<String> serialNumbers,
Map<String, Map<String, Integer>> txPowerMap Map<String, Map<String, Integer>> txPowerMap
) { ) {
String band = UCentralUtils.getBandFromChannel(channel);
Set<String> managedBSSIDs = getManagedBSSIDs(model); Set<String> managedBSSIDs = getManagedBSSIDs(model);
Map<String, List<Integer>> bssidToRssiValues = Map<String, List<Integer>> bssidToRssiValues =
buildRssiMap(managedBSSIDs, model.latestWifiScans, band); buildRssiMap(managedBSSIDs, model.latestWifiScans, band);
logger.debug("Starting TPC for the {} band", band); logger.debug("Starting TPC for the {} band", band);
for (String serialNumber : serialNumbers) { for (String serialNumber : serialNumbers) {
List<State> states = model.latestStates.get(serialNumber); State state = model.latestState.get(serialNumber);
State state = states.get(states.size() - 1);
if ( if (
state == null || state.radios == null || state == null || state.radios == null ||
state.radios.length == 0 state.radios.length == 0
@@ -373,16 +361,9 @@ public class MeasurementBasedApApTPC extends TPC {
State.Radio radio = state.radios[idx]; State.Radio radio = state.radios[idx];
// this specific SSID is not on the band of interest // this specific SSID is not on the band of interest
JsonObject deviceCapability = model.latestDeviceCapabilities if (
.get(serialNumber); !UCentralUtils.isChannelInBand(radio.channel, band)
if (deviceCapability == null) { ) {
continue;
}
final String radioBand = ModelerUtils.getBand(
radio,
deviceCapability
);
if (radioBand == null || !radioBand.equals(band)) {
continue; continue;
} }
@@ -426,27 +407,9 @@ public class MeasurementBasedApApTPC extends TPC {
@Override @Override
public Map<String, Map<String, Integer>> computeTxPowerMap() { public Map<String, Map<String, Integer>> computeTxPowerMap() {
Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>();
Map<String, Map<Integer, List<String>>> bandToChannelToAps = Map<Integer, List<String>> apsPerChannel = getApsPerChannel();
getApsPerChannel(); for (Map.Entry<Integer, List<String>> e : apsPerChannel.entrySet()) {
for ( buildTxPowerMapForChannel(e.getKey(), e.getValue(), txPowerMap);
Map.Entry<String, Map<Integer, List<String>>> bandEntry : bandToChannelToAps
.entrySet()
) {
final String band = bandEntry.getKey();
Map<Integer, List<String>> channelToAps = bandEntry.getValue();
for (
Map.Entry<Integer, List<String>> channelEntry : channelToAps
.entrySet()
) {
final int channel = channelEntry.getKey();
List<String> serialNumbers = channelEntry.getValue();
buildTxPowerMapForChannel(
band,
channel,
serialNumbers,
txPowerMap
);
}
} }
return txPowerMap; return txPowerMap;
} }

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.optimizers.tpc; package com.facebook.openwifirrm.optimizers.tpc;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -15,15 +15,13 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralUtils; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.cloudsdk.models.ap.State; import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.ucentral.models.State;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel;
import com.facebook.openwifi.rrm.modules.ModelerUtils;
import com.google.gson.JsonObject;
/** /**
* Measurement-based AP-client algorithm. * Measurement-based AP-client algorithm.
@@ -293,10 +291,10 @@ public class MeasurementBasedApClientTPC extends TPC {
public Map<String, Map<String, Integer>> computeTxPowerMap() { public Map<String, Map<String, Integer>> computeTxPowerMap() {
Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>();
for (Map.Entry<String, List<State>> e : model.latestStates.entrySet()) { for (Map.Entry<String, State> e : model.latestState.entrySet()) {
String serialNumber = e.getKey(); String serialNumber = e.getKey();
List<State> states = e.getValue(); State state = e.getValue();
State state = states.get(states.size() - 1);
if (state.radios == null || state.radios.length == 0) { if (state.radios == null || state.radios.length == 0) {
logger.debug( logger.debug(
"Device {}: No radios found, skipping...", "Device {}: No radios found, skipping...",
@@ -307,15 +305,8 @@ public class MeasurementBasedApClientTPC extends TPC {
Map<String, Integer> radioMap = new TreeMap<>(); Map<String, Integer> radioMap = new TreeMap<>();
for (State.Radio radio : state.radios) { for (State.Radio radio : state.radios) {
JsonObject deviceCapability = model.latestDeviceCapabilities int currentChannel = radio.channel;
.get(serialNumber); String band = UCentralUtils.getBandFromChannel(currentChannel);
if (deviceCapability == null) {
continue;
}
final String band = ModelerUtils.getBand(
radio,
deviceCapability
);
if (band == null) { if (band == null) {
continue; continue;
} }

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.optimizers.tpc; package com.facebook.openwifirrm.optimizers.tpc;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -14,12 +14,13 @@ import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.TreeMap; import java.util.TreeMap;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralConstants; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel; import com.facebook.openwifirrm.ucentral.UCentralConstants;
/** /**
* Random tx power initializer. * Random tx power initializer.
@@ -121,7 +122,7 @@ public class RandomTxPowerInitializer extends TPC {
if (!setDifferentTxPowerPerAp) { if (!setDifferentTxPowerPerAp) {
List<Integer> txPowerChoices = List<Integer> txPowerChoices =
new ArrayList<>(DEFAULT_TX_POWER_CHOICES); new ArrayList<>(DEFAULT_TX_POWER_CHOICES);
for (String serialNumber : model.latestStates.keySet()) { for (String serialNumber : model.latestState.keySet()) {
for (String band : UCentralConstants.BANDS) { for (String band : UCentralConstants.BANDS) {
txPowerChoices = updateTxPowerChoices( txPowerChoices = updateTxPowerChoices(
band, band,
@@ -136,21 +137,14 @@ public class RandomTxPowerInitializer extends TPC {
logger.info("Default power: {}", defaultTxPower); logger.info("Default power: {}", defaultTxPower);
} }
Map<String, Map<Integer, List<String>>> bandToChannelToAps = Map<Integer, List<String>> apsPerChannel = getApsPerChannel();
getApsPerChannel();
Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>(); Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>();
for ( for (Map.Entry<Integer, List<String>> e : apsPerChannel.entrySet()) {
Map.Entry<String, Map<Integer, List<String>>> bandEntry : bandToChannelToAps int channel = e.getKey();
.entrySet() List<String> serialNumbers = e.getValue();
) {
final String band = bandEntry.getKey(); String band = UCentralUtils.getBandFromChannel(channel);
Map<Integer, List<String>> channelToAps = bandEntry.getValue();
for (
Map.Entry<Integer, List<String>> channelEntry : channelToAps
.entrySet()
) {
List<String> serialNumbers = channelEntry.getValue();
for (String serialNumber : serialNumbers) { for (String serialNumber : serialNumbers) {
int txPower = defaultTxPower; int txPower = defaultTxPower;
if (setDifferentTxPowerPerAp) { if (setDifferentTxPowerPerAp) {
@@ -162,8 +156,7 @@ public class RandomTxPowerInitializer extends TPC {
txPower = curTxPowerChoices txPower = curTxPowerChoices
.get(rng.nextInt(curTxPowerChoices.size())); .get(rng.nextInt(curTxPowerChoices.size()));
} }
txPowerMap txPowerMap.computeIfAbsent(serialNumber, k -> new TreeMap<>())
.computeIfAbsent(serialNumber, k -> new TreeMap<>())
.put(band, txPower); .put(band, txPower);
logger.info( logger.info(
"Device {} band {}: Assigning tx power = {}", "Device {} band {}: Assigning tx power = {}",
@@ -173,7 +166,7 @@ public class RandomTxPowerInitializer extends TPC {
); );
} }
} }
}
return txPowerMap; return txPowerMap;
} }
} }

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.rrm.optimizers.tpc; package com.facebook.openwifirrm.optimizers.tpc;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -20,13 +20,11 @@ import java.util.stream.IntStream;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.models.ap.State; import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifi.rrm.DeviceConfig; import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifi.rrm.DeviceDataManager; import com.facebook.openwifirrm.modules.ConfigManager;
import com.facebook.openwifi.rrm.modules.ConfigManager; import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel; import com.facebook.openwifirrm.ucentral.models.State;
import com.facebook.openwifi.rrm.modules.ModelerUtils;
import com.google.gson.JsonObject;
/** /**
* TPC (Transmit Power Control) base class. * TPC (Transmit Power Control) base class.
@@ -72,7 +70,7 @@ public abstract class TPC {
this.model.latestWifiScans.keySet() this.model.latestWifiScans.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber) .removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)
); );
this.model.latestStates.keySet() this.model.latestState.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber) .removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)
); );
this.model.latestDeviceStatusRadios.keySet() this.model.latestDeviceStatusRadios.keySet()
@@ -182,14 +180,13 @@ public abstract class TPC {
/** /**
* Get AP serial numbers per channel. * Get AP serial numbers per channel.
* *
* @return map from band to channel to list of AP serial numbers * @return the map from channel to the list of AP serial numbers
*/ */
protected Map<String, Map<Integer, List<String>>> getApsPerChannel() { protected Map<Integer, List<String>> getApsPerChannel() {
Map<String, Map<Integer, List<String>>> apsPerChannel = new TreeMap<>(); Map<Integer, List<String>> apsPerChannel = new TreeMap<>();
for (Map.Entry<String, List<State>> e : model.latestStates.entrySet()) { for (Map.Entry<String, State> e : model.latestState.entrySet()) {
String serialNumber = e.getKey(); String serialNumber = e.getKey();
List<State> states = e.getValue(); State state = e.getValue();
State state = states.get(states.size() - 1);
if (state.radios == null || state.radios.length == 0) { if (state.radios == null || state.radios.length == 0) {
logger.debug( logger.debug(
@@ -204,20 +201,7 @@ public abstract class TPC {
if (currentChannel == 0) { if (currentChannel == 0) {
continue; continue;
} }
JsonObject deviceCapability =
model.latestDeviceCapabilities.get(serialNumber);
if (deviceCapability == null) {
continue;
}
final String band = ModelerUtils.getBand(
radio,
deviceCapability
);
if (band == null) {
continue;
}
apsPerChannel apsPerChannel
.computeIfAbsent(band, k -> new TreeMap<>())
.computeIfAbsent(currentChannel, k -> new ArrayList<>()) .computeIfAbsent(currentChannel, k -> new ArrayList<>())
.add(serialNumber); .add(serialNumber);
} }

View File

@@ -6,14 +6,14 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk; package com.facebook.openwifirrm.ucentral;
import java.util.Objects; import java.util.Objects;
import com.facebook.openwifi.cloudsdk.ies.Country; import com.facebook.openwifirrm.ucentral.informationelement.Country;
import com.facebook.openwifi.cloudsdk.ies.LocalPowerConstraint; import com.facebook.openwifirrm.ucentral.informationelement.LocalPowerConstraint;
import com.facebook.openwifi.cloudsdk.ies.QbssLoad; import com.facebook.openwifirrm.ucentral.informationelement.QbssLoad;
import com.facebook.openwifi.cloudsdk.ies.TxPwrInfo; import com.facebook.openwifirrm.ucentral.informationelement.TxPwrInfo;
/** Wrapper class containing information elements */ /** Wrapper class containing information elements */
public final class InformationElements { public final class InformationElements {

View File

@@ -6,13 +6,13 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.kafka; package com.facebook.openwifirrm.ucentral;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.kafka.common.errors.WakeupException; import org.apache.kafka.common.errors.WakeupException;
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
/** /**
* Kafka runner. * Kafka runner.

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk; package com.facebook.openwifirrm.ucentral;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;

View File

@@ -6,8 +6,9 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk; package com.facebook.openwifirrm.ucentral;
import java.time.Instant;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -20,21 +21,24 @@ import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.models.gw.CommandInfo; import com.facebook.openwifirrm.RRMConfig.UCentralConfig.UCentralSocketParams;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceCapabilities; import com.facebook.openwifirrm.ucentral.gw.models.CommandInfo;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceConfigureRequest; import com.facebook.openwifirrm.ucentral.gw.models.DeviceCapabilities;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceListWithStatus; import com.facebook.openwifirrm.ucentral.gw.models.DeviceConfigureRequest;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus; import com.facebook.openwifirrm.ucentral.gw.models.DeviceListWithStatus;
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus;
import com.facebook.openwifi.cloudsdk.models.gw.StatisticsRecords; import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
import com.facebook.openwifi.cloudsdk.models.gw.SystemInfoResults; import com.facebook.openwifirrm.ucentral.gw.models.StatisticsRecords;
import com.facebook.openwifi.cloudsdk.models.gw.TokenValidationResult; import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults;
import com.facebook.openwifi.cloudsdk.models.gw.WifiScanRequest; import com.facebook.openwifirrm.ucentral.gw.models.TokenValidationResult;
import com.facebook.openwifi.cloudsdk.models.prov.EntityList; import com.facebook.openwifirrm.ucentral.gw.models.WebTokenRefreshRequest;
import com.facebook.openwifi.cloudsdk.models.prov.InventoryTagList; import com.facebook.openwifirrm.ucentral.gw.models.WebTokenResult;
import com.facebook.openwifi.cloudsdk.models.prov.RRMDetails; import com.facebook.openwifirrm.ucentral.gw.models.WifiScanRequest;
import com.facebook.openwifi.cloudsdk.models.prov.SerialNumberList; import com.facebook.openwifirrm.ucentral.prov.models.EntityList;
import com.facebook.openwifi.cloudsdk.models.prov.VenueList; import com.facebook.openwifirrm.ucentral.prov.models.InventoryTagList;
import com.facebook.openwifirrm.ucentral.prov.models.RRMDetails;
import com.facebook.openwifirrm.ucentral.prov.models.SerialNumberList;
import com.facebook.openwifirrm.ucentral.prov.models.VenueList;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException; import com.google.gson.JsonSyntaxException;
@@ -126,14 +130,8 @@ public class UCentralClient {
/** uCentral password */ /** uCentral password */
private final String password; private final String password;
/** Connection timeout for all requests, in ms */ /** Socket parameters */
private final int connectTimeoutMs; private final UCentralSocketParams socketParams;
/** Socket timeout for all requests, in ms */
private final int socketTimeoutMs;
/** Socket timeout for wifi scan requests, in ms */
private final int wifiScanTimeoutMs;
/** The learned service endpoints. */ /** The learned service endpoints. */
private final Map<String, ServiceEvent> serviceEndpoints = new HashMap<>(); private final Map<String, ServiceEvent> serviceEndpoints = new HashMap<>();
@@ -142,7 +140,18 @@ public class UCentralClient {
* The access token obtained from uCentralSec, needed only when using public * The access token obtained from uCentralSec, needed only when using public
* endpoints. * endpoints.
*/ */
private String accessToken; private WebTokenResult accessToken;
/**
* The unix timestamp (in seconds) keeps track of when the accessToken is created.
*/
private long created;
/**
* The unix timestamp (in seconds) keeps track of last time when the accessToken
* is accessed.
*/
private long lastAccess;
/** /**
* Constructor. * Constructor.
@@ -152,9 +161,7 @@ public class UCentralClient {
* (if needed) * (if needed)
* @param username uCentral username (for public endpoints only) * @param username uCentral username (for public endpoints only)
* @param password uCentral password (for public endpoints only) * @param password uCentral password (for public endpoints only)
* @param connectTimeoutMs connection timeout for all requests, in ms * @param socketParams Socket parameters
* @param socketTimeoutMs socket timeout for all requests, in ms
* @param wifiScanTimeoutMs socket timeout for wifi scan requests, in ms
*/ */
public UCentralClient( public UCentralClient(
String rrmEndpoint, String rrmEndpoint,
@@ -162,17 +169,13 @@ public class UCentralClient {
String uCentralSecPublicEndpoint, String uCentralSecPublicEndpoint,
String username, String username,
String password, String password,
int connectTimeoutMs, UCentralSocketParams socketParams
int socketTimeoutMs,
int wifiScanTimeoutMs
) { ) {
this.rrmEndpoint = rrmEndpoint; this.rrmEndpoint = rrmEndpoint;
this.usePublicEndpoints = usePublicEndpoints; this.usePublicEndpoints = usePublicEndpoints;
this.username = username; this.username = username;
this.password = password; this.password = password;
this.connectTimeoutMs = connectTimeoutMs; this.socketParams = socketParams;
this.socketTimeoutMs = socketTimeoutMs;
this.wifiScanTimeoutMs = wifiScanTimeoutMs;
if (usePublicEndpoints) { if (usePublicEndpoints) {
setServicePublicEndpoint(OWSEC_SERVICE, uCentralSecPublicEndpoint); setServicePublicEndpoint(OWSEC_SERVICE, uCentralSecPublicEndpoint);
@@ -195,7 +198,8 @@ public class UCentralClient {
Map<String, Object> body = new HashMap<>(); Map<String, Object> body = new HashMap<>();
body.put("userId", username); body.put("userId", username);
body.put("password", password); body.put("password", password);
HttpResponse<String> response = httpPost("oauth2", OWSEC_SERVICE, body); HttpResponse<String> response =
httpPost("oauth2", OWSEC_SERVICE, body, null);
if (!response.isSuccess()) { if (!response.isSuccess()) {
logger.error( logger.error(
"Login failed: Response code {}, body: {}", "Login failed: Response code {}, body: {}",
@@ -206,27 +210,146 @@ public class UCentralClient {
} }
// Parse access token from response // Parse access token from response
JSONObject respBody; WebTokenResult token;
try { try {
respBody = new JSONObject(response.getBody()); token = gson.fromJson(response.getBody(), WebTokenResult.class);
} catch (JSONException e) { } catch (JsonSyntaxException e) {
logger.error("Login failed: Unexpected response", e); logger.error("Login failed: Unexpected response", e);
logger.debug("Response body: {}", response.getBody()); logger.debug("Response body: {}", response.getBody());
return false; return false;
} }
if (!respBody.has("access_token")) { if (
token == null || token.access_token == null ||
token.access_token.isEmpty()
) {
logger.error("Login failed: Missing access token"); logger.error("Login failed: Missing access token");
logger.debug("Response body: {}", respBody.toString()); logger.debug("Response body: {}", response.getBody());
return false; return false;
} }
this.accessToken = respBody.getString("access_token"); this.accessToken = token;
this.created = accessToken.created;
this.lastAccess = accessToken.created;
logger.info("Login successful as user: {}", username); logger.info("Login successful as user: {}", username);
logger.debug("Access token: {}", accessToken); logger.debug("Access token: {}", accessToken.access_token);
logger.debug("Refresh token: {}", accessToken.refresh_token);
// Load system endpoints // Load system endpoints
return loadSystemEndpoints(); return loadSystemEndpoints();
} }
/**
* when using public endpoints, refresh the access token if it's expired.
*/
public synchronized void refreshAccessToken() {
if (usePublicEndpoints) {
refreshAccessTokenImpl();
}
}
/**
* Check if the token is completely expired even if
* for a token refresh request
*
* @return true if the refresh token is expired
*/
private boolean isAccessTokenExpired() {
if (accessToken == null) {
return true;
}
return created + accessToken.expires_in <
Instant.now().getEpochSecond();
}
/**
* Check if an access token is expired.
*
* @return true if the access token is expired
*/
private boolean isAccessTokenTimedOut() {
if (accessToken == null) {
return true;
}
return lastAccess + accessToken.idle_timeout <
Instant.now().getEpochSecond();
}
/**
* Refresh the access toke when time out. If the refresh token is expired, login again.
* If the access token is expired, POST a WebTokenRefreshRequest to refresh token.
*/
private void refreshAccessTokenImpl() {
if (!usePublicEndpoints) {
return;
}
if (isAccessTokenExpired()) {
synchronized (this) {
if (isAccessTokenExpired()) {
logger.info("Token is expired, login again");
login();
}
}
} else if (isAccessTokenTimedOut()) {
synchronized (this) {
if (isAccessTokenTimedOut()) {
logger.debug("Access token timed out, refreshing the token");
accessToken = refreshToken();
created = Instant.now().getEpochSecond();
lastAccess = created;
if (accessToken != null) {
logger.debug("Successfully refresh token.");
}else{
logger.error(
"Fail to refresh token with access token: {}",
accessToken.access_token
);
}
}
}
}
}
/**
* POST a WebTokenRefreshRequest to refresh the access token.
*
* @return valid access token if success, otherwise return null.
*/
private WebTokenResult refreshToken() {
if (accessToken == null) {
return null;
}
WebTokenRefreshRequest refreshRequest = new WebTokenRefreshRequest();
refreshRequest.userId = username;
refreshRequest.refreshToken = accessToken.refresh_token;
logger.debug("refresh token: {}", accessToken.refresh_token);
Map<String, Object> parameters =
Collections.singletonMap("grant_type", "refresh_token");
HttpResponse<String> response =
httpPost(
"oauth2",
OWSEC_SERVICE,
refreshRequest,
parameters
);
if (!response.isSuccess()) {
logger.error(
"Failed to refresh token: Response code {}, body: {}",
response.getStatus(),
response.getBody()
);
return null;
}
try {
return gson.fromJson(response.getBody(), WebTokenResult.class);
} catch (JsonSyntaxException e) {
logger.error(
"Failed to serialize WebTokenResult: Unexpected response:",
e
);
logger.debug("Response body: {}", response.getBody());
return null;
}
}
/** Read system endpoint URLs from uCentralSec. */ /** Read system endpoint URLs from uCentralSec. */
private boolean loadSystemEndpoints() { private boolean loadSystemEndpoints() {
// Make request // Make request
@@ -316,8 +439,8 @@ public class UCentralClient {
endpoint, endpoint,
service, service,
parameters, parameters,
connectTimeoutMs, socketParams.connectTimeoutMs,
socketTimeoutMs socketParams.socketTimeoutMs
); );
} }
@@ -335,8 +458,12 @@ public class UCentralClient {
.connectTimeout(connectTimeoutMs) .connectTimeout(connectTimeoutMs)
.socketTimeout(socketTimeoutMs); .socketTimeout(socketTimeoutMs);
if (usePublicEndpoints) { if (usePublicEndpoints) {
if (accessToken != null) { if (!isAccessTokenExpired()) {
req.header("Authorization", "Bearer " + accessToken); req.header(
"Authorization",
"Bearer " + accessToken.access_token
);
lastAccess = Instant.now().getEpochSecond();
} }
} else { } else {
req req
@@ -350,26 +477,29 @@ public class UCentralClient {
} }
} }
/** Send a POST request with a JSON body. */ /** Send a POST request with a JSON body and query params. */
private HttpResponse<String> httpPost( private HttpResponse<String> httpPost(
String endpoint, String endpoint,
String service, String service,
Object body Object body,
Map<String, Object> parameters
) { ) {
return httpPost( return httpPost(
endpoint, endpoint,
service, service,
body, body,
connectTimeoutMs, parameters,
socketTimeoutMs socketParams.connectTimeoutMs,
socketParams.socketTimeoutMs
); );
} }
/** Send a POST request with a JSON body using given timeout values. */ /** Send a POST request with a JSON body and query params using given timeout values. */
private HttpResponse<String> httpPost( private HttpResponse<String> httpPost(
String endpoint, String endpoint,
String service, String service,
Object body, Object body,
Map<String, Object> parameters,
int connectTimeoutMs, int connectTimeoutMs,
int socketTimeoutMs int socketTimeoutMs
) { ) {
@@ -378,9 +508,16 @@ public class UCentralClient {
.header("accept", "application/json") .header("accept", "application/json")
.connectTimeout(connectTimeoutMs) .connectTimeout(connectTimeoutMs)
.socketTimeout(socketTimeoutMs); .socketTimeout(socketTimeoutMs);
if (parameters != null && !parameters.isEmpty()) {
req.queryString(parameters);
}
if (usePublicEndpoints) { if (usePublicEndpoints) {
if (accessToken != null) { if (!isAccessTokenExpired()) {
req.header("Authorization", "Bearer " + accessToken); req.header(
"Authorization",
"Bearer " + accessToken.access_token
);
lastAccess = Instant.now().getEpochSecond();
} }
} else { } else {
req req
@@ -465,8 +602,9 @@ public class UCentralClient {
String.format("device/%s/wifiscan", serialNumber), String.format("device/%s/wifiscan", serialNumber),
OWGW_SERVICE, OWGW_SERVICE,
req, req,
connectTimeoutMs, null,
wifiScanTimeoutMs socketParams.connectTimeoutMs,
socketParams.wifiScanTimeoutMs
); );
if (!response.isSuccess()) { if (!response.isSuccess()) {
logger.error("Error: {}", response.getBody()); logger.error("Error: {}", response.getBody());
@@ -493,7 +631,8 @@ public class UCentralClient {
HttpResponse<String> response = httpPost( HttpResponse<String> response = httpPost(
String.format("device/%s/configure", serialNumber), String.format("device/%s/configure", serialNumber),
OWGW_SERVICE, OWGW_SERVICE,
req req,
null
); );
if (!response.isSuccess()) { if (!response.isSuccess()) {
logger.error("Error: {}", response.getBody()); logger.error("Error: {}", response.getBody());

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk; package com.facebook.openwifirrm.ucentral;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@@ -22,7 +22,7 @@ public final class UCentralConstants {
public static final String BAND_2G = "2G"; public static final String BAND_2G = "2G";
/** String of the 5 GHz band */ /** String of the 5 GHz band */
public static final String BAND_5G = "5G"; public static final String BAND_5G = "5G";
/** List of all bands ordered from lowest to highest */ /** List of all bands */
public static final List<String> BANDS = Collections public static final List<String> BANDS = Collections
.unmodifiableList(Arrays.asList(BAND_2G, BAND_5G)); .unmodifiableList(Arrays.asList(BAND_2G, BAND_5G));

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.kafka; package com.facebook.openwifirrm.ucentral;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
@@ -29,8 +29,7 @@ import org.apache.kafka.common.serialization.StringDeserializer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralClient; import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.kafka; package com.facebook.openwifirrm.ucentral;
import java.util.Properties; import java.util.Properties;
@@ -18,7 +18,7 @@ import org.apache.kafka.common.serialization.StringSerializer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
import com.google.gson.Gson; import com.google.gson.Gson;
/** /**

View File

@@ -6,11 +6,11 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk; package com.facebook.openwifirrm.ucentral;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -21,11 +21,14 @@ import java.util.Set;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.ies.Country; import com.facebook.openwifirrm.RRMConfig;
import com.facebook.openwifi.cloudsdk.ies.LocalPowerConstraint; import com.facebook.openwifirrm.Utils;
import com.facebook.openwifi.cloudsdk.ies.QbssLoad; import com.facebook.openwifirrm.optimizers.channel.ChannelOptimizer;
import com.facebook.openwifi.cloudsdk.ies.TxPwrInfo; import com.facebook.openwifirrm.ucentral.informationelement.Country;
import com.facebook.openwifi.cloudsdk.models.ap.State; import com.facebook.openwifirrm.ucentral.informationelement.LocalPowerConstraint;
import com.facebook.openwifirrm.ucentral.informationelement.QbssLoad;
import com.facebook.openwifirrm.ucentral.informationelement.TxPwrInfo;
import com.facebook.openwifirrm.ucentral.models.State;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
@@ -44,47 +47,25 @@ public class UCentralUtils {
/** The Gson instance. */ /** The Gson instance. */
private static final Gson gson = new Gson(); private static final Gson gson = new Gson();
/** Map from band to ordered (increasing) list of available channels */ /** Map of band to the band-specific lowest available channel*/
public static final Map<String, List<Integer>> AVAILABLE_CHANNELS_BAND = public static final Map<String, Integer> LOWER_CHANNEL_LIMIT =
Collections new HashMap<>();
.unmodifiableMap(buildBandToChannelsMap()); static {
UCentralUtils.LOWER_CHANNEL_LIMIT.put(UCentralConstants.BAND_2G, 1);
UCentralUtils.LOWER_CHANNEL_LIMIT.put(UCentralConstants.BAND_5G, 36);
}
/** Map of band to the band-specific highest available channel*/
public static final Map<String, Integer> UPPER_CHANNEL_LIMIT =
new HashMap<>();
static {
UCentralUtils.UPPER_CHANNEL_LIMIT.put(UCentralConstants.BAND_2G, 11);
UCentralUtils.UPPER_CHANNEL_LIMIT.put(UCentralConstants.BAND_5G, 165);
}
// This class should not be instantiated. // This class should not be instantiated.
private UCentralUtils() {} private UCentralUtils() {}
/**
* Builds map from band to ordered (increasing) list of available channels.
*/
private static Map<String, List<Integer>> buildBandToChannelsMap() {
Map<String, List<Integer>> bandToChannelsMap = new HashMap<>();
bandToChannelsMap.put(
UCentralConstants.BAND_5G,
Collections.unmodifiableList(
Arrays.asList(36, 40, 44, 48, 149, 153, 157, 161, 165)
)
);
// NOTE: later, we may want to support channels 12, 13, and/or 14, if
// the AP supports it and OWF vendors will use them
bandToChannelsMap.put(
UCentralConstants.BAND_2G,
Collections.unmodifiableList(
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
)
);
return bandToChannelsMap;
}
/** Return the lowest available channel for the given band */
public static int getLowerChannelLimit(String band) {
return AVAILABLE_CHANNELS_BAND.get(band).get(0);
}
/** Return the lowest available channel for the given band */
public static int getUpperChannelLimit(String band) {
List<Integer> channels = AVAILABLE_CHANNELS_BAND.get(band);
return channels.get(channels.size() - 1);
}
/** /**
* Parse a JSON wifi scan result into a list of WifiScanEntry objects. * Parse a JSON wifi scan result into a list of WifiScanEntry objects.
* *
@@ -168,7 +149,7 @@ public class UCentralUtils {
break; break;
} }
} catch (Exception e) { } catch (Exception e) {
logger.error(String.format("Skipping invalid IE %s", ie), e); logger.debug("Skipping invalid IE {}", ie);
continue; continue;
} }
} }
@@ -418,6 +399,47 @@ public class UCentralUtils {
return bssidMap; return bssidMap;
} }
/** Generate the RRM service key. */
public static String generateServiceKey(
RRMConfig.ServiceConfig serviceConfig
) {
try {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.update(serviceConfig.publicEndpoint.getBytes());
sha256.update(serviceConfig.privateEndpoint.getBytes());
return Utils.bytesToHex(sha256.digest());
} catch (NoSuchAlgorithmException e) {
logger.error("Unable to generate service key", e);
return "";
}
}
/**
* Converts channel number to that channel's center frequency in MHz.
*
* @param channel channel number. See
* {@link ChannelOptimizer#AVAILABLE_CHANNELS_BAND} for channels
* in each band.
* @return the center frequency of the given channel in MHz
*/
public static int channelToFrequencyMHz(int channel) {
if (
ChannelOptimizer.AVAILABLE_CHANNELS_BAND
.get(UCentralConstants.BAND_2G)
.contains(channel)
) {
return 2407 + 5 * channel;
} else if (
ChannelOptimizer.AVAILABLE_CHANNELS_BAND
.get(UCentralConstants.BAND_5G)
.contains(channel)
) {
return 5000 + channel;
} else {
throw new IllegalArgumentException("Must provide a valid channel.");
}
}
/** /**
* Determines if the given channel is in the given band. * Determines if the given channel is in the given band.
* *
@@ -426,17 +448,25 @@ public class UCentralUtils {
* @return true if the given channel is in the given band; false otherwise * @return true if the given channel is in the given band; false otherwise
*/ */
public static boolean isChannelInBand(int channel, String band) { public static boolean isChannelInBand(int channel, String band) {
return AVAILABLE_CHANNELS_BAND.get(band).contains(channel); return LOWER_CHANNEL_LIMIT.get(band) <= channel &&
channel <= UPPER_CHANNEL_LIMIT.get(band);
} }
/** Return which band contains the given frequency (MHz). */ /**
public static String freqToBand(int freqMHz) { * Given the channel, gets the band by checking lower bound and upper bound
if (2412 <= freqMHz && freqMHz <= 2484) { * of each band
return "2G"; *
} else { * @param channel channel number
return "5G"; * @return band if the channel can be mapped to a valid band; null otherwise
*/
public static String getBandFromChannel(int channel) {
for (String band : UCentralConstants.BANDS) {
if (isChannelInBand(channel, band)) {
return band;
} }
} }
return null;
}
/** /**
* Tries to parse channel width, if it encounters an error it will return null. * Tries to parse channel width, if it encounters an error it will return null.

View File

@@ -6,11 +6,11 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk; package com.facebook.openwifirrm.ucentral;
import java.util.Objects; import java.util.Objects;
import com.facebook.openwifi.cloudsdk.models.ap.WifiScanEntryResult; import com.facebook.openwifirrm.ucentral.models.WifiScanEntryResult;
/** /**
* Extends {@link WifiScanEntryResult} to track the response time of the entry. * Extends {@link WifiScanEntryResult} to track the response time of the entry.

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
public class AclTemplate { public class AclTemplate {
public boolean Read; public boolean Read;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
public class DeviceConfigureRequest { public class DeviceConfigureRequest {
public String serialNumber; public String serialNumber;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
import java.util.List; import java.util.List;

View File

@@ -6,6 +6,6 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
public enum DeviceType { AP, SWITCH, IOT, MESH } public enum DeviceType { AP, SWITCH, IOT, MESH }

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
import java.util.List; import java.util.List;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
public class MfaAuthInfo { public class MfaAuthInfo {
public boolean enabled; public boolean enabled;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
public class MobilePhoneNumber { public class MobilePhoneNumber {
public String number; public String number;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
public class NoteInfo { public class NoteInfo {
public long created; public long created;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
public class ServiceEvent { public class ServiceEvent {
public static final String EVENT_JOIN = "join"; public static final String EVENT_JOIN = "join";

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
import java.util.List; import java.util.List;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
import java.util.List; import java.util.List;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
public class TokenValidationResult { public class TokenValidationResult {
public UserInfo userInfo; public UserInfo userInfo;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
import java.util.List; import java.util.List;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
import java.util.List; import java.util.List;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
public enum VerifiedCertificate { public enum VerifiedCertificate {
NO_CERTIFICATE, VALID_CERTIFICATE, MISMATCH_SERIAL, VERIFIED NO_CERTIFICATE, VALID_CERTIFICATE, MISMATCH_SERIAL, VERIFIED

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
public class WebTokenAclTemplate { public class WebTokenAclTemplate {
public AclTemplate aclTemplate; public AclTemplate aclTemplate;

View File

@@ -0,0 +1,14 @@
/*
* 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.openwifirrm.ucentral.gw.models;
public class WebTokenRefreshRequest {
public String userId;
public String refreshToken;
}

View File

@@ -6,13 +6,13 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
public class WebTokenResult { public class WebTokenResult {
public String access_token; public String access_token;
public String refresh_token; public String refresh_token;
public String token_type; public String token_type;
public long expires_in; public int expires_in;
public int idle_timeout; public int idle_timeout;
public String username; public String username;
public long created; public long created;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.gw; package com.facebook.openwifirrm.ucentral.gw.models;
import java.util.List; import java.util.List;

View File

@@ -6,13 +6,16 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.ies; package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@@ -22,6 +25,8 @@ import com.google.gson.JsonObject;
* javadocs is taken from the specification. * javadocs is taken from the specification.
*/ */
public class Country { public class Country {
private static final Logger logger = LoggerFactory.getLogger(Country.class);
/** Defined in 802.11 */ /** Defined in 802.11 */
public static final int TYPE = 7; public static final int TYPE = 7;
@@ -120,9 +125,8 @@ public class Country {
JsonElement constraintsObject = contents.get("constraints"); JsonElement constraintsObject = contents.get("constraints");
if (constraintsObject != null) { if (constraintsObject != null) {
for (JsonElement jsonElement : constraintsObject.getAsJsonArray()) { for (JsonElement jsonElement : constraintsObject.getAsJsonArray()) {
JsonObject innerElem = jsonElement.getAsJsonObject();
CountryInfo countryInfo = CountryInfo countryInfo =
CountryInfo.parse(innerElem.get("Country Info").getAsJsonObject()); CountryInfo.parse(jsonElement.getAsJsonObject());
constraints.add(countryInfo); constraints.add(countryInfo);
} }
} }

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.ies; package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.ies; package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.Objects; import java.util.Objects;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.ies; package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.Objects; import java.util.Objects;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.ies; package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.Objects; import java.util.Objects;
@@ -25,7 +25,7 @@ public class TxPwrInfo {
public static final int TYPE = 195; public static final int TYPE = 195;
/** Local maximum transmit power for 20 MHz. Required field. */ /** Local maximum transmit power for 20 MHz. Required field. */
public final int localMaxTxPwrConstraint20MHz; public final Integer localMaxTxPwrConstraint20MHz;
/** Local maximum transmit power for 40 MHz. Optional field. */ /** Local maximum transmit power for 40 MHz. Optional field. */
public final Integer localMaxTxPwrConstraint40MHz; public final Integer localMaxTxPwrConstraint40MHz;
/** Local maximum transmit power for 80 MHz. Optional field. */ /** Local maximum transmit power for 80 MHz. Optional field. */
@@ -36,9 +36,9 @@ public class TxPwrInfo {
/** Constructor */ /** Constructor */
public TxPwrInfo( public TxPwrInfo(
int localMaxTxPwrConstraint20MHz, int localMaxTxPwrConstraint20MHz,
Integer localMaxTxPwrConstraint40MHz, int localMaxTxPwrConstraint40MHz,
Integer localMaxTxPwrConstraint80MHz, int localMaxTxPwrConstraint80MHz,
Integer localMaxTxPwrConstraint160MHz int localMaxTxPwrConstraint160MHz
) { ) {
this.localMaxTxPwrConstraint20MHz = localMaxTxPwrConstraint20MHz; this.localMaxTxPwrConstraint20MHz = localMaxTxPwrConstraint20MHz;
this.localMaxTxPwrConstraint40MHz = localMaxTxPwrConstraint40MHz; this.localMaxTxPwrConstraint40MHz = localMaxTxPwrConstraint40MHz;
@@ -48,17 +48,16 @@ public class TxPwrInfo {
/** Parse TxPwrInfo IE from appropriate Json object. */ /** Parse TxPwrInfo IE from appropriate Json object. */
public static TxPwrInfo parse(JsonObject contents) { public static TxPwrInfo parse(JsonObject contents) {
JsonObject innerObj = contents.get("Tx Pwr Info").getAsJsonObject();
// required field // required field
int localMaxTxPwrConstraint20MHz = int localMaxTxPwrConstraint20MHz =
innerObj.get("Local Max Tx Pwr Constraint 20MHz").getAsInt(); contents.get("Local Max Tx Pwr Constraint 20MHz").getAsInt();
// optional field // optional field
Integer localMaxTxPwrConstraint40MHz = Integer localMaxTxPwrConstraint40MHz =
parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 40MHz"); parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
Integer localMaxTxPwrConstraint80MHz = Integer localMaxTxPwrConstraint80MHz =
parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 80MHz"); parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
Integer localMaxTxPwrConstraint160MHz = Integer localMaxTxPwrConstraint160MHz =
parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 160MHz"); parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
return new TxPwrInfo( return new TxPwrInfo(
localMaxTxPwrConstraint20MHz, localMaxTxPwrConstraint20MHz,
localMaxTxPwrConstraint40MHz, localMaxTxPwrConstraint40MHz,

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.ies; package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.ap; package com.facebook.openwifirrm.ucentral.models;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.ap; package com.facebook.openwifirrm.ucentral.models;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.ap; package com.facebook.openwifirrm.ucentral.models;
import java.util.Objects; import java.util.Objects;

View File

@@ -6,11 +6,11 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.prov; package com.facebook.openwifirrm.ucentral.prov.models;
import java.util.List; import java.util.List;
import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo; import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
public class DeviceConfiguration { public class DeviceConfiguration {
public static class DeviceConfigurationElement { public static class DeviceConfigurationElement {

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.prov; package com.facebook.openwifirrm.ucentral.prov.models;
public class DeviceRules { public class DeviceRules {
public String rcOnly; public String rcOnly;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.prov; package com.facebook.openwifirrm.ucentral.prov.models;
import java.util.ArrayList; import java.util.ArrayList;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.prov; package com.facebook.openwifirrm.ucentral.prov.models;
public class DiGraphEntry { public class DiGraphEntry {
public String parent; public String parent;

View File

@@ -6,11 +6,11 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.prov; package com.facebook.openwifirrm.ucentral.prov.models;
import java.util.List; import java.util.List;
import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo; import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
public class Entity { public class Entity {
// from ObjectInfo // from ObjectInfo

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.prov; package com.facebook.openwifirrm.ucentral.prov.models;
import java.util.List; import java.util.List;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.prov; package com.facebook.openwifirrm.ucentral.prov.models;
import java.util.List; import java.util.List;

View File

@@ -6,11 +6,11 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.prov; package com.facebook.openwifirrm.ucentral.prov.models;
import java.util.List; import java.util.List;
import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo; import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
public class InventoryTag { public class InventoryTag {
// from ObjectInfo // from ObjectInfo

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
package com.facebook.openwifi.cloudsdk.models.prov; package com.facebook.openwifirrm.ucentral.prov.models;
import java.util.List; import java.util.List;

Some files were not shown because too many files have changed in this diff Show More