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'
cache: maven
- name: Build with Maven
run: mvn javadoc:aggregate
run: mvn javadoc:javadoc
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:

11
.gitignore vendored
View File

@@ -1,9 +1,12 @@
/target/
*/target/
/target
/*.log*
/device_config.json
/settings.json
/topology.json
# Eclipse
.settings/
bin/
/.settings/
/bin/
.metadata
.classpath
.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
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
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
WORKDIR /usr/src/java
COPY docker-entrypoint.sh /
COPY --from=build /usr/src/java/owrrm/target/openwifi-rrm.jar /usr/local/bin/
EXPOSE 16789 16790
COPY --from=build /usr/src/java/target/openwifi-rrm.jar /usr/local/bin/
EXPOSE 16789
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["java", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", \
"-jar", "/usr/local/bin/openwifi-rrm.jar", \

View File

@@ -1,10 +1,12 @@
# 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 is an [Apache Maven] project with the following modules:
* `lib-cloudsdk` - OpenWiFi CloudSDK Java Library
* `owrrm` - OpenWiFi RRM Service
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).
## Requirements
* **Running:** JRE 11.
@@ -14,7 +16,7 @@ This is an [Apache Maven] project with the following modules:
```
$ 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
[Dockerfile](Dockerfile).
@@ -25,7 +27,34 @@ $ mvn test
```
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
[spotless/eclipse-java-formatter.xml](spotless/eclipse-java-formatter.xml)).
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
protocol: TCP
restapiinternal:
servicePort: 16790
targetPort: 16790
servicePort: 17007
targetPort: 17007
protocol: TCP
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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.facebook</groupId>
<artifactId>openwifi-base</artifactId>
<artifactId>openwifi-rrm</artifactId>
<version>2.7.0</version>
<packaging>pom</packaging>
<modules>
<module>lib-cloudsdk</module>
<module>owrrm</module>
</modules>
<properties>
<!-- Hack for static files located in root project -->
<myproject.root>${project.basedir}</myproject.root>
<java.version>11</java.version>
<slf4j.version>1.7.32</slf4j.version>
<junit.version>5.7.2</junit.version>
<swagger.version>2.1.10</swagger.version>
<mainClassName>com.facebook.openwifirrm.Launcher</mainClassName>
<appendVersionString></appendVersionString>
<!-- do not abort builds on autoformatter errors -->
<spotless.check.skip>true</spotless.check.skip>
</properties>
<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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -38,7 +48,6 @@
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
<configuration>
@@ -59,6 +68,13 @@
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${mainClassName}</Main-Class>
</manifestEntries>
</transformer>
</transformers>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
@@ -107,13 +123,13 @@
<configuration>
<java>
<eclipse>
<file>${myproject.root}/spotless/eclipse-java-formatter.xml</file>
<file>${project.basedir}/spotless/eclipse-java-formatter.xml</file>
<version>4.12.0</version>
</eclipse>
<trimTrailingWhitespace />
<removeUnusedImports/>
<licenseHeader>
<file>${myproject.root}/spotless/license-header.txt</file>
<file>${project.basedir}/spotless/license-header.txt</file>
</licenseHeader>
</java>
</configuration>
@@ -127,9 +143,7 @@
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
@@ -145,11 +159,13 @@
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.picocli</groupId>
@@ -211,11 +227,5 @@
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

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

View File

@@ -6,7 +6,7 @@
* 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.util.List;

View File

@@ -6,7 +6,7 @@
* 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.FileNotFoundException;
@@ -47,17 +47,17 @@ public class DeviceDataManager {
private final ReadWriteLock topologyLock = new ReentrantReadWriteLock();
/** Lock on {@link #deviceLayeredConfig}. */
public final ReadWriteLock deviceLayeredConfigLock =
private final ReadWriteLock deviceLayeredConfigLock =
new ReentrantReadWriteLock();
/** The current device topology. */
public DeviceTopology topology;
private DeviceTopology topology;
/** The current layered device config. */
public DeviceLayeredConfig deviceLayeredConfig;
private DeviceLayeredConfig deviceLayeredConfig;
/** The cached device configs (map of serial number to computed config). */
public Map<String, DeviceConfig> cachedDeviceConfigs =
private Map<String, DeviceConfig> cachedDeviceConfigs =
new ConcurrentHashMap<>();
/** 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.
*/
package com.facebook.openwifi.rrm;
package com.facebook.openwifirrm;
import java.util.Map;
import java.util.TreeMap;

View File

@@ -6,7 +6,7 @@
* 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.TreeMap;

View File

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

View File

@@ -6,7 +6,7 @@
* 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.List;
@@ -18,18 +18,18 @@ import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralClient;
import com.facebook.openwifi.cloudsdk.kafka.KafkaRunner;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaProducer;
import com.facebook.openwifi.cloudsdk.models.gw.SystemInfoResults;
import com.facebook.openwifi.rrm.modules.ApiServer;
import com.facebook.openwifi.rrm.modules.ConfigManager;
import com.facebook.openwifi.rrm.modules.DataCollector;
import com.facebook.openwifi.rrm.modules.Modeler;
import com.facebook.openwifi.rrm.modules.ProvMonitor;
import com.facebook.openwifi.rrm.modules.RRMScheduler;
import com.facebook.openwifi.rrm.mysql.DatabaseManager;
import com.facebook.openwifirrm.modules.ApiServer;
import com.facebook.openwifirrm.modules.ConfigManager;
import com.facebook.openwifirrm.modules.DataCollector;
import com.facebook.openwifirrm.modules.Modeler;
import com.facebook.openwifirrm.modules.ProvMonitor;
import com.facebook.openwifirrm.modules.RRMScheduler;
import com.facebook.openwifirrm.mysql.DatabaseManager;
import com.facebook.openwifirrm.ucentral.KafkaRunner;
import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer;
import com.facebook.openwifirrm.ucentral.UCentralKafkaProducer;
import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults;
/**
* RRM service runner.

View File

@@ -6,7 +6,7 @@
* 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.Map;
@@ -14,17 +14,17 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.rrm.modules.ConfigManager;
import com.facebook.openwifi.rrm.modules.Modeler;
import com.facebook.openwifi.rrm.optimizers.channel.ChannelOptimizer;
import com.facebook.openwifi.rrm.optimizers.channel.LeastUsedChannelOptimizer;
import com.facebook.openwifi.rrm.optimizers.channel.RandomChannelInitializer;
import com.facebook.openwifi.rrm.optimizers.channel.UnmanagedApAwareChannelOptimizer;
import com.facebook.openwifi.rrm.optimizers.tpc.LocationBasedOptimalTPC;
import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApApTPC;
import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApClientTPC;
import com.facebook.openwifi.rrm.optimizers.tpc.RandomTxPowerInitializer;
import com.facebook.openwifi.rrm.optimizers.tpc.TPC;
import com.facebook.openwifirrm.modules.ConfigManager;
import com.facebook.openwifirrm.modules.Modeler;
import com.facebook.openwifirrm.optimizers.channel.ChannelOptimizer;
import com.facebook.openwifirrm.optimizers.channel.LeastUsedChannelOptimizer;
import com.facebook.openwifirrm.optimizers.channel.RandomChannelInitializer;
import com.facebook.openwifirrm.optimizers.channel.UnmanagedApAwareChannelOptimizer;
import com.facebook.openwifirrm.optimizers.tpc.LocationBasedOptimalTPC;
import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApApTPC;
import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApClientTPC;
import com.facebook.openwifirrm.optimizers.tpc.RandomTxPowerInitializer;
import com.facebook.openwifirrm.optimizers.tpc.TPC;
/**
* RRM algorithm model and utility methods.

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.openwifi.rrm;
package com.facebook.openwifirrm;
import java.util.Map;
@@ -34,7 +34,7 @@ public class RRMConfig {
* Private endpoint for the RRM service
* ({@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
@@ -60,7 +60,7 @@ public class RRMConfig {
* ({@code SERVICECONFIG_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. */
@@ -309,12 +309,6 @@ public class RRMConfig {
* ({@code MODELERPARAMS_WIFISCANBUFFERSIZE})
*/
public int wifiScanBufferSize = 10;
/**
* Maximum rounds of States to store per device
* ({@code MODELERPARAMS_STATEBUFFERSIZE})
*/
public int stateBufferSize = 10;
}
/** Modeler parameters. */
@@ -325,16 +319,10 @@ public class RRMConfig {
*/
public class ApiServerParams {
/**
* The HTTP port to listen on for internal traffic, or -1 to disable
* ({@code APISERVERPARAMS_INTERNALHTTPPORT})
* The HTTP port to listen on, or -1 to disable
* ({@code APISERVERPARAMS_HTTPPORT})
*/
public int internalHttpPort = 16790;
/**
* The HTTP port to listen on for external traffic, or -1 to disable
* ({@code APISERVERPARAMS_EXTERNALHTTPPORT})
*/
public int externalHttpPort = 16789;
public int httpPort = 16789;
/**
* Comma-separated list of all allowed CORS domains (exact match
@@ -544,16 +532,10 @@ public class RRMConfig {
if ((v = env.get("MODELERPARAMS_WIFISCANBUFFERSIZE")) != null) {
modelerParams.wifiScanBufferSize = Integer.parseInt(v);
}
if ((v = env.get("MODELERPARAMS_STATEBUFFERSIZE")) != null) {
modelerParams.stateBufferSize = Integer.parseInt(v);
}
ModuleConfig.ApiServerParams apiServerParams =
config.moduleConfig.apiServerParams;
if ((v = env.get("APISERVERPARAMS_INTERNALHTTPPORT")) != null) {
apiServerParams.internalHttpPort = Integer.parseInt(v);
}
if ((v = env.get("APISERVERPARAMS_EXTERNALHTTPPORT")) != null) {
apiServerParams.externalHttpPort = Integer.parseInt(v);
if ((v = env.get("APISERVERPARAMS_HTTPPORT")) != null) {
apiServerParams.httpPort = Integer.parseInt(v);
}
if ((v = env.get("APISERVERPARAMS_CORSDOMAINLIST")) != null) {
apiServerParams.corsDomainList = v;

View File

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

View File

@@ -6,7 +6,7 @@
* 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.FileNotFoundException;
@@ -16,8 +16,6 @@ import java.io.InputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;
@@ -25,8 +23,6 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -35,8 +31,6 @@ import com.google.gson.GsonBuilder;
* Generic utility methods.
*/
public class Utils {
private static final Logger logger = LoggerFactory.getLogger(Utils.class);
/** Hex value array for use in {@link #longToMac(long)}. */
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) {
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.
*/
package com.facebook.openwifi.rrm;
package com.facebook.openwifirrm;
import picocli.CommandLine.IVersionProvider;

View File

@@ -6,7 +6,7 @@
* 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.

View File

@@ -6,7 +6,7 @@
* 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.

View File

@@ -6,7 +6,7 @@
* 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.URI;
@@ -35,33 +35,31 @@ import org.reflections.util.ConfigurationBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralClient;
import com.facebook.openwifi.cloudsdk.models.gw.SystemInfoResults;
import com.facebook.openwifi.cloudsdk.models.gw.TokenValidationResult;
import com.facebook.openwifi.cloudsdk.models.prov.rrm.Algorithm;
import com.facebook.openwifi.cloudsdk.models.prov.rrm.Provider;
import com.facebook.openwifi.rrm.CustomJettyServerFactory;
import com.facebook.openwifi.rrm.DeviceConfig;
import com.facebook.openwifi.rrm.DeviceDataManager;
import com.facebook.openwifi.rrm.DeviceLayeredConfig;
import com.facebook.openwifi.rrm.DeviceTopology;
import com.facebook.openwifi.rrm.RRMAlgorithm;
import com.facebook.openwifi.rrm.Utils;
import com.facebook.openwifi.rrm.VersionProvider;
import com.facebook.openwifi.rrm.RRMConfig.ServiceConfig;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ApiServerParams;
import com.facebook.openwifi.rrm.Utils.LruCache;
import com.facebook.openwifi.rrm.optimizers.channel.LeastUsedChannelOptimizer;
import com.facebook.openwifi.rrm.optimizers.channel.RandomChannelInitializer;
import com.facebook.openwifi.rrm.optimizers.channel.UnmanagedApAwareChannelOptimizer;
import com.facebook.openwifi.rrm.optimizers.tpc.LocationBasedOptimalTPC;
import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApApTPC;
import com.facebook.openwifi.rrm.optimizers.tpc.MeasurementBasedApClientTPC;
import com.facebook.openwifi.rrm.optimizers.tpc.RandomTxPowerInitializer;
import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifirrm.DeviceLayeredConfig;
import com.facebook.openwifirrm.DeviceTopology;
import com.facebook.openwifirrm.RRMAlgorithm;
import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ApiServerParams;
import com.facebook.openwifirrm.RRMConfig.ServiceConfig;
import com.facebook.openwifirrm.Utils.LruCache;
import com.facebook.openwifirrm.VersionProvider;
import com.facebook.openwifirrm.optimizers.channel.LeastUsedChannelOptimizer;
import com.facebook.openwifirrm.optimizers.channel.RandomChannelInitializer;
import com.facebook.openwifirrm.optimizers.channel.UnmanagedApAwareChannelOptimizer;
import com.facebook.openwifirrm.optimizers.tpc.LocationBasedOptimalTPC;
import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApApTPC;
import com.facebook.openwifirrm.optimizers.tpc.MeasurementBasedApClientTPC;
import com.facebook.openwifirrm.optimizers.tpc.RandomTxPowerInitializer;
import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults;
import com.facebook.openwifirrm.ucentral.gw.models.TokenValidationResult;
import com.facebook.openwifirrm.ucentral.prov.rrm.models.Algorithm;
import com.facebook.openwifirrm.ucentral.prov.rrm.models.Provider;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.openjdk.jol.info.GraphLayout;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.core.util.Yaml;
import io.swagger.v3.jaxrs2.Reader;
@@ -83,9 +81,7 @@ import io.swagger.v3.oas.models.OpenAPI;
import spark.Request;
import spark.Response;
import spark.Route;
import spark.Service;
import spark.embeddedserver.EmbeddedServers;
import spark.embeddedserver.jetty.EmbeddedJettyFactory;
import spark.Spark;
/**
* HTTP API server.
@@ -114,27 +110,6 @@ public class ApiServer implements Runnable {
private static final Logger logger =
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. */
private final ApiServerParams params;
@@ -189,10 +164,9 @@ public class ApiServer implements Runnable {
UCentralClient client,
RRMScheduler scheduler
) {
this.service = Service.ignite();
this.params = params;
this.serviceConfig = serviceConfig;
this.serviceKey = Utils.generateServiceKey(serviceConfig);
this.serviceKey = UCentralUtils.generateServiceKey(serviceConfig);
this.deviceDataManager = deviceDataManager;
this.configManager = configManager;
this.modeler = modeler;
@@ -220,117 +194,64 @@ public class ApiServer implements Runnable {
return ret;
}
/**
* Block until initialization finishes. Just calls the method on the
* underlying service.
*/
public void awaitInitialization() {
service.awaitInitialization();
}
@Override
public void run() {
this.startTimeMs = System.currentTimeMillis();
if (params.internalHttpPort == -1 && params.externalHttpPort == -1) {
if (params.httpPort == -1) {
logger.info("API server is disabled.");
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) {
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);
Spark.port(params.httpPort);
// Configure API docs hosting
service.staticFiles.location("/public");
service.get("/openapi.yaml", this::getOpenApiYaml);
service.get("/openapi.json", this::getOpenApiJson);
Spark.staticFiles.location("/public");
Spark.get("/openapi.yaml", this::getOpenApiYaml);
Spark.get("/openapi.json", this::getOpenApiJson);
// Install routes
service.before(this::beforeFilter);
service.after(this::afterFilter);
service.options("/*", this::options);
service.get("/api/v1/system", new SystemEndpoint());
service.post("/api/v1/system", new SetSystemEndpoint());
service.get("/api/v1/provider", new ProviderEndpoint());
service.get("/api/v1/algorithms", new AlgorithmsEndpoint());
service.put("/api/v1/runRRM", new RunRRMEndpoint());
service.get("/api/v1/getTopology", new GetTopologyEndpoint());
service.post("/api/v1/setTopology", new SetTopologyEndpoint());
service.get(
Spark.before(this::beforeFilter);
Spark.after(this::afterFilter);
Spark.options("/*", this::options);
Spark.get("/api/v1/system", new SystemEndpoint());
Spark.post("/api/v1/system", new SetSystemEndpoint());
Spark.get("/api/v1/provider", new ProviderEndpoint());
Spark.get("/api/v1/algorithms", new AlgorithmsEndpoint());
Spark.put("/api/v1/runRRM", new RunRRMEndpoint());
Spark.get("/api/v1/getTopology", new GetTopologyEndpoint());
Spark.post("/api/v1/setTopology", new SetTopologyEndpoint());
Spark.get(
"/api/v1/getDeviceLayeredConfig",
new GetDeviceLayeredConfigEndpoint()
);
service.get("/api/v1/getDeviceConfig", new GetDeviceConfigEndpoint());
service.post(
Spark.get("/api/v1/getDeviceConfig", new GetDeviceConfigEndpoint());
Spark.post(
"/api/v1/setDeviceNetworkConfig",
new SetDeviceNetworkConfigEndpoint()
);
service.post(
Spark.post(
"/api/v1/setDeviceZoneConfig",
new SetDeviceZoneConfigEndpoint()
);
service.post(
Spark.post(
"/api/v1/setDeviceApConfig",
new SetDeviceApConfigEndpoint()
);
service.post(
Spark.post(
"/api/v1/modifyDeviceApConfig",
new ModifyDeviceApConfigEndpoint()
);
service.get("/api/v1/currentModel", new GetCurrentModelEndpoint());
service.get("/api/v1/optimizeChannel", new OptimizeChannelEndpoint());
service.get("/api/v1/optimizeTxPower", new OptimizeTxPowerEndpoint());
service.get("/api/v1/memory", new MemoryEndpoint(this));
Spark.get("/api/v1/currentModel", new GetCurrentModelEndpoint());
Spark.get("/api/v1/optimizeChannel", new OptimizeChannelEndpoint());
Spark.get("/api/v1/optimizeTxPower", new OptimizeTxPowerEndpoint());
logger.info(
"API server listening for HTTP internal on port {} and external on port {}",
params.internalHttpPort,
params.externalHttpPort
);
logger.info("API server listening on HTTP port {}", params.httpPort);
}
/** Stop the server. */
public void shutdown() {
service.stop();
}
/**
* Block until stop finishes. Just calls the method on the underlying service.
*/
public void awaitStop() {
service.awaitStop();
Spark.stop();
}
/** Reconstructs a URL. */
@@ -348,18 +269,16 @@ public class ApiServer implements Runnable {
* HTTP 403 response and return false.
*/
private boolean performOpenWifiAuth(Request request, Response response) {
int port = request.port();
boolean internal = port > 0 && port == params.internalHttpPort;
if (internal) {
// TODO check if request came from internal endpoint
boolean internal = true;
String internalName = request.headers("X-INTERNAL-NAME");
if (internalName != null) {
if (internal && internalName != null) {
// Internal request, validate "X-API-KEY"
String apiKey = request.headers("X-API-KEY");
if (apiKey != null && apiKey.equals(serviceKey)) {
// auth success
return true;
}
}
} else {
// External request, validate token:
// Authorization: Bearer <token>
@@ -378,7 +297,7 @@ public class ApiServer implements Runnable {
}
// auth failure
service.halt(403, "Forbidden");
Spark.halt(403, "Forbidden");
return false;
}
@@ -406,11 +325,10 @@ public class ApiServer implements Runnable {
private void beforeFilter(Request request, Response response) {
// Log requests
logger.debug(
"[{}] {} {} on port {}",
"[{}] {} {}",
request.ip(),
request.requestMethod(),
getFullUrl(request.pathInfo(), request.queryString()),
request.port()
getFullUrl(request.pathInfo(), request.queryString())
);
// 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")
public class OptimizeTxPowerEndpoint implements Route {
// Hack for use in @ApiResponse -> @Content -> @Schema

View File

@@ -6,7 +6,7 @@
* 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.HashMap;
@@ -21,13 +21,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralApConfiguration;
import com.facebook.openwifi.cloudsdk.UCentralClient;
import com.facebook.openwifi.cloudsdk.UCentralUtils;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus;
import com.facebook.openwifi.rrm.DeviceConfig;
import com.facebook.openwifi.rrm.DeviceDataManager;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ConfigManagerParams;
import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ConfigManagerParams;
import com.facebook.openwifirrm.ucentral.UCentralApConfiguration;
import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus;
/**
* Device configuration manager module.
@@ -46,7 +46,7 @@ public class ConfigManager implements Runnable {
private final UCentralClient client;
/** Runtime per-device data. */
public class DeviceData {
private class DeviceData {
/** Last received device config. */
public UCentralApConfiguration config;
@@ -55,7 +55,7 @@ public class ConfigManager implements Runnable {
}
/** 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). */
private Thread mainThread;
@@ -171,6 +171,7 @@ public class ConfigManager implements Runnable {
return;
}
}
client.refreshAccessToken();
// Fetch device list
List<DeviceWithStatus> devices = client.getDevices();

View File

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

View File

@@ -6,7 +6,7 @@
* 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.List;
@@ -20,21 +20,21 @@ import java.util.concurrent.LinkedBlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralApConfiguration;
import com.facebook.openwifi.cloudsdk.UCentralClient;
import com.facebook.openwifi.cloudsdk.UCentralUtils;
import com.facebook.openwifi.cloudsdk.WifiScanEntry;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer;
import com.facebook.openwifi.cloudsdk.kafka.UCentralKafkaConsumer.KafkaRecord;
import com.facebook.openwifi.cloudsdk.models.ap.State;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceCapabilities;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus;
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent;
import com.facebook.openwifi.cloudsdk.models.gw.StatisticsRecords;
import com.facebook.openwifi.rrm.DeviceConfig;
import com.facebook.openwifi.rrm.DeviceDataManager;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ModelerParams;
import com.facebook.openwifi.rrm.Utils;
import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ModelerParams;
import com.facebook.openwifirrm.Utils;
import com.facebook.openwifirrm.ucentral.UCentralApConfiguration;
import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer;
import com.facebook.openwifirrm.ucentral.UCentralKafkaConsumer.KafkaRecord;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifirrm.ucentral.gw.models.DeviceCapabilities;
import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus;
import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
import com.facebook.openwifirrm.ucentral.gw.models.StatisticsRecords;
import com.facebook.openwifirrm.ucentral.models.State;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
@@ -50,7 +50,7 @@ public class Modeler implements Runnable {
private final ModelerParams params;
/** The device data manager. */
public final DeviceDataManager deviceDataManager;
private final DeviceDataManager deviceDataManager;
/** The uCentral client instance. */
private final UCentralClient client;
@@ -74,7 +74,7 @@ public class Modeler implements Runnable {
}
/** The blocking data queue. */
public final BlockingQueue<InputData> dataQueue =
private final BlockingQueue<InputData> dataQueue =
new LinkedBlockingQueue<>();
/** Data model representation. */
@@ -93,9 +93,8 @@ public class Modeler implements Runnable {
public Map<String, List<List<WifiScanEntry>>> latestWifiScans =
new ConcurrentHashMap<>();
/** List of latest states per device. */
public Map<String, List<State>> latestStates =
new ConcurrentHashMap<>();
/** List of latest state per device. */
public Map<String, State> latestState = new ConcurrentHashMap<>();
/** List of radio info per device. */
public Map<String, JsonArray> latestDeviceStatusRadios =
@@ -239,6 +238,7 @@ public class Modeler implements Runnable {
return;
}
}
client.refreshAccessToken();
// TODO: backfill data from database?
@@ -268,10 +268,7 @@ public class Modeler implements Runnable {
if (state != null) {
try {
State stateModel = gson.fromJson(state, State.class);
dataModel.latestStates.computeIfAbsent(
device.serialNumber,
k -> new LinkedList<>()
).add(stateModel);
dataModel.latestState.put(device.serialNumber, stateModel);
logger.debug(
"Device {}: added initial state from uCentralGw",
device.serialNumber
@@ -303,17 +300,8 @@ public class Modeler implements Runnable {
if (state != null) {
try {
State stateModel = gson.fromJson(state, State.class);
List<State> latestStatesList = dataModel.latestStates
.computeIfAbsent(
record.serialNumber,
k -> new LinkedList<>()
);
while (
latestStatesList.size() >= params.stateBufferSize
) {
latestStatesList.remove(0);
}
latestStatesList.add(stateModel);
dataModel.latestState
.put(record.serialNumber, stateModel);
stateUpdates.add(record.serialNumber);
} catch (JsonSyntaxException e) {
logger.error(
@@ -436,7 +424,7 @@ public class Modeler implements Runnable {
logger.debug("Removed some wifi scan entries from data model");
}
if (
dataModel.latestStates.entrySet()
dataModel.latestState.entrySet()
.removeIf(e -> !isRRMEnabled(e.getKey()))
) {
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.
*/
package com.facebook.openwifi.rrm.modules;
package com.facebook.openwifirrm.modules;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.AggregatedState;
import com.facebook.openwifi.cloudsdk.WifiScanEntry;
import com.facebook.openwifi.cloudsdk.ies.HTOperation;
import com.facebook.openwifi.cloudsdk.ies.VHTOperation;
import com.facebook.openwifi.cloudsdk.models.ap.State;
import com.facebook.openwifi.cloudsdk.models.ap.State.Interface;
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;
import com.facebook.openwifirrm.aggregators.Aggregator;
import com.facebook.openwifirrm.aggregators.MeanAggregator;
import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifirrm.ucentral.informationelement.HTOperation;
import com.facebook.openwifirrm.ucentral.informationelement.VHTOperation;
/**
* Modeler utilities.
@@ -300,7 +291,7 @@ public class ModelerUtils {
/**
* 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)
*/
public static Map<String, Map<String, WifiScanEntry>> getAggregatedWifiScans(
@@ -390,196 +381,4 @@ public class ModelerUtils {
}
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.
*/
package com.facebook.openwifi.rrm.modules;
package com.facebook.openwifirrm.modules;
import java.util.Arrays;
import java.util.HashMap;
@@ -18,19 +18,19 @@ import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralClient;
import com.facebook.openwifi.cloudsdk.models.prov.InventoryTag;
import com.facebook.openwifi.cloudsdk.models.prov.InventoryTagList;
import com.facebook.openwifi.cloudsdk.models.prov.RRMDetails;
import com.facebook.openwifi.cloudsdk.models.prov.SerialNumberList;
import com.facebook.openwifi.cloudsdk.models.prov.Venue;
import com.facebook.openwifi.cloudsdk.models.prov.VenueList;
import com.facebook.openwifi.rrm.DeviceConfig;
import com.facebook.openwifi.rrm.DeviceDataManager;
import com.facebook.openwifi.rrm.DeviceTopology;
import com.facebook.openwifi.rrm.RRMAlgorithm;
import com.facebook.openwifi.rrm.RRMSchedule;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.ProvMonitorParams;
import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifirrm.DeviceTopology;
import com.facebook.openwifirrm.RRMAlgorithm;
import com.facebook.openwifirrm.RRMConfig.ModuleConfig.ProvMonitorParams;
import com.facebook.openwifirrm.RRMSchedule;
import com.facebook.openwifirrm.ucentral.UCentralClient;
import com.facebook.openwifirrm.ucentral.prov.models.InventoryTag;
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.Venue;
import com.facebook.openwifirrm.ucentral.prov.models.VenueList;
/**
* owprov monitor module.
@@ -103,6 +103,7 @@ public class ProvMonitor implements Runnable {
return;
}
}
client.refreshAccessToken();
// Fetch data from owprov
// 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.
*/
package com.facebook.openwifi.rrm.modules;
package com.facebook.openwifirrm.modules;
import java.text.ParseException;
import java.util.Arrays;
@@ -32,11 +32,11 @@ import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.rrm.DeviceConfig;
import com.facebook.openwifi.rrm.DeviceDataManager;
import com.facebook.openwifi.rrm.RRMAlgorithm;
import com.facebook.openwifi.rrm.RRMSchedule;
import com.facebook.openwifi.rrm.RRMConfig.ModuleConfig.RRMSchedulerParams;
import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifirrm.RRMAlgorithm;
import com.facebook.openwifirrm.RRMSchedule;
import com.facebook.openwifirrm.RRMConfig.ModuleConfig.RRMSchedulerParams;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

View File

@@ -6,7 +6,7 @@
* 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.DriverManager;
@@ -26,9 +26,9 @@ import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.WifiScanEntry;
import com.facebook.openwifi.cloudsdk.models.ap.State;
import com.facebook.openwifi.rrm.Utils;
import com.facebook.openwifirrm.Utils;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifirrm.ucentral.models.State;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

View File

@@ -6,7 +6,7 @@
* 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.

View File

@@ -6,7 +6,7 @@
* 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.Arrays;
@@ -18,18 +18,16 @@ import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralConstants;
import com.facebook.openwifi.cloudsdk.UCentralUtils;
import com.facebook.openwifi.cloudsdk.WifiScanEntry;
import com.facebook.openwifi.cloudsdk.ies.HTOperation;
import com.facebook.openwifi.cloudsdk.ies.VHTOperation;
import com.facebook.openwifi.cloudsdk.models.ap.State;
import com.facebook.openwifi.rrm.DeviceConfig;
import com.facebook.openwifi.rrm.DeviceDataManager;
import com.facebook.openwifi.rrm.modules.ConfigManager;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel;
import com.facebook.openwifi.rrm.modules.ModelerUtils;
import com.google.gson.JsonObject;
import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifirrm.modules.ConfigManager;
import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.ucentral.UCentralConstants;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifirrm.ucentral.informationelement.HTOperation;
import com.facebook.openwifirrm.ucentral.informationelement.VHTOperation;
import com.facebook.openwifirrm.ucentral.models.State;
/**
* Channel optimizer base class.
@@ -41,6 +39,24 @@ public abstract class ChannelOptimizer {
/** Minimum supported channel width (MHz), inclusive. */
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 */
protected static final Map<Integer, List<Integer>> AVAILABLE_CHANNELS_WIDTH =
new HashMap<>();
@@ -138,7 +154,7 @@ public abstract class ChannelOptimizer {
// Remove model entries not in the given zone
this.model.latestWifiScans.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber));
this.model.latestStates.keySet()
this.model.latestState.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber));
this.model.latestDeviceStatusRadios.keySet()
.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber));
@@ -181,7 +197,7 @@ public abstract class ChannelOptimizer {
String vhtOper
) {
if (
UCentralUtils.AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G)
AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G)
.contains(channel)
) {
// 2.4G, it only supports 20 MHz
@@ -300,11 +316,7 @@ public abstract class ChannelOptimizer {
List<WifiScanEntry> scanRespsFiltered =
new ArrayList<WifiScanEntry>();
for (WifiScanEntry entry : scanResps) {
final String entryBand = UCentralUtils
.freqToBand(entry.frequency);
if (entryBand == null || !entryBand.equals(band)) {
continue;
}
if (UCentralUtils.isChannelInBand(entry.channel, band)) {
int channelWidth = getChannelWidthFromWiFiScan(
entry.channel,
entry.ht_oper,
@@ -324,6 +336,7 @@ public abstract class ChannelOptimizer {
scanRespsFiltered.add(newEntry);
}
}
}
if (scanRespsFiltered.size() == 0) {
// 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 serialNumber the device's serial number
* @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
* given band; returns a current channel of 0 if no channel in the given
* band is found.
@@ -357,8 +369,7 @@ public abstract class ChannelOptimizer {
protected static int[] getCurrentChannel(
String band,
String serialNumber,
State state,
Map<String, JsonObject> latestDeviceCapabilities
State state
) {
int currentChannel = 0;
int currentChannelWidth = MIN_CHANNEL_WIDTH;
@@ -368,28 +379,14 @@ public abstract class ChannelOptimizer {
radioIndex < state.radios.length;
radioIndex++
) {
State.Radio radio = state.radios[radioIndex];
// check if radio is in band of interest
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;
int tempChannel = state.radios[radioIndex].channel;
if (UCentralUtils.isChannelInBand(tempChannel, band)) {
currentChannel = tempChannel;
// treat as two separate 80MHz channel and only assign to one
// TODO: support 80p80 properly
Integer parsedChannelWidth = UCentralUtils
.parseChannelWidth(
radio.channel_width,
state.radios[radioIndex].channel_width,
true
);
if (parsedChannelWidth != null) {
@@ -399,10 +396,11 @@ public abstract class ChannelOptimizer {
logger.error(
"Invalid channel width {}",
radio.channel_width
state.radios[radioIndex].channel_width
);
continue;
}
}
return new int[] { currentChannel, currentChannelWidth };
}

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
* 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.HashMap;
@@ -17,10 +17,10 @@ import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralConstants;
import com.facebook.openwifi.cloudsdk.WifiScanEntry;
import com.facebook.openwifi.rrm.DeviceDataManager;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.ucentral.UCentralConstants;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
/**
* Unmanaged AP aware least used channel optimizer.

View File

@@ -6,7 +6,7 @@
* 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.Collections;
@@ -18,11 +18,12 @@ import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.models.ap.State;
import com.facebook.openwifi.rrm.DeviceConfig;
import com.facebook.openwifi.rrm.DeviceDataManager;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel;
import com.facebook.openwifi.rrm.modules.ModelerUtils;
import com.facebook.openwifirrm.DeviceConfig;
import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.modules.ModelerUtils;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifirrm.ucentral.models.State;
/**
* Location-based optimal TPC algorithm.
@@ -152,7 +153,6 @@ public class LocationBasedOptimalTPC extends TPC {
/**
* Calculate new tx powers for the given band.
*
* @param band band (e.g., "2G")
* @param channel 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
@@ -160,13 +160,13 @@ public class LocationBasedOptimalTPC extends TPC {
* this method with the new tx powers.
*/
private void buildTxPowerMapForChannel(
String band,
int channel,
List<String> serialNumbers,
Map<String, Map<String, Integer>> txPowerMap
) {
int numOfAPs = 0;
int boundary = 100;
String band = UCentralUtils.getBandFromChannel(channel);
Map<String, Integer> validAPs = new TreeMap<>();
List<Double> apLocX = 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)
// Update txPowerChoices, boundary, apLocX, apLocY for the optimization
for (String serialNumber : serialNumbers) {
List<State> states = model.latestStates.get(serialNumber);
State state = states.get(states.size() - 1);
State state = model.latestState.get(serialNumber);
// Ignore the device if its radio is not active
if (state.radios == null || state.radios.length == 0) {
@@ -283,27 +282,9 @@ public class LocationBasedOptimalTPC extends TPC {
@Override
public Map<String, Map<String, Integer>> computeTxPowerMap() {
Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>();
Map<String, Map<Integer, List<String>>> bandToChannelToAps =
getApsPerChannel();
for (
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
);
}
Map<Integer, List<String>> apsPerChannel = getApsPerChannel();
for (Map.Entry<Integer, List<String>> e : apsPerChannel.entrySet()) {
buildTxPowerMapForChannel(e.getKey(), e.getValue(), txPowerMap);
}
return txPowerMap;
}

View File

@@ -6,7 +6,7 @@
* 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.Collections;
@@ -20,13 +20,11 @@ import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralUtils;
import com.facebook.openwifi.cloudsdk.WifiScanEntry;
import com.facebook.openwifi.cloudsdk.models.ap.State;
import com.facebook.openwifi.rrm.DeviceDataManager;
import com.facebook.openwifi.rrm.modules.Modeler.DataModel;
import com.facebook.openwifi.rrm.modules.ModelerUtils;
import com.google.gson.JsonObject;
import com.facebook.openwifirrm.DeviceDataManager;
import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifirrm.ucentral.models.State;
/**
* Measurement-based AP-AP TPC algorithm.
@@ -156,9 +154,8 @@ public class MeasurementBasedApApTPC extends TPC {
*/
protected static Set<String> getManagedBSSIDs(DataModel model) {
Set<String> managedBSSIDs = new HashSet<>();
for (Map.Entry<String, List<State>> e : model.latestStates.entrySet()) {
List<State> states = e.getValue();
State state = states.get(states.size() - 1);
for (Map.Entry<String, State> e : model.latestState.entrySet()) {
State state = e.getValue();
if (state.interfaces == null) {
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
latestScan.stream()
.filter((entry) -> {
if (!managedBSSIDs.contains(entry.bssid)) {
return false;
}
String entryBand = UCentralUtils
.freqToBand(entry.frequency);
return entryBand != null && entryBand.equals(band);
})
.filter(entry -> (managedBSSIDs.contains(entry.bssid) && UCentralUtils.isChannelInBand(entry.channel, band)))
.forEach(
entry -> {
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 .
*
* @param band band (e.g., "2G")
* @param channel 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)
* and is updated by this method with the new tx powers.
*/
protected void buildTxPowerMapForChannel(
String band,
int channel,
List<String> serialNumbers,
Map<String, Map<String, Integer>> txPowerMap
) {
String band = UCentralUtils.getBandFromChannel(channel);
Set<String> managedBSSIDs = getManagedBSSIDs(model);
Map<String, List<Integer>> bssidToRssiValues =
buildRssiMap(managedBSSIDs, model.latestWifiScans, band);
logger.debug("Starting TPC for the {} band", band);
for (String serialNumber : serialNumbers) {
List<State> states = model.latestStates.get(serialNumber);
State state = states.get(states.size() - 1);
State state = model.latestState.get(serialNumber);
if (
state == null || state.radios == null ||
state.radios.length == 0
@@ -373,16 +361,9 @@ public class MeasurementBasedApApTPC extends TPC {
State.Radio radio = state.radios[idx];
// this specific SSID is not on the band of interest
JsonObject deviceCapability = model.latestDeviceCapabilities
.get(serialNumber);
if (deviceCapability == null) {
continue;
}
final String radioBand = ModelerUtils.getBand(
radio,
deviceCapability
);
if (radioBand == null || !radioBand.equals(band)) {
if (
!UCentralUtils.isChannelInBand(radio.channel, band)
) {
continue;
}
@@ -426,27 +407,9 @@ public class MeasurementBasedApApTPC extends TPC {
@Override
public Map<String, Map<String, Integer>> computeTxPowerMap() {
Map<String, Map<String, Integer>> txPowerMap = new TreeMap<>();
Map<String, Map<Integer, List<String>>> bandToChannelToAps =
getApsPerChannel();
for (
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
);
}
Map<Integer, List<String>> apsPerChannel = getApsPerChannel();
for (Map.Entry<Integer, List<String>> e : apsPerChannel.entrySet()) {
buildTxPowerMapForChannel(e.getKey(), e.getValue(), txPowerMap);
}
return txPowerMap;
}

View File

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

View File

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

View File

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

View File

@@ -6,14 +6,14 @@
* 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 com.facebook.openwifi.cloudsdk.ies.Country;
import com.facebook.openwifi.cloudsdk.ies.LocalPowerConstraint;
import com.facebook.openwifi.cloudsdk.ies.QbssLoad;
import com.facebook.openwifi.cloudsdk.ies.TxPwrInfo;
import com.facebook.openwifirrm.ucentral.informationelement.Country;
import com.facebook.openwifirrm.ucentral.informationelement.LocalPowerConstraint;
import com.facebook.openwifirrm.ucentral.informationelement.QbssLoad;
import com.facebook.openwifirrm.ucentral.informationelement.TxPwrInfo;
/** Wrapper class containing information elements */
public final class InformationElements {

View File

@@ -6,13 +6,13 @@
* 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 org.apache.kafka.common.errors.WakeupException;
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent;
import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
/**
* Kafka runner.

View File

@@ -6,7 +6,7 @@
* 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.Set;

View File

@@ -6,8 +6,9 @@
* 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.HashMap;
import java.util.List;
@@ -20,21 +21,24 @@ import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.models.gw.CommandInfo;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceCapabilities;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceConfigureRequest;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceListWithStatus;
import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus;
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent;
import com.facebook.openwifi.cloudsdk.models.gw.StatisticsRecords;
import com.facebook.openwifi.cloudsdk.models.gw.SystemInfoResults;
import com.facebook.openwifi.cloudsdk.models.gw.TokenValidationResult;
import com.facebook.openwifi.cloudsdk.models.gw.WifiScanRequest;
import com.facebook.openwifi.cloudsdk.models.prov.EntityList;
import com.facebook.openwifi.cloudsdk.models.prov.InventoryTagList;
import com.facebook.openwifi.cloudsdk.models.prov.RRMDetails;
import com.facebook.openwifi.cloudsdk.models.prov.SerialNumberList;
import com.facebook.openwifi.cloudsdk.models.prov.VenueList;
import com.facebook.openwifirrm.RRMConfig.UCentralConfig.UCentralSocketParams;
import com.facebook.openwifirrm.ucentral.gw.models.CommandInfo;
import com.facebook.openwifirrm.ucentral.gw.models.DeviceCapabilities;
import com.facebook.openwifirrm.ucentral.gw.models.DeviceConfigureRequest;
import com.facebook.openwifirrm.ucentral.gw.models.DeviceListWithStatus;
import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus;
import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
import com.facebook.openwifirrm.ucentral.gw.models.StatisticsRecords;
import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults;
import com.facebook.openwifirrm.ucentral.gw.models.TokenValidationResult;
import com.facebook.openwifirrm.ucentral.gw.models.WebTokenRefreshRequest;
import com.facebook.openwifirrm.ucentral.gw.models.WebTokenResult;
import com.facebook.openwifirrm.ucentral.gw.models.WifiScanRequest;
import com.facebook.openwifirrm.ucentral.prov.models.EntityList;
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.JsonSyntaxException;
@@ -126,14 +130,8 @@ public class UCentralClient {
/** uCentral password */
private final String password;
/** Connection timeout for all requests, in ms */
private final int connectTimeoutMs;
/** Socket timeout for all requests, in ms */
private final int socketTimeoutMs;
/** Socket timeout for wifi scan requests, in ms */
private final int wifiScanTimeoutMs;
/** Socket parameters */
private final UCentralSocketParams socketParams;
/** The learned service endpoints. */
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
* 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.
@@ -152,9 +161,7 @@ public class UCentralClient {
* (if needed)
* @param username uCentral username (for public endpoints only)
* @param password uCentral password (for public endpoints only)
* @param connectTimeoutMs connection timeout for all requests, in ms
* @param socketTimeoutMs socket timeout for all requests, in ms
* @param wifiScanTimeoutMs socket timeout for wifi scan requests, in ms
* @param socketParams Socket parameters
*/
public UCentralClient(
String rrmEndpoint,
@@ -162,17 +169,13 @@ public class UCentralClient {
String uCentralSecPublicEndpoint,
String username,
String password,
int connectTimeoutMs,
int socketTimeoutMs,
int wifiScanTimeoutMs
UCentralSocketParams socketParams
) {
this.rrmEndpoint = rrmEndpoint;
this.usePublicEndpoints = usePublicEndpoints;
this.username = username;
this.password = password;
this.connectTimeoutMs = connectTimeoutMs;
this.socketTimeoutMs = socketTimeoutMs;
this.wifiScanTimeoutMs = wifiScanTimeoutMs;
this.socketParams = socketParams;
if (usePublicEndpoints) {
setServicePublicEndpoint(OWSEC_SERVICE, uCentralSecPublicEndpoint);
@@ -195,7 +198,8 @@ public class UCentralClient {
Map<String, Object> body = new HashMap<>();
body.put("userId", username);
body.put("password", password);
HttpResponse<String> response = httpPost("oauth2", OWSEC_SERVICE, body);
HttpResponse<String> response =
httpPost("oauth2", OWSEC_SERVICE, body, null);
if (!response.isSuccess()) {
logger.error(
"Login failed: Response code {}, body: {}",
@@ -206,27 +210,146 @@ public class UCentralClient {
}
// Parse access token from response
JSONObject respBody;
WebTokenResult token;
try {
respBody = new JSONObject(response.getBody());
} catch (JSONException e) {
token = gson.fromJson(response.getBody(), WebTokenResult.class);
} catch (JsonSyntaxException e) {
logger.error("Login failed: Unexpected response", e);
logger.debug("Response body: {}", response.getBody());
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.debug("Response body: {}", respBody.toString());
logger.debug("Response body: {}", response.getBody());
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.debug("Access token: {}", accessToken);
logger.debug("Access token: {}", accessToken.access_token);
logger.debug("Refresh token: {}", accessToken.refresh_token);
// Load system endpoints
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. */
private boolean loadSystemEndpoints() {
// Make request
@@ -316,8 +439,8 @@ public class UCentralClient {
endpoint,
service,
parameters,
connectTimeoutMs,
socketTimeoutMs
socketParams.connectTimeoutMs,
socketParams.socketTimeoutMs
);
}
@@ -335,8 +458,12 @@ public class UCentralClient {
.connectTimeout(connectTimeoutMs)
.socketTimeout(socketTimeoutMs);
if (usePublicEndpoints) {
if (accessToken != null) {
req.header("Authorization", "Bearer " + accessToken);
if (!isAccessTokenExpired()) {
req.header(
"Authorization",
"Bearer " + accessToken.access_token
);
lastAccess = Instant.now().getEpochSecond();
}
} else {
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(
String endpoint,
String service,
Object body
Object body,
Map<String, Object> parameters
) {
return httpPost(
endpoint,
service,
body,
connectTimeoutMs,
socketTimeoutMs
parameters,
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(
String endpoint,
String service,
Object body,
Map<String, Object> parameters,
int connectTimeoutMs,
int socketTimeoutMs
) {
@@ -378,9 +508,16 @@ public class UCentralClient {
.header("accept", "application/json")
.connectTimeout(connectTimeoutMs)
.socketTimeout(socketTimeoutMs);
if (parameters != null && !parameters.isEmpty()) {
req.queryString(parameters);
}
if (usePublicEndpoints) {
if (accessToken != null) {
req.header("Authorization", "Bearer " + accessToken);
if (!isAccessTokenExpired()) {
req.header(
"Authorization",
"Bearer " + accessToken.access_token
);
lastAccess = Instant.now().getEpochSecond();
}
} else {
req
@@ -465,8 +602,9 @@ public class UCentralClient {
String.format("device/%s/wifiscan", serialNumber),
OWGW_SERVICE,
req,
connectTimeoutMs,
wifiScanTimeoutMs
null,
socketParams.connectTimeoutMs,
socketParams.wifiScanTimeoutMs
);
if (!response.isSuccess()) {
logger.error("Error: {}", response.getBody());
@@ -493,7 +631,8 @@ public class UCentralClient {
HttpResponse<String> response = httpPost(
String.format("device/%s/configure", serialNumber),
OWGW_SERVICE,
req
req,
null
);
if (!response.isSuccess()) {
logger.error("Error: {}", response.getBody());

View File

@@ -6,7 +6,7 @@
* 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.Collections;
@@ -22,7 +22,7 @@ public final class UCentralConstants {
public static final String BAND_2G = "2G";
/** String of the 5 GHz band */
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
.unmodifiableList(Arrays.asList(BAND_2G, BAND_5G));

View File

@@ -6,7 +6,7 @@
* 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.util.ArrayList;
@@ -29,8 +29,7 @@ import org.apache.kafka.common.serialization.StringDeserializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.UCentralClient;
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.JsonObject;

View File

@@ -6,7 +6,7 @@
* 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;
@@ -18,7 +18,7 @@ import org.apache.kafka.common.serialization.StringSerializer;
import org.slf4j.Logger;
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;
/**

View File

@@ -6,11 +6,11 @@
* 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.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -21,11 +21,14 @@ import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.facebook.openwifi.cloudsdk.ies.Country;
import com.facebook.openwifi.cloudsdk.ies.LocalPowerConstraint;
import com.facebook.openwifi.cloudsdk.ies.QbssLoad;
import com.facebook.openwifi.cloudsdk.ies.TxPwrInfo;
import com.facebook.openwifi.cloudsdk.models.ap.State;
import com.facebook.openwifirrm.RRMConfig;
import com.facebook.openwifirrm.Utils;
import com.facebook.openwifirrm.optimizers.channel.ChannelOptimizer;
import com.facebook.openwifirrm.ucentral.informationelement.Country;
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.JsonArray;
import com.google.gson.JsonElement;
@@ -44,47 +47,25 @@ public class UCentralUtils {
/** The Gson instance. */
private static final Gson gson = new Gson();
/** Map from band to ordered (increasing) list of available channels */
public static final Map<String, List<Integer>> AVAILABLE_CHANNELS_BAND =
Collections
.unmodifiableMap(buildBandToChannelsMap());
/** Map of band to the band-specific lowest available channel*/
public static final Map<String, Integer> LOWER_CHANNEL_LIMIT =
new HashMap<>();
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.
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.
*
@@ -168,7 +149,7 @@ public class UCentralUtils {
break;
}
} catch (Exception e) {
logger.error(String.format("Skipping invalid IE %s", ie), e);
logger.debug("Skipping invalid IE {}", ie);
continue;
}
}
@@ -418,6 +399,47 @@ public class UCentralUtils {
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.
*
@@ -426,17 +448,25 @@ public class UCentralUtils {
* @return true if the given channel is in the given band; false otherwise
*/
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) {
if (2412 <= freqMHz && freqMHz <= 2484) {
return "2G";
} else {
return "5G";
/**
* Given the channel, gets the band by checking lower bound and upper bound
* of each band
*
* @param channel channel number
* @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.

View File

@@ -6,11 +6,11 @@
* 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 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.

View File

@@ -6,7 +6,7 @@
* 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 boolean Read;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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 String serialNumber;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,6 +6,6 @@
* 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 }

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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 boolean enabled;

View File

@@ -6,7 +6,7 @@
* 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 String number;

View File

@@ -6,7 +6,7 @@
* 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 long created;

View File

@@ -6,7 +6,7 @@
* 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 static final String EVENT_JOIN = "join";

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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 UserInfo userInfo;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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 {
NO_CERTIFICATE, VALID_CERTIFICATE, MISMATCH_SERIAL, VERIFIED

View File

@@ -6,7 +6,7 @@
* 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 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.
*/
package com.facebook.openwifi.cloudsdk.models.gw;
package com.facebook.openwifirrm.ucentral.gw.models;
public class WebTokenResult {
public String access_token;
public String refresh_token;
public String token_type;
public long expires_in;
public int expires_in;
public int idle_timeout;
public String username;
public long created;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,13 +6,16 @@
* 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.Collections;
import java.util.List;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -22,6 +25,8 @@ import com.google.gson.JsonObject;
* javadocs is taken from the specification.
*/
public class Country {
private static final Logger logger = LoggerFactory.getLogger(Country.class);
/** Defined in 802.11 */
public static final int TYPE = 7;
@@ -120,9 +125,8 @@ public class Country {
JsonElement constraintsObject = contents.get("constraints");
if (constraintsObject != null) {
for (JsonElement jsonElement : constraintsObject.getAsJsonArray()) {
JsonObject innerElem = jsonElement.getAsJsonObject();
CountryInfo countryInfo =
CountryInfo.parse(innerElem.get("Country Info").getAsJsonObject());
CountryInfo.parse(jsonElement.getAsJsonObject());
constraints.add(countryInfo);
}
}

View File

@@ -6,7 +6,7 @@
* 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.Objects;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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;
@@ -25,7 +25,7 @@ public class TxPwrInfo {
public static final int TYPE = 195;
/** 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. */
public final Integer localMaxTxPwrConstraint40MHz;
/** Local maximum transmit power for 80 MHz. Optional field. */
@@ -36,9 +36,9 @@ public class TxPwrInfo {
/** Constructor */
public TxPwrInfo(
int localMaxTxPwrConstraint20MHz,
Integer localMaxTxPwrConstraint40MHz,
Integer localMaxTxPwrConstraint80MHz,
Integer localMaxTxPwrConstraint160MHz
int localMaxTxPwrConstraint40MHz,
int localMaxTxPwrConstraint80MHz,
int localMaxTxPwrConstraint160MHz
) {
this.localMaxTxPwrConstraint20MHz = localMaxTxPwrConstraint20MHz;
this.localMaxTxPwrConstraint40MHz = localMaxTxPwrConstraint40MHz;
@@ -48,17 +48,16 @@ public class TxPwrInfo {
/** Parse TxPwrInfo IE from appropriate Json object. */
public static TxPwrInfo parse(JsonObject contents) {
JsonObject innerObj = contents.get("Tx Pwr Info").getAsJsonObject();
// required field
int localMaxTxPwrConstraint20MHz =
innerObj.get("Local Max Tx Pwr Constraint 20MHz").getAsInt();
contents.get("Local Max Tx Pwr Constraint 20MHz").getAsInt();
// optional field
Integer localMaxTxPwrConstraint40MHz =
parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 40MHz");
parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
Integer localMaxTxPwrConstraint80MHz =
parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 80MHz");
parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
Integer localMaxTxPwrConstraint160MHz =
parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 160MHz");
parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
return new TxPwrInfo(
localMaxTxPwrConstraint20MHz,
localMaxTxPwrConstraint40MHz,

View File

@@ -6,7 +6,7 @@
* 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.Objects;

View File

@@ -6,7 +6,7 @@
* 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.annotations.SerializedName;

View File

@@ -6,7 +6,7 @@
* 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.annotations.SerializedName;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,11 +6,11 @@
* 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 com.facebook.openwifi.cloudsdk.models.gw.NoteInfo;
import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
public class DeviceConfiguration {
public static class DeviceConfigurationElement {

View File

@@ -6,7 +6,7 @@
* 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 String rcOnly;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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 String parent;

View File

@@ -6,11 +6,11 @@
* 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 com.facebook.openwifi.cloudsdk.models.gw.NoteInfo;
import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
public class Entity {
// from ObjectInfo

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,7 +6,7 @@
* 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;

View File

@@ -6,11 +6,11 @@
* 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 com.facebook.openwifi.cloudsdk.models.gw.NoteInfo;
import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
public class InventoryTag {
// from ObjectInfo

View File

@@ -6,7 +6,7 @@
* 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;

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