diff --git a/oscar_python/_utils.py b/oscar_python/_utils.py index 945d8c4..5f3a27b 100644 --- a/oscar_python/_utils.py +++ b/oscar_python/_utils.py @@ -15,6 +15,7 @@ import base64 import json import os +import time as _time import requests import liboidcagent as agent _DEFAULT_TIMEOUT = 60 @@ -32,21 +33,29 @@ def make_request(c, path, method, **kwargs): url = c.endpoint+path - if method in ["post", "put"]: - if "token" in kwargs.keys() and kwargs["token"]: - headers = get_headers_with_token(kwargs["token"]) - req_kwargs = {"headers": headers, "verify": c.ssl, "timeout": timeout} - if "data" in kwargs.keys() and kwargs["data"]: - req_kwargs["data"] = kwargs["data"] - result = requests.request(method, url, **req_kwargs) - else: - result = requests.request(method, url, headers=headers, verify=c.ssl, timeout=timeout) + max_retries = 3 + for attempt in range(max_retries): + if method in ["post", "put", "delete"]: + if "token" in kwargs.keys() and kwargs["token"]: + headers = get_headers_with_token(kwargs["token"]) + req_kwargs = {"headers": headers, "verify": c.ssl, "timeout": timeout} + if "data" in kwargs.keys() and kwargs["data"]: + req_kwargs["data"] = kwargs["data"] + req_kwargs["headers"]["Content-Type"] = "application/json" + result = requests.request(method, url, **req_kwargs) + else: + result = requests.request(method, url, headers=headers, verify=c.ssl, timeout=timeout) - if "handle" in kwargs.keys() and kwargs["handle"] is False: - return result + if "handle" in kwargs.keys() and kwargs["handle"] is False: + return result - result.raise_for_status() - return result + if result.status_code == 500 and method == "put": + if attempt < max_retries - 1: + _time.sleep(0.5 * (2 ** attempt)) + continue + + result.raise_for_status() + return result def get_headers(c): diff --git a/oscar_python/client.py b/oscar_python/client.py index 8c74e8d..2ceae41 100644 --- a/oscar_python/client.py +++ b/oscar_python/client.py @@ -33,6 +33,7 @@ _BUCKETS_PATH = "/system/buckets" _METRICS_PATH = "/system/metrics" _QUOTAS_USER_PATH = "/system/quotas/user" +_FEDERATION_PATH = "/system/federation" # _JOB_PATH = "/job" @@ -340,3 +341,30 @@ def get_user_quota(self, user_id): def update_user_quota(self, user_id, cpu, memory): data = json.dumps({"cpu": cpu, "memory": memory}) return utils.make_request(self, _QUOTAS_USER_PATH + "/" + user_id, _PUT, data=data) + + """ Get federation members for a service """ + def get_federation(self, service_name): + return utils.make_request(self, _FEDERATION_PATH + "/" + service_name, _GET) + + """ Add federation members to a service """ + def add_federation_members(self, service_name, members, clusters=None, storage_providers=None): + data = {"members": members} + if clusters: + data["clusters"] = clusters + if storage_providers: + data["storage_providers"] = storage_providers + return utils.make_request(self, _FEDERATION_PATH + "/" + service_name, _POST, data=json.dumps(data)) + + """ Update federation members of a service """ + def update_federation_members(self, service_name, members, update, clusters=None, storage_providers=None): + data = {"members": members, "update": update} + if clusters: + data["clusters"] = clusters + if storage_providers: + data["storage_providers"] = storage_providers + return utils.make_request(self, _FEDERATION_PATH + "/" + service_name, _PUT, data=json.dumps(data)) + + """ Remove federation members from a service """ + def remove_federation_members(self, service_name, members, delete=False): + data = {"members": members, "delete": delete} + return utils.make_request(self, _FEDERATION_PATH + "/" + service_name, _DELETE, data=json.dumps(data)) diff --git a/tests/test_client.py b/tests/test_client.py index 16ba450..7797718 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -322,3 +322,91 @@ def test_update_user_quota(options): client.update_user_quota("test_user", "2", "4Gi") mock_request.assert_called_once_with(client, "/system/quotas/user/test_user", "put", data=json.dumps({"cpu": "2", "memory": "4Gi"})) + + +def test_get_federation(options): + client = Client(options) + with patch('oscar_python._utils.make_request') as mock_request: + client.get_federation("test_service") + mock_request.assert_called_once_with(client, "/system/federation/test_service", "get") + + +def test_add_federation_members(options): + client = Client(options) + members = [{"type": "oscar", "cluster_id": "c1", "service_name": "s1", "priority": 0}] + with patch('oscar_python._utils.make_request') as mock_request: + client.add_federation_members("test_service", members) + mock_request.assert_called_once_with( + client, "/system/federation/test_service", "post", + data=json.dumps({"members": members})) + + +def test_add_federation_members_with_clusters(options): + client = Client(options) + members = [{"type": "oscar", "cluster_id": "c1", "service_name": "s1", "priority": 0}] + clusters = {"c1": {"endpoint": "https://c1.com", "auth_user": "u", "auth_password": "p"}} + with patch('oscar_python._utils.make_request') as mock_request: + client.add_federation_members("test_service", members, clusters=clusters) + mock_request.assert_called_once_with( + client, "/system/federation/test_service", "post", + data=json.dumps({"members": members, "clusters": clusters})) + + +def test_add_federation_members_with_storage_providers(options): + client = Client(options) + members = [{"type": "oscar", "cluster_id": "c1", "service_name": "s1", "priority": 0}] + clusters = {"c1": {"endpoint": "https://c1.com", "auth_user": "u", "auth_password": "p"}} + storage_providers = {"minio": {"my-minio": {"endpoint": "https://minio.com", "access_key": "ak", "secret_key": "sk"}}} + with patch('oscar_python._utils.make_request') as mock_request: + client.add_federation_members("test_service", members, clusters=clusters, + storage_providers=storage_providers) + mock_request.assert_called_once_with( + client, "/system/federation/test_service", "post", + data=json.dumps({"members": members, "clusters": clusters, + "storage_providers": storage_providers})) + + +def test_update_federation_members(options): + client = Client(options) + members = [{"type": "oscar", "cluster_id": "c1", "service_name": "s1", "priority": 0}] + update = [{"type": "oscar", "cluster_id": "c1", "service_name": "s1", "priority": 5}] + with patch('oscar_python._utils.make_request') as mock_request: + client.update_federation_members("test_service", members, update) + mock_request.assert_called_once_with( + client, "/system/federation/test_service", "put", + data=json.dumps({"members": members, "update": update})) + + +def test_update_federation_members_with_clusters(options): + client = Client(options) + members = [{"type": "oscar", "cluster_id": "c1", "service_name": "s1", "priority": 0}] + update = [{"type": "oscar", "cluster_id": "c1", "service_name": "s1", "priority": 5}] + clusters = {"c1": {"endpoint": "https://c1.com", "auth_user": "u", "auth_password": "new"}} + storage_providers = {"minio": {"my-minio": {"endpoint": "https://minio.com", "access_key": "ak", "secret_key": "sk"}}} + with patch('oscar_python._utils.make_request') as mock_request: + client.update_federation_members("test_service", members, update, + clusters=clusters, storage_providers=storage_providers) + mock_request.assert_called_once_with( + client, "/system/federation/test_service", "put", + data=json.dumps({"members": members, "update": update, + "clusters": clusters, "storage_providers": storage_providers})) + + +def test_remove_federation_members(options): + client = Client(options) + members = [{"type": "oscar", "cluster_id": "c1", "service_name": "s1", "priority": 0}] + with patch('oscar_python._utils.make_request') as mock_request: + client.remove_federation_members("test_service", members) + mock_request.assert_called_once_with( + client, "/system/federation/test_service", "delete", + data=json.dumps({"members": members, "delete": False})) + + +def test_remove_federation_members_with_delete(options): + client = Client(options) + members = [{"type": "oscar", "cluster_id": "c1", "service_name": "s1", "priority": 0}] + with patch('oscar_python._utils.make_request') as mock_request: + client.remove_federation_members("test_service", members, delete=True) + mock_request.assert_called_once_with( + client, "/system/federation/test_service", "delete", + data=json.dumps({"members": members, "delete": True}))