From 399c04e810fa88e55fc06c5cfa6c78f25dd12f92 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 4 Dec 2020 11:04:30 +0100 Subject: [PATCH 1/4] cloud-setup: fix handling cancellation of internal GET operation for EC2 provider There are two GCancellable at work: one is provided by the user during nmcs_provider_get_config(), and one is used internally for the individual HTTP GET requests. In _get_config_fetch_done_cb(), if the error reason is "cancelled", then it means that our internal iface_data->cancellable was cancelled. Probably because an error happend (like a timeout or the user cancelled the external GCancellable). In that case, we must not report that the task completed with a cancellation, because we need to preserve the error that was the original cause. --- clients/cloud-setup/nmcs-provider-ec2.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/clients/cloud-setup/nmcs-provider-ec2.c b/clients/cloud-setup/nmcs-provider-ec2.c index fb82914f38..7977eb640c 100644 --- a/clients/cloud-setup/nmcs-provider-ec2.c +++ b/clients/cloud-setup/nmcs-provider-ec2.c @@ -235,8 +235,16 @@ _get_config_fetch_done_cb(NMHttpClient *http_client, } } + /* If nm_utils_error_is_cancelled(error), then our internal iface_data->cancellable + * was cancelled, because the overall request failed. From point of view of the + * caller, this does not mean that a cancellation happened. It also means, our + * request overall is already about to fail. */ + nm_assert(!nm_utils_error_is_cancelled(error) || iface_data->error); + iface_data->n_pending--; - _get_config_task_maybe_return(iface_data, g_steal_pointer(&error)); + _get_config_task_maybe_return(iface_data, + nm_utils_error_is_cancelled(error) ? NULL + : g_steal_pointer(&error)); } static void From 422ab25626e2ec31930ec646850e28a78cd72b0c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 4 Dec 2020 11:21:19 +0100 Subject: [PATCH 2/4] cloud-setup: in EC2's _get_config_task_maybe_return() cancel internal requests on any error "iface_data->cancellable" is an internal cancellable for the parallel HTTP requests. Once we encounter a failure, those requests are all obsolete and must be cancelled. --- clients/cloud-setup/nmcs-provider-ec2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clients/cloud-setup/nmcs-provider-ec2.c b/clients/cloud-setup/nmcs-provider-ec2.c index 7977eb640c..d96974e94e 100644 --- a/clients/cloud-setup/nmcs-provider-ec2.c +++ b/clients/cloud-setup/nmcs-provider-ec2.c @@ -151,6 +151,8 @@ _get_config_task_maybe_return(GetConfigIfaceData *iface_data, GError *error_take iface_data->error = error_take; } else g_error_free(error_take); + + nm_clear_g_cancellable(&iface_data->cancellable); } if (iface_data->n_pending > 0) From be8a3f9902f330bbe8cac58233a7b10bcebd225e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Fri, 4 Dec 2020 11:23:28 +0100 Subject: [PATCH 3/4] cloud-setup: simplify cancellation in _get_config_fetch_cancelled_cb() If we call g_cancellable_connect() on a GCancellable that is already cancelled, then the callback is invoked synchronously. We need to handle that. However, we can slightly simplify the code. There is no change in behavior, but we can always let the cancelled callback return the result. --- clients/cloud-setup/nmcs-provider-ec2.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clients/cloud-setup/nmcs-provider-ec2.c b/clients/cloud-setup/nmcs-provider-ec2.c index d96974e94e..9d0ee95429 100644 --- a/clients/cloud-setup/nmcs-provider-ec2.c +++ b/clients/cloud-setup/nmcs-provider-ec2.c @@ -268,9 +268,6 @@ _get_config_fetch_cancelled_cb(GObject *object, gpointer user_data) { GetConfigIfaceData *iface_data = user_data; - if (iface_data->cancelled_id == 0) - return; - nm_clear_g_signal_handler(g_task_get_cancellable(iface_data->get_config_data->task), &iface_data->cancelled_id); _get_config_task_maybe_return(iface_data, nm_utils_error_new_cancelled(FALSE, NULL)); @@ -334,7 +331,7 @@ _get_config_metadata_ready_cb(GObject *source, GAsyncResult *result, gpointer us iface_data, NULL); if (cancelled_id == 0) { - _get_config_task_maybe_return(iface_data, nm_utils_error_new_cancelled(FALSE, NULL)); + /* the callback was already invoked synchronously and the task already returned. */ return; } From a4f1fa08938b12bd97c3dde0066ad5a4ceb8055a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 3 Dec 2020 18:12:21 +0100 Subject: [PATCH 4/4] man: add `man 8 nm-cloud-setup` https://bugzilla.redhat.com/show_bug.cgi?id=1867997 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/issues/ ## 600 --- Makefile.am | 7 + configure.ac | 1 + contrib/fedora/rpm/NetworkManager.spec | 1 + docs/api/Makefile.am | 4 + docs/api/network-manager-docs.xml | 1 + man/meson.build | 4 + man/nm-cloud-setup.xml | 263 +++++++++++++++++++++++++ 7 files changed, 281 insertions(+) create mode 100644 man/nm-cloud-setup.xml diff --git a/Makefile.am b/Makefile.am index 0ea43de987..b5d5e8cb41 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5165,6 +5165,13 @@ EXTRA_DIST += man/nm-openvswitch.7 dist_dependencies += man/nm-openvswitch.7 endif +if BUILD_NM_CLOUD_SETUP +man_pages += man/nm-cloud-setup.8 +else +EXTRA_DIST += man/nm-cloud-setup.8 +dist_dependencies += man/nm-cloud-setup.8 +endif + CLEANFILES += \ man/common.ent diff --git a/configure.ac b/configure.ac index 8fd65d1371..e8b07654d9 100644 --- a/configure.ac +++ b/configure.ac @@ -1271,6 +1271,7 @@ if test "$build_docs" != "yes" -a \ -f "$srcdir"/man/nmtui.1 -a \ \ -f "$srcdir"/man/nm-openvswitch.7 -a \ + -f "$srcdir"/man/nm-cloud-setup.8 -a \ \ -f "$srcdir"/man/nm-settings-dbus.5 -a \ -f "$srcdir"/man/nm-settings-ifcfg-rh.5 -a \ diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec index c5d3696eac..6fa30762f2 100644 --- a/contrib/fedora/rpm/NetworkManager.spec +++ b/contrib/fedora/rpm/NetworkManager.spec @@ -1131,6 +1131,7 @@ fi %{systemd_dir}/nm-cloud-setup.timer %{nmlibdir}/dispatcher.d/90-nm-cloud-setup.sh %{nmlibdir}/dispatcher.d/no-wait.d/90-nm-cloud-setup.sh +%{_mandir}/man8/nm-cloud-setup.8* %endif diff --git a/docs/api/Makefile.am b/docs/api/Makefile.am index 4f370449a2..65eed1a534 100644 --- a/docs/api/Makefile.am +++ b/docs/api/Makefile.am @@ -99,6 +99,10 @@ if WITH_OPENVSWITCH content_files += $(top_builddir)/man/nm-openvswitch.xml endif +if BUILD_NM_CLOUD_SETUP +content_files += $(top_builddir)/man/nm-cloud-setup.xml +endif + if CONFIG_PLUGIN_IFCFG_RH content_files += $(top_builddir)/man/nm-settings-ifcfg-rh.xml endif diff --git a/docs/api/network-manager-docs.xml b/docs/api/network-manager-docs.xml index a97eaa895d..7a57582caa 100644 --- a/docs/api/network-manager-docs.xml +++ b/docs/api/network-manager-docs.xml @@ -79,6 +79,7 @@ + diff --git a/man/meson.build b/man/meson.build index df6e783790..d31d473112 100644 --- a/man/meson.build +++ b/man/meson.build @@ -38,6 +38,10 @@ if enable_ovs mans += [['nm-openvswitch', '7']] endif +if enable_nm_cloud_setup + mans += [['nm-cloud-setup', '8']] +endif + foreach man: mans input = man[0] + '.xml' content_files += join_paths(meson.current_source_dir(), input) diff --git a/man/nm-cloud-setup.xml b/man/nm-cloud-setup.xml new file mode 100644 index 0000000000..16c695a97f --- /dev/null +++ b/man/nm-cloud-setup.xml @@ -0,0 +1,263 @@ + + + +%entities; +]> + + + + + + nm-cloud-setup + Automatic Network Configuration in Cloud with NetworkManager + + + + nm-cloud-setup + 8 + NetworkManager + Automatic Network Configuration in Cloud with NetworkManager + &NM_VERSION; + + + + nm-cloud-setup + Overview of Automatic Network Configuration in Cloud + + + + Overview + + When running a virtual machine in a public cloud environment, it is + desirable to automatically configure the network of that VM. + In simple setups, the VM only has one network interface and the public + cloud supports automatic configuration via DHCP, DHCP6 or IPv6 autoconf. + However, on the virtual machine might have multiple network + interfaces, or multiple IP addresses and IP subnets + on one interface. Also, the administrator can reconfigure those settings + while the machine is running. NetworkManager's nm-cloud-setup is a tool + that automatically picks up such configuration and updates the network + configuration of the host. + + Multiple cloud providers are supported. See . + + + + Use + + The goal of nm-cloud-setup is to be configuration-less and work automatically. + All you need is to opt-in to the desired cloud providers (see ) + and run /usr/libexec/nm-cloud-setup. + + Usually this is done by enabling the nm-cloud-setup.service systemd service + and let it run periodically. For that there is both a nm-cloud-setup.timer systemd timer + and a NetworkManager dispatcher script. + + + + Details + + + nm-cloud-setup configures the network by fetching the configuration from + the well-known meta data server of the cloud provider. That means, it already + needs the network configured to the point where it can reach the meta data + server. Commonly that means, that a simple connection profile is activated + that possibly uses DHCP to get the primary IP address. NetworkManager will + create such a profile for ethernet devices automatically if it is not configured + otherwise via "no-auto-default" setting in NetworkManager.conf. + One possible alternative may be to create such an initial profile with + nmcli device connect "$DEVICE" or + nmcli connection add type ethernet .... + + + nm-cloud-setup modifies the run time configuration akin to nmcli device modify. + With this approach, the configuration is not persisted + and only preserved until the device disconnects. + + + /usr/libexec/nm-cloud-setup + + The binary /usr/libexec/nm-cloud-setup does most of the + work. It supports no command line arguments but can be configured via environment + variables. + See for the supported environment variables. + + By default, all cloud providers are disabled unless you opt-in by enabling one + or several providers. If cloud providers are enabled, the program + tries to fetch the host's configuration from a meta data server of the cloud via HTTP. + If configuration could be not fetched, no cloud provider are detected and the + program quits. + If host configuration is obtained, the corresponding cloud provider is + successfully detected. Then the network of the host will be configured. + + It is intended to re-run nm-cloud-setup every time when the configuration + (maybe) changes. The tool is idempotent, so it should be OK to also run it + more often than necessary. You could run /usr/libexec/nm-cloud-setup + directly. However it may be preferable to restart the nm-cloud-setup systemd + service instead or use the timer or dispatcher script to run it periodically (see below). + + + + nm-cloud-setup.service systemd unit + Usually /usr/libexec/nm-cloud-setup is not run directly, + but only by systemctl restart nm-cloud-setup.service. This + ensures that the tool only runs once at any time. It also allows to integrate + use the nm-cloud-setup systemd timer, + and to enable/disable the service via systemd. + + As you need to set environment variable to configure nm-cloud-setup binary, + you can do so via systemd override files. Try systemctl edit nm-cloud-setup.service. + + + + nm-cloud-setup.timer systemd timer + /usr/libexec/nm-cloud-setup is intended to run + whenever an update is necessary. For example, during boot when when + changing the network configuration of the virtual machine via the cloud + provider. + + One way to do this, is by enabling the nm-cloud-setup.timer systemd timer + with systemctl enable --now nm-cloud-setup.timer. + + + + /usr/lib/NetworkManager/dispatcher.d/90-nm-cloud-setup.sh + + There is also a NetworkManager dispatcher script that will + run for example when an interface is activated by NetworkManager. + Together with the nm-cloud-setup.timer systemd timer this + script is to automatically pick up changes to the network. + + The dispatcher script will do nothing, unless the systemd service is + enabled. To use the dispatcher script you should therefor run + systemctl enable nm-cloud-setup.service once. + + + + + + Environment Variables + + The environment variables are used to configure /usr/libexec/nm-cloud-setup. + You may want to configure them in the systemd service with systemctl edit nm-cloud-setup.service. + + + + NM_CLOUD_SETUP_LOG: control the logging verbosity. Set it + one of TRACE, DEBUG, INFO, + WARN, ERR or OFF. The program + will print message on stdout and the default level is WARN. + + + NM_CLOUD_SETUP_AZURE: boolean, whether Microsoft Azure support is enabled. Defaults + to no. + + + NM_CLOUD_SETUP_EC2: boolean, whether Amazon EC2 (AWS) support is enabled. Defaults + to no. + + + NM_CLOUD_SETUP_GCP: boolean, whether Google GCP support is enabled. Defaults + to no. + + + + + + + Supported Cloud Providers + + + Amazon EC2 (AWS) + + The tools tries to fetch configuration from http://169.254.169.254/. Currently, it only + configures IPv4 and does nothing about IPv6. It will do the following. + + + + First fetch http://169.254.169.254/latest/meta-data/ to determine whether the + expected API is present. This determines whether EC2 environment is detected and whether to proceed + to configure the host using EC2 meta data. + + + Fetch http://169.254.169.254/2018-09-24/meta-data/network/interfaces/macs/ to get the list + of available interface. Interfaces are identified by their MAC address. + + + Then for each interface fetch http://169.254.169.254/2018-09-24/meta-data/network/interfaces/macs/$MAC/subnet-ipv4-cidr-block + and http://169.254.169.254/2018-09-24/meta-data/network/interfaces/macs/$MAC/local-ipv4s. + Thereby we get a list of local IPv4 addresses and one CIDR subnet block. + + + Then nm-cloud-setup iterates over all interfaces for which it could fetch IP 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 org.freedesktop.nm-cloud-setup.skip=yes, + it is skipped. + Then, the tool will change the runtime configuration of the device. + + + Add static IPv4 addresses for all the configured addresses from local-ipv4s with + prefix length according to subnet-ipv4-cidr-block. For example, + we might have here 2 IP addresses like "172.16.5.3/24,172.16.5.4/24". + + + Choose a route table 30400 + the index of the interface and + add a default route 0.0.0.0/0. The gateway + is the first IP address in the CIDR subnet block. For + example, we might get a route "0.0.0.0/0 172.16.5.1 10 table=30401". + + + Finally, add a policy routing rule for each address. For example + "priority 30401 from 172.16.5.3/32 table 30401, priority 30401 from 172.16.5.4/32 table 30401". + + + With above example, this roughly corresponds for interface eth0 to + nmcli device modify "eth0" ipv4.addresses "172.16.5.3/24,172.16.5.4/24" ipv4.routes "0.0.0.0/0 172.16.5.1 10 table=30401" ipv4.routing-rules "priority 30401 from 172.16.5.3/32 table 30401, priority 30401 from 172.16.5.4/32 table 30401". + Note that this replaces the previous addresses, routes and rules with the new information. + But also note that this only changes the run time configuration of the device. The + connection profile is not affected by that. + + + + + + + Google Cloud Platform (GCP) + + The tools tries to fetch configuration from http://metadata.google.internal/. + + + + Microsoft Azure + + The tools tries to fetch configuration from http://169.254.169.254/. + + + + + + See Also + + NetworkManager8 + nmcli1 + + +