mirror of
				https://github.com/Telecominfraproject/wlan-cloud-owprov.git
				synced 2025-10-31 02:27:52 +00:00 
			
		
		
		
	Compare commits
	
		
			42 Commits
		
	
	
		
			v2.6.0-RC4
			...
			v2.7.0-RC1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 3a33815096 | ||
|   | f515bb8e30 | ||
|   | 02fd6d726a | ||
|   | 27ffb31a7c | ||
|   | 5fd9831d6b | ||
|   | fed085cc4a | ||
|   | 121fee841e | ||
|   | 4c6f03ba14 | ||
|   | 0fb9478675 | ||
|   | 97c2af83fd | ||
|   | 599ba0793c | ||
|   | 6df780dba3 | ||
|   | a00287ae85 | ||
|   | c7a300b81e | ||
|   | 5dc507a82e | ||
|   | 8b3e1326b7 | ||
|   | 667f8bc4bd | ||
|   | 0168301d6b | ||
|   | 1a0b00e989 | ||
|   | 7c2229f3d6 | ||
|   | 541df429ec | ||
|   | 6f9fe6cd5d | ||
|   | 2594a2c5f2 | ||
|   | 2e02d96523 | ||
|   | a1375f9468 | ||
|   | 777bc0f2aa | ||
|   | bd0e99309c | ||
|   | 6cb6a60142 | ||
|   | fc7947394d | ||
|   | 1341e15874 | ||
|   | f065815df3 | ||
|   | 03ba51e869 | ||
|   | efd7ef2a9b | ||
|   | 4b90a6e893 | ||
|   | f5cb4a5a87 | ||
|   | e3e4aac202 | ||
|   | 5945d02b3d | ||
|   | 0ac192cdc0 | ||
|   | 1b5eb87eef | ||
|   | 46db18d7cd | ||
|   | 30b8665d7d | ||
|   | c8b3a3b060 | 
| @@ -1,5 +1,5 @@ | |||||||
| cmake_minimum_required(VERSION 3.13) | cmake_minimum_required(VERSION 3.13) | ||||||
| project(owprov VERSION 2.6.0) | project(owprov VERSION 2.7.0) | ||||||
|  |  | ||||||
| set(CMAKE_CXX_STANDARD 17) | set(CMAKE_CXX_STANDARD 17) | ||||||
|  |  | ||||||
| @@ -134,7 +134,14 @@ add_executable(owprov | |||||||
|         src/storage/storage_variables.cpp src/storage/storage_variables.h |         src/storage/storage_variables.cpp src/storage/storage_variables.h | ||||||
|         src/RESTAPI/RESTAPI_variables_handler.cpp src/RESTAPI/RESTAPI_variables_handler.h |         src/RESTAPI/RESTAPI_variables_handler.cpp src/RESTAPI/RESTAPI_variables_handler.h | ||||||
|         src/RESTAPI/RESTAPI_variables_list_handler.cpp src/RESTAPI/RESTAPI_variables_list_handler.h |         src/RESTAPI/RESTAPI_variables_list_handler.cpp src/RESTAPI/RESTAPI_variables_list_handler.h | ||||||
|         src/FileDownloader.cpp src/FileDownloader.h src/Tasks/VenueConfigUpdater.h src/Kafka_ProvUpdater.cpp src/Kafka_ProvUpdater.h src/storage/storage_operataor.cpp src/storage/storage_operataor.h src/storage/storage_sub_devices.cpp src/storage/storage_sub_devices.h src/storage/storage_service_class.cpp src/storage/storage_service_class.h src/RESTAPI/RESTAPI_sub_devices_list_handler.cpp src/RESTAPI/RESTAPI_sub_devices_list_handler.h src/RESTAPI/RESTAPI_sub_devices_handler.cpp src/RESTAPI/RESTAPI_sub_devices_handler.h src/RESTAPI/RESTAPI_service_class_list_handler.cpp src/RESTAPI/RESTAPI_service_class_list_handler.h src/RESTAPI/RESTAPI_service_class_handler.cpp src/RESTAPI/RESTAPI_service_class_handler.h src/RESTAPI/RESTAPI_operators_list_handler.cpp src/RESTAPI/RESTAPI_operators_list_handler.h src/RESTAPI/RESTAPI_operators_handler.cpp src/RESTAPI/RESTAPI_operators_handler.h src/storage/storage_op_contacts.cpp src/storage/storage_op_contacts.h src/storage/storage_op_locations.cpp src/storage/storage_op_locations.h src/RESTAPI/RESTAPI_op_contact_list_handler.cpp src/RESTAPI/RESTAPI_op_contact_list_handler.h src/RESTAPI/RESTAPI_op_contact_handler.cpp src/RESTAPI/RESTAPI_op_contact_handler.h src/RESTAPI/RESTAPI_op_location_list_handler.cpp src/RESTAPI/RESTAPI_op_location_list_handler.h src/RESTAPI/RESTAPI_op_location_handler.cpp src/RESTAPI/RESTAPI_op_location_handler.h src/ProvWebSocketClient.cpp src/ProvWebSocketClient.h src/Tasks/VenueRebooter.h src/Tasks/VenueUpgrade.h src/sdks/SDK_fms.cpp src/sdks/SDK_fms.h) |         src/FileDownloader.cpp src/FileDownloader.h | ||||||
|  |         src/Tasks/VenueConfigUpdater.h | ||||||
|  |         src/libs/croncpp.h | ||||||
|  |         src/Kafka_ProvUpdater.cpp src/Kafka_ProvUpdater.h | ||||||
|  |         src/storage/storage_operataor.cpp src/storage/storage_operataor.h | ||||||
|  |         src/storage/storage_sub_devices.cpp src/storage/storage_sub_devices.h | ||||||
|  |         src/storage/storage_service_class.cpp src/storage/storage_service_class.h | ||||||
|  |         src/RESTAPI/RESTAPI_sub_devices_list_handler.cpp src/RESTAPI/RESTAPI_sub_devices_list_handler.h src/RESTAPI/RESTAPI_sub_devices_handler.cpp src/RESTAPI/RESTAPI_sub_devices_handler.h src/RESTAPI/RESTAPI_service_class_list_handler.cpp src/RESTAPI/RESTAPI_service_class_list_handler.h src/RESTAPI/RESTAPI_service_class_handler.cpp src/RESTAPI/RESTAPI_service_class_handler.h src/RESTAPI/RESTAPI_operators_list_handler.cpp src/RESTAPI/RESTAPI_operators_list_handler.h src/RESTAPI/RESTAPI_operators_handler.cpp src/RESTAPI/RESTAPI_operators_handler.h src/storage/storage_op_contacts.cpp src/storage/storage_op_contacts.h src/storage/storage_op_locations.cpp src/storage/storage_op_locations.h src/RESTAPI/RESTAPI_op_contact_list_handler.cpp src/RESTAPI/RESTAPI_op_contact_list_handler.h src/RESTAPI/RESTAPI_op_contact_handler.cpp src/RESTAPI/RESTAPI_op_contact_handler.h src/RESTAPI/RESTAPI_op_location_list_handler.cpp src/RESTAPI/RESTAPI_op_location_list_handler.h src/RESTAPI/RESTAPI_op_location_handler.cpp src/RESTAPI/RESTAPI_op_location_handler.h src/ProvWebSocketClient.cpp src/ProvWebSocketClient.h src/Tasks/VenueRebooter.h src/Tasks/VenueUpgrade.h src/sdks/SDK_fms.cpp src/sdks/SDK_fms.h) | ||||||
|  |  | ||||||
| target_link_libraries(owprov PUBLIC | target_link_libraries(owprov PUBLIC | ||||||
|         ${Poco_LIBRARIES} |         ${Poco_LIBRARIES} | ||||||
|   | |||||||
							
								
								
									
										54
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -1,4 +1,10 @@ | |||||||
| FROM alpine:3.15 AS build-base | ARG ALPINE_VERSION=3.16.2 | ||||||
|  | ARG POCO_VERSION=poco-tip-v1 | ||||||
|  | ARG FMTLIB_VERSION=9.0.0 | ||||||
|  | ARG CPPKAFKA_VERSION=tip-v1 | ||||||
|  | ARG JSON_VALIDATOR_VERSION=2.1.0 | ||||||
|  |  | ||||||
|  | FROM alpine:$ALPINE_VERSION AS build-base | ||||||
|  |  | ||||||
| RUN apk add --update --no-cache \ | RUN apk add --update --no-cache \ | ||||||
|     make cmake g++ git \ |     make cmake g++ git \ | ||||||
| @@ -9,8 +15,10 @@ RUN apk add --update --no-cache \ | |||||||
|  |  | ||||||
| FROM build-base AS poco-build | FROM build-base AS poco-build | ||||||
|  |  | ||||||
| ADD https://api.github.com/repos/stephb9959/poco/git/refs/heads/master version.json | ARG POCO_VERSION | ||||||
| RUN git clone https://github.com/stephb9959/poco /poco |  | ||||||
|  | ADD https://api.github.com/repos/AriliaWireless/poco/git/refs/tags/${POCO_VERSION} version.json | ||||||
|  | RUN git clone https://github.com/AriliaWireless/poco --branch ${POCO_VERSION} /poco | ||||||
|  |  | ||||||
| WORKDIR /poco | WORKDIR /poco | ||||||
| RUN mkdir cmake-build | RUN mkdir cmake-build | ||||||
| @@ -19,10 +27,26 @@ RUN cmake .. | |||||||
| RUN cmake --build . --config Release -j8 | RUN cmake --build . --config Release -j8 | ||||||
| RUN cmake --build . --target install | RUN cmake --build . --target install | ||||||
|  |  | ||||||
|  | FROM build-base AS fmtlib-build | ||||||
|  |  | ||||||
|  | ARG FMTLIB_VERSION | ||||||
|  |  | ||||||
|  | ADD https://api.github.com/repos/fmtlib/fmt/git/refs/tags/${FMTLIB_VERSION} version.json | ||||||
|  | RUN git clone https://github.com/fmtlib/fmt --branch ${FMTLIB_VERSION} /fmtlib | ||||||
|  |  | ||||||
|  | WORKDIR /fmtlib | ||||||
|  | RUN mkdir cmake-build | ||||||
|  | WORKDIR cmake-build | ||||||
|  | RUN cmake .. | ||||||
|  | RUN make | ||||||
|  | RUN make install | ||||||
|  |  | ||||||
| FROM build-base AS cppkafka-build | FROM build-base AS cppkafka-build | ||||||
|  |  | ||||||
| ADD https://api.github.com/repos/stephb9959/cppkafka/git/refs/heads/master version.json | ARG CPPKAFKA_VERSION | ||||||
| RUN git clone https://github.com/stephb9959/cppkafka /cppkafka |  | ||||||
|  | ADD https://api.github.com/repos/AriliaWireless/cppkafka/git/refs/tags/${CPPKAFKA_VERSION} version.json | ||||||
|  | RUN git clone https://github.com/AriliaWireless/cppkafka --branch ${CPPKAFKA_VERSION} /cppkafka | ||||||
|  |  | ||||||
| WORKDIR /cppkafka | WORKDIR /cppkafka | ||||||
| RUN mkdir cmake-build | RUN mkdir cmake-build | ||||||
| @@ -33,8 +57,10 @@ RUN cmake --build . --target install | |||||||
|  |  | ||||||
| FROM build-base AS json-schema-validator-build | FROM build-base AS json-schema-validator-build | ||||||
|  |  | ||||||
| ADD https://api.github.com/repos/pboettch/json-schema-validator/git/refs/heads/master version.json | ARG JSON_VALIDATOR_VERSION | ||||||
| RUN git clone https://github.com/pboettch/json-schema-validator /json-schema-validator |  | ||||||
|  | ADD https://api.github.com/repos/pboettch/json-schema-validator/git/refs/tags/${JSON_VALIDATOR_VERSION} version.json | ||||||
|  | RUN git clone https://github.com/pboettch/json-schema-validator --branch ${JSON_VALIDATOR_VERSION} /json-schema-validator | ||||||
|  |  | ||||||
| WORKDIR /json-schema-validator | WORKDIR /json-schema-validator | ||||||
| RUN mkdir cmake-build | RUN mkdir cmake-build | ||||||
| @@ -43,18 +69,6 @@ RUN cmake .. | |||||||
| RUN make | RUN make | ||||||
| RUN make install | RUN make install | ||||||
|  |  | ||||||
| FROM build-base AS fmtlib-build |  | ||||||
|  |  | ||||||
| ADD https://api.github.com/repos/fmtlib/fmt/git/refs/heads/master version.json |  | ||||||
| RUN git clone https://github.com/fmtlib/fmt /fmtlib |  | ||||||
|  |  | ||||||
| WORKDIR /fmtlib |  | ||||||
| RUN mkdir cmake-build |  | ||||||
| WORKDIR cmake-build |  | ||||||
| RUN cmake .. |  | ||||||
| RUN make |  | ||||||
| RUN make install |  | ||||||
|  |  | ||||||
| FROM build-base AS owprov-build | FROM build-base AS owprov-build | ||||||
|  |  | ||||||
| ADD CMakeLists.txt build /owprov/ | ADD CMakeLists.txt build /owprov/ | ||||||
| @@ -77,7 +91,7 @@ WORKDIR /owprov/cmake-build | |||||||
| RUN cmake .. | RUN cmake .. | ||||||
| RUN cmake --build . --config Release -j8 | RUN cmake --build . --config Release -j8 | ||||||
|  |  | ||||||
| FROM alpine:3.15 | FROM alpine:$ALPINE_VERSION | ||||||
|  |  | ||||||
| ENV OWPROV_USER=owprov \ | ENV OWPROV_USER=owprov \ | ||||||
|     OWPROV_ROOT=/owprov-data \ |     OWPROV_ROOT=/owprov-data \ | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								helm/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								helm/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,3 @@ | |||||||
| *.swp | *.swp | ||||||
|  | Chart.lock | ||||||
|  | charts/ | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| {{- $root := . -}} | {{- $root := . -}} | ||||||
|  | {{- $storageType := index .Values.configProperties "storage.type" -}} | ||||||
| --- | --- | ||||||
| apiVersion: apps/v1 | apiVersion: apps/v1 | ||||||
| kind: Deployment | kind: Deployment | ||||||
| @@ -46,6 +47,39 @@ spec: | |||||||
|             - -timeout |             - -timeout | ||||||
|             - 600s |             - 600s | ||||||
|  |  | ||||||
|  | {{- if eq $storageType "postgresql" }} | ||||||
|  |         - name: wait-postgres | ||||||
|  |           image: "{{ .Values.images.owprov.repository }}:{{ .Values.images.owprov.tag }}" | ||||||
|  |           imagePullPolicy: {{ .Values.images.owprov.pullPolicy }} | ||||||
|  |           command: | ||||||
|  |             - /wait-for-postgres.sh | ||||||
|  |             - {{ index .Values.configProperties "storage.type.postgresql.host" }} | ||||||
|  |             - echo | ||||||
|  |             - "PostgreSQL is ready" | ||||||
|  |           env: | ||||||
|  |             - name: KUBERNETES_DEPLOYED | ||||||
|  |               value: "{{ now }}" | ||||||
|  |           {{- range $key, $value := .Values.public_env_variables }} | ||||||
|  |             - name: {{ $key }} | ||||||
|  |               value: {{ $value | quote }} | ||||||
|  |           {{- end }} | ||||||
|  |           {{- range $key, $value := .Values.secret_env_variables }} | ||||||
|  |             - name: {{ $key }} | ||||||
|  |               valueFrom: | ||||||
|  |                 secretKeyRef: | ||||||
|  |                   name: {{ include "owprov.fullname" $root }}-env | ||||||
|  |                   key: {{ $key }} | ||||||
|  |           {{- end }} | ||||||
|  |           volumeMounts: | ||||||
|  |           {{- range .Values.volumes.owprov }} | ||||||
|  |           - name: {{ .name }} | ||||||
|  |             mountPath: {{ .mountPath }} | ||||||
|  |             {{- if .subPath }} | ||||||
|  |             subPath: {{ .subPath }} | ||||||
|  |             {{- end }} | ||||||
|  |           {{- end }} | ||||||
|  | {{- end }} | ||||||
|  |  | ||||||
|       containers: |       containers: | ||||||
|  |  | ||||||
|         - name: owprov |         - name: owprov | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ fullnameOverride: "" | |||||||
| images: | images: | ||||||
|   owprov: |   owprov: | ||||||
|     repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owprov |     repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owprov | ||||||
|     tag: main |     tag: v2.7.0-RC1 | ||||||
|     pullPolicy: Always |     pullPolicy: Always | ||||||
| #    regcred: | #    regcred: | ||||||
| #      registry: tip-tip-wlan-cloud-ucentral.jfrog.io | #      registry: tip-tip-wlan-cloud-ucentral.jfrog.io | ||||||
|   | |||||||
| @@ -1958,11 +1958,6 @@ paths: | |||||||
|           schema: |           schema: | ||||||
|             type: boolean |             type: boolean | ||||||
|           required: false |           required: false | ||||||
|         - in: query |  | ||||||
|           name: deviceType |  | ||||||
|           schema: |  | ||||||
|             type: string |  | ||||||
|           required: false |  | ||||||
|         - in: query |         - in: query | ||||||
|           description: Pagination start (starts at 1. If not specified, 1 is assumed) |           description: Pagination start (starts at 1. If not specified, 1 is assumed) | ||||||
|           name: offset |           name: offset | ||||||
| @@ -2034,6 +2029,21 @@ paths: | |||||||
|             type: string |             type: string | ||||||
|             format: uuid |             format: uuid | ||||||
|           required: false |           required: false | ||||||
|  |         - in: query | ||||||
|  |           description: return RRM settings for a specific device | ||||||
|  |           name: rrmSettings | ||||||
|  |           schema: | ||||||
|  |             type: boolean | ||||||
|  |             default: false | ||||||
|  |           required: false | ||||||
|  |         - in: query | ||||||
|  |           description: return the resolved configuration for a specific device | ||||||
|  |           name:   resolveConfig | ||||||
|  |           schema: | ||||||
|  |             type: boolean | ||||||
|  |             default: false | ||||||
|  |           required: false | ||||||
|  |  | ||||||
|       responses: |       responses: | ||||||
|         200: |         200: | ||||||
|           description: Return a list of elements |           description: Return a list of elements | ||||||
| @@ -2255,12 +2265,6 @@ paths: | |||||||
|             type: string |             type: string | ||||||
|             example: serial1,serial2,serial3 |             example: serial1,serial2,serial3 | ||||||
|           required: false |           required: false | ||||||
|         - in: query |  | ||||||
|           description: only serial numbers of full device details |  | ||||||
|           name: serialOnly |  | ||||||
|           schema: |  | ||||||
|             type: boolean |  | ||||||
|           required: false |  | ||||||
|         - in: query |         - in: query | ||||||
|           description: return the number of devices |           description: return the number of devices | ||||||
|           name: countOnly |           name: countOnly | ||||||
|   | |||||||
							
								
								
									
										174
									
								
								openapi/rrm_provider.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								openapi/rrm_provider.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | |||||||
|  | openapi: 3.0.1 | ||||||
|  | info: | ||||||
|  |   title: OpenWiFi RRM Provider Model | ||||||
|  |   description: Definitions and APIs to manages an OpenWiFi RRM Providers. | ||||||
|  |   version: 1.0.0 | ||||||
|  |   license: | ||||||
|  |     name: BSD3 | ||||||
|  |     url: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE | ||||||
|  |  | ||||||
|  | servers: | ||||||
|  |   - url: 'https://localhost:16022/api/v1' | ||||||
|  |  | ||||||
|  | security: | ||||||
|  |   - bearerAuth: [] | ||||||
|  |   - ApiKeyAuth: [] | ||||||
|  |  | ||||||
|  | components: | ||||||
|  |   securitySchemes: | ||||||
|  |     ApiKeyAuth: | ||||||
|  |       type: apiKey | ||||||
|  |       in: header | ||||||
|  |       name: X-API-KEY | ||||||
|  |     bearerAuth: | ||||||
|  |       type: http | ||||||
|  |       scheme: bearer | ||||||
|  |       bearerFormat: JWT | ||||||
|  |  | ||||||
|  |   responses: | ||||||
|  |     NotFound: | ||||||
|  |       $ref: 'https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/openpapi/owsec.yaml#/components/responses/NotFound' | ||||||
|  |     Unauthorized: | ||||||
|  |       $ref: 'https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/openpapi/owsec.yaml#/components/responses/Unauthorized' | ||||||
|  |     Success: | ||||||
|  |       $ref: 'https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/openpapi/owsec.yaml#/components/responses/Success' | ||||||
|  |     BadRequest: | ||||||
|  |       $ref: 'https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/openpapi/owsec.yaml#/components/responses/BadRequest' | ||||||
|  |  | ||||||
|  |   schemas: | ||||||
|  |  | ||||||
|  |     Provider: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         vendor: | ||||||
|  |           description: The name of the vendor for display. | ||||||
|  |           type: string | ||||||
|  |           minLength: 1 | ||||||
|  |           maxLength: 128 | ||||||
|  |         vendorShortname: | ||||||
|  |           description: A shortname for the vendor. Only letters and numbers are allowed. This is the name used internally. | ||||||
|  |           type: string | ||||||
|  |           minLength: 4 | ||||||
|  |           maxLength: 16 | ||||||
|  |         version: | ||||||
|  |           description: An identifier that will help users identify the version of the RRM module they are using. | ||||||
|  |           type: string | ||||||
|  |         about: | ||||||
|  |           description: A link to the Vendor page for this RRM Module | ||||||
|  |           type: string | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     Algorithm: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         name: | ||||||
|  |           description: A display for this algorithm. | ||||||
|  |           type: string | ||||||
|  |           minLength: 1 | ||||||
|  |           maxLength: 128 | ||||||
|  |         description: | ||||||
|  |           description: A description of the algorithm. | ||||||
|  |           type: string | ||||||
|  |         shortName: | ||||||
|  |           description: This is the name used internally. | ||||||
|  |           type: string | ||||||
|  |           minLength: 4 | ||||||
|  |           maxLength: 16 | ||||||
|  |         parameterFormat: | ||||||
|  |           description: this is a Regex used to validate the input. If this is empty, no validation will be performed. | ||||||
|  |           type: string | ||||||
|  |         parameterSamples: | ||||||
|  |           description: These samples will be displayed in the UI to the user trying to configure the options | ||||||
|  |           type: array | ||||||
|  |           items: | ||||||
|  |             type: string | ||||||
|  |         helper: | ||||||
|  |           description: A link to a web page or PDF document explaining the algorithm and its parameters | ||||||
|  |           type: string | ||||||
|  |  | ||||||
|  |     Algorithms: | ||||||
|  |       description: The list of all algorithms supported by the vendor | ||||||
|  |       type: array | ||||||
|  |       items: | ||||||
|  |         $ref: '#/components/schemas/Algorithm' | ||||||
|  |  | ||||||
|  | paths: | ||||||
|  |   /provider: | ||||||
|  |     get: | ||||||
|  |       tags: | ||||||
|  |         - RRM | ||||||
|  |       operationId: getProvider | ||||||
|  |       summary: Retrieve information about the provider for this RRM Module | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           $ref: '#/components/schemas/Provider' | ||||||
|  |         400: | ||||||
|  |           $ref: '#/components/responses/BadRequest' | ||||||
|  |         403: | ||||||
|  |           $ref: '#/components/responses/Unauthorized' | ||||||
|  |         404: | ||||||
|  |           $ref: '#/components/responses/NotFound' | ||||||
|  |  | ||||||
|  |   /algorithms: | ||||||
|  |     get: | ||||||
|  |       tags: | ||||||
|  |         - RRM | ||||||
|  |       operationId: getAlgorithms | ||||||
|  |       summary: Retrieve a lists of algorithms supported in the module. | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           $ref: '#/components/schemas/Algorithms' | ||||||
|  |         400: | ||||||
|  |           $ref: '#/components/responses/BadRequest' | ||||||
|  |         403: | ||||||
|  |           $ref: '#/components/responses/Unauthorized' | ||||||
|  |         404: | ||||||
|  |           $ref: '#/components/responses/NotFound' | ||||||
|  |  | ||||||
|  |   /runRRM: | ||||||
|  |     put: | ||||||
|  |       tags: | ||||||
|  |         - RRM | ||||||
|  |       operationId: runRRMNow | ||||||
|  |       summary: Run a specific or default RRM algorithm. The UI user or CLI user will have the ability to run an algorithm on demand. | ||||||
|  |       parameters: | ||||||
|  |         - in: query | ||||||
|  |           description: | ||||||
|  |           name: venue | ||||||
|  |           schema: | ||||||
|  |             type: string | ||||||
|  |             format: uuid | ||||||
|  |           required: true | ||||||
|  |         - in: query | ||||||
|  |           description: Perform RRM without updating anything. This may be used by an admin to see what RRM would do. | ||||||
|  |           name: mock | ||||||
|  |           schema: | ||||||
|  |             type: boolean | ||||||
|  |             default: false | ||||||
|  |           required: false | ||||||
|  |         - in: query | ||||||
|  |           description: Specify the RRM algorithm to use. If omitted, select the default algorithm. | ||||||
|  |           schema: | ||||||
|  |             type: string | ||||||
|  |           required: false | ||||||
|  |         - in: query | ||||||
|  |           description: Specify the parameters to use with the RRM algorithm to use. If omitted, select the default parameters. | ||||||
|  |           schema: | ||||||
|  |             type: string | ||||||
|  |           required: false | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           description: Return the list of actions that were or would be performed. | ||||||
|  |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 type: array | ||||||
|  |                 items: | ||||||
|  |                   type: string | ||||||
|  |         400: | ||||||
|  |           $ref: '#/components/responses/BadRequest' | ||||||
|  |         403: | ||||||
|  |           $ref: '#/components/responses/Unauthorized' | ||||||
|  |         404: | ||||||
|  |           $ref: '#/components/responses/NotFound' | ||||||
|  |  | ||||||
| @@ -33,7 +33,7 @@ namespace OpenWifi { | |||||||
|             void Stop() override; |             void Stop() override; | ||||||
|             void ConnectionReceived( const std::string & Key, const std::string & Payload) { |             void ConnectionReceived( const std::string & Key, const std::string & Payload) { | ||||||
|                 std::lock_guard G(Mutex_); |                 std::lock_guard G(Mutex_); | ||||||
|                 Logger().information(Poco::format("Device(%s): Connection/Ping message.", Key)); |                 poco_debug(Logger(),Poco::format("Device(%s): Connection/Ping message.", Key)); | ||||||
|                 Queue_.enqueueNotification( new DiscoveryMessage(Key,Payload)); |                 Queue_.enqueueNotification( new DiscoveryMessage(Key,Payload)); | ||||||
|             } |             } | ||||||
|             void run() override; |             void run() override; | ||||||
|   | |||||||
| @@ -33,22 +33,22 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|             std::lock_guard G(Mutex_); |             std::lock_guard G(Mutex_); | ||||||
|  |  | ||||||
|             for(auto &job:jobs_) { |             for(auto ¤t_job:jobs_) { | ||||||
|                 if(job!=nullptr) { |                 if(current_job!=nullptr) { | ||||||
|                     if(job->Started()==0 && Pool_.used()<Pool_.available()) { |                     if(current_job->Started()==0 && Pool_.used()<Pool_.available()) { | ||||||
|                         job->Logger().information(fmt::format("Starting {}: {}",job->JobId(),job->Name())); |                         current_job->Logger().information(fmt::format("Starting {}: {}",current_job->JobId(),current_job->Name())); | ||||||
|                         job->Start(); |                         current_job->Start(); | ||||||
|                         Pool_.start(*job); |                         Pool_.start(*current_job); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             for(auto it = jobs_.begin(); it!=jobs_.end();) { |             for(auto it = jobs_.begin(); it!=jobs_.end();) {\ | ||||||
|                 if(*it!=nullptr && (*it)->Completed()!=0) { |                 auto current_job = *it; | ||||||
|                     auto tmp = it; |                 if(current_job!=nullptr && current_job->Completed()!=0) { | ||||||
|                     (*it)->Logger().information(fmt::format("Completed {}: {}",(*it)->JobId(),(*it)->Name())); |                     current_job->Logger().information(fmt::format("Completed {}: {}",current_job->JobId(),current_job->Name())); | ||||||
|                     it = jobs_.erase(it); |                     it = jobs_.erase(it); | ||||||
|                     delete *tmp; |                     delete current_job; | ||||||
|                 } else { |                 } else { | ||||||
|                     ++it; |                     ++it; | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ namespace OpenWifi { | |||||||
|                                                               bool &Done, std::string &Answer) { |                                                               bool &Done, std::string &Answer) { | ||||||
|         Done = false; |         Done = false; | ||||||
|         auto Prefix = O->get("serial_prefix").toString(); |         auto Prefix = O->get("serial_prefix").toString(); | ||||||
|  |         Poco::toLowerInPlace(Prefix); | ||||||
|         Logger().information(Poco::format("serial_number_search: %s", Prefix)); |         Logger().information(Poco::format("serial_number_search: %s", Prefix)); | ||||||
|         if (!Prefix.empty() && Prefix.length() < 13) { |         if (!Prefix.empty() && Prefix.length() < 13) { | ||||||
|             std::vector<uint64_t> Numbers; |             std::vector<uint64_t> Numbers; | ||||||
| @@ -83,6 +84,7 @@ namespace OpenWifi { | |||||||
|         Done = false; |         Done = false; | ||||||
|         auto operatorId = O->get("operatorId").toString(); |         auto operatorId = O->get("operatorId").toString(); | ||||||
|         auto Prefix = O->get("serial_prefix").toString(); |         auto Prefix = O->get("serial_prefix").toString(); | ||||||
|  |         Poco::toLowerInPlace(Prefix); | ||||||
|         std::string Query; |         std::string Query; | ||||||
|  |  | ||||||
|         if(Prefix[0]=='*') { |         if(Prefix[0]=='*') { | ||||||
|   | |||||||
| @@ -9,6 +9,9 @@ | |||||||
| #include "framework/MicroService.h" | #include "framework/MicroService.h" | ||||||
| #include "framework/ConfigurationValidator.h" | #include "framework/ConfigurationValidator.h" | ||||||
| #include "sdks/SDK_sec.h" | #include "sdks/SDK_sec.h" | ||||||
|  | #include "Poco/StringTokenizer.h" | ||||||
|  |  | ||||||
|  | #include "libs/croncpp.h" | ||||||
|  |  | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|  |  | ||||||
| @@ -405,7 +408,9 @@ namespace OpenWifi { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     inline bool ValidateConfigBlock(const ProvObjects::DeviceConfiguration &Config, RESTAPI::Errors::msg & Error) { |     inline bool ValidateConfigBlock(const ProvObjects::DeviceConfiguration &Config, RESTAPI::Errors::msg & Error) { | ||||||
|         static const std::vector<std::string> SectionNames{ "globals", "interfaces", "metrics", "radios", "services", "unit" }; |         static const std::vector<std::string> SectionNames{ "globals", "interfaces", "metrics", "radios", "services", | ||||||
|  |                                                             "unit", "definitions", "ethernet", "switch", "config-raw", | ||||||
|  |                                                             "third-party" }; | ||||||
|  |  | ||||||
|         for(const auto &i:Config.configuration) { |         for(const auto &i:Config.configuration) { | ||||||
|             Poco::JSON::Parser  P; |             Poco::JSON::Parser  P; | ||||||
| @@ -521,12 +526,39 @@ namespace OpenWifi { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return Result; |         return Result; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     inline bool ValidSchedule(const std::string &v) { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             auto cron = cron::make_cron(v); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         catch (cron::bad_cronexpr const & ex) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline bool ValidRRM(const std::string &v) { | ||||||
|  |         if((v=="no") || (v=="inherit")) return true; | ||||||
|  |         try { | ||||||
|  |             Poco::JSON::Parser  P; | ||||||
|  |             auto O = P.parse(v).extract<Poco::JSON::Object::Ptr>(); | ||||||
|  |  | ||||||
|  |             ProvObjects::RRMDetails D; | ||||||
|  |             if(D.from_json(O)) { | ||||||
|  |                 return ValidSchedule(D.schedule); | ||||||
|  |             } | ||||||
|  |         } catch (...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     inline bool ValidDeviceRules(const ProvObjects::DeviceRules & DR) { |     inline bool ValidDeviceRules(const ProvObjects::DeviceRules & DR) { | ||||||
|         return  (DR.rrm=="yes" || DR.rrm=="no" || DR.rrm=="inherit") && |         return  (ValidRRM(DR.rrm)) && | ||||||
|                 (DR.firmwareUpgrade=="yes" || DR.firmwareUpgrade=="no" || DR.firmwareUpgrade=="inherit") && |                 (DR.firmwareUpgrade=="yes" || DR.firmwareUpgrade=="no" || DR.firmwareUpgrade=="inherit") && | ||||||
|                 (DR.rcOnly=="yes" || DR.rcOnly=="no" || DR.rcOnly=="inherit"); |                 (DR.rcOnly=="yes" || DR.rcOnly=="no" || DR.rcOnly=="inherit"); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -41,7 +41,8 @@ namespace OpenWifi{ | |||||||
|         if (SerialNumber.empty() || !DB_.GetRecord(RESTAPI::Protocol::SERIALNUMBER, SerialNumber, Existing)) { |         if (SerialNumber.empty() || !DB_.GetRecord(RESTAPI::Protocol::SERIALNUMBER, SerialNumber, Existing)) { | ||||||
|             return NotFound(); |             return NotFound(); | ||||||
|         } |         } | ||||||
|         Logger().debug(Poco::format("%s,%s: Retrieving inventory information.", Existing.serialNumber, Existing.info.id )); |         Logger().debug( | ||||||
|  |                 Poco::format("%s,%s: Retrieving inventory information.", Existing.serialNumber, Existing.info.id)); | ||||||
|  |  | ||||||
|         Poco::JSON::Object Answer; |         Poco::JSON::Object Answer; | ||||||
|         std::string Arg; |         std::string Arg; | ||||||
| @@ -58,13 +59,29 @@ namespace OpenWifi{ | |||||||
|                 Answer.set("config", "none"); |                 Answer.set("config", "none"); | ||||||
|             } |             } | ||||||
|             return ReturnObject(Answer); |             return ReturnObject(Answer); | ||||||
|         } else if(HasParameter("firmwareOptions", Arg) && Arg=="true") { |         } else if (GetBoolParameter("firmwareOptions", false)) { | ||||||
|             ProvObjects::DeviceRules Rules; |             ProvObjects::DeviceRules Rules; | ||||||
|             StorageService()->InventoryDB().EvaluateDeviceSerialNumberRules(SerialNumber, Rules); |             StorageService()->InventoryDB().EvaluateDeviceSerialNumberRules(SerialNumber, Rules); | ||||||
|             Answer.set("firmwareUpgrade", Rules.firmwareUpgrade); |             Answer.set("firmwareUpgrade", Rules.firmwareUpgrade); | ||||||
|             Answer.set("firmwareRCOnly", Rules.rcOnly == "yes"); |             Answer.set("firmwareRCOnly", Rules.rcOnly == "yes"); | ||||||
|             return ReturnObject(Answer); |             return ReturnObject(Answer); | ||||||
|         } else if(HasParameter("applyConfiguration",Arg) && Arg=="true") { |         } else if(GetBoolParameter("rrmSettings",false)) { | ||||||
|  |             ProvObjects::DeviceRules Rules; | ||||||
|  |             StorageService()->InventoryDB().EvaluateDeviceSerialNumberRules(SerialNumber, Rules); | ||||||
|  |             if(Rules.rrm=="no" || Rules.rrm=="inherit") { | ||||||
|  |                 Answer.set("rrm", Rules.rrm); | ||||||
|  |             } else { | ||||||
|  |                 ProvObjects::RRMDetails D; | ||||||
|  |                 Poco::JSON::Parser  P; | ||||||
|  |                 try { | ||||||
|  |                     auto Obj = P.parse(Rules.rrm).extract<Poco::JSON::Object::Ptr>(); | ||||||
|  |                     Answer.set("rrm", Obj); | ||||||
|  |                 } catch (...) { | ||||||
|  |                     Answer.set("rrm", "invalid"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return ReturnObject(Answer); | ||||||
|  |         } else if(GetBoolParameter("applyConfiguration", false)) { | ||||||
|             Logger().debug(Poco::format("%s: Retrieving configuration.",Existing.serialNumber)); |             Logger().debug(Poco::format("%s: Retrieving configuration.",Existing.serialNumber)); | ||||||
|             auto Device = std::make_shared<APConfig>(SerialNumber, Existing.deviceType, Logger(), false); |             auto Device = std::make_shared<APConfig>(SerialNumber, Existing.deviceType, Logger(), false); | ||||||
|             auto Configuration = Poco::makeShared<Poco::JSON::Object>(); |             auto Configuration = Poco::makeShared<Poco::JSON::Object>(); | ||||||
| @@ -91,6 +108,19 @@ namespace OpenWifi{ | |||||||
|             } |             } | ||||||
|             Results.to_json(Answer); |             Results.to_json(Answer); | ||||||
|             return ReturnObject(Answer); |             return ReturnObject(Answer); | ||||||
|  |         } else if(GetBoolParameter("resolveConfig", false)) { | ||||||
|  |             Logger().debug(Poco::format("%s: Retrieving configuration.",Existing.serialNumber)); | ||||||
|  |             auto Device = std::make_shared<APConfig>(SerialNumber, Existing.deviceType, Logger(), false); | ||||||
|  |             auto Configuration = Poco::makeShared<Poco::JSON::Object>(); | ||||||
|  |             Poco::JSON::Object ErrorsObj, WarningsObj; | ||||||
|  |             ProvObjects::InventoryConfigApplyResult Results; | ||||||
|  |             Logger().debug(Poco::format("%s: Computing configuration.",Existing.serialNumber)); | ||||||
|  |             if (Device->Get(Configuration)) { | ||||||
|  |                 Answer.set("configuration", Configuration); | ||||||
|  |             } else { | ||||||
|  |                 Answer.set("error", 1); | ||||||
|  |             } | ||||||
|  |             return ReturnObject(Answer); | ||||||
|         }   else if(QB_.AdditionalInfo) { |         }   else if(QB_.AdditionalInfo) { | ||||||
|             AddExtendedInfo(Existing,Answer); |             AddExtendedInfo(Existing,Answer); | ||||||
|         } |         } | ||||||
| @@ -136,6 +166,7 @@ namespace OpenWifi{ | |||||||
|  |  | ||||||
|     void RESTAPI_inventory_handler::DoPost() { |     void RESTAPI_inventory_handler::DoPost() { | ||||||
|         std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER,""); |         std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER,""); | ||||||
|  |         Poco::toLowerInPlace(SerialNumber); | ||||||
|         if(SerialNumber.empty()) { |         if(SerialNumber.empty()) { | ||||||
|             return BadRequest(RESTAPI::Errors::MissingSerialNumber); |             return BadRequest(RESTAPI::Errors::MissingSerialNumber); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -101,7 +101,7 @@ namespace OpenWifi { | |||||||
|                 { "email", UserName }, |                 { "email", UserName }, | ||||||
|                 { "signupUUID" , SignupUUID }, |                 { "signupUUID" , SignupUUID }, | ||||||
|                 { "owner" , SignupOperator.info.id }, |                 { "owner" , SignupOperator.info.id }, | ||||||
|  |                 { "operatorName", SignupOperator.registrationId } | ||||||
|         }, Body, 30000); |         }, Body, 30000); | ||||||
|  |  | ||||||
|         Poco::JSON::Object::Ptr Answer; |         Poco::JSON::Object::Ptr Answer; | ||||||
|   | |||||||
| @@ -1159,5 +1159,40 @@ namespace OpenWifi::ProvObjects { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void RRMAlgorithmDetails::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj,"name",name); | ||||||
|  |         field_to_json(Obj,"parameters",parameters); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     bool RRMAlgorithmDetails::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             field_from_json(Obj,"name",name); | ||||||
|  |             field_from_json(Obj,"parameters",parameters); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void RRMDetails::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj,"vendor",vendor); | ||||||
|  |         field_to_json(Obj,"schedule",schedule); | ||||||
|  |         field_to_json(Obj,"algorithms",algorithms); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool RRMDetails::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             field_from_json(Obj,"vendor",vendor); | ||||||
|  |             field_from_json(Obj,"schedule",schedule); | ||||||
|  |             field_from_json(Obj,"algorithms",algorithms); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -62,6 +62,21 @@ namespace OpenWifi::ProvObjects { | |||||||
|     }; |     }; | ||||||
|     typedef std::vector<ManagementPolicy>      ManagementPolicyVec; |     typedef std::vector<ManagementPolicy>      ManagementPolicyVec; | ||||||
|  |  | ||||||
|  |     struct RRMAlgorithmDetails { | ||||||
|  |         std::string     name; | ||||||
|  |         std::string     parameters; | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct RRMDetails { | ||||||
|  |         std::string     vendor; | ||||||
|  |         std::string     schedule; | ||||||
|  |         std::vector<RRMAlgorithmDetails>    algorithms; | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     struct DeviceRules { |     struct DeviceRules { | ||||||
|         std::string     rcOnly{"inherit"}; |         std::string     rcOnly{"inherit"}; | ||||||
|         std::string     rrm{"inherit"}; |         std::string     rrm{"inherit"}; | ||||||
|   | |||||||
| @@ -195,18 +195,24 @@ namespace OpenWifi { | |||||||
|         // check that all inventory in venues and entities actually exists, if not, fix it. |         // check that all inventory in venues and entities actually exists, if not, fix it. | ||||||
|         auto FixVenueDevices = [&](const ProvObjects::Venue &V) -> bool { |         auto FixVenueDevices = [&](const ProvObjects::Venue &V) -> bool { | ||||||
|             Types::UUIDvec_t NewDevices; |             Types::UUIDvec_t NewDevices; | ||||||
|  |             bool modified=false; | ||||||
|             for(const auto &device:V.devices) { |             for(const auto &device:V.devices) { | ||||||
|                 ProvObjects::InventoryTag T; |                 ProvObjects::InventoryTag T; | ||||||
|                 if(InventoryDB().GetRecord("id", device, T)) { |                 if(InventoryDB().GetRecord("id", device, T)) { | ||||||
|                     NewDevices.emplace_back(device); |                     NewDevices.emplace_back(device); | ||||||
|                 } else { |                 } else { | ||||||
|  |                     modified=true; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             if(NewDevices!=V.devices) { |  | ||||||
|                 Logger().warning(fmt::format("  fixing venue: {}", V.info.name)); |  | ||||||
|             ProvObjects::Venue NewVenue = V; |             ProvObjects::Venue NewVenue = V; | ||||||
|  |             if(V.deviceRules.rrm=="yes") { | ||||||
|  |                 NewVenue.deviceRules.rrm="inherit"; | ||||||
|  |                 modified=true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if(modified) { | ||||||
|  |                 Logger().warning(fmt::format("  fixing venue: {}", V.info.name)); | ||||||
|                 NewVenue.devices = NewDevices; |                 NewVenue.devices = NewDevices; | ||||||
|                 VenueDB().UpdateRecord("id", V.info.id, NewVenue); |                 VenueDB().UpdateRecord("id", V.info.id, NewVenue); | ||||||
|             } |             } | ||||||
| @@ -266,10 +272,16 @@ namespace OpenWifi { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             ProvObjects::Entity NewEntity = E; | ||||||
|  |  | ||||||
|  |             if(E.deviceRules.rrm=="yes") { | ||||||
|  |                 NewEntity.deviceRules.rrm="inherit"; | ||||||
|  |                 Modified=true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             if(Modified) |             if(Modified) | ||||||
|             { |             { | ||||||
|                 Logger().warning(fmt::format("  fixing entity: {}",E.info.name)); |                 Logger().warning(fmt::format("  fixing entity: {}",E.info.name)); | ||||||
|                 ProvObjects::Entity NewEntity = E; |  | ||||||
|                 NewEntity.devices = NewDevices; |                 NewEntity.devices = NewDevices; | ||||||
|                 NewEntity.contacts = NewContacts; |                 NewEntity.contacts = NewContacts; | ||||||
|                 NewEntity.locations = NewLocations; |                 NewEntity.locations = NewLocations; | ||||||
| @@ -304,6 +316,11 @@ namespace OpenWifi { | |||||||
|                 modified=true; |                 modified=true; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             if(T.deviceRules.rrm=="yes") { | ||||||
|  |                 NewTag.deviceRules.rrm = "inherit"; | ||||||
|  |                 modified=true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             if(modified) { |             if(modified) { | ||||||
|                 Logger().warning(fmt::format("  fixing entity: {}",T.info.name)); |                 Logger().warning(fmt::format("  fixing entity: {}",T.info.name)); | ||||||
|                 InventoryDB().UpdateRecord("id", T.info.id, NewTag); |                 InventoryDB().UpdateRecord("id", T.info.id, NewTag); | ||||||
| @@ -311,12 +328,67 @@ namespace OpenWifi { | |||||||
|             return true; |             return true; | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  |         auto FixConfiguration = [&](const ProvObjects::DeviceConfiguration &C) -> bool { | ||||||
|  |             ProvObjects::DeviceConfiguration NewConfig{C}; | ||||||
|  |  | ||||||
|  |             bool modified = false; | ||||||
|  |  | ||||||
|  |             if (C.deviceRules.rrm == "yes") { | ||||||
|  |                 NewConfig.deviceRules.rrm = "inherit"; | ||||||
|  |                 modified = true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (modified) { | ||||||
|  |                 Logger().warning(fmt::format("  fixing configuration: {}", C.info.name)); | ||||||
|  |                 ConfigurationDB().UpdateRecord("id", C.info.id, NewConfig); | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         auto FixOperator = [&](const ProvObjects::Operator &O) -> bool { | ||||||
|  |             ProvObjects::Operator NewOp{O}; | ||||||
|  |             bool modified = false; | ||||||
|  |  | ||||||
|  |             if (O.deviceRules.rrm == "yes") { | ||||||
|  |                 NewOp.deviceRules.rrm = "inherit"; | ||||||
|  |                 modified = true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (modified) { | ||||||
|  |                 Logger().warning(fmt::format("  fixing operator: {}", O.info.name)); | ||||||
|  |                 OperatorDB().UpdateRecord("id", O.info.id, NewOp); | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         auto FixSubscriber = [&](const ProvObjects::SubscriberDevice &O) -> bool { | ||||||
|  |             ProvObjects::SubscriberDevice NewSub{O}; | ||||||
|  |             bool modified = false; | ||||||
|  |  | ||||||
|  |             if (O.deviceRules.rrm == "yes") { | ||||||
|  |                 NewSub.deviceRules.rrm = "inherit"; | ||||||
|  |                 modified = true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (modified) { | ||||||
|  |                 Logger().warning(fmt::format("  fixing subscriber: {}", O.info.name)); | ||||||
|  |                 SubscriberDeviceDB().UpdateRecord("id", O.info.id, NewSub); | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         }; | ||||||
|  |  | ||||||
|         Logger().information("Checking DB consistency: venues"); |         Logger().information("Checking DB consistency: venues"); | ||||||
|         VenueDB().Iterate(FixVenueDevices); |         VenueDB().Iterate(FixVenueDevices); | ||||||
|         Logger().information("Checking DB consistency: entities"); |         Logger().information("Checking DB consistency: entities"); | ||||||
|         EntityDB().Iterate(FixEntity); |         EntityDB().Iterate(FixEntity); | ||||||
|         Logger().information("Checking DB consistency: inventory"); |         Logger().information("Checking DB consistency: inventory"); | ||||||
|         InventoryDB().Iterate(FixInventory); |         InventoryDB().Iterate(FixInventory); | ||||||
|  |         Logger().information("Checking DB consistency: configurations"); | ||||||
|  |         ConfigurationDB().Iterate(FixConfiguration); | ||||||
|  |         Logger().information("Checking DB consistency: operators"); | ||||||
|  |         OperatorDB().Iterate(FixOperator); | ||||||
|  |         Logger().information("Checking DB consistency: subscribers"); | ||||||
|  |         SubscriberDeviceDB().Iterate(FixSubscriber); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void Storage::InitializeSystemDBs() { |     void Storage::InitializeSystemDBs() { | ||||||
|   | |||||||
| @@ -20,8 +20,6 @@ namespace OpenWifi { | |||||||
|                 auto Status = Results->get("status").extract<Poco::JSON::Object::Ptr>(); |                 auto Status = Results->get("status").extract<Poco::JSON::Object::Ptr>(); | ||||||
|                 auto Rejected = Status->getArray("rejected"); |                 auto Rejected = Status->getArray("rejected"); | ||||||
|                 std::transform(Rejected->begin(),Rejected->end(),std::back_inserter(Warnings), [](auto i) -> auto { return i.toString(); }); |                 std::transform(Rejected->begin(),Rejected->end(),std::back_inserter(Warnings), [](auto i) -> auto { return i.toString(); }); | ||||||
| //                for(const auto &i:*Rejected) |  | ||||||
|                 //                  Warnings.push_back(i.toString()); |  | ||||||
|             } |             } | ||||||
|         } catch (...) { |         } catch (...) { | ||||||
|         } |         } | ||||||
| @@ -106,79 +104,67 @@ namespace OpenWifi { | |||||||
|             ProvObjects::Venue  Venue; |             ProvObjects::Venue  Venue; | ||||||
|             uint64_t Updated = 0, Failed = 0 , BadConfigs = 0 ; |             uint64_t Updated = 0, Failed = 0 , BadConfigs = 0 ; | ||||||
|             if(StorageService()->VenueDB().GetRecord("id",VenueUUID_,Venue)) { |             if(StorageService()->VenueDB().GetRecord("id",VenueUUID_,Venue)) { | ||||||
|                 const std::size_t MaxThreads=16; |  | ||||||
|                 struct tState { |  | ||||||
|                     Poco::Thread                thr_; |  | ||||||
|                     VenueDeviceConfigUpdater    *task= nullptr; |  | ||||||
|                 }; |  | ||||||
|  |  | ||||||
|                 N.content.title = fmt::format("Updating {} configurations", Venue.info.name); |                 N.content.title = fmt::format("Updating {} configurations", Venue.info.name); | ||||||
|                 N.content.jobId = JobId(); |                 N.content.jobId = JobId(); | ||||||
|  |  | ||||||
|                 std::array<tState,MaxThreads> Tasks; |                 Poco::ThreadPool    Pool_; | ||||||
|  |                 std::list<VenueDeviceConfigUpdater*> JobList; | ||||||
|  |  | ||||||
|  |  | ||||||
|                 for(const auto &uuid:Venue.devices) { |                 for(const auto &uuid:Venue.devices) { | ||||||
|                     auto NewTask = new VenueDeviceConfigUpdater(uuid, Venue.info.name, Logger()); |                     auto NewTask = new VenueDeviceConfigUpdater(uuid, Venue.info.name, Logger()); | ||||||
|                     // std::cout << "Scheduling config push for " << uuid << std::endl; |                     bool TaskAdded=false; | ||||||
|                     bool found_slot = false; |                     while(!TaskAdded) { | ||||||
|                     while (!found_slot) { |                         if (Pool_.available()) { | ||||||
|                         for (auto &cur_task: Tasks) { |                             JobList.push_back(NewTask); | ||||||
|                             if (cur_task.task == nullptr) { |                             Pool_.start(*NewTask); | ||||||
|                                 cur_task.task = NewTask; |                             TaskAdded = true; | ||||||
|                                 cur_task.thr_.start(*NewTask); |                             continue; | ||||||
|                                 found_slot = true; |  | ||||||
|                                 break; |  | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                         //  Let's look for a slot... |                     for(auto job_it = JobList.begin(); job_it !=JobList.end();) { | ||||||
|                         if (!found_slot) { |                         VenueDeviceConfigUpdater * current_job = *job_it; | ||||||
|                             for (auto &cur_task: Tasks) { |                         if(current_job!= nullptr && current_job->done_) { | ||||||
|                                 if (cur_task.task != nullptr && cur_task.task->started_) { |                             Updated += current_job->updated_; | ||||||
|                                     if (cur_task.thr_.isRunning()) |                             Failed += current_job->failed_; | ||||||
|                                         continue; |                             BadConfigs += current_job->bad_config_; | ||||||
|                                     if (!cur_task.thr_.isRunning() && cur_task.task->done_) { |                             if(current_job->updated_) { | ||||||
|                                         cur_task.thr_.join(); |                                 N.content.success.push_back(current_job->SerialNumber); | ||||||
|                                         Updated += cur_task.task->updated_; |                             } else if(current_job->failed_) { | ||||||
|                                         Failed += cur_task.task->failed_; |                                 N.content.warning.push_back(current_job->SerialNumber); | ||||||
|                                         BadConfigs += cur_task.task->bad_config_; |  | ||||||
|                                         cur_task.task->started_ = cur_task.task->done_ = false; |  | ||||||
|                                         delete cur_task.task; |  | ||||||
|                                         cur_task.task = nullptr; |  | ||||||
|                                     } |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 Logger().debug("Waiting for outstanding update threads to finish."); |  | ||||||
|                 bool stillTasksRunning=true; |  | ||||||
|                 while(stillTasksRunning) { |  | ||||||
|                     stillTasksRunning = false; |  | ||||||
|                     for(auto &cur_task:Tasks) { |  | ||||||
|                         if(cur_task.task!= nullptr && cur_task.task->started_) { |  | ||||||
|                             if(cur_task.thr_.isRunning()) { |  | ||||||
|                                 stillTasksRunning = true; |  | ||||||
|                                 continue; |  | ||||||
|                             } |  | ||||||
|                             if(!cur_task.thr_.isRunning() && cur_task.task->done_) { |  | ||||||
|                                 cur_task.thr_.join(); |  | ||||||
|                                 if(cur_task.task->updated_) { |  | ||||||
|                                     Updated++; |  | ||||||
|                                     N.content.success.push_back(cur_task.task->SerialNumber); |  | ||||||
|                                 } else if(cur_task.task->failed_) { |  | ||||||
|                                     Failed++; |  | ||||||
|                                     N.content.warning.push_back(cur_task.task->SerialNumber); |  | ||||||
|                             } else { |                             } else { | ||||||
|                                     BadConfigs++; |                                 N.content.error.push_back(current_job->SerialNumber); | ||||||
|                                     N.content.error.push_back(cur_task.task->SerialNumber); |  | ||||||
|                             } |                             } | ||||||
|                                 cur_task.task->started_ = cur_task.task->done_ = false; |                             job_it = JobList.erase(job_it); | ||||||
|                                 delete cur_task.task; |                             delete current_job; | ||||||
|                                 cur_task.task = nullptr; |                         } else { | ||||||
|  |                             ++job_it; | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  |                 Logger().debug("Waiting for outstanding update threads to finish."); | ||||||
|  |                 Pool_.joinAll(); | ||||||
|  |                 for(auto job_it = JobList.begin(); job_it !=JobList.end();) { | ||||||
|  |                     VenueDeviceConfigUpdater * current_job = *job_it; | ||||||
|  |                     if(current_job!= nullptr && current_job->done_) { | ||||||
|  |                         Updated += current_job->updated_; | ||||||
|  |                         Failed += current_job->failed_; | ||||||
|  |                         BadConfigs += current_job->bad_config_; | ||||||
|  |                         if(current_job->updated_) { | ||||||
|  |                             N.content.success.push_back(current_job->SerialNumber); | ||||||
|  |                         } else if(current_job->failed_) { | ||||||
|  |                             N.content.warning.push_back(current_job->SerialNumber); | ||||||
|  |                         } else { | ||||||
|  |                             N.content.error.push_back(current_job->SerialNumber); | ||||||
|  |                         } | ||||||
|  |                         job_it = JobList.erase(job_it); | ||||||
|  |                         delete current_job; | ||||||
|  |                     } else { | ||||||
|  |                         ++job_it; | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 N.content.details = fmt::format("Job {} Completed: {} updated, {} failed to update, {} bad configurations. ", |                 N.content.details = fmt::format("Job {} Completed: {} updated, {} failed to update, {} bad configurations. ", | ||||||
| @@ -189,6 +175,7 @@ namespace OpenWifi { | |||||||
|                 Logger().warning(N.content.details); |                 Logger().warning(N.content.details); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             // std::cout << N.content.details << std::endl; | ||||||
|             WebSocketClientNotificationVenueUpdateJobCompletionToUser(UserInfo().email, N); |             WebSocketClientNotificationVenueUpdateJobCompletionToUser(UserInfo().email, N); | ||||||
|             Logger().information(fmt::format("Job {} Completed: {} updated, {} failed to update , {} bad configurations.", |             Logger().information(fmt::format("Job {} Completed: {} updated, {} failed to update , {} bad configurations.", | ||||||
|                                              JobId(), Updated ,Failed, BadConfigs)); |                                              JobId(), Updated ,Failed, BadConfigs)); | ||||||
|   | |||||||
| @@ -66,77 +66,59 @@ namespace OpenWifi { | |||||||
|             ProvObjects::Venue  Venue; |             ProvObjects::Venue  Venue; | ||||||
|             uint64_t rebooted_ = 0, failed_ = 0; |             uint64_t rebooted_ = 0, failed_ = 0; | ||||||
|             if(StorageService()->VenueDB().GetRecord("id",VenueUUID_,Venue)) { |             if(StorageService()->VenueDB().GetRecord("id",VenueUUID_,Venue)) { | ||||||
|                 const std::size_t MaxThreads=16; |  | ||||||
|                 struct tState { |  | ||||||
|                     Poco::Thread                thr_; |  | ||||||
|                     VenueDeviceRebooter    *task= nullptr; |  | ||||||
|                 }; |  | ||||||
|  |  | ||||||
|                 N.content.title = fmt::format("Rebooting {} devices.", Venue.info.name); |                 N.content.title = fmt::format("Rebooting {} devices.", Venue.info.name); | ||||||
|                 N.content.jobId = JobId(); |                 N.content.jobId = JobId(); | ||||||
|  |  | ||||||
|                 std::array<tState,MaxThreads> Tasks; |                 Poco::ThreadPool    Pool_; | ||||||
|  |                 std::list<VenueDeviceRebooter*> JobList; | ||||||
|  |  | ||||||
|                 for(const auto &uuid:Venue.devices) { |                 for(const auto &uuid:Venue.devices) { | ||||||
|                     auto NewTask = new VenueDeviceRebooter(uuid, Venue.info.name, Logger()); |                     auto NewTask = new VenueDeviceRebooter(uuid, Venue.info.name, Logger()); | ||||||
|                     // std::cout << "Scheduling config push for " << uuid << std::endl; |                     bool TaskAdded=false; | ||||||
|                     bool found_slot = false; |                     while(!TaskAdded) { | ||||||
|                     while (!found_slot) { |                         if (Pool_.available()) { | ||||||
|                         for (auto &cur_task: Tasks) { |                             JobList.push_back(NewTask); | ||||||
|                             if (cur_task.task == nullptr) { |                             Pool_.start(*NewTask); | ||||||
|                                 cur_task.task = NewTask; |                             TaskAdded = true; | ||||||
|                                 cur_task.thr_.start(*NewTask); |                             continue; | ||||||
|                                 found_slot = true; |  | ||||||
|                                 break; |  | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                         //  Let's look for a slot... |                     for(auto job_it = JobList.begin(); job_it !=JobList.end();) { | ||||||
|                         if (!found_slot) { |                         VenueDeviceRebooter * current_job = *job_it; | ||||||
|                             for (auto &cur_task: Tasks) { |                         if(current_job!= nullptr && current_job->done_) { | ||||||
|                                 if (cur_task.task != nullptr && cur_task.task->started_) { |                             if(current_job->rebooted_) | ||||||
|                                     if (cur_task.thr_.isRunning()) |                                 N.content.success.push_back(current_job->SerialNumber); | ||||||
|                                         continue; |                             else | ||||||
|                                     if (!cur_task.thr_.isRunning() && cur_task.task->done_) { |                                 N.content.warning.push_back(current_job->SerialNumber); | ||||||
|                                         cur_task.thr_.join(); |                             rebooted_ += current_job->rebooted_; | ||||||
|                                         rebooted_ += cur_task.task->rebooted_; |                             failed_ += current_job->failed_; | ||||||
|                                         failed_ += cur_task.task->failed_; |                             job_it = JobList.erase(job_it); | ||||||
|                                         cur_task.task->started_ = cur_task.task->done_ = false; |                             delete current_job; | ||||||
|                                         delete cur_task.task; |                         } else { | ||||||
|                                         cur_task.task = nullptr; |                             ++job_it; | ||||||
|                                     } |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 Logger().debug("Waiting for outstanding update threads to finish."); |                 Logger().debug("Waiting for outstanding update threads to finish."); | ||||||
|                 bool stillTasksRunning=true; |                 Pool_.joinAll(); | ||||||
|                 while(stillTasksRunning) { |                 for(auto job_it = JobList.begin(); job_it !=JobList.end();) { | ||||||
|                     stillTasksRunning = false; |                     VenueDeviceRebooter * current_job = *job_it; | ||||||
|                     for(auto &cur_task:Tasks) { |                     if(current_job!= nullptr && current_job->done_) { | ||||||
|                         if(cur_task.task!= nullptr && cur_task.task->started_) { |                         if(current_job->rebooted_) | ||||||
|                             if(cur_task.thr_.isRunning()) { |                             N.content.success.push_back(current_job->SerialNumber); | ||||||
|                                 stillTasksRunning = true; |                         else | ||||||
|                                 continue; |                             N.content.warning.push_back(current_job->SerialNumber); | ||||||
|                             } |                         rebooted_ += current_job->rebooted_; | ||||||
|                             if(!cur_task.thr_.isRunning() && cur_task.task->done_) { |                         failed_ += current_job->failed_; | ||||||
|                                 cur_task.thr_.join(); |                         job_it = JobList.erase(job_it); | ||||||
|                                 if(cur_task.task->rebooted_) { |                         delete current_job; | ||||||
|                                     rebooted_++; |                     } else { | ||||||
|                                     N.content.success.push_back(cur_task.task->SerialNumber); |                         ++job_it; | ||||||
|                                 } else if(cur_task.task->failed_) { |  | ||||||
|                                     failed_++; |  | ||||||
|                                     N.content.warning.push_back(cur_task.task->SerialNumber); |  | ||||||
|                                 } |  | ||||||
|                                 cur_task.task->started_ = cur_task.task->done_ = false; |  | ||||||
|                                 delete cur_task.task; |  | ||||||
|                                 cur_task.task = nullptr; |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 N.content.details = fmt::format("Job {} Completed: {} rebooted, {} failed to reboot.", |                 N.content.details = fmt::format("Job {} Completed: {} rebooted, {} failed to reboot.", | ||||||
|                                                 JobId(), rebooted_ ,failed_); |                                                 JobId(), rebooted_ ,failed_); | ||||||
|  |  | ||||||
| @@ -145,6 +127,7 @@ namespace OpenWifi { | |||||||
|                 Logger().warning(N.content.details); |                 Logger().warning(N.content.details); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  |             // std::cout << N.content.details << std::endl; | ||||||
|             WebSocketClientNotificationVenueRebootCompletionToUser(UserInfo().email,N); |             WebSocketClientNotificationVenueRebootCompletionToUser(UserInfo().email,N); | ||||||
|             Logger().information(fmt::format("Job {} Completed: {} rebooted, {} failed to reboot.", |             Logger().information(fmt::format("Job {} Completed: {} rebooted, {} failed to reboot.", | ||||||
|                                              JobId(), rebooted_ ,failed_)); |                                              JobId(), rebooted_ ,failed_)); | ||||||
|   | |||||||
| @@ -31,7 +31,7 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|                 Storage::ApplyRules(rules_,Device.deviceRules); |                 Storage::ApplyRules(rules_,Device.deviceRules); | ||||||
|                 if(Device.deviceRules.firmwareUpgrade=="no") { |                 if(Device.deviceRules.firmwareUpgrade=="no") { | ||||||
|                     std::cout << "Skipped Upgrade:" << Device.serialNumber << std::endl; |                     poco_debug(Logger(),fmt::format("Skipped Upgrade: {}", Device.serialNumber)); | ||||||
|                     skipped_++; |                     skipped_++; | ||||||
|                     done_=true; |                     done_=true; | ||||||
|                     return; |                     return; | ||||||
| @@ -40,24 +40,25 @@ namespace OpenWifi { | |||||||
|                 FMSObjects::Firmware    F; |                 FMSObjects::Firmware    F; | ||||||
|                 if(SDK::FMS::Firmware::GetLatest(Device.deviceType,Device.deviceRules.rcOnly=="yes",F)) { |                 if(SDK::FMS::Firmware::GetLatest(Device.deviceType,Device.deviceRules.rcOnly=="yes",F)) { | ||||||
|                     if (SDK::GW::Device::Upgrade(nullptr, Device.serialNumber, 0, F.uri)) { |                     if (SDK::GW::Device::Upgrade(nullptr, Device.serialNumber, 0, F.uri)) { | ||||||
|                         std::cout << "Upgraded:" << Device.serialNumber << " to " << F.uri << std::endl; |  | ||||||
|                         Logger().debug(fmt::format("{}: Upgraded.",Device.serialNumber)); |                         Logger().debug(fmt::format("{}: Upgraded.",Device.serialNumber)); | ||||||
|                         upgraded_++; |                         upgraded_++; | ||||||
|                     } else { |                     } else { | ||||||
|                         std::cout << "Did not Upgrade:" << Device.serialNumber << " to " << F.uri << std::endl; |  | ||||||
|                         Logger().information(fmt::format("{}: Not Upgraded.", Device.serialNumber)); |                         Logger().information(fmt::format("{}: Not Upgraded.", Device.serialNumber)); | ||||||
|                         failed_++; |                         not_connected_++; | ||||||
|                     } |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     std::cout << "Did not Upgrade:" << Device.serialNumber << " to <unknown>" << std::endl; |                     Logger().information(fmt::format("{}: Not Upgraded. No firmware available.", Device.serialNumber)); | ||||||
|                     failed_++; |                     no_firmware_++; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             done_ = true; |             done_ = true; | ||||||
|             // std::cout << "Done push for " << Device.serialNumber << std::endl; |             // std::cout << "Done push for " << Device.serialNumber << std::endl; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         uint64_t        upgraded_=0, failed_=0, skipped_=0; |         std::uint64_t   upgraded_ = 0, | ||||||
|  |                         not_connected_ = 0, | ||||||
|  |                         skipped_ = 0, | ||||||
|  |                         no_firmware_ = 0; | ||||||
|         bool            started_ = false, |         bool            started_ = false, | ||||||
|                         done_ = false; |                         done_ = false; | ||||||
|         std::string     SerialNumber; |         std::string     SerialNumber; | ||||||
| @@ -82,98 +83,98 @@ namespace OpenWifi { | |||||||
|             Utils::SetThreadName("venue-upgr"); |             Utils::SetThreadName("venue-upgr"); | ||||||
|             auto VenueUUID_ = Parameter(0); |             auto VenueUUID_ = Parameter(0); | ||||||
|  |  | ||||||
|             WebSocketClientNotificationVenueRebootList_t        N; |             WebSocketClientNotificationVenueUpgradeList_t        N; | ||||||
|  |  | ||||||
|             ProvObjects::Venue  Venue; |             ProvObjects::Venue  Venue; | ||||||
|             uint64_t upgraded_ = 0, failed_ = 0; |             uint64_t    upgraded_ = 0, | ||||||
|  |                         not_connected_ = 0, | ||||||
|  |                         skipped_ = 0, | ||||||
|  |                         no_firmware_ = 0; | ||||||
|             if(StorageService()->VenueDB().GetRecord("id",VenueUUID_,Venue)) { |             if(StorageService()->VenueDB().GetRecord("id",VenueUUID_,Venue)) { | ||||||
|                 const std::size_t MaxThreads=16; |  | ||||||
|                 struct tState { |  | ||||||
|                     Poco::Thread                thr_; |  | ||||||
|                     VenueDeviceUpgrade    *task= nullptr; |  | ||||||
|                 }; |  | ||||||
|  |  | ||||||
|                 N.content.title = fmt::format("Upgrading {} devices.", Venue.info.name); |                 N.content.title = fmt::format("Upgrading {} devices.", Venue.info.name); | ||||||
|                 N.content.jobId = JobId(); |                 N.content.jobId = JobId(); | ||||||
|  |  | ||||||
|                 std::array<tState,MaxThreads> Tasks; |                 Poco::ThreadPool                Pool_; | ||||||
|  |                 std::list<VenueDeviceUpgrade*>  JobList; | ||||||
|                 ProvObjects::DeviceRules        Rules; |                 ProvObjects::DeviceRules        Rules; | ||||||
|  |  | ||||||
|                 StorageService()->VenueDB().EvaluateDeviceRules(Venue.info.id, Rules); |                 StorageService()->VenueDB().EvaluateDeviceRules(Venue.info.id, Rules); | ||||||
|  |  | ||||||
|                 for(const auto &uuid:Venue.devices) { |                 for(const auto &uuid:Venue.devices) { | ||||||
|                     auto NewTask = new VenueDeviceUpgrade(uuid, Venue.info.name, Rules, Logger()); |                     auto NewTask = new VenueDeviceUpgrade(uuid, Venue.info.name, Rules, Logger()); | ||||||
|                     // std::cout << "Scheduling config push for " << uuid << std::endl; |                     bool TaskAdded = false; | ||||||
|                     bool found_slot = false; |                     while (!TaskAdded) { | ||||||
|                     while (!found_slot) { |                         if (Pool_.available()) { | ||||||
|                         for (auto &cur_task: Tasks) { |                             JobList.push_back(NewTask); | ||||||
|                             if (cur_task.task == nullptr) { |                             Pool_.start(*NewTask); | ||||||
|                                 cur_task.task = NewTask; |                             TaskAdded = true; | ||||||
|                                 cur_task.thr_.start(*NewTask); |  | ||||||
|                                 found_slot = true; |  | ||||||
|                                 break; |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         //  Let's look for a slot... |  | ||||||
|                         if (!found_slot) { |  | ||||||
|                             for (auto &cur_task: Tasks) { |  | ||||||
|                                 if (cur_task.task != nullptr && cur_task.task->started_) { |  | ||||||
|                                     if (cur_task.thr_.isRunning()) |  | ||||||
|                                         continue; |  | ||||||
|                                     if (!cur_task.thr_.isRunning() && cur_task.task->done_) { |  | ||||||
|                                         cur_task.thr_.join(); |  | ||||||
|                                         upgraded_ += cur_task.task->upgraded_; |  | ||||||
|                                         failed_ += cur_task.task->failed_; |  | ||||||
|                                         cur_task.task->started_ = cur_task.task->done_ = false; |  | ||||||
|                                         delete cur_task.task; |  | ||||||
|                                         cur_task.task = nullptr; |  | ||||||
|                                     } |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 Logger().debug("Waiting for outstanding update threads to finish."); |  | ||||||
|                 bool stillTasksRunning=true; |  | ||||||
|                 while(stillTasksRunning) { |  | ||||||
|                     stillTasksRunning = false; |  | ||||||
|                     for(auto &cur_task:Tasks) { |  | ||||||
|                         if(cur_task.task!= nullptr && cur_task.task->started_) { |  | ||||||
|                             if(cur_task.thr_.isRunning()) { |  | ||||||
|                                 stillTasksRunning = true; |  | ||||||
|                             continue; |                             continue; | ||||||
|                         } |                         } | ||||||
|                             if(!cur_task.thr_.isRunning() && cur_task.task->done_) { |  | ||||||
|                                 cur_task.thr_.join(); |  | ||||||
|                                 if(cur_task.task->upgraded_) { |  | ||||||
|                                     upgraded_++; |  | ||||||
|                                     N.content.success.push_back(cur_task.task->SerialNumber); |  | ||||||
|                                 } else if(cur_task.task->failed_) { |  | ||||||
|                                     failed_++; |  | ||||||
|                                     N.content.warning.push_back(cur_task.task->SerialNumber); |  | ||||||
|                                 } |  | ||||||
|                                 cur_task.task->started_ = cur_task.task->done_ = false; |  | ||||||
|                                 delete cur_task.task; |  | ||||||
|                                 cur_task.task = nullptr; |  | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|  |                     for (auto job_it = JobList.begin(); job_it != JobList.end();) { | ||||||
|  |                         VenueDeviceUpgrade *current_job = *job_it; | ||||||
|  |                         if (current_job != nullptr && current_job->done_) { | ||||||
|  |                             if (current_job->upgraded_) | ||||||
|  |                                 N.content.success.push_back(current_job->SerialNumber); | ||||||
|  |                             else  if (current_job->skipped_) | ||||||
|  |                                 N.content.skipped.push_back(current_job->SerialNumber); | ||||||
|  |                             else  if (current_job->not_connected_) | ||||||
|  |                                 N.content.not_connected.push_back(current_job->SerialNumber); | ||||||
|  |                             else  if (current_job->no_firmware_) | ||||||
|  |                                 N.content.no_firmware.push_back(current_job->SerialNumber); | ||||||
|  |                             upgraded_ += current_job->upgraded_; | ||||||
|  |                             skipped_ += current_job->skipped_; | ||||||
|  |                             no_firmware_ += current_job->no_firmware_; | ||||||
|  |                             not_connected_ += current_job->not_connected_; | ||||||
|  |                             job_it = JobList.erase(job_it); | ||||||
|  |                             delete current_job; | ||||||
|  |                         } else { | ||||||
|  |                             ++job_it; | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 N.content.details = fmt::format("Job {} Completed: {} upgraded, {} failed to upgrade.", |                 Logger().debug("Waiting for outstanding upgrade threads to finish."); | ||||||
|                                                 JobId(), upgraded_ ,failed_); |                 Pool_.joinAll(); | ||||||
|  |                 for(auto job_it = JobList.begin(); job_it !=JobList.end();) { | ||||||
|  |                     VenueDeviceUpgrade * current_job = *job_it; | ||||||
|  |                     if(current_job!= nullptr && current_job->done_) { | ||||||
|  |                         if (current_job->upgraded_) | ||||||
|  |                             N.content.success.push_back(current_job->SerialNumber); | ||||||
|  |                         else  if (current_job->skipped_) | ||||||
|  |                             N.content.skipped.push_back(current_job->SerialNumber); | ||||||
|  |                         else  if (current_job->not_connected_) | ||||||
|  |                             N.content.not_connected.push_back(current_job->SerialNumber); | ||||||
|  |                         else  if (current_job->no_firmware_) | ||||||
|  |                             N.content.no_firmware.push_back(current_job->SerialNumber); | ||||||
|  |                         upgraded_ += current_job->upgraded_; | ||||||
|  |                         skipped_ += current_job->skipped_; | ||||||
|  |                         no_firmware_ += current_job->no_firmware_; | ||||||
|  |                         not_connected_ += current_job->not_connected_; | ||||||
|  |                         job_it = JobList.erase(job_it); | ||||||
|  |                         delete current_job; | ||||||
|  |                     } else { | ||||||
|  |                         ++job_it; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 N.content.details = fmt::format("Job {} Completed: {} upgraded, {} not connected, {} skipped, {} no firmware.", | ||||||
|  |                                                 JobId(), | ||||||
|  |                                                 upgraded_ , | ||||||
|  |                                                 not_connected_, | ||||||
|  |                                                 skipped_, | ||||||
|  |                                                 no_firmware_); | ||||||
|             } else { |             } else { | ||||||
|                 N.content.details = fmt::format("Venue {} no longer exists.",VenueUUID_); |                 N.content.details = fmt::format("Venue {} no longer exists.",VenueUUID_); | ||||||
|                 Logger().warning(N.content.details); |                 Logger().warning(N.content.details); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             WebSocketClientNotificationVenueRebootCompletionToUser(UserInfo().email,N); |             // std::cout << N.content.details << std::endl; | ||||||
|             Logger().information(fmt::format("Job {} Completed: {} upgraded, {} failed to upgrade.", |             WebSocketClientNotificationVenueUpgradeCompletionToUser(UserInfo().email,N); | ||||||
|                                              JobId(), upgraded_ ,failed_)); |             Logger().information(N.content.details); | ||||||
|             Utils::SetThreadName("free"); |             Utils::SetThreadName("free"); | ||||||
|  |  | ||||||
|             Complete(); |             Complete(); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|   | |||||||
| @@ -27,6 +27,11 @@ namespace OpenWifi { | |||||||
|     inline uint64_t Now() { return std::time(nullptr); }; |     inline uint64_t Now() { return std::time(nullptr); }; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | namespace OpenWifi::Utils { | ||||||
|  |     std::vector<unsigned char> base64decode(const std::string& input); | ||||||
|  |     std::string base64encode(const unsigned char *input, uint32_t size); | ||||||
|  | } | ||||||
|  |  | ||||||
| using namespace std::chrono_literals; | using namespace std::chrono_literals; | ||||||
|  |  | ||||||
| #include "Poco/Util/Application.h" | #include "Poco/Util/Application.h" | ||||||
| @@ -238,6 +243,11 @@ namespace OpenWifi::RESTAPI_utils { | |||||||
|         Obj.set(Field,Value); |         Obj.set(Field,Value); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Poco::Data::BLOB &Value) { | ||||||
|  |         auto Result = Utils::base64encode((const unsigned char *)Value.rawContent(),Value.size()); | ||||||
|  |         Obj.set(Field,Result); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::StringPairVec & S) { |     inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::StringPairVec & S) { | ||||||
|         Poco::JSON::Array   Array; |         Poco::JSON::Array   Array; | ||||||
|         for(const auto &i:S) { |         for(const auto &i:S) { | ||||||
| @@ -377,6 +387,13 @@ namespace OpenWifi::RESTAPI_utils { | |||||||
|             Value = (uint64_t)Obj->get(Field); |             Value = (uint64_t)Obj->get(Field); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Poco::Data::BLOB &Value) { | ||||||
|  |         if(Obj->has(Field) && !Obj->isNull(Field)) { | ||||||
|  |             auto Result = Utils::base64decode(Obj->get(Field).toString()); | ||||||
|  |             Value.assignRaw((const unsigned char *)&Result[0],Result.size()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::StringPairVec &Vec) { |     inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::StringPairVec &Vec) { | ||||||
|         if(Obj->isArray(Field) && !Obj->isNull(Field)) { |         if(Obj->isArray(Field) && !Obj->isNull(Field)) { | ||||||
|             auto O = Obj->getArray(Field); |             auto O = Obj->getArray(Field); | ||||||
| @@ -686,6 +703,19 @@ namespace OpenWifi::Utils { | |||||||
|         return (std::all_of(UUID.begin(),UUID.end(),[&](auto i){ if(i=='-') dashes++; return i=='-' || std::isxdigit(i);})) && (dashes>0); |         return (std::all_of(UUID.begin(),UUID.end(),[&](auto i){ if(i=='-') dashes++; return i=='-' || std::isxdigit(i);})) && (dashes>0); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | 	template <typename ...Args> std::string ComputeHash(Args&&... args) { | ||||||
|  | 		Poco::SHA2Engine    E; | ||||||
|  | 		auto as_string = [](auto p) { | ||||||
|  | 			if constexpr(std::is_arithmetic_v<decltype(p)>) { | ||||||
|  | 				return std::to_string(p); | ||||||
|  | 			} else { | ||||||
|  | 				return p; | ||||||
|  | 			} | ||||||
|  | 		}; | ||||||
|  | 		(E.update(as_string(args)),...); | ||||||
|  | 		return Poco::SHA2Engine::digestToHex(E.digest()); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|     [[nodiscard]] inline std::vector<std::string> Split(const std::string &List, char Delimiter=',' ) { |     [[nodiscard]] inline std::vector<std::string> Split(const std::string &List, char Delimiter=',' ) { | ||||||
|         std::vector<std::string> ReturnList; |         std::vector<std::string> ReturnList; | ||||||
|  |  | ||||||
| @@ -1382,13 +1412,14 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
| 	    [[nodiscard]] inline const std::string &Address() const { return address_; }; | 	    [[nodiscard]] inline const std::string &Address() const { return address_; }; | ||||||
| 	    [[nodiscard]] inline uint32_t Port() const { return port_; }; | 	    [[nodiscard]] inline uint32_t Port() const { return port_; }; | ||||||
| 	    [[nodiscard]] inline const std::string &KeyFile() const { return key_file_; }; | 	    [[nodiscard]] inline auto KeyFile() const { return key_file_; }; | ||||||
| 	    [[nodiscard]] inline const std::string &CertFile() const { return cert_file_; }; | 	    [[nodiscard]] inline auto CertFile() const { return cert_file_; }; | ||||||
| 	    [[nodiscard]] inline const std::string &RootCA() const { return root_ca_; }; | 	    [[nodiscard]] inline auto RootCA() const { return root_ca_; }; | ||||||
| 	    [[nodiscard]] inline const std::string &KeyFilePassword() const { return key_file_password_; }; | 	    [[nodiscard]] inline auto KeyFilePassword() const { return key_file_password_; }; | ||||||
| 	    [[nodiscard]] inline const std::string &IssuerCertFile() const { return issuer_cert_file_; }; | 	    [[nodiscard]] inline auto IssuerCertFile() const { return issuer_cert_file_; }; | ||||||
| 	    [[nodiscard]] inline const std::string &Name() const { return name_; }; | 	    [[nodiscard]] inline auto Name() const { return name_; }; | ||||||
| 	    [[nodiscard]] inline int Backlog() const { return backlog_; } | 	    [[nodiscard]] inline int Backlog() const { return backlog_; } | ||||||
|  | 		[[nodiscard]] inline auto Cas() const { return cas_; } | ||||||
|  |  | ||||||
| 	    [[nodiscard]] inline Poco::Net::SecureServerSocket CreateSecureSocket(Poco::Logger &L) const { | 	    [[nodiscard]] inline Poco::Net::SecureServerSocket CreateSecureSocket(Poco::Logger &L) const { | ||||||
| 	        Poco::Net::Context::Params P; | 	        Poco::Net::Context::Params P; | ||||||
| @@ -1868,8 +1899,8 @@ namespace OpenWifi { | |||||||
| 	            Request = &RequestIn; | 	            Request = &RequestIn; | ||||||
| 	            Response = &ResponseIn; | 	            Response = &ResponseIn; | ||||||
|  |  | ||||||
| 				std::string th_name = "restsvr_" + std::to_string(TransactionId_); | //				std::string th_name = "restsvr_" + std::to_string(TransactionId_); | ||||||
| 				Utils::SetThreadName(th_name.c_str()); | //				Utils::SetThreadName(th_name.c_str()); | ||||||
|  |  | ||||||
|                 if(Request->getContentLength()>0) { |                 if(Request->getContentLength()>0) { | ||||||
|                     if(Request->getContentType().find("application/json")!=std::string::npos) { |                     if(Request->getContentType().find("application/json")!=std::string::npos) { | ||||||
| @@ -2064,6 +2095,17 @@ namespace OpenWifi { | |||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, Poco::Data::BLOB &Value) { | ||||||
|  |             if(O->has(Field)) { | ||||||
|  |                 std::string Content = O->get(Field).toString(); | ||||||
|  |                 auto DecodedBlob = Utils::base64decode(Content); | ||||||
|  |                 Value.assignRaw((const unsigned char *)&DecodedBlob[0],DecodedBlob.size()); | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|         template <typename T> bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, const T &value, T & assignee) { |         template <typename T> bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, const T &value, T & assignee) { | ||||||
|             if(O->has(Field)) { |             if(O->has(Field)) { | ||||||
|                 assignee = value; |                 assignee = value; | ||||||
| @@ -2404,6 +2446,7 @@ namespace OpenWifi { | |||||||
|             Poco::Net::HTTPServerResponse       *Response= nullptr; |             Poco::Net::HTTPServerResponse       *Response= nullptr; | ||||||
|             SecurityObjects::UserInfoAndPolicy 	UserInfo_; |             SecurityObjects::UserInfoAndPolicy 	UserInfo_; | ||||||
|             QueryBlock					QB_; |             QueryBlock					QB_; | ||||||
|  | 			const std::string & Requester() const { return REST_Requester_; } | ||||||
| 	    protected: | 	    protected: | ||||||
| 	        BindingMap 					Bindings_; | 	        BindingMap 					Bindings_; | ||||||
| 	        Poco::URI::QueryParameters 	Parameters_; | 	        Poco::URI::QueryParameters 	Parameters_; | ||||||
| @@ -2420,6 +2463,7 @@ namespace OpenWifi { | |||||||
| 	        RateLimit                   MyRates_; | 	        RateLimit                   MyRates_; | ||||||
|             uint64_t                    TransactionId_; |             uint64_t                    TransactionId_; | ||||||
|             Poco::JSON::Object::Ptr     ParsedBody_; |             Poco::JSON::Object::Ptr     ParsedBody_; | ||||||
|  | 			std::string					REST_Requester_; | ||||||
| 	    }; | 	    }; | ||||||
|  |  | ||||||
| 	    class RESTAPI_UnknownRequestHandler : public RESTAPIHandler { | 	    class RESTAPI_UnknownRequestHandler : public RESTAPIHandler { | ||||||
| @@ -2684,7 +2728,7 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
| 		inline void run() override { | 		inline void run() override { | ||||||
| 			Poco::AutoPtr<Poco::Notification>	Note(Queue_.waitDequeueNotification()); | 			Poco::AutoPtr<Poco::Notification>	Note(Queue_.waitDequeueNotification()); | ||||||
| 			Utils::SetThreadName("kafka-dispatch"); | 			Utils::SetThreadName("kafka:dispatch"); | ||||||
| 			while(Note && Running_) { | 			while(Note && Running_) { | ||||||
| 				auto Msg = dynamic_cast<KafkaMessage*>(Note.get()); | 				auto Msg = dynamic_cast<KafkaMessage*>(Note.get()); | ||||||
| 				if(Msg!= nullptr) { | 				if(Msg!= nullptr) { | ||||||
| @@ -2994,7 +3038,7 @@ namespace OpenWifi { | |||||||
| 	    } | 	    } | ||||||
| 	    int Start() override; | 	    int Start() override; | ||||||
| 	    inline void Stop() override { | 	    inline void Stop() override { | ||||||
| 	        Logger().information("Stopping "); | 	        Logger().information("Stopping..."); | ||||||
| 	        for( const auto & svr : RESTServers_ ) | 	        for( const auto & svr : RESTServers_ ) | ||||||
| 	            svr->stop(); | 	            svr->stop(); | ||||||
| 			Pool_.stopAll(); | 			Pool_.stopAll(); | ||||||
| @@ -3002,22 +3046,23 @@ namespace OpenWifi { | |||||||
| 	        RESTServers_.clear(); | 	        RESTServers_.clear(); | ||||||
| 	    } | 	    } | ||||||
|  |  | ||||||
|  |  | ||||||
| 	    inline void reinitialize(Poco::Util::Application &self) override; | 	    inline void reinitialize(Poco::Util::Application &self) override; | ||||||
|  |  | ||||||
| 	    inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) { | 	    inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) { | ||||||
| 	        RESTAPIHandler::BindingMap Bindings; | 	        RESTAPIHandler::BindingMap Bindings; | ||||||
| 			Utils::SetThreadName(fmt::format("rest_ext_{}",Id).c_str()); | 			Utils::SetThreadName(fmt::format("x-rest:{}",Id).c_str()); | ||||||
| 	        return RESTAPI_ExtRouter(Path, Bindings, Logger(), Server_, Id); | 	        return RESTAPI_ExtRouter(Path, Bindings, Logger(), Server_, Id); | ||||||
| 	    } | 	    } | ||||||
|  |         const Poco::ThreadPool & Pool() { return Pool_; } | ||||||
|  |  | ||||||
| 	private: | 	private: | ||||||
| 	    std::vector<std::unique_ptr<Poco::Net::HTTPServer>>   RESTServers_; | 	    std::vector<std::unique_ptr<Poco::Net::HTTPServer>>   RESTServers_; | ||||||
| 	    Poco::ThreadPool	    Pool_; | 	    Poco::ThreadPool	    Pool_{"x-rest",2,32}; | ||||||
| 	    RESTAPI_GenericServer   Server_; | 	    RESTAPI_GenericServer   Server_; | ||||||
|  |  | ||||||
|         RESTAPI_ExtServer() noexcept: |         RESTAPI_ExtServer() noexcept: | ||||||
| 	    SubSystemServer("RESTAPI_ExtServer", "RESTAPIServer", "openwifi.restapi"), | 	    SubSystemServer("RESTAPI_ExtServer", "REST-XSRV", "openwifi.restapi") | ||||||
|         Pool_("RESTAPI_ExtServer",4,50,120) |  | ||||||
|             { |             { | ||||||
|             } |             } | ||||||
| 	}; | 	}; | ||||||
| @@ -3030,7 +3075,7 @@ namespace OpenWifi { | |||||||
| 	    inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override { | 	    inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override { | ||||||
| 			try { | 			try { | ||||||
| 				Poco::URI uri(Request.getURI()); | 				Poco::URI uri(Request.getURI()); | ||||||
| 				Utils::SetThreadName(fmt::format("rest_ext_{}",TransactionId_).c_str()); | 				Utils::SetThreadName(fmt::format("x-rest:{}",TransactionId_).c_str()); | ||||||
| 				return RESTAPI_ExtServer()->CallServer(uri.getPath(), TransactionId_++); | 				return RESTAPI_ExtServer()->CallServer(uri.getPath(), TransactionId_++); | ||||||
| 			} catch (...) { | 			} catch (...) { | ||||||
|  |  | ||||||
| @@ -3128,7 +3173,7 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
| 	    inline int Start() override; | 	    inline int Start() override; | ||||||
| 	    inline void Stop() override { | 	    inline void Stop() override { | ||||||
| 	        Logger().information("Stopping "); | 	        Logger().information("Stopping..."); | ||||||
| 	        for( const auto & svr : RESTServers_ ) | 	        for( const auto & svr : RESTServers_ ) | ||||||
| 	            svr->stop(); | 	            svr->stop(); | ||||||
| 			Pool_.stopAll(); | 			Pool_.stopAll(); | ||||||
| @@ -3139,17 +3184,18 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
| 	    inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) { | 	    inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) { | ||||||
| 	        RESTAPIHandler::BindingMap Bindings; | 	        RESTAPIHandler::BindingMap Bindings; | ||||||
| 			Utils::SetThreadName(fmt::format("rest_int_{}",Id).c_str()); | 			Utils::SetThreadName(fmt::format("i-rest:{}",Id).c_str()); | ||||||
| 	        return RESTAPI_IntRouter(Path, Bindings, Logger(), Server_, Id); | 	        return RESTAPI_IntRouter(Path, Bindings, Logger(), Server_, Id); | ||||||
| 	    } | 	    } | ||||||
|  |  | ||||||
|  |         const Poco::ThreadPool & Pool() { return Pool_; } | ||||||
| 	private: | 	private: | ||||||
| 	    std::vector<std::unique_ptr<Poco::Net::HTTPServer>>   RESTServers_; | 	    std::vector<std::unique_ptr<Poco::Net::HTTPServer>>   RESTServers_; | ||||||
| 	    Poco::ThreadPool	    Pool_; | 	    Poco::ThreadPool	    Pool_{"i-rest",2,16}; | ||||||
| 	    RESTAPI_GenericServer   Server_; | 	    RESTAPI_GenericServer   Server_; | ||||||
|  |  | ||||||
|         RESTAPI_IntServer() noexcept: |         RESTAPI_IntServer() noexcept: | ||||||
| 		   SubSystemServer("RESTAPI_IntServer", "REST-ISRV", "openwifi.internal.restapi"), | 		   SubSystemServer("RESTAPI_IntServer", "REST-ISRV", "openwifi.internal.restapi") | ||||||
|             Pool_("RESTAPI_IntServer",4,50,120) |  | ||||||
|         { |         { | ||||||
|         } |         } | ||||||
| 	}; | 	}; | ||||||
| @@ -3160,6 +3206,7 @@ namespace OpenWifi { | |||||||
| 	public: | 	public: | ||||||
|         inline IntRequestHandlerFactory() = default; |         inline IntRequestHandlerFactory() = default; | ||||||
| 	    inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override { | 	    inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override { | ||||||
|  | 			Utils::SetThreadName(fmt::format("i-rest:{}",TransactionId_).c_str()); | ||||||
| 	        Poco::URI uri(Request.getURI()); | 	        Poco::URI uri(Request.getURI()); | ||||||
| 	        return RESTAPI_IntServer()->CallServer(uri.getPath(), TransactionId_); | 	        return RESTAPI_IntServer()->CallServer(uri.getPath(), TransactionId_); | ||||||
| 	    } | 	    } | ||||||
| @@ -3203,7 +3250,6 @@ namespace OpenWifi { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		[[nodiscard]] std::string Version() { return Version_; } | 		[[nodiscard]] std::string Version() { return Version_; } | ||||||
| 		// [[nodiscard]] const Poco::SharedPtr<Poco::Crypto::RSAKey> & Key() { return AppKey_; } |  | ||||||
| 		[[nodiscard]] inline const std::string & DataDir() { return DataDir_; } | 		[[nodiscard]] inline const std::string & DataDir() { return DataDir_; } | ||||||
| 		[[nodiscard]] inline const std::string & WWWAssetsDir() { return WWWAssetsDir_; } | 		[[nodiscard]] inline const std::string & WWWAssetsDir() { return WWWAssetsDir_; } | ||||||
| 		[[nodiscard]] bool Debug() const { return DebugMode_; } | 		[[nodiscard]] bool Debug() const { return DebugMode_; } | ||||||
| @@ -3236,6 +3282,11 @@ namespace OpenWifi { | |||||||
|             return Poco::Logger::get(Name); |             return Poco::Logger::get(Name); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         virtual void GetExtraConfiguration(Poco::JSON::Object & Cfg) { | ||||||
|  |             Cfg.set("additionalConfiguration",false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|         static inline void Exit(int Reason); |         static inline void Exit(int Reason); | ||||||
| 		inline void BusMessageReceived(const std::string &Key, const std::string & Payload); | 		inline void BusMessageReceived(const std::string &Key, const std::string & Payload); | ||||||
| 		inline MicroServiceMetaVec GetServices(const std::string & Type); | 		inline MicroServiceMetaVec GetServices(const std::string & Type); | ||||||
| @@ -3272,7 +3323,6 @@ namespace OpenWifi { | |||||||
| 		inline std::string ConfigPath(const std::string &Key); | 		inline std::string ConfigPath(const std::string &Key); | ||||||
| 		inline std::string Encrypt(const std::string &S); | 		inline std::string Encrypt(const std::string &S); | ||||||
| 		inline std::string Decrypt(const std::string &S); | 		inline std::string Decrypt(const std::string &S); | ||||||
| 		inline std::string CreateHash(const std::string &S); |  | ||||||
| 		inline std::string MakeSystemEventMessage( const std::string & Type ) const; | 		inline std::string MakeSystemEventMessage( const std::string & Type ) const; | ||||||
| 		[[nodiscard]] inline bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); | 		[[nodiscard]] inline bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); | ||||||
| 		inline static void SavePID(); | 		inline static void SavePID(); | ||||||
| @@ -3302,6 +3352,9 @@ namespace OpenWifi { | |||||||
|                 return Signer_.sign(T,Algo); |                 return Signer_.sign(T,Algo); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | 		inline Poco::ThreadPool & TimerPool() { return TimerPool_; } | ||||||
|  |  | ||||||
| 	  private: | 	  private: | ||||||
| 	    static MicroService         * instance_; | 	    static MicroService         * instance_; | ||||||
| 		bool                        HelpRequested_ = false; | 		bool                        HelpRequested_ = false; | ||||||
| @@ -3315,7 +3368,6 @@ namespace OpenWifi { | |||||||
| 		std::string                 WWWAssetsDir_; | 		std::string                 WWWAssetsDir_; | ||||||
| 		Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory(); | 		Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory(); | ||||||
| 		Poco::Crypto::Cipher        * Cipher_ = nullptr; | 		Poco::Crypto::Cipher        * Cipher_ = nullptr; | ||||||
| 		Poco::SHA2Engine			SHA2_; |  | ||||||
| 		MicroServiceMetaMap			Services_; | 		MicroServiceMetaMap			Services_; | ||||||
| 		std::string 				MyHash_; | 		std::string 				MyHash_; | ||||||
| 		std::string 				MyPrivateEndPoint_; | 		std::string 				MyPrivateEndPoint_; | ||||||
| @@ -3336,6 +3388,7 @@ namespace OpenWifi { | |||||||
|         bool                        NoBuiltInCrypto_=false; |         bool                        NoBuiltInCrypto_=false; | ||||||
|         Poco::JWT::Signer	        Signer_; |         Poco::JWT::Signer	        Signer_; | ||||||
| 		Poco::Logger				&Logger_; | 		Poco::Logger				&Logger_; | ||||||
|  | 		Poco::ThreadPool			TimerPool_{"timer:pool",2,16}; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| 	inline void MicroService::Exit(int Reason) { | 	inline void MicroService::Exit(int Reason) { | ||||||
| @@ -3485,7 +3538,7 @@ namespace OpenWifi { | |||||||
| 	    MyPrivateEndPoint_ = ConfigGetString("openwifi.system.uri.private"); | 	    MyPrivateEndPoint_ = ConfigGetString("openwifi.system.uri.private"); | ||||||
| 	    MyPublicEndPoint_ = ConfigGetString("openwifi.system.uri.public"); | 	    MyPublicEndPoint_ = ConfigGetString("openwifi.system.uri.public"); | ||||||
| 	    UIURI_ = ConfigGetString("openwifi.system.uri.ui"); | 	    UIURI_ = ConfigGetString("openwifi.system.uri.ui"); | ||||||
| 	    MyHash_ = CreateHash(MyPublicEndPoint_); | 	    MyHash_ = Utils::ComputeHash(MyPublicEndPoint_); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	void MicroServicePostInitialization(); | 	void MicroServicePostInitialization(); | ||||||
| @@ -3548,8 +3601,6 @@ namespace OpenWifi { | |||||||
|     void DaemonPostInitialization(Poco::Util::Application &self); |     void DaemonPostInitialization(Poco::Util::Application &self); | ||||||
|  |  | ||||||
| 	inline void MicroService::initialize(Poco::Util::Application &self) { | 	inline void MicroService::initialize(Poco::Util::Application &self) { | ||||||
| 		// Utils::SetThreadName("microservice"); |  | ||||||
|  |  | ||||||
| 		// add the default services | 		// add the default services | ||||||
|         LoadConfigurationFile(); |         LoadConfigurationFile(); | ||||||
|         InitializeLoggingSystem(); |         InitializeLoggingSystem(); | ||||||
| @@ -3824,11 +3875,6 @@ namespace OpenWifi { | |||||||
| 	    return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; | 	    return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	inline std::string MicroService::CreateHash(const std::string &S) { |  | ||||||
| 	    SHA2_.update(S); |  | ||||||
| 	    return Utils::ToHex(SHA2_.digest()); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline std::string MicroService::MakeSystemEventMessage( const std::string & Type ) const { | 	inline std::string MicroService::MakeSystemEventMessage( const std::string & Type ) const { | ||||||
| 	    Poco::JSON::Object	Obj; | 	    Poco::JSON::Object	Obj; | ||||||
| 	    Obj.set(KafkaTopics::ServiceEvents::Fields::EVENT,Type); | 	    Obj.set(KafkaTopics::ServiceEvents::Fields::EVENT,Type); | ||||||
| @@ -3891,6 +3937,7 @@ namespace OpenWifi { | |||||||
|             Params->setMaxThreads(50); |             Params->setMaxThreads(50); | ||||||
|             Params->setMaxQueued(200); |             Params->setMaxQueued(200); | ||||||
|             Params->setKeepAlive(true); |             Params->setKeepAlive(true); | ||||||
|  | 			Params->setName("ws:xrest"); | ||||||
|  |  | ||||||
|             std::unique_ptr<Poco::Net::HTTPServer>  NewServer; |             std::unique_ptr<Poco::Net::HTTPServer>  NewServer; | ||||||
|             if(MicroService::instance().NoAPISecurity()) { |             if(MicroService::instance().NoAPISecurity()) { | ||||||
| @@ -3927,6 +3974,7 @@ namespace OpenWifi { | |||||||
|             Params->setMaxThreads(50); |             Params->setMaxThreads(50); | ||||||
|             Params->setMaxQueued(200); |             Params->setMaxQueued(200); | ||||||
|             Params->setKeepAlive(true); |             Params->setKeepAlive(true); | ||||||
|  | 			Params->setName("ws:irest"); | ||||||
|  |  | ||||||
|             std::unique_ptr<Poco::Net::HTTPServer>  NewServer; |             std::unique_ptr<Poco::Net::HTTPServer>  NewServer; | ||||||
|             if(MicroService::instance().NoAPISecurity()) { |             if(MicroService::instance().NoAPISecurity()) { | ||||||
| @@ -3944,8 +3992,6 @@ namespace OpenWifi { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     inline int MicroService::main([[maybe_unused]] const ArgVec &args) { |     inline int MicroService::main([[maybe_unused]] const ArgVec &args) { | ||||||
|  |  | ||||||
| 		// Utils::SetThreadName("main"); |  | ||||||
| 	    MyErrorHandler	ErrorHandler(*this); | 	    MyErrorHandler	ErrorHandler(*this); | ||||||
| 	    Poco::ErrorHandler::set(&ErrorHandler); | 	    Poco::ErrorHandler::set(&ErrorHandler); | ||||||
|  |  | ||||||
| @@ -4052,6 +4098,7 @@ namespace OpenWifi { | |||||||
| 	        Port_ = (int)MicroService::instance().ConfigGetInt("alb.port",15015); | 	        Port_ = (int)MicroService::instance().ConfigGetInt("alb.port",15015); | ||||||
| 	        Socket_ = std::make_unique<Poco::Net::ServerSocket>(Port_); | 	        Socket_ = std::make_unique<Poco::Net::ServerSocket>(Port_); | ||||||
| 	        auto Params = new Poco::Net::HTTPServerParams; | 	        auto Params = new Poco::Net::HTTPServerParams; | ||||||
|  | 			Params->setName("ws:alb"); | ||||||
| 	        Server_ = std::make_unique<Poco::Net::HTTPServer>(new ALBRequestHandlerFactory(Logger()), *Socket_, Params); | 	        Server_ = std::make_unique<Poco::Net::HTTPServer>(new ALBRequestHandlerFactory(Logger()), *Socket_, Params); | ||||||
| 	        Server_->start(); | 	        Server_->start(); | ||||||
| 	    } | 	    } | ||||||
| @@ -4061,7 +4108,7 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|     inline void BusEventManager::run() { |     inline void BusEventManager::run() { | ||||||
|         Running_ = true; |         Running_ = true; | ||||||
| 		Utils::SetThreadName("BusEventManager"); | 		Utils::SetThreadName("fmwk:EventMgr"); | ||||||
|         auto Msg = MicroService::instance().MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN); |         auto Msg = MicroService::instance().MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN); | ||||||
|         KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroService::instance().PrivateEndPoint(),Msg, false); |         KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroService::instance().PrivateEndPoint(),Msg, false); | ||||||
|         while(Running_) { |         while(Running_) { | ||||||
| @@ -4148,7 +4195,7 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
| 	inline void KafkaProducer::run() { | 	inline void KafkaProducer::run() { | ||||||
|  |  | ||||||
| 		Utils::SetThreadName("KafkaProducer"); | 		Utils::SetThreadName("Kafka:Prod"); | ||||||
| 	    cppkafka::Configuration Config({ | 	    cppkafka::Configuration Config({ | ||||||
|             { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, |             { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, | ||||||
|             { "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") } |             { "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") } | ||||||
| @@ -4187,7 +4234,7 @@ namespace OpenWifi { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	inline void KafkaConsumer::run() { | 	inline void KafkaConsumer::run() { | ||||||
| 		Utils::SetThreadName("KafkaConsumer"); | 		Utils::SetThreadName("Kafka:Cons"); | ||||||
|  |  | ||||||
| 	    cppkafka::Configuration Config({ | 	    cppkafka::Configuration Config({ | ||||||
| 	        { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, | 	        { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, | ||||||
| @@ -4327,6 +4374,11 @@ namespace OpenWifi { | |||||||
| 	            Answer.set("certificates", Certificates); | 	            Answer.set("certificates", Certificates); | ||||||
| 	            return ReturnObject(Answer); | 	            return ReturnObject(Answer); | ||||||
| 	        } | 	        } | ||||||
|  |             if(GetBoolParameter("extraConfiguration")) { | ||||||
|  |                 Poco::JSON::Object  Answer; | ||||||
|  |                 MicroService::instance().GetExtraConfiguration(Answer); | ||||||
|  |                 return ReturnObject(Answer); | ||||||
|  |             } | ||||||
| 	        BadRequest(RESTAPI::Errors::InvalidCommand); | 	        BadRequest(RESTAPI::Errors::InvalidCommand); | ||||||
| 	    } | 	    } | ||||||
|  |  | ||||||
| @@ -4699,6 +4751,7 @@ namespace OpenWifi { | |||||||
|     inline bool RESTAPIHandler::IsAuthorized( bool & Expired , [[maybe_unused]] bool & Contacted , bool Sub ) { |     inline bool RESTAPIHandler::IsAuthorized( bool & Expired , [[maybe_unused]] bool & Contacted , bool Sub ) { | ||||||
|         if(Internal_ && Request->has("X-INTERNAL-NAME")) { |         if(Internal_ && Request->has("X-INTERNAL-NAME")) { | ||||||
|             auto Allowed = MicroService::instance().IsValidAPIKEY(*Request); |             auto Allowed = MicroService::instance().IsValidAPIKEY(*Request); | ||||||
|  | 			Contacted = true; | ||||||
|             if(!Allowed) { |             if(!Allowed) { | ||||||
|                 if(Server_.LogBadTokens(false)) { |                 if(Server_.LogBadTokens(false)) { | ||||||
|                     Logger_.debug(fmt::format("I-REQ-DENIED({}): Method={} Path={}", |                     Logger_.debug(fmt::format("I-REQ-DENIED({}): Method={} Path={}", | ||||||
| @@ -4707,6 +4760,7 @@ namespace OpenWifi { | |||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 auto Id = Request->get("X-INTERNAL-NAME", "unknown"); |                 auto Id = Request->get("X-INTERNAL-NAME", "unknown"); | ||||||
|  | 				REST_Requester_ = Id; | ||||||
|                 if(Server_.LogIt(Request->getMethod(),true)) { |                 if(Server_.LogIt(Request->getMethod(),true)) { | ||||||
|                     Logger_.debug(fmt::format("I-REQ-ALLOWED({}): User='{}' Method={} Path={}", |                     Logger_.debug(fmt::format("I-REQ-ALLOWED({}): User='{}' Method={} Path={}", | ||||||
|                                                Utils::FormatIPv6(Request->clientAddress().toString()), Id, |                                                Utils::FormatIPv6(Request->clientAddress().toString()), Id, | ||||||
| @@ -4730,6 +4784,7 @@ namespace OpenWifi { | |||||||
| #else | #else | ||||||
|             if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, Expired, Contacted, Sub)) { |             if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, Expired, Contacted, Sub)) { | ||||||
| #endif | #endif | ||||||
|  | 				REST_Requester_ = UserInfo_.userinfo.email; | ||||||
|                 if(Server_.LogIt(Request->getMethod(),true)) { |                 if(Server_.LogIt(Request->getMethod(),true)) { | ||||||
|                     Logger_.debug(fmt::format("X-REQ-ALLOWED({}): User='{}@{}' Method={} Path={}", |                     Logger_.debug(fmt::format("X-REQ-ALLOWED({}): User='{}@{}' Method={} Path={}", | ||||||
|                                                UserInfo_.userinfo.email, |                                                UserInfo_.userinfo.email, | ||||||
| @@ -4812,7 +4867,7 @@ namespace OpenWifi { | |||||||
|         void run() override; |         void run() override; | ||||||
|         // MyParallelSocketReactor &ReactorPool(); |         // MyParallelSocketReactor &ReactorPool(); | ||||||
| 		Poco::Net::SocketReactor & Reactor() { return Reactor_; } | 		Poco::Net::SocketReactor & Reactor() { return Reactor_; } | ||||||
|         void NewClient(Poco::Net::WebSocket &WS, const std::string &Id); |         void NewClient(Poco::Net::WebSocket &WS, const std::string &Id, const std::string &UserName); | ||||||
|         bool Register(WebSocketClient *Client, const std::string &Id); |         bool Register(WebSocketClient *Client, const std::string &Id); | ||||||
|         void SetProcessor(WebSocketClientProcessor *F); |         void SetProcessor(WebSocketClientProcessor *F); | ||||||
|         void UnRegister(const std::string &Id); |         void UnRegister(const std::string &Id); | ||||||
| @@ -4863,7 +4918,10 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|     class WebSocketClient { |     class WebSocketClient { | ||||||
|     public: |     public: | ||||||
|         explicit WebSocketClient(Poco::Net::WebSocket &WS, const std::string &Id, Poco::Logger &L, |         explicit WebSocketClient(Poco::Net::WebSocket &WS, | ||||||
|  | 							   		const std::string &Id, | ||||||
|  | 							   		const std::string &UserName, | ||||||
|  | 							   		Poco::Logger &L, | ||||||
|                                  	WebSocketClientProcessor *Processor); |                                  	WebSocketClientProcessor *Processor); | ||||||
|         virtual ~WebSocketClient(); |         virtual ~WebSocketClient(); | ||||||
|         [[nodiscard]] inline const std::string &Id(); |         [[nodiscard]] inline const std::string &Id(); | ||||||
| @@ -4873,8 +4931,9 @@ namespace OpenWifi { | |||||||
|         std::unique_ptr<Poco::Net::WebSocket> WS_; |         std::unique_ptr<Poco::Net::WebSocket> WS_; | ||||||
|         Poco::Net::SocketReactor 	&Reactor_; |         Poco::Net::SocketReactor 	&Reactor_; | ||||||
|         std::string 				Id_; |         std::string 				Id_; | ||||||
|  | 		std::string					UserName_; | ||||||
|         Poco::Logger 				&Logger_; |         Poco::Logger 				&Logger_; | ||||||
|         bool Authenticated_ = false; |         std::atomic_bool 			Authenticated_ = false; | ||||||
|         SecurityObjects::UserInfoAndPolicy UserInfo_; |         SecurityObjects::UserInfoAndPolicy UserInfo_; | ||||||
|         WebSocketClientProcessor *Processor_ = nullptr; |         WebSocketClientProcessor *Processor_ = nullptr; | ||||||
|         void OnSocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf); |         void OnSocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf); | ||||||
| @@ -4882,33 +4941,9 @@ namespace OpenWifi { | |||||||
|         void OnSocketError(const Poco::AutoPtr<Poco::Net::ErrorNotification> &pNf); |         void OnSocketError(const Poco::AutoPtr<Poco::Net::ErrorNotification> &pNf); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
| /*    inline MyParallelSocketReactor::MyParallelSocketReactor(uint32_t NumReactors) : |     inline void WebSocketClientServer::NewClient(Poco::Net::WebSocket & WS, const std::string &Id, const std::string &UserName ) { | ||||||
|             NumReactors_(NumReactors) |  | ||||||
|     { |  | ||||||
|         Reactors_ = new Poco::Net::SocketReactor[NumReactors_]; |  | ||||||
|         for(uint32_t i=0;i<NumReactors_;i++) { |  | ||||||
|             ReactorPool_.start(Reactors_[i]); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     inline MyParallelSocketReactor::~MyParallelSocketReactor() { |  | ||||||
|         for(uint32_t i=0;i<NumReactors_;i++) { |  | ||||||
|             Reactors_[i].stop(); |  | ||||||
|         } |  | ||||||
|         ReactorPool_.stopAll(); |  | ||||||
|         ReactorPool_.joinAll(); |  | ||||||
|         delete [] Reactors_; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     inline Poco::Net::SocketReactor & MyParallelSocketReactor::Reactor() { |  | ||||||
|         return Reactors_[ rand() % NumReactors_ ]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // inline MyParallelSocketReactor & WebSocketClientServer::ReactorPool() { return *ReactorPool_; } |  | ||||||
| */ |  | ||||||
|     inline void WebSocketClientServer::NewClient(Poco::Net::WebSocket & WS, const std::string &Id) { |  | ||||||
|         std::lock_guard G(Mutex_); |         std::lock_guard G(Mutex_); | ||||||
|         auto Client = new WebSocketClient(WS,Id,Logger(), Processor_); |         auto Client = new WebSocketClient(WS,Id,UserName,Logger(), Processor_); | ||||||
|         Clients_[Id] = std::make_pair(Client,""); |         Clients_[Id] = std::make_pair(Client,""); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -4938,13 +4973,13 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|     [[nodiscard]] inline bool SendToUser(const std::string &userName, const std::string &Payload); |     [[nodiscard]] inline bool SendToUser(const std::string &userName, const std::string &Payload); | ||||||
|     inline WebSocketClientServer::WebSocketClientServer() noexcept: |     inline WebSocketClientServer::WebSocketClientServer() noexcept: | ||||||
|             SubSystemServer("WebSocketClientServer", "WSCLNT-SVR", "websocketclients") |             SubSystemServer("WebSocketClientServer", "UI-WSCLNT-SVR", "websocketclients") | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     inline void WebSocketClientServer::run() { |     inline void WebSocketClientServer::run() { | ||||||
|         Running_ = true ; |         Running_ = true ; | ||||||
| 		Utils::SetThreadName("ws:clnt-svr"); | 		Utils::SetThreadName("ws:uiclnt-svr"); | ||||||
|         while(Running_) { |         while(Running_) { | ||||||
|             Poco::Thread::trySleep(2000); |             Poco::Thread::trySleep(2000); | ||||||
|  |  | ||||||
| @@ -5025,6 +5060,7 @@ namespace OpenWifi { | |||||||
| 			auto Op = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK; | 			auto Op = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK; | ||||||
|  |  | ||||||
| 			if (n == 0) { | 			if (n == 0) { | ||||||
|  | 				Logger().warning(Poco::format("CLOSE(%s): %s UI Client is closing WS connection.", Id_, UserName_)); | ||||||
| 				return delete this; | 				return delete this; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| @@ -5037,7 +5073,7 @@ namespace OpenWifi { | |||||||
| 			case Poco::Net::WebSocket::FRAME_OP_PONG: { | 			case Poco::Net::WebSocket::FRAME_OP_PONG: { | ||||||
| 			} break; | 			} break; | ||||||
| 			case Poco::Net::WebSocket::FRAME_OP_CLOSE: { | 			case Poco::Net::WebSocket::FRAME_OP_CLOSE: { | ||||||
| 				Logger().warning(Poco::format("CLOSE(%s): Client is closing its connection.", Id_)); | 				Logger().warning(Poco::format("CLOSE(%s): %s UI Client is closing WS connection.", Id_, UserName_)); | ||||||
| 				Done = true; | 				Done = true; | ||||||
| 			} break; | 			} break; | ||||||
| 			case Poco::Net::WebSocket::FRAME_OP_TEXT: { | 			case Poco::Net::WebSocket::FRAME_OP_TEXT: { | ||||||
| @@ -5049,6 +5085,8 @@ namespace OpenWifi { | |||||||
| 					if (Tokens.size() == 2 && | 					if (Tokens.size() == 2 && | ||||||
| 						AuthClient()->IsAuthorized(Tokens[1], UserInfo_, Expired, Contacted)) { | 						AuthClient()->IsAuthorized(Tokens[1], UserInfo_, Expired, Contacted)) { | ||||||
| 						Authenticated_ = true; | 						Authenticated_ = true; | ||||||
|  | 						UserName_ = UserInfo_.userinfo.email; | ||||||
|  | 						Logger().warning(Poco::format("START(%s): %s UI Client is starting WS connection.", Id_, UserName_)); | ||||||
| 						std::string S{"Welcome! Bienvenue! Bienvenidos!"}; | 						std::string S{"Welcome! Bienvenue! Bienvenidos!"}; | ||||||
| 						WS_->sendFrame(S.c_str(), S.size()); | 						WS_->sendFrame(S.c_str(), S.size()); | ||||||
| 						WebSocketClientServer()->SetUser(Id_, UserInfo_.userinfo.email); | 						WebSocketClientServer()->SetUser(Id_, UserInfo_.userinfo.email); | ||||||
| @@ -5094,9 +5132,10 @@ namespace OpenWifi { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     inline WebSocketClient::WebSocketClient( Poco::Net::WebSocket & WS , const std::string &Id, Poco::Logger & L, WebSocketClientProcessor * Processor) : |     inline WebSocketClient::WebSocketClient( Poco::Net::WebSocket & WS , const std::string &Id, const std::string &UserName, Poco::Logger & L, WebSocketClientProcessor * Processor) : | ||||||
|             Reactor_(WebSocketClientServer()->Reactor()), |             Reactor_(WebSocketClientServer()->Reactor()), | ||||||
|             Id_(Id), |             Id_(Id), | ||||||
|  | 			UserName_(UserName), | ||||||
|             Logger_(L), |             Logger_(L), | ||||||
|             Processor_(Processor) { |             Processor_(Processor) { | ||||||
|         try { |         try { | ||||||
| @@ -5176,9 +5215,8 @@ namespace OpenWifi { | |||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     Poco::Net::WebSocket WS(*Request, *Response); |                     Poco::Net::WebSocket WS(*Request, *Response); | ||||||
|                     Logger().information("WebSocket connection established."); |  | ||||||
|                     auto Id = MicroService::CreateUUID(); |                     auto Id = MicroService::CreateUUID(); | ||||||
|                     WebSocketClientServer()->NewClient(WS,Id); |                     WebSocketClientServer()->NewClient(WS,Id,UserInfo_.userinfo.email); | ||||||
|                 } |                 } | ||||||
|                 catch (...) { |                 catch (...) { | ||||||
|                     std::cout << "Cannot create websocket client..." << std::endl; |                     std::cout << "Cannot create websocket client..." << std::endl; | ||||||
|   | |||||||
| @@ -146,6 +146,10 @@ namespace OpenWifi { | |||||||
|         WebSocketClientServer()->SendUserNotification(User,N); |         WebSocketClientServer()->SendUserNotification(User,N); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     ///// | ||||||
|  |     ///// | ||||||
|  |     ///// | ||||||
|  |  | ||||||
|     struct WebSocketNotificationRebootList { |     struct WebSocketNotificationRebootList { | ||||||
|         std::string                 title, |         std::string                 title, | ||||||
|                 details, |                 details, | ||||||
| @@ -189,5 +193,58 @@ namespace OpenWifi { | |||||||
|         WebSocketClientServer()->SendUserNotification(User,N); |         WebSocketClientServer()->SendUserNotification(User,N); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     ///// | ||||||
|  |     ///// | ||||||
|  |     ///// | ||||||
|  |  | ||||||
|  |     struct WebSocketNotificationUpgradeList { | ||||||
|  |         std::string                 title, | ||||||
|  |                 details, | ||||||
|  |                 jobId; | ||||||
|  |         std::vector<std::string>    success, | ||||||
|  |                                     skipped, | ||||||
|  |                                     no_firmware, | ||||||
|  |                                     not_connected; | ||||||
|  |         uint64_t                    timeStamp=OpenWifi::Now(); | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     typedef WebSocketNotification<WebSocketNotificationUpgradeList> WebSocketClientNotificationVenueUpgradeList_t; | ||||||
|  |  | ||||||
|  |     inline void WebSocketNotificationUpgradeList::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"title",title); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"jobId",jobId); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"success",success); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"notConnected",not_connected); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"noFirmware",no_firmware); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"skipped",skipped); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"timeStamp",timeStamp); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"details",details); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline bool WebSocketNotificationUpgradeList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"title",title); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"jobId",jobId); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"success",success); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"notConnected",not_connected); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"noFirmware",no_firmware); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"skipped",skipped); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"timeStamp",timeStamp); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"details",details); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline void WebSocketClientNotificationVenueUpgradeCompletionToUser( const std::string & User, WebSocketClientNotificationVenueUpgradeList_t &N) { | ||||||
|  |         N.type = "venue_upgrader"; | ||||||
|  |         WebSocketClientServer()->SendUserNotification(User,N); | ||||||
|  |     } | ||||||
|  |  | ||||||
| } // namespace OpenWifi | } // namespace OpenWifi | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										937
									
								
								src/libs/croncpp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										937
									
								
								src/libs/croncpp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,937 @@ | |||||||
|  | /* | ||||||
|  |     MIT License | ||||||
|  |  | ||||||
|  |     Copyright (c) 2018 Marius Bancila | ||||||
|  |  | ||||||
|  |     Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |     of this software and associated documentation files (the "Software"), to deal | ||||||
|  |     in the Software without restriction, including without limitation the rights | ||||||
|  |     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |     copies of the Software, and to permit persons to whom the Software is | ||||||
|  |     furnished to do so, subject to the following conditions: | ||||||
|  |  | ||||||
|  |     The above copyright notice and this permission notice shall be included in all | ||||||
|  |     copies or substantial portions of the Software. | ||||||
|  |  | ||||||
|  |     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  |     SOFTWARE. | ||||||
|  |  | ||||||
|  |     This file is from https://github.com/mariusbancila/croncpp.git. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <vector> | ||||||
|  | #include <string> | ||||||
|  | #include <sstream> | ||||||
|  | #include <bitset> | ||||||
|  | #include <cctype> | ||||||
|  | #include <ctime> | ||||||
|  | #include <iomanip> | ||||||
|  | #include <algorithm> | ||||||
|  | #include <chrono> | ||||||
|  |  | ||||||
|  | #if __cplusplus > 201402L | ||||||
|  | #include <string_view> | ||||||
|  | #define CRONCPP_IS_CPP17 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | namespace cron | ||||||
|  | { | ||||||
|  | #ifdef CRONCPP_IS_CPP17 | ||||||
|  |    #define  CRONCPP_STRING_VIEW       std::string_view | ||||||
|  |    #define  CRONCPP_STRING_VIEW_NPOS  std::string_view::npos | ||||||
|  |    #define  CRONCPP_CONSTEXPTR        constexpr | ||||||
|  | #else | ||||||
|  |    #define  CRONCPP_STRING_VIEW       std::string const & | ||||||
|  |    #define  CRONCPP_STRING_VIEW_NPOS  std::string::npos | ||||||
|  |    #define  CRONCPP_CONSTEXPTR | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |    using cron_int  = uint8_t; | ||||||
|  |  | ||||||
|  |    constexpr std::time_t INVALID_TIME = static_cast<std::time_t>(-1); | ||||||
|  |  | ||||||
|  |    constexpr size_t INVALID_INDEX = static_cast<size_t>(-1); | ||||||
|  |  | ||||||
|  |    class cronexpr; | ||||||
|  |  | ||||||
|  |    namespace detail | ||||||
|  |    { | ||||||
|  |       enum class cron_field | ||||||
|  |       { | ||||||
|  |          second, | ||||||
|  |          minute, | ||||||
|  |          hour_of_day, | ||||||
|  |          day_of_week, | ||||||
|  |          day_of_month, | ||||||
|  |          month, | ||||||
|  |          year | ||||||
|  |       }; | ||||||
|  |  | ||||||
|  |       template <typename Traits> | ||||||
|  |       static bool find_next(cronexpr const & cex, | ||||||
|  |                             std::tm& date, | ||||||
|  |                             size_t const dot); | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    struct bad_cronexpr : public std::runtime_error | ||||||
|  |    { | ||||||
|  |    public: | ||||||
|  |       explicit bad_cronexpr(CRONCPP_STRING_VIEW message) : | ||||||
|  |          std::runtime_error(message.data()) | ||||||
|  |       {} | ||||||
|  |    }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |    struct cron_standard_traits | ||||||
|  |    { | ||||||
|  |       static const cron_int CRON_MIN_SECONDS = 0; | ||||||
|  |       static const cron_int CRON_MAX_SECONDS = 59; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_MINUTES = 0; | ||||||
|  |       static const cron_int CRON_MAX_MINUTES = 59; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_HOURS = 0; | ||||||
|  |       static const cron_int CRON_MAX_HOURS = 23; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_DAYS_OF_WEEK = 0; | ||||||
|  |       static const cron_int CRON_MAX_DAYS_OF_WEEK = 6; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_DAYS_OF_MONTH = 1; | ||||||
|  |       static const cron_int CRON_MAX_DAYS_OF_MONTH = 31; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_MONTHS = 1; | ||||||
|  |       static const cron_int CRON_MAX_MONTHS = 12; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MAX_YEARS_DIFF = 4; | ||||||
|  |  | ||||||
|  | #ifdef CRONCPP_IS_CPP17 | ||||||
|  |       static const inline std::vector<std::string> DAYS = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; | ||||||
|  |       static const inline std::vector<std::string> MONTHS = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; | ||||||
|  | #else | ||||||
|  |       static std::vector<std::string>& DAYS() | ||||||
|  |       { | ||||||
|  |          static std::vector<std::string> days = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; | ||||||
|  |          return days; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       static std::vector<std::string>& MONTHS() | ||||||
|  |       { | ||||||
|  |          static std::vector<std::string> months = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; | ||||||
|  |          return months; | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |    }; | ||||||
|  |  | ||||||
|  |    struct cron_oracle_traits | ||||||
|  |    { | ||||||
|  |       static const cron_int CRON_MIN_SECONDS = 0; | ||||||
|  |       static const cron_int CRON_MAX_SECONDS = 59; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_MINUTES = 0; | ||||||
|  |       static const cron_int CRON_MAX_MINUTES = 59; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_HOURS = 0; | ||||||
|  |       static const cron_int CRON_MAX_HOURS = 23; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_DAYS_OF_WEEK = 1; | ||||||
|  |       static const cron_int CRON_MAX_DAYS_OF_WEEK = 7; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_DAYS_OF_MONTH = 1; | ||||||
|  |       static const cron_int CRON_MAX_DAYS_OF_MONTH = 31; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_MONTHS = 0; | ||||||
|  |       static const cron_int CRON_MAX_MONTHS = 11; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MAX_YEARS_DIFF = 4; | ||||||
|  |  | ||||||
|  | #ifdef CRONCPP_IS_CPP17 | ||||||
|  |       static const inline std::vector<std::string> DAYS = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; | ||||||
|  |       static const inline std::vector<std::string> MONTHS = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; | ||||||
|  | #else | ||||||
|  |  | ||||||
|  |       static std::vector<std::string>& DAYS() | ||||||
|  |       { | ||||||
|  |          static std::vector<std::string> days = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; | ||||||
|  |          return days; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       static std::vector<std::string>& MONTHS() | ||||||
|  |       { | ||||||
|  |          static std::vector<std::string> months = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; | ||||||
|  |          return months; | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |    }; | ||||||
|  |  | ||||||
|  |    struct cron_quartz_traits | ||||||
|  |    { | ||||||
|  |       static const cron_int CRON_MIN_SECONDS = 0; | ||||||
|  |       static const cron_int CRON_MAX_SECONDS = 59; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_MINUTES = 0; | ||||||
|  |       static const cron_int CRON_MAX_MINUTES = 59; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_HOURS = 0; | ||||||
|  |       static const cron_int CRON_MAX_HOURS = 23; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_DAYS_OF_WEEK = 1; | ||||||
|  |       static const cron_int CRON_MAX_DAYS_OF_WEEK = 7; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_DAYS_OF_MONTH = 1; | ||||||
|  |       static const cron_int CRON_MAX_DAYS_OF_MONTH = 31; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MIN_MONTHS = 1; | ||||||
|  |       static const cron_int CRON_MAX_MONTHS = 12; | ||||||
|  |  | ||||||
|  |       static const cron_int CRON_MAX_YEARS_DIFF = 4; | ||||||
|  |  | ||||||
|  | #ifdef CRONCPP_IS_CPP17 | ||||||
|  |       static const inline std::vector<std::string> DAYS = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; | ||||||
|  |       static const inline std::vector<std::string> MONTHS = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; | ||||||
|  | #else | ||||||
|  |       static std::vector<std::string>& DAYS() | ||||||
|  |       { | ||||||
|  |          static std::vector<std::string> days = { "NIL", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; | ||||||
|  |          return days; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       static std::vector<std::string>& MONTHS() | ||||||
|  |       { | ||||||
|  |          static std::vector<std::string> months = { "NIL", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; | ||||||
|  |          return months; | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |    }; | ||||||
|  |  | ||||||
|  |    class cronexpr; | ||||||
|  |  | ||||||
|  |    template <typename Traits = cron_standard_traits> | ||||||
|  |    static cronexpr make_cron(CRONCPP_STRING_VIEW expr); | ||||||
|  |  | ||||||
|  |    class cronexpr | ||||||
|  |    { | ||||||
|  |       std::bitset<60> seconds; | ||||||
|  |       std::bitset<60> minutes; | ||||||
|  |       std::bitset<24> hours; | ||||||
|  |       std::bitset<7>  days_of_week; | ||||||
|  |       std::bitset<31> days_of_month; | ||||||
|  |       std::bitset<12> months; | ||||||
|  |       std::string     expr; | ||||||
|  |  | ||||||
|  |       friend bool operator==(cronexpr const & e1, cronexpr const & e2); | ||||||
|  |       friend bool operator!=(cronexpr const & e1, cronexpr const & e2); | ||||||
|  |  | ||||||
|  |       template <typename Traits> | ||||||
|  |       friend bool detail::find_next(cronexpr const & cex, | ||||||
|  |                                     std::tm& date, | ||||||
|  |                                     size_t const dot); | ||||||
|  |  | ||||||
|  |       friend std::string to_cronstr(cronexpr const& cex); | ||||||
|  |       friend std::string to_string(cronexpr const & cex); | ||||||
|  |  | ||||||
|  |       template <typename Traits> | ||||||
|  |       friend cronexpr make_cron(CRONCPP_STRING_VIEW expr); | ||||||
|  |    }; | ||||||
|  |  | ||||||
|  |    inline bool operator==(cronexpr const & e1, cronexpr const & e2) | ||||||
|  |    { | ||||||
|  |       return | ||||||
|  |          e1.seconds == e2.seconds && | ||||||
|  |          e1.minutes == e2.minutes && | ||||||
|  |          e1.hours == e2.hours && | ||||||
|  |          e1.days_of_week == e2.days_of_week && | ||||||
|  |          e1.days_of_month == e2.days_of_month && | ||||||
|  |          e1.months == e2.months; | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    inline bool operator!=(cronexpr const & e1, cronexpr const & e2) | ||||||
|  |    { | ||||||
|  |       return !(e1 == e2); | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    inline std::string to_string(cronexpr const & cex) | ||||||
|  |    { | ||||||
|  |       return | ||||||
|  |          cex.seconds.to_string() + " " + | ||||||
|  |          cex.minutes.to_string() + " " + | ||||||
|  |          cex.hours.to_string() + " " + | ||||||
|  |          cex.days_of_month.to_string() + " " + | ||||||
|  |          cex.months.to_string() + " " + | ||||||
|  |          cex.days_of_week.to_string(); | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    inline std::string to_cronstr(cronexpr const& cex) | ||||||
|  |    { | ||||||
|  |       return cex.expr; | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    namespace utils | ||||||
|  |    { | ||||||
|  |       inline std::time_t tm_to_time(std::tm& date) | ||||||
|  |       { | ||||||
|  |          return std::mktime(&date); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       inline std::tm* time_to_tm(std::time_t const * date, std::tm* const out) | ||||||
|  |       { | ||||||
|  | #ifdef _WIN32 | ||||||
|  |          errno_t err = localtime_s(out, date); | ||||||
|  |          return 0 == err ? out : nullptr; | ||||||
|  | #else | ||||||
|  |          return localtime_r(date, out); | ||||||
|  | #endif | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       inline std::tm to_tm(CRONCPP_STRING_VIEW time) | ||||||
|  |       { | ||||||
|  |          std::tm result; | ||||||
|  | #if __cplusplus > 201103L | ||||||
|  |          std::istringstream str(time.data()); | ||||||
|  |          str.imbue(std::locale(setlocale(LC_ALL, nullptr))); | ||||||
|  |  | ||||||
|  |          str >> std::get_time(&result, "%Y-%m-%d %H:%M:%S"); | ||||||
|  |          if (str.fail()) throw std::runtime_error("Parsing date failed!"); | ||||||
|  | #else | ||||||
|  |          int year = 1900; | ||||||
|  |          int month = 1; | ||||||
|  |          int day = 1; | ||||||
|  |          int hour = 0; | ||||||
|  |          int minute = 0; | ||||||
|  |          int second = 0; | ||||||
|  |          sscanf(time.data(), "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second); | ||||||
|  |          result.tm_year = year - 1900; | ||||||
|  |          result.tm_mon = month - 1; | ||||||
|  |          result.tm_mday = day; | ||||||
|  |          result.tm_hour = hour; | ||||||
|  |          result.tm_min = minute; | ||||||
|  |          result.tm_sec = second; | ||||||
|  | #endif | ||||||
|  |          result.tm_isdst = -1; // DST info not available | ||||||
|  |  | ||||||
|  |          return result; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       inline std::string to_string(std::tm const & tm) | ||||||
|  |       { | ||||||
|  | #if __cplusplus > 201103L | ||||||
|  |          std::ostringstream str; | ||||||
|  |          str.imbue(std::locale(setlocale(LC_ALL, nullptr))); | ||||||
|  |          str << std::put_time(&tm, "%Y-%m-%d %H:%M:%S"); | ||||||
|  |          if (str.fail()) throw std::runtime_error("Writing date failed!"); | ||||||
|  |  | ||||||
|  |          return str.str(); | ||||||
|  | #else | ||||||
|  |          char buff[70] = {0}; | ||||||
|  |          strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", &tm); | ||||||
|  |          return std::string(buff); | ||||||
|  | #endif | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       inline std::string to_upper(std::string text) | ||||||
|  |       { | ||||||
|  |          std::transform(std::begin(text), std::end(text), | ||||||
|  |             std::begin(text), [](char const c) { return static_cast<char>(std::toupper(c)); }); | ||||||
|  |  | ||||||
|  |          return text; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       static std::vector<std::string> split(CRONCPP_STRING_VIEW text, char const delimiter) | ||||||
|  |       { | ||||||
|  |          std::vector<std::string> tokens; | ||||||
|  |          std::string token; | ||||||
|  |          std::istringstream tokenStream(text.data()); | ||||||
|  |          while (std::getline(tokenStream, token, delimiter)) | ||||||
|  |          { | ||||||
|  |             tokens.push_back(token); | ||||||
|  |          } | ||||||
|  |          return tokens; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       CRONCPP_CONSTEXPTR inline bool contains(CRONCPP_STRING_VIEW text, char const ch) noexcept | ||||||
|  |       { | ||||||
|  |          return CRONCPP_STRING_VIEW_NPOS != text.find_first_of(ch); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    namespace detail | ||||||
|  |    { | ||||||
|  |  | ||||||
|  |       inline cron_int to_cron_int(CRONCPP_STRING_VIEW text) | ||||||
|  |       { | ||||||
|  |          try | ||||||
|  |          { | ||||||
|  |             return static_cast<cron_int>(std::stoul(text.data())); | ||||||
|  |          } | ||||||
|  |          catch (std::exception const & ex) | ||||||
|  |          { | ||||||
|  |             throw bad_cronexpr(ex.what()); | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       static std::string replace_ordinals( | ||||||
|  |          std::string text, | ||||||
|  |          std::vector<std::string> const & replacement) | ||||||
|  |       { | ||||||
|  |          for (size_t i = 0; i < replacement.size(); ++i) | ||||||
|  |          { | ||||||
|  |             auto pos = text.find(replacement[i]); | ||||||
|  |             if (std::string::npos != pos) | ||||||
|  |                text.replace(pos, 3 ,std::to_string(i)); | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          return text; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       static std::pair<cron_int, cron_int> make_range( | ||||||
|  |          CRONCPP_STRING_VIEW field, | ||||||
|  |          cron_int const minval, | ||||||
|  |          cron_int const maxval) | ||||||
|  |       { | ||||||
|  |          cron_int first = 0; | ||||||
|  |          cron_int last = 0; | ||||||
|  |          if (field.size() == 1 && field[0] == '*') | ||||||
|  |          { | ||||||
|  |             first = minval; | ||||||
|  |             last = maxval; | ||||||
|  |          } | ||||||
|  |          else if (!utils::contains(field, '-')) | ||||||
|  |          { | ||||||
|  |             first = to_cron_int(field); | ||||||
|  |             last = first; | ||||||
|  |          } | ||||||
|  |          else | ||||||
|  |          { | ||||||
|  |             auto parts = utils::split(field, '-'); | ||||||
|  |             if (parts.size() != 2) | ||||||
|  |                throw bad_cronexpr("Specified range requires two fields"); | ||||||
|  |  | ||||||
|  |             first = to_cron_int(parts[0]); | ||||||
|  |             last = to_cron_int(parts[1]); | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          if (first > maxval || last > maxval) | ||||||
|  |          { | ||||||
|  |             throw bad_cronexpr("Specified range exceeds maximum"); | ||||||
|  |          } | ||||||
|  |          if (first < minval || last < minval) | ||||||
|  |          { | ||||||
|  |             throw bad_cronexpr("Specified range is less than minimum"); | ||||||
|  |          } | ||||||
|  |          if (first > last) | ||||||
|  |          { | ||||||
|  |             throw bad_cronexpr("Specified range start exceeds range end"); | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          return { first, last }; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       template <size_t N> | ||||||
|  |       static void set_cron_field( | ||||||
|  |          CRONCPP_STRING_VIEW value, | ||||||
|  |          std::bitset<N>& target, | ||||||
|  |          cron_int const minval, | ||||||
|  |          cron_int const maxval) | ||||||
|  |       { | ||||||
|  |          if(value.length() > 0 && value[value.length()-1] == ',') | ||||||
|  |             throw bad_cronexpr("Value cannot end with comma"); | ||||||
|  |  | ||||||
|  |          auto fields = utils::split(value, ','); | ||||||
|  |          if (fields.empty()) | ||||||
|  |             throw bad_cronexpr("Expression parsing error"); | ||||||
|  |  | ||||||
|  |          for (auto const & field : fields) | ||||||
|  |          { | ||||||
|  |             if (!utils::contains(field, '/')) | ||||||
|  |             { | ||||||
|  | #ifdef CRONCPP_IS_CPP17 | ||||||
|  |                auto[first, last] = detail::make_range(field, minval, maxval); | ||||||
|  | #else | ||||||
|  |                auto range = detail::make_range(field, minval, maxval); | ||||||
|  |                auto first = range.first; | ||||||
|  |                auto last = range.second; | ||||||
|  | #endif | ||||||
|  |                for (cron_int i = first - minval; i <= last - minval; ++i) | ||||||
|  |                { | ||||||
|  |                   target.set(i); | ||||||
|  |                } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                auto parts = utils::split(field, '/'); | ||||||
|  |                if (parts.size() != 2) | ||||||
|  |                   throw bad_cronexpr("Incrementer must have two fields"); | ||||||
|  |  | ||||||
|  | #ifdef CRONCPP_IS_CPP17 | ||||||
|  |                auto[first, last] = detail::make_range(parts[0], minval, maxval); | ||||||
|  | #else | ||||||
|  |                auto range = detail::make_range(parts[0], minval, maxval); | ||||||
|  |                auto first = range.first; | ||||||
|  |                auto last = range.second; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |                if (!utils::contains(parts[0], '-')) | ||||||
|  |                { | ||||||
|  |                   last = maxval; | ||||||
|  |                } | ||||||
|  |  | ||||||
|  |                auto delta = detail::to_cron_int(parts[1]); | ||||||
|  |                if(delta <= 0) | ||||||
|  |                   throw bad_cronexpr("Incrementer must be a positive value"); | ||||||
|  |  | ||||||
|  |                for (cron_int i = first - minval; i <= last - minval; i += delta) | ||||||
|  |                { | ||||||
|  |                   target.set(i); | ||||||
|  |                } | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       template <typename Traits> | ||||||
|  |       static void set_cron_days_of_week( | ||||||
|  |          std::string value, | ||||||
|  |          std::bitset<7>& target) | ||||||
|  |       { | ||||||
|  |          auto days = utils::to_upper(value); | ||||||
|  |          auto days_replaced = detail::replace_ordinals( | ||||||
|  |             days, | ||||||
|  | #ifdef CRONCPP_IS_CPP17 | ||||||
|  |             Traits::DAYS | ||||||
|  | #else | ||||||
|  |             Traits::DAYS() | ||||||
|  | #endif | ||||||
|  |          ); | ||||||
|  |  | ||||||
|  |          if (days_replaced.size() == 1 && days_replaced[0] == '?') | ||||||
|  |             days_replaced[0] = '*'; | ||||||
|  |  | ||||||
|  |          set_cron_field( | ||||||
|  |             days_replaced, | ||||||
|  |             target, | ||||||
|  |             Traits::CRON_MIN_DAYS_OF_WEEK, | ||||||
|  |             Traits::CRON_MAX_DAYS_OF_WEEK); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       template <typename Traits> | ||||||
|  |       static void set_cron_days_of_month( | ||||||
|  |          std::string value, | ||||||
|  |          std::bitset<31>& target) | ||||||
|  |       { | ||||||
|  |          if (value.size() == 1 && value[0] == '?') | ||||||
|  |             value[0] = '*'; | ||||||
|  |  | ||||||
|  |          set_cron_field( | ||||||
|  |             value, | ||||||
|  |             target, | ||||||
|  |             Traits::CRON_MIN_DAYS_OF_MONTH, | ||||||
|  |             Traits::CRON_MAX_DAYS_OF_MONTH); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       template <typename Traits> | ||||||
|  |       static void set_cron_month( | ||||||
|  |          std::string value, | ||||||
|  |          std::bitset<12>& target) | ||||||
|  |       { | ||||||
|  |          auto month = utils::to_upper(value); | ||||||
|  |          auto month_replaced = replace_ordinals( | ||||||
|  |             month, | ||||||
|  | #ifdef CRONCPP_IS_CPP17 | ||||||
|  |             Traits::MONTHS | ||||||
|  | #else | ||||||
|  |             Traits::MONTHS() | ||||||
|  | #endif | ||||||
|  |          ); | ||||||
|  |  | ||||||
|  |          set_cron_field( | ||||||
|  |             month_replaced, | ||||||
|  |             target, | ||||||
|  |             Traits::CRON_MIN_MONTHS, | ||||||
|  |             Traits::CRON_MAX_MONTHS); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       template <size_t N> | ||||||
|  |       inline size_t next_set_bit( | ||||||
|  |          std::bitset<N> const & target, | ||||||
|  |          size_t /*minimum*/, | ||||||
|  |          size_t /*maximum*/, | ||||||
|  |          size_t offset) | ||||||
|  |       { | ||||||
|  |          for (auto i = offset; i < N; ++i) | ||||||
|  |          { | ||||||
|  |             if (target.test(i)) return i; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          return INVALID_INDEX; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       inline void add_to_field( | ||||||
|  |          std::tm& date, | ||||||
|  |          cron_field const field, | ||||||
|  |          int const val) | ||||||
|  |       { | ||||||
|  |          switch (field) | ||||||
|  |          { | ||||||
|  |          case cron_field::second: | ||||||
|  |             date.tm_sec += val; | ||||||
|  |             break; | ||||||
|  |          case cron_field::minute: | ||||||
|  |             date.tm_min += val; | ||||||
|  |             break; | ||||||
|  |          case cron_field::hour_of_day: | ||||||
|  |             date.tm_hour += val; | ||||||
|  |             break; | ||||||
|  |          case cron_field::day_of_week: | ||||||
|  |          case cron_field::day_of_month: | ||||||
|  |             date.tm_mday += val; | ||||||
|  |             break; | ||||||
|  |          case cron_field::month: | ||||||
|  |             date.tm_mon += val; | ||||||
|  |             break; | ||||||
|  |          case cron_field::year: | ||||||
|  |             date.tm_year += val; | ||||||
|  |             break; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          if (INVALID_TIME == utils::tm_to_time(date)) | ||||||
|  |             throw bad_cronexpr("Invalid time expression"); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       inline void set_field( | ||||||
|  |          std::tm& date, | ||||||
|  |          cron_field const field, | ||||||
|  |          int const val) | ||||||
|  |       { | ||||||
|  |          switch (field) | ||||||
|  |          { | ||||||
|  |          case cron_field::second: | ||||||
|  |             date.tm_sec = val; | ||||||
|  |             break; | ||||||
|  |          case cron_field::minute: | ||||||
|  |             date.tm_min = val; | ||||||
|  |             break; | ||||||
|  |          case cron_field::hour_of_day: | ||||||
|  |             date.tm_hour = val; | ||||||
|  |             break; | ||||||
|  |          case cron_field::day_of_week: | ||||||
|  |             date.tm_wday = val; | ||||||
|  |             break; | ||||||
|  |          case cron_field::day_of_month: | ||||||
|  |             date.tm_mday = val; | ||||||
|  |             break; | ||||||
|  |          case cron_field::month: | ||||||
|  |             date.tm_mon = val; | ||||||
|  |             break; | ||||||
|  |          case cron_field::year: | ||||||
|  |             date.tm_year = val; | ||||||
|  |             break; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          if (INVALID_TIME == utils::tm_to_time(date)) | ||||||
|  |             throw bad_cronexpr("Invalid time expression"); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       inline void reset_field( | ||||||
|  |          std::tm& date, | ||||||
|  |          cron_field const field) | ||||||
|  |       { | ||||||
|  |          switch (field) | ||||||
|  |          { | ||||||
|  |          case cron_field::second: | ||||||
|  |             date.tm_sec = 0; | ||||||
|  |             break; | ||||||
|  |          case cron_field::minute: | ||||||
|  |             date.tm_min = 0; | ||||||
|  |             break; | ||||||
|  |          case cron_field::hour_of_day: | ||||||
|  |             date.tm_hour = 0; | ||||||
|  |             break; | ||||||
|  |          case cron_field::day_of_week: | ||||||
|  |             date.tm_wday = 0; | ||||||
|  |             break; | ||||||
|  |          case cron_field::day_of_month: | ||||||
|  |             date.tm_mday = 1; | ||||||
|  |             break; | ||||||
|  |          case cron_field::month: | ||||||
|  |             date.tm_mon = 0; | ||||||
|  |             break; | ||||||
|  |          case cron_field::year: | ||||||
|  |             date.tm_year = 0; | ||||||
|  |             break; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          if (INVALID_TIME == utils::tm_to_time(date)) | ||||||
|  |             throw bad_cronexpr("Invalid time expression"); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       inline void reset_all_fields( | ||||||
|  |          std::tm& date, | ||||||
|  |          std::bitset<7> const & marked_fields) | ||||||
|  |       { | ||||||
|  |          for (size_t i = 0; i < marked_fields.size(); ++i) | ||||||
|  |          { | ||||||
|  |             if (marked_fields.test(i)) | ||||||
|  |                reset_field(date, static_cast<cron_field>(i)); | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       inline void mark_field( | ||||||
|  |          std::bitset<7> & orders, | ||||||
|  |          cron_field const field) | ||||||
|  |       { | ||||||
|  |          if (!orders.test(static_cast<size_t>(field))) | ||||||
|  |             orders.set(static_cast<size_t>(field)); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       template <size_t N> | ||||||
|  |       static size_t find_next( | ||||||
|  |          std::bitset<N> const & target, | ||||||
|  |          std::tm& date, | ||||||
|  |          unsigned int const minimum, | ||||||
|  |          unsigned int const maximum, | ||||||
|  |          unsigned int const value, | ||||||
|  |          cron_field const field, | ||||||
|  |          cron_field const next_field, | ||||||
|  |          std::bitset<7> const & marked_fields) | ||||||
|  |       { | ||||||
|  |          auto next_value = next_set_bit(target, minimum, maximum, value); | ||||||
|  |          if (INVALID_INDEX == next_value) | ||||||
|  |          { | ||||||
|  |             add_to_field(date, next_field, 1); | ||||||
|  |             reset_field(date, field); | ||||||
|  |             next_value = next_set_bit(target, minimum, maximum, 0); | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          if (INVALID_INDEX == next_value || next_value != value) | ||||||
|  |          { | ||||||
|  |             set_field(date, field, static_cast<int>(next_value)); | ||||||
|  |             reset_all_fields(date, marked_fields); | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          return next_value; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       template <typename Traits> | ||||||
|  |       static size_t find_next_day( | ||||||
|  |          std::tm& date, | ||||||
|  |          std::bitset<31> const & days_of_month, | ||||||
|  |          size_t day_of_month, | ||||||
|  |          std::bitset<7> const & days_of_week, | ||||||
|  |          size_t day_of_week, | ||||||
|  |          std::bitset<7> const & marked_fields) | ||||||
|  |       { | ||||||
|  |          unsigned int count = 0; | ||||||
|  |          unsigned int maximum = 366; | ||||||
|  |          while ( | ||||||
|  |             (!days_of_month.test(day_of_month - Traits::CRON_MIN_DAYS_OF_MONTH) || | ||||||
|  |             !days_of_week.test(day_of_week - Traits::CRON_MIN_DAYS_OF_WEEK)) | ||||||
|  |             && count++ < maximum) | ||||||
|  |          { | ||||||
|  |             add_to_field(date, cron_field::day_of_month, 1); | ||||||
|  |  | ||||||
|  |             day_of_month = date.tm_mday; | ||||||
|  |             day_of_week = date.tm_wday; | ||||||
|  |  | ||||||
|  |             reset_all_fields(date, marked_fields); | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          return day_of_month; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       template <typename Traits> | ||||||
|  |       static bool find_next(cronexpr const & cex, | ||||||
|  |                             std::tm& date, | ||||||
|  |                             size_t const dot) | ||||||
|  |       { | ||||||
|  |          bool res = true; | ||||||
|  |  | ||||||
|  |          std::bitset<7> marked_fields{ 0 }; | ||||||
|  |          std::bitset<7> empty_list{ 0 }; | ||||||
|  |  | ||||||
|  |          unsigned int second = date.tm_sec; | ||||||
|  |          auto updated_second = find_next( | ||||||
|  |             cex.seconds, | ||||||
|  |             date, | ||||||
|  |             Traits::CRON_MIN_SECONDS, | ||||||
|  |             Traits::CRON_MAX_SECONDS, | ||||||
|  |             second, | ||||||
|  |             cron_field::second, | ||||||
|  |             cron_field::minute, | ||||||
|  |             empty_list); | ||||||
|  |  | ||||||
|  |          if (second == updated_second) | ||||||
|  |          { | ||||||
|  |             mark_field(marked_fields, cron_field::second); | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          unsigned int minute = date.tm_min; | ||||||
|  |          auto update_minute = find_next( | ||||||
|  |             cex.minutes, | ||||||
|  |             date, | ||||||
|  |             Traits::CRON_MIN_MINUTES, | ||||||
|  |             Traits::CRON_MAX_MINUTES, | ||||||
|  |             minute, | ||||||
|  |             cron_field::minute, | ||||||
|  |             cron_field::hour_of_day, | ||||||
|  |             marked_fields); | ||||||
|  |          if (minute == update_minute) | ||||||
|  |          { | ||||||
|  |             mark_field(marked_fields, cron_field::minute); | ||||||
|  |          } | ||||||
|  |          else | ||||||
|  |          { | ||||||
|  |             res = find_next<Traits>(cex, date, dot); | ||||||
|  |             if (!res) return res; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          unsigned int hour = date.tm_hour; | ||||||
|  |          auto updated_hour = find_next( | ||||||
|  |             cex.hours, | ||||||
|  |             date, | ||||||
|  |             Traits::CRON_MIN_HOURS, | ||||||
|  |             Traits::CRON_MAX_HOURS, | ||||||
|  |             hour, | ||||||
|  |             cron_field::hour_of_day, | ||||||
|  |             cron_field::day_of_week, | ||||||
|  |             marked_fields); | ||||||
|  |          if (hour == updated_hour) | ||||||
|  |          { | ||||||
|  |             mark_field(marked_fields, cron_field::hour_of_day); | ||||||
|  |          } | ||||||
|  |          else | ||||||
|  |          { | ||||||
|  |             res = find_next<Traits>(cex, date, dot); | ||||||
|  |             if (!res) return res; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          unsigned int day_of_week = date.tm_wday; | ||||||
|  |          unsigned int day_of_month = date.tm_mday; | ||||||
|  |          auto updated_day_of_month = find_next_day<Traits>( | ||||||
|  |             date, | ||||||
|  |             cex.days_of_month, | ||||||
|  |             day_of_month, | ||||||
|  |             cex.days_of_week, | ||||||
|  |             day_of_week, | ||||||
|  |             marked_fields); | ||||||
|  |          if (day_of_month == updated_day_of_month) | ||||||
|  |          { | ||||||
|  |             mark_field(marked_fields, cron_field::day_of_month); | ||||||
|  |          } | ||||||
|  |          else | ||||||
|  |          { | ||||||
|  |             res = find_next<Traits>(cex, date, dot); | ||||||
|  |             if (!res) return res; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          unsigned int month = date.tm_mon; | ||||||
|  |          auto updated_month = find_next( | ||||||
|  |             cex.months, | ||||||
|  |             date, | ||||||
|  |             Traits::CRON_MIN_MONTHS, | ||||||
|  |             Traits::CRON_MAX_MONTHS, | ||||||
|  |             month, | ||||||
|  |             cron_field::month, | ||||||
|  |             cron_field::year, | ||||||
|  |             marked_fields); | ||||||
|  |          if (month != updated_month) | ||||||
|  |          { | ||||||
|  |             if (date.tm_year - dot > Traits::CRON_MAX_YEARS_DIFF) | ||||||
|  |                return false; | ||||||
|  |  | ||||||
|  |             res = find_next<Traits>(cex, date, dot); | ||||||
|  |             if (!res) return res; | ||||||
|  |          } | ||||||
|  |  | ||||||
|  |          return res; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    template <typename Traits> | ||||||
|  |    static cronexpr make_cron(CRONCPP_STRING_VIEW expr) | ||||||
|  |    { | ||||||
|  |       cronexpr cex; | ||||||
|  |  | ||||||
|  |       if (expr.empty()) | ||||||
|  |          throw bad_cronexpr("Invalid empty cron expression"); | ||||||
|  |  | ||||||
|  |       auto fields = utils::split(expr, ' '); | ||||||
|  |       fields.erase( | ||||||
|  |          std::remove_if(std::begin(fields), std::end(fields), | ||||||
|  |             [](CRONCPP_STRING_VIEW s) {return s.empty(); }), | ||||||
|  |          std::end(fields)); | ||||||
|  |       if (fields.size() != 6) | ||||||
|  |          throw bad_cronexpr("cron expression must have six fields"); | ||||||
|  |  | ||||||
|  |       detail::set_cron_field(fields[0], cex.seconds, Traits::CRON_MIN_SECONDS, Traits::CRON_MAX_SECONDS); | ||||||
|  |       detail::set_cron_field(fields[1], cex.minutes, Traits::CRON_MIN_MINUTES, Traits::CRON_MAX_MINUTES); | ||||||
|  |       detail::set_cron_field(fields[2], cex.hours, Traits::CRON_MIN_HOURS, Traits::CRON_MAX_HOURS); | ||||||
|  |  | ||||||
|  |       detail::set_cron_days_of_week<Traits>(fields[5], cex.days_of_week); | ||||||
|  |  | ||||||
|  |       detail::set_cron_days_of_month<Traits>(fields[3], cex.days_of_month); | ||||||
|  |  | ||||||
|  |       detail::set_cron_month<Traits>(fields[4], cex.months); | ||||||
|  |  | ||||||
|  |       cex.expr = expr; | ||||||
|  |  | ||||||
|  |       return cex; | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    template <typename Traits = cron_standard_traits> | ||||||
|  |    static std::tm cron_next(cronexpr const & cex, std::tm date) | ||||||
|  |    { | ||||||
|  |       time_t original = utils::tm_to_time(date); | ||||||
|  |       if (INVALID_TIME == original) return {}; | ||||||
|  |  | ||||||
|  |       if (!detail::find_next<Traits>(cex, date, date.tm_year)) | ||||||
|  |          return {}; | ||||||
|  |  | ||||||
|  |       time_t calculated = utils::tm_to_time(date); | ||||||
|  |       if (INVALID_TIME == calculated) return {}; | ||||||
|  |  | ||||||
|  |       if (calculated == original) | ||||||
|  |       { | ||||||
|  |          add_to_field(date, detail::cron_field::second, 1); | ||||||
|  |          if (!detail::find_next<Traits>(cex, date, date.tm_year)) | ||||||
|  |             return {}; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return date; | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |    template <typename Traits = cron_standard_traits> | ||||||
|  |    static std::time_t cron_next(cronexpr const & cex, std::time_t const & date) | ||||||
|  |    { | ||||||
|  |       std::tm val; | ||||||
|  |       std::tm* dt = utils::time_to_tm(&date, &val); | ||||||
|  |       if (dt == nullptr) return INVALID_TIME; | ||||||
|  |  | ||||||
|  |       time_t original = utils::tm_to_time(*dt); | ||||||
|  |       if (INVALID_TIME == original) return INVALID_TIME; | ||||||
|  |  | ||||||
|  |       if(!detail::find_next<Traits>(cex, *dt, dt->tm_year)) | ||||||
|  |          return INVALID_TIME; | ||||||
|  |  | ||||||
|  |       time_t calculated = utils::tm_to_time(*dt); | ||||||
|  |       if (INVALID_TIME == calculated) return calculated; | ||||||
|  |  | ||||||
|  |       if (calculated == original) | ||||||
|  |       { | ||||||
|  |          add_to_field(*dt, detail::cron_field::second, 1); | ||||||
|  |          if(!detail::find_next<Traits>(cex, *dt, dt->tm_year)) | ||||||
|  |             return INVALID_TIME; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       return utils::tm_to_time(*dt); | ||||||
|  |    } | ||||||
|  |  | ||||||
|  |   template <typename Traits = cron_standard_traits> | ||||||
|  |   static std::chrono::system_clock::time_point cron_next(cronexpr const & cex, std::chrono::system_clock::time_point const & time_point) { | ||||||
|  |      return std::chrono::system_clock::from_time_t(cron_next<Traits>(cex, std::chrono::system_clock::to_time_t(time_point))); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -83,12 +83,13 @@ namespace OpenWifi { | |||||||
|     InventoryDB::InventoryDB( OpenWifi::DBType T, Poco::Data::SessionPool & P, Poco::Logger &L) : |     InventoryDB::InventoryDB( OpenWifi::DBType T, Poco::Data::SessionPool & P, Poco::Logger &L) : | ||||||
|         DB(T, "inventory", InventoryDB_Fields, InventoryDB_Indexes, P, L, "inv") {} |         DB(T, "inventory", InventoryDB_Fields, InventoryDB_Indexes, P, L, "inv") {} | ||||||
|  |  | ||||||
|     bool InventoryDB::CreateFromConnection( const std::string &SerialNumber, |     bool InventoryDB::CreateFromConnection( const std::string &SerialNumberRaw, | ||||||
|                                             const std::string &ConnectionInfo, |                                             const std::string &ConnectionInfo, | ||||||
|                                             const std::string &DeviceType, |                                             const std::string &DeviceType, | ||||||
|                                             const std::string &Locale) { |                                             const std::string &Locale) { | ||||||
|  |  | ||||||
|         ProvObjects::InventoryTag   ExistingDevice; |         ProvObjects::InventoryTag   ExistingDevice; | ||||||
|  |         auto SerialNumber = Poco::toLower(SerialNumberRaw); | ||||||
|         if(!GetRecord("serialNumber",SerialNumber,ExistingDevice)) { |         if(!GetRecord("serialNumber",SerialNumber,ExistingDevice)) { | ||||||
|             ProvObjects::InventoryTag   NewDevice; |             ProvObjects::InventoryTag   NewDevice; | ||||||
|             uint64_t Now = OpenWifi::Now(); |             uint64_t Now = OpenWifi::Now(); | ||||||
| @@ -223,7 +224,7 @@ namespace OpenWifi { | |||||||
|             ProvObjects::DeviceRules    Rules; |             ProvObjects::DeviceRules    Rules; | ||||||
|             std::string     SerialNumber = Utils::IntToSerialNumber(i); |             std::string     SerialNumber = Utils::IntToSerialNumber(i); | ||||||
|             if(EvaluateDeviceSerialNumberRules(SerialNumber,Rules)) { |             if(EvaluateDeviceSerialNumberRules(SerialNumber,Rules)) { | ||||||
|                 if(Rules.rrm=="yes") |                 if(Rules.rrm!="no" && Rules.rrm!="inherit") | ||||||
|                     DeviceList.push_back(SerialNumber); |                     DeviceList.push_back(SerialNumber); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -97,7 +97,7 @@ if [ -z ${OWPROV_OVERRIDE+x} ]; then | |||||||
| 		port="$(echo $hostport | sed -e 's,^.*:,:,g' -e 's,.*:\([0-9]*\).*,\1,g' -e 's,[^0-9],,g')" | 		port="$(echo $hostport | sed -e 's,^.*:,:,g' -e 's,.*:\([0-9]*\).*,\1,g' -e 's,[^0-9],,g')" | ||||||
| 		path="$(echo $url | grep / | cut -d/ -f2-)" | 		path="$(echo $url | grep / | cut -d/ -f2-)" | ||||||
| 		export OWPROV=${url} | 		export OWPROV=${url} | ||||||
| 		echo "Using ${OWPROV}..." | 		echo "Using PROV=${OWPROV}..." | ||||||
| 	else | 	else | ||||||
| 		echo "OWPROV endpoint is not found:" | 		echo "OWPROV endpoint is not found:" | ||||||
| 		jq < ${result_file} | 		jq < ${result_file} | ||||||
| @@ -108,6 +108,33 @@ else | |||||||
| fi | fi | ||||||
| } | } | ||||||
|  |  | ||||||
|  | setrrm() { | ||||||
|  |     if [ -z ${OWRRM_OVERRIDE+x} ]; then | ||||||
|  |     curl  ${FLAGS} -X GET "https://${OWSEC}/api/v1/systemEndpoints" \ | ||||||
|  |         -H "Accept: application/json" \ | ||||||
|  |         -H "Content-Type: application/json" \ | ||||||
|  |         -H "Authorization: Bearer ${token}"  > ${result_file} | ||||||
|  |     rawurl="$(cat ${result_file} | jq -r '.endpoints[] | select( .type == "owrrm" ) | .uri')" | ||||||
|  |     if [[ ! -z "${rawurl}" ]]; then | ||||||
|  |             proto="$(echo $rawurl | grep :// | sed -e's,^\(.*://\).*,\1,g')" | ||||||
|  |     url="$(echo ${rawurl/$proto/})" | ||||||
|  |     user="$(echo $url | grep @ | cut -d@ -f1)" | ||||||
|  |     hostport="$(echo ${url/$user@/} | cut -d/ -f1)" | ||||||
|  |     host="$(echo $hostport | sed -e 's,:.*,,g')" | ||||||
|  |     port="$(echo $hostport | sed -e 's,^.*:,:,g' -e 's,.*:\([0-9]*\).*,\1,g' -e 's,[^0-9],,g')" | ||||||
|  |     path="$(echo $url | grep / | cut -d/ -f2-)" | ||||||
|  |     export OWRRM=${url} | ||||||
|  |     echo "Using RRM=${OWRRM}..." | ||||||
|  |     else | ||||||
|  |     echo "OWRRM endpoint is not found:" | ||||||
|  |     jq < ${result_file} | ||||||
|  |     exit 1 | ||||||
|  |     fi | ||||||
|  |     else | ||||||
|  |     export OWRRM=${OWRRM_OVERRIDE} | ||||||
|  |     fi | ||||||
|  | } | ||||||
|  |  | ||||||
| logout() { | logout() { | ||||||
|   curl  ${FLAGS} -X DELETE "https://${OWSEC}/api/v1/oauth2/${token}" \ |   curl  ${FLAGS} -X DELETE "https://${OWSEC}/api/v1/oauth2/${token}" \ | ||||||
|         -H "Content-Type: application/json" \ |         -H "Content-Type: application/json" \ | ||||||
| @@ -507,6 +534,24 @@ getvenuedevices() { | |||||||
|     jq < ${result_file} |     jq < ${result_file} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | listrrmalgos() { | ||||||
|  |     setrrm | ||||||
|  |     curl    ${FLAGS} -X GET "http://${OWRRM}/api/v1/algorithms" \ | ||||||
|  |         -H "Content-Type: application/json" \ | ||||||
|  |         -H "Authorization: Bearer ${token}" \ | ||||||
|  |         -H "Accept: application/json" > ${result_file} | ||||||
|  |     jq < ${result_file} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | rrmprovider() { | ||||||
|  |     setrrm | ||||||
|  |     curl    ${FLAGS} -X GET "http://${OWRRM}/api/v1/provider" \ | ||||||
|  |         -H "Content-Type: application/json" \ | ||||||
|  |         -H "Authorization: Bearer ${token}" \ | ||||||
|  |         -H "Accept: application/json" > ${result_file} | ||||||
|  |     jq < ${result_file} | ||||||
|  | } | ||||||
|  |  | ||||||
| shopt -s nocasematch | shopt -s nocasematch | ||||||
| case "$1" in | case "$1" in | ||||||
|     "login") login; echo "You are logged in..."  ; logout ;; |     "login") login; echo "You are logged in..."  ; logout ;; | ||||||
| @@ -555,6 +600,8 @@ case "$1" in | |||||||
|     "getsubdevs") login; getsubdevs $2; logout;; |     "getsubdevs") login; getsubdevs $2; logout;; | ||||||
|     "listvenues") login; listvenues $2; logout;; |     "listvenues") login; listvenues $2; logout;; | ||||||
|     "getvenuedevices") login; getvenuedevices $2; logout;; |     "getvenuedevices") login; getvenuedevices $2; logout;; | ||||||
|  |     "listrrmalgos") login; listrrmalgos; logout;; | ||||||
|  |     "rrmprovider") login; rrmprovider; logout;; | ||||||
|     *) help ;; |     *) help ;; | ||||||
| esac | esac | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user