mirror of
https://github.com/Telecominfraproject/wlan-cloud-rrm.git
synced 2025-10-30 02:02:28 +00:00
Compare commits
6 Commits
scale-test
...
token_refr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fb30ce7e4 | ||
|
|
350a45b616 | ||
|
|
52dae760d8 | ||
|
|
343fc7b6ee | ||
|
|
2a952f56a9 | ||
|
|
52a2258c2d |
2
.github/workflows/deploy-gh-pages.yml
vendored
2
.github/workflows/deploy-gh-pages.yml
vendored
@@ -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
11
.gitignore
vendored
@@ -1,9 +1,12 @@
|
||||
/target/
|
||||
*/target/
|
||||
/target
|
||||
/*.log*
|
||||
/device_config.json
|
||||
/settings.json
|
||||
/topology.json
|
||||
|
||||
# Eclipse
|
||||
.settings/
|
||||
bin/
|
||||
/.settings/
|
||||
/bin/
|
||||
.metadata
|
||||
.classpath
|
||||
.project
|
||||
|
||||
@@ -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", \
|
||||
|
||||
43
README.md
43
README.md
@@ -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):
|
||||
|
||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@@ -29,8 +29,8 @@ services:
|
||||
targetPort: 16789
|
||||
protocol: TCP
|
||||
restapiinternal:
|
||||
servicePort: 16790
|
||||
targetPort: 16790
|
||||
servicePort: 17007
|
||||
targetPort: 17007
|
||||
protocol: TCP
|
||||
|
||||
checks:
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# OpenWiFi CloudSDK Java Library
|
||||
A Java library providing clients and models for the OpenWiFi uCentral-based
|
||||
CloudSDK.
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
4
owrrm/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
/*.log*
|
||||
/device_config.json
|
||||
/settings.json
|
||||
/topology.json
|
||||
@@ -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/
|
||||
143
owrrm/pom.xml
143
owrrm/pom.xml
@@ -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>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
412
pom.xml
412
pom.xml
@@ -2,220 +2,230 @@
|
||||
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>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<configuration>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>**/*.md</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>log4j:log4j</artifact>
|
||||
<excludes>
|
||||
<!-- CVE-2021-4104 -->
|
||||
<exclude>org/apache/log4j/net/JMSAppender.class</exclude>
|
||||
<!-- CVE-2019-17571 -->
|
||||
<exclude>org/apache/log4j/net/SocketServer.class</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<configuration>
|
||||
<doclint>all,-missing</doclint>
|
||||
<notimestamp>true</notimestamp>
|
||||
<bottom>Copyright © Meta Platforms, Inc. and affiliates.</bottom>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!-- Generate "openapi.yaml" in project root, NOT packaged -->
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-maven-plugin</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
<configuration>
|
||||
<outputFileName>openapi</outputFileName>
|
||||
<outputPath>${project.basedir}</outputPath>
|
||||
<outputFormat>YAML</outputFormat>
|
||||
<prettyPrint>TRUE</prettyPrint>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>resolve</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.diffplug.spotless</groupId>
|
||||
<artifactId>spotless-maven-plugin</artifactId>
|
||||
<version>2.25.0</version>
|
||||
<configuration>
|
||||
<java>
|
||||
<eclipse>
|
||||
<file>${myproject.root}/spotless/eclipse-java-formatter.xml</file>
|
||||
<version>4.12.0</version>
|
||||
</eclipse>
|
||||
<trimTrailingWhitespace />
|
||||
<removeUnusedImports/>
|
||||
<licenseHeader>
|
||||
<file>${myproject.root}/spotless/license-header.txt</file>
|
||||
</licenseHeader>
|
||||
</java>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>info.picocli</groupId>
|
||||
<artifactId>picocli</artifactId>
|
||||
<version>4.6.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20210307</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.konghq</groupId>
|
||||
<artifactId>unirest-java</artifactId>
|
||||
<version>3.11.09</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.kafka</groupId>
|
||||
<artifactId>kafka-clients</artifactId>
|
||||
<version>2.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.26</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>4.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sparkjava</groupId>
|
||||
<artifactId>spark-core</artifactId>
|
||||
<version>2.9.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<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>
|
||||
<version>3.8.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<configuration>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>**/*.md</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>log4j:log4j</artifact>
|
||||
<excludes>
|
||||
<!-- CVE-2021-4104 -->
|
||||
<exclude>org/apache/log4j/net/JMSAppender.class</exclude>
|
||||
<!-- CVE-2019-17571 -->
|
||||
<exclude>org/apache/log4j/net/SocketServer.class</exclude>
|
||||
</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>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<configuration>
|
||||
<doclint>all,-missing</doclint>
|
||||
<notimestamp>true</notimestamp>
|
||||
<bottom>Copyright © Meta Platforms, Inc. and affiliates.</bottom>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!-- Generate "openapi.yaml" in project root, NOT packaged -->
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-jaxrs2</artifactId>
|
||||
<artifactId>swagger-maven-plugin</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>javax.ws.rs-api</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.10.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<configuration>
|
||||
<outputFileName>openapi</outputFileName>
|
||||
<outputPath>${project.basedir}</outputPath>
|
||||
<outputFormat>YAML</outputFormat>
|
||||
<prettyPrint>TRUE</prettyPrint>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>resolve</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.diffplug.spotless</groupId>
|
||||
<artifactId>spotless-maven-plugin</artifactId>
|
||||
<version>2.25.0</version>
|
||||
<configuration>
|
||||
<java>
|
||||
<eclipse>
|
||||
<file>${project.basedir}/spotless/eclipse-java-formatter.xml</file>
|
||||
<version>4.12.0</version>
|
||||
</eclipse>
|
||||
<trimTrailingWhitespace />
|
||||
<removeUnusedImports/>
|
||||
<licenseHeader>
|
||||
<file>${project.basedir}/spotless/license-header.txt</file>
|
||||
</licenseHeader>
|
||||
</java>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<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>
|
||||
<artifactId>picocli</artifactId>
|
||||
<version>4.6.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20210307</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.konghq</groupId>
|
||||
<artifactId>unirest-java</artifactId>
|
||||
<version>3.11.09</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.kafka</groupId>
|
||||
<artifactId>kafka-clients</artifactId>
|
||||
<version>2.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.26</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>4.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sparkjava</groupId>
|
||||
<artifactId>spark-core</artifactId>
|
||||
<version>2.9.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-jaxrs2</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjdk.jol</groupId>
|
||||
<artifactId>jol-core</artifactId>
|
||||
<version>0.16</version>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>javax.ws.rs-api</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependency>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.10.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.quartz-scheduler</groupId>
|
||||
<artifactId>quartz</artifactId>
|
||||
<version>2.3.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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). */
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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 "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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,17 +269,15 @@ 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) {
|
||||
String internalName = request.headers("X-INTERNAL-NAME");
|
||||
if (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;
|
||||
}
|
||||
// TODO check if request came from internal endpoint
|
||||
boolean internal = true;
|
||||
String internalName = request.headers("X-INTERNAL-NAME");
|
||||
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:
|
||||
@@ -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
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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");
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
@@ -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,28 +316,25 @@ 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;
|
||||
}
|
||||
int channelWidth = getChannelWidthFromWiFiScan(
|
||||
entry.channel,
|
||||
entry.ht_oper,
|
||||
entry.vht_oper
|
||||
);
|
||||
int primaryChannel =
|
||||
getPrimaryChannel(entry.channel, channelWidth);
|
||||
List<Integer> coveredChannels =
|
||||
getCoveredChannels(
|
||||
if (UCentralUtils.isChannelInBand(entry.channel, band)) {
|
||||
int channelWidth = getChannelWidthFromWiFiScan(
|
||||
entry.channel,
|
||||
primaryChannel,
|
||||
channelWidth
|
||||
entry.ht_oper,
|
||||
entry.vht_oper
|
||||
);
|
||||
for (Integer newChannel : coveredChannels) {
|
||||
WifiScanEntry newEntry = new WifiScanEntry(entry);
|
||||
newEntry.channel = newChannel;
|
||||
scanRespsFiltered.add(newEntry);
|
||||
int primaryChannel =
|
||||
getPrimaryChannel(entry.channel, channelWidth);
|
||||
List<Integer> coveredChannels =
|
||||
getCoveredChannels(
|
||||
entry.channel,
|
||||
primaryChannel,
|
||||
channelWidth
|
||||
);
|
||||
for (Integer newChannel : coveredChannels) {
|
||||
WifiScanEntry newEntry = new WifiScanEntry(entry);
|
||||
newEntry.channel = newChannel;
|
||||
scanRespsFiltered.add(newEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,40 +379,27 @@ 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 = 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(
|
||||
state.radios[radioIndex].channel_width,
|
||||
true
|
||||
);
|
||||
if (parsedChannelWidth != null) {
|
||||
currentChannelWidth = parsedChannelWidth;
|
||||
break;
|
||||
}
|
||||
|
||||
int tempChannel = radio.channel;
|
||||
currentChannel = tempChannel;
|
||||
// treat as two separate 80MHz channel and only assign to one
|
||||
// TODO: support 80p80 properly
|
||||
Integer parsedChannelWidth = UCentralUtils
|
||||
.parseChannelWidth(
|
||||
radio.channel_width,
|
||||
true
|
||||
logger.error(
|
||||
"Invalid channel width {}",
|
||||
state.radios[radioIndex].channel_width
|
||||
);
|
||||
if (parsedChannelWidth != null) {
|
||||
currentChannelWidth = parsedChannelWidth;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.error(
|
||||
"Invalid channel width {}",
|
||||
radio.channel_width
|
||||
);
|
||||
continue;
|
||||
}
|
||||
return new int[] { currentChannel, currentChannelWidth };
|
||||
}
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,44 +137,36 @@ 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 (String serialNumber : serialNumbers) {
|
||||
int txPower = defaultTxPower;
|
||||
if (setDifferentTxPowerPerAp) {
|
||||
List<Integer> curTxPowerChoices = updateTxPowerChoices(
|
||||
band,
|
||||
serialNumber,
|
||||
DEFAULT_TX_POWER_CHOICES
|
||||
);
|
||||
txPower = curTxPowerChoices
|
||||
.get(rng.nextInt(curTxPowerChoices.size()));
|
||||
}
|
||||
txPowerMap
|
||||
.computeIfAbsent(serialNumber, k -> new TreeMap<>())
|
||||
.put(band, txPower);
|
||||
logger.info(
|
||||
"Device {} band {}: Assigning tx power = {}",
|
||||
serialNumber,
|
||||
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) {
|
||||
List<Integer> curTxPowerChoices = updateTxPowerChoices(
|
||||
band,
|
||||
txPower
|
||||
serialNumber,
|
||||
DEFAULT_TX_POWER_CHOICES
|
||||
);
|
||||
txPower = curTxPowerChoices
|
||||
.get(rng.nextInt(curTxPowerChoices.size()));
|
||||
}
|
||||
txPowerMap.computeIfAbsent(serialNumber, k -> new TreeMap<>())
|
||||
.put(band, txPower);
|
||||
logger.info(
|
||||
"Device {} band {}: Assigning tx power = {}",
|
||||
serialNumber,
|
||||
band,
|
||||
txPower
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return txPowerMap;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
@@ -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());
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -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,16 +448,24 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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 }
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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";
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user