diff options
Diffstat (limited to 'src/nm-cloud-setup/nmcs-provider-oci.c')
| -rw-r--r-- | src/nm-cloud-setup/nmcs-provider-oci.c | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/src/nm-cloud-setup/nmcs-provider-oci.c b/src/nm-cloud-setup/nmcs-provider-oci.c new file mode 100644 index 00000000..b0b0edf9 --- /dev/null +++ b/src/nm-cloud-setup/nmcs-provider-oci.c @@ -0,0 +1,221 @@ +/* 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; +} |