/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "libnm-client-aux-extern/nm-default-client.h" #include "nmcs-provider-oci.h" #include "nm-cloud-setup-utils.h" #include "libnm-glib-aux/nm-jansson.h" /*****************************************************************************/ #define HTTP_TIMEOUT_MS 3000 #define NM_OCI_HEADER "Authorization:Bearer Oracle" #define NM_OCI_HOST "169.254.169.254" #define NM_OCI_BASE "http://" NM_OCI_HOST NMCS_DEFINE_HOST_BASE(_oci_base, NMCS_ENV_NM_CLOUD_SETUP_OCI_HOST, NM_OCI_BASE); #define _oci_uri_concat(...) nmcs_utils_uri_build_concat(_oci_base(), "opc/v2/", __VA_ARGS__) /*****************************************************************************/ struct _NMCSProviderOCI { NMCSProvider parent; }; struct _NMCSProviderOCIClass { NMCSProviderClass parent; }; G_DEFINE_TYPE(NMCSProviderOCI, nmcs_provider_oci, NMCS_TYPE_PROVIDER); /*****************************************************************************/ static void _detect_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) { gs_unref_object GTask *task = user_data; gs_free_error GError *get_error = NULL; gs_free_error GError *error = NULL; nm_http_client_poll_req_finish(NM_HTTP_CLIENT(source), result, NULL, NULL, &get_error); if (nm_utils_error_is_cancelled(get_error)) { g_task_return_error(task, g_steal_pointer(&get_error)); return; } if (get_error) { nm_utils_error_set(&error, NM_UTILS_ERROR_UNKNOWN, "failure to get OCI instance data: %s", get_error->message); g_task_return_error(task, g_steal_pointer(&error)); return; } g_task_return_boolean(task, TRUE); } static void detect(NMCSProvider *provider, GTask *task) { NMHttpClient *http_client; gs_free char *uri = NULL; http_client = nmcs_provider_get_http_client(provider); nm_http_client_poll_req(http_client, (uri = _oci_uri_concat("instance")), HTTP_TIMEOUT_MS, 256 * 1024, 7000, 1000, NM_MAKE_STRV(NM_OCI_HEADER), NULL, g_task_get_cancellable(task), NULL, NULL, _detect_done_cb, task); } /*****************************************************************************/ #define _VNIC_WARN(msg) _LOGW("get-config: " msg "(VNIC %s idx=%zu)", vnic_id, i) static void _get_config_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) { NMCSProviderGetConfigTaskData *get_config_data; NMCSProviderGetConfigIfaceData *config_iface_data; gs_unref_bytes GBytes *response = NULL; gs_free_error GError *error = NULL; nm_auto_decref_json json_t *vnics = NULL; size_t i; nm_http_client_poll_req_finish(NM_HTTP_CLIENT(source), result, NULL, &response, &error); if (nm_utils_error_is_cancelled(error)) return; get_config_data = user_data; if (error) goto out; vnics = json_loads(g_bytes_get_data(response, NULL), JSON_REJECT_DUPLICATES, NULL); if (!vnics || !json_is_array(vnics)) { nm_utils_error_set(&error, NM_UTILS_ERROR_UNKNOWN, "get-config: JSON parse failure, can't configure VNICs"); goto out; } for (i = 0; i < json_array_size(vnics); i++) { json_t *vnic, *field; const char *vnic_id = "", *val; gs_free char *mac = NULL; in_addr_t addr; int prefix; vnic = json_array_get(vnics, i); if (!json_is_object(vnic)) { _VNIC_WARN("JSON parse failure, ignoring VNIC"); continue; } field = json_object_get(vnic, "vnicId"); vnic_id = field && json_is_string(field) ? json_string_value(field) : ""; field = json_object_get(vnic, "macAddr"); val = field && json_is_string(field) ? json_string_value(field) : NULL; if (!val) { _VNIC_WARN("missing or invalid 'macAddr', ignoring VNIC"); continue; } mac = nmcs_utils_hwaddr_normalize(val, json_string_length(field)); config_iface_data = nmcs_provider_get_config_iface_data_create(get_config_data, FALSE, mac); config_iface_data->iface_idx = i; field = json_object_get(vnic, "privateIp"); val = field && json_is_string(field) ? json_string_value(field) : NULL; if (val && nm_inet_parse_bin(AF_INET, val, NULL, &addr)) { config_iface_data->has_ipv4s = TRUE; config_iface_data->ipv4s_len = 1; config_iface_data->ipv4s_arr = g_new(in_addr_t, 1); config_iface_data->ipv4s_arr[0] = addr; } else { _VNIC_WARN("missing or invalid 'privateIp'"); } field = json_object_get(vnic, "virtualRouterIp"); val = field && json_is_string(field) ? json_string_value(field) : NULL; if (val && nm_inet_parse_bin(AF_INET, val, NULL, &addr)) { config_iface_data->has_gateway = TRUE; config_iface_data->gateway = addr; } else { _VNIC_WARN("missing or invalid 'virtualRouterIp'"); } field = json_object_get(vnic, "subnetCidrBlock"); val = field && json_is_string(field) ? json_string_value(field) : NULL; if (val && nm_inet_parse_with_prefix_bin(AF_INET, val, NULL, &addr, &prefix)) { config_iface_data->has_cidr = TRUE; config_iface_data->cidr_addr = addr; config_iface_data->cidr_prefix = prefix; } else { _VNIC_WARN("missing or invalid 'subnetCidrBlock'"); } } out: _nmcs_provider_get_config_task_maybe_return(get_config_data, g_steal_pointer(&error)); } static void get_config(NMCSProvider *provider, NMCSProviderGetConfigTaskData *get_config_data) { gs_free const char *uri = NULL; nm_http_client_poll_req(nmcs_provider_get_http_client(provider), (uri = _oci_uri_concat("vnics")), HTTP_TIMEOUT_MS, 256 * 1024, 15000, 1000, NM_MAKE_STRV(NM_OCI_HEADER), NULL, get_config_data->intern_cancellable, NULL, NULL, _get_config_done_cb, get_config_data); } /*****************************************************************************/ static void nmcs_provider_oci_init(NMCSProviderOCI *self) {} static void dispose(GObject *object) { G_OBJECT_CLASS(nmcs_provider_oci_parent_class)->dispose(object); } static void nmcs_provider_oci_class_init(NMCSProviderOCIClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); NMCSProviderClass *provider_class = NMCS_PROVIDER_CLASS(klass); object_class->dispose = dispose; provider_class->_name = "oci"; provider_class->_env_provider_enabled = NMCS_ENV_NM_CLOUD_SETUP_OCI; provider_class->detect = detect; provider_class->get_config = get_config; }