diff --git a/gso/services/infoblox.py b/gso/services/infoblox.py index 1011ffac51e3a984fe96bdb3ad949c8f842e527d..132387f235b886b7130579d4f2ae8346405832df 100644 --- a/gso/services/infoblox.py +++ b/gso/services/infoblox.py @@ -34,7 +34,7 @@ def _allocate_network( dns_view: str, netmask: int, containers: list[str], - comment: str, + comment: str | None = "", ) -> ipaddress.IPv4Network | ipaddress.IPv6Network: for container in [ipaddress.ip_network(con) for con in containers]: for network in container.subnets(new_prefix=netmask): @@ -58,7 +58,7 @@ def allocate_v4_network(service_type: str, comment: str | None = "") -> ipaddres containers = getattr(oss, service_type).V4.containers dns_view = getattr(oss, service_type).dns_view - return _allocate_network(conn, dns_view, netmask, containers, comment) + return ipaddress.IPv4Network(_allocate_network(conn, dns_view, netmask, containers, comment)) def allocate_v6_network(service_type: str, comment: str | None = "") -> ipaddress.IPv6Network: @@ -67,11 +67,11 @@ def allocate_v6_network(service_type: str, comment: str | None = "") -> ipaddres containers = getattr(oss, service_type).V6.containers dns_view = getattr(oss, service_type).dns_view - return _allocate_network(conn, dns_view, netmask, containers, comment) + return ipaddress.IPv6Network(_allocate_network(conn, dns_view, netmask, containers, comment)) def delete_network(ip_network: ipaddress.IPv4Network | ipaddress.IPv6Network) -> None: - conn = _setup_connection() + conn, _ = _setup_connection() network = objects.Network.search(conn, cidr=str(ip_network)) if network: network.delete() @@ -92,7 +92,7 @@ def allocate_host( created_v6 = None for ipv6_range in allocation_networks_v6: - v6_alloc = objects.IPAllocation.next_available_ip_from_cidr(dns_view, ipv6_range) + v6_alloc = objects.IPAllocation.next_available_ip_from_cidr(dns_view, str(ipv6_range)) ipv6_object = objects.IP.create(ip=v6_alloc, mac="00:00:00:00:00:00", configure_for_dhcp=False) try: new_host = objects.HostRecord.create( @@ -107,12 +107,13 @@ def allocate_host( created_v4 = None for ipv4_range in allocation_networks_v4: - v4_alloc = objects.IPAllocation.next_available_ip_from_cidr(dns_view, ipv4_range) + v4_alloc = objects.IPAllocation.next_available_ip_from_cidr(dns_view, str(ipv4_range)) ipv4_object = objects.IP.create(ip=v4_alloc, mac="00:00:00:00:00:00", configure_for_dhcp=False) new_host = objects.HostRecord.search(conn, name=hostname) new_host.ipv4addrs = [ipv4_object] try: new_host.update() + new_host = objects.HostRecord.search(conn, name=hostname) created_v4 = ipaddress.IPv4Address(new_host.ipv4addr) except InfobloxCannotUpdateObject: logger.warning(f"Cannot find 1 available IP address in network {ipv4_range}.") @@ -124,7 +125,7 @@ def allocate_host( def delete_host_by_ip(ip_addr: ipaddress.IPv4Address | ipaddress.IPv6Address) -> None: - conn = _setup_connection() + conn, _ = _setup_connection() host = objects.HostRecord.search(conn, ipv4addr=ip_addr) if host: host.delete() @@ -133,6 +134,6 @@ def delete_host_by_ip(ip_addr: ipaddress.IPv4Address | ipaddress.IPv6Address) -> def delete_host_by_fqdn(fqdn: str) -> None: - conn = _setup_connection() + conn, _ = _setup_connection() host = objects.HostRecord.search(conn, name=fqdn) host.delete() diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py index bf70d821fb0d01f3222f4ef807512ec80f9bed71..702656b0f46ef2c07a9ee12d778129e450da9596 100644 --- a/gso/workflows/iptrunk/create_iptrunk.py +++ b/gso/workflows/iptrunk/create_iptrunk.py @@ -10,7 +10,7 @@ from gso.products.product_blocks import PhyPortCapacity from gso.products.product_blocks.iptrunk import IptrunkType from gso.products.product_types.iptrunk import IptrunkInactive, IptrunkProvisioning from gso.products.product_types.router import Router -from gso.services import ipam, provisioning_proxy, subscriptions +from gso.services import infoblox, provisioning_proxy, subscriptions from gso.services.provisioning_proxy import pp_interaction from gso.workflows.utils import customer_selector @@ -90,15 +90,13 @@ def create_subscription(product: UUIDstr, customer: UUIDstr) -> State: @step("Get information from IPAM") def get_info_from_ipam(subscription: IptrunkProvisioning) -> State: - # TODO: get info about how these should be generated - subscription.iptrunk.iptrunk_ipv4_network = ipam.allocate_ipv4_network( - service_type="TRUNK", - comment=subscription.iptrunk.iptrunk_description, - ).v4 - subscription.iptrunk.iptrunk_ipv6_network = ipam.allocate_ipv6_network( - service_type="TRUNK", - comment=subscription.iptrunk.iptrunk_description, - ).v6 + subscription.iptrunk.iptrunk_ipv4_network = infoblox.allocate_v4_network( + "TRUNK", subscription.iptrunk.iptrunk_description + ) + subscription.iptrunk.iptrunk_ipv6_network = infoblox.allocate_v6_network( + "TRUNK", subscription.iptrunk.iptrunk_description + ) + return {"subscription": subscription} diff --git a/gso/workflows/iptrunk/terminate_iptrunk.py b/gso/workflows/iptrunk/terminate_iptrunk.py index 4572bb650bd93d628d329844d9d816411daeb3a1..7ace475fe0780d1f402eff522416e349beb684e1 100644 --- a/gso/workflows/iptrunk/terminate_iptrunk.py +++ b/gso/workflows/iptrunk/terminate_iptrunk.py @@ -10,8 +10,7 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form from workflows.iptrunk import set_isis_to_9000 from gso.products.product_types.iptrunk import Iptrunk -from gso.services import ipam, provisioning_proxy -from gso.services.ipam import V4ServiceNetwork, V6ServiceNetwork +from gso.services import infoblox, provisioning_proxy from gso.services.provisioning_proxy import pp_interaction @@ -59,21 +58,17 @@ def deprovision_ip_trunk_real(subscription: Iptrunk, process_id: UUIDstr) -> Sta @step("Deprovision IPv4 networks") -def deprovision_ip_trunk_ipv4(subscription: Iptrunk) -> dict[str, V4ServiceNetwork | V6ServiceNetwork]: - service_network = ipam.delete_network( - network=ipaddress.ip_network(subscription.iptrunk.iptrunk_ipv4_network), - service_type="TRUNK", - ) - return {"service_network": service_network} +def deprovision_ip_trunk_ipv4(subscription: Iptrunk) -> dict: + infoblox.delete_network(ipaddress.IPv4Network(subscription.iptrunk.iptrunk_ipv4_network)) + + return {"subscription": subscription} @step("Deprovision IPv6 networks") -def deprovision_ip_trunk_ipv6(subscription: Iptrunk) -> dict[str, V4ServiceNetwork | V6ServiceNetwork]: - service_network = ipam.delete_network( - network=ipaddress.ip_network(subscription.iptrunk.iptrunk_ipv6_network), - service_type="TRUNK", - ) - return {"service_network": service_network} +def deprovision_ip_trunk_ipv6(subscription: Iptrunk) -> dict: + infoblox.delete_network(ipaddress.IPv6Network(subscription.iptrunk.iptrunk_ipv6_network)) + + return {"subscription": subscription} @workflow( @@ -82,8 +77,8 @@ def deprovision_ip_trunk_ipv6(subscription: Iptrunk) -> dict[str, V4ServiceNetwo target=Target.TERMINATE, ) def terminate_iptrunk() -> StepList: - run_config_steps = conditional(lambda state: state.get("remove_configuration", True)) - run_ipam_steps = conditional(lambda state: state.get("clean_up_ipam", True)) + run_config_steps = conditional(lambda state: state["remove_configuration"]) + run_ipam_steps = conditional(lambda state: state["clean_up_ipam"]) config_steps = ( StepList([set_isis_to_9000]) diff --git a/gso/workflows/router/create_router.py b/gso/workflows/router/create_router.py index c94826f286cb49e44d5c77fb10a7f5888b6ff309..265e98b409c3e3ae632c21ac57c1f485142d4ed9 100644 --- a/gso/workflows/router/create_router.py +++ b/gso/workflows/router/create_router.py @@ -6,7 +6,7 @@ from orchestrator.forms import FormPage from orchestrator.forms.validators import Choice from orchestrator.targets import Target from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr -from orchestrator.workflow import StepList, done, init, step, workflow +from orchestrator.workflow import StepList, conditional, done, init, step, workflow from orchestrator.workflows.steps import resync, set_status, store_process_subscription from orchestrator.workflows.utils import wrap_create_initial_input_form @@ -15,7 +15,7 @@ from gso.products.product_types import router from gso.products.product_types.router import RouterInactive, RouterProvisioning from gso.products.product_types.site import Site from gso.products.shared import PortNumber -from gso.services import ipam, provisioning_proxy, subscriptions +from gso.services import infoblox, provisioning_proxy, subscriptions from gso.services.provisioning_proxy import pp_interaction from gso.workflows.utils import customer_selector, iso_from_ipv4 @@ -59,26 +59,26 @@ def create_subscription(product: UUIDstr, customer: UUIDstr) -> State: } -@step("Get information from IPAM") -def get_info_from_ipam(subscription: RouterProvisioning, is_ias_connected: bool) -> State: - lo0_alias = re.sub(".geant.net", "", subscription.router.router_fqdn) - lo0_name = f"lo0.{lo0_alias}" - lo0_addr = ipam.allocate_host(hostname=lo0_name, service_type="LO", cname_aliases=[lo0_alias]) - subscription.router.router_lo_ipv4_address = lo0_addr.v4 - subscription.router.router_lo_ipv6_address = lo0_addr.v6 +@step("Allocate loopback interfaces in IPAM") +def ipam_allocate_loopback(subscription: RouterProvisioning, is_ias_connected: bool) -> State: + fqdn = subscription.router.router_fqdn + loopback_v4, loopback_v6 = infoblox.allocate_host(f"lo0.{fqdn}", "LO", [fqdn]) + + subscription.router.router_lo_ipv4_address = loopback_v4 + subscription.router.router_lo_ipv6_address = loopback_v6 subscription.router.router_lo_iso_address = iso_from_ipv4(subscription.router.router_lo_ipv4_address) subscription.router.router_is_ias_connected = is_ias_connected - if is_ias_connected is True: - subscription.router.router_si_ipv4_network = ipam.allocate_ipv4_network( - service_type="SI", comment=f"SI for {lo0_name}" - ).v4 - subscription.router.router_ias_lt_ipv4_network = ipam.allocate_ipv4_network( - service_type="LT_IAS", comment=f"LT for {lo0_name}" - ).v4 - subscription.router.router_ias_lt_ipv6_network = ipam.allocate_ipv6_network( - service_type="LT_IAS", comment=f"LT for {lo0_name}" - ).v6 + return {"subscription": subscription} + + +@step("Allocate IAS connection in IPAM") +def ipam_allocate_ias_networks(subscription: RouterProvisioning) -> State: + fqdn = subscription.router.router_fqdn + + subscription.router.router_si_ipv4_network = infoblox.allocate_v4_network("SI", f"SI for {fqdn}") + subscription.router.router_ias_lt_ipv4_network = infoblox.allocate_v4_network("LT_IAS", f"LT for {fqdn}") + subscription.router.router_ias_lt_ipv6_network = infoblox.allocate_v6_network("LT_IAS", f"LT for {fqdn}") return {"subscription": subscription} @@ -142,12 +142,15 @@ def provision_router_real(subscription: RouterProvisioning, process_id: UUIDstr) target=Target.CREATE, ) def create_router() -> StepList: + should_allocate_ias = conditional(lambda state: state["is_ias_connected"]) + return ( init >> create_subscription >> store_process_subscription(Target.CREATE) >> initialize_subscription - >> get_info_from_ipam + >> ipam_allocate_loopback + >> should_allocate_ias(ipam_allocate_ias_networks) >> pp_interaction(provision_router_dry, 3) >> pp_interaction(provision_router_real, 3) >> set_status(SubscriptionLifecycle.ACTIVE) diff --git a/gso/workflows/router/terminate_router.py b/gso/workflows/router/terminate_router.py index 3276a8fb31f8ae053b25edfe137c55260b432885..ede95de5528b973281773b9b4f10fffa416a0bc4 100644 --- a/gso/workflows/router/terminate_router.py +++ b/gso/workflows/router/terminate_router.py @@ -10,8 +10,7 @@ from orchestrator.workflows.steps import resync, set_status, store_process_subsc from orchestrator.workflows.utils import wrap_modify_initial_input_form from gso.products.product_types.router import Router -from gso.services import ipam -from gso.services.ipam import HostAddresses, V4ServiceNetwork, V6ServiceNetwork +from gso.services import infoblox logger = logging.getLogger(__name__) @@ -31,47 +30,26 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: return user_input.dict() -@step("Deprovision loopback IPs from IPAM/DNS") -def deprovision_loopback_ips(subscription: Router) -> dict[str, HostAddresses]: - input_host_addresses = ipam.HostAddresses( - v4=ipaddress.IPv4Address(subscription.router.router_lo_ipv4_address), - v6=ipaddress.IPv6Address(subscription.router.router_lo_ipv6_address), - ) - fqdn_as_list = subscription.router.router_fqdn.split(".") - hostname = str(fqdn_as_list[0]) + "." + str(fqdn_as_list[1]) + "." + str(fqdn_as_list[2]) - lo0_name = "lo0." + hostname - host_addresses = ipam.delete_host( - hostname=lo0_name, - host_addresses=input_host_addresses, - cname_aliases=[hostname], - service_type="LO", - ) - return {"addresses": host_addresses} +@step("Deprovision loopback IPs from IPAM") +def deprovision_loopback_ips(subscription: Router) -> dict: + infoblox.delete_host_by_ip(ipaddress.IPv4Address(subscription.router.router_lo_ipv4_address)) + return {"subscription": subscription} -@step("Deprovision SI- interface IPs from IPAM/DNS") -def deprovision_si_ips(subscription: Router) -> dict[str, V4ServiceNetwork | V6ServiceNetwork]: - service_network = ipam.delete_network( - network=ipaddress.ip_network(subscription.router.router_si_ipv4_network), # type: ignore - service_type="SI", - ) - return {"service_network": service_network} +@step("Deprovision SI interface network from IPAM") +def deprovision_si_ips(subscription: Router) -> dict: + infoblox.delete_network(ipaddress.IPv4Network(subscription.router.router_si_ipv4_network)) -@step("Deprovision LT- interface (IAS) IPs from IPAM/DNS") -def deprovision_lt_ips(subscription: Router) -> dict[str, V4ServiceNetwork | V6ServiceNetwork]: - service_network_v4 = ipam.delete_network( - network=ipaddress.ip_network(subscription.router.router_ias_lt_ipv4_network), # type: ignore - service_type="LT_IAS", - ) - service_network_v6 = ipam.delete_network( - network=ipaddress.ip_network(subscription.router.router_ias_lt_ipv6_network), # type: ignore - service_type="LT_IAS", - ) - return { - "service_network_v4": service_network_v4, - "service_network_v6": service_network_v6, - } + return {"subscription": subscription} + + +@step("Deprovision IAS LT interfaces from IPAM") +def deprovision_lt_ips(subscription: Router) -> dict: + infoblox.delete_network(ipaddress.IPv4Network(subscription.router.router_ias_lt_ipv4_network)) + infoblox.delete_network(ipaddress.IPv6Network(subscription.router.router_ias_lt_ipv6_network)) + + return {"subscription": subscription} @step("Remove configuration from router") @@ -85,14 +63,13 @@ def remove_config_from_router() -> None: target=Target.TERMINATE, ) def terminate_router() -> StepList: - run_ipam_steps = conditional(lambda state: state.get("clean_up_ipam", True)) - run_config_steps = conditional(lambda state: state.get("remove_configuration", True)) - run_ias_removal = conditional(lambda subscription: "router_ias_lt_ipv4_network" in subscription.get("router", {})) - run_si_removal = conditional(lambda subscription: "router_si_ipv4_network" in subscription.get("router", {})) + run_ipam_steps = conditional(lambda state: state["clean_up_ipam"]) + run_config_steps = conditional(lambda state: state["remove_configuration"]) + run_ias_removal = conditional(lambda state: state["subscription"]["router"]["router_is_ias_connected"]) ipam_steps = ( StepList([deprovision_loopback_ips]) - >> run_si_removal(deprovision_si_ips) + >> run_ias_removal(deprovision_si_ips) >> run_ias_removal(deprovision_lt_ips) )