Exemple #1
0
def get_cluster_stack(
    container_engine_client, compartment_id, cluster_id, node_kwargs=None
):

    if not node_kwargs:
        node_kwargs = dict()

    stack = dict(id=None, cluster=None, node_pools=[])

    cluster = get(container_engine_client, "get_cluster", cluster_id)
    if not cluster:
        return stack

    stack["cluster"] = cluster
    if hasattr(cluster, "id"):
        stack["id"] = cluster.id

    # Get node pools (NodePoolSummaries)
    node_pool_summaries = list_entities(
        container_engine_client,
        "list_node_pools",
        compartment_id,
        cluster_id=cluster.id,
        **node_kwargs,
    )

    for summary in node_pool_summaries:
        node_pool = get(container_engine_client, "get_node_pool", summary.id)
        if node_pool:
            stack["node_pools"].append(node_pool)
    return stack
Exemple #2
0
def create_cluster(container_engine_client, create_cluster_details, create_kwargs=None):
    if not create_kwargs:
        create_kwargs = dict(
            wait_for_states=[
                WorkRequest.STATUS_SUCCEEDED,
                WorkRequest.OPERATION_TYPE_CLUSTER_CREATE,
                WorkRequest.STATUS_FAILED,
            ]
        )

    created_response = create(
        container_engine_client,
        "create_cluster",
        create_cluster_details,
        **create_kwargs,
    )

    if not created_response:
        return None

    cluster = None
    # NOTE, only supports a single cluster creation for now
    if created_response.resources:
        cluster_id = created_response.resources[0].identifier
        # Get the actual created cluster
        cluster = get(container_engine_client, "get_cluster", cluster_id)

    return cluster
Exemple #3
0
def get_vcn_stack(network_client, compartment_id, vcn_id):
    stack = {}
    vcn = get(network_client, "get_vcn", vcn_id)
    if not vcn:
        return stack
    gateways = {
        gateway.id: gateway
        for gateway in list_entities(
            network_client,
            "list_internet_gateways",
            compartment_id,
            vcn_id=vcn.id,
        )
    }
    subnets = {
        subnet.id: subnet
        for subnet in list_entities(
            network_client, "list_subnets", compartment_id, vcn_id=vcn.id)
    }
    stack = {
        "id": vcn.id,
        "vcn": vcn,
        "internet_gateways": gateways,
        "subnets": subnets,
    }
    return stack
Exemple #4
0
def get_cluster_by_name(
    container_engine_client, compartment_id, name, cluster_kwargs=None
):
    if not cluster_kwargs:
        cluster_kwargs = {}

    cluster_kwargs.update(dict(name=name))

    clusters = list_clusters(container_engine_client, compartment_id, cluster_kwargs)
    if clusters:
        # Convert to cluster type
        cluster = get(container_engine_client, "get_cluster", clusters[0].id)
        return cluster
    return None
Exemple #5
0
def refresh_vcn_stack(network_client, compartment_id, vcn_kwargs=None):
    if not vcn_kwargs:
        vcn_kwargs = {}

    vcn = None
    if "id" in vcn_kwargs and vcn_kwargs["id"]:
        vcn = get(network_client, "get_vcn", vcn_kwargs["id"])
    elif "display_name" in vcn_kwargs:
        vcn = get_vcn_by_name(network_client, compartment_id,
                              vcn_kwargs["display_name"])

    if not vcn:
        return {}

    return get_vcn_stack(network_client, compartment_id, vcn.id)
Exemple #6
0
def get_instance_endpoints(compute_client, network_client, compartment_id,
                           instance_id):
    vnic_attachments = list_entities(compute_client,
                                     "list_vnic_attachments",
                                     compartment_id,
                                     instance_id=instance_id)

    instance_endpoints = []
    for vnic_attach in vnic_attachments:
        vnic = get(network_client, "get_vnic", vnic_attach.vnic_id)
        endpoints = {}
        if hasattr(vnic, "public_ip") and vnic.public_ip:
            endpoints["public_ip"] = vnic.public_ip
        if hasattr(vnic, "private_ip") and vnic.private_ip:
            endpoints["private_ip"] = vnic.private_ip
        instance_endpoints.append(endpoints)
    return instance_endpoints
Exemple #7
0
    def test_vcn_stack(self):
        # Extract the ip of the instance
        self.vcn_stack = new_vcn_stack(
            self.network_client,
            self.options["profile"]["compartment_id"],
            vcn_kwargs=self.options["vcn"],
            internet_gateway_kwargs=self.options["internetgateway"],
            route_table_kwargs=self.options["routetable"],
            subnet_kwargs=self.options["subnet"],
        )

        self.assertTrue(valid_vcn_stack(self.vcn_stack))
        _vcn_stack = get_vcn_stack(
            self.network_client,
            self.options["profile"]["compartment_id"],
            self.vcn_stack["id"],
        )
        self.assertTrue(valid_vcn_stack(_vcn_stack))
        self.assertEqual(self.vcn_stack["id"], _vcn_stack["id"])
        self.assertEqual(self.vcn_stack["vcn"], _vcn_stack["vcn"])
        self.assertDictEqual(self.vcn_stack["internet_gateways"],
                             _vcn_stack["internet_gateways"])
        self.assertDictEqual(self.vcn_stack["subnets"], _vcn_stack["subnets"])

        new_vcn_id = get(self.network_client, "get_vcn", self.vcn_stack["id"])

        self.assertEqual(self.vcn_stack["id"], new_vcn_id.id)
        self.assertEqual(self.vcn_stack["vcn"], new_vcn_id)

        # Custom equal check
        self.assertTrue(equal_vcn_stack(self.vcn_stack, _vcn_stack))

        new_vcn_name = get_vcn_by_name(
            self.network_client,
            self.options["profile"]["compartment_id"],
            self.options["vcn"]["display_name"],
        )

        self.assertEqual(self.vcn_stack["id"], new_vcn_name.id)
        self.assertEqual(self.vcn_stack["vcn"], new_vcn_name)
Exemple #8
0
def create_node_pool(container_engine_client, create_node_pool_details):
    created_response = create(
        container_engine_client,
        "create_node_pool",
        create_node_pool_details,
        wait_for_states=[
            WorkRequest.STATUS_SUCCEEDED,
            WorkRequest.OPERATION_TYPE_NODEPOOL_CREATE,
            WorkRequest.STATUS_FAILED,
        ],
    )
    if not created_response:
        return None

    node_pool = None
    # NOTE, only supports a single cluster creation for now
    if created_response.resources:
        node_pool_id = created_response.resources[0].identifier
        # Get the actual created cluster
        node_pool = get(container_engine_client, "get_node_pool", node_pool_id)

    return node_pool
Exemple #9
0
def get_cluster(compute_client, compartment_id, cluster_id, kwargs=None):
    if not kwargs:
        kwargs = {}
    return get(compute_client, "get_cluster", cluster_id, **kwargs)
Exemple #10
0
def delete_cluster_stack(container_engine_client, cluster_id, delete_vcn=False):
    # Will implicitly delete the node pool as well
    cluster = get(container_engine_client, "get_cluster", cluster_id)
    if not cluster:
        return False
    return delete_cluster(container_engine_client, cluster_id)
Exemple #11
0
def get_instance(compute_client, compartment_id, instance_id, kwargs=None):
    if not kwargs:
        kwargs = {}
    return get(compute_client, "get_instance", instance_id, **kwargs)
Exemple #12
0
    def test_cluster_stack(self):
        # Need vcn stack for the cluster stack
        self.vcn_stack = new_vcn_stack(
            self.network_client,
            self.options["profile"]["compartment_id"],
            vcn_kwargs=self.options["vcn"],
            internet_gateway_kwargs=self.options["internetgateway"],
            route_table_kwargs=self.options["routetable"],
            subnet_kwargs=self.options["subnet"],
        )
        self.assertTrue(valid_vcn_stack(self.vcn_stack))

        # Available images
        available_images = list_entities(
            self.compute_client, "list_images",
            self.options["profile"]["compartment_id"],
            **self.options["cluster"]["node"]["image"])

        if not available_images:
            raise ValueError(
                "No valid image could be found with options: {}".format(
                    self.options["cluster"]["node"]["image"]))

        if len(available_images) > 1:
            raise ValueError(
                "More than 1 image was found with options: {}".format(
                    self.options["cluster"]["node"]["image"]))

        image = available_images[0]

        # Prepare cluster details
        cluster_details = gen_cluster_stack_details(self.vcn_stack["id"],
                                                    self.vcn_stack["subnets"],
                                                    image, **self.options)

        self.cluster_stack = new_cluster_stack(
            self.container_engine_client,
            cluster_details["create_cluster"],
            cluster_details["create_node_pool"],
        )
        self.assertTrue(valid_cluster_stack(self.cluster_stack))

        _cluster_stack = get_cluster_stack(
            self.container_engine_client,
            self.options["profile"]["compartment_id"],
            self.cluster_stack["id"],
        )
        self.assertTrue(valid_cluster_stack(_cluster_stack))
        self.assertEqual(self.cluster_stack["id"], _cluster_stack["id"])
        self.assertEqual(self.cluster_stack["cluster"],
                         _cluster_stack["cluster"])
        self.assertListEqual(self.cluster_stack["node_pools"],
                             _cluster_stack["node_pools"])

        new_cluster_id = get(self.container_engine_client, "get_cluster",
                             self.cluster_stack["id"])

        new_cluster_name = get_cluster_by_name(
            self.container_engine_client,
            self.options["profile"]["compartment_id"],
            self.cluster_stack["cluster"].name,
        )

        self.assertEqual(self.cluster_stack["id"], new_cluster_id.id)
        self.assertEqual(self.cluster_stack["cluster"], new_cluster_name)
Exemple #13
0
def create_subnet_stack(
    network_client,
    compartment_id,
    vcn,
    internet_gateway_kwargs=None,
    route_table_kwargs=None,
    subnet_kwargs=None,
    use_default_route_table=False,
):

    if not internet_gateway_kwargs:
        internet_gateway_kwargs = {}

    if not subnet_kwargs:
        subnet_kwargs = {}

    if not route_table_kwargs:
        route_table_kwargs = {}

    routetable = None

    # use_default_route -> use the default route table
    if use_default_route_table:
        routetable = get_route_table_by_name(network_client, compartment_id,
                                             vcn.id, "DEFAULT")
    else:
        if "id" in route_table_kwargs and route_table_kwargs["id"]:
            routetable = get(network_client, "get_route_table",
                             route_table_kwargs["id"])
        elif ("display_name" in route_table_kwargs
              and route_table_kwargs["display_name"]):
            routetable = get_route_table_by_name(
                network_client,
                compartment_id,
                vcn.id,
                route_table_kwargs["display_name"],
            )

        if not routetable:
            # Create new
            route_rules = []

            gateway = None
            if "id" in internet_gateway_kwargs and internet_gateway_kwargs[
                    "id"]:
                gateway = get(
                    network_client,
                    "get_internet_gateway",
                    internet_gateway_kwargs["id"],
                )
            elif ("display_name" in internet_gateway_kwargs
                  and internet_gateway_kwargs["display_name"]):
                gateway = get_internet_gateway_by_name(
                    network_client,
                    compartment_id,
                    vcn.id,
                    internet_gateway_kwargs["display_name"],
                )

            if not gateway:
                # Each VCN can maximum have 1 IG
                create_ig_details = prepare_details(
                    CreateInternetGatewayDetails,
                    compartment_id=compartment_id,
                    vcn_id=vcn.id,
                    **internet_gateway_kwargs,
                )

                gateway = create_internet_gateway(network_client,
                                                  create_ig_details)
                if not gateway:
                    # TODO, log error
                    return None

            route_rules = []
            if "routerules" in route_table_kwargs:
                for route_rule in route_table_kwargs["routerules"]:
                    route_rule_details = prepare_details(
                        RouteRule, network_entity_id=gateway.id, **route_rule)
                    if route_rule_details:
                        route_rules.append(route_rule_details)
                route_table_kwargs.pop("routerules")

            create_rt_details = prepare_details(
                CreateRouteTableDetails,
                compartment_id=compartment_id,
                vcn_id=vcn.id,
                route_rules=route_rules,
                **route_table_kwargs,
            )

            routetable = create_route_table(network_client, create_rt_details)

    if not routetable:
        # TODO, log error
        return None

    # Create subnet
    create_subnet_details = prepare_details(
        CreateSubnetDetails,
        compartment_id=compartment_id,
        vcn_id=vcn.id,
        route_table_id=routetable.id,
        **subnet_kwargs,
    )

    return create_subnet(network_client, create_subnet_details)
Exemple #14
0
def delete_vcn_stack(network_client, compartment_id, vcn_id=None, vcn=None):
    if not vcn_id and not vcn:
        raise ValueError("Either vcn_id or vcn must be provided")

    if vcn_id:
        vcn = get(network_client, "get_vcn", vcn_id)

    remove_stack = {
        "id": False,
        "vcn": False,
        "subnets": {},
        "route_tables": [],
        "internet_gateways": {},
        "security_lists": [],
        "dhcp_options": [],
        "local_peering_gateways": [],
        "nat_gateways": [],
        "service_gateways": [],
    }
    if vcn:
        vcn_subnets = list_entities(network_client,
                                    "list_subnets",
                                    compartment_id,
                                    vcn_id=vcn.id)
        for subnet in vcn_subnets:
            deleted = delete(
                network_client,
                "delete_subnet",
                subnet.id,
                wait_for_states=[Subnet.LIFECYCLE_STATE_TERMINATED],
                retry_strategy=DEFAULT_RETRY_STRATEGY,
            )
            remove_stack["subnets"][subnet.id] = deleted

        # Delete all the routes (and disable the gateway target
        # if they are the default which means that they can't be deleted)
        routes = list_entities(
            network_client,
            "list_route_tables",
            compartment_id,
            vcn_id=vcn.id,
            sort_by="TIMECREATED",
            sort_order="ASC",
        )
        # Disable routes on the default route table
        if routes:
            # Disable all routes
            for route in routes:
                update_details = UpdateRouteTableDetails(route_rules=[])
                update(
                    network_client,
                    "update_route_table",
                    route.id,
                    update_details,
                    wait_for_states=[RouteTable.LIFECYCLE_STATE_AVAILABLE],
                    retry_strategy=DEFAULT_RETRY_STRATEGY,
                )

            # Delete all non default routes
            if len(routes) > 1:
                for route in routes[1:]:
                    deleted = delete(
                        network_client,
                        "delete_route_table",
                        route.id,
                        wait_for_states=[
                            RouteTable.LIFECYCLE_STATE_TERMINATED
                        ],
                        retry_strategy=DEFAULT_RETRY_STRATEGY,
                    )
                    remove_stack["route_tables"].append(deleted)

        # Delete all gateways (can't delete the default)
        gateways = list_entities(
            network_client,
            "list_internet_gateways",
            compartment_id,
            vcn_id=vcn.id,
            sort_by="TIMECREATED",
            sort_order="ASC",
        )
        for gateway in gateways:
            deleted = delete(
                network_client,
                "delete_internet_gateway",
                gateway.id,
                wait_for_states=[InternetGateway.LIFECYCLE_STATE_TERMINATED],
                retry_strategy=DEFAULT_RETRY_STRATEGY,
            )
            remove_stack["internet_gateways"][gateway.id] = deleted

        # Delete all security lists
        securities = list_entities(
            network_client,
            "list_security_lists",
            compartment_id,
            vcn_id=vcn.id,
            sort_by="TIMECREATED",
            sort_order="ASC",
        )
        # Can't delete the detault
        if len(securities) > 1:
            for security in securities[1:]:
                deleted = delete(
                    network_client,
                    "delete_security_list",
                    security.id,
                    wait_for_states=[SecurityList.LIFECYCLE_STATE_TERMINATED],
                    retry_strategy=DEFAULT_RETRY_STRATEGY,
                )
                remove_stack["security_lists"].append(deleted)

        # Delete all DHCP options
        dhcp_options = list_entities(
            network_client,
            "list_dhcp_options",
            compartment_id,
            vcn_id=vcn.id,
            sort_by="TIMECREATED",
            sort_order="ASC",
        )

        if len(dhcp_options) > 1:
            for dhcp_option in dhcp_options[1:]:
                deleted = delete(
                    network_client,
                    "delete_dhcp_options",
                    dhcp_option.id,
                    wait_for_states=[DhcpOptions.LIFECYCLE_STATE_TERMINATED],
                    retry_strategy=DEFAULT_RETRY_STRATEGY,
                )
                remove_stack["dhcp_options"].append(deleted)

        # Delete local peering gateways
        local_peering_gateways = list_entities(network_client,
                                               "list_local_peering_gateways",
                                               compartment_id,
                                               vcn_id=vcn.id)
        for local_peering_gateway in local_peering_gateways:
            deleted = delete(
                network_client,
                "delete_local_peering_gateway",
                local_peering_gateway.id,
                wait_for_states=[
                    LocalPeeringGateway.LIFECYCLE_STATE_TERMINATED
                ],
                retry_strategy=DEFAULT_RETRY_STRATEGY,
            )
            remove_stack["local_peering_gateways"].append(deleted)

        # Delete all NAT gateways
        nat_gateways = list_entities(network_client,
                                     "list_nat_gateways",
                                     compartment_id,
                                     vcn_id=vcn.id)
        for gateway in nat_gateways:
            deleted = delete(
                network_client,
                "delete_nat_gateway",
                gateway.id,
                wait_for_states=[NatGateway.LIFECYCLE_STATE_TERMINATED],
                retry_strategy=DEFAULT_RETRY_STRATEGY,
            )
            remove_stack["nat_gateways"].append(deleted)

        # Delete Service Gateways
        service_gateways = list_entities(network_client,
                                         "list_service_gateways",
                                         compartment_id,
                                         vcn_id=vcn.id)
        for service_gateway in service_gateways:
            deleted = delete(
                network_client,
                "delete_service_gateway",
                service_gateway.id,
                wait_for_states=[ServiceGateway.LIFECYCLE_STATE_TERMINATED],
                retry_strategy=DEFAULT_RETRY_STRATEGY,
            )
            remove_stack["service_gateways"].append(deleted)

        # The delete_vcn defaults internally to succeed_on_not_found
        # https://github.com/oracle/oci-python-sdk/blob/bafa4f0d68be097568772cd3cda250e60cb61a0c/src/oci/core/virtual_network_client_composite_operations.py#L1758
        deleted = delete(
            network_client,
            "delete_vcn",
            vcn.id,
            wait_for_states=[Vcn.LIFECYCLE_STATE_TERMINATED],
            retry_strategy=DEFAULT_RETRY_STRATEGY,
        )
        remove_stack["id"] = vcn.id
        remove_stack["vcn"] = deleted
    return remove_stack
Exemple #15
0
def ensure_vcn_stack(
    network_client,
    compartment_id,
    vcn_kwargs=None,
    internet_gateway_kwargs=None,
    route_table_kwargs=None,
    subnet_kwargs=None,
):

    if not vcn_kwargs:
        vcn_kwargs = {}

    if not internet_gateway_kwargs:
        internet_gateway_kwargs = {}

    if not route_table_kwargs:
        route_table_kwargs = {}

    if not subnet_kwargs:
        subnet_kwargs = {}

    vcn = None
    if "id" in vcn_kwargs and vcn_kwargs["id"]:
        vcn = get(network_client, "get_vcn", vcn_kwargs["id"])
    elif "display_name" in vcn_kwargs and vcn_kwargs["display_name"]:
        vcn = get_vcn_by_name(network_client, compartment_id,
                              vcn_kwargs["display_name"])

    if not vcn:
        return new_vcn_stack(
            network_client,
            compartment_id,
            vcn_kwargs=vcn_kwargs,
            internet_gateway_kwargs=internet_gateway_kwargs,
            route_table_kwargs=route_table_kwargs,
            subnet_kwargs=subnet_kwargs,
        )

    stack = get_vcn_stack(network_client, compartment_id, vcn.id)
    # Validate whether the rest of the stack is a match
    # if not, add new elements to the stack
    if internet_gateway_kwargs:
        gateway = get_internet_gateway_in_vcn_stack(
            stack,
            internet_gateway_kwargs=internet_gateway_kwargs,
            optional_value_kwargs=["id", "display_name"],
        )
        if not gateway:
            create_ig_details = prepare_details(
                CreateInternetGatewayDetails,
                compartment_id=compartment_id,
                vcn_id=vcn.id,
                **internet_gateway_kwargs,
            )

            gateway = create_internet_gateway(network_client,
                                              create_ig_details)
            if not gateway:
                raise RuntimeError(
                    "Failed to create internet gateway with details: {}".
                    format(create_ig_details))

    if subnet_kwargs:
        subnet = get_subnet_in_vcn_stack(
            stack,
            subnet_kwargs=subnet_kwargs,
            optional_value_kwargs=["id", "display_name"],
        )
        if not subnet:
            # Add subnet that reflects subnet_kwargs to the stack
            subnet = create_subnet_stack(
                network_client,
                compartment_id,
                stack["vcn"],
                internet_gateway_kwargs=internet_gateway_kwargs,
                route_table_kwargs=route_table_kwargs,
                subnet_kwargs=subnet_kwargs,
            )
            if not subnet:
                return False
    return True
Exemple #16
0
def update_vcn_stack(
    network_client,
    compartment_id,
    vcn_kwargs=None,
    internet_gateway_kwargs=None,
    route_table_kwargs=None,
    subnet_kwargs=None,
):

    if not vcn_kwargs:
        vcn_kwargs = {}

    if not internet_gateway_kwargs:
        internet_gateway_kwargs = {}

    if not route_table_kwargs:
        route_table_kwargs = {}

    if not subnet_kwargs:
        subnet_kwargs = {}

    vcn = None
    if "id" in vcn_kwargs and vcn_kwargs["id"]:
        vcn = get(network_client, "get_vcn", vcn_kwargs["id"])
    elif "display_name" in vcn_kwargs and vcn_kwargs["display_name"]:
        vcn = get_vcn_by_name(network_client, compartment_id,
                              vcn_kwargs["display_name"])

    if not vcn:
        return new_vcn_stack(
            network_client,
            compartment_id,
            vcn_kwargs=vcn_kwargs,
            internet_gateway_kwargs=internet_gateway_kwargs,
            route_table_kwargs=route_table_kwargs,
            subnet_kwargs=subnet_kwargs,
        )

    stack = get_vcn_stack(network_client, compartment_id, vcn.id)
    if vcn_kwargs:
        update_vcn_details = prepare_details(UpdateVcnDetails, **vcn_kwargs)
        updated_vcn = update(network_client, "update_vcn", vcn.id,
                             update_vcn_details)
        if not updated_vcn:
            raise RuntimeError("Failed to update VCN: {}".format(vcn.id))
        stack["vcn"] = updated_vcn

    if internet_gateway_kwargs:
        existing_ig = None
        if "id" in internet_gateway_kwargs and internet_gateway_kwargs["id"]:
            existing_ig = find_in_dict({"id": internet_gateway_kwargs["id"]},
                                       stack["internet_gateways"])
        elif "display_name" in internet_gateway_kwargs:
            existing_ig = find_in_dict(
                {"display_name": internet_gateway_kwargs["display_name"]},
                stack["internet_gateways"],
            )
        if existing_ig:
            update_ie_details = prepare_details(UpdateInternetGatewayDetails,
                                                **internet_gateway_kwargs)
            updated_ie = update(
                network_client,
                "update_internet_gateway",
                existing_ig.id,
                update_ie_details,
            )
            if not updated_ie:
                raise RuntimeError(
                    "Failed to update Internet Gateway: {}".format(
                        existing_ig.id))
            stack["internet_gateways"][updated_ie.id] = updated_ie
        else:
            create_ie_details = prepare_details(CreateInternetGatewayDetails,
                                                **internet_gateway_kwargs)
            ie = create_internet_gateway(network_client, create_ie_details)
            if ie:
                stack["internet_gateways"][ie.id] = ie
    if subnet_kwargs:
        existing_subnet = None
        if "id" in subnet_kwargs and subnet_kwargs["id"]:
            existing_subnet = find_in_dict({"id": subnet_kwargs["id"]},
                                           stack["subnets"])
        elif "display_name" in subnet_kwargs:
            existing_subnet = find_in_dict(
                {"display_name": subnet_kwargs["display_name"]},
                stack["subnets"])
        if existing_subnet:
            update_subnet_details = prepare_details(UpdateSubnetDetails,
                                                    **subnet_kwargs)
            updated_subnet = update(
                network_client,
                "update_subnet",
                existing_subnet.id,
                update_subnet_details,
            )
            if not updated_subnet:
                raise RuntimeError("Failed to update Subnet: {}".format(
                    existing_subnet.id))
            stack["subnets"][updated_subnet.id] = updated_subnet
        else:
            create_subnet_details = prepare_details(CreateSubnetDetails,
                                                    **subnet_kwargs)
            subnet = create_subnet(
                network_client,
                create_subnet_details,
            )
            if subnet:
                stack["subnets"][subnet.id] = subnet
    return stack