mirror of
				https://github.com/Telecominfraproject/wlan-cloud-rrm.git
				synced 2025-10-31 18:47:55 +00:00 
			
		
		
		
	Compare commits
	
		
			14 Commits
		
	
	
		
			token_refr
			...
			scale-test
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 0279437f5c | ||
|   | c635af6c1d | ||
|   | 37a904087d | ||
|   | 6df81b7fef | ||
|   | 8171cc74ae | ||
|   | 215d73fee5 | ||
|   | ecbf8fa644 | ||
|   | 19928e0286 | ||
|   | da978611d0 | ||
|   | e42eaa747b | ||
|   | 264f114be2 | ||
|   | dd2b485b00 | ||
|   | 033d93beff | ||
|   | e5d5f7d5c0 | 
							
								
								
									
										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:javadoc | ||||
|         run: mvn javadoc:aggregate | ||||
|       - name: Deploy to GitHub Pages | ||||
|         uses: peaceiris/actions-gh-pages@v3 | ||||
|         with: | ||||
|   | ||||
							
								
								
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,12 +1,9 @@ | ||||
| /target | ||||
| /*.log* | ||||
| /device_config.json | ||||
| /settings.json | ||||
| /topology.json | ||||
| /target/ | ||||
| */target/ | ||||
|  | ||||
| # Eclipse | ||||
| /.settings/ | ||||
| /bin/ | ||||
| .settings/ | ||||
| bin/ | ||||
| .metadata | ||||
| .classpath | ||||
| .project | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| FROM maven:3-jdk-11 as build | ||||
| FROM maven:3-eclipse-temurin-11 as build | ||||
| WORKDIR /usr/src/java | ||||
| COPY . . | ||||
| RUN mvn clean package -DappendVersionString="$(./scripts/get_build_version.sh)" | ||||
| RUN mvn clean package -pl owrrm -am -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/target/openwifi-rrm.jar /usr/local/bin/ | ||||
| EXPOSE 16789 | ||||
| COPY --from=build /usr/src/java/owrrm/target/openwifi-rrm.jar /usr/local/bin/ | ||||
| EXPOSE 16789 16790 | ||||
| ENTRYPOINT ["/docker-entrypoint.sh"] | ||||
| CMD ["java", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", \ | ||||
|      "-jar", "/usr/local/bin/openwifi-rrm.jar", \ | ||||
|   | ||||
							
								
								
									
										43
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,12 +1,10 @@ | ||||
| # 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. | ||||
| [See here](owrrm/README.md) for details. | ||||
|  | ||||
| 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). | ||||
| ## Project Structure | ||||
| This is an [Apache Maven] project with the following modules: | ||||
| * `lib-cloudsdk` - OpenWiFi CloudSDK Java Library | ||||
| * `owrrm` - OpenWiFi RRM Service | ||||
|  | ||||
| ## Requirements | ||||
| * **Running:** JRE 11. | ||||
| @@ -16,7 +14,7 @@ algorithms are run (manually or periodically). | ||||
| ``` | ||||
| $ mvn package [-DskipTests] | ||||
| ``` | ||||
| This will build a runnable JAR located at `target/openwifi-rrm.jar`. | ||||
| This will build a runnable JAR located at `owrrm/target/openwifi-rrm.jar`. | ||||
|  | ||||
| Alternatively, Docker builds can be launched using the provided | ||||
| [Dockerfile](Dockerfile). | ||||
| @@ -27,34 +25,7 @@ $ mvn test | ||||
| ``` | ||||
| Unit tests are written using [JUnit 5]. | ||||
|  | ||||
| ## 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 Style | ||||
| 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): | ||||
|   | ||||
| @@ -29,8 +29,8 @@ services: | ||||
|         targetPort: 16789 | ||||
|         protocol: TCP | ||||
|       restapiinternal: | ||||
|         servicePort: 17007 | ||||
|         targetPort: 17007 | ||||
|         servicePort: 16790 | ||||
|         targetPort: 16790 | ||||
|         protocol: TCP | ||||
|  | ||||
| checks: | ||||
|   | ||||
							
								
								
									
										3
									
								
								lib-cloudsdk/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								lib-cloudsdk/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| # OpenWiFi CloudSDK Java Library | ||||
| A Java library providing clients and models for the OpenWiFi uCentral-based | ||||
| CloudSDK. | ||||
							
								
								
									
										80
									
								
								lib-cloudsdk/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								lib-cloudsdk/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| <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> | ||||
| @@ -0,0 +1,201 @@ | ||||
| /* | ||||
|  * 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; | ||||
| 	} | ||||
| } | ||||
| @@ -6,14 +6,14 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral; | ||||
| package com.facebook.openwifi.cloudsdk; | ||||
| 
 | ||||
| import java.util.Objects; | ||||
| 
 | ||||
| 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.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; | ||||
| 
 | ||||
| /** Wrapper class containing information elements */ | ||||
| public final class InformationElements { | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral; | ||||
| package com.facebook.openwifi.cloudsdk; | ||||
| 
 | ||||
| import java.util.HashSet; | ||||
| import java.util.Set; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral; | ||||
| package com.facebook.openwifi.cloudsdk; | ||||
| 
 | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| @@ -20,22 +20,21 @@ import org.json.JSONObject; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| 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.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.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.google.gson.Gson; | ||||
| import com.google.gson.JsonSyntaxException; | ||||
| 
 | ||||
| @@ -127,8 +126,14 @@ public class UCentralClient { | ||||
| 	/** uCentral password */ | ||||
| 	private final String password; | ||||
| 
 | ||||
| 	/** Socket parameters */ | ||||
| 	private final UCentralSocketParams socketParams; | ||||
| 	/** 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; | ||||
| 
 | ||||
| 	/** The learned service endpoints. */ | ||||
| 	private final Map<String, ServiceEvent> serviceEndpoints = new HashMap<>(); | ||||
| @@ -147,7 +152,9 @@ public class UCentralClient { | ||||
| 	 *        (if needed) | ||||
| 	 * @param username uCentral username (for public endpoints only) | ||||
| 	 * @param password uCentral password (for public endpoints only) | ||||
| 	 * @param socketParams Socket parameters | ||||
| 	 * @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 | ||||
| 	 */ | ||||
| 	public UCentralClient( | ||||
| 		String rrmEndpoint, | ||||
| @@ -155,13 +162,17 @@ public class UCentralClient { | ||||
| 		String uCentralSecPublicEndpoint, | ||||
| 		String username, | ||||
| 		String password, | ||||
| 		UCentralSocketParams socketParams | ||||
| 		int connectTimeoutMs, | ||||
| 		int socketTimeoutMs, | ||||
| 		int wifiScanTimeoutMs | ||||
| 	) { | ||||
| 		this.rrmEndpoint = rrmEndpoint; | ||||
| 		this.usePublicEndpoints = usePublicEndpoints; | ||||
| 		this.username = username; | ||||
| 		this.password = password; | ||||
| 		this.socketParams = socketParams; | ||||
| 		this.connectTimeoutMs = connectTimeoutMs; | ||||
| 		this.socketTimeoutMs = socketTimeoutMs; | ||||
| 		this.wifiScanTimeoutMs = wifiScanTimeoutMs; | ||||
| 
 | ||||
| 		if (usePublicEndpoints) { | ||||
| 			setServicePublicEndpoint(OWSEC_SERVICE, uCentralSecPublicEndpoint); | ||||
| @@ -305,8 +316,8 @@ public class UCentralClient { | ||||
| 			endpoint, | ||||
| 			service, | ||||
| 			parameters, | ||||
| 			socketParams.connectTimeoutMs, | ||||
| 			socketParams.socketTimeoutMs | ||||
| 			connectTimeoutMs, | ||||
| 			socketTimeoutMs | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| @@ -349,8 +360,8 @@ public class UCentralClient { | ||||
| 			endpoint, | ||||
| 			service, | ||||
| 			body, | ||||
| 			socketParams.connectTimeoutMs, | ||||
| 			socketParams.socketTimeoutMs | ||||
| 			connectTimeoutMs, | ||||
| 			socketTimeoutMs | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| @@ -454,8 +465,8 @@ public class UCentralClient { | ||||
| 			String.format("device/%s/wifiscan", serialNumber), | ||||
| 			OWGW_SERVICE, | ||||
| 			req, | ||||
| 			socketParams.connectTimeoutMs, | ||||
| 			socketParams.wifiScanTimeoutMs | ||||
| 			connectTimeoutMs, | ||||
| 			wifiScanTimeoutMs | ||||
| 		); | ||||
| 		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.openwifirrm.ucentral; | ||||
| package com.facebook.openwifi.cloudsdk; | ||||
| 
 | ||||
| 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 */ | ||||
| 	/** List of all bands ordered from lowest to highest */ | ||||
| 	public static final List<String> BANDS = Collections | ||||
| 		.unmodifiableList(Arrays.asList(BAND_2G, BAND_5G)); | ||||
| 
 | ||||
| @@ -6,11 +6,11 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral; | ||||
| package com.facebook.openwifi.cloudsdk; | ||||
| 
 | ||||
| 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,14 +21,11 @@ import java.util.Set; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| 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.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.google.gson.Gson; | ||||
| import com.google.gson.JsonArray; | ||||
| import com.google.gson.JsonElement; | ||||
| @@ -47,25 +44,47 @@ public class UCentralUtils { | ||||
| 	/** The Gson instance. */ | ||||
| 	private static final Gson gson = new Gson(); | ||||
| 
 | ||||
| 	/** 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); | ||||
| 	} | ||||
| 	/** Map from band to ordered (increasing) list of available channels */ | ||||
| 	public static final Map<String, List<Integer>> AVAILABLE_CHANNELS_BAND = | ||||
| 		Collections | ||||
| 			.unmodifiableMap(buildBandToChannelsMap()); | ||||
| 
 | ||||
| 	// 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. | ||||
| 	 * | ||||
| @@ -149,7 +168,7 @@ public class UCentralUtils { | ||||
| 					break; | ||||
| 				} | ||||
| 			} catch (Exception e) { | ||||
| 				logger.debug("Skipping invalid IE {}", ie); | ||||
| 				logger.error(String.format("Skipping invalid IE %s", ie), e); | ||||
| 				continue; | ||||
| 			} | ||||
| 		} | ||||
| @@ -399,47 +418,6 @@ 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. | ||||
| 	 * | ||||
| @@ -448,25 +426,17 @@ 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 LOWER_CHANNEL_LIMIT.get(band) <= channel && | ||||
| 			channel <= UPPER_CHANNEL_LIMIT.get(band); | ||||
| 		return AVAILABLE_CHANNELS_BAND.get(band).contains(channel); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 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 which band contains the given frequency (MHz). */ | ||||
| 	public static String freqToBand(int freqMHz) { | ||||
| 		if (2412 <= freqMHz && freqMHz <= 2484) { | ||||
| 			return "2G"; | ||||
| 		} else { | ||||
| 			return "5G"; | ||||
| 		} | ||||
| 	} | ||||
| 		return null; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Tries to parse channel width, if it encounters an error it will return null. | ||||
| @@ -6,11 +6,11 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral; | ||||
| package com.facebook.openwifi.cloudsdk; | ||||
| 
 | ||||
| import java.util.Objects; | ||||
| 
 | ||||
| import com.facebook.openwifirrm.ucentral.models.WifiScanEntryResult; | ||||
| import com.facebook.openwifi.cloudsdk.models.ap.WifiScanEntryResult; | ||||
| 
 | ||||
| /** | ||||
|  * Extends {@link WifiScanEntryResult} to track the response time of the entry. | ||||
| @@ -6,16 +6,13 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.informationelement; | ||||
| package com.facebook.openwifi.cloudsdk.ies; | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| @@ -25,8 +22,6 @@ 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; | ||||
| 
 | ||||
| @@ -125,8 +120,9 @@ public class Country { | ||||
| 		JsonElement constraintsObject = contents.get("constraints"); | ||||
| 		if (constraintsObject != null) { | ||||
| 			for (JsonElement jsonElement : constraintsObject.getAsJsonArray()) { | ||||
| 				JsonObject innerElem = jsonElement.getAsJsonObject(); | ||||
| 				CountryInfo countryInfo = | ||||
| 					CountryInfo.parse(jsonElement.getAsJsonObject()); | ||||
| 					CountryInfo.parse(innerElem.get("Country Info").getAsJsonObject()); | ||||
| 				constraints.add(countryInfo); | ||||
| 			} | ||||
| 		} | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.informationelement; | ||||
| package com.facebook.openwifi.cloudsdk.ies; | ||||
| 
 | ||||
| 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.openwifirrm.ucentral.informationelement; | ||||
| package com.facebook.openwifi.cloudsdk.ies; | ||||
| 
 | ||||
| import java.util.Objects; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.informationelement; | ||||
| package com.facebook.openwifi.cloudsdk.ies; | ||||
| 
 | ||||
| import java.util.Objects; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.informationelement; | ||||
| package com.facebook.openwifi.cloudsdk.ies; | ||||
| 
 | ||||
| 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 Integer localMaxTxPwrConstraint20MHz; | ||||
| 	public final int 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, | ||||
| 		int localMaxTxPwrConstraint40MHz, | ||||
| 		int localMaxTxPwrConstraint80MHz, | ||||
| 		int localMaxTxPwrConstraint160MHz | ||||
| 		Integer localMaxTxPwrConstraint40MHz, | ||||
| 		Integer localMaxTxPwrConstraint80MHz, | ||||
| 		Integer localMaxTxPwrConstraint160MHz | ||||
| 	) { | ||||
| 		this.localMaxTxPwrConstraint20MHz = localMaxTxPwrConstraint20MHz; | ||||
| 		this.localMaxTxPwrConstraint40MHz = localMaxTxPwrConstraint40MHz; | ||||
| @@ -48,16 +48,17 @@ 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 = | ||||
| 			contents.get("Local Max Tx Pwr Constraint 20MHz").getAsInt(); | ||||
| 			innerObj.get("Local Max Tx Pwr Constraint 20MHz").getAsInt(); | ||||
| 		// optional field | ||||
| 		Integer localMaxTxPwrConstraint40MHz = | ||||
| 			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz"); | ||||
| 			parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 40MHz"); | ||||
| 		Integer localMaxTxPwrConstraint80MHz = | ||||
| 			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz"); | ||||
| 			parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 80MHz"); | ||||
| 		Integer localMaxTxPwrConstraint160MHz = | ||||
| 			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz"); | ||||
| 			parseOptionalField(innerObj, "Local Max Tx Pwr Constraint 160MHz"); | ||||
| 		return new TxPwrInfo( | ||||
| 			localMaxTxPwrConstraint20MHz, | ||||
| 			localMaxTxPwrConstraint40MHz, | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.informationelement; | ||||
| package com.facebook.openwifi.cloudsdk.ies; | ||||
| 
 | ||||
| import java.util.Arrays; | ||||
| import java.util.Objects; | ||||
| @@ -6,13 +6,13 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral; | ||||
| package com.facebook.openwifi.cloudsdk.kafka; | ||||
| 
 | ||||
| import java.util.concurrent.atomic.AtomicBoolean; | ||||
| 
 | ||||
| import org.apache.kafka.common.errors.WakeupException; | ||||
| 
 | ||||
| import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent; | ||||
| import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; | ||||
| 
 | ||||
| /** | ||||
|  * Kafka runner. | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral; | ||||
| package com.facebook.openwifi.cloudsdk.kafka; | ||||
| 
 | ||||
| import java.time.Duration; | ||||
| import java.util.ArrayList; | ||||
| @@ -29,7 +29,8 @@ import org.apache.kafka.common.serialization.StringDeserializer; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent; | ||||
| import com.facebook.openwifi.cloudsdk.UCentralClient; | ||||
| import com.facebook.openwifi.cloudsdk.models.gw.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.openwifirrm.ucentral; | ||||
| package com.facebook.openwifi.cloudsdk.kafka; | ||||
| 
 | ||||
| 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.openwifirrm.ucentral.gw.models.ServiceEvent; | ||||
| import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent; | ||||
| import com.google.gson.Gson; | ||||
| 
 | ||||
| /** | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.ap; | ||||
| 
 | ||||
| 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.openwifirrm.ucentral.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.ap; | ||||
| 
 | ||||
| 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.openwifirrm.ucentral.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.ap; | ||||
| 
 | ||||
| import java.util.Objects; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| public class AclTemplate { | ||||
| 	public boolean Read; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| import com.google.gson.JsonObject; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| import com.google.gson.JsonObject; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| public class DeviceConfigureRequest { | ||||
| 	public String serialNumber; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,6 +6,6 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| public enum DeviceType { AP, SWITCH, IOT, MESH } | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| public class MfaAuthInfo { | ||||
| 	public boolean enabled; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| public class MobilePhoneNumber { | ||||
| 	public String number; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| public class NoteInfo { | ||||
| 	public long created; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| 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.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| import com.google.gson.JsonObject; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| public class TokenValidationResult { | ||||
| 	public UserInfo userInfo; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| 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.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| public class WebTokenAclTemplate { | ||||
| 	public AclTemplate aclTemplate; | ||||
| @@ -6,13 +6,13 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| public class WebTokenResult { | ||||
| 	public String access_token; | ||||
| 	public String refresh_token; | ||||
| 	public String token_type; | ||||
| 	public int expires_in; | ||||
| 	public long 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.openwifirrm.ucentral.gw.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.gw; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,11 +6,11 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo; | ||||
| import com.facebook.openwifi.cloudsdk.models.gw.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.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| public class DeviceRules { | ||||
| 	public String rcOnly; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| public class DiGraphEntry { | ||||
| 	public String parent; | ||||
| @@ -6,11 +6,11 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo; | ||||
| import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo; | ||||
| 
 | ||||
| public class Entity { | ||||
| 	// from ObjectInfo | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,11 +6,11 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo; | ||||
| import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo; | ||||
| 
 | ||||
| public class InventoryTag { | ||||
| 	// from ObjectInfo | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| public class RRMAlgorithmDetails { | ||||
| 	public String name; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,11 +6,11 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo; | ||||
| import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo; | ||||
| 
 | ||||
| public class Venue { | ||||
| 	// from ObjectInfo | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.rrm.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov.rrm; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.prov.rrm.models; | ||||
| package com.facebook.openwifi.cloudsdk.models.prov.rrm; | ||||
| 
 | ||||
| public class Provider { | ||||
| 	public String vendor; | ||||
							
								
								
									
										6
									
								
								lib-cloudsdk/src/main/resources/log4j.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								lib-cloudsdk/src/main/resources/log4j.properties
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| 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 | ||||
| @@ -6,15 +6,15 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral; | ||||
| package com.facebook.openwifi.cloudsdk; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertFalse; | ||||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||||
| 
 | ||||
| import java.util.Collections; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||||
| import static org.junit.jupiter.api.Assertions.assertFalse; | ||||
| 
 | ||||
| import org.junit.jupiter.api.Test; | ||||
| 
 | ||||
| public class UCentralUtilsTest { | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.informationelement; | ||||
| package com.facebook.openwifi.cloudsdk.ies; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.ucentral.informationelement; | ||||
| package com.facebook.openwifi.cloudsdk.ies; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||||
| 
 | ||||
							
								
								
									
										4
									
								
								owrrm/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								owrrm/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| /*.log* | ||||
| /device_config.json | ||||
| /settings.json | ||||
| /topology.json | ||||
							
								
								
									
										39
									
								
								owrrm/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								owrrm/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| # 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/ | ||||
| Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB | 
							
								
								
									
										143
									
								
								owrrm/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								owrrm/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| <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> | ||||
| @@ -0,0 +1,160 @@ | ||||
| /* | ||||
|  * 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(); | ||||
| 	} | ||||
| } | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm; | ||||
| package com.facebook.openwifi.rrm; | ||||
| 
 | ||||
| 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.openwifirrm; | ||||
| package com.facebook.openwifi.rrm; | ||||
| 
 | ||||
| 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}. */ | ||||
| 	private final ReadWriteLock deviceLayeredConfigLock = | ||||
| 	public final ReadWriteLock deviceLayeredConfigLock = | ||||
| 		new ReentrantReadWriteLock(); | ||||
| 
 | ||||
| 	/** The current device topology. */ | ||||
| 	private DeviceTopology topology; | ||||
| 	public DeviceTopology topology; | ||||
| 
 | ||||
| 	/** The current layered device config. */ | ||||
| 	private DeviceLayeredConfig deviceLayeredConfig; | ||||
| 	public DeviceLayeredConfig deviceLayeredConfig; | ||||
| 
 | ||||
| 	/** The cached device configs (map of serial number to computed config). */ | ||||
| 	private Map<String, DeviceConfig> cachedDeviceConfigs = | ||||
| 	public 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.openwifirrm; | ||||
| package com.facebook.openwifi.rrm; | ||||
| 
 | ||||
| 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.openwifirrm; | ||||
| package com.facebook.openwifi.rrm; | ||||
| 
 | ||||
| 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.openwifirrm; | ||||
| package com.facebook.openwifi.rrm; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.FileWriter; | ||||
| @@ -18,11 +18,10 @@ import org.json.JSONObject; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| 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.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.google.gson.Gson; | ||||
| import com.google.gson.GsonBuilder; | ||||
| 
 | ||||
| @@ -155,8 +154,7 @@ public class Launcher implements Callable<Integer> { | ||||
| 				: DEFAULT_DEVICE_LAYERED_CONFIG_FILE | ||||
| 		); | ||||
| 
 | ||||
| 		String serviceKey = | ||||
| 			UCentralUtils.generateServiceKey(config.serviceConfig); | ||||
| 		String serviceKey = Utils.generateServiceKey(config.serviceConfig); | ||||
| 
 | ||||
| 		// Instantiate clients | ||||
| 		UCentralClient.verifySsl(config.uCentralConfig.verifySsl); | ||||
| @@ -166,7 +164,9 @@ public class Launcher implements Callable<Integer> { | ||||
| 			config.uCentralConfig.uCentralSecPublicEndpoint, | ||||
| 			config.uCentralConfig.username, | ||||
| 			config.uCentralConfig.password, | ||||
| 			config.uCentralConfig.uCentralSocketParams | ||||
| 			config.uCentralConfig.uCentralSocketParams.connectTimeoutMs, | ||||
| 			config.uCentralConfig.uCentralSocketParams.socketTimeoutMs, | ||||
| 			config.uCentralConfig.uCentralSocketParams.wifiScanTimeoutMs | ||||
| 		); | ||||
| 		UCentralKafkaConsumer consumer; | ||||
| 		UCentralKafkaProducer producer; | ||||
| @@ -265,7 +265,7 @@ public class Launcher implements Callable<Integer> { | ||||
| 			.setPrettyPrinting() | ||||
| 			.serializeNulls() // for here only!! | ||||
| 			.create(); | ||||
| 		logger.info(gson.toJson(DeviceConfig.createDefault())); | ||||
| 		System.out.println(gson.toJson(DeviceConfig.createDefault())); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm; | ||||
| package com.facebook.openwifi.rrm; | ||||
| 
 | ||||
| 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.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; | ||||
| 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; | ||||
| 
 | ||||
| /** | ||||
|  * RRM service runner. | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm; | ||||
| package com.facebook.openwifi.rrm; | ||||
| 
 | ||||
| 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.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; | ||||
| 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; | ||||
| 
 | ||||
| /** | ||||
|  * RRM algorithm model and utility methods. | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm; | ||||
| package com.facebook.openwifi.rrm; | ||||
| 
 | ||||
| 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:16789"; // see ApiServerParams.httpPort | ||||
| 		public String privateEndpoint = "http://owrrm.wlan.local:16790"; // see ApiServerParams.internalHttpPort | ||||
| 
 | ||||
| 		/** | ||||
| 		 * 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/ALGORITHMS.md"; | ||||
| 			"https://github.com/Telecominfraproject/wlan-cloud-rrm/blob/main/owrrm/ALGORITHMS.md"; | ||||
| 	} | ||||
| 
 | ||||
| 	/** Service configuration. */ | ||||
| @@ -309,6 +309,12 @@ 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. */ | ||||
| @@ -319,10 +325,16 @@ public class RRMConfig { | ||||
| 		 */ | ||||
| 		public class ApiServerParams { | ||||
| 			/** | ||||
| 			 * The HTTP port to listen on, or -1 to disable | ||||
| 			 * ({@code APISERVERPARAMS_HTTPPORT}) | ||||
| 			 * The HTTP port to listen on for internal traffic, or -1 to disable | ||||
| 			 * ({@code APISERVERPARAMS_INTERNALHTTPPORT}) | ||||
| 			 */ | ||||
| 			public int httpPort = 16789; | ||||
| 			public int internalHttpPort = 16790; | ||||
| 
 | ||||
| 			/** | ||||
| 			 * The HTTP port to listen on for external traffic, or -1 to disable | ||||
| 			 * ({@code APISERVERPARAMS_EXTERNALHTTPPORT}) | ||||
| 			 */ | ||||
| 			public int externalHttpPort = 16789; | ||||
| 
 | ||||
| 			/** | ||||
| 			 * Comma-separated list of all allowed CORS domains (exact match | ||||
| @@ -532,10 +544,16 @@ 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_HTTPPORT")) != null) { | ||||
| 			apiServerParams.httpPort = Integer.parseInt(v); | ||||
| 		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_CORSDOMAINLIST")) != null) { | ||||
| 			apiServerParams.corsDomainList = v; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm; | ||||
| package com.facebook.openwifi.rrm; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm; | ||||
| package com.facebook.openwifi.rrm; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.io.FileNotFoundException; | ||||
| @@ -16,6 +16,8 @@ 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; | ||||
| @@ -23,6 +25,8 @@ 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; | ||||
| @@ -31,6 +35,8 @@ 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(); | ||||
| 
 | ||||
| @@ -193,4 +199,19 @@ 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.openwifirrm; | ||||
| package com.facebook.openwifi.rrm; | ||||
| 
 | ||||
| import picocli.CommandLine.IVersionProvider; | ||||
| 
 | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.aggregators; | ||||
| package com.facebook.openwifi.rrm.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.openwifirrm.aggregators; | ||||
| package com.facebook.openwifi.rrm.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.openwifirrm.modules; | ||||
| package com.facebook.openwifi.rrm.modules; | ||||
| 
 | ||||
| import java.net.InetAddress; | ||||
| import java.net.URI; | ||||
| @@ -35,31 +35,33 @@ import org.reflections.util.ConfigurationBuilder; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| 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.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.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; | ||||
| @@ -81,7 +83,9 @@ import io.swagger.v3.oas.models.OpenAPI; | ||||
| import spark.Request; | ||||
| import spark.Response; | ||||
| import spark.Route; | ||||
| import spark.Spark; | ||||
| import spark.Service; | ||||
| import spark.embeddedserver.EmbeddedServers; | ||||
| import spark.embeddedserver.jetty.EmbeddedJettyFactory; | ||||
| 
 | ||||
| /** | ||||
|  * HTTP API server. | ||||
| @@ -110,6 +114,27 @@ 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; | ||||
| 
 | ||||
| @@ -164,9 +189,10 @@ public class ApiServer implements Runnable { | ||||
| 		UCentralClient client, | ||||
| 		RRMScheduler scheduler | ||||
| 	) { | ||||
| 		this.service = Service.ignite(); | ||||
| 		this.params = params; | ||||
| 		this.serviceConfig = serviceConfig; | ||||
| 		this.serviceKey = UCentralUtils.generateServiceKey(serviceConfig); | ||||
| 		this.serviceKey = Utils.generateServiceKey(serviceConfig); | ||||
| 		this.deviceDataManager = deviceDataManager; | ||||
| 		this.configManager = configManager; | ||||
| 		this.modeler = modeler; | ||||
| @@ -194,64 +220,117 @@ 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.httpPort == -1) { | ||||
| 		if (params.internalHttpPort == -1 && params.externalHttpPort == -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; | ||||
| 		} | ||||
| 
 | ||||
| 		Spark.port(params.httpPort); | ||||
| 		EmbeddedServers.add( | ||||
| 			SPARK_EMBEDDED_SERVER_IDENTIFIER, | ||||
| 			new EmbeddedJettyFactory( | ||||
| 				new CustomJettyServerFactory( | ||||
| 					params.internalHttpPort, | ||||
| 					params.externalHttpPort | ||||
| 				) | ||||
| 			) | ||||
| 		); | ||||
| 
 | ||||
| 		// use the embedded server factory added above, this is required so that we | ||||
| 		// don't mess up the default factory which can and will be used for | ||||
| 		// additional Spark services in testing | ||||
| 		service.embeddedServerIdentifier(SPARK_EMBEDDED_SERVER_IDENTIFIER); | ||||
| 
 | ||||
| 		// Usually you would call this with an actual port and Spark would spin up a | ||||
| 		// port on it. However, since we're putting our own connectors in so that we | ||||
| 		// can use two ports and Spark has logic to use connectors that already exist | ||||
| 		// so it doesn't matter what port we pass in here as long as it's not one of | ||||
| 		// the actual ports we're using (Spark has some weird logic where it still | ||||
| 		// tries to bind to the port). | ||||
| 		// @see EmbeddedJettyServer | ||||
| 		service.port(0); | ||||
| 
 | ||||
| 		// Configure API docs hosting | ||||
| 		Spark.staticFiles.location("/public"); | ||||
| 		Spark.get("/openapi.yaml", this::getOpenApiYaml); | ||||
| 		Spark.get("/openapi.json", this::getOpenApiJson); | ||||
| 		service.staticFiles.location("/public"); | ||||
| 		service.get("/openapi.yaml", this::getOpenApiYaml); | ||||
| 		service.get("/openapi.json", this::getOpenApiJson); | ||||
| 
 | ||||
| 		// Install routes | ||||
| 		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( | ||||
| 		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( | ||||
| 			"/api/v1/getDeviceLayeredConfig", | ||||
| 			new GetDeviceLayeredConfigEndpoint() | ||||
| 		); | ||||
| 		Spark.get("/api/v1/getDeviceConfig", new GetDeviceConfigEndpoint()); | ||||
| 		Spark.post( | ||||
| 		service.get("/api/v1/getDeviceConfig", new GetDeviceConfigEndpoint()); | ||||
| 		service.post( | ||||
| 			"/api/v1/setDeviceNetworkConfig", | ||||
| 			new SetDeviceNetworkConfigEndpoint() | ||||
| 		); | ||||
| 		Spark.post( | ||||
| 		service.post( | ||||
| 			"/api/v1/setDeviceZoneConfig", | ||||
| 			new SetDeviceZoneConfigEndpoint() | ||||
| 		); | ||||
| 		Spark.post( | ||||
| 		service.post( | ||||
| 			"/api/v1/setDeviceApConfig", | ||||
| 			new SetDeviceApConfigEndpoint() | ||||
| 		); | ||||
| 		Spark.post( | ||||
| 		service.post( | ||||
| 			"/api/v1/modifyDeviceApConfig", | ||||
| 			new ModifyDeviceApConfigEndpoint() | ||||
| 		); | ||||
| 		Spark.get("/api/v1/currentModel", new GetCurrentModelEndpoint()); | ||||
| 		Spark.get("/api/v1/optimizeChannel", new OptimizeChannelEndpoint()); | ||||
| 		Spark.get("/api/v1/optimizeTxPower", new OptimizeTxPowerEndpoint()); | ||||
| 		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)); | ||||
| 
 | ||||
| 		logger.info("API server listening on HTTP port {}", params.httpPort); | ||||
| 		logger.info( | ||||
| 			"API server listening for HTTP internal on port {} and external on port {}", | ||||
| 			params.internalHttpPort, | ||||
| 			params.externalHttpPort | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	/** Stop the server. */ | ||||
| 	public void shutdown() { | ||||
| 		Spark.stop(); | ||||
| 		service.stop(); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Block until stop finishes. Just calls the method on the underlying service. | ||||
| 	 */ | ||||
| 	public void awaitStop() { | ||||
| 		service.awaitStop(); | ||||
| 	} | ||||
| 
 | ||||
| 	/** Reconstructs a URL. */ | ||||
| @@ -269,16 +348,18 @@ public class ApiServer implements Runnable { | ||||
| 	 * HTTP 403 response and return false. | ||||
| 	 */ | ||||
| 	private boolean performOpenWifiAuth(Request request, Response response) { | ||||
| 		// TODO check if request came from internal endpoint | ||||
| 		boolean internal = true; | ||||
| 		int port = request.port(); | ||||
| 		boolean internal = port > 0 && port == params.internalHttpPort; | ||||
| 		if (internal) { | ||||
| 			String internalName = request.headers("X-INTERNAL-NAME"); | ||||
| 		if (internal && internalName != null) { | ||||
| 			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; | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			// External request, validate token: | ||||
| 			//   Authorization: Bearer <token> | ||||
| @@ -297,7 +378,7 @@ public class ApiServer implements Runnable { | ||||
| 		} | ||||
| 
 | ||||
| 		// auth failure | ||||
| 		Spark.halt(403, "Forbidden"); | ||||
| 		service.halt(403, "Forbidden"); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| @@ -325,10 +406,11 @@ 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()) | ||||
| 			getFullUrl(request.pathInfo(), request.queryString()), | ||||
| 			request.port() | ||||
| 		); | ||||
| 
 | ||||
| 		// Remove "Server: Jetty" header | ||||
| @@ -1280,6 +1362,91 @@ 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.openwifirrm.modules; | ||||
| package com.facebook.openwifi.rrm.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.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; | ||||
| 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; | ||||
| 
 | ||||
| /** | ||||
|  * Device configuration manager module. | ||||
| @@ -46,7 +46,7 @@ public class ConfigManager implements Runnable { | ||||
| 	private final UCentralClient client; | ||||
| 
 | ||||
| 	/** Runtime per-device data. */ | ||||
| 	private class DeviceData { | ||||
| 	public 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. */ | ||||
| 	private Map<String, DeviceData> deviceDataMap = new TreeMap<>(); | ||||
| 	public Map<String, DeviceData> deviceDataMap = new TreeMap<>(); | ||||
| 
 | ||||
| 	/** The main thread reference (i.e. where {@link #run()} is invoked). */ | ||||
| 	private Thread mainThread; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.modules; | ||||
| package com.facebook.openwifi.rrm.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.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.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.google.gson.Gson; | ||||
| import com.google.gson.JsonArray; | ||||
| import com.google.gson.JsonElement; | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.modules; | ||||
| package com.facebook.openwifi.rrm.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.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.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.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. */ | ||||
| 	private final DeviceDataManager deviceDataManager; | ||||
| 	public final DeviceDataManager deviceDataManager; | ||||
| 
 | ||||
| 	/** The uCentral client instance. */ | ||||
| 	private final UCentralClient client; | ||||
| @@ -74,7 +74,7 @@ public class Modeler implements Runnable { | ||||
| 	} | ||||
| 
 | ||||
| 	/** The blocking data queue. */ | ||||
| 	private final BlockingQueue<InputData> dataQueue = | ||||
| 	public final BlockingQueue<InputData> dataQueue = | ||||
| 		new LinkedBlockingQueue<>(); | ||||
| 
 | ||||
| 	/** Data model representation. */ | ||||
| @@ -93,8 +93,9 @@ public class Modeler implements Runnable { | ||||
| 		public Map<String, List<List<WifiScanEntry>>> latestWifiScans = | ||||
| 			new ConcurrentHashMap<>(); | ||||
| 
 | ||||
| 		/** List of latest state per device. */ | ||||
| 		public Map<String, State> latestState = new ConcurrentHashMap<>(); | ||||
| 		/** List of latest states per device. */ | ||||
| 		public Map<String, List<State>> latestStates = | ||||
| 			new ConcurrentHashMap<>(); | ||||
| 
 | ||||
| 		/** List of radio info per device. */ | ||||
| 		public Map<String, JsonArray> latestDeviceStatusRadios = | ||||
| @@ -267,7 +268,10 @@ public class Modeler implements Runnable { | ||||
| 			if (state != null) { | ||||
| 				try { | ||||
| 					State stateModel = gson.fromJson(state, State.class); | ||||
| 					dataModel.latestState.put(device.serialNumber, stateModel); | ||||
| 					dataModel.latestStates.computeIfAbsent( | ||||
| 						device.serialNumber, | ||||
| 						k -> new LinkedList<>() | ||||
| 					).add(stateModel); | ||||
| 					logger.debug( | ||||
| 						"Device {}: added initial state from uCentralGw", | ||||
| 						device.serialNumber | ||||
| @@ -299,8 +303,17 @@ public class Modeler implements Runnable { | ||||
| 				if (state != null) { | ||||
| 					try { | ||||
| 						State stateModel = gson.fromJson(state, State.class); | ||||
| 						dataModel.latestState | ||||
| 							.put(record.serialNumber, stateModel); | ||||
| 						List<State> latestStatesList = dataModel.latestStates | ||||
| 							.computeIfAbsent( | ||||
| 								record.serialNumber, | ||||
| 								k -> new LinkedList<>() | ||||
| 							); | ||||
| 						while ( | ||||
| 							latestStatesList.size() >= params.stateBufferSize | ||||
| 						) { | ||||
| 							latestStatesList.remove(0); | ||||
| 						} | ||||
| 						latestStatesList.add(stateModel); | ||||
| 						stateUpdates.add(record.serialNumber); | ||||
| 					} catch (JsonSyntaxException e) { | ||||
| 						logger.error( | ||||
| @@ -423,7 +436,7 @@ public class Modeler implements Runnable { | ||||
| 			logger.debug("Removed some wifi scan entries from data model"); | ||||
| 		} | ||||
| 		if ( | ||||
| 			dataModel.latestState.entrySet() | ||||
| 			dataModel.latestStates.entrySet() | ||||
| 				.removeIf(e -> !isRRMEnabled(e.getKey())) | ||||
| 		) { | ||||
| 			logger.debug("Removed some state entries from data model"); | ||||
| @@ -6,24 +6,33 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.modules; | ||||
| package com.facebook.openwifi.rrm.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.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; | ||||
| 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; | ||||
| 
 | ||||
| /** | ||||
|  * Modeler utilities. | ||||
| @@ -291,7 +300,7 @@ public class ModelerUtils { | ||||
| 	/** | ||||
| 	 * Compute aggregated wifiscans using a given reference time. | ||||
| 	 * | ||||
| 	 * @see #getAggregatedWifiScans(com.facebook.openwifirrm.modules.Modeler.DataModel, | ||||
| 	 * @see #getAggregatedWifiScans(com.facebook.openwifi.rrm.modules.Modeler.DataModel, | ||||
| 	 *      long, Aggregator) | ||||
| 	 */ | ||||
| 	public static Map<String, Map<String, WifiScanEntry>> getAggregatedWifiScans( | ||||
| @@ -381,4 +390,196 @@ 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.openwifirrm.modules; | ||||
| package com.facebook.openwifi.rrm.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.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; | ||||
| 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; | ||||
| 
 | ||||
| /** | ||||
|  * owprov monitor module. | ||||
| @@ -6,7 +6,7 @@ | ||||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| package com.facebook.openwifirrm.modules; | ||||
| package com.facebook.openwifi.rrm.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.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.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.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.openwifirrm.mysql; | ||||
| package com.facebook.openwifi.rrm.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.openwifirrm.Utils; | ||||
| import com.facebook.openwifirrm.ucentral.WifiScanEntry; | ||||
| import com.facebook.openwifirrm.ucentral.models.State; | ||||
| import com.facebook.openwifi.cloudsdk.WifiScanEntry; | ||||
| import com.facebook.openwifi.cloudsdk.models.ap.State; | ||||
| import com.facebook.openwifi.rrm.Utils; | ||||
| 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.openwifirrm.mysql; | ||||
| package com.facebook.openwifi.rrm.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.openwifirrm.optimizers.channel; | ||||
| package com.facebook.openwifi.rrm.optimizers.channel; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| @@ -18,16 +18,18 @@ import java.util.Map; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| 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; | ||||
| 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; | ||||
| 
 | ||||
| /** | ||||
|  * Channel optimizer base class. | ||||
| @@ -39,24 +41,6 @@ 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<>(); | ||||
| @@ -154,7 +138,7 @@ public abstract class ChannelOptimizer { | ||||
| 		// Remove model entries not in the given zone | ||||
| 		this.model.latestWifiScans.keySet() | ||||
| 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); | ||||
| 		this.model.latestState.keySet() | ||||
| 		this.model.latestStates.keySet() | ||||
| 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); | ||||
| 		this.model.latestDeviceStatusRadios.keySet() | ||||
| 			.removeIf(serialNumber -> !deviceConfigs.containsKey(serialNumber)); | ||||
| @@ -197,7 +181,7 @@ public abstract class ChannelOptimizer { | ||||
| 		String vhtOper | ||||
| 	) { | ||||
| 		if ( | ||||
| 			AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G) | ||||
| 			UCentralUtils.AVAILABLE_CHANNELS_BAND.get(UCentralConstants.BAND_2G) | ||||
| 				.contains(channel) | ||||
| 		) { | ||||
| 			// 2.4G, it only supports 20 MHz | ||||
| @@ -316,7 +300,11 @@ public abstract class ChannelOptimizer { | ||||
| 			List<WifiScanEntry> scanRespsFiltered = | ||||
| 				new ArrayList<WifiScanEntry>(); | ||||
| 			for (WifiScanEntry entry : scanResps) { | ||||
| 				if (UCentralUtils.isChannelInBand(entry.channel, band)) { | ||||
| 				final String entryBand = UCentralUtils | ||||
| 					.freqToBand(entry.frequency); | ||||
| 				if (entryBand == null || !entryBand.equals(band)) { | ||||
| 					continue; | ||||
| 				} | ||||
| 				int channelWidth = getChannelWidthFromWiFiScan( | ||||
| 					entry.channel, | ||||
| 					entry.ht_oper, | ||||
| @@ -336,7 +324,6 @@ public abstract class ChannelOptimizer { | ||||
| 					scanRespsFiltered.add(newEntry); | ||||
| 				} | ||||
| 			} | ||||
| 			} | ||||
| 
 | ||||
| 			if (scanRespsFiltered.size() == 0) { | ||||
| 				// 3. Filter out APs with empty scan results (on a particular band) | ||||
| @@ -362,6 +349,7 @@ 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. | ||||
| @@ -369,7 +357,8 @@ public abstract class ChannelOptimizer { | ||||
| 	protected static int[] getCurrentChannel( | ||||
| 		String band, | ||||
| 		String serialNumber, | ||||
| 		State state | ||||
| 		State state, | ||||
| 		Map<String, JsonObject> latestDeviceCapabilities | ||||
| 	) { | ||||
| 		int currentChannel = 0; | ||||
| 		int currentChannelWidth = MIN_CHANNEL_WIDTH; | ||||
| @@ -379,14 +368,28 @@ public abstract class ChannelOptimizer { | ||||
| 			radioIndex < state.radios.length; | ||||
| 			radioIndex++ | ||||
| 		) { | ||||
| 			int tempChannel = state.radios[radioIndex].channel; | ||||
| 			if (UCentralUtils.isChannelInBand(tempChannel, band)) { | ||||
| 			State.Radio radio = state.radios[radioIndex]; | ||||
| 			// check if radio is in band of interest | ||||
| 			JsonObject deviceCapability = | ||||
| 				latestDeviceCapabilities.get(serialNumber); | ||||
| 			if (deviceCapability == null) { | ||||
| 				continue; | ||||
| 			} | ||||
| 			final String radioBand = ModelerUtils.getBand( | ||||
| 				radio, | ||||
| 				deviceCapability | ||||
| 			); | ||||
| 			if (radioBand == null || !radioBand.equals(band)) { | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			int tempChannel = radio.channel; | ||||
| 			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, | ||||
| 					radio.channel_width, | ||||
| 					true | ||||
| 				); | ||||
| 			if (parsedChannelWidth != null) { | ||||
| @@ -396,11 +399,10 @@ public abstract class ChannelOptimizer { | ||||
| 
 | ||||
| 			logger.error( | ||||
| 				"Invalid channel width {}", | ||||
| 					state.radios[radioIndex].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.openwifirrm.optimizers.channel; | ||||
| package com.facebook.openwifi.rrm.optimizers.channel; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashSet; | ||||
| @@ -20,12 +20,13 @@ import java.util.stream.Collectors; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| 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; | ||||
| 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; | ||||
| 
 | ||||
| /** | ||||
|  * Least used channel optimizer. | ||||
| @@ -89,13 +90,13 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer { | ||||
| 	protected static Map<Integer, Integer> getOccupiedOverlapChannels( | ||||
| 		Map<Integer, Integer> occupiedChannels | ||||
| 	) { | ||||
| 		int maxChannel = | ||||
| 			UCentralUtils.UPPER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G); | ||||
| 		int minChannel = | ||||
| 			UCentralUtils.LOWER_CHANNEL_LIMIT.get(UCentralConstants.BAND_2G); | ||||
| 		final int maxChannel = | ||||
| 			UCentralUtils.getUpperChannelLimit(UCentralConstants.BAND_2G); | ||||
| 		final int minChannel = | ||||
| 			UCentralUtils.getLowerChannelLimit(UCentralConstants.BAND_2G); | ||||
| 		Map<Integer, Integer> occupiedOverlapChannels = new TreeMap<>(); | ||||
| 		for ( | ||||
| 			int overlapChannel : AVAILABLE_CHANNELS_BAND | ||||
| 			int overlapChannel : UCentralUtils.AVAILABLE_CHANNELS_BAND | ||||
| 				.get(UCentralConstants.BAND_2G) | ||||
| 		) { | ||||
| 			int occupancy = 0; | ||||
| @@ -337,11 +338,13 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer { | ||||
| 			UCentralUtils.getDeviceAvailableChannels( | ||||
| 				model.latestDeviceStatusRadios, | ||||
| 				model.latestDeviceCapabilities, | ||||
| 				AVAILABLE_CHANNELS_BAND | ||||
| 				UCentralUtils.AVAILABLE_CHANNELS_BAND | ||||
| 			); | ||||
| 
 | ||||
| 		Map<String, State> latestState = | ||||
| 			ModelerUtils.getLatestState(model.latestStates); | ||||
| 		Map<String, String> bssidsMap = | ||||
| 			UCentralUtils.getBssidsMap(model.latestState); | ||||
| 			UCentralUtils.getBssidsMap(latestState); | ||||
| 
 | ||||
| 		for (String band : bandsMap.keySet()) { | ||||
| 			// Performance metrics | ||||
| @@ -369,11 +372,12 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer { | ||||
| 					availableChannelsList == null || | ||||
| 						availableChannelsList.isEmpty() | ||||
| 				) { | ||||
| 					availableChannelsList = AVAILABLE_CHANNELS_BAND.get(band); | ||||
| 					availableChannelsList = | ||||
| 						UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band); | ||||
| 				} | ||||
| 
 | ||||
| 				// Get current channel of the device | ||||
| 				State state = model.latestState.get(serialNumber); | ||||
| 				State state = latestState.get(serialNumber); | ||||
| 				if (state == null) { | ||||
| 					logger.debug( | ||||
| 						"Device {}: No state found, skipping...", | ||||
| @@ -389,7 +393,12 @@ public class LeastUsedChannelOptimizer extends ChannelOptimizer { | ||||
| 					continue; | ||||
| 				} | ||||
| 				int[] currentChannelInfo = | ||||
| 					getCurrentChannel(band, serialNumber, state); | ||||
| 					getCurrentChannel( | ||||
| 						band, | ||||
| 						serialNumber, | ||||
| 						state, | ||||
| 						model.latestDeviceCapabilities | ||||
| 					); | ||||
| 				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.openwifirrm.optimizers.channel; | ||||
| package com.facebook.openwifi.rrm.optimizers.channel; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| @@ -19,11 +19,12 @@ import java.util.TreeSet; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| 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; | ||||
| 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; | ||||
| 
 | ||||
| /** | ||||
|  * Random channel initializer. | ||||
| @@ -125,11 +126,13 @@ public class RandomChannelInitializer extends ChannelOptimizer { | ||||
| 			UCentralUtils.getDeviceAvailableChannels( | ||||
| 				model.latestDeviceStatusRadios, | ||||
| 				model.latestDeviceCapabilities, | ||||
| 				AVAILABLE_CHANNELS_BAND | ||||
| 				UCentralUtils.AVAILABLE_CHANNELS_BAND | ||||
| 			); | ||||
| 
 | ||||
| 		Map<String, State> latestState = | ||||
| 			ModelerUtils.getLatestState(model.latestStates); | ||||
| 		Map<String, String> bssidsMap = | ||||
| 			UCentralUtils.getBssidsMap(model.latestState); | ||||
| 			UCentralUtils.getBssidsMap(latestState); | ||||
| 
 | ||||
| 		for (Map.Entry<String, List<String>> entry : bandsMap.entrySet()) { | ||||
| 			// Performance metrics | ||||
| @@ -149,7 +152,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<>( | ||||
| 				AVAILABLE_CHANNELS_BAND.get(band) | ||||
| 				UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band) | ||||
| 			); | ||||
| 			for (String serialNumber : entry.getValue()) { | ||||
| 				List<Integer> deviceChannelsList = deviceAvailableChannels | ||||
| @@ -158,14 +161,16 @@ public class RandomChannelInitializer extends ChannelOptimizer { | ||||
| 				if ( | ||||
| 					deviceChannelsList == null || deviceChannelsList.isEmpty() | ||||
| 				) { | ||||
| 					deviceChannelsList = AVAILABLE_CHANNELS_BAND.get(band); | ||||
| 					deviceChannelsList = | ||||
| 						UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band); | ||||
| 				} | ||||
| 				availableChannelsList.retainAll(deviceChannelsList); | ||||
| 			} | ||||
| 			if ( | ||||
| 				availableChannelsList == null || availableChannelsList.isEmpty() | ||||
| 			) { | ||||
| 				availableChannelsList = AVAILABLE_CHANNELS_BAND.get(band); | ||||
| 				availableChannelsList = | ||||
| 					UCentralUtils.AVAILABLE_CHANNELS_BAND.get(band); | ||||
| 				logger.debug( | ||||
| 					"The intersection of the device channels lists is empty!!! " + | ||||
| 						"Fall back to the default channels list" | ||||
| @@ -183,7 +188,7 @@ public class RandomChannelInitializer extends ChannelOptimizer { | ||||
| 						? rng.nextInt(availableChannelsList.size()) : defaultChannelIndex | ||||
| 				); | ||||
| 
 | ||||
| 				State state = model.latestState.get(serialNumber); | ||||
| 				State state = latestState.get(serialNumber); | ||||
| 				if (state == null) { | ||||
| 					logger.debug( | ||||
| 						"Device {}: No state found, skipping...", | ||||
| @@ -199,7 +204,12 @@ public class RandomChannelInitializer extends ChannelOptimizer { | ||||
| 					continue; | ||||
| 				} | ||||
| 				int[] currentChannelInfo = | ||||
| 					getCurrentChannel(band, serialNumber, state); | ||||
| 					getCurrentChannel( | ||||
| 						band, | ||||
| 						serialNumber, | ||||
| 						state, | ||||
| 						model.latestDeviceCapabilities | ||||
| 					); | ||||
| 				int currentChannel = currentChannelInfo[0]; | ||||
| 				int currentChannelWidth = currentChannelInfo[1]; | ||||
| 				if (currentChannel == 0) { | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user