mirror of
https://github.com/Telecominfraproject/wlan-cloud-owprov.git
synced 2025-10-29 17:52:28 +00:00
Compare commits
61 Commits
v2.6.0-RC4
...
v2.7.0-RC4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad526ebf1d | ||
|
|
8de53277e6 | ||
|
|
93fbb3017a | ||
|
|
2e4d1ad3e8 | ||
|
|
109a9affc5 | ||
|
|
9c65813735 | ||
|
|
7d0bdf059d | ||
|
|
93e4b069c4 | ||
|
|
4fe1367651 | ||
|
|
5f5f2fd699 | ||
|
|
c2e0d32e0d | ||
|
|
cab81a3930 | ||
|
|
01395f11a3 | ||
|
|
250c12acf1 | ||
|
|
e23d04c1d0 | ||
|
|
b48955e791 | ||
|
|
e58eb38d53 | ||
|
|
791af9aeba | ||
|
|
67081917a9 | ||
|
|
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)
|
||||
project(owprov VERSION 2.6.0)
|
||||
project(owprov VERSION 2.7.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
@@ -58,6 +58,8 @@ include_directories(/usr/local/include /usr/local/opt/openssl/include src inclu
|
||||
|
||||
configure_file(src/ow_version.h.in ${PROJECT_SOURCE_DIR}/src/ow_version.h @ONLY)
|
||||
|
||||
add_definitions(-DPOCO_LOG_DEBUG="1")
|
||||
|
||||
add_compile_options(-Wall -Wextra)
|
||||
if(ASAN)
|
||||
add_compile_options(-fsanitize=address)
|
||||
@@ -76,6 +78,7 @@ add_executable(owprov
|
||||
src/framework/ConfigurationValidator.cpp
|
||||
src/framework/ConfigurationValidator.h
|
||||
src/framework/ow_constants.h
|
||||
src/framework/MicroServiceErrorHandler.h
|
||||
src/framework/WebSocketClientNotifications.h
|
||||
src/RESTObjects/RESTAPI_SecurityObjects.h src/RESTObjects/RESTAPI_SecurityObjects.cpp
|
||||
src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h
|
||||
@@ -134,7 +137,14 @@ add_executable(owprov
|
||||
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_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
|
||||
${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 \
|
||||
make cmake g++ git \
|
||||
@@ -9,8 +15,10 @@ RUN apk add --update --no-cache \
|
||||
|
||||
FROM build-base AS poco-build
|
||||
|
||||
ADD https://api.github.com/repos/stephb9959/poco/git/refs/heads/master version.json
|
||||
RUN git clone https://github.com/stephb9959/poco /poco
|
||||
ARG POCO_VERSION
|
||||
|
||||
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
|
||||
RUN mkdir cmake-build
|
||||
@@ -19,10 +27,26 @@ RUN cmake ..
|
||||
RUN cmake --build . --config Release -j8
|
||||
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
|
||||
|
||||
ADD https://api.github.com/repos/stephb9959/cppkafka/git/refs/heads/master version.json
|
||||
RUN git clone https://github.com/stephb9959/cppkafka /cppkafka
|
||||
ARG CPPKAFKA_VERSION
|
||||
|
||||
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
|
||||
RUN mkdir cmake-build
|
||||
@@ -33,8 +57,10 @@ RUN cmake --build . --target install
|
||||
|
||||
FROM build-base AS json-schema-validator-build
|
||||
|
||||
ADD https://api.github.com/repos/pboettch/json-schema-validator/git/refs/heads/master version.json
|
||||
RUN git clone https://github.com/pboettch/json-schema-validator /json-schema-validator
|
||||
ARG JSON_VALIDATOR_VERSION
|
||||
|
||||
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
|
||||
RUN mkdir cmake-build
|
||||
@@ -43,18 +69,6 @@ RUN cmake ..
|
||||
RUN make
|
||||
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
|
||||
|
||||
ADD CMakeLists.txt build /owprov/
|
||||
@@ -77,7 +91,7 @@ WORKDIR /owprov/cmake-build
|
||||
RUN cmake ..
|
||||
RUN cmake --build . --config Release -j8
|
||||
|
||||
FROM alpine:3.15
|
||||
FROM alpine:$ALPINE_VERSION
|
||||
|
||||
ENV OWPROV_USER=owprov \
|
||||
OWPROV_ROOT=/owprov-data \
|
||||
|
||||
@@ -24,6 +24,7 @@ if [[ "$TEMPLATE_CONFIG" = 'true' ]]; then
|
||||
SYSTEM_URI_PRIVATE=${SYSTEM_URI_PRIVATE:-"https://localhost:17005"} \
|
||||
SYSTEM_URI_PUBLIC=${SYSTEM_URI_PUBLIC:-"https://localhost:16005"} \
|
||||
SYSTEM_URI_UI=${SYSTEM_URI_UI:-"http://localhost"} \
|
||||
SECURITY_RESTAPI_DISABLE=${SECURITY_RESTAPI_DISABLE:-"false"} \
|
||||
KAFKA_ENABLE=${KAFKA_ENABLE:-"true"} \
|
||||
KAFKA_BROKERLIST=${KAFKA_BROKERLIST:-"localhost:9092"} \
|
||||
KAFKA_SSL_CA_LOCATION=${KAFKA_SSL_CA_LOCATION:-""} \
|
||||
|
||||
2
helm/.gitignore
vendored
2
helm/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
*.swp
|
||||
Chart.lock
|
||||
charts/
|
||||
|
||||
@@ -70,8 +70,8 @@ The following table lists the configurable parameters of the chart and their def
|
||||
| persistence.size | string | Defines PV size | `'10Gi'` |
|
||||
| public_env_variables | hash | Defines list of environment variables to be passed to the Provisioning | |
|
||||
| configProperties | hash | Configuration properties that should be passed to the application in `owprov.properties`. May be passed by key in set (i.e. `configProperties."rtty\.token"`) | |
|
||||
| certs | hash | Defines files (keys and certificates) that should be passed to the Provisioning (PEM format is adviced to be used) (see `volumes.owprov` on where it is mounted) | |
|
||||
|
||||
| existingCertsSecret | string | Existing Kubernetes secret containing all required certificates and private keys for microservice operation. If set, certificates from `certs` key are ignored | `""` |
|
||||
| certs | hash | Defines files (keys and certificates) that should be passed to the Gateway (PEM format is adviced to be used) (see `volumes.owprov` on where it is mounted). If `existingCertsSecret` is set, certificates passed this way will not be used. | |
|
||||
|
||||
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{{- $root := . -}}
|
||||
{{- $storageType := index .Values.configProperties "storage.type" -}}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -46,6 +47,39 @@ spec:
|
||||
- -timeout
|
||||
- 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:
|
||||
|
||||
- name: owprov
|
||||
|
||||
@@ -9,7 +9,7 @@ fullnameOverride: ""
|
||||
images:
|
||||
owprov:
|
||||
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owprov
|
||||
tag: main
|
||||
tag: v2.7.0-RC4
|
||||
pullPolicy: Always
|
||||
# regcred:
|
||||
# registry: tip-tip-wlan-cloud-ucentral.jfrog.io
|
||||
@@ -71,7 +71,7 @@ volumes:
|
||||
mountPath: /owprov-data/certs
|
||||
volumeDefinition: |
|
||||
secret:
|
||||
secretName: {{ include "owprov.fullname" . }}-certs
|
||||
secretName: {{ if .Values.existingCertsSecret }}{{ .Values.existingCertsSecret }}{{ else }}{{ include "owprov.fullname" . }}-certs{{ end }}
|
||||
# Change this if you want to use another volume type
|
||||
- name: persist
|
||||
mountPath: /owprov-data/persist
|
||||
@@ -199,6 +199,9 @@ configProperties:
|
||||
storage.type.mysql.username: stephb
|
||||
storage.type.mysql.password: snoopy99
|
||||
|
||||
# NOTE: List of required certificates may be found in "certs" key. Alternative way to pass required certificates is to create external secret with all required certificates and set secret name in "existingCertsSecret" key. Details may be found in https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy/tree/main/chart#tldr
|
||||
existingCertsSecret: ""
|
||||
|
||||
certs:
|
||||
# restapi-ca.pem: ""
|
||||
# restapi-cert.pem: ""
|
||||
|
||||
@@ -1958,11 +1958,6 @@ paths:
|
||||
schema:
|
||||
type: boolean
|
||||
required: false
|
||||
- in: query
|
||||
name: deviceType
|
||||
schema:
|
||||
type: string
|
||||
required: false
|
||||
- in: query
|
||||
description: Pagination start (starts at 1. If not specified, 1 is assumed)
|
||||
name: offset
|
||||
@@ -2034,6 +2029,21 @@ paths:
|
||||
type: string
|
||||
format: uuid
|
||||
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:
|
||||
200:
|
||||
description: Return a list of elements
|
||||
@@ -2255,12 +2265,6 @@ paths:
|
||||
type: string
|
||||
example: serial1,serial2,serial3
|
||||
required: false
|
||||
- in: query
|
||||
description: only serial numbers of full device details
|
||||
name: serialOnly
|
||||
schema:
|
||||
type: boolean
|
||||
required: false
|
||||
- in: query
|
||||
description: return the number of devices
|
||||
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'
|
||||
|
||||
@@ -34,6 +34,7 @@ openwifi.system.uri.private = https://localhost:17005
|
||||
openwifi.system.uri.public = https://ucentral.dpaas.arilia.com:16005
|
||||
openwifi.system.commandchannel = /tmp/app.owprov
|
||||
openwifi.system.uri.ui = owprov-ui.arilia.com
|
||||
openwifi.security.restapi.disable = false
|
||||
|
||||
firmware.updater.upgrade = false
|
||||
firmware.updater.releaseonly = false
|
||||
@@ -115,4 +116,4 @@ storage.type.mysql.connectiontimeout = 60
|
||||
########################################################################
|
||||
logging.type = file
|
||||
logging.path = $OWPROV_ROOT/logs
|
||||
logging.level = debug
|
||||
logging.level = debug
|
||||
|
||||
@@ -39,6 +39,7 @@ openwifi.system.uri.private = ${SYSTEM_URI_PRIVATE}
|
||||
openwifi.system.uri.public = ${SYSTEM_URI_PUBLIC}
|
||||
openwifi.system.commandchannel = /tmp/app.ucentralfms
|
||||
openwifi.system.uri.ui = ${SYSTEM_URI_UI}
|
||||
openwifi.security.restapi.disable = ${SECURITY_RESTAPI_DISABLE}
|
||||
|
||||
#############################
|
||||
# Generic information for all micro services
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace OpenWifi {
|
||||
void Stop() override;
|
||||
void ConnectionReceived( const std::string & Key, const std::string & Payload) {
|
||||
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));
|
||||
}
|
||||
void run() override;
|
||||
|
||||
@@ -73,17 +73,25 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
auto App = OpenWifi::Daemon::instance();
|
||||
auto ExitCode = App->run(argc, argv);
|
||||
delete App;
|
||||
int ExitCode;
|
||||
try {
|
||||
Poco::Net::SSLManager::instance().initializeServer(nullptr, nullptr, nullptr);
|
||||
auto App = OpenWifi::Daemon::instance();
|
||||
ExitCode = App->run(argc, argv);
|
||||
Poco::Net::SSLManager::instance().shutdown();
|
||||
} catch (Poco::Exception &exc) {
|
||||
ExitCode = Poco::Util::Application::EXIT_SOFTWARE;
|
||||
std::cout << exc.displayText() << std::endl;
|
||||
} catch (std::exception &exc) {
|
||||
ExitCode = Poco::Util::Application::EXIT_TEMPFAIL;
|
||||
std::cout << exc.what() << std::endl;
|
||||
} catch (...) {
|
||||
ExitCode = Poco::Util::Application::EXIT_TEMPFAIL;
|
||||
std::cout << "Exception on closure" << std::endl;
|
||||
}
|
||||
|
||||
return ExitCode;
|
||||
|
||||
} catch (Poco::Exception &exc) {
|
||||
std::cerr << exc.displayText() << std::endl;
|
||||
return Poco::Util::Application::EXIT_SOFTWARE;
|
||||
}
|
||||
std::cout << "Exitcode: " << ExitCode << std::endl;
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
// end of namespace
|
||||
@@ -33,22 +33,22 @@ namespace OpenWifi {
|
||||
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
for(auto &job:jobs_) {
|
||||
if(job!=nullptr) {
|
||||
if(job->Started()==0 && Pool_.used()<Pool_.available()) {
|
||||
job->Logger().information(fmt::format("Starting {}: {}",job->JobId(),job->Name()));
|
||||
job->Start();
|
||||
Pool_.start(*job);
|
||||
for(auto ¤t_job:jobs_) {
|
||||
if(current_job!=nullptr) {
|
||||
if(current_job->Started()==0 && Pool_.used()<Pool_.available()) {
|
||||
current_job->Logger().information(fmt::format("Starting {}: {}",current_job->JobId(),current_job->Name()));
|
||||
current_job->Start();
|
||||
Pool_.start(*current_job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(auto it = jobs_.begin(); it!=jobs_.end();) {
|
||||
if(*it!=nullptr && (*it)->Completed()!=0) {
|
||||
auto tmp = it;
|
||||
(*it)->Logger().information(fmt::format("Completed {}: {}",(*it)->JobId(),(*it)->Name()));
|
||||
for(auto it = jobs_.begin(); it!=jobs_.end();) {\
|
||||
auto current_job = *it;
|
||||
if(current_job!=nullptr && current_job->Completed()!=0) {
|
||||
current_job->Logger().information(fmt::format("Completed {}: {}",current_job->JobId(),current_job->Name()));
|
||||
it = jobs_.erase(it);
|
||||
delete *tmp;
|
||||
delete current_job;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace OpenWifi {
|
||||
bool &Done, std::string &Answer) {
|
||||
Done = false;
|
||||
auto Prefix = O->get("serial_prefix").toString();
|
||||
Poco::toLowerInPlace(Prefix);
|
||||
Logger().information(Poco::format("serial_number_search: %s", Prefix));
|
||||
if (!Prefix.empty() && Prefix.length() < 13) {
|
||||
std::vector<uint64_t> Numbers;
|
||||
@@ -83,6 +84,7 @@ namespace OpenWifi {
|
||||
Done = false;
|
||||
auto operatorId = O->get("operatorId").toString();
|
||||
auto Prefix = O->get("serial_prefix").toString();
|
||||
Poco::toLowerInPlace(Prefix);
|
||||
std::string Query;
|
||||
|
||||
if(Prefix[0]=='*') {
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/ConfigurationValidator.h"
|
||||
#include "sdks/SDK_sec.h"
|
||||
#include "Poco/StringTokenizer.h"
|
||||
|
||||
#include "libs/croncpp.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
@@ -405,7 +408,9 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
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) {
|
||||
Poco::JSON::Parser P;
|
||||
@@ -521,12 +526,39 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
return (DR.rrm=="yes" || DR.rrm=="no" || DR.rrm=="inherit") &&
|
||||
return (ValidRRM(DR.rrm)) &&
|
||||
(DR.firmwareUpgrade=="yes" || DR.firmwareUpgrade=="no" || DR.firmwareUpgrade=="inherit") &&
|
||||
(DR.rcOnly=="yes" || DR.rcOnly=="no" || DR.rcOnly=="inherit");
|
||||
}
|
||||
|
||||
@@ -35,36 +35,53 @@ namespace OpenWifi{
|
||||
|
||||
void RESTAPI_inventory_handler::DoGet() {
|
||||
|
||||
ProvObjects::InventoryTag Existing;
|
||||
std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER,"");
|
||||
Logger().debug(Poco::format("%s: Retrieving inventory information.",SerialNumber));
|
||||
if(SerialNumber.empty() || !DB_.GetRecord(RESTAPI::Protocol::SERIALNUMBER,SerialNumber,Existing)) {
|
||||
ProvObjects::InventoryTag Existing;
|
||||
std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
|
||||
Logger().debug(Poco::format("%s: Retrieving inventory information.", SerialNumber));
|
||||
if (SerialNumber.empty() || !DB_.GetRecord(RESTAPI::Protocol::SERIALNUMBER, SerialNumber, Existing)) {
|
||||
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;
|
||||
if(HasParameter("config",Arg) && Arg=="true") {
|
||||
bool Explain = (HasParameter("explain",Arg) && Arg == "true");
|
||||
APConfig Device(SerialNumber,Existing.deviceType,Logger(), Explain);
|
||||
if (HasParameter("config", Arg) && Arg == "true") {
|
||||
bool Explain = (HasParameter("explain", Arg) && Arg == "true");
|
||||
APConfig Device(SerialNumber, Existing.deviceType, Logger(), Explain);
|
||||
|
||||
auto Configuration = Poco::makeShared<Poco::JSON::Object>();
|
||||
if(Device.Get(Configuration)) {
|
||||
if (Device.Get(Configuration)) {
|
||||
Answer.set("config", Configuration);
|
||||
if(Explain)
|
||||
if (Explain)
|
||||
Answer.set("explanation", Device.Explanation());
|
||||
} else {
|
||||
Answer.set("config","none");
|
||||
Answer.set("config", "none");
|
||||
}
|
||||
return ReturnObject(Answer);
|
||||
} else if(HasParameter("firmwareOptions", Arg) && Arg=="true") {
|
||||
} else if (GetBoolParameter("firmwareOptions", false)) {
|
||||
ProvObjects::DeviceRules Rules;
|
||||
StorageService()->InventoryDB().EvaluateDeviceSerialNumberRules(SerialNumber,Rules);
|
||||
Answer.set("firmwareUpgrade",Rules.firmwareUpgrade);
|
||||
Answer.set("firmwareRCOnly", Rules.rcOnly == "yes" );
|
||||
StorageService()->InventoryDB().EvaluateDeviceSerialNumberRules(SerialNumber, Rules);
|
||||
Answer.set("firmwareUpgrade", Rules.firmwareUpgrade);
|
||||
Answer.set("firmwareRCOnly", Rules.rcOnly == "yes");
|
||||
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));
|
||||
auto Device = std::make_shared<APConfig>(SerialNumber, Existing.deviceType, Logger(), false);
|
||||
auto Configuration = Poco::makeShared<Poco::JSON::Object>();
|
||||
@@ -91,6 +108,19 @@ namespace OpenWifi{
|
||||
}
|
||||
Results.to_json(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) {
|
||||
AddExtendedInfo(Existing,Answer);
|
||||
}
|
||||
@@ -136,6 +166,7 @@ namespace OpenWifi{
|
||||
|
||||
void RESTAPI_inventory_handler::DoPost() {
|
||||
std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER,"");
|
||||
Poco::toLowerInPlace(SerialNumber);
|
||||
if(SerialNumber.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
|
||||
}
|
||||
@@ -154,6 +185,11 @@ namespace OpenWifi{
|
||||
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
||||
}
|
||||
|
||||
NormalizeMac(NewObject.serialNumber);
|
||||
if(SerialNumber!=NewObject.serialNumber) {
|
||||
return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
|
||||
}
|
||||
|
||||
if((RawObject->has("deviceRules") && !ValidDeviceRules(NewObject.deviceRules,*this))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace OpenWifi {
|
||||
{ "email", UserName },
|
||||
{ "signupUUID" , SignupUUID },
|
||||
{ "owner" , SignupOperator.info.id },
|
||||
|
||||
{ "operatorName", SignupOperator.registrationId }
|
||||
}, Body, 30000);
|
||||
|
||||
Poco::JSON::Object::Ptr Answer;
|
||||
|
||||
@@ -203,6 +203,10 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj,"kafkaClients", kafkaClients);
|
||||
field_to_json(Obj,"kafkaPackets", kafkaPackets);
|
||||
field_to_json(Obj,"locale", locale);
|
||||
field_to_json(Obj,"started", started);
|
||||
field_to_json(Obj,"sessionId", sessionId);
|
||||
field_to_json(Obj,"connectionCompletionTime", connectionCompletionTime);
|
||||
field_to_json(Obj,"totalConnectionTime", OpenWifi::Now() - started);
|
||||
|
||||
switch(VerifiedCertificate) {
|
||||
case NO_CERTIFICATE:
|
||||
@@ -218,6 +222,21 @@ namespace OpenWifi::GWObjects {
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceConnectionStatistics::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"averageConnectionTime", averageConnectionTime);
|
||||
field_to_json(Obj,"connectedDevices", connectedDevices );
|
||||
}
|
||||
|
||||
bool DeviceConnectionStatistics::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"averageConnectionTime", averageConnectionTime);
|
||||
field_from_json(Obj,"connectedDevices", connectedDevices );
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RttySessionDetails::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"serialNumber", SerialNumber);
|
||||
field_to_json(Obj,"server", Server);
|
||||
@@ -293,7 +312,6 @@ namespace OpenWifi::GWObjects {
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void RadiusProxyPoolList::to_json(Poco::JSON::Object &Obj) const {
|
||||
@@ -314,6 +332,8 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj,"description",description);
|
||||
field_to_json(Obj,"authConfig",authConfig);
|
||||
field_to_json(Obj,"acctConfig",acctConfig);
|
||||
field_to_json(Obj,"coaConfig",coaConfig);
|
||||
field_to_json(Obj,"useByDefault",useByDefault);
|
||||
}
|
||||
|
||||
bool RadiusProxyPool::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
@@ -322,6 +342,8 @@ namespace OpenWifi::GWObjects {
|
||||
field_from_json(Obj,"description",description);
|
||||
field_from_json(Obj,"authConfig",authConfig);
|
||||
field_from_json(Obj,"acctConfig",acctConfig);
|
||||
field_from_json(Obj,"coaConfig",coaConfig);
|
||||
field_from_json(Obj,"useByDefault",useByDefault);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
@@ -329,7 +351,7 @@ namespace OpenWifi::GWObjects {
|
||||
}
|
||||
|
||||
void RadiusProxyServerConfig::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"policy",strategy);
|
||||
field_to_json(Obj,"strategy",strategy);
|
||||
field_to_json(Obj,"monitor",monitor);
|
||||
field_to_json(Obj,"monitorMethod",monitorMethod);
|
||||
field_to_json(Obj,"methodParameters",methodParameters);
|
||||
@@ -338,7 +360,7 @@ namespace OpenWifi::GWObjects {
|
||||
|
||||
bool RadiusProxyServerConfig::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"policy",strategy);
|
||||
field_from_json(Obj,"strategy",strategy);
|
||||
field_from_json(Obj,"monitor",monitor);
|
||||
field_from_json(Obj,"monitorMethod",monitorMethod);
|
||||
field_from_json(Obj,"methodParameters",methodParameters);
|
||||
@@ -354,6 +376,16 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj,"ip",ip);
|
||||
field_to_json(Obj,"port",port);
|
||||
field_to_json(Obj,"weight",weight);
|
||||
field_to_json(Obj,"secret",secret);
|
||||
field_to_json(Obj,"certificate",certificate);
|
||||
field_to_json(Obj,"radsec",radsec);
|
||||
field_to_json(Obj,"radsecPort",radsecPort);
|
||||
field_to_json(Obj,"radsecSecret",radsecSecret);
|
||||
field_to_json(Obj,"radsecCacerts",radsecCacerts);
|
||||
field_to_json(Obj,"radsecCert",radsecCert);
|
||||
field_to_json(Obj,"radsecKey",radsecKey);
|
||||
field_to_json(Obj,"radsecRealms",radsecRealms);
|
||||
field_to_json(Obj,"ignore",ignore);
|
||||
}
|
||||
|
||||
bool RadiusProxyServerEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
@@ -362,6 +394,16 @@ namespace OpenWifi::GWObjects {
|
||||
field_from_json(Obj,"ip",ip);
|
||||
field_from_json(Obj,"port",port);
|
||||
field_from_json(Obj,"weight",weight);
|
||||
field_from_json(Obj,"secret",secret);
|
||||
field_from_json(Obj,"certificate",certificate);
|
||||
field_from_json(Obj,"radsec",radsec);
|
||||
field_from_json(Obj,"radsecSecret",radsecSecret);
|
||||
field_from_json(Obj,"radsecPort",radsecPort);
|
||||
field_from_json(Obj,"radsecCacerts",radsecCacerts);
|
||||
field_from_json(Obj,"radsecCert",radsecCert);
|
||||
field_from_json(Obj,"radsecKey",radsecKey);
|
||||
field_from_json(Obj,"radsecRealms",radsecRealms);
|
||||
field_from_json(Obj,"ignore",ignore);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ namespace OpenWifi::GWObjects {
|
||||
uint64_t kafkaPackets=0;
|
||||
uint64_t websocketPackets=0;
|
||||
std::string locale;
|
||||
uint64_t started=0;
|
||||
uint64_t sessionId=0;
|
||||
double connectionCompletionTime=0.0;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
@@ -71,6 +75,13 @@ namespace OpenWifi::GWObjects {
|
||||
void Print() const;
|
||||
};
|
||||
|
||||
struct DeviceConnectionStatistics {
|
||||
std::uint64_t connectedDevices = 0;
|
||||
std::uint64_t averageConnectionTime = 0;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct Statistics {
|
||||
std::string SerialNumber;
|
||||
uint64_t UUID = 0 ;
|
||||
@@ -216,6 +227,16 @@ namespace OpenWifi::GWObjects {
|
||||
std::string ip;
|
||||
uint16_t port=0;
|
||||
uint64_t weight=0;
|
||||
std::string secret;
|
||||
std::string certificate;
|
||||
bool radsec=false;
|
||||
uint16_t radsecPort=2083;
|
||||
std::string radsecSecret;
|
||||
std::string radsecKey;
|
||||
std::string radsecCert;
|
||||
std::vector<std::string> radsecCacerts;
|
||||
std::vector<std::string> radsecRealms;
|
||||
bool ignore=false;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
@@ -237,6 +258,8 @@ namespace OpenWifi::GWObjects {
|
||||
std::string description;
|
||||
RadiusProxyServerConfig authConfig;
|
||||
RadiusProxyServerConfig acctConfig;
|
||||
RadiusProxyServerConfig coaConfig;
|
||||
bool useByDefault=false;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
|
||||
@@ -1159,5 +1159,40 @@ namespace OpenWifi::ProvObjects {
|
||||
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;
|
||||
|
||||
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 {
|
||||
std::string rcOnly{"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.
|
||||
auto FixVenueDevices = [&](const ProvObjects::Venue &V) -> bool {
|
||||
Types::UUIDvec_t NewDevices;
|
||||
bool modified=false;
|
||||
for(const auto &device:V.devices) {
|
||||
ProvObjects::InventoryTag T;
|
||||
if(InventoryDB().GetRecord("id", device, T)) {
|
||||
NewDevices.emplace_back(device);
|
||||
} else {
|
||||
|
||||
modified=true;
|
||||
}
|
||||
}
|
||||
|
||||
if(NewDevices!=V.devices) {
|
||||
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));
|
||||
ProvObjects::Venue NewVenue = V;
|
||||
NewVenue.devices = NewDevices;
|
||||
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)
|
||||
{
|
||||
Logger().warning(fmt::format(" fixing entity: {}",E.info.name));
|
||||
ProvObjects::Entity NewEntity = E;
|
||||
NewEntity.devices = NewDevices;
|
||||
NewEntity.contacts = NewContacts;
|
||||
NewEntity.locations = NewLocations;
|
||||
@@ -304,6 +316,11 @@ namespace OpenWifi {
|
||||
modified=true;
|
||||
}
|
||||
|
||||
if(T.deviceRules.rrm=="yes") {
|
||||
NewTag.deviceRules.rrm = "inherit";
|
||||
modified=true;
|
||||
}
|
||||
|
||||
if(modified) {
|
||||
Logger().warning(fmt::format(" fixing entity: {}",T.info.name));
|
||||
InventoryDB().UpdateRecord("id", T.info.id, NewTag);
|
||||
@@ -311,12 +328,67 @@ namespace OpenWifi {
|
||||
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");
|
||||
VenueDB().Iterate(FixVenueDevices);
|
||||
Logger().information("Checking DB consistency: entities");
|
||||
EntityDB().Iterate(FixEntity);
|
||||
Logger().information("Checking DB consistency: inventory");
|
||||
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() {
|
||||
|
||||
@@ -20,8 +20,6 @@ namespace OpenWifi {
|
||||
auto Status = Results->get("status").extract<Poco::JSON::Object::Ptr>();
|
||||
auto Rejected = Status->getArray("rejected");
|
||||
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 (...) {
|
||||
}
|
||||
@@ -106,78 +104,66 @@ namespace OpenWifi {
|
||||
ProvObjects::Venue Venue;
|
||||
uint64_t Updated = 0, Failed = 0 , BadConfigs = 0 ;
|
||||
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.jobId = JobId();
|
||||
|
||||
std::array<tState,MaxThreads> Tasks;
|
||||
Poco::ThreadPool Pool_;
|
||||
std::list<VenueDeviceConfigUpdater*> JobList;
|
||||
|
||||
|
||||
for(const auto &uuid:Venue.devices) {
|
||||
auto NewTask = new VenueDeviceConfigUpdater(uuid, Venue.info.name, Logger());
|
||||
// std::cout << "Scheduling config push for " << uuid << std::endl;
|
||||
bool found_slot = false;
|
||||
while (!found_slot) {
|
||||
for (auto &cur_task: Tasks) {
|
||||
if (cur_task.task == nullptr) {
|
||||
cur_task.task = NewTask;
|
||||
cur_task.thr_.start(*NewTask);
|
||||
found_slot = true;
|
||||
break;
|
||||
}
|
||||
bool TaskAdded=false;
|
||||
while(!TaskAdded) {
|
||||
if (Pool_.available()) {
|
||||
JobList.push_back(NewTask);
|
||||
Pool_.start(*NewTask);
|
||||
TaskAdded = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
Updated += cur_task.task->updated_;
|
||||
Failed += cur_task.task->failed_;
|
||||
BadConfigs += cur_task.task->bad_config_;
|
||||
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();) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
BadConfigs++;
|
||||
N.content.error.push_back(cur_task.task->SerialNumber);
|
||||
}
|
||||
cur_task.task->started_ = cur_task.task->done_ = false;
|
||||
delete cur_task.task;
|
||||
cur_task.task = nullptr;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +175,7 @@ namespace OpenWifi {
|
||||
Logger().warning(N.content.details);
|
||||
}
|
||||
|
||||
// std::cout << N.content.details << std::endl;
|
||||
WebSocketClientNotificationVenueUpdateJobCompletionToUser(UserInfo().email, N);
|
||||
Logger().information(fmt::format("Job {} Completed: {} updated, {} failed to update , {} bad configurations.",
|
||||
JobId(), Updated ,Failed, BadConfigs));
|
||||
|
||||
@@ -66,77 +66,59 @@ namespace OpenWifi {
|
||||
ProvObjects::Venue Venue;
|
||||
uint64_t rebooted_ = 0, failed_ = 0;
|
||||
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.jobId = JobId();
|
||||
|
||||
std::array<tState,MaxThreads> Tasks;
|
||||
Poco::ThreadPool Pool_;
|
||||
std::list<VenueDeviceRebooter*> JobList;
|
||||
|
||||
for(const auto &uuid:Venue.devices) {
|
||||
auto NewTask = new VenueDeviceRebooter(uuid, Venue.info.name, Logger());
|
||||
// std::cout << "Scheduling config push for " << uuid << std::endl;
|
||||
bool found_slot = false;
|
||||
while (!found_slot) {
|
||||
for (auto &cur_task: Tasks) {
|
||||
if (cur_task.task == nullptr) {
|
||||
cur_task.task = NewTask;
|
||||
cur_task.thr_.start(*NewTask);
|
||||
found_slot = true;
|
||||
break;
|
||||
}
|
||||
bool TaskAdded=false;
|
||||
while(!TaskAdded) {
|
||||
if (Pool_.available()) {
|
||||
JobList.push_back(NewTask);
|
||||
Pool_.start(*NewTask);
|
||||
TaskAdded = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
rebooted_ += cur_task.task->rebooted_;
|
||||
failed_ += cur_task.task->failed_;
|
||||
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();) {
|
||||
VenueDeviceRebooter * current_job = *job_it;
|
||||
if(current_job!= nullptr && current_job->done_) {
|
||||
if(current_job->rebooted_)
|
||||
N.content.success.push_back(current_job->SerialNumber);
|
||||
else
|
||||
N.content.warning.push_back(current_job->SerialNumber);
|
||||
rebooted_ += current_job->rebooted_;
|
||||
failed_ += current_job->failed_;
|
||||
job_it = JobList.erase(job_it);
|
||||
delete current_job;
|
||||
} else {
|
||||
++job_it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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->rebooted_) {
|
||||
rebooted_++;
|
||||
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;
|
||||
}
|
||||
}
|
||||
Pool_.joinAll();
|
||||
for(auto job_it = JobList.begin(); job_it !=JobList.end();) {
|
||||
VenueDeviceRebooter * current_job = *job_it;
|
||||
if(current_job!= nullptr && current_job->done_) {
|
||||
if(current_job->rebooted_)
|
||||
N.content.success.push_back(current_job->SerialNumber);
|
||||
else
|
||||
N.content.warning.push_back(current_job->SerialNumber);
|
||||
rebooted_ += current_job->rebooted_;
|
||||
failed_ += current_job->failed_;
|
||||
job_it = JobList.erase(job_it);
|
||||
delete current_job;
|
||||
} else {
|
||||
++job_it;
|
||||
}
|
||||
}
|
||||
|
||||
N.content.details = fmt::format("Job {} Completed: {} rebooted, {} failed to reboot.",
|
||||
JobId(), rebooted_ ,failed_);
|
||||
|
||||
@@ -145,6 +127,7 @@ namespace OpenWifi {
|
||||
Logger().warning(N.content.details);
|
||||
}
|
||||
|
||||
// std::cout << N.content.details << std::endl;
|
||||
WebSocketClientNotificationVenueRebootCompletionToUser(UserInfo().email,N);
|
||||
Logger().information(fmt::format("Job {} Completed: {} rebooted, {} failed to reboot.",
|
||||
JobId(), rebooted_ ,failed_));
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace OpenWifi {
|
||||
|
||||
Storage::ApplyRules(rules_,Device.deviceRules);
|
||||
if(Device.deviceRules.firmwareUpgrade=="no") {
|
||||
std::cout << "Skipped Upgrade:" << Device.serialNumber << std::endl;
|
||||
poco_debug(Logger(),fmt::format("Skipped Upgrade: {}", Device.serialNumber));
|
||||
skipped_++;
|
||||
done_=true;
|
||||
return;
|
||||
@@ -40,24 +40,25 @@ namespace OpenWifi {
|
||||
FMSObjects::Firmware F;
|
||||
if(SDK::FMS::Firmware::GetLatest(Device.deviceType,Device.deviceRules.rcOnly=="yes",F)) {
|
||||
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));
|
||||
upgraded_++;
|
||||
} else {
|
||||
std::cout << "Did not Upgrade:" << Device.serialNumber << " to " << F.uri << std::endl;
|
||||
Logger().information(fmt::format("{}: Not Upgraded.", Device.serialNumber));
|
||||
failed_++;
|
||||
not_connected_++;
|
||||
}
|
||||
} else {
|
||||
std::cout << "Did not Upgrade:" << Device.serialNumber << " to <unknown>" << std::endl;
|
||||
failed_++;
|
||||
Logger().information(fmt::format("{}: Not Upgraded. No firmware available.", Device.serialNumber));
|
||||
no_firmware_++;
|
||||
}
|
||||
}
|
||||
done_ = true;
|
||||
// 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,
|
||||
done_ = false;
|
||||
std::string SerialNumber;
|
||||
@@ -82,98 +83,98 @@ namespace OpenWifi {
|
||||
Utils::SetThreadName("venue-upgr");
|
||||
auto VenueUUID_ = Parameter(0);
|
||||
|
||||
WebSocketClientNotificationVenueRebootList_t N;
|
||||
WebSocketClientNotificationVenueUpgradeList_t N;
|
||||
|
||||
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)) {
|
||||
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.jobId = JobId();
|
||||
|
||||
std::array<tState,MaxThreads> Tasks;
|
||||
ProvObjects::DeviceRules Rules;
|
||||
Poco::ThreadPool Pool_;
|
||||
std::list<VenueDeviceUpgrade*> JobList;
|
||||
ProvObjects::DeviceRules Rules;
|
||||
|
||||
StorageService()->VenueDB().EvaluateDeviceRules(Venue.info.id, Rules);
|
||||
|
||||
for(const auto &uuid:Venue.devices) {
|
||||
auto NewTask = new VenueDeviceUpgrade(uuid, Venue.info.name, Rules,Logger());
|
||||
// std::cout << "Scheduling config push for " << uuid << std::endl;
|
||||
bool found_slot = false;
|
||||
while (!found_slot) {
|
||||
for (auto &cur_task: Tasks) {
|
||||
if (cur_task.task == nullptr) {
|
||||
cur_task.task = NewTask;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto NewTask = new VenueDeviceUpgrade(uuid, Venue.info.name, Rules, Logger());
|
||||
bool TaskAdded = false;
|
||||
while (!TaskAdded) {
|
||||
if (Pool_.available()) {
|
||||
JobList.push_back(NewTask);
|
||||
Pool_.start(*NewTask);
|
||||
TaskAdded = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
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->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.",
|
||||
JobId(), upgraded_ ,failed_);
|
||||
Logger().debug("Waiting for outstanding upgrade threads to finish.");
|
||||
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 {
|
||||
N.content.details = fmt::format("Venue {} no longer exists.",VenueUUID_);
|
||||
Logger().warning(N.content.details);
|
||||
}
|
||||
|
||||
WebSocketClientNotificationVenueRebootCompletionToUser(UserInfo().email,N);
|
||||
Logger().information(fmt::format("Job {} Completed: {} upgraded, {} failed to upgrade.",
|
||||
JobId(), upgraded_ ,failed_));
|
||||
// std::cout << N.content.details << std::endl;
|
||||
WebSocketClientNotificationVenueUpgradeCompletionToUser(UserInfo().email,N);
|
||||
Logger().information(N.content.details);
|
||||
Utils::SetThreadName("free");
|
||||
|
||||
Complete();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ static json DefaultUCentralSchema = R"(
|
||||
"switch": {
|
||||
"$ref": "#/$defs/switch"
|
||||
},
|
||||
"radios": {
|
||||
"radiosgrep": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/radio"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
169
src/framework/MicroServiceErrorHandler.h
Normal file
169
src/framework/MicroServiceErrorHandler.h
Normal file
@@ -0,0 +1,169 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-09-29.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "Poco/Util/Application.h"
|
||||
#include "Poco/ErrorHandler.h"
|
||||
#include "Poco/Net/NetException.h"
|
||||
#include "Poco/Net/SSLException.h"
|
||||
#include "Poco/JSON/Template.h"
|
||||
#include "Poco/Thread.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class MicroServiceErrorHandler : public Poco::ErrorHandler {
|
||||
public:
|
||||
explicit MicroServiceErrorHandler(Poco::Util::Application &App) : App_(App) {
|
||||
}
|
||||
|
||||
inline void exception(const Poco::Exception & Base) override {
|
||||
try {
|
||||
if(Poco::Thread::current()!= nullptr) {
|
||||
t_name = Poco::Thread::current()->getName();
|
||||
t_id = Poco::Thread::current()->id();
|
||||
} else {
|
||||
t_name = "startup_code";
|
||||
t_id = 0;
|
||||
}
|
||||
|
||||
App_.logger().log(Base);
|
||||
Base.rethrow();
|
||||
|
||||
} catch (const Poco::Net::InvalidCertificateException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::InvalidCertificateException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::InvalidSocketException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::InvalidSocketException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::WebSocketException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::WebSocketException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::ConnectionResetException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::ConnectionResetException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::CertificateValidationException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::CertificateValidationException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::SSLConnectionUnexpectedlyClosedException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::SSLConnectionUnexpectedlyClosedException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::SSLContextException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::SSLContextException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::SSLException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::SSLException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
|
||||
} catch (const Poco::Net::InvalidAddressException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::InvalidAddressException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
|
||||
} catch (const Poco::Net::NetException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::NetException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
|
||||
} catch (const Poco::IOException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::IOException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::RuntimeException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::RuntimeException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::JSON::JSONTemplateException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::JSON::JSONTemplateException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::JSON::JSONException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::JSON::JSONException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::ApplicationException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::ApplicationException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Exception thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (...) {
|
||||
poco_error(App_.logger(), fmt::format("Poco:Generic thr_name={}",t_name, t_id));
|
||||
}
|
||||
}
|
||||
|
||||
inline void exception(const std::exception & E) override {
|
||||
if(Poco::Thread::current()!= nullptr) {
|
||||
t_name = Poco::Thread::current()->getName();
|
||||
t_id = Poco::Thread::current()->id();
|
||||
} else {
|
||||
t_name = "startup_code";
|
||||
t_id = 0;
|
||||
}
|
||||
poco_warning(App_.logger(), fmt::format("std::exception in {}: {} thr_id={}",
|
||||
t_name,E.what(),
|
||||
t_id));
|
||||
}
|
||||
|
||||
inline void exception() override {
|
||||
if(Poco::Thread::current()!= nullptr) {
|
||||
t_name = Poco::Thread::current()->getName();
|
||||
t_id = Poco::Thread::current()->id();
|
||||
} else {
|
||||
t_name = "startup_code";
|
||||
t_id = 0;
|
||||
}
|
||||
poco_warning(App_.logger(), fmt::format("generic exception in {} thr_id={}",
|
||||
t_name, t_id));
|
||||
}
|
||||
private:
|
||||
Poco::Util::Application &App_;
|
||||
std::string t_name;
|
||||
int t_id=0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -33,7 +33,6 @@ namespace OpenWifi {
|
||||
int Start() override {
|
||||
std::lock_guard Guard(Mutex_);
|
||||
|
||||
Logger().setLevel(Poco::Message::PRIO_INFORMATION);
|
||||
Logger().notice("Starting.");
|
||||
std::string DBType = MicroService::instance().ConfigGetString("storage.type");
|
||||
|
||||
|
||||
@@ -146,6 +146,10 @@ namespace OpenWifi {
|
||||
WebSocketClientServer()->SendUserNotification(User,N);
|
||||
}
|
||||
|
||||
/////
|
||||
/////
|
||||
/////
|
||||
|
||||
struct WebSocketNotificationRebootList {
|
||||
std::string title,
|
||||
details,
|
||||
@@ -189,5 +193,58 @@ namespace OpenWifi {
|
||||
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
|
||||
|
||||
|
||||
@@ -196,6 +196,8 @@ namespace OpenWifi::RESTAPI::Errors {
|
||||
static const struct msg InvalidRadiusServerEntry{1142,"RADIUS Server IP address invalid or port missing."};
|
||||
static const struct msg InvalidRadiusServerWeigth{1143,"RADIUS Server IP weight cannot be 0."};
|
||||
|
||||
static const struct msg MaximumRTTYSessionsReached{1144,"Too many RTTY sessions currently active"};
|
||||
static const struct msg DeviceIsAlreadyBusy{1145,"Device is already executing a command. Please try later."};
|
||||
}
|
||||
|
||||
|
||||
@@ -428,6 +430,7 @@ namespace OpenWifi::uCentralProtocol {
|
||||
static const char *RADIUSDATA = "data";
|
||||
static const char *RADIUSACCT = "acct";
|
||||
static const char *RADIUSAUTH = "auth";
|
||||
static const char *RADIUSCOA = "coa";
|
||||
static const char *RADIUSDST = "dst";
|
||||
static const char *IES = "ies";
|
||||
}
|
||||
@@ -444,6 +447,7 @@ namespace OpenWifi::uCentralProtocol::Events {
|
||||
static const char *RECOVERY = "recovery";
|
||||
static const char *TELEMETRY = "telemetry";
|
||||
static const char *DEVICEUPDATE = "deviceupdate";
|
||||
static const char *VENUE_BROADCAST = "venue_broadcast";
|
||||
|
||||
enum EVENT_MSG {
|
||||
ET_UNKNOWN,
|
||||
@@ -456,7 +460,8 @@ namespace OpenWifi::uCentralProtocol::Events {
|
||||
ET_CFGPENDING,
|
||||
ET_RECOVERY,
|
||||
ET_DEVICEUPDATE,
|
||||
ET_TELEMETRY
|
||||
ET_TELEMETRY,
|
||||
ET_VENUEBROADCAST
|
||||
};
|
||||
|
||||
inline EVENT_MSG EventFromString(const std::string & Method) {
|
||||
@@ -480,6 +485,8 @@ namespace OpenWifi::uCentralProtocol::Events {
|
||||
return ET_RECOVERY;
|
||||
else if(strcmp(TELEMETRY,Method.c_str())==0)
|
||||
return ET_TELEMETRY;
|
||||
else if(strcmp(VENUE_BROADCAST,Method.c_str())==0)
|
||||
return ET_VENUEBROADCAST;
|
||||
return ET_UNKNOWN;
|
||||
};
|
||||
}
|
||||
|
||||
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) :
|
||||
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 &DeviceType,
|
||||
const std::string &Locale) {
|
||||
|
||||
ProvObjects::InventoryTag ExistingDevice;
|
||||
auto SerialNumber = Poco::toLower(SerialNumberRaw);
|
||||
if(!GetRecord("serialNumber",SerialNumber,ExistingDevice)) {
|
||||
ProvObjects::InventoryTag NewDevice;
|
||||
uint64_t Now = OpenWifi::Now();
|
||||
@@ -223,7 +224,7 @@ namespace OpenWifi {
|
||||
ProvObjects::DeviceRules Rules;
|
||||
std::string SerialNumber = Utils::IntToSerialNumber(i);
|
||||
if(EvaluateDeviceSerialNumberRules(SerialNumber,Rules)) {
|
||||
if(Rules.rrm=="yes")
|
||||
if(Rules.rrm!="no" && Rules.rrm!="inherit")
|
||||
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')"
|
||||
path="$(echo $url | grep / | cut -d/ -f2-)"
|
||||
export OWPROV=${url}
|
||||
echo "Using ${OWPROV}..."
|
||||
echo "Using PROV=${OWPROV}..."
|
||||
else
|
||||
echo "OWPROV endpoint is not found:"
|
||||
jq < ${result_file}
|
||||
@@ -108,6 +108,33 @@ else
|
||||
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() {
|
||||
curl ${FLAGS} -X DELETE "https://${OWSEC}/api/v1/oauth2/${token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
@@ -507,6 +534,24 @@ getvenuedevices() {
|
||||
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
|
||||
case "$1" in
|
||||
"login") login; echo "You are logged in..." ; logout ;;
|
||||
@@ -555,6 +600,8 @@ case "$1" in
|
||||
"getsubdevs") login; getsubdevs $2; logout;;
|
||||
"listvenues") login; listvenues $2; logout;;
|
||||
"getvenuedevices") login; getvenuedevices $2; logout;;
|
||||
"listrrmalgos") login; listrrmalgos; logout;;
|
||||
"rrmprovider") login; rrmprovider; logout;;
|
||||
*) help ;;
|
||||
esac
|
||||
|
||||
|
||||
Reference in New Issue
Block a user