def test_convergence_parametrization(client_library_server_current, mocked_session): MAX_ITER = 2 WAIT_TIME = 1 cl = ClientLibrary( url="https://0.0.0.0/fake_url/", username="******", password="******", convergence_wait_max_iter=MAX_ITER, convergence_wait_time=WAIT_TIME, ) # check that passing of value from client to lab is working lab = cl.create_lab() assert lab.wait_max_iterations == MAX_ITER assert lab.wait_time == WAIT_TIME with patch.object(Lab, "has_converged", return_value=False): with pytest.raises(RuntimeError) as err: lab.wait_until_lab_converged() assert ("has not converged, maximum tries %s exceeded" % MAX_ITER) in err.value.args[0] # try to override values on function with pytest.raises(RuntimeError) as err: lab.wait_until_lab_converged(max_iterations=1) assert ("has not converged, maximum tries %s exceeded" % 1) in err.value.args[0]
def test_import_lab_from_path_ng( client_library_compatible_version, mocked_session, tmp_path: Path ): client_library = ClientLibrary( url="http://0.0.0.0/fake_url/", username="******", password="******" ) Lab.sync = Mock() topology_data = '{"nodes": [], "links": [], "interfaces": []}' (tmp_path / "topology.ng").write_text(topology_data) with patch.object(Lab, "sync", autospec=True) as sync_mock: lab = client_library.import_lab_from_path( topology=(tmp_path / "topology.ng").as_posix() ) assert lab.title is not None assert lab.lab_base_url.startswith("https://0.0.0.0/fake_url/api/v0/labs/") client_library.session.post.assert_called_once_with( "https://0.0.0.0/fake_url/api/v0/import?is_json=true&title=topology.ng", data=topology_data, ) client_library.session.post.assert_called_once() client_library.session.post.return_value.raise_for_status.assert_called_once() sync_mock.assert_called_once_with()
def test_client_library_init_url(client_library_server_current, monkeypatch, via, params): (fail, url) = params expected_parts = None if fail else urlsplit(url) if via == "environment": env = url url = None else: env = "http://badhost" if url else None if env is None: monkeypatch.delenv("VIRL2_URL", raising=False) else: monkeypatch.setenv("VIRL2_URL", env) if fail: with pytest.raises( (InitializationError, requests.exceptions.InvalidURL)): ClientLibrary(url=url, username="******", password="******", allow_http=True) else: cl = ClientLibrary(url, username="******", password="******", allow_http=True) url_parts = urlsplit(cl._context.base_url) assert url_parts.scheme == (expected_parts.scheme or "https") assert url_parts.hostname == (expected_parts.hostname or expected_parts.path) assert url_parts.port == expected_parts.port assert url_parts.path == "/api/v0/" assert cl._context.base_url.endswith("/api/v0/") assert cl.username == "virl2" assert cl.password == "virl2"
def test_import_lab_offline( client_library_compatible_version, mocked_session, tmp_path: Path ): client_library = ClientLibrary( url="http://0.0.0.0/fake_url/", username="******", password="******" ) topology_file_path = "import_export/SampleData/topology-v0_0_4.json" topology = pkg_resources.resource_string("simple_common", topology_file_path) client_library.import_lab(topology, "topology-v0_0_4", offline=True)
def test_import_lab_offline(client_library_server_2_0_0, mocked_session, tmp_path: Path): client_library = ClientLibrary(url="http://0.0.0.0/fake_url/", username="******", password="******") topology_file_path = "tests/test_data/sample_topology.json" with open(topology_file_path) as fh: topology_file = fh.read() client_library.import_lab(topology_file, "topology-v0_0_4", offline=True)
def test_ssl_certificate_from_env_variable( client_library_compatible_version, monkeypatch, mocked_session ): monkeypatch.setitem(os.environ, "CA_BUNDLE", "/home/user/cert.pem") cl = ClientLibrary(url="http://0.0.0.0/fake_url/", username="******", password="******") assert cl.is_system_ready() assert cl.session.verify == "/home/user/cert.pem" assert cl.session.mock_calls[:4] == [ call.get("https://0.0.0.0/fake_url/api/v0/authok"), call.get().raise_for_status(), ]
def test_ssl_certificate_from_env_variable(monkeypatch, mocked_session): monkeypatch.setitem(os.environ, "CA_BUNDLE", "/home/user/cert.pem") cl = ClientLibrary(url="http://0.0.0.0/fake_url/", username="******", password="******") cl.wait_for_lld_connected() assert cl.session.verify == "/home/user/cert.pem" assert cl.session.mock_calls == [ call.get("https://0.0.0.0/fake_url/api/v0/labs"), call.get().raise_for_status(), call.get("https://0.0.0.0/fake_url/api/v0/wait_for_lld_connected"), call.get().raise_for_status() ]
def test_ssl_certificate(client_library_server_2_0_0, mocked_session): cl = ClientLibrary( url="http://0.0.0.0/fake_url/", username="******", password="******", ssl_verify="/home/user/cert.pem", ) cl.is_system_ready(wait=True) assert cl.session.verify == "/home/user/cert.pem" assert cl.session.mock_calls[:4] == [ call.get("https://0.0.0.0/fake_url/api/v0/authok"), call.get().raise_for_status(), ]
def test_client_library_str_and_repr(client_library_compatible_version): client_library = ClientLibrary("somehost", "virl2", password="******") assert ( repr(client_library) == "ClientLibrary('somehost', 'virl2', 'virl2', True, False, False)" ) assert str(client_library) == "ClientLibrary URL: https://somehost/api/v0/"
def test_ssl_certificate(mocked_session): cl = ClientLibrary( url="http://0.0.0.0/fake_url/", username="******", password="******", ssl_verify="/home/user/cert.pem", ) cl.wait_for_lld_connected() assert cl.session.verify == "/home/user/cert.pem" assert cl.session.mock_calls == [ call.get("https://0.0.0.0/fake_url/api/v0/labs"), call.get().raise_for_status(), call.get("https://0.0.0.0/fake_url/api/v0/wait_for_lld_connected"), call.get().raise_for_status() ]
def test_client_minor_version_lt_warn_1(client_library_server_2_19_0, caplog): with caplog.at_level(logging.WARNING): client_library = ClientLibrary("somehost", "virl2", password="******") assert client_library is not None assert ( "Please ensure the client version is compatible with the controller version. " "Client {}, controller 2.19.0.".format(CURRENT_VERSION) in caplog.text)
def test_client_library_init_password(monkeypatch, via, parms): url = "validhostname" (fail, password) = parms if via == "environment": # can't set a None value for an environment variable monkeypatch.setenv("VIRL2_PASS", password or "") password = None if fail: with pytest.raises( (InitializationError, requests.exceptions.InvalidURL)): cl = ClientLibrary(url=url, username="******", password=password) else: cl = ClientLibrary(url, username="******", password=password) assert cl.username == "virl2" assert cl.password == parms[1] assert cl._context.base_url == "https://validhostname/api/v0/"
def test_major_version_mismatch(client_library_server_1_0_0): with pytest.raises(InitializationError) as err: ClientLibrary("somehost", "virl2", password="******") assert str( err.value ) == "Major version mismatch. Client {}, controller 1.0.0.".format( CURRENT_VERSION)
def test_exact_version_no_warn(client_library_server_current, caplog): with caplog.at_level(logging.WARNING): client_library = ClientLibrary("somehost", "virl2", password="******") assert client_library is not None assert ( "Please ensure the client version is compatible with the controller version. " "Client {}, controller 2.0.0.".format(CURRENT_VERSION) not in caplog.text)
def test_exact_version_no_warn(client_library_exact_version, caplog): with caplog.at_level(logging.WARNING): client_library = ClientLibrary("somehost", "virl2", password="******") assert client_library is not None assert ( "Please ensure the client version is compatible with the server version. client 2.1.0, server 2.0.0" not in caplog.text )
def test_client_library_init_url(monkeypatch, via, parms): (fail, url) = parms if via == "environment": monkeypatch.setenv("VIRL2_URL", url) url = None if fail: with pytest.raises( (InitializationError, requests.exceptions.InvalidURL)): cl = ClientLibrary(url=url, username="******", password="******") else: cl = ClientLibrary(url, username="******", password="******") url_parts = urlsplit(cl._context.base_url) assert url_parts.scheme == "https" assert url_parts.hostname == "somehost" assert url_parts.port == 443 or url_parts.port is None assert cl._context.base_url.endswith("/api/v0/") assert cl.username == "virl2" assert cl.password == "virl2"
def test_client_library_init_allow_http(): cl = ClientLibrary("http://somehost", "virl2", "virl2", allow_http=True) url_parts = urlsplit(cl._context.base_url) assert url_parts.scheme == "http" assert url_parts.hostname == "somehost" assert url_parts.port is None assert cl._context.base_url.endswith("/api/v0/") assert cl.username == "virl2" assert cl.password == "virl2"
def test_incompatible_version(client_library_server_2_0_0): with pytest.raises(InitializationError) as err: with patch.object(ClientLibrary, "INCOMPATIBLE_CONTROLLER_VERSIONS", new=[Version("2.0.0")]): ClientLibrary("somehost", "virl2", password="******") assert (str( err.value) == "Controller version 2.0.0 is marked incompatible! " "List of versions marked explicitly as incompatible: [2.0.0].")
def test_import_lab_from_path_virl_title(client_library_server_current, mocked_session, tmp_path: Path): cl = ClientLibrary(url="https://0.0.0.0/fake_url/", username="******", password="******") Lab.sync = Mock() new_title = "new_title" (tmp_path / "topology.virl").write_text("<?xml version='1.0' encoding='UTF-8'?>") with patch.object(Lab, "sync", autospec=True) as sync_mock: lab = cl.import_lab_from_path(path=(tmp_path / "topology.virl").as_posix(), title=new_title) assert lab.title is not None assert lab.lab_base_url.startswith("https://0.0.0.0/fake_url/api/v0/labs/") cl.session.post.assert_called_once_with( f"https://0.0.0.0/fake_url/api/v0/import/virl-1x?title={new_title}", data="<?xml version='1.0' encoding='UTF-8'?>", )
def test_import_lab_from_path_virl( client_library_compatible_version, mocked_session, tmp_path: Path ): cl = ClientLibrary(url="http://0.0.0.0/fake_url/", username="******", password="******") Lab.sync = Mock() (tmp_path / "topology.virl").write_text("<?xml version='1.0' encoding='UTF-8'?>") with patch.object(Lab, "sync", autospec=True) as sync_mock: lab = cl.import_lab_from_path(topology=(tmp_path / "topology.virl").as_posix()) assert lab.title is not None assert lab.lab_base_url.startswith("https://0.0.0.0/fake_url/api/v0/labs/") cl.session.post.assert_called_once_with( "https://0.0.0.0/fake_url/api/v0/import/virl-1x?title=topology.virl", data="<?xml version='1.0' encoding='UTF-8'?>", ) cl.session.post.assert_called_once() cl.session.post.return_value.raise_for_status.assert_called_once() sync_mock.assert_called_once_with()
def test_auth_and_reauth_token(): # TODO: need to check what the purpose of this test is, and how it works with the automatic auth check on CL init # if there's environ vars for username and password set # then delete them b/c we rely on specific usernames # and passwords for this test! try: del os.environ["SIMPLE_PASS"] del os.environ["SIMPLE_USER"] except: pass # mock always successful authentication: responses.add(responses.POST, "https://0.0.0.0/fake_url/api/v0/authenticate", json="7bbcan78a98bch7nh3cm7hao3nc7") responses.add(responses.GET, "https://0.0.0.0/fake_url/api/v0/wait_for_lld_connected") responses.add(responses.GET, "https://0.0.0.0/fake_url/api/v0/wait_for_lld_connected", status=401) responses.add(responses.GET, "https://0.0.0.0/fake_url/api/v0/wait_for_lld_connected") responses.add(responses.GET, "https://0.0.0.0/fake_url/api/v0/wait_for_lld_connected", status=401) with pytest.raises(InitializationError): # Test returns custom exception when instructed to raise onk cl = ClientLibrary(url="http://0.0.0.0/fake_url/", username="******", password="******", raise_for_auth_failure=True) cl = ClientLibrary(url="http://0.0.0.0/fake_url/", username="******", password="******") cl.wait_for_lld_connected() # last request fails as after reauthentication status code of response is still 401: # with pytest.raises(requests.exceptions.HTTPError) as: # client_library.wait_for_lld_connected() # assert exc.value.response.status_code ==1 assert (responses.calls[0].request.url == "https://0.0.0.0/fake_url/api/v0/authenticate") assert json.loads(responses.calls[0].request.body) == { "username": "******", "password": "******", } assert (responses.calls[1].request.url == "https://0.0.0.0/fake_url/api/v0/labs") assert responses.calls[1].request.method == "GET" assert (responses.calls[2].request.url == "https://0.0.0.0/fake_url/api/v0/authenticate") assert (responses.calls[3].request.url == "https://0.0.0.0/fake_url/api/v0/labs") assert (responses.calls[4].request.url == "https://0.0.0.0/fake_url/api/v0/wait_for_lld_connected") assert len(responses.calls) == 5
def test_client_library_init_password(client_library_server_current, monkeypatch, via, params): url = "validhostname" (fail, password) = params if via == "environment": # can't set a None value for an environment variable env = password or "" password = None else: env = "badpass" if password else None if env is None: monkeypatch.delenv("VIRL2_PASS", raising=False) else: monkeypatch.setenv("VIRL2_PASS", env) if fail: with pytest.raises( (InitializationError, requests.exceptions.InvalidURL)): ClientLibrary(url=url, username="******", password=password) else: cl = ClientLibrary(url, username="******", password=password) assert cl.username == "virl2" assert cl.password == params[1] assert cl._context.base_url == "https://validhostname/api/v0/"
def test_auth_and_reauth_token(client_library_server_current): # mock failed authentication: responses.add(responses.POST, "https://0.0.0.0/fake_url/api/v0/authenticate", status=403) responses.add(responses.GET, "https://0.0.0.0/fake_url/api/v0/authok", status=401) # mock successful authentication: responses.add( responses.POST, "https://0.0.0.0/fake_url/api/v0/authenticate", json="7bbcan78a98bch7nh3cm7hao3nc7", ) responses.add(responses.GET, "https://0.0.0.0/fake_url/api/v0/authok") # mock get labs responses.add(responses.GET, "https://0.0.0.0/fake_url/api/v0/labs", json=[]) with pytest.raises(InitializationError): # Test returns custom exception when instructed to raise on failure ClientLibrary( url="https://0.0.0.0/fake_url/", username="******", password="******", raise_for_auth_failure=True, ) cl = ClientLibrary(url="https://0.0.0.0/fake_url/", username="******", password="******") cl.all_labs() # for idx, item in enumerate(responses.calls): # print(idx, item.request.url) # # this is what we expect: # 0 https://0.0.0.0/fake_url/api/v0/authenticate # 1 https://0.0.0.0/fake_url/api/v0/authenticate # 2 https://0.0.0.0/fake_url/api/v0/authok # 3 https://0.0.0.0/fake_url/api/v0/authenticate # 4 https://0.0.0.0/fake_url/api/v0/authok # 5 https://0.0.0.0/fake_url/api/v0/labs assert (responses.calls[0].request.url == "https://0.0.0.0/fake_url/api/v0/authenticate") assert json.loads(responses.calls[0].request.body) == { "username": "******", "password": "******", } assert (responses.calls[1].request.url == "https://0.0.0.0/fake_url/api/v0/authenticate") assert responses.calls[ 2].request.url == "https://0.0.0.0/fake_url/api/v0/authok" assert (responses.calls[3].request.url == "https://0.0.0.0/fake_url/api/v0/authenticate") assert responses.calls[ 4].request.url == "https://0.0.0.0/fake_url/api/v0/authok" assert responses.calls[ 5].request.url == "https://0.0.0.0/fake_url/api/v0/labs" assert len(responses.calls) == 6
def test_auth_and_reauth_token(client_library_server_2_0_0): # TODO: need to check what the purpose of this test is, and how it # works with the automatic auth check on CL init # if there's environ vars for username and password set # then delete them b/c we rely on specific usernames # and passwords for this test! # docs: https://github.com/getsentry/responses try: del os.environ["VIRL2_PASS"] del os.environ["VIRL2_USER"] except KeyError: pass # mock failed authentication: responses.add(responses.POST, "https://0.0.0.0/fake_url/api/v0/authenticate", status=403) responses.add(responses.GET, "https://0.0.0.0/fake_url/api/v0/authok", status=401) # mock successful authentication: responses.add( responses.POST, "https://0.0.0.0/fake_url/api/v0/authenticate", json="7bbcan78a98bch7nh3cm7hao3nc7", ) responses.add(responses.GET, "https://0.0.0.0/fake_url/api/v0/authok") # mock get labs responses.add(responses.GET, "https://0.0.0.0/fake_url/api/v0/labs", json=[]) with pytest.raises(InitializationError): # Test returns custom exception when instructed to raise on failure ClientLibrary( url="http://0.0.0.0/fake_url/", username="******", password="******", raise_for_auth_failure=True, ) cl = ClientLibrary(url="http://0.0.0.0/fake_url/", username="******", password="******") cl.all_labs() # for idx, item in enumerate(responses.calls): # print(idx, item.request.url) # # this is what we expect: # 0 https://0.0.0.0/fake_url/api/v0/authenticate # 1 https://0.0.0.0/fake_url/api/v0/authenticate # 2 https://0.0.0.0/fake_url/api/v0/authok # 3 https://0.0.0.0/fake_url/api/v0/authenticate # 4 https://0.0.0.0/fake_url/api/v0/authok # 5 https://0.0.0.0/fake_url/api/v0/labs assert (responses.calls[0].request.url == "https://0.0.0.0/fake_url/api/v0/authenticate") assert json.loads(responses.calls[0].request.body) == { "username": "******", "password": "******", } assert (responses.calls[1].request.url == "https://0.0.0.0/fake_url/api/v0/authenticate") assert responses.calls[ 2].request.url == "https://0.0.0.0/fake_url/api/v0/authok" assert (responses.calls[3].request.url == "https://0.0.0.0/fake_url/api/v0/authenticate") assert responses.calls[ 4].request.url == "https://0.0.0.0/fake_url/api/v0/authok" assert responses.calls[ 5].request.url == "https://0.0.0.0/fake_url/api/v0/labs" assert len(responses.calls) == 6
def test_client_library_init_disallow_http(client_library_server_current): with pytest.raises(InitializationError, match="must be https"): ClientLibrary("http://somehost", "virl2", "virl2") with pytest.raises(InitializationError, match="must be https"): ClientLibrary("http://somehost", "virl2", "virl2", allow_http=False)
def test_major_version_mismatch(client_library_incompatible_version): with pytest.raises(InitializationError) as err: ClientLibrary("somehost", "virl2", password="******") assert str(err.value) == "Major version mismatch. server 1.0.0, client 2.1.0"