mirror of
				https://github.com/Telecominfraproject/wlan-testing.git
				synced 2025-11-04 12:57:55 +00:00 
			
		
		
		
	WIFI-13443: Add: Schema Validation (#913)
* Added Schema Validation. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Added schema_validation/base.txt Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Modified static commit-id in base.txt and sub-suites in schema_validation. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Updated base.text commit-id to check scenario of no new firmware found. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Add: Schema Validation through AP Terminal (State Messages) Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Modified: Schema Validation using AP State Messages. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Add/Modify: Marker and Description to Schema Validation test plan. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Modify: Allure reporting for Schema Validation through AP Terminal testcase. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Modify Schema Validation reporting. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Modify: Fail message in allure for schema validation. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Add: --commit-id parameter in CLI and modified schema validation through Github test case. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Remove unused function from schema validation. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Modify: Allure reporting for schema validation. Fix: bug in schema_validation through github. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Modify: Schema Validation through GitHub. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Modify: Schema Validation Testplan. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Moved SV files from test/ -> basic/ Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Added ow_sanity_lf marker to Schema Validation tests. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Modified schema validation code to handle cases where 'properties' or 'items' are missing from schema. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Modified the Schema Validation through Github to bypass wlan-ap repo and directly fetch latest commit from wlan-ucentral-schema repo. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Modified Schema Validation through github to not report properties key missing when patternProperties or key present. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> * Increased time before fetching state message and added additional checks. Signed-off-by: jitendracandela <jitendra.kushavah@candelatech.com> --------- Co-authored-by: jitendracandela <jitendra.kushavah@candelatech.com>
This commit is contained in:
		@@ -141,6 +141,11 @@ def pytest_addoption(parser):
 | 
				
			|||||||
        default=False,
 | 
					        default=False,
 | 
				
			||||||
        help="Select the port for AP Up Down tests"
 | 
					        help="Select the port for AP Up Down tests"
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    parser.addoption(
 | 
				
			||||||
 | 
					        "--commit-id",
 | 
				
			||||||
 | 
					        default=None,
 | 
				
			||||||
 | 
					        help="Used to pass the full SHA of a commit-id."
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.fixture(scope="session")
 | 
					@pytest.fixture(scope="session")
 | 
				
			||||||
@@ -176,6 +181,13 @@ def selected_port(request):
 | 
				
			|||||||
    yield current_port
 | 
					    yield current_port
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture(scope="session")
 | 
				
			||||||
 | 
					def commit_id(request):
 | 
				
			||||||
 | 
					    """yields the commit-id option selection"""
 | 
				
			||||||
 | 
					    commit_id = request.config.getoption("--commit-id")
 | 
				
			||||||
 | 
					    yield commit_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@pytest.fixture(scope="session")
 | 
					@pytest.fixture(scope="session")
 | 
				
			||||||
def num_stations(request):
 | 
					def num_stations(request):
 | 
				
			||||||
    """yields the testbed option selection"""
 | 
					    """yields the testbed option selection"""
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								tests/e2e/basic/schema_validation/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/e2e/basic/schema_validation/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										238
									
								
								tests/e2e/basic/schema_validation/master-config-1.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								tests/e2e/basic/schema_validation/master-config-1.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,238 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"uuid": 2,
 | 
				
			||||||
 | 
						"radios": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"band": "5G",
 | 
				
			||||||
 | 
								"channel": 36,
 | 
				
			||||||
 | 
								"channel-mode": "HE",
 | 
				
			||||||
 | 
								"channel-width": 80,
 | 
				
			||||||
 | 
								"country": "US"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"band": "2G",
 | 
				
			||||||
 | 
								"channel": 11,
 | 
				
			||||||
 | 
								"channel-mode": "HE",
 | 
				
			||||||
 | 
								"channel-width": 20,
 | 
				
			||||||
 | 
								"country": "US"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
						"interfaces": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "WAN",
 | 
				
			||||||
 | 
								"role": "upstream",
 | 
				
			||||||
 | 
								"services": [ "lldp", "ssh" ],
 | 
				
			||||||
 | 
								"ethernet": [
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"select-ports": [
 | 
				
			||||||
 | 
											"WAN*"
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								],
 | 
				
			||||||
 | 
								"ipv4": {
 | 
				
			||||||
 | 
									"addressing": "dynamic"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"ssids": [
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
										"name": "multi-psk-1",
 | 
				
			||||||
 | 
					                    "role": "downstream",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"2G", "5G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "psk",
 | 
				
			||||||
 | 
											"key": "OpenWifi",
 | 
				
			||||||
 | 
											"ieee80211w": "optional"
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
					                  "multi-psk": [
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												"key": "wpassidkey300",
 | 
				
			||||||
 | 
												"vlan-id": 300
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											{
 | 
				
			||||||
 | 
												"key": "wpassidkey400",
 | 
				
			||||||
 | 
					                            "vlan-id": 400
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									},{
 | 
				
			||||||
 | 
										"name": "roaming-ratelimit-2",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"5G",
 | 
				
			||||||
 | 
											"2G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "psk2",
 | 
				
			||||||
 | 
					                      "key": "OpenWifi",
 | 
				
			||||||
 | 
											"ieee80211w": "optional"
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
					                  "roaming": {
 | 
				
			||||||
 | 
											"message-exchange": "ds",
 | 
				
			||||||
 | 
											"generate-psk": true
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										"services": [ "wifi-steering" ],
 | 
				
			||||||
 | 
										"rate-limit": {
 | 
				
			||||||
 | 
											"ingress-rate": 50,
 | 
				
			||||||
 | 
											"egress-rate": 50
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
					 {
 | 
				
			||||||
 | 
										"name": "owe-2g-7",
 | 
				
			||||||
 | 
					                    "role": "downstream",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"2G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "owe",
 | 
				
			||||||
 | 
											"ieee80211w": "required"
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
						 				"rrm": {
 | 
				
			||||||
 | 
											"reduced-neighbor-reporting": true
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
									},{
 | 
				
			||||||
 | 
										"name": "owe-5g-8",
 | 
				
			||||||
 | 
					                    "role": "downstream",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
										    "5G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "owe",
 | 
				
			||||||
 | 
											"ieee80211w": "required"
 | 
				
			||||||
 | 
										},  "rrm": {
 | 
				
			||||||
 | 
					                                                "reduced-neighbor-reporting": true
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
										"name": "radius-ratelimit-3",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"5G",
 | 
				
			||||||
 | 
											"2G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "wpa2",
 | 
				
			||||||
 | 
											"ieee80211w": "optional"
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										"rate-limit": {
 | 
				
			||||||
 | 
											"ingress-rate": 50,
 | 
				
			||||||
 | 
											"egress-rate": 50
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										"radius": {
 | 
				
			||||||
 | 
											"authentication": {
 | 
				
			||||||
 | 
												"host": "18.189.85.200",
 | 
				
			||||||
 | 
												"port": 1812,
 | 
				
			||||||
 | 
												"secret": "testing123"
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											"accounting": {
 | 
				
			||||||
 | 
												"host": "18.189.85.200",
 | 
				
			||||||
 | 
												"port": 1813,
 | 
				
			||||||
 | 
												"secret": "testing123"
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
					               {
 | 
				
			||||||
 | 
					          "name": "captive-credential-4",
 | 
				
			||||||
 | 
					          "services": [
 | 
				
			||||||
 | 
					            "captive"
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          "wifi-bands": [
 | 
				
			||||||
 | 
					            "5G",
 | 
				
			||||||
 | 
					            "2G"
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          "bss-mode": "ap",
 | 
				
			||||||
 | 
					          "encryption": {
 | 
				
			||||||
 | 
					            "proto": "psk2",
 | 
				
			||||||
 | 
					            "key": "OpenWifi",
 | 
				
			||||||
 | 
					            "ieee80211w": "optional"
 | 
				
			||||||
 | 
					          }, "captive": {
 | 
				
			||||||
 | 
					      "auth-mode": "credentials",
 | 
				
			||||||
 | 
					      "credentials": [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          "username": "user1",
 | 
				
			||||||
 | 
					          "password": "password1"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					      "walled-garden-fqdn": [
 | 
				
			||||||
 | 
					        "*.google.com",
 | 
				
			||||||
 | 
					        "telecominfraproject.com"
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                    "bss-mode": "ap",
 | 
				
			||||||
 | 
					                    "encryption": {
 | 
				
			||||||
 | 
					                        "ieee80211w": "optional",
 | 
				
			||||||
 | 
					                        "proto": "psk2-radius"
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    "name": "radius-MPSK1-5",
 | 
				
			||||||
 | 
					                    "wifi-bands": [
 | 
				
			||||||
 | 
					                        "2G","5G"
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    "radius": {
 | 
				
			||||||
 | 
					                        "authentication": {
 | 
				
			||||||
 | 
					                                "host": "18.117.247.76",
 | 
				
			||||||
 | 
					                                "port": 1812,
 | 
				
			||||||
 | 
					                                "secret": "secret"
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        "accounting": {
 | 
				
			||||||
 | 
					                                "host": "18.117.247.76",
 | 
				
			||||||
 | 
					                                "port": 1813,
 | 
				
			||||||
 | 
					                                "secret": "secret"
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }]},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "LAN",
 | 
				
			||||||
 | 
								"role": "downstream",
 | 
				
			||||||
 | 
								"services": [ "ssh", "lldp" ],
 | 
				
			||||||
 | 
								"ethernet": [
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"select-ports": [
 | 
				
			||||||
 | 
											"LAN*"
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								],
 | 
				
			||||||
 | 
								"ipv4": {
 | 
				
			||||||
 | 
									"addressing": "static",
 | 
				
			||||||
 | 
									"subnet": "192.168.1.1/24",
 | 
				
			||||||
 | 
									"dhcp": {
 | 
				
			||||||
 | 
										"lease-first": 10,
 | 
				
			||||||
 | 
										"lease-count": 100,
 | 
				
			||||||
 | 
										"lease-time": "6h"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
						"metrics": {
 | 
				
			||||||
 | 
							"statistics": {
 | 
				
			||||||
 | 
								"interval": 120,
 | 
				
			||||||
 | 
								"types": [ "ssids", "lldp", "clients" ]
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"health": {
 | 
				
			||||||
 | 
								"interval": 120
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"wifi-frames": {
 | 
				
			||||||
 | 
								"filters": [ "probe", "auth" ]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"services": {
 | 
				
			||||||
 | 
					        "wifi-steering": {
 | 
				
			||||||
 | 
								"mode": "local",
 | 
				
			||||||
 | 
								"network": "upstream",
 | 
				
			||||||
 | 
								"assoc-steering": true,
 | 
				
			||||||
 | 
								"required-snr": -85,
 | 
				
			||||||
 | 
								"required-probe-snr": -80,
 | 
				
			||||||
 | 
								"required-roam-snr": -80,
 | 
				
			||||||
 | 
								"load-kick-threshold": 90
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					    	"lldp": {
 | 
				
			||||||
 | 
								"describe": "uCentral",
 | 
				
			||||||
 | 
								"location": "universe"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"ssh": {
 | 
				
			||||||
 | 
								"port": 22
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										259
									
								
								tests/e2e/basic/schema_validation/master-config-2.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								tests/e2e/basic/schema_validation/master-config-2.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,259 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"uuid": 2,
 | 
				
			||||||
 | 
						"globals": {
 | 
				
			||||||
 | 
							"wireless-multimedia": {
 | 
				
			||||||
 | 
								"profile": "rfc8325"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"radios": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"band": "2G",
 | 
				
			||||||
 | 
								"country": "CA",
 | 
				
			||||||
 | 
								"channel-mode": "HE",
 | 
				
			||||||
 | 
								"channel-width": 40,
 | 
				
			||||||
 | 
								"channel": 11,
 | 
				
			||||||
 | 
					           "dtim-period": 3
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
								"band": "5G",
 | 
				
			||||||
 | 
								"channel": 36,
 | 
				
			||||||
 | 
								"channel-mode": "HE",
 | 
				
			||||||
 | 
								"channel-width": 80,
 | 
				
			||||||
 | 
								"country": "CA",
 | 
				
			||||||
 | 
					         "dtim-period": 3
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
						"interfaces": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "WAN",
 | 
				
			||||||
 | 
								"role": "upstream",
 | 
				
			||||||
 | 
								"services": [ "lldp" ],
 | 
				
			||||||
 | 
								"ethernet": [
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"select-ports": [
 | 
				
			||||||
 | 
											"WAN*"
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								],
 | 
				
			||||||
 | 
								"ipv4": {
 | 
				
			||||||
 | 
									"addressing": "dynamic"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"ssids": [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
										"name": "radsec-proxy-2",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"2G", "5G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "wpa2",
 | 
				
			||||||
 | 
											"ieee80211w": "optional"
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										"radius": {
 | 
				
			||||||
 | 
											"authentication": {
 | 
				
			||||||
 | 
												"host": "10.28.3.100",
 | 
				
			||||||
 | 
												"port": 1812,
 | 
				
			||||||
 | 
												"secret": "testing123"
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
											"accounting": {
 | 
				
			||||||
 | 
												"host": "10.28.3.100",
 | 
				
			||||||
 | 
												"port": 1813,
 | 
				
			||||||
 | 
												"secret": "testing123"
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										"services": [ "radius-gw-proxy" ]
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
					             {
 | 
				
			||||||
 | 
					                    "bss-mode": "ap",
 | 
				
			||||||
 | 
					                    "encryption": {
 | 
				
			||||||
 | 
					                        "ieee80211w": "optional",
 | 
				
			||||||
 | 
					                        "key": "OpenWifi1",
 | 
				
			||||||
 | 
					                        "proto": "psk2"
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    "isolate-clients": true,
 | 
				
			||||||
 | 
					                    "name": "Client-isolation-3",
 | 
				
			||||||
 | 
					                    "wifi-bands": [
 | 
				
			||||||
 | 
					                        "2G","5G"
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					             {
 | 
				
			||||||
 | 
					          "name": "local-mac-acl-4",
 | 
				
			||||||
 | 
					          "access-control-list": {
 | 
				
			||||||
 | 
					            "mode": "allow",
 | 
				
			||||||
 | 
					            "mac-address": [
 | 
				
			||||||
 | 
					              "ac:67:5d:7e:90:a0"
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "wifi-bands": [
 | 
				
			||||||
 | 
					            "2G",
 | 
				
			||||||
 | 
					            "5G"
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          "bss-mode": "ap",
 | 
				
			||||||
 | 
					          "encryption": {
 | 
				
			||||||
 | 
					            "proto": "none",
 | 
				
			||||||
 | 
					            "ieee80211w": "optional"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					          "name": "radius-mac-acl-5",
 | 
				
			||||||
 | 
					          "wifi-bands": [
 | 
				
			||||||
 | 
					            "2G","5G"
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          "bss-mode": "ap",
 | 
				
			||||||
 | 
					          "encryption": {
 | 
				
			||||||
 | 
					            "proto": "none"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "radius": {
 | 
				
			||||||
 | 
					            "authentication": {
 | 
				
			||||||
 | 
					              "host": "192.168.178.192",
 | 
				
			||||||
 | 
					              "port": 1812,
 | 
				
			||||||
 | 
					              "secret": "secret",
 | 
				
			||||||
 | 
					              "mac-filter": true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },{
 | 
				
			||||||
 | 
										"name": "owe-transition-2G-6",
 | 
				
			||||||
 | 
					                    "role": "downstream",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"2G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "owe-transition",
 | 
				
			||||||
 | 
											"ieee80211w": "required"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},{
 | 
				
			||||||
 | 
										"name": "owe-transition-5G-6",
 | 
				
			||||||
 | 
					                    "role": "downstream",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"5G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "owe-transition",
 | 
				
			||||||
 | 
											"ieee80211w": "required"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
										"name": "power-save-7",
 | 
				
			||||||
 | 
					                    "role": "downstream",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"2G","5G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
					                    "power-save": true,
 | 
				
			||||||
 | 
					                    "unicast-conversion": false,
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "psk2",
 | 
				
			||||||
 | 
											"key": "OpenWifi",
 | 
				
			||||||
 | 
											"ieee80211w": "optional"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					          "name": "captive-uam-8",
 | 
				
			||||||
 | 
					          "services": [
 | 
				
			||||||
 | 
					            "captive"
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          "wifi-bands": [
 | 
				
			||||||
 | 
					            "5G",
 | 
				
			||||||
 | 
					            "2G"
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          "bss-mode": "ap",
 | 
				
			||||||
 | 
					          "encryption": {
 | 
				
			||||||
 | 
					            "proto": "psk2",
 | 
				
			||||||
 | 
					            "key": "OpenWifi",
 | 
				
			||||||
 | 
					            "ieee80211w": "optional"
 | 
				
			||||||
 | 
					          }, "captive": {
 | 
				
			||||||
 | 
					      "auth-mode": "uam",
 | 
				
			||||||
 | 
					      "uam-port": 3990,
 | 
				
			||||||
 | 
					      "uam-secret": "hotsys123",
 | 
				
			||||||
 | 
					      "uam-server": "https://customer.hotspotsystem.com/customer/hotspotlogin.php",
 | 
				
			||||||
 | 
					      "nasid": "AlmondLabs",
 | 
				
			||||||
 | 
					      "auth-server": "radius.hotspotsystem.com",
 | 
				
			||||||
 | 
					      "auth-port": 1812,
 | 
				
			||||||
 | 
					      "auth-secret": "hotsys123",
 | 
				
			||||||
 | 
					      "walled-garden-fqdn": [
 | 
				
			||||||
 | 
					        "*.google.com",
 | 
				
			||||||
 | 
						"facebook.com",
 | 
				
			||||||
 | 
					        "telecominfraproject.com",
 | 
				
			||||||
 | 
					        "customer.hotspotsystem.com"
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					        }]
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "LAN",
 | 
				
			||||||
 | 
								"role": "downstream",
 | 
				
			||||||
 | 
								"services": [ "ssh", "lldp" ],
 | 
				
			||||||
 | 
								"ethernet": [
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"select-ports": [
 | 
				
			||||||
 | 
											"LAN*"
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								],
 | 
				
			||||||
 | 
								"ipv4": {
 | 
				
			||||||
 | 
									"addressing": "static",
 | 
				
			||||||
 | 
									"subnet": "192.168.1.1/24",
 | 
				
			||||||
 | 
									"dhcp": {
 | 
				
			||||||
 | 
										"lease-first": 10,
 | 
				
			||||||
 | 
										"lease-count": 100,
 | 
				
			||||||
 | 
										"lease-time": "6h"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
						"metrics": {
 | 
				
			||||||
 | 
							"statistics": {
 | 
				
			||||||
 | 
								"interval": 120,
 | 
				
			||||||
 | 
								"types": [ "ssids", "lldp", "clients" ]
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"health": {
 | 
				
			||||||
 | 
								"interval": 120
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"services": {
 | 
				
			||||||
 | 
							"lldp": {
 | 
				
			||||||
 | 
								"describe": "uCentral",
 | 
				
			||||||
 | 
								"location": "universe"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"ssh": {
 | 
				
			||||||
 | 
								"port": 22
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"quality-of-service": {
 | 
				
			||||||
 | 
								"select-ports": [ "WAN" ],
 | 
				
			||||||
 | 
								"bandwidth_up": 1000,
 | 
				
			||||||
 | 
								"bandwidth_down": 1000,
 | 
				
			||||||
 | 
								"bulk-detection": {
 | 
				
			||||||
 | 
									"dscp": "CS1",
 | 
				
			||||||
 | 
									"packets-per-second": 500
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"classifier": [
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"dscp":  "CS1",
 | 
				
			||||||
 | 
										"ports": [
 | 
				
			||||||
 | 
											{ "protocol": "any", "port": 53 },
 | 
				
			||||||
 | 
											{ "protocol": "tcp", "port": 80 }
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"dns": [
 | 
				
			||||||
 | 
											{ "fqdn": "telecominfraproject.com", "suffix-matching": false }
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}, {
 | 
				
			||||||
 | 
										"dscp":  "AF41",
 | 
				
			||||||
 | 
										"dns": [
 | 
				
			||||||
 | 
											{ "fqdn": "zoom.us" }
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								]
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"airtime-fairness": {
 | 
				
			||||||
 | 
								"voice-weight": 4,
 | 
				
			||||||
 | 
								"packet-threshold": 100,
 | 
				
			||||||
 | 
								"bulk-threshold": 50,
 | 
				
			||||||
 | 
								"priority-threshold": 30,
 | 
				
			||||||
 | 
								"weight-normal": 256,
 | 
				
			||||||
 | 
								"weight-priority": 384,
 | 
				
			||||||
 | 
								"weight-bulk": 128
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										192
									
								
								tests/e2e/basic/schema_validation/master-config-3.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								tests/e2e/basic/schema_validation/master-config-3.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,192 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"uuid": 2,
 | 
				
			||||||
 | 
						"radios": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"band": "5G",
 | 
				
			||||||
 | 
								"channel": 149,
 | 
				
			||||||
 | 
								"channel-mode": "HE",
 | 
				
			||||||
 | 
								"channel-width": 80,
 | 
				
			||||||
 | 
								"country": "CA"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"band": "2G",
 | 
				
			||||||
 | 
								"channel": 11,
 | 
				
			||||||
 | 
								"channel-mode": "HE",
 | 
				
			||||||
 | 
								"channel-width": 20,
 | 
				
			||||||
 | 
								"country": "CA"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
						"interfaces": [
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "WAN",
 | 
				
			||||||
 | 
								"role": "upstream",
 | 
				
			||||||
 | 
								"tunnel": {
 | 
				
			||||||
 | 
								   "proto": "mesh"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"services": [ "lldp" ],
 | 
				
			||||||
 | 
								"ethernet": [
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"select-ports": [
 | 
				
			||||||
 | 
											"WAN*"
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								],
 | 
				
			||||||
 | 
								"ipv4": {
 | 
				
			||||||
 | 
									"addressing": "dynamic"
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"ssids": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
										"name": "mesh-transit-1",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"5G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "mesh",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "psk2",
 | 
				
			||||||
 | 
											"key": "meshpassword",
 | 
				
			||||||
 | 
											"ieee80211w": "optional"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
										"name": "Mesh-SSID-2G",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"2G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "psk",
 | 
				
			||||||
 | 
					                       "key": "OpenWifi",
 | 
				
			||||||
 | 
											"ieee80211w": "optional"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
										 {
 | 
				
			||||||
 | 
										"name": "Mesh-SSID-5G",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"5G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "psk",
 | 
				
			||||||
 | 
					                         "key": "OpenWifi",
 | 
				
			||||||
 | 
											"ieee80211w": "optional"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
										 },
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
										"name": "usteering-air-3",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"5G",
 | 
				
			||||||
 | 
											"2G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "psk2",
 | 
				
			||||||
 | 
											"key": "password@123",
 | 
				
			||||||
 | 
											"ieee80211w": "optional"
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										"roaming": {
 | 
				
			||||||
 | 
											"message-exchange": "air",
 | 
				
			||||||
 | 
											"generate-psk": true
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										"services": [ "wifi-steering" ]
 | 
				
			||||||
 | 
									},{
 | 
				
			||||||
 | 
										"name": "Uchannel-ds-4",
 | 
				
			||||||
 | 
										"wifi-bands": [
 | 
				
			||||||
 | 
											"2G","5G"
 | 
				
			||||||
 | 
										],
 | 
				
			||||||
 | 
										"bss-mode": "ap",
 | 
				
			||||||
 | 
										"encryption": {
 | 
				
			||||||
 | 
											"proto": "psk2",
 | 
				
			||||||
 | 
											"key": "OpenWifi",
 | 
				
			||||||
 | 
											"ieee80211w": "optional"
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										"roaming": {
 | 
				
			||||||
 | 
											"message-exchange": "ds",
 | 
				
			||||||
 | 
											"generate-psk": true
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										"rrm": {                        
 | 
				
			||||||
 | 
										  "neighbor-reporting": true,              
 | 
				
			||||||
 | 
										  "ftm-responder": true,                     
 | 
				
			||||||
 | 
					                      "stationary-ap": true                    },
 | 
				
			||||||
 | 
										"services": [ "wifi-steering" ]
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
					               {
 | 
				
			||||||
 | 
					          "name": "captive-radius-5",
 | 
				
			||||||
 | 
					          "services": [
 | 
				
			||||||
 | 
					            "captive"
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          "wifi-bands": [
 | 
				
			||||||
 | 
					            "5G",
 | 
				
			||||||
 | 
					            "2G"
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          "bss-mode": "ap",
 | 
				
			||||||
 | 
					          "encryption": {
 | 
				
			||||||
 | 
					            "proto": "psk2",
 | 
				
			||||||
 | 
					            "key": "OpenWifi",
 | 
				
			||||||
 | 
					            "ieee80211w": "optional"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					       "captive": {
 | 
				
			||||||
 | 
					      "auth-mode": "radius",
 | 
				
			||||||
 | 
					      "auth-server": "10.28.3.100",
 | 
				
			||||||
 | 
					      "auth-port": 1812,
 | 
				
			||||||
 | 
					      "auth-secret": "testing123",
 | 
				
			||||||
 | 
					      "walled-garden-fqdn": [
 | 
				
			||||||
 | 
					        "*.google.com",
 | 
				
			||||||
 | 
					        "telecominfraproject.com"
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
								]
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"name": "LAN",
 | 
				
			||||||
 | 
								"role": "downstream",
 | 
				
			||||||
 | 
								"services": [ "ssh", "lldp" ],
 | 
				
			||||||
 | 
								"ethernet": [
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										"select-ports": [
 | 
				
			||||||
 | 
											"LAN*"
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								],
 | 
				
			||||||
 | 
								"ipv4": {
 | 
				
			||||||
 | 
									"addressing": "static",
 | 
				
			||||||
 | 
									"subnet": "192.168.1.1/24",
 | 
				
			||||||
 | 
									"dhcp": {
 | 
				
			||||||
 | 
										"lease-first": 10,
 | 
				
			||||||
 | 
										"lease-count": 100,
 | 
				
			||||||
 | 
										"lease-time": "6h"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
						"metrics": {
 | 
				
			||||||
 | 
							"statistics": {
 | 
				
			||||||
 | 
								"interval": 120,
 | 
				
			||||||
 | 
								"types": [ "ssids", "lldp", "clients" ]
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"health": {
 | 
				
			||||||
 | 
								"interval": 120
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"wifi-frames": {
 | 
				
			||||||
 | 
								"filters": [ "probe", "auth" ]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"services": {
 | 
				
			||||||
 | 
							"lldp": {
 | 
				
			||||||
 | 
								"describe": "uCentral",
 | 
				
			||||||
 | 
								"location": "universe"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					      "wifi-steering": {
 | 
				
			||||||
 | 
								"mode": "local",
 | 
				
			||||||
 | 
								"network": "upstream",
 | 
				
			||||||
 | 
								"assoc-steering": true,
 | 
				
			||||||
 | 
								"required-snr": -85,
 | 
				
			||||||
 | 
								"required-probe-snr": -80,
 | 
				
			||||||
 | 
								"required-roam-snr": -80,
 | 
				
			||||||
 | 
								"load-kick-threshold": 90
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"ssh": {
 | 
				
			||||||
 | 
								"port": 22
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										562
									
								
								tests/e2e/basic/schema_validation/test_schema_validation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										562
									
								
								tests/e2e/basic/schema_validation/test_schema_validation.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,562 @@
 | 
				
			|||||||
 | 
					import pytest
 | 
				
			||||||
 | 
					import allure
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					import requests
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					from tabulate import tabulate
 | 
				
			||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pytestmark = [pytest.mark.schema_validation, pytest.mark.ow_sanity_lf]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Get the directory of the current test config file
 | 
				
			||||||
 | 
					test_file_dir = os.path.dirname(os.path.abspath(__file__))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Construct the file path relative to the config file directory
 | 
				
			||||||
 | 
					file_path = os.path.join(test_file_dir, 'master-config-1.json')
 | 
				
			||||||
 | 
					with open(file_path, 'r') as file:
 | 
				
			||||||
 | 
					    json_string = file.read()
 | 
				
			||||||
 | 
					    config_data_1 = json.loads(json_string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					file_path2 = os.path.join(test_file_dir, 'master-config-2.json')
 | 
				
			||||||
 | 
					with open(file_path, 'r') as file:
 | 
				
			||||||
 | 
					    json_string = file.read()
 | 
				
			||||||
 | 
					    config_data_2 = json.loads(json_string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					file_path3 = os.path.join(test_file_dir, 'master-config-3.json')
 | 
				
			||||||
 | 
					with open(file_path, 'r') as file:
 | 
				
			||||||
 | 
					    json_string = file.read()
 | 
				
			||||||
 | 
					    config_data_3 = json.loads(json_string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def make_raw_url(url):
 | 
				
			||||||
 | 
					    return url.replace("github.com", "raw.githubusercontent.com").replace("/blob/", "/")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_github_file(url, path=None, commit_id=None):
 | 
				
			||||||
 | 
					    if commit_id:
 | 
				
			||||||
 | 
					        url = url.replace("main", commit_id)
 | 
				
			||||||
 | 
					    if path:
 | 
				
			||||||
 | 
					        url = url + path
 | 
				
			||||||
 | 
					    url = make_raw_url(url)
 | 
				
			||||||
 | 
					    logging.info(f"Fetching {url}")
 | 
				
			||||||
 | 
					    response = requests.get(url)
 | 
				
			||||||
 | 
					    if response.status_code != 200:
 | 
				
			||||||
 | 
					        logging.info(f"Failed to fetch {url}. Status code: {response.status_code}")
 | 
				
			||||||
 | 
					        pytest.skip("Failed to fetch the schema file from GitHub. Make sure the commit-id is one from "
 | 
				
			||||||
 | 
					                    "wlan-ucentral-schema repo.")
 | 
				
			||||||
 | 
					    return response.text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def validate_schema_through_github(commit_id, path):
 | 
				
			||||||
 | 
					    def get_commit_id(owner, repo, path="", headers=None):
 | 
				
			||||||
 | 
					        if headers is None:
 | 
				
			||||||
 | 
					            headers = {"Accept": "application/vnd.github.v3+json"}
 | 
				
			||||||
 | 
					        url = f"https://api.github.com/repos/{owner}/{repo}/commits"
 | 
				
			||||||
 | 
					        params = {"sha": "main", "path": path}
 | 
				
			||||||
 | 
					        response = requests.get(url, params=params, headers=headers)
 | 
				
			||||||
 | 
					        if response.status_code == 200:
 | 
				
			||||||
 | 
					            data = response.json()
 | 
				
			||||||
 | 
					            commit_id = data[0]['sha']
 | 
				
			||||||
 | 
					            return commit_id
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            logging.info(f"Failed to fetch commit-id. Status code: {response.status_code}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def compare_dicts(dict1, dict2, path="", added_keys=None, removed_keys=None, changed_items=None):
 | 
				
			||||||
 | 
					        if dict1 == dict2:
 | 
				
			||||||
 | 
					            return added_keys, removed_keys, changed_items
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if changed_items is None:
 | 
				
			||||||
 | 
					            changed_items = []
 | 
				
			||||||
 | 
					        if removed_keys is None:
 | 
				
			||||||
 | 
					            removed_keys = set()
 | 
				
			||||||
 | 
					        if added_keys is None:
 | 
				
			||||||
 | 
					            added_keys = set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for key in set(dict1.keys()) | set(dict2.keys()):
 | 
				
			||||||
 | 
					            new_path = f"{path} > {key}" if path else key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if key not in dict1:
 | 
				
			||||||
 | 
					                added_keys.add((new_path, f"{dict2[key]}"))
 | 
				
			||||||
 | 
					            elif key not in dict2:
 | 
				
			||||||
 | 
					                removed_keys.add(new_path)
 | 
				
			||||||
 | 
					            elif dict1[key] != dict2[key]:
 | 
				
			||||||
 | 
					                if type(dict1[key]) == type(dict2[key]):
 | 
				
			||||||
 | 
					                    if isinstance(dict1[key], dict):
 | 
				
			||||||
 | 
					                        added_keys, removed_keys, changed_items = compare_dicts(dict1[key], dict2[key], path=new_path,
 | 
				
			||||||
 | 
					                                                                                added_keys=added_keys,
 | 
				
			||||||
 | 
					                                                                                removed_keys=removed_keys,
 | 
				
			||||||
 | 
					                                                                                changed_items=changed_items)
 | 
				
			||||||
 | 
					                    elif isinstance(dict1[key], list):
 | 
				
			||||||
 | 
					                        if len(dict1[key]) != len(dict2[key]):
 | 
				
			||||||
 | 
					                            changed_items.append((f"length of array at {new_path}", f"{len(dict1[key])}",
 | 
				
			||||||
 | 
					                                                  f"{len(dict2[key])}"))
 | 
				
			||||||
 | 
					                            changed_items.append((new_path, f"{dict1[key]}", f"{dict2[key]}"))
 | 
				
			||||||
 | 
					                        else:
 | 
				
			||||||
 | 
					                            for index, (val1, val2) in enumerate(zip(dict1[key], dict2[key])):
 | 
				
			||||||
 | 
					                                added_keys, removed_keys, changed_items = compare_dicts(val1, val2,
 | 
				
			||||||
 | 
					                                                                                        path=f"{new_path} > [item {index}]",
 | 
				
			||||||
 | 
					                                                                                        added_keys=added_keys,
 | 
				
			||||||
 | 
					                                                                                        removed_keys=removed_keys,
 | 
				
			||||||
 | 
					                                                                                        changed_items=changed_items)
 | 
				
			||||||
 | 
					                    elif isinstance(dict1[key], str) or isinstance(dict1[key], int) or isinstance(dict1[key], float):
 | 
				
			||||||
 | 
					                        changed_items.append((new_path, f"{dict1[key]}", f"{dict2[key]}"))
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    changed_items.append((new_path, f"{dict1[key]}", f"{dict2[key]}"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return added_keys, removed_keys, changed_items
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def generate_diff(wlan_ucentral_schema_url, latest_version_id, previous_version_id, path):
 | 
				
			||||||
 | 
					        previous_schema_pretty_json = get_github_file(wlan_ucentral_schema_url, path, previous_version_id)
 | 
				
			||||||
 | 
					        updated_schema_pretty_json = get_github_file(wlan_ucentral_schema_url, path, latest_version_id)
 | 
				
			||||||
 | 
					        allure.attach(previous_schema_pretty_json, name=f"OLD {path}:")
 | 
				
			||||||
 | 
					        allure.attach(updated_schema_pretty_json, name=f"NEW {path}:")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if updated_schema_pretty_json == previous_schema_pretty_json:
 | 
				
			||||||
 | 
					            logging.info(f"No changes found at {path}. Exiting.")
 | 
				
			||||||
 | 
					            return None, None, None
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            logging.info(f"Changes found at {path}. Proceeding with the comparison.")
 | 
				
			||||||
 | 
					            added_keys, removed_keys, changed_items = compare_dicts(json.loads(previous_schema_pretty_json),
 | 
				
			||||||
 | 
					                                                                    json.loads(updated_schema_pretty_json))
 | 
				
			||||||
 | 
					            return added_keys, removed_keys, changed_items
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if commit_id is None:
 | 
				
			||||||
 | 
					        logging.info("Use --commit-id to the pass an old commit-id of tip/wlan-ucentral-schema repo. Skipping the test.")
 | 
				
			||||||
 | 
					        pytest.skip("Use --commit-id to the pass an old commit-id of tip/wlan-ucentral-schema repo. Skipping the test.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    latest_version_id = get_commit_id(owner="Telecominfraproject", repo="wlan-ucentral-schema")
 | 
				
			||||||
 | 
					    logging.info(f"Latest Commit-ID of wlan-ucentral-schema = {latest_version_id}")
 | 
				
			||||||
 | 
					    allure.attach(latest_version_id, name="Latest commit-id of wlan-ucentral-schema:")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    previous_version_id = commit_id
 | 
				
			||||||
 | 
					    logging.info(f"Passed Commit-ID of wlan-ucentral-schema: {previous_version_id}")
 | 
				
			||||||
 | 
					    allure.attach(previous_version_id, name=f"Passed Commit-ID of wlan-ucentral-schema:")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if latest_version_id == previous_version_id:
 | 
				
			||||||
 | 
					        logging.info("No new commit-id found in wlan-ucentral-schema. Exiting.")
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    logging.info("New commit found. Proceeding with the schema validation.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    wlan_ucentral_schema_url = "https://github.com/Telecominfraproject/wlan-ucentral-schema/blob/main"
 | 
				
			||||||
 | 
					    added_keys, removed_keys, changed_items = (
 | 
				
			||||||
 | 
					        generate_diff(wlan_ucentral_schema_url, latest_version_id, previous_version_id, path=path))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if added_keys or removed_keys or changed_items:
 | 
				
			||||||
 | 
					        logging.info(f"Differences found in the schema:")
 | 
				
			||||||
 | 
					        if added_keys:
 | 
				
			||||||
 | 
					            added_keys = [list(key) for key in added_keys]
 | 
				
			||||||
 | 
					            added_keys = sorted(added_keys)
 | 
				
			||||||
 | 
					            message = ("Note: These keys were not present in old schema and have been added in the new schema.\n\n"
 | 
				
			||||||
 | 
					                       + tabulate(added_keys, headers=['Key Paths', 'Values'], tablefmt='fancy_grid'))
 | 
				
			||||||
 | 
					            logging.info("\nAdded keys:\n" + message + "\n")
 | 
				
			||||||
 | 
					            allure.attach(message, name="Added keys:")
 | 
				
			||||||
 | 
					        if removed_keys:
 | 
				
			||||||
 | 
					            removed_keys = [[key] for key in removed_keys]
 | 
				
			||||||
 | 
					            removed_keys = sorted(removed_keys)
 | 
				
			||||||
 | 
					            message = ("Note: These keys were present in the old schema but have been removed in the new schema.\n\n"
 | 
				
			||||||
 | 
					                       + tabulate(removed_keys, headers=['Key Paths'], tablefmt='fancy_grid'))
 | 
				
			||||||
 | 
					            logging.info("\nRemoved keys:\n" + message + "\n")
 | 
				
			||||||
 | 
					            allure.attach(message, name="Removed keys:")
 | 
				
			||||||
 | 
					        if changed_items:
 | 
				
			||||||
 | 
					            changed_items = [list(key) for key in changed_items]
 | 
				
			||||||
 | 
					            changed_items = sorted(changed_items)
 | 
				
			||||||
 | 
					            message = ("Note: The value at these key paths have been modified.\n\n"
 | 
				
			||||||
 | 
					                       + tabulate(changed_items, headers=['Key Paths', 'Old Value', 'New Value'], tablefmt='fancy_grid'))
 | 
				
			||||||
 | 
					            logging.info("\nChanged Items:\n" + message + "\n")
 | 
				
			||||||
 | 
					            allure.attach(message, name="Changed items:")
 | 
				
			||||||
 | 
					        pytest.fail(f"Differences found in the schema, check Test Body for Added/Removed/Changed items")
 | 
				
			||||||
 | 
					    return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def validate_state_message_through_ap(test_object, target_object, config_data):
 | 
				
			||||||
 | 
					    def get_type_of_message(message):
 | 
				
			||||||
 | 
					        type_of_message = "unknown"
 | 
				
			||||||
 | 
					        if isinstance(message, dict):
 | 
				
			||||||
 | 
					            type_of_message = "object"
 | 
				
			||||||
 | 
					        elif isinstance(message, list):
 | 
				
			||||||
 | 
					            type_of_message = "array"
 | 
				
			||||||
 | 
					        elif isinstance(message, int):
 | 
				
			||||||
 | 
					            type_of_message = "integer"
 | 
				
			||||||
 | 
					        elif isinstance(message, float):
 | 
				
			||||||
 | 
					            type_of_message = "number"
 | 
				
			||||||
 | 
					        elif isinstance(message, str):
 | 
				
			||||||
 | 
					            type_of_message = "string"
 | 
				
			||||||
 | 
					        return type_of_message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def verify_type_of_value(message, schema, path):
 | 
				
			||||||
 | 
					        if '$ref' in schema:
 | 
				
			||||||
 | 
					            return verify_type_of_value(message,
 | 
				
			||||||
 | 
					                                        full_schema[schema['$ref'].split('/')[1]][schema['$ref'].split('/')[2]],
 | 
				
			||||||
 | 
					                                        path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nonlocal missing_keys, type_mismatch, enum_mismatch, pattern_mismatch, other_discrepancies
 | 
				
			||||||
 | 
					        if 'enum' in schema:
 | 
				
			||||||
 | 
					            if message not in schema['enum']:
 | 
				
			||||||
 | 
					                enum_mismatch.add(f"{path} = '{message}' is not in the schema enum: {schema['enum']}.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if 'type' not in schema:
 | 
				
			||||||
 | 
					            discrepancy = f"Type not defined in schema for '{path}'. "
 | 
				
			||||||
 | 
					            if 'properties' in schema:
 | 
				
			||||||
 | 
					                discrepancy += f"Assumed type as 'object' for this path to continue."
 | 
				
			||||||
 | 
					                schema['type'] = 'object'
 | 
				
			||||||
 | 
					            elif 'items' in schema:
 | 
				
			||||||
 | 
					                discrepancy += f"Assumed type as 'array' for this path to continue."
 | 
				
			||||||
 | 
					                schema['type'] = 'array'
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                discrepancy += "Could not validate this path any further."
 | 
				
			||||||
 | 
					                other_discrepancies.add(discrepancy)
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            other_discrepancies.add(discrepancy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if schema['type'] == 'integer':
 | 
				
			||||||
 | 
					            if not isinstance(message, int):
 | 
				
			||||||
 | 
					                type_mismatch.add((path, get_type_of_message(message), 'integer'))
 | 
				
			||||||
 | 
					        elif schema['type'] == 'number':
 | 
				
			||||||
 | 
					            if not isinstance(message, int) and not isinstance(message, float):
 | 
				
			||||||
 | 
					                type_mismatch.add((path, get_type_of_message(message), 'number'))
 | 
				
			||||||
 | 
					        elif schema['type'] == 'string':
 | 
				
			||||||
 | 
					            if not isinstance(message, str):
 | 
				
			||||||
 | 
					                type_mismatch.add((path, get_type_of_message(message), 'string'))
 | 
				
			||||||
 | 
					        elif schema['type'] == 'array':
 | 
				
			||||||
 | 
					            if not isinstance(message, list):
 | 
				
			||||||
 | 
					                type_mismatch.add((path, get_type_of_message(message), 'array'))
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            if 'properties' in schema:
 | 
				
			||||||
 | 
					                other_discrepancies.add(f"An array can't have properties, at '{path}'.")
 | 
				
			||||||
 | 
					            if 'items' not in schema:
 | 
				
			||||||
 | 
					                other_discrepancies.add(f"Items not defined in schema for array at '{path}'.")
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            for i in range(len(message)):
 | 
				
			||||||
 | 
					                verify_type_of_value(message[i], schema['items'], f"{path} > [item]")
 | 
				
			||||||
 | 
					        elif schema['type'] == 'object':
 | 
				
			||||||
 | 
					            if not isinstance(message, dict):
 | 
				
			||||||
 | 
					                type_mismatch.add((path, get_type_of_message(message), 'object'))
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            if 'items' in schema:
 | 
				
			||||||
 | 
					                other_discrepancies.add(f"An object can't have items, at '{path}'.")
 | 
				
			||||||
 | 
					            if 'properties' not in schema and 'patternProperties' not in schema and '$ref' not in schema:
 | 
				
			||||||
 | 
					                other_discrepancies.add(f"Properties not defined in schema for object at '{path}'.")
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            for key in message:
 | 
				
			||||||
 | 
					                if 'patternProperties' in schema:
 | 
				
			||||||
 | 
					                    pattern = ""
 | 
				
			||||||
 | 
					                    for key_name in schema['patternProperties']:
 | 
				
			||||||
 | 
					                        pattern = key_name
 | 
				
			||||||
 | 
					                    if not re.match(pattern, key, re.IGNORECASE):
 | 
				
			||||||
 | 
					                        pattern_mismatch.add(f"Key name '{path} > \"{key}\"' does not match the pattern '{pattern}'"
 | 
				
			||||||
 | 
					                                             f" in schema.")
 | 
				
			||||||
 | 
					                    return verify_type_of_value(message[key], schema['patternProperties'][pattern],
 | 
				
			||||||
 | 
					                                                f"{path} > {key}")
 | 
				
			||||||
 | 
					                if key == '$ref':
 | 
				
			||||||
 | 
					                    if 'ref' not in schema['properties']:
 | 
				
			||||||
 | 
					                        missing_keys.add(f'{path}.ref')
 | 
				
			||||||
 | 
					                        continue
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        return verify_type_of_value(message['$ref'], schema['properties']['ref'],
 | 
				
			||||||
 | 
					                                                    f"{path}.'$ref'")
 | 
				
			||||||
 | 
					                elif 'properties' in schema and key not in schema['properties']:
 | 
				
			||||||
 | 
					                    missing_keys.add(f'{path} > {key}')
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                verify_type_of_value(message[key], schema['properties'][key], f"{path} > {key}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for ap in range(len(target_object.device_under_tests_info)):
 | 
				
			||||||
 | 
					        serial_number = target_object.device_under_tests_info[ap]['identifier']
 | 
				
			||||||
 | 
					        logging.info(f"Configuration : {config_data}")
 | 
				
			||||||
 | 
					        payload = {"configuration": json.dumps(config_data), "serialNumber": serial_number, "UUID": 1}
 | 
				
			||||||
 | 
					        uri = target_object.firmware_library_object.sdk_client.build_uri(
 | 
				
			||||||
 | 
					            "device/" + serial_number + "/configure")
 | 
				
			||||||
 | 
					        logging.info("Sending Command: " + "\n" + str(uri) + "\n" +
 | 
				
			||||||
 | 
					                     "TimeStamp: " + str(datetime.utcnow()) + "\n" +
 | 
				
			||||||
 | 
					                     "Data: " + str(json.dumps(payload, indent=2)) + "\n" +
 | 
				
			||||||
 | 
					                     "Headers: " + str(target_object.firmware_library_object.sdk_client.make_headers()))
 | 
				
			||||||
 | 
					        allure.attach(name="Push Config:", body="Sending Command: " + "\n" + str(uri) + "\n" +
 | 
				
			||||||
 | 
					                                                "TimeStamp: " + str(datetime.utcnow()) + "\n" +
 | 
				
			||||||
 | 
					                                                "Data: " + str(payload) + "\n" +
 | 
				
			||||||
 | 
					                                                "Headers: " + str(
 | 
				
			||||||
 | 
					            target_object.firmware_library_object.sdk_client.make_headers()))
 | 
				
			||||||
 | 
					        resp = requests.post(uri, data=json.dumps(payload),
 | 
				
			||||||
 | 
					                             headers=target_object.firmware_library_object.sdk_client.make_headers(),
 | 
				
			||||||
 | 
					                             verify=False, timeout=120)
 | 
				
			||||||
 | 
					        logging.info(resp.json())
 | 
				
			||||||
 | 
					        allure.attach(name=f"Response - {resp.status_code}{resp.reason}", body=str(resp.json()))
 | 
				
			||||||
 | 
					        if int(resp.status_code) == 200:
 | 
				
			||||||
 | 
					            time.sleep(120)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            pytest.fail("Configuration Push Failed")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check RX message from AP after config push
 | 
				
			||||||
 | 
					        target_object.dut_library_object.get_dut_logs()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # get pushed config from ap
 | 
				
			||||||
 | 
					        target_object.dut_library_object.run_generic_command(cmd="cat /etc/ucentral/ucentral.active",
 | 
				
			||||||
 | 
					                                                                 attach_allure=True)
 | 
				
			||||||
 | 
					        # check ssid info in iwinfo
 | 
				
			||||||
 | 
					        iw_info = target_object.dut_library_object.get_iwinfo()
 | 
				
			||||||
 | 
					        if iw_info is not None:
 | 
				
			||||||
 | 
					            matches = re.findall(r'(\S+)\s+ESSID:\s+"(.*?)"', iw_info)
 | 
				
			||||||
 | 
					            if matches and len(matches) != 0:
 | 
				
			||||||
 | 
					                data = {interface: essid for interface, essid in matches}
 | 
				
			||||||
 | 
					                logging.info(f"All available interfaces and ssid:\n{data}")
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                pytest.fail("Some or ALL Configured SSID's are not present in iwinfo")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        radio_5g = None
 | 
				
			||||||
 | 
					        dict_all_radios_5g = {"wave2_5g_radios": test_object.wave2_5g_radios,
 | 
				
			||||||
 | 
					                              "wave1_radios": test_object.wave1_radios,
 | 
				
			||||||
 | 
					                              "mtk_radios": test_object.mtk_radios,
 | 
				
			||||||
 | 
					                              "ax200_radios": test_object.ax200_radios,
 | 
				
			||||||
 | 
					                              "ax210_radios": test_object.ax210_radios}
 | 
				
			||||||
 | 
					        for radio in dict_all_radios_5g:
 | 
				
			||||||
 | 
					            if len(dict_all_radios_5g[radio]) > 0:
 | 
				
			||||||
 | 
					                radio_5g = dict_all_radios_5g[radio][0]
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        test_object.pre_cleanup()
 | 
				
			||||||
 | 
					        fiveg_sta_got_ip = test_object.client_connect_using_radio(ssid="captive-credential-4",
 | 
				
			||||||
 | 
					                                                                       passkey="OpenWifi",
 | 
				
			||||||
 | 
					                                                                       security="wpa2", radio=radio_5g,
 | 
				
			||||||
 | 
					                                                                       station_name=["station-5G"],
 | 
				
			||||||
 | 
					                                                                       attach_port_info=False,
 | 
				
			||||||
 | 
					                                                                       attach_station_data=False)
 | 
				
			||||||
 | 
					        if not fiveg_sta_got_ip:
 | 
				
			||||||
 | 
					            logging.info("Station did not get IP address")
 | 
				
			||||||
 | 
					            pytest.fail("Station did not get IP address")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logging.info("Waiting for 30 seconds before fetching state message...")
 | 
				
			||||||
 | 
					        time.sleep(30)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Fetching the schema from GitHub
 | 
				
			||||||
 | 
					        full_schema = get_github_file("https://github.com/Telecominfraproject/wlan-ucentral-schema/blob/main"
 | 
				
			||||||
 | 
					                                      "/ucentral.state.pretty.json")
 | 
				
			||||||
 | 
					        logging.info(f"State Schema: \n{full_schema}")
 | 
				
			||||||
 | 
					        allure.attach(full_schema, name=f"Schema:")
 | 
				
			||||||
 | 
					        full_schema = json.loads(full_schema)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Fetching the state message from AP
 | 
				
			||||||
 | 
					        full_message = target_object.get_dut_library_object().run_generic_command(cmd="cat /tmp/ucentral.state",
 | 
				
			||||||
 | 
					                                                                                      idx=ap, print_log=True)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            full_message = json.dumps(json.loads(full_message), indent=4)
 | 
				
			||||||
 | 
					        except json.JSONDecodeError:
 | 
				
			||||||
 | 
					            logging.info("Extra characters appeared as part of the state message from AP!")
 | 
				
			||||||
 | 
					            allure.attach(full_message, name="Response with extra characters as received from AP:")
 | 
				
			||||||
 | 
					            logging.info("Trying to remove extra characters.")
 | 
				
			||||||
 | 
					            full_message = '{' + re.split(r"{", full_message, maxsplit=1)[1].strip()
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                full_message = json.dumps(json.loads(full_message), indent=4)
 | 
				
			||||||
 | 
					            except json.JSONDecodeError:
 | 
				
			||||||
 | 
					                logging.info("Failed to remove the extra unwanted characters.")
 | 
				
			||||||
 | 
					                logging.info(f"State Message after trial: \n{full_message}")
 | 
				
			||||||
 | 
					                pytest.fail("Extra characters appeared as part of the state message from AP!")
 | 
				
			||||||
 | 
					        logging.info(f"State Message: \n{full_message}")
 | 
				
			||||||
 | 
					        allure.attach(full_message, name=f"State Message:")
 | 
				
			||||||
 | 
					        full_message = json.loads(full_message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for key in full_message["state"]:
 | 
				
			||||||
 | 
					            full_message[key] = full_message["state"][key]
 | 
				
			||||||
 | 
					        del full_message["state"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        missing_keys = set()
 | 
				
			||||||
 | 
					        type_mismatch = set()
 | 
				
			||||||
 | 
					        enum_mismatch = set()
 | 
				
			||||||
 | 
					        pattern_mismatch = set()
 | 
				
			||||||
 | 
					        other_discrepancies = set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if full_schema['type'] == 'object':
 | 
				
			||||||
 | 
					            if not isinstance(full_message, dict):
 | 
				
			||||||
 | 
					                type_mismatch.add(("State Message", 'unknown', 'object'))
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                for key in full_message:
 | 
				
			||||||
 | 
					                    if (key == '$ref' and 'ref' not in full_schema['properties']) or (
 | 
				
			||||||
 | 
					                            key not in full_schema['properties']):
 | 
				
			||||||
 | 
					                        missing_keys.add(key)
 | 
				
			||||||
 | 
					                        continue
 | 
				
			||||||
 | 
					                    verify_type_of_value(full_message[key], full_schema['properties'][key], key)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            logging.info(
 | 
				
			||||||
 | 
					                f"Did not expect type of state message in the schema to be {full_schema['type']} and not 'object'.")
 | 
				
			||||||
 | 
					            pytest.skip(
 | 
				
			||||||
 | 
					                f"Did not expect type of state message in the schema to be {full_schema['type']} and not 'object'.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if missing_keys or type_mismatch or enum_mismatch or pattern_mismatch or other_discrepancies:
 | 
				
			||||||
 | 
					            logging.info("Detected Discrepancies:\n")
 | 
				
			||||||
 | 
					            if missing_keys:
 | 
				
			||||||
 | 
					                missing_keys = [[key] for key in missing_keys]
 | 
				
			||||||
 | 
					                missing_keys = sorted(missing_keys)
 | 
				
			||||||
 | 
					                message = ("Note: These keys are present in the state message received from AP but missing in the "
 | 
				
			||||||
 | 
					                           "state schema.\n\n" + tabulate(missing_keys, headers=['Key Paths'], tablefmt='fancy_grid'))
 | 
				
			||||||
 | 
					                logging.info("\nMissing Keys:\n" + message + "\n")
 | 
				
			||||||
 | 
					                allure.attach(message, name="Missing keys:")
 | 
				
			||||||
 | 
					            if type_mismatch:
 | 
				
			||||||
 | 
					                type_mismatch = [list(key) for key in type_mismatch]
 | 
				
			||||||
 | 
					                type_mismatch = sorted(type_mismatch)
 | 
				
			||||||
 | 
					                message = ("Note: The type of values present in the state message received from AP is different "
 | 
				
			||||||
 | 
					                           "from the one described in the state schema.\n\n"
 | 
				
			||||||
 | 
					                           + tabulate(type_mismatch, headers=['Key Path', 'Type in State Message'
 | 
				
			||||||
 | 
					                            , 'Type according to State Schema'], tablefmt='fancy_grid'))
 | 
				
			||||||
 | 
					                logging.info("\nType Mismatches:\n" + message + "\n")
 | 
				
			||||||
 | 
					                allure.attach(message, name="Type Mismatches:")
 | 
				
			||||||
 | 
					            if enum_mismatch:
 | 
				
			||||||
 | 
					                enum_mismatch = [[key] for key in enum_mismatch]
 | 
				
			||||||
 | 
					                enum_mismatch = sorted(enum_mismatch)
 | 
				
			||||||
 | 
					                message = ("Note: Enums are predefined possible values of a key in the schema, the value present at "
 | 
				
			||||||
 | 
					                           "the following keys are not part of the enum in state schema.\n\n"
 | 
				
			||||||
 | 
					                           + tabulate(enum_mismatch, tablefmt='fancy_grid'))
 | 
				
			||||||
 | 
					                logging.info("\nEnum Mismatches:\n" + message + "\n")
 | 
				
			||||||
 | 
					                allure.attach(message, name="Enum Mismatches:")
 | 
				
			||||||
 | 
					            if pattern_mismatch:
 | 
				
			||||||
 | 
					                pattern_mismatch = [[key] for key in pattern_mismatch]
 | 
				
			||||||
 | 
					                pattern_mismatch = sorted(pattern_mismatch)
 | 
				
			||||||
 | 
					                message = ("Note: Patterns are defined for some of the keys in the schema, the key name present "
 | 
				
			||||||
 | 
					                           "inside the state message does not match the specified pattern in the state "
 | 
				
			||||||
 | 
					                           "schema.\n\n" + tabulate(pattern_mismatch, tablefmt='fancy_grid'))
 | 
				
			||||||
 | 
					                logging.info("\nPattern Mismatches:\n" + message + "\n")
 | 
				
			||||||
 | 
					                allure.attach(message, name="Pattern Mismatches:")
 | 
				
			||||||
 | 
					            if other_discrepancies:
 | 
				
			||||||
 | 
					                other_discrepancies = [[key] for key in other_discrepancies]
 | 
				
			||||||
 | 
					                other_discrepancies = sorted(other_discrepancies)
 | 
				
			||||||
 | 
					                message = ("Note: These are possible problems with the schema itself.\n\n" +
 | 
				
			||||||
 | 
					                           tabulate(other_discrepancies, tablefmt='fancy_grid'))
 | 
				
			||||||
 | 
					                logging.info("\nOther Discrepancies:\n" + message + "\n")
 | 
				
			||||||
 | 
					                allure.attach(message, name="Other Discrepancies:")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            pytest.fail("Detected Discrepancies: Check Test Body for Missing Keys or Type/Pattern/Enum Mismatches")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            logging.info("No discrepancies found.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@allure.feature("Schema Validation")
 | 
				
			||||||
 | 
					@allure.parent_suite("Schema Validation")
 | 
				
			||||||
 | 
					@allure.suite("Through GitHub")
 | 
				
			||||||
 | 
					@pytest.mark.through_github
 | 
				
			||||||
 | 
					class TestSchemaValidationThroughGitHub(object):
 | 
				
			||||||
 | 
					    @allure.sub_suite("Schema JSON")
 | 
				
			||||||
 | 
					    @pytest.mark.schema_json
 | 
				
			||||||
 | 
					    @allure.title("Checking ucentral.schema.json")
 | 
				
			||||||
 | 
					    @allure.testcase(url="https://telecominfraproject.atlassian.net/browse/WIFI-13443", name="WIFI-13443")
 | 
				
			||||||
 | 
					    def test_schema_through_github(self, commit_id):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Validating the ucentral schema to ensure consistency and integrity in the system. The validation process
 | 
				
			||||||
 | 
					        involves detecting any changes in the schema YML files and comparing them periodically after any new commits
 | 
				
			||||||
 | 
					        int the wlan-ucentral-schema repo.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Objective is to identify any modifications, additions, or removals in the file: ucentral.schema.json.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Unique Marker:
 | 
				
			||||||
 | 
					        schema_validation and through_github and schema_json
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        validate_schema_through_github(commit_id, "/ucentral.schema.json")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @allure.sub_suite("Schema JSON")
 | 
				
			||||||
 | 
					    @pytest.mark.schema_full_json
 | 
				
			||||||
 | 
					    @allure.title("Checking ucentral.schema.full.json")
 | 
				
			||||||
 | 
					    @allure.testcase(url="https://telecominfraproject.atlassian.net/browse/WIFI-13443", name="WIFI-13443")
 | 
				
			||||||
 | 
					    def test_schema_full_through_github(self, commit_id):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Validating the ucentral schema to ensure consistency and integrity in the system. The validation process
 | 
				
			||||||
 | 
					        involves detecting any changes in the schema YML files and comparing them periodically after any new commits
 | 
				
			||||||
 | 
					        int the wlan-ucentral-schema repo.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Objective is to identify any modifications, additions, or removals in the file: ucentral.schema.full.json.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Unique Marker:
 | 
				
			||||||
 | 
					        schema_validation and through_github and schema_full_json
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        validate_schema_through_github(commit_id, "/ucentral.schema.full.json")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @allure.sub_suite("Schema JSON")
 | 
				
			||||||
 | 
					    @pytest.mark.schema_pretty_json
 | 
				
			||||||
 | 
					    @allure.title("Checking ucentral.schema.pretty.json")
 | 
				
			||||||
 | 
					    @allure.testcase(url="https://telecominfraproject.atlassian.net/browse/WIFI-13443", name="WIFI-13443")
 | 
				
			||||||
 | 
					    def test_schema_pretty_through_github(self, commit_id):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Validating the ucentral schema to ensure consistency and integrity in the system. The validation process
 | 
				
			||||||
 | 
					        involves detecting any changes in the schema YML files and comparing them periodically after any new commits
 | 
				
			||||||
 | 
					        int the wlan-ucentral-schema repo.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Objective is to identify any modifications, additions, or removals in the file: ucentral.schema.pretty.json.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Unique Marker:
 | 
				
			||||||
 | 
					        schema_validation and through_github and schema_pretty_json
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        validate_schema_through_github(commit_id, "/ucentral.schema.pretty.json")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @allure.sub_suite("State JSON")
 | 
				
			||||||
 | 
					    @pytest.mark.state_pretty_json
 | 
				
			||||||
 | 
					    @allure.title("Checking ucentral.state.pretty.json")
 | 
				
			||||||
 | 
					    @allure.testcase(url="https://telecominfraproject.atlassian.net/browse/WIFI-13443", name="WIFI-13443")
 | 
				
			||||||
 | 
					    def test_state_pretty_through_github(self, commit_id):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Validating the ucentral schema to ensure consistency and integrity in the system. The validation process
 | 
				
			||||||
 | 
					        involves detecting any changes in the schema YML files and comparing them periodically after any new commits
 | 
				
			||||||
 | 
					        int the wlan-ucentral-schema repo.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Objective is to identify any modifications, additions, or removals in the file: ucentral.state.pretty.json.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Unique Marker:
 | 
				
			||||||
 | 
					        schema_validation and through_github and state_pretty_json
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        validate_schema_through_github(commit_id, "/ucentral.state.pretty.json")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@allure.feature("Schema Validation")
 | 
				
			||||||
 | 
					@allure.parent_suite("Schema Validation")
 | 
				
			||||||
 | 
					@allure.suite("Through AP")
 | 
				
			||||||
 | 
					@allure.sub_suite("Schema Validation through State Messages")
 | 
				
			||||||
 | 
					@pytest.mark.through_ap_terminal
 | 
				
			||||||
 | 
					class TestSchemaValidationThroughAPTerminal(object):
 | 
				
			||||||
 | 
					    @pytest.mark.master_config_1
 | 
				
			||||||
 | 
					    @allure.title("Pushing master config-1")
 | 
				
			||||||
 | 
					    @allure.testcase(url="https://telecominfraproject.atlassian.net/browse/WIFI-13567", name="WIFI-13567")
 | 
				
			||||||
 | 
					    def test_state_message_schema_master_config_1(self, get_test_library, get_target_object, get_dut_logs_per_test_case,
 | 
				
			||||||
 | 
					                                                  get_test_device_logs, check_connectivity):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Validating the ucentral schema to ensure consistency and integrity in the system. The validation 
 | 
				
			||||||
 | 
					        process involves detecting any changes in the schema YML files and comparing them between the 
 | 
				
			||||||
 | 
					        live current state received from the AP and out ucentral state json output file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Objective is to detect discrepancies in data types (e.g., string to integer) and object structures.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Unique Marker:
 | 
				
			||||||
 | 
					        schema_validation and through_ap_terminal and master_config_1
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        validate_state_message_through_ap(get_test_library, get_target_object, config_data=config_data_1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @pytest.mark.master_config_2
 | 
				
			||||||
 | 
					    @allure.title("Pushing master config-2")
 | 
				
			||||||
 | 
					    @allure.testcase(url="https://telecominfraproject.atlassian.net/browse/WIFI-13567", name="WIFI-13567")
 | 
				
			||||||
 | 
					    def test_state_message_schema_master_config_2(self, get_test_library, get_target_object, get_dut_logs_per_test_case,
 | 
				
			||||||
 | 
					                                                  get_test_device_logs, check_connectivity):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Validating the ucentral schema to ensure consistency and integrity in the system. The validation
 | 
				
			||||||
 | 
					        process involves detecting any changes in the schema YML files and comparing them between the
 | 
				
			||||||
 | 
					        live current state received from the AP and out ucentral state json output file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Objective is to detect discrepancies in data types (e.g., string to integer) and object structures.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Unique Marker:
 | 
				
			||||||
 | 
					        schema_validation and through_ap_terminal and master_config_2
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        validate_state_message_through_ap(get_test_library, get_target_object, config_data=config_data_2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @pytest.mark.master_config_3
 | 
				
			||||||
 | 
					    @allure.title("Pushing master config-3")
 | 
				
			||||||
 | 
					    @allure.testcase(url="https://telecominfraproject.atlassian.net/browse/WIFI-13567", name="WIFI-13567")
 | 
				
			||||||
 | 
					    def test_state_message_schema_master_config_3(self, get_test_library, get_target_object, get_dut_logs_per_test_case,
 | 
				
			||||||
 | 
					                                                  get_test_device_logs, check_connectivity):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Validating the ucentral schema to ensure consistency and integrity in the system. The validation
 | 
				
			||||||
 | 
					        process involves detecting any changes in the schema YML files and comparing them between the
 | 
				
			||||||
 | 
					        live current state received from the AP and out ucentral state json output file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Objective is to detect discrepancies in data types (e.g., string to integer) and object structures.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Unique Marker:
 | 
				
			||||||
 | 
					        schema_validation and through_ap_terminal and master_config_3
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        validate_state_message_through_ap(get_test_library, get_target_object, config_data=config_data_3)
 | 
				
			||||||
		Reference in New Issue
	
	Block a user