From ad16d7e68aa8252d05b945f88d1ec1f028ee9715 Mon Sep 17 00:00:00 2001 From: Ubuntu <jorge.sasiain@ehu.eus> Date: Tue, 2 May 2023 13:03:52 +0000 Subject: [PATCH] NAT-152: some cleanup and pending TODOs --- gso/oss-params-example.json | 2 +- gso/services/_ipam.py | 37 +++++++++++++++++++++++-------------- gso/services/ipam.py | 8 ++++---- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/gso/oss-params-example.json b/gso/oss-params-example.json index 73b7a630..84ce7df3 100644 --- a/gso/oss-params-example.json +++ b/gso/oss-params-example.json @@ -12,7 +12,7 @@ "V4": {"container": "1.1.0.0/24", "mask": 31}, "V6": {"container": "dead:beef::/64", "mask": 126}, "domain_name": ".lo" - }, + }, "TRUNK": { "V4": {"container": "1.1.1.0/24", "mask": 31}, "V6": {"container": "dead:beef::/64", "mask": 126}, diff --git a/gso/services/_ipam.py b/gso/services/_ipam.py index 1c9f7d4c..7e9ad241 100644 --- a/gso/services/_ipam.py +++ b/gso/services/_ipam.py @@ -106,6 +106,7 @@ def find_networks(network_container=None, network=None, ip_version=4): auth=HTTPBasicAuth(infoblox_params.username, infoblox_params.password), verify=False ) + # TODO: propagate "network not found" error to caller 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() @@ -135,7 +136,7 @@ def get_network_capacity(network=None): return utilization -def _allocate_network(infoblox_params: settings.InfoBloxParams, network_params: Union[settings.V4NetworkParams, settings.V6NetworkParams], ip_version=4): +def _allocate_network(infoblox_params: settings.InfoBloxParams, network_params: Union[settings.V4NetworkParams, settings.V6NetworkParams], ip_version=4) -> Union[V4ServiceNetwork, V6ServiceNetwork]: assert ip_version in [4, 6] endpoint = 'network' if ip_version == 4 else 'ipv6network' @@ -177,15 +178,17 @@ def _allocate_network(infoblox_params: settings.InfoBloxParams, network_params: headers={'content-type': "application/json"}, verify=False ) + # TODO: handle no more available space for networks in the container assert r.status_code >= 200 and r.status_code < 300, f"HTTP error {r.status_code}: {r.reason}\n\n{r.text}" + assert 'network' in r.json() allocated_network = r.json()['network'] if ip_version==4: return V4ServiceNetwork(v4=allocated_network) else: return V6ServiceNetwork(v6=allocated_network) -def allocate_service_ipv4_network(service_type): +def allocate_service_ipv4_network(service_type) -> V4ServiceNetwork: """ Allocate IPv4 network within the container of the specified service type. """ @@ -195,7 +198,7 @@ def allocate_service_ipv4_network(service_type): assert hasattr(ipam_params, service_type) and service_type != 'INFOBLOX', "Invalid service type." return _allocate_network(ipam_params.INFOBLOX, getattr(ipam_params, service_type).V4, 4) -def allocate_service_ipv6_network(service_type): +def allocate_service_ipv6_network(service_type) -> V6ServiceNetwork: """ Allocate IPv6 network within the container of the specified service type. """ @@ -205,10 +208,12 @@ def allocate_service_ipv6_network(service_type): assert hasattr(ipam_params, service_type) and service_type != 'INFOBLOX', "Invalid service type." return _allocate_network(ipam_params.INFOBLOX, getattr(ipam_params, service_type).V6, 6) -def delete_network(network): +def delete_network(network) -> Union[V4ServiceNetwork, V6ServiceNetwork]: """ Delete IPv4 or IPv6 network by CIDR. """ + # TODO: should we check that there are no hosts in this network? + # Deleting a network deletes the hosts in it, but not the associated DNS records. oss = settings.load_oss_params() assert oss.IPAM.INFOBLOX infoblox_params = oss.IPAM.INFOBLOX @@ -240,17 +245,20 @@ def _find_next_available_ip(infoblox_params, network_ref): auth=HTTPBasicAuth(infoblox_params.username, infoblox_params.password), verify=False ) + # TODO: handle no more available IPs in the network assert r.status_code >= 200 and r.status_code < 300, f"HTTP error {r.status_code}: {r.reason}\n\n{r.text}" assert 'ips' in r.json() received_ip = r.json()['ips'] assert len(received_ip) == 1 return received_ip[0] -def allocate_host(hostname=None, addr=None, network=None): +def allocate_host(hostname=None, addr=None, network=None) -> Union[V4HostAddress, V6HostAddress]: """ If network is not None, allocate host in that network. Otherwise if addr is not None, allocate host with that address. + hostname parameter must be full name including domain name. """ + # TODO: should hostnames be unique (i.e. fail if hostname already exists in this domain)? assert addr or network, "You must specify either the host address or the network CIDR." oss = settings.load_oss_params() assert oss.IPAM.INFOBLOX @@ -267,8 +275,6 @@ def allocate_host(hostname=None, addr=None, network=None): else: ip_version = _ip_addr_version(addr) - # TODO: use same request for both IP and DNS records - ipv4_req_payload = { "ipv4addrs": [ { @@ -333,7 +339,7 @@ def allocate_host(hostname=None, addr=None, network=None): else: return V6HostAddress(v6=addr) -def allocate_host_by_service_type(hostname=None, service_type=None): +def allocate_service_host(hostname=None, service_type=None) -> HostAddresses: """ Allocate host with both IPv4 and IPv6 address (and respective DNS records). The first network with available space for this service type is used. @@ -359,7 +365,9 @@ def allocate_host_by_service_type(hostname=None, service_type=None): if capacity < 1000: first_nonfull_ipv4_network = ipv4_network_info["network"] break - # TODO: create a new network if available space in the container. + # Create a new network if the existing networks in the container for the service type are all full. + if not first_nonfull_ipv4_network: + first_nonfull_ipv4_network = str(allocate_service_ipv4_network(service_type=service_type).v4) assert first_nonfull_ipv4_network, "No available IPv4 addresses for this service type." v4_host = allocate_host(hostname=hostname+domain_name, network=first_nonfull_ipv4_network) @@ -368,6 +376,7 @@ def allocate_host_by_service_type(hostname=None, service_type=None): ipv6_networks_info = find_networks(network_container=str(ipv6_container), ip_version=6) assert len(ipv6_networks_info) >= 1, "No IPv6 network exists in the container for this service type." assert 'network' in ipv6_networks_info[0] + # TODO: if "no available IP" error, create a new network? v6_host = allocate_host(hostname=hostname+domain_name, network=ipv6_networks_info[0]['network']) return HostAddresses(v4=v4_host,v6=v6_host) @@ -451,7 +460,7 @@ if __name__ == '__main__': elif choice == '2': ip_version = int(input("Enter IP version (4 or 6): ")) - networks = find_networks(ip_version=ip_version) #, network_container="10.255.255.0/24" + networks = find_networks(ip_version=ip_version) print(json.dumps(networks, indent=2)) elif choice == '3': @@ -477,21 +486,21 @@ if __name__ == '__main__': print(json.dumps(str(deleted_network), indent=2)) elif choice == '6': - hostname = input("Enter host name: ") + hostname = input("Enter host name (full name including domain name): ") addr = input("Enter IP address to allocate: ") alloc_ip = allocate_host(hostname=hostname, addr=addr) print(json.dumps(str(alloc_ip), indent=2)) elif choice == '7': - hostname = input("Enter host name: ") + hostname = input("Enter host name (full name including domain name): ") network = input("Enter an existing network to allocate from (in CIDR notation): ") alloc_ip = allocate_host(hostname=hostname, network=network) print(json.dumps(str(alloc_ip), indent=2)) elif choice == '8': - hostname = input("Enter host name: ") + hostname = input("Enter host name (not including domain name): ") service_type = input("Enter service type: ") - alloc_ip = allocate_host_by_service_type(hostname=hostname, service_type=service_type) + alloc_ip = allocate_service_host(hostname=hostname, service_type=service_type) print(json.dumps(str(alloc_ip), indent=2)) elif choice == '9': diff --git a/gso/services/ipam.py b/gso/services/ipam.py index 14c80ce7..fc646e43 100644 --- a/gso/services/ipam.py +++ b/gso/services/ipam.py @@ -39,10 +39,10 @@ def new_service_networks(service_type) -> ServiceNetworks: v6=v6_service_network) -def new_device_lo_address(hostname) -> HostAddresses: - return _ipam.allocate_host_by_service_type(hostname=hostname, service_type='LO') +def new_service_host(hostname, service_type) -> HostAddresses: + return _ipam.allocate_service_host(hostname=hostname, service_type=service_type) if __name__ == '__main__': - #new_service_networks('TRUNK') - new_device_lo_address('newhost1') \ No newline at end of file + new_service_networks('TRUNK') + #new_service_host('newhost12', 'TRUNK') \ No newline at end of file -- GitLab