cloud-setup: Add OCI (Oracle Cloud) provider
Initial support for OCI. It doesn't support VLAN configuration yet as the requirements are not clear. It doesn't support secondary IP addresses because the IMDS server doesn't expose them. Instead of using plain text format, it gets a single response in JSON format and parses it. The dependency to jansson is now mandatory for that.
This commit is contained in:
1
NEWS
1
NEWS
@@ -22,6 +22,7 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
|
||||
* Support automatically adding routes to DNS servers via the
|
||||
ipv4.routed-dns and ipv6.routed-dns properties; when enabled, each
|
||||
name server is reached only via the device that specifies it.
|
||||
* Support OCI in nm-cloud-setup
|
||||
|
||||
=============================================
|
||||
NetworkManager-1.50
|
||||
|
@@ -184,6 +184,10 @@
|
||||
<para><literal>NM_CLOUD_SETUP_ALIYUN</literal>: boolean, whether Alibaba Cloud (Aliyun) support is enabled. Defaults
|
||||
to <literal>no</literal>.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><literal>NM_CLOUD_SETUP_OCI</literal>: boolean, whether Oracle Cloud (OCI) support is enabled. Defaults
|
||||
to <literal>no</literal>.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</refsect1>
|
||||
@@ -417,6 +421,34 @@ ln -s /etc/systemd/system/timers.target.wants/nm-cloud-setup.timer /usr/lib/syst
|
||||
</itemizedlist>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title>Oracle Cloud (OCI)</title>
|
||||
|
||||
<para>For OCI, the tools tries to fetch configuration from <literal>http://169.254.169.254/</literal>. Currently, it only
|
||||
configures IPv4 and does nothing about IPv6. It will do the following.</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>First fetch <literal>http://169.254.169.254/opc/v2/instance</literal> to determine whether the
|
||||
expected API is present. This determines whether OCI environment is detected and whether to proceed
|
||||
to configure the host using OCI meta data.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Fetch <literal>http://169.254.169.254/opc/v2/vnics</literal> to get the configuration
|
||||
for all the VNICs, getting their MAC address, private IP address, gateway and subnet block.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Then nm-cloud-setup iterates over all interfaces for which it could fetch a configuration.
|
||||
If no ethernet device for the respective MAC address is found, it is skipped.
|
||||
Also, if the device is currently not activated in NetworkManager or if the currently
|
||||
activated profile has a user-data <literal>org.freedesktop.nm-cloud-setup.skip=yes</literal>,
|
||||
it is skipped. Also, there is only one interface and one IP address, the tool does nothing.</para>
|
||||
<para>Then the tool configures the system like doing for AWS environment. That is, using source based policy routing
|
||||
with the tables/rules 30200/30400.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</refsect2>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@@ -800,6 +800,7 @@ endif
|
||||
enable_nm_cloud_setup = get_option('nm_cloud_setup')
|
||||
if enable_nm_cloud_setup
|
||||
assert(libcurl_dep.found(), 'nm-cloud-setup requires libcurl library. Use -Dnm_cloud_setup=false to disable it')
|
||||
assert(jansson_dep.found(), 'nm-cloud-setup requires jansson library. Use -Dnm_cloud_setup=false to disable it')
|
||||
endif
|
||||
|
||||
enable_docs = get_option('docs')
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "nmcs-provider-gcp.h"
|
||||
#include "nmcs-provider-azure.h"
|
||||
#include "nmcs-provider-aliyun.h"
|
||||
#include "nmcs-provider-oci.h"
|
||||
#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -104,6 +105,7 @@ _provider_detect(SigTermData *sigterm_data)
|
||||
NMCS_TYPE_PROVIDER_GCP,
|
||||
NMCS_TYPE_PROVIDER_AZURE,
|
||||
NMCS_TYPE_PROVIDER_ALIYUN,
|
||||
NMCS_TYPE_PROVIDER_OCI,
|
||||
};
|
||||
int i;
|
||||
gulong cancellable_signal_id;
|
||||
|
@@ -36,12 +36,14 @@ libnm_cloud_setup_core = static_library(
|
||||
'nmcs-provider-gcp.c',
|
||||
'nmcs-provider-azure.c',
|
||||
'nmcs-provider-aliyun.c',
|
||||
'nmcs-provider-oci.c',
|
||||
'nmcs-provider.c',
|
||||
),
|
||||
dependencies: [
|
||||
libnm_dep,
|
||||
glib_dep,
|
||||
libcurl_dep,
|
||||
jansson_dep,
|
||||
],
|
||||
)
|
||||
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_AZURE "NM_CLOUD_SETUP_AZURE"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_EC2 "NM_CLOUD_SETUP_EC2"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_GCP "NM_CLOUD_SETUP_GCP"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_OCI "NM_CLOUD_SETUP_OCI"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_LOG "NM_CLOUD_SETUP_LOG"
|
||||
|
||||
/* Undocumented/internal environment variables for configuring nm-cloud-setup.
|
||||
@@ -20,6 +21,7 @@
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_AZURE_HOST "NM_CLOUD_SETUP_AZURE_HOST"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_EC2_HOST "NM_CLOUD_SETUP_EC2_HOST"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_GCP_HOST "NM_CLOUD_SETUP_GCP_HOST"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_OCI_HOST "NM_CLOUD_SETUP_OCI_HOST"
|
||||
#define NMCS_ENV_NM_CLOUD_SETUP_MAP_INTERFACES "NM_CLOUD_SETUP_MAP_INTERFACES"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@@ -31,6 +31,7 @@ ExecStart=@libexecdir@/nm-cloud-setup
|
||||
#Environment=NM_CLOUD_SETUP_GCP=yes
|
||||
#Environment=NM_CLOUD_SETUP_AZURE=yes
|
||||
#Environment=NM_CLOUD_SETUP_ALIYUN=yes
|
||||
#Environment=NM_CLOUD_SETUP_OCI=yes
|
||||
|
||||
CapabilityBoundingSet=
|
||||
KeyringMode=private
|
||||
|
221
src/nm-cloud-setup/nmcs-provider-oci.c
Normal file
221
src/nm-cloud-setup/nmcs-provider-oci.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
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)) {
|
||||
_LOGW("get-config: JSON parse failure for VNIC at index %zu, ignoring VNIC", i);
|
||||
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) {
|
||||
_LOGW("get-config: missing or invalid 'macAddr' (VNIC %s idx=%zu), ignoring VNIC",
|
||||
vnic_id,
|
||||
i);
|
||||
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 {
|
||||
_LOGW("get-config: missing or invalid 'privateIp' (VNIC %s idx=%zu)", vnic_id, i);
|
||||
}
|
||||
|
||||
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 {
|
||||
_LOGW("get-config: missing or invalid 'virtualRouterIp' (VNIC %s idx=%zu)", vnic_id, i);
|
||||
}
|
||||
|
||||
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 {
|
||||
_LOGW("get-config: missing or invalid 'subnetCidrBlock' (VNIC %s idx=%zu)", vnic_id, i);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
27
src/nm-cloud-setup/nmcs-provider-oci.h
Normal file
27
src/nm-cloud-setup/nmcs-provider-oci.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#ifndef __NMCS_PROVIDER_OCI_H__
|
||||
#define __NMCS_PROVIDER_OCI_H__
|
||||
|
||||
#include "nmcs-provider.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct _NMCSProviderOCI NMCSProviderOCI;
|
||||
typedef struct _NMCSProviderOCIClass NMCSProviderOCIClass;
|
||||
|
||||
#define NMCS_TYPE_PROVIDER_OCI (nmcs_provider_oci_get_type())
|
||||
#define NMCS_PROVIDER_OCI(obj) \
|
||||
(_NM_G_TYPE_CHECK_INSTANCE_CAST((obj), NMCS_TYPE_PROVIDER_OCI, NMCSProviderOCI))
|
||||
#define NMCS_PROVIDER_OCI_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), NMCS_TYPE_PROVIDER_OCI, NMCSProviderOCIClass))
|
||||
#define NMCS_IS_PROVIDER_OCI(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMCS_TYPE_PROVIDER_OCI))
|
||||
#define NMCS_IS_PROVIDER_OCI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMCS_TYPE_PROVIDER_OCI))
|
||||
#define NMCS_PROVIDER_OCI_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS((obj), NMCS_TYPE_PROVIDER_OCI, NMCSProviderOCIClass))
|
||||
|
||||
GType nmcs_provider_oci_get_type(void);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#endif /* __NMCS_PROVIDER_OCI_H__ */
|
Reference in New Issue
Block a user