From 7bd2e0a5b323027a9ce511942bbb48d494b2fc81 Mon Sep 17 00:00:00 2001 From: Sviatoslav Boichuk Date: Thu, 1 Aug 2024 14:36:05 +0300 Subject: [PATCH] Use JSON schema to validate configure commands --- Cargo.lock | 746 ++++++++++++++++++++++++----------- Cargo.toml | 3 + README.md | 90 +++-- run_cgw.sh | 113 +++--- src/cgw_app_args.rs | 116 +++++- src/cgw_connection_server.rs | 112 +++++- src/cgw_device.rs | 16 +- src/cgw_devices_cache.rs | 10 +- src/cgw_errors.rs | 2 + src/cgw_remote_discovery.rs | 8 +- src/cgw_ucentral_parser.rs | 150 +++++++ 11 files changed, 1022 insertions(+), 344 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 051f193..00cc915 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,20 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -168,6 +182,12 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.3.0" @@ -213,8 +233,8 @@ dependencies = [ "bytes", "futures-util", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.29", "itoa", "matchit", "memchr", @@ -223,7 +243,7 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 0.1.2", "tower", "tower-layer", "tower-service", @@ -239,7 +259,7 @@ dependencies = [ "bytes", "futures-util", "http 0.2.12", - "http-body", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -312,6 +332,21 @@ dependencies = [ "which", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -339,6 +374,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + [[package]] name = "byteorder" version = "1.5.0" @@ -402,6 +443,46 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "4.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + [[package]] name = "cmake" version = "0.1.50" @@ -638,6 +719,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +[[package]] +name = "fancy-regex" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + [[package]] name = "fastrand" version = "2.1.0" @@ -666,6 +758,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -675,6 +782,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fraction" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f158e3ff0a1b334408dc9fb811cd99b446986f4d8b741bb08f9df1604085ae7" +dependencies = [ + "lazy_static", + "num", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -787,8 +904,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -822,6 +941,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.2.6", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -927,6 +1065,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.9.3" @@ -955,9 +1116,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", - "http-body", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -970,168 +1131,121 @@ dependencies = [ ] [[package]] -name = "hyper-timeout" -version = "0.4.1" +name = "hyper" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ - "hyper", + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "itoa", "pin-project-lite", + "smallvec", "tokio", - "tokio-io-timeout", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", + "want", ] [[package]] -name = "icu_collections" -version = "1.5.0" +name = "hyper-rustls" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "rustls 0.23.10", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", ] [[package]] -name = "icu_locid_transform" -version = "1.5.0" +name = "hyper-timeout" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", + "hyper 0.14.29", + "pin-project-lite", + "tokio", + "tokio-io-timeout", ] [[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" +name = "hyper-tls" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", + "bytes", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", ] [[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.0" +name = "hyper-util" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", ] [[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" +name = "iana-time-zone" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", ] [[package]] -name = "icu_provider_macros" -version = "1.5.0" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", + "cc", ] [[package]] name = "idna" -version = "1.0.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "icu_normalizer", - "icu_properties", - "smallvec", - "utf8_iter", + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -1154,12 +1268,27 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "is_terminal_polyfill" version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +[[package]] +name = "iso8601" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" +dependencies = [ + "nom", +] + [[package]] name = "itertools" version = "0.12.1" @@ -1193,6 +1322,36 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonschema" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0afd06142c9bcb03f4a8787c77897a87b6be9c4918f1946c33caa714c27578" +dependencies = [ + "ahash", + "anyhow", + "base64 0.22.1", + "bytecount", + "clap", + "fancy-regex", + "fraction", + "getrandom", + "iso8601", + "itoa", + "memchr", + "num-cmp", + "once_cell", + "parking_lot", + "percent-encoding", + "regex", + "reqwest", + "serde", + "serde_json", + "time", + "url", + "uuid", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1239,12 +1398,6 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" -[[package]] -name = "litemap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" - [[package]] name = "lock_api" version = "0.4.12" @@ -1355,6 +1508,23 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nom" version = "7.1.3" @@ -1365,6 +1535,20 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.5" @@ -1375,6 +1559,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1390,6 +1589,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1454,12 +1675,50 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -1883,6 +2142,50 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +[[package]] +name = "reqwest" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.17.8" @@ -2230,12 +2533,6 @@ dependencies = [ "der", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "stringprep" version = "0.1.5" @@ -2247,6 +2544,12 @@ dependencies = [ "unicode-properties", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.5.0" @@ -2281,6 +2584,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + [[package]] name = "synstructure" version = "0.13.1" @@ -2292,6 +2601,27 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.10.1" @@ -2355,16 +2685,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -2420,6 +2740,16 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-pg-mapper" version = "0.2.0" @@ -2568,10 +2898,10 @@ dependencies = [ "axum", "base64 0.21.7", "bytes", - "h2", + "h2 0.3.26", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.29", "hyper-timeout", "percent-encoding", "pin-project", @@ -2724,6 +3054,7 @@ dependencies = [ "futures-channel", "futures-executor", "futures-util", + "jsonschema", "lazy_static", "log", "petgraph", @@ -2732,6 +3063,7 @@ dependencies = [ "prost-build", "rdkafka", "redis", + "reqwest", "rlimit", "rustls-pemfile", "rustls-pki-types", @@ -2747,6 +3079,7 @@ dependencies = [ "tonic", "tonic-build", "tungstenite 0.23.0", + "url", "uuid", "warp", "x509-parser", @@ -2796,9 +3129,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -2811,18 +3144,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "utf8parse" version = "0.2.2" @@ -2870,7 +3191,7 @@ dependencies = [ "futures-util", "headers", "http 0.2.12", - "hyper", + "hyper 0.14.29", "log", "mime", "mime_guess", @@ -2925,6 +3246,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -3145,16 +3478,14 @@ dependencies = [ ] [[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" +name = "winreg" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] [[package]] name = "x509-certificate" @@ -3193,48 +3524,23 @@ dependencies = [ ] [[package]] -name = "yoke" -version = "0.7.4" +name = "zerocopy" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", + "zerocopy-derive", ] [[package]] -name = "yoke-derive" -version = "0.7.4" +name = "zerocopy-derive" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", - "synstructure", ] [[package]] @@ -3256,25 +3562,3 @@ dependencies = [ "quote", "syn 2.0.66", ] - -[[package]] -name = "zerovec" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] diff --git a/Cargo.toml b/Cargo.toml index a2f67e9..0f6094d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,9 @@ rustls-pki-types = { version = "1.7.0" } x509-parser = { version = "0.16.0" } chrono = { version = "0.4.38" } derive_more = { version = "0.99.17" } +reqwest = { version = "0.12.5", features = ["json"] } +jsonschema = { version = "0.18.0" } +url = { version = "2.5.2" } [build-dependencies] tonic-build = "0.11.0" diff --git a/README.md b/README.md index 7dd8b53..7634e95 100644 --- a/README.md +++ b/README.md @@ -55,70 +55,76 @@ Running application with default arguments might not be desired behavior. And thus the run script utilizes the following list of *enviroment* variables that you can define before running it to alternate behavior of the app. The following list is a list of enviroment variables you can define to configure cgw-app behavior in certain way: ``` -CGW_ID - Shard ID -CGW_GRPC_LISTENING_IP - IP to bind gRPC server to (listens for gRPC requests from remote CGWs) -CGW_GRPC_LISTENING_PORT - Port to bind gRPC server to (listens for gRPC requests from remote CGWs) -CGW_GRPC_PUBLIC_HOST - IP or hostname for Redis record (remote CGWs will connect to this particular shard through provided host record; - it's up to deployment config whether remote CGW#1 will be able to access this CGW#0, for example, through provided hostname/IP) -CGW_GRPC_PUBLIC_PORT - PORT for Redis record -CGW_WSS_IP - IP to bind websocket server to (listens for incoming WSS connections from underlying devices - infrastructures) -CGW_WSS_PORT - PORT to bind WSS server to -CGW_WSS_CAS - Web socket CAS certificate file name -CGW_WSS_CERT - Web socket server certificate file name -CGW_WSS_KEY - Web socket server private key file name -CGW_KAFKA_HOST - IP or hostname of remote KAFKA server to connect to (NB API) -CGW_KAFKA_PORT - PORT of remote KAFKA server to connect to -CGW_DB_HOST - IP or hostname of remote database server to connect to -CGW_DB_PORT - PORT of remote database server to connect to -CGW_DB_USER - PSQL DB username (credentials) to use upon connect to DB -CGW_DB_PASS - PSQL DB password (credentials) to use upon connect to DB -CGW_DB_TLS - Utilize TLS connection with DB server -CGW_REDIS_HOST - IP or hostname of remote redis-db server to connect to -CGW_REDIS_PORT - PORT of remote redis-db server to connect to -CGW_REDIS_USERNAME - REDIS username (credentials) to use upon connect to -CGW_REDIS_PASSWORD - REDIS password (credentials) to use upon connect to -CGW_REDIS_TLS - Utilize TLS connection with REDIS server -CGW_LOG_LEVEL - Log level to start CGW application with (debug, info) -CGW_METRICS_PORT - PORT of metrics to connect to -CGW_CERTS_PATH - Path to certificates located on host machine -CGW_ALLOW_CERT_MISMATCH - Allow client certificate CN and device MAC address mismatch (used for OWLS) -CGW_NB_INFRA_CERTS_DIR - Path to NB infrastructure (Redis, PostgreSQL) certificates located on host machine -CGW_NB_INFRA_TLS - Utilize TLS connection with NB infrastructure (Redis, PostgreSQL) - If set enabled - the CGW_DB_TLS and CGW_REDIS_TLS values will be ignored and - the TLS connection will be used for Redis and PostgreSQL connection +CGW_ID - Shard ID +CGW_GRPC_LISTENING_IP - IP to bind gRPC server to (listens for gRPC requests from remote CGWs) +CGW_GRPC_LISTENING_PORT - Port to bind gRPC server to (listens for gRPC requests from remote CGWs) +CGW_GRPC_PUBLIC_HOST - IP or hostname for Redis record (remote CGWs will connect to this particular shard through provided host record; + it's up to deployment config whether remote CGW#1 will be able to access this CGW#0, for example, through provided hostname/IP) +CGW_GRPC_PUBLIC_PORT - PORT for Redis record +CGW_WSS_IP - IP to bind websocket server to (listens for incoming WSS connections from underlying devices - infrastructures) +CGW_WSS_PORT - PORT to bind WSS server to +CGW_WSS_CAS - Web socket CAS certificate file name +CGW_WSS_CERT - Web socket server certificate file name +CGW_WSS_KEY - Web socket server private key file name +CGW_KAFKA_HOST - IP or hostname of remote KAFKA server to connect to (NB API) +CGW_KAFKA_PORT - PORT of remote KAFKA server to connect to +CGW_DB_HOST - IP or hostname of remote database server to connect to +CGW_DB_PORT - PORT of remote database server to connect to +CGW_DB_USER - PSQL DB username (credentials) to use upon connect to DB +CGW_DB_PASS - PSQL DB password (credentials) to use upon connect to DB +CGW_DB_TLS - Utilize TLS connection with DB server +CGW_REDIS_HOST - IP or hostname of remote redis-db server to connect to +CGW_REDIS_PORT - PORT of remote redis-db server to connect to +CGW_REDIS_USERNAME - REDIS username (credentials) to use upon connect to +CGW_REDIS_PASSWORD - REDIS password (credentials) to use upon connect to +CGW_REDIS_TLS - Utilize TLS connection with REDIS server +CGW_LOG_LEVEL - Log level to start CGW application with (debug, info) +CGW_METRICS_PORT - PORT of metrics to connect to +CGW_CERTS_PATH - Path to certificates located on host machine +CGW_ALLOW_CERT_MISMATCH - Allow client certificate CN and device MAC address mismatch (used for OWLS) +CGW_NB_INFRA_CERTS_DIR - Path to NB infrastructure (Redis, PostgreSQL) certificates located on host machine +CGW_NB_INFRA_TLS - Utilize TLS connection with NB infrastructure (Redis, PostgreSQL) + If set enabled - the CGW_DB_TLS and CGW_REDIS_TLS values will be ignored and + the TLS connection will be used for Redis and PostgreSQL connection +CGW_UCENTRAL_AP_DATAMODEL_URI - Path to AP Config message JSON Validation schema: + 1. URI in format: "http[s]://", e.g https://somewhere.com/schema.json + 2. Path to local file: "", e.g /etc/host/schema.json +CGW_UCENTRAL_SWITCH_DATAMODEL_URI - Path to Switch Config message JSON Validation schema ``` Example of properly configured list of env variables to start CGW: ```console $ export | grep CGW -declare -x CGW_DB_HOST="localhost" # PSQL server is located at the local host +declare -x CGW_DB_HOST="localhost" declare -x CGW_DB_PORT="5432" -declare -x CGW_DB_USERNAME="cgw" # PSQL login credentials (username) default 'cgw' will be used -declare -x CGW_DB_PASS="123" # PSQL login credentials (password) default '123' will be used +declare -x CGW_DB_USERNAME="cgw" +declare -x CGW_DB_PASS="123" declare -x CGW_DB_TLS="no" -declare -x CGW_GRPC_LISTENING_IP="127.0.0.1" # Local default subnet is 127.0.0.1/24 +declare -x CGW_GRPC_LISTENING_IP="127.0.0.1" declare -x CGW_GRPC_LISTENING_PORT="50051" declare -x CGW_GRPC_PUBLIC_HOST="localhost" declare -x CGW_GRPC_PUBLIC_PORT="50051" declare -x CGW_ID="0" -declare -x CGW_KAFKA_HOST="localhost" # Kafka is located at the local host +declare -x CGW_KAFKA_HOST="localhost" declare -x CGW_KAFKA_PORT="9092" declare -x CGW_LOG_LEVEL="debug" -declare -x CGW_REDIS_HOST="localhost" # Redis server can be found at the local host +declare -x CGW_REDIS_HOST="localhost" declare -x CGW_REDIS_PORT="6379" -declare -x CGW_REDIS_USERNAME="cgw" # REDIS login credentials (username) - optional -declare -x CGW_REDIS_PASSWORD="123" # REDIS login credentials (password) - optional +declare -x CGW_REDIS_USERNAME="cgw" +declare -x CGW_REDIS_PASSWORD="123" declare -x CGW_REDIS_TLS="no" declare -x CGW_METRICS_PORT="8080" -declare -x CGW_WSS_IP="0.0.0.0" # Accept WSS connections at all interfaces / subnets +declare -x CGW_WSS_IP="0.0.0.0" declare -x CGW_WSS_PORT="15002" declare -x CGW_WSS_CAS="cas.pem" declare -x CGW_WSS_CERT="cert.pem" declare -x CGW_WSS_KEY="key.pem" -declare -x CGW_CERTS_PATH="/etc/ssl/certs" # Path to certificates located on host machine -declare -x CGW_ALLOW_CERT_MISMATCH="no" # Allow client certificate CN and device MAC address mismatch +declare -x CGW_CERTS_PATH="/etc/ssl/certs" +declare -x CGW_ALLOW_CERT_MISMATCH="no" declare -x CGW_NB_INFRA_CERTS_PATH="/etc/nb_infra_certs" declare -x CGW_NB_INFRA_TLS="no" +declare -x CGW_UCENTRAL_AP_DATAMODEL_URI="https://raw.githubusercontent.com/Telecominfraproject/wlan-ucentral-schema/main/ucentral.schema.json" +declare -x CGW_UCENTRAL_SWITCH_DATAMODEL_URI="https://raw.githubusercontent.com/Telecominfraproject/ols-ucentral-schema/main/ucentral.schema.json" ``` # Certificates The CGW uses two different sets of certificate configuration: diff --git a/run_cgw.sh b/run_cgw.sh index c75aee3..b528fc6 100755 --- a/run_cgw.sh +++ b/run_cgw.sh @@ -43,6 +43,9 @@ DEFAULT_NB_INFRA_TLS="no" DEFAULT_ALLOW_CERT_MISMATCH="no" +DEFAULT_UCENTRAL_AP_DATAMODEL_URI="https://raw.githubusercontent.com/Telecominfraproject/wlan-ucentral-schema/main/ucentral.schema.json" +DEFAULT_UCENTRAL_SWITCH_DATAMODEL_URI="https://raw.githubusercontent.com/Telecominfraproject/ols-ucentral-schema/main/ucentral.schema.json" + export CGW_LOG_LEVEL="${CGW_LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" export CGW_ID="${CGW_ID:-$DEFAULT_ID}" export CGW_WSS_IP="${CGW_WSS_IP:-$DEFAULT_WSS_IP}" @@ -73,6 +76,8 @@ export CGW_CERTS_PATH="${CGW_CERTS_PATH:-$DEFAULT_CERTS_PATH}" export CGW_ALLOW_CERT_MISMATCH="${CGW_ALLOW_CERT_MISMATCH:-$DEFAULT_ALLOW_CERT_MISMATCH}" export CGW_NB_INFRA_CERTS_PATH="${CGW_NB_INFRA_CERTS_PATH:-$DEFAULT_CERTS_PATH}" export CGW_NB_INFRA_TLS="${CGW_NB_INFRA_TLS:-$DEFAULT_NB_INFRA_TLS}" +export CGW_UCENTRAL_AP_DATAMODEL_URI="${CGW_UCENTRAL_AP_DATAMODEL_URI:-$DEFAULT_UCENTRAL_AP_DATAMODEL_URI}" +export CGW_UCENTRAL_SWITCH_DATAMODEL_URI="${CGW_UCENTRAL_SWITCH_DATAMODEL_URI:-$DEFAULT_UCENTRAL_SWITCH_DATAMODEL_URI}" if [ -z "${!CGW_REDIS_USERNAME}" ]; then export CGW_REDIS_USERNAME="${CGW_REDIS_USERNAME}" @@ -83,61 +88,65 @@ if [ -z "${!CGW_REDIS_PASSWORD}" ]; then fi echo "Starting CGW..." -echo "CGW LOG LEVEL : $CGW_LOG_LEVEL" -echo "CGW ID : $CGW_ID" -echo "CGW WSS THREAD NUM : $DEFAULT_WSS_THREAD_NUM" -echo "CGW WSS IP/PORT : $CGW_WSS_IP:$CGW_WSS_PORT" -echo "CGW WSS CAS : $CGW_WSS_CAS" -echo "CGW WSS CERT : $CGW_WSS_CERT" -echo "CGW WSS KEY : $CGW_WSS_KEY" -echo "CGW GRPC PUBLIC HOST/PORT : $CGW_GRPC_PUBLIC_HOST:$CGW_GRPC_PUBLIC_PORT" -echo "CGW GRPC LISTENING IP/PORT : $CGW_GRPC_LISTENING_IP:$CGW_GRPC_LISTENING_PORT" -echo "CGW KAFKA HOST/PORT : $CGW_KAFKA_HOST:$CGW_KAFKA_PORT" -echo "CGW KAFKA TOPIC : $CGW_KAFKA_CONSUME_TOPIC:$CGW_KAFKA_PRODUCE_TOPIC" -echo "CGW DB NAME : $CGW_DB_NAME" -echo "CGW DB HOST/PORT : $CGW_DB_HOST:$CGW_DB_PORT" -echo "CGW DB TLS : $CGW_DB_TLS" -echo "CGW REDIS HOST/PORT : $CGW_REDIS_HOST:$CGW_REDIS_PORT" -echo "CGW REDIS TLS : $CGW_REDIS_TLS" -echo "CGW METRICS PORT : $CGW_METRICS_PORT" -echo "CGW CERTS PATH : $CGW_CERTS_PATH" -echo "CGW ALLOW CERT MISMATCH : $CGW_ALLOW_CERT_MISMATCH" -echo "CGW NB INFRA CERTS PATH : $CGW_NB_INFRA_CERTS_PATH" -echo "CGW NB INFRA TLS : $CGW_NB_INFRA_TLS" +echo "CGW LOG LEVEL : $CGW_LOG_LEVEL" +echo "CGW ID : $CGW_ID" +echo "CGW WSS THREAD NUM : $DEFAULT_WSS_THREAD_NUM" +echo "CGW WSS IP/PORT : $CGW_WSS_IP:$CGW_WSS_PORT" +echo "CGW WSS CAS : $CGW_WSS_CAS" +echo "CGW WSS CERT : $CGW_WSS_CERT" +echo "CGW WSS KEY : $CGW_WSS_KEY" +echo "CGW GRPC PUBLIC HOST/PORT : $CGW_GRPC_PUBLIC_HOST:$CGW_GRPC_PUBLIC_PORT" +echo "CGW GRPC LISTENING IP/PORT : $CGW_GRPC_LISTENING_IP:$CGW_GRPC_LISTENING_PORT" +echo "CGW KAFKA HOST/PORT : $CGW_KAFKA_HOST:$CGW_KAFKA_PORT" +echo "CGW KAFKA TOPIC : $CGW_KAFKA_CONSUME_TOPIC:$CGW_KAFKA_PRODUCE_TOPIC" +echo "CGW DB NAME : $CGW_DB_NAME" +echo "CGW DB HOST/PORT : $CGW_DB_HOST:$CGW_DB_PORT" +echo "CGW DB TLS : $CGW_DB_TLS" +echo "CGW REDIS HOST/PORT : $CGW_REDIS_HOST:$CGW_REDIS_PORT" +echo "CGW REDIS TLS : $CGW_REDIS_TLS" +echo "CGW METRICS PORT : $CGW_METRICS_PORT" +echo "CGW CERTS PATH : $CGW_CERTS_PATH" +echo "CGW ALLOW CERT MISMATCH : $CGW_ALLOW_CERT_MISMATCH" +echo "CGW NB INFRA CERTS PATH : $CGW_NB_INFRA_CERTS_PATH" +echo "CGW NB INFRA TLS : $CGW_NB_INFRA_TLS" +echo "CGW UCENTRAL AP DATAMODEL URI : $CGW_UCENTRAL_AP_DATAMODEL_URI" +echo "CGW UCENTRAL SWITCH DATAMODEL URI : $CGW_UCENTRAL_SWITCH_DATAMODEL_URI" docker run \ --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \ -v $CGW_CERTS_PATH:$CONTAINTER_CERTS_VOLUME \ -v $CGW_NB_INFRA_CERTS_PATH:$CONTAINTER_NB_INFRA_CERTS_VOLUME \ - -e CGW_LOG_LEVEL \ - -e CGW_ID \ - -e CGW_WSS_IP \ - -e CGW_WSS_PORT \ - -e DEFAULT_WSS_THREAD_NUM \ - -e CGW_WSS_CAS \ - -e CGW_WSS_CERT \ - -e CGW_WSS_KEY \ - -e CGW_GRPC_LISTENING_IP \ - -e CGW_GRPC_LISTENING_PORT \ - -e CGW_GRPC_PUBLIC_HOST \ - -e CGW_GRPC_PUBLIC_PORT \ - -e CGW_KAFKA_HOST \ - -e CGW_KAFKA_PORT \ - -e CGW_KAFKA_CONSUME_TOPIC \ - -e CGW_KAFKA_PRODUCE_TOPIC \ - -e CGW_DB_NAME \ - -e CGW_DB_HOST \ - -e CGW_DB_PORT \ - -e CGW_DB_USERNAME \ - -e CGW_DB_PASSWORD \ - -e CGW_DB_TLS \ - -e CGW_REDIS_HOST \ - -e CGW_REDIS_PORT \ - -e CGW_REDIS_USERNAME \ - -e CGW_REDIS_PASSWORD \ - -e CGW_REDIS_TLS \ - -e CGW_FEATURE_TOPOMAP_ENABLE \ - -e CGW_METRICS_PORT \ - -e CGW_ALLOW_CERT_MISMATCH \ - -e CGW_NB_INFRA_TLS \ + -e CGW_LOG_LEVEL \ + -e CGW_ID \ + -e CGW_WSS_IP \ + -e CGW_WSS_PORT \ + -e DEFAULT_WSS_THREAD_NUM \ + -e CGW_WSS_CAS \ + -e CGW_WSS_CERT \ + -e CGW_WSS_KEY \ + -e CGW_GRPC_LISTENING_IP \ + -e CGW_GRPC_LISTENING_PORT \ + -e CGW_GRPC_PUBLIC_HOST \ + -e CGW_GRPC_PUBLIC_PORT \ + -e CGW_KAFKA_HOST \ + -e CGW_KAFKA_PORT \ + -e CGW_KAFKA_CONSUME_TOPIC \ + -e CGW_KAFKA_PRODUCE_TOPIC \ + -e CGW_DB_NAME \ + -e CGW_DB_HOST \ + -e CGW_DB_PORT \ + -e CGW_DB_USERNAME \ + -e CGW_DB_PASSWORD \ + -e CGW_DB_TLS \ + -e CGW_REDIS_HOST \ + -e CGW_REDIS_PORT \ + -e CGW_REDIS_USERNAME \ + -e CGW_REDIS_PASSWORD \ + -e CGW_REDIS_TLS \ + -e CGW_FEATURE_TOPOMAP_ENABLE \ + -e CGW_METRICS_PORT \ + -e CGW_ALLOW_CERT_MISMATCH \ + -e CGW_NB_INFRA_TLS \ + -e CGW_UCENTRAL_AP_DATAMODEL_URI \ + -e CGW_UCENTRAL_SWITCH_DATAMODEL_URI \ -d -t --network=host --name $2 $1 ucentral-cgw diff --git a/src/cgw_app_args.rs b/src/cgw_app_args.rs index 3875bf1..f09748f 100644 --- a/src/cgw_app_args.rs +++ b/src/cgw_app_args.rs @@ -1,4 +1,11 @@ -use std::{env, net::Ipv4Addr, str::FromStr}; +use std::{ + env, + net::Ipv4Addr, + path::{Path, PathBuf}, + str::FromStr, +}; + +use url::Url; use crate::{ cgw_errors::{Error, Result}, @@ -34,6 +41,8 @@ const CGW_DEFAULT_ALLOW_CERT_MISMATCH: &str = "no"; const CGW_DEFAULT_METRICS_PORT: u16 = 8080; const CGW_DEFAULT_TOPOMAP_STATE: bool = false; const CGW_DEFAULT_NB_INFRA_TLS: &str = "no"; +const CGW_DEFAULT_UCENTRAL_AP_DATAMODEL_URI: &str = "https://raw.githubusercontent.com/Telecominfraproject/wlan-ucentral-schema/main/ucentral.schema.json"; +const CGW_DEFAULT_UCENTRAL_SWITCH_DATAMODEL_URI: &str = "https://raw.githubusercontent.com/Telecominfraproject/ols-ucentral-schema/main/ucentral.schema.json"; pub struct CGWWSSArgs { /// Number of thread in a threadpool dedicated for handling secure websocket connections @@ -410,6 +419,107 @@ impl CGWMetricsArgs { } } +#[derive(Clone, Debug)] +pub enum CGWValionSchemaRef { + SchemaUri(Url), + SchemaPath(PathBuf), +} + +#[derive(Clone)] +pub struct CGWValidationSchemaArgs { + // URI to AP data model schema + pub ap_schema_uri: CGWValionSchemaRef, + // URI to Switch data model schema + pub switch_schema_uri: CGWValionSchemaRef, +} + +impl CGWValidationSchemaArgs { + fn parse() -> Result { + let ap_schema_uri: CGWValionSchemaRef = match env::var("CGW_UCENTRAL_AP_DATAMODEL_URI") { + Ok(uri) => { + // CGW_UCENTRAL_AP_DATAMODEL_URI is set + if Path::new(&uri).exists() { + // CGW_UCENTRAL_AP_DATAMODEL_URI - is path to local file and file exist + match PathBuf::from_str(&uri) { + Ok(path) => CGWValionSchemaRef::SchemaPath(path), + Err(e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_UCENTRAL_AP_DATAMODEL_URI! Invalid URI: {}, err: {}", + uri, e + ))); + } + } + } else { + match Url::parse(&uri) { + Ok(url) => CGWValionSchemaRef::SchemaUri(url), + Err(e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_UCENTRAL_AP_DATAMODEL_URI! Invalid URI: {}, err: {}", + uri, e + ))); + } + } + } + } + // Environment variable was not set - use default + Err(_e) => match Url::parse(CGW_DEFAULT_UCENTRAL_AP_DATAMODEL_URI) { + // CGW_UCENTRAL_AP_DATAMODEL_URI was not set - try to use default + Ok(uri) => CGWValionSchemaRef::SchemaUri(uri), + Err(e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse default CGW_UCENTRAL_AP_DATAMODEL_URI! Invalid URI: {}, err: {}", + CGW_DEFAULT_UCENTRAL_AP_DATAMODEL_URI, e + ))); + } + }, + }; + + let switch_schema_uri: CGWValionSchemaRef = match env::var( + "CGW_UCENTRAL_SWITCH_DATAMODEL_URI", + ) { + Ok(uri) => { + // CGW_UCENTRAL_SWITCH_DATAMODEL_URI is set + if Path::new(&uri).exists() { + match PathBuf::from_str(&uri) { + Ok(path) => CGWValionSchemaRef::SchemaPath(path), + Err(e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_UCENTRAL_SWITCH_DATAMODEL_URI! Invalid URI: {}, err: {}", + uri, e + ))); + } + } + } else { + match Url::parse(&uri) { + Ok(url) => CGWValionSchemaRef::SchemaUri(url), + Err(e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse CGW_UCENTRAL_SWITCH_DATAMODEL_URI! Invalid URI: {}, err: {}", + uri, e + ))); + } + } + } + } + // Environment variable was not set - use default + Err(_e) => match Url::from_str(CGW_DEFAULT_UCENTRAL_SWITCH_DATAMODEL_URI) { + Ok(url) => CGWValionSchemaRef::SchemaUri(url), + Err(e) => { + return Err(Error::AppArgsParser(format!( + "Failed to parse default CGW_UCENTRAL_SWITCH_DATAMODEL_URI! Invalid value: {}, err: {}", + CGW_DEFAULT_UCENTRAL_SWITCH_DATAMODEL_URI, e + ))); + } + }, + }; + + Ok(CGWValidationSchemaArgs { + ap_schema_uri, + switch_schema_uri, + }) + } +} + pub struct AppArgs { /// Loglevel of application pub log_level: AppCoreLogLevel, @@ -434,6 +544,8 @@ pub struct AppArgs { pub redis_args: CGWRedisArgs, pub metrics_args: CGWMetricsArgs, + + pub validation_schema: CGWValidationSchemaArgs, } impl AppArgs { @@ -479,6 +591,7 @@ impl AppArgs { let mut db_args = CGWDBArgs::parse()?; let mut redis_args = CGWRedisArgs::parse()?; let metrics_args = CGWMetricsArgs::parse()?; + let validation_schema = CGWValidationSchemaArgs::parse()?; if nb_infra_tls { redis_args.redis_tls = nb_infra_tls; @@ -496,6 +609,7 @@ impl AppArgs { db_args, redis_args, metrics_args, + validation_schema, }) } } diff --git a/src/cgw_connection_server.rs b/src/cgw_connection_server.rs index 4216b76..fe65fb9 100644 --- a/src/cgw_connection_server.rs +++ b/src/cgw_connection_server.rs @@ -1,5 +1,5 @@ use crate::cgw_device::{ - cgw_detect_device_chages, CGWDevice, CGWDeviceCapabilities, CGWDeviceState, + cgw_detect_device_chages, CGWDevice, CGWDeviceCapabilities, CGWDeviceState, CGWDeviceType, }; use crate::cgw_nb_api_listener::{ cgw_construct_device_capabilities_changed_msg, cgw_construct_device_enqueue_response, @@ -13,7 +13,9 @@ use crate::cgw_tls::cgw_tls_get_cn_from_stream; use crate::cgw_ucentral_messages_queue_manager::{ CGWUCentralMessagesQueueItem, CGW_MESSAGES_QUEUE, }; -use crate::cgw_ucentral_parser::cgw_ucentral_parse_command_message; +use crate::cgw_ucentral_parser::{ + cgw_ucentral_parse_command_message, CGWUCentralCommandType, CGWUCentralConfigValidators, +}; use crate::cgw_ucentral_topology_map::CGWUCentralTopologyMap; use crate::AppArgs; @@ -31,6 +33,7 @@ use crate::{ use crate::cgw_errors::{Error, Result}; +use std::str::FromStr; use std::{collections::HashMap, net::SocketAddr, sync::Arc}; use tokio::{ net::TcpStream, @@ -152,6 +155,10 @@ pub struct CGWConnectionServer { // Key: device MAC, Value: Device devices_cache: Arc>, + // UCentral command messages validators + // for access points and switches + config_validator: CGWUCentralConfigValidators, + // User-supplied arguments can disable state/realtime events // processing by underlying connections processors. pub feature_topomap_enabled: bool, @@ -332,6 +339,21 @@ impl CGWConnectionServer { } }; + let config_validator = + match CGWUCentralConfigValidators::new(app_args.validation_schema.clone()) { + Ok(validator) => validator, + Err(e) => { + error!( + "Can't create CGW Connection server: Config validator create failed: {}", + e.to_string(), + ); + return Err(Error::ConnectionServer(format!( + "Can't create CGW Connection server: Config validator create failed: {}", + e.to_string(), + ))); + } + }; + let server = Arc::new(CGWConnectionServer { allow_mismatch: app_args.wss_args.allow_mismatch, local_cgw_id: app_args.cgw_id, @@ -348,6 +370,7 @@ impl CGWConnectionServer { mbox_relay_msg_runtime_handle: relay_msg_mbox_runtime_handle, devices_cache: Arc::new(RwLock::new(CGWDevicesCache::new())), feature_topomap_enabled: app_args.feature_topomap_enabled, + config_validator, }); let server_clone = server.clone(); @@ -1104,16 +1127,64 @@ impl CGWConnectionServer { // 1. Parse message from NB if let Ok(parsed_cmd) = cgw_ucentral_parse_command_message(&msg.clone()) { - let queue_msg: CGWUCentralMessagesQueueItem = - CGWUCentralMessagesQueueItem::new(parsed_cmd, msg); - - // 2. Add message to queue - { - let queue_lock = CGW_MESSAGES_QUEUE.read().await; - let _ = - queue_lock.push_device_message(device_mac, queue_msg).await; + if parsed_cmd.cmd_type == CGWUCentralCommandType::Configure { + // 2. Get device type + let devices_cache = self.devices_cache.read().await; + match devices_cache.get_device(&device_mac) { + Some(dev) => { + let device_type = dev.get_device_type(); + match self + .config_validator + .validate_config_message(&msg, device_type) + { + Ok(()) => { + let queue_msg: CGWUCentralMessagesQueueItem = + CGWUCentralMessagesQueueItem::new( + parsed_cmd, msg, + ); + + // 3. Add message to queue + { + let queue_lock = + CGW_MESSAGES_QUEUE.read().await; + let _ = queue_lock + .push_device_message( + device_mac, queue_msg, + ) + .await; + } + } + Err(e) => { + error!("Failed to validate config message! Invalid configure message for device: {device_mac}"); + if let Ok(resp) = cgw_construct_device_enqueue_response( + uuid, + false, + Some(format!("Failed to validate config message! Invalid configure message for device: {device_mac}, uuid {uuid}\n{}", e.to_string())), + ) { + self.enqueue_mbox_message_from_cgw_to_nb_api(gid, resp); + } else { + error!("Failed to construct device_enqueue message"); + } + continue; + } + } + } + None => { + error!("Failed to validate config message! Device {device_mac} does not exist in cache!"); + continue; + } + } } } else { + if let Ok(resp) = cgw_construct_device_enqueue_response( + uuid, + false, + Some(format!("Failed to parse command message to device: {device_mac}, uuid {uuid}")), + ) { + self.enqueue_mbox_message_from_cgw_to_nb_api(gid, resp); + } else { + error!("Failed to construct device_enqueue message"); + } error!("Failed to parse UCentral command"); } } @@ -1259,14 +1330,23 @@ impl CGWConnectionServer { connmap_w_lock.len() + 1 ); + let device_type = match CGWDeviceType::from_str(caps.platform.as_str()) { + Ok(dev_type) => dev_type, + Err(_e) => { + error!("Failed to parse device {device_mac} type!"); + CGWDeviceType::CGWDeviceUnknown + } + }; + // Received new connection - check if infra exist in cache // If exists - it already should have assigned group // If not - simply add to cache - set gid == 0, devices should't remain in DB let mut devices_cache = self.devices_cache.write().await; let mut device_group_id: i32 = 0; - if let Some(device) = devices_cache.get_device(&device_mac) { + if let Some(device) = devices_cache.get_device_mut(&device_mac) { device_group_id = device.get_device_group_id(); device.set_device_state(CGWDeviceState::CGWDeviceConnected); + device.set_device_type(device_type); let group_id = device.get_device_group_id(); if let Some(group_owner_id) = self @@ -1356,7 +1436,13 @@ impl CGWConnectionServer { devices_cache.add_device( &device_mac, - &CGWDevice::new(CGWDeviceState::CGWDeviceConnected, 0, false, caps), + &CGWDevice::new( + device_type, + CGWDeviceState::CGWDeviceConnected, + 0, + false, + caps, + ), ); if let Ok(resp) = cgw_construct_unassigned_infra_connection_msg( @@ -1400,7 +1486,7 @@ impl CGWConnectionServer { connmap_w_lock.remove(&device_mac); let mut devices_cache = self.devices_cache.write().await; - if let Some(device) = devices_cache.get_device(&device_mac) { + if let Some(device) = devices_cache.get_device_mut(&device_mac) { if device.get_device_remains_in_db() { device.set_device_state(CGWDeviceState::CGWDeviceDisconnected); } else { diff --git a/src/cgw_device.rs b/src/cgw_device.rs index 5906364..f8787dc 100644 --- a/src/cgw_device.rs +++ b/src/cgw_device.rs @@ -3,10 +3,12 @@ use std::str::FromStr; use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)] pub enum CGWDeviceType { CGWDeviceAP, CGWDeviceSwitch, + #[default] + CGWDeviceUnknown, } impl FromStr for CGWDeviceType { @@ -16,6 +18,7 @@ impl FromStr for CGWDeviceType { match s { "ap" => Ok(CGWDeviceType::CGWDeviceAP), "switch" => Ok(CGWDeviceType::CGWDeviceSwitch), + "unknown" => Ok(CGWDeviceType::CGWDeviceUnknown), _ => Err(()), } } @@ -56,6 +59,7 @@ impl CGWDeviceCapabilities { #[derive(Clone, Default, Deserialize, Serialize)] pub struct CGWDevice { + dev_type: CGWDeviceType, state: CGWDeviceState, group_id: i32, remains_in_db: bool, @@ -64,12 +68,14 @@ pub struct CGWDevice { impl CGWDevice { pub fn new( + dev_type: CGWDeviceType, state: CGWDeviceState, group_id: i32, remains_in_db: bool, capabilities: CGWDeviceCapabilities, ) -> CGWDevice { CGWDevice { + dev_type, state, group_id, remains_in_db, @@ -77,6 +83,14 @@ impl CGWDevice { } } + pub fn set_device_type(&mut self, dev_type: CGWDeviceType) { + self.dev_type = dev_type; + } + + pub fn get_device_type(&self) -> CGWDeviceType { + self.dev_type + } + pub fn set_device_state(&mut self, new_state: CGWDeviceState) { self.state = new_state; } diff --git a/src/cgw_devices_cache.rs b/src/cgw_devices_cache.rs index 103fd71..d826bef 100644 --- a/src/cgw_devices_cache.rs +++ b/src/cgw_devices_cache.rs @@ -64,7 +64,7 @@ impl CGWDevicesCache { self.cache.get(key).is_some() } - pub fn get_device(&mut self, key: &MacAddress) -> Option<&mut CGWDevice> { + pub fn get_device_mut(&mut self, key: &MacAddress) -> Option<&mut CGWDevice> { if let Some(value) = self.cache.get_mut(key) { Some(value) } else { @@ -72,6 +72,14 @@ impl CGWDevicesCache { } } + pub fn get_device(&self, key: &MacAddress) -> Option<&CGWDevice> { + if let Some(value) = self.cache.get(key) { + Some(value) + } else { + None + } + } + #[allow(dead_code)] pub fn get_device_id(&self, key: &MacAddress) -> Option { self.cache.get(key).map(|value| value.get_device_group_id()) diff --git a/src/cgw_errors.rs b/src/cgw_errors.rs index d00673b..a8d2175 100644 --- a/src/cgw_errors.rs +++ b/src/cgw_errors.rs @@ -21,6 +21,8 @@ pub enum Error { UCentralParser(&'static str), + UCentralValidator(String), + UCentralMessagesQueue(&'static str), AppArgsParser(String), diff --git a/src/cgw_remote_discovery.rs b/src/cgw_remote_discovery.rs index 854e1cf..6c82c5b 100644 --- a/src/cgw_remote_discovery.rs +++ b/src/cgw_remote_discovery.rs @@ -1,7 +1,7 @@ use crate::{ cgw_app_args::CGWRedisArgs, cgw_db_accessor::{CGWDBAccessor, CGWDBInfra, CGWDBInfrastructureGroup}, - cgw_device::{CGWDevice, CGWDeviceState}, + cgw_device::{CGWDevice, CGWDeviceState, CGWDeviceType}, cgw_devices_cache::CGWDevicesCache, cgw_errors::{Error, Result}, cgw_metrics::{ @@ -427,6 +427,7 @@ impl CGWRemoteDiscovery { devices_cache.add_device( &item.mac, &CGWDevice::new( + CGWDeviceType::default(), CGWDeviceState::CGWDeviceDisconnected, item.infra_group_id, true, @@ -757,13 +758,14 @@ impl CGWRemoteDiscovery { let mut devices_cache = cache.write().await; let device_mac = infras[i]; - if let Some(device) = devices_cache.get_device(&device_mac) { + if let Some(device) = devices_cache.get_device_mut(&device_mac) { device.set_device_group_id(gid); device.set_device_remains_in_db(true); } else { devices_cache.add_device( &device_mac, &CGWDevice::new( + CGWDeviceType::default(), CGWDeviceState::CGWDeviceDisconnected, gid, true, @@ -816,7 +818,7 @@ impl CGWRemoteDiscovery { } else { let mut devices_cache = cache.write().await; let device_mac = infras[i]; - if let Some(device) = devices_cache.get_device(&device_mac) { + if let Some(device) = devices_cache.get_device_mut(&device_mac) { if device.get_device_state() == CGWDeviceState::CGWDeviceConnected { device.set_device_remains_in_db(false); device.set_device_group_id(0); diff --git a/src/cgw_ucentral_parser.rs b/src/cgw_ucentral_parser.rs index 607c4d3..2936a7f 100644 --- a/src/cgw_ucentral_parser.rs +++ b/src/cgw_ucentral_parser.rs @@ -1,11 +1,17 @@ +use std::fs::File; +use std::io::BufReader; +use std::path::Path; use std::str::FromStr; use eui48::MacAddress; +use jsonschema::JSONSchema; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use tokio_tungstenite::tungstenite::protocol::Message; +use url::Url; +use crate::cgw_app_args::{CGWValidationSchemaArgs, CGWValionSchemaRef}; use crate::cgw_errors::{Error, Result}; use crate::{ @@ -15,6 +21,76 @@ use crate::{ pub type CGWUCentralJRPCMessage = Map; +pub struct CGWUCentralConfigValidators { + ap_schema: JSONSchema, + switch_schema: JSONSchema, +} + +impl CGWUCentralConfigValidators { + pub fn new(uris: CGWValidationSchemaArgs) -> Result { + let ap_schema = cgw_initialize_json_validator(uris.ap_schema_uri)?; + let switch_schema = cgw_initialize_json_validator(uris.switch_schema_uri)?; + + Ok(CGWUCentralConfigValidators { + ap_schema, + switch_schema, + }) + } + + pub fn validate_config_message(&self, message: &str, device_type: CGWDeviceType) -> Result<()> { + let msg: CGWUCentralJRPCMessage = match serde_json::from_str(message) { + Ok(m) => m, + Err(e) => { + error!("Failed to parse input json {e}"); + return Err(Error::UCentralParser("Failed to parse input json")); + } + }; + + let config = match msg.get("params") { + Some(cfg) => cfg, + None => { + error!("Failed to get configs, invalid config message recevied"); + return Err(Error::UCentralParser( + "Failed to get configs, invalid config message recevied", + )); + } + }; + + let config = match config.get("config") { + Some(cfg) => cfg, + None => { + error!("Failed to get config params, invalid config message recevied"); + return Err(Error::UCentralParser( + "Failed to get config params, invalid config message recevied", + )); + } + }; + + let result = match device_type { + CGWDeviceType::CGWDeviceAP => self.ap_schema.validate(config), + CGWDeviceType::CGWDeviceSwitch => self.switch_schema.validate(config), + CGWDeviceType::CGWDeviceUnknown => { + error!("Failed to validate configure message for device type unknown"); + return Err(Error::UCentralParser( + "Failed to validate configure message for device type unknown", + )); + } + }; + + let mut json_errors: String = String::new(); + if let Err(errors) = result { + for error in errors { + json_errors += &format!("JSON: Validation error: {}\n", error); + json_errors += &format!("JSON: Instance path: {}\n", error.instance_path); + } + error!("{json_errors}"); + return Err(Error::UCentralValidator(json_errors)); + } + + Ok(()) + } +} + #[derive(Debug, Default, Deserialize, Serialize, PartialEq)] pub struct CGWUCentralEventLog { pub serial: MacAddress, @@ -340,5 +416,79 @@ pub fn cgw_ucentral_event_parse( CGWDeviceType::CGWDeviceSwitch => { cgw_ucentral_switch_parse_message(feature_topomap_enabled, message, timestamp) } + CGWDeviceType::CGWDeviceUnknown => Err(Error::UCentralParser( + "Failed to parse event message for device type unknown", + )), + } +} + +fn cgw_get_json_validation_schema(schema_ref: CGWValionSchemaRef) -> Result { + match schema_ref { + CGWValionSchemaRef::SchemaUri(url) => cgw_download_json_validation_schemas(url), + CGWValionSchemaRef::SchemaPath(path) => cgw_load_json_validation_schemas(path.as_path()), + } +} + +fn cgw_download_json_validation_schemas(url: Url) -> Result { + let client = reqwest::blocking::Client::new(); + let response = match client.get(url.clone()).send() { + Ok(r) => match r.text() { + Ok(t) => t, + Err(e) => { + return Err(Error::UCentralValidator(format!( + "Failed to convert response from target URI {url} to text fromat: {e}" + ))); + } + }, + Err(e) => { + return Err(Error::UCentralValidator(format!( + "Failed to receive response from target URI {url}: {e}" + ))); + } + }; + + match serde_json::from_str(&response) { + Ok(json_schema) => Ok(json_schema), + Err(e) => Err(Error::UCentralValidator(format!( + "Failed to deserialize text response from target URI {url}: {e}" + ))), + } +} + +fn cgw_load_json_validation_schemas(path: &Path) -> Result { + let file = match File::open(path) { + Ok(f) => f, + Err(e) => { + return Err(Error::UCentralValidator(format!( + "Failed to open TLS certificate file: {}. Error: {}", + path.display(), + e + ))); + } + }; + + let reader = BufReader::new(file); + match serde_json::from_reader(reader) { + Ok(json_schema) => Ok(json_schema), + Err(e) => Err(Error::UCentralValidator(format!( + "Failed to read JSON schema from file {}: {e}", + path.display() + ))), + } +} + +pub fn cgw_initialize_json_validator(schema_ref: CGWValionSchemaRef) -> Result { + let schema = match cgw_get_json_validation_schema(schema_ref) { + Ok(sch) => sch, + Err(e) => { + return Err(Error::UCentralValidator(e.to_string())); + } + }; + + match JSONSchema::compile(&schema) { + Ok(json_schema) => Ok(json_schema), + Err(e) => Err(Error::UCentralValidator(format!( + "Failed to compile input schema to validation tree: {e}", + ))), } }