diff --git a/gso/services/_ipam.py b/gso/services/_ipam.py index 01dc0b0957b175ecb9cc1509db241e269c0cfa38..2707a53ddc0ef4c2111baca5b8ac640f80a9b614 100644 --- a/gso/services/_ipam.py +++ b/gso/services/_ipam.py @@ -1,5 +1,4 @@ import ipaddress -import json import requests from enum import Enum from pydantic import BaseSettings @@ -439,7 +438,7 @@ def allocate_service_host(hostname=None, Below methods are not used for supported outside calls """ - +''' def _find_containers(network=None, ip_version=4): """ If network is not None, find that container. @@ -588,8 +587,8 @@ def _get_network_usage_status(network): assert r.status_code >= 200 and r.status_code < 300, \ f"HTTP error {r.status_code}: {r.reason}\n\n{r.text}" return r.json() - - +''' +''' if __name__ == '__main__': while True: print("1. Find all containers") @@ -679,3 +678,4 @@ if __name__ == '__main__': else: print("Invalid choice. Please try again.") +''' diff --git a/gso/services/ipam.py b/gso/services/ipam.py index 44b75bacbe6ff9387f742e9ecbde0e9e5b4a2029..c41a1239a14506e3104aaad5d5e4dcdfadd4dcae 100644 --- a/gso/services/ipam.py +++ b/gso/services/ipam.py @@ -55,6 +55,7 @@ def new_service_host(hostname, extattrs=extattrs) +''' if __name__ == '__main__': # sample call flow to allocate two loopback interfaces and a trunk service # new_service_host can be called passing networks or addresses @@ -74,6 +75,8 @@ if __name__ == '__main__': ) lo1_v4_host_address = lo1_service_networks.v4.network_address lo1_v6_host_address = lo1_service_networks.v6.network_address + print(lo1_v4_host_address) + print(lo1_v6_host_address) lo1_host_addresses = HostAddresses(v4=lo1_v4_host_address, v6=lo1_v6_host_address) new_service_host(hostname=hostname_A, @@ -107,3 +110,4 @@ if __name__ == '__main__': new_service_host(hostname=hostname_B, service_type='TRUNK', service_networks=trunk12_service_networks) +''' diff --git a/requirements.txt b/requirements.txt index 243292eec5c1bf77b11fc4166714d056f98fd313..3ac3e8394ad7a4649d8b87cd724f0d44ed8c1e03 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ orchestrator-core==1.0.0 +pydantic requests pytest +responses diff --git a/setup.py b/setup.py index 50d3dc63c04f6f68ec902ab6478942f252c4dc40..7062c773b0d214c582e7c5b9db49da806d1eae59 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,8 @@ setup( url=('https://gitlab.geant.org/goat/geant-service-orchestrator'), packages=find_packages(), install_requires=[ - 'requests', 'orchestrator-core==1.0.0', + 'pydantic', + 'requests', ] ) diff --git a/test/test_ipam.py b/test/test_ipam.py new file mode 100644 index 0000000000000000000000000000000000000000..6c7c5dd14bc86b995a120b860af0b8c611a1e4cc --- /dev/null +++ b/test/test_ipam.py @@ -0,0 +1,194 @@ +import contextlib +import ipaddress +import json +import os +import socket +import pytest +import re +import responses +import tempfile + +from gso.services import ipam + + +@pytest.fixture(scope='session') +def configuration_data(): + with contextlib.closing( + socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: + s.bind(('', 0)) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + yield { + "GENERAL": { + "public_hostname": "https://gap.geant.org" + }, + "RESOURCE_MANAGER_API_PREFIX": "http://localhost:44444", + "IPAM": { + "INFOBLOX": { + "scheme": "https", + "wapi_version": "v2.12", + "host": "10.0.0.1", + "username": "robot-user", + "password": "robot-user-password" + }, + "LO": { + "V4": {"containers": ["10.255.255.0/24"], "mask": 32}, + "V6": {"containers": ["dead:beef::/64"], "mask": 128}, + "domain_name": ".lo" + }, + "TRUNK": { + "V4": { + "containers": ["10.255.255.0/24", "10.255.254.0/24"], + "mask": 31 + }, + "V6": { + "containers": ["dead:beef::/64", "dead:beee::/64"], + "mask": 126 + }, + "domain_name": ".trunk" + }, + "GEANT_IP": { + "V4": { + "containers": ["10.255.255.0/24", "10.255.254.0/24"], + "mask": 31 + }, + "V6": { + "containers": ["dead:beef::/64", "dead:beee::/64"], + "mask": 126 + }, + "domain_name": ".geantip" + } + }, + "PROVISIONING_PROXY": { + "scheme": "https", + "api_base": "localhost:44444", + "auth": "Bearer <token>", + "api_version": 1123 + } + } + + +@pytest.fixture(scope='session') +def data_config_filename(configuration_data): + file_name = os.path.join( + tempfile.gettempdir(), os.urandom(24).hex()) + open(file_name, 'x').close() + with open(file_name, 'wb') as f: + f.write(json.dumps(configuration_data).encode('utf-8')) + f.flush() + + os.environ['OSS_PARAMS_FILENAME'] = f.name + + yield f.name + + +@responses.activate +def test_new_service_networks(data_config_filename, service_type='LO'): + + responses.add( + method=responses.POST, + url=re.compile(r'.*/wapi.*/network.*'), + json={ + '_ref': 'network/ZG5zLm5ldHdvcmskMTAuMjU1LjI1NS4yMC8zMi8w:10.255.255.20/32/default', # noqa: E501 + 'network': '10.255.255.20/32' + } + ) + + responses.add( + method=responses.POST, + url=re.compile(r'.*/wapi.*/ipv6network.*'), + json={ + '_ref': 'ipv6network/ZG5zLm5ldHdvcmskZGVhZDpiZWVmOjoxOC8xMjgvMA:dead%3Abeef%3A%3A18/128/default', # noqa: E501 + 'network': 'dead:beef::18/128' + } + ) + + service_networks = ipam.new_service_networks(service_type='LO') + assert service_networks == ipam.ServiceNetworks( + v4=ipaddress.ip_network('10.255.255.20/32'), + v6=ipaddress.ip_network('dead:beef::18/128') + ) + + +@responses.activate +def test_new_service_host(data_config_filename, service_type='LO'): + + responses.add( + method=responses.POST, + url=re.compile(r'.*/wapi.*/record:host$'), + json='record:host/ZG5zLmhvc3QkLm5vbl9ETlNfaG9zdF9yb290LjAuMTY4MzcwNTU4MzY3MC5nc28udGVzdA:test.lo/%20' # noqa: E501 + ) + + responses.add( + method=responses.POST, + url=re.compile(r'.*/wapi.*/record:a$'), + json='record:a/ZG5zLmJpbmRfYSQuX2RlZmF1bHQuZ3NvLHRlc3QsMTAuMjU1LjI1NS44:test.lo/default' # noqa: E501 + ) + + responses.add( + method=responses.POST, + url=re.compile(r'.*/wapi.*/record:aaaa$'), + json='record:aaaa/ZG5zLmJpbmRfYSQuX2RlZmF1bHQuZ3NvLHRlc3QsMTAuMjU1LjI1NS44:test.lo/default' # noqa: E501 + ) + + responses.add( + method=responses.GET, + url=re.compile(r'.*/wapi.*/network.*'), + json=[ + { + "_ref": "network/ZG5zLm5ldHdvcmskMTAuMjU1LjI1NS4yMC8zMi8w:10.255.255.20/32/default", # noqa: E501 + "network": "10.255.255.20/32", + "network_view": "default" + } + ] + + ) + + responses.add( + method=responses.GET, + url=re.compile(r'.*/wapi.*/ipv6network.*'), + json=[ + { + "_ref": "ipv6network/ZG5zLm5ldHdvcmskZGVhZDpiZWVmOjoxOC8xMjgvMA:dead%3Abeef%3A%3A18/128/default", # noqa: E501 + "network": "dead:beef::18/128", + "network_view": "default" + } + ] + ) + + responses.add( + method=responses.POST, + url=re.compile(r'.*/wapi.*/network.*/.*?_function=next_available_ip&num=1.*'), # noqa: E501 + json={'ips': ['10.255.255.20']} + ) + + responses.add( + method=responses.POST, + url=re.compile(r'.*/wapi.*/ipv6network.*/.*?_function=next_available_ip&num=1.*'), # noqa: E501 + json={'ips': ['dead:beef::18']} + ) + + service_hosts = ipam.new_service_host( + hostname='test', + service_type='LO', + host_addresses=ipam.HostAddresses( + v4=ipaddress.ip_address('10.255.255.20'), + v6=ipaddress.ip_address('dead:beef::18') + ) + ) + assert service_hosts == ipam.HostAddresses( + v4=ipaddress.ip_address('10.255.255.20'), + v6=ipaddress.ip_address('dead:beef::18') + ) + + service_hosts = ipam.new_service_host( + hostname='test', + service_type='LO', + service_networks=ipam.ServiceNetworks( + v4=ipaddress.ip_network('10.255.255.20/32'), + v6=ipaddress.ip_network('dead:beef::18/128') + ) + ) + assert service_hosts == ipam.HostAddresses( + v4=ipaddress.ip_address('10.255.255.20'), + v6=ipaddress.ip_address('dead:beef::18') + )