cloud-setup,core: merge branch 'th/cloud-setup-preserve-external-ip'

https://bugzilla.redhat.com/show_bug.cgi?id=2132754

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1476
This commit is contained in:
Thomas Haller 2022-12-14 17:34:37 +01:00
commit f0e8b6f0e2
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
18 changed files with 525 additions and 72 deletions

View file

@ -182,6 +182,7 @@ EXTRA_DIST += \
examples/python/gi/add_connection.py \
examples/python/gi/checkpoint.py \
examples/python/gi/deactivate-all.py \
examples/python/gi/device-reapply.py \
examples/python/gi/device-state-ip4config.py \
examples/python/gi/dns.py \
examples/python/gi/firewall-zone.py \

View file

@ -0,0 +1,147 @@
#!/usr/bin/env python
# SPDX-License-Identifier: LGPL-2.1-or-later
import os
import sys
import gi
gi.require_version("NM", "1.0")
from gi.repository import NM, GLib, Gio, GObject
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def kf_from_data(data):
kf = GLib.KeyFile.new()
kf.load_from_data(data, 18446744073709551615, GLib.KeyFileFlags.NONE)
return kf
def kf_to_data(kf):
data, l = kf.to_data()
return data
def connection_to_kf(connection):
return kf_to_data(NM.keyfile_write(connection, NM.KeyfileHandlerFlags.NONE))
def connection_from_kf(data):
base_dir = os.getcwd()
return NM.keyfile_read(kf_from_data(data), base_dir, NM.KeyfileHandlerFlags.NONE)
def connection_from_stdin():
return connection_from_kf(sys.stdin.read())
def device_get_applied_connection(device):
mainloop = GLib.MainLoop()
r = []
def cb(device, result):
try:
connection, version_id = device.get_applied_connection_finish(result)
except Exception as e:
r.append(e)
else:
r.append(connection)
r.append(version_id)
mainloop.quit()
device.get_applied_connection_async(0, None, cb)
mainloop.run()
if len(r) == 1:
raise r[0]
connection, version_id = r
return connection, version_id
def device_reapply(device, connection, version_id, reapply_flags):
mainloop = GLib.MainLoop()
r = []
def cb(device, result):
try:
device.reapply_finish(result)
except Exception as e:
r.append(e)
mainloop.quit()
device.reapply_async(connection, version_id or 0, reapply_flags, None, cb)
mainloop.run()
if len(r) == 1:
raise r[0]
def parse_args():
import argparse
parser = argparse.ArgumentParser(
prog="device-reapply.py",
description="Example program to interact with the applied connection",
)
parser.add_argument("mode", choices=["get", "reapply", "modify"])
parser.add_argument("device")
parser.add_argument("-V", "--version-id", type=int)
parser.add_argument("-s", "--stdin", action="store_true")
parser.add_argument("-p", "--preserve-external-ip", action="store_true")
return parser.parse_args()
def main():
args = parse_args()
nmc = NM.Client.new()
device = [d for d in nmc.get_devices() if d.get_iface() == args.device]
if not device:
raise Exception(f'Device "{args.device}" not found')
if len(device) != 1:
raise Exception(f'Not unique device "{args.device}" found')
device = device[0]
assert not args.stdin or args.mode == "modify"
assert not args.preserve_external_ip or args.mode in ["modify", "reapply"]
if args.mode == "get":
connection, version_id = device_get_applied_connection(device)
version_id_matches = args.version_id is None or args.version_id == version_id
print(
f'# Applied connection on "{device.get_iface()}": "{connection.get_id()}" ({connection.get_uuid()}, {connection.get_connection_type()})'
)
s = "" if version_id_matches else f" (expected {args.version_id})"
print(f"# version-id={version_id}{s}")
print(f"#")
print(f"{connection_to_kf(connection)}")
if not version_id_matches:
eprint(
f"Applied version-id does not match (expects {args.version_id} but got {version_id})"
)
sys.exit(1)
sys.exit(0)
if args.mode == "reapply":
new_connection = None
elif args.stdin:
new_connection = connection_from_stdin()
else:
new_connection, _ = device_get_applied_connection(device)
reapply_flags = 0
if args.preserve_external_ip:
reapply_flags = 1 # NM.DeviceReapplyFlags.PRESERVE_EXTERNAL_IP
device_reapply(device, new_connection, args.version_id, reapply_flags)
if __name__ == "__main__":
main()

View file

@ -322,7 +322,7 @@
Reapply:
@connection: The optional connection settings that will be reapplied on the device. If empty, the currently active settings-connection will be used. The connection cannot arbitrarily differ from the current applied-connection otherwise the call will fail. Only certain changes are supported, like adding or removing IP addresses.
@version_id: If non-zero, the current version id of the applied-connection must match. The current version id can be retrieved via GetAppliedConnection. This optional argument allows to catch concurrent modifications between the GetAppliedConnection call and Reapply.
@flags: Flags which would modify the behavior of the Reapply call. There are no flags defined currently and the users should use the value of 0.
@flags: Flags which would modify the behavior of the Reapply call. Invalid flags are rejected.
Attempts to update the configuration of a device without deactivating it.
NetworkManager has the concept of connections, which are profiles that
@ -344,6 +344,9 @@
Reapply can make the applied-connection different from the
settings-connection, just like updating the settings-connection can make
them different.
Since 1.42, "preserve-external-ip" flag (0x1) is supported to not remove
externally added IP addresses and routes on the device during reapply.
-->
<method name="Reapply">
<arg name="connection" type="a{sa{sv}}" direction="in"/>

View file

@ -456,6 +456,22 @@
-->
<property name="Version" type="s" access="read"/>
<!--
VersionInfo:
NetworkManager version and capabilities.
The first element in the array is the NM_VERSION of the daemon. It is a binary representation
of the "Version" and can be compared numerically. The version is encoded as
"(major &lt;&lt; 16 | minor &lt;&lt; 8 | micro)".
The following elements are a bitfield of static capabilities of the daemon. See
#NMVersionInfoCapability for the available capability numbers.
Since: 1.42
-->
<property name="VersionInfo" type="au" access="read"/>
<!--
Capabilities:

View file

@ -12830,6 +12830,7 @@ reapply_connection(NMDevice *self, NMConnection *con_old, NMConnection *con_new)
* the current settings connection
* @version_id: either zero, or the current version id for the applied
* connection.
* @reapply_flags: the #NMDeviceReapplyFlags.
* @audit_args: on return, a string representing the changes
* @error: the error if %FALSE is returned
*
@ -12839,11 +12840,12 @@ reapply_connection(NMDevice *self, NMConnection *con_old, NMConnection *con_new)
* Return: %FALSE if the new configuration can not be reapplied.
*/
static gboolean
check_and_reapply_connection(NMDevice *self,
NMConnection *connection,
guint64 version_id,
char **audit_args,
GError **error)
check_and_reapply_connection(NMDevice *self,
NMConnection *connection,
guint64 version_id,
NMDeviceReapplyFlags reapply_flags,
char **audit_args,
GError **error)
{
NMDeviceClass *klass = NM_DEVICE_GET_CLASS(self);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self);
@ -13011,7 +13013,12 @@ check_and_reapply_connection(NMDevice *self,
reactivate_proxy_config(self);
nm_device_l3cfg_commit(self, NM_L3_CFG_COMMIT_TYPE_REAPPLY, FALSE);
nm_device_l3cfg_commit(
self,
NM_FLAGS_HAS(reapply_flags, NM_DEVICE_REAPPLY_FLAGS_PRESERVE_EXTERNAL_IP)
? NM_L3_CFG_COMMIT_TYPE_UPDATE
: NM_L3_CFG_COMMIT_TYPE_REAPPLY,
FALSE);
}
if (priv->state >= NM_DEVICE_STATE_IP_CHECK)
@ -13028,12 +13035,18 @@ nm_device_reapply(NMDevice *self, NMConnection *connection, GError **error)
{
g_return_val_if_fail(NM_IS_DEVICE(self), FALSE);
return check_and_reapply_connection(self, connection, 0, NULL, error);
return check_and_reapply_connection(self,
connection,
0,
NM_DEVICE_REAPPLY_FLAGS_NONE,
NULL,
error);
}
typedef struct {
NMConnection *connection;
guint64 version_id;
NMConnection *connection;
guint64 version_id;
NMDeviceReapplyFlags reapply_flags;
} ReapplyData;
static void
@ -13044,16 +13057,16 @@ reapply_cb(NMDevice *self,
gpointer user_data)
{
ReapplyData *reapply_data = user_data;
guint64 version_id = 0;
gs_unref_object NMConnection *connection = NULL;
GError *local = NULL;
gs_free char *audit_args = NULL;
guint64 version_id;
gs_unref_object NMConnection *connection = NULL;
NMDeviceReapplyFlags reapply_flags;
GError *local = NULL;
gs_free char *audit_args = NULL;
if (reapply_data) {
connection = reapply_data->connection;
version_id = reapply_data->version_id;
g_slice_free(ReapplyData, reapply_data);
}
connection = reapply_data->connection;
version_id = reapply_data->version_id;
reapply_flags = reapply_data->reapply_flags;
nm_g_slice_free(reapply_data);
if (error) {
nm_audit_log_device_op(NM_AUDIT_OP_DEVICE_REAPPLY,
@ -13073,6 +13086,7 @@ reapply_cb(NMDevice *self,
connection
?: nm_device_get_settings_connection_get_connection(self),
version_id,
reapply_flags,
&audit_args,
&local)) {
nm_audit_log_device_op(NM_AUDIT_OP_DEVICE_REAPPLY,
@ -13106,12 +13120,12 @@ impl_device_reapply(NMDBusObject *obj,
ReapplyData *reapply_data;
gs_unref_variant GVariant *settings = NULL;
guint64 version_id;
guint32 flags;
guint32 reapply_flags_u;
NMDeviceReapplyFlags reapply_flags;
g_variant_get(parameters, "(@a{sa{sv}}tu)", &settings, &version_id, &flags);
g_variant_get(parameters, "(@a{sa{sv}}tu)", &settings, &version_id, &reapply_flags_u);
/* No flags supported as of now. */
if (flags != 0) {
if (NM_FLAGS_ANY(reapply_flags_u, ~((guint32) NM_DEVICE_REAPPLY_FLAGS_PRESERVE_EXTERNAL_IP))) {
error =
g_error_new_literal(NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "Invalid flags specified");
nm_audit_log_device_op(NM_AUDIT_OP_DEVICE_REAPPLY,
@ -13124,6 +13138,9 @@ impl_device_reapply(NMDBusObject *obj,
return;
}
reapply_flags = reapply_flags_u;
nm_assert(reapply_flags_u == reapply_flags);
if (priv->state < NM_DEVICE_STATE_PREPARE || priv->state > NM_DEVICE_STATE_ACTIVATED) {
error = g_error_new_literal(NM_DEVICE_ERROR,
NM_DEVICE_ERROR_NOT_ACTIVE,
@ -13161,12 +13178,12 @@ impl_device_reapply(NMDBusObject *obj,
nm_connection_clear_secrets(connection);
}
if (connection || version_id) {
reapply_data = g_slice_new(ReapplyData);
reapply_data->connection = connection;
reapply_data->version_id = version_id;
} else
reapply_data = NULL;
reapply_data = g_slice_new(ReapplyData);
*reapply_data = (ReapplyData){
.connection = connection,
.version_id = version_id,
.reapply_flags = reapply_flags,
};
nm_device_auth_request(self,
invocation,
@ -13202,7 +13219,7 @@ impl_device_get_applied_connection(NMDBusObject *obj,
if (flags != 0) {
g_dbus_method_invocation_return_error_literal(invocation,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_FAILED,
NM_DEVICE_ERROR_INVALID_ARGUMENT,
"Invalid flags specified");
return;
}

View file

@ -115,6 +115,7 @@ static guint signals[LAST_SIGNAL] = {0};
NM_GOBJECT_PROPERTIES_DEFINE(NMManager,
PROP_VERSION,
PROP_VERSION_INFO,
PROP_CAPABILITIES,
PROP_STATE,
PROP_STARTUP,
@ -420,6 +421,31 @@ static NM_CACHED_QUARK_FCN("autoconnect-root", autoconnect_root_quark);
/*****************************************************************************/
static GVariant *
_version_info_get(void)
{
const guint32 arr[] = {
NM_VERSION,
};
/* The array contains as first element NM_VERSION, which can be
* used to numerically compare the version (see also NM_ENCODE_VERSION,
* nm_utils_version(), nm_encode_version() and nm_decode_version().
*
* The following elements of the array are a bitfield of capabilities.
* These capabilities should only depend on compile-time abilities
* (unlike NM_MANAGER_CAPABILITIES, NMCapability). The supported values
* are from NMVersionInfoCapability enum. This way to expose capabilities
* is more cumbersome but more efficient compared to NM_MANAGER_CAPABILITIES.
* As such, it is cheap to add capabilities for something, where you would
* avoid it as NM_MANAGER_CAPABILITIES due to the overhead.
*/
return nm_g_variant_new_au(arr, G_N_ELEMENTS(arr));
}
/*****************************************************************************/
static gboolean
_connection_is_vpn(NMConnection *connection)
{
@ -8133,6 +8159,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
case PROP_VERSION:
g_value_set_string(value, VERSION);
break;
case PROP_VERSION_INFO:
g_value_set_variant(value, _version_info_get());
break;
case PROP_CAPABILITIES:
g_value_set_variant(value,
nm_g_variant_new_au(nm_g_array_first_p(priv->capabilities, guint32),
@ -8646,6 +8675,9 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = {
NM_MANAGER_ACTIVATING_CONNECTION),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Startup", "b", NM_MANAGER_STARTUP),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Version", "s", NM_MANAGER_VERSION),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("VersionInfo",
"au",
NM_MANAGER_VERSION_INFO),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Capabilities",
"au",
NM_MANAGER_CAPABILITIES),
@ -8704,6 +8736,14 @@ nm_manager_class_init(NMManagerClass *manager_class)
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_VERSION_INFO] =
g_param_spec_variant(NM_MANAGER_VERSION_INFO,
"",
"",
G_VARIANT_TYPE("au"),
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
obj_properties[PROP_CAPABILITIES] =
g_param_spec_variant(NM_MANAGER_CAPABILITIES,
"",

View file

@ -20,6 +20,7 @@
(G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_MANAGER, NMManagerClass))
#define NM_MANAGER_VERSION "version"
#define NM_MANAGER_VERSION_INFO "version-info"
#define NM_MANAGER_CAPABILITIES "capabilities"
#define NM_MANAGER_STATE "state"
#define NM_MANAGER_STARTUP "startup"

View file

@ -135,3 +135,64 @@ nmc_client_new_waitsync(GCancellable *cancellable,
}
return TRUE;
}
/*****************************************************************************/
guint32
nmc_client_has_version_info_v(NMClient *nmc)
{
const guint32 *ver;
gsize len;
ver = nm_client_get_version_info(nmc, &len);
if (len < 1)
return 0;
return ver[0];
}
gboolean
nmc_client_has_version_info_capability(NMClient *nmc, NMVersionInfoCapability capability)
{
const guint32 *ver;
gsize len;
gsize idx;
gsize idx_hi;
gsize idx_lo;
ver = nm_client_get_version_info(nmc, &len);
if (len < 2)
return FALSE;
len--;
ver++;
idx = (gsize) capability;
if (idx >= G_MAXSIZE - 31u)
return FALSE;
idx_hi = ((idx + 31u) / 32u);
idx_lo = (idx % 32u);
if (idx_hi > len)
return FALSE;
return NM_FLAGS_ANY(ver[idx_hi], (1ull << idx_lo));
}
gboolean
nmc_client_has_capability(NMClient *nmc, NMCapability capability)
{
const guint32 *caps;
gsize len;
gsize i;
caps = nm_client_get_capabilities(nmc, &len);
for (i = 0; i < len; i++) {
if (caps[i] == capability)
return TRUE;
}
return FALSE;
}

View file

@ -21,4 +21,8 @@ gboolean nmc_client_new_waitsync(GCancellable *cancellable,
const char *first_property_name,
...);
guint32 nmc_client_has_version_info_v(NMClient *nmc);
gboolean nmc_client_has_version_info_capability(NMClient *nmc, NMVersionInfoCapability capability);
gboolean nmc_client_has_capability(NMClient *nmc, NMCapability capability);
#endif /* __NM_LIBNM_AUX_H__ */

View file

@ -1881,9 +1881,11 @@ global:
libnm_1_42_0 {
global:
nm_client_get_version_info;
nm_client_wait_shutdown;
nm_client_wait_shutdown_finish;
nm_device_loopback_get_type;
nm_device_reapply_flags_get_type;
nm_range_cmp;
nm_range_from_str;
nm_range_get_range;
@ -1906,4 +1908,5 @@ global:
nm_setting_ovs_port_remove_trunk_by_value;
nm_setting_vlan_get_protocol;
nm_utils_ensure_gtypes;
nm_version_info_capability_get_type;
} libnm_1_40_0;

View file

@ -217,6 +217,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMClient,
PROP_DNS_RC_MANAGER,
PROP_DNS_CONFIGURATION,
PROP_CHECKPOINTS,
PROP_VERSION_INFO,
PROP_CAPABILITIES,
PROP_PERMISSIONS_STATE, );
@ -305,7 +306,9 @@ typedef struct {
char *connectivity_check_uri;
char *version;
guint32 *capabilities_arr;
guint32 *version_info_arr;
gsize capabilities_len;
gsize version_info_len;
guint32 connectivity;
guint32 state;
guint32 metered;
@ -6300,6 +6303,57 @@ nm_client_get_capabilities(NMClient *client, gsize *length)
return priv->nm.capabilities_arr;
}
/**
* nm_client_get_version_info:
* @client: the #NMClient instance
* @length: (out): the number of returned capabilities.
*
* If available, the first element in the array is NM_VERSION which
* encodes the daemon version as "(major << 16 | minor << 8 | micro)".
* The following elements are a bitfield of %NMVersionInfoCapabilities
* that indicate that the daemon supports a certain capability.
*
* Returns: (transfer none) (array length=length): the
* list of capabilities reported by the server or %NULL
* if the capabilities are unknown.
*
* Since: 1.42
*/
const guint32 *
nm_client_get_version_info(NMClient *client, gsize *length)
{
NMClientPrivate *priv;
g_return_val_if_fail(NM_IS_CLIENT(client), NULL);
g_return_val_if_fail(length, NULL);
priv = NM_CLIENT_GET_PRIVATE(client);
*length = priv->nm.version_info_len;
return priv->nm.version_info_arr;
}
static NMLDBusNotifyUpdatePropFlags
_notify_update_prop_nm_au(guint32 **p_arr, gsize *p_len, GVariant *value)
{
nm_clear_g_free(p_arr);
*p_len = 0;
if (value) {
const guint32 *arr;
gsize len;
arr = g_variant_get_fixed_array(value, &len, sizeof(guint32));
*p_len = len;
*p_arr = g_new(guint32, len + 1);
if (len > 0)
memcpy(*p_arr, arr, len * sizeof(guint32));
(*p_arr)[len] = 0;
}
return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY;
}
static NMLDBusNotifyUpdatePropFlags
_notify_update_prop_nm_capabilities(NMClient *self,
NMLDBusObject *dbobj,
@ -6311,22 +6365,21 @@ _notify_update_prop_nm_capabilities(NMClient *self,
nm_assert(G_OBJECT(self) == dbobj->nmobj);
nm_clear_g_free(&priv->nm.capabilities_arr);
priv->nm.capabilities_len = 0;
return _notify_update_prop_nm_au(&priv->nm.capabilities_arr, &priv->nm.capabilities_len, value);
}
if (value) {
const guint32 *arr;
gsize len;
static NMLDBusNotifyUpdatePropFlags
_notify_update_prop_nm_version_info(NMClient *self,
NMLDBusObject *dbobj,
const NMLDBusMetaIface *meta_iface,
guint dbus_property_idx,
GVariant *value)
{
NMClientPrivate *priv = NM_CLIENT_GET_PRIVATE(self);
arr = g_variant_get_fixed_array(value, &len, sizeof(guint32));
priv->nm.capabilities_len = len;
priv->nm.capabilities_arr = g_new(guint32, len + 1);
if (len > 0)
memcpy(priv->nm.capabilities_arr, arr, len * sizeof(guint32));
priv->nm.capabilities_arr[len] = 0;
}
nm_assert(G_OBJECT(self) == dbobj->nmobj);
return NML_DBUS_NOTIFY_UPDATE_PROP_FLAGS_NOTIFY;
return _notify_update_prop_nm_au(&priv->nm.version_info_arr, &priv->nm.version_info_len, value);
}
/*****************************************************************************/
@ -7541,13 +7594,15 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
case PROP_CHECKPOINTS:
g_value_take_boxed(value, _nm_utils_copy_object_array(nm_client_get_checkpoints(self)));
break;
case PROP_VERSION_INFO:
case PROP_CAPABILITIES:
{
const guint32 *arr;
GArray *out;
gsize len;
arr = nm_client_get_capabilities(self, &len);
arr = (prop_id == PROP_VERSION_INFO) ? nm_client_get_version_info(self, &len)
: nm_client_get_capabilities(self, &len);
if (arr) {
out = g_array_new(TRUE, FALSE, sizeof(guint32));
g_array_append_vals(out, arr, len);
@ -8104,6 +8159,10 @@ const NMLDBusMetaIface _nml_dbus_meta_iface_nm = NML_DBUS_META_IFACE_INIT_PROP(
NML_DBUS_META_PROPERTY_INIT_B("Startup", PROP_STARTUP, NMClient, _priv.nm.startup),
NML_DBUS_META_PROPERTY_INIT_U("State", PROP_STATE, NMClient, _priv.nm.state),
NML_DBUS_META_PROPERTY_INIT_S("Version", PROP_VERSION, NMClient, _priv.nm.version),
NML_DBUS_META_PROPERTY_INIT_FCN("VersionInfo",
PROP_VERSION_INFO,
"au",
_notify_update_prop_nm_version_info, ),
NML_DBUS_META_PROPERTY_INIT_IGNORE("WimaxEnabled", "b"),
NML_DBUS_META_PROPERTY_INIT_IGNORE("WimaxHardwareEnabled", "b"),
NML_DBUS_META_PROPERTY_INIT_B("WirelessEnabled",
@ -8242,6 +8301,24 @@ nm_client_class_init(NMClientClass *client_class)
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* NMClient:version-info: (type GArray(guint32))
*
* Expose version info and capabilities of NetworkManager. If non-empty,
* the first element is NM_VERSION, which encodes the version of the
* daemon as "(major << 16 | minor << 8 | micro)". The following elements
* is a bitfields of %NMVersionInfoCapabilities. If a bit is set, then
* the running NetworkManager has the respective capability.
*
* Since: 1.42
*/
obj_properties[PROP_VERSION_INFO] =
g_param_spec_boxed(NM_CLIENT_VERSION_INFO,
"",
"",
G_TYPE_ARRAY,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* NMClient:state:
*

View file

@ -2499,7 +2499,7 @@ nm_device_reapply_finish(NMDevice *device, GAsyncResult *result, GError **error)
/**
* nm_device_get_applied_connection:
* @device: a #NMDevice
* @flags: the flags argument. Currently, this value must always be zero.
* @flags: the flags argument. See #NMDeviceReapplyFlags.
* @version_id: (out) (allow-none): returns the current version id of
* the applied connection
* @cancellable: a #GCancellable, or %NULL
@ -2562,7 +2562,7 @@ nm_device_get_applied_connection(NMDevice *device,
/**
* nm_device_get_applied_connection_async:
* @device: a #NMDevice
* @flags: the flags argument. Currently, this value must always be zero.
* @flags: the flags argument. See #NMDeviceReapplyFlags.
* @cancellable: a #GCancellable, or %NULL
* @callback: callback to be called when the reapply operation completes
* @user_data: caller-specific data passed to @callback

View file

@ -47,6 +47,7 @@ typedef enum /*< flags >*/ {
#define NM_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NM_TYPE_CLIENT, NMClientClass))
#define NM_CLIENT_VERSION "version"
#define NM_CLIENT_VERSION_INFO "version-info"
#define NM_CLIENT_STATE "state"
#define NM_CLIENT_STARTUP "startup"
#define NM_CLIENT_NM_RUNNING "nm-running"
@ -187,9 +188,13 @@ NM_AVAILABLE_IN_1_22
const char *nm_client_get_dbus_name_owner(NMClient *client);
const char *nm_client_get_version(NMClient *client);
NMState nm_client_get_state(NMClient *client);
gboolean nm_client_get_startup(NMClient *client);
gboolean nm_client_get_nm_running(NMClient *client);
NM_AVAILABLE_IN_1_42
const guint32 *nm_client_get_version_info(NMClient *client, gsize *length);
NMState nm_client_get_state(NMClient *client);
gboolean nm_client_get_startup(NMClient *client);
gboolean nm_client_get_nm_running(NMClient *client);
NMObject *nm_client_get_object_by_path(NMClient *client, const char *dbus_path);

View file

@ -90,6 +90,20 @@
#define NM_DBUS_INTERFACE_DNS_MANAGER "org.freedesktop.NetworkManager.DnsManager"
#define NM_DBUS_PATH_DNS_MANAGER "/org/freedesktop/NetworkManager/DnsManager"
/**
* NMVersionInfoCapability:
* %_NM_VERSION_INFO_CAPABILITY_UNUSED: a dummy capability. It has no meaning,
* don't use it.
*
* Currently no enum values are defined. These capabilities are exposed
* on D-Bus in the "VersionInfo" bit field.
*
* Since: 1.42
*/
typedef enum {
_NM_VERSION_INFO_CAPABILITY_UNUSED = 0x7FFFFFFFu,
} NMVersionInfoCapability;
/**
* NMCapability:
* @NM_CAPABILITY_TEAM: Teams can be managed. This means the team device plugin
@ -1147,6 +1161,22 @@ typedef enum /*< flags >*/ {
NM_SETTINGS_UPDATE2_FLAG_NO_REAPPLY = 0x40,
} NMSettingsUpdate2Flags;
/**
* NMDeviceReapplyFlags:
* @NM_DEVICE_REAPPLY_FLAGS_NONE: no flag set.
* @NM_DEVICE_REAPPLY_FLAGS_PRESERVE_EXTERNAL_IP: during reapply,
* preserve external IP addresses and routes.
*
* Flags for the Reapply() D-Bus call of a device and
* nm_device_reapply_async().
*
* Since: 1.42
*/
typedef enum /*< flags >*/ {
NM_DEVICE_REAPPLY_FLAGS_NONE = 0,
NM_DEVICE_REAPPLY_FLAGS_PRESERVE_EXTERNAL_IP = 0x1,
} NMDeviceReapplyFlags;
/**
* NMTernary:
* @NM_TERNARY_DEFAULT: use the globally-configured default value.

View file

@ -1989,6 +1989,20 @@ nm_g_array_unref(GArray *arr)
&g_array_index(arr, Type, _len); \
})
#define nm_g_array_append_simple(arr, val) \
G_STMT_START \
{ \
/* Similar to `g_array_append_val()`, but `g_array_append_val()`
* only works with lvalues. That makes sense if the value is a larger
* struct and you anyway have a pointer to it. It doesn't make sense
* if you have a list of int and want to append a number literal.
*
* nm_g_array_append_simple() is different. It depends on typeof(val)
* to be compatible. */ \
(*nm_g_array_append_new((arr), typeof(val))) = (val); \
} \
G_STMT_END
/*****************************************************************************/
static inline GPtrArray *

View file

@ -251,24 +251,38 @@ _get_config(GCancellable *sigterm_cancellable, NMCSProvider *provider, NMClient
/*****************************************************************************/
static gboolean
_nmc_skip_connection(NMConnection *connection)
_nmc_skip_connection_by_user_data(NMConnection *connection)
{
NMSettingUser *s_user;
const char *v;
s_user = NM_SETTING_USER(nm_connection_get_setting(connection, NM_TYPE_SETTING_USER));
if (!s_user)
return FALSE;
#define USER_TAG_SKIP "org.freedesktop.nm-cloud-setup.skip"
nm_assert(nm_setting_user_check_key(USER_TAG_SKIP, NULL));
v = nm_setting_user_get_data(s_user, USER_TAG_SKIP);
return _nm_utils_ascii_str_to_bool(v, FALSE);
s_user = NM_SETTING_USER(nm_connection_get_setting(connection, NM_TYPE_SETTING_USER));
if (s_user) {
v = nm_setting_user_get_data(s_user, USER_TAG_SKIP);
if (_nm_utils_ascii_str_to_bool(v, FALSE))
return TRUE;
}
return FALSE;
}
static gboolean
_nmc_skip_connection_by_type(NMConnection *connection)
{
if (!nm_streq0(nm_connection_get_connection_type(connection), NM_SETTING_WIRED_SETTING_NAME))
return TRUE;
if (!nm_connection_get_setting_ip4_config(connection))
return TRUE;
return FALSE;
}
static void
_nmc_mangle_connection(NMDevice *device,
NMConnection *connection,
const NMCSProviderGetConfigResult *result,
@ -291,12 +305,8 @@ _nmc_mangle_connection(NMDevice *device,
NM_SET_OUT(out_skipped_single_addr, FALSE);
NM_SET_OUT(out_changed, FALSE);
if (!nm_streq0(nm_connection_get_connection_type(connection), NM_SETTING_WIRED_SETTING_NAME))
return FALSE;
s_ip = nm_connection_get_setting_ip4_config(connection);
if (!s_ip)
return FALSE;
nm_assert(NM_IS_SETTING_IP4_CONFIG(s_ip));
if ((ac = nm_device_get_active_connection(device))
&& (remote_connection = NM_CONNECTION(nm_active_connection_get_connection(ac))))
@ -428,7 +438,6 @@ _nmc_mangle_connection(NMDevice *device,
rules_new->len);
NM_SET_OUT(out_changed, addrs_changed || routes_changed || rules_changed);
return TRUE;
}
/*****************************************************************************/
@ -450,6 +459,7 @@ _config_one(GCancellable *sigterm_cancellable,
gboolean version_id_changed;
guint try_count;
gboolean any_changes = FALSE;
gboolean maybe_no_preserved_external_ip;
g_main_context_iteration(NULL, FALSE);
@ -483,6 +493,8 @@ _config_one(GCancellable *sigterm_cancellable,
try_count = 0;
try_again:
g_clear_object(&applied_connection);
g_clear_error(&error);
applied_connection = nmcs_device_get_applied_connection(device,
sigterm_cancellable,
@ -496,23 +508,25 @@ try_again:
return any_changes;
}
if (_nmc_skip_connection(applied_connection)) {
if (_nmc_skip_connection_by_user_data(applied_connection)) {
_LOGD("config device %s: skip applied connection due to user data %s",
hwaddr,
USER_TAG_SKIP);
return any_changes;
}
if (!_nmc_mangle_connection(device,
applied_connection,
result,
config_data,
&skipped_single_addr,
&changed)) {
if (_nmc_skip_connection_by_type(applied_connection)) {
_LOGD("config device %s: device has no suitable applied connection. Skip", hwaddr);
return any_changes;
}
_nmc_mangle_connection(device,
applied_connection,
result,
config_data,
&skipped_single_addr,
&changed);
if (!changed) {
if (skipped_single_addr) {
_LOGD("config device %s: device needs no update to applied connection \"%s\" (%s) "
@ -538,16 +552,21 @@ try_again:
/* we are about to call Reapply(). Even if that fails, it counts as if we changed something. */
any_changes = TRUE;
/* "preserve-external-ip" flag was only introduced in 1.41.6 (but maybe backported!).
* If we run 1.41.6+, we are sure that it's gonna work. Otherwise, we take into account
* that the call might fail due to the invalid flag and we retry. */
maybe_no_preserved_external_ip =
(nmc_client_has_version_info_v(nmc) < NM_ENCODE_VERSION(1, 41, 6));
if (!nmcs_device_reapply(device,
sigterm_cancellable,
applied_connection,
applied_version_id,
maybe_no_preserved_external_ip,
&version_id_changed,
&error)) {
if (version_id_changed && try_count < 5) {
_LOGD("config device %s: applied connection changed in the meantime. Retry...", hwaddr);
g_clear_object(&applied_connection);
g_clear_error(&error);
try_count++;
goto try_again;
}

View file

@ -822,6 +822,7 @@ nmcs_device_reapply(NMDevice *device,
GCancellable *sigterm_cancellable,
NMConnection *connection,
guint64 version_id,
gboolean maybe_no_preserved_external_ip,
gboolean *out_version_id_changed,
GError **error)
{
@ -829,11 +830,13 @@ nmcs_device_reapply(NMDevice *device,
DeviceReapplyData data = {
.main_loop = main_loop,
};
NMDeviceReapplyFlags reapply_flags = NM_DEVICE_REAPPLY_FLAGS_PRESERVE_EXTERNAL_IP;
again:
nm_device_reapply_async(device,
connection,
version_id,
0,
reapply_flags,
sigterm_cancellable,
_nmcs_device_reapply_cb,
&data);
@ -841,6 +844,17 @@ nmcs_device_reapply(NMDevice *device,
g_main_loop_run(main_loop);
if (data.error) {
if (maybe_no_preserved_external_ip
&& reapply_flags == NM_DEVICE_REAPPLY_FLAGS_PRESERVE_EXTERNAL_IP
&& nm_g_error_matches(data.error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED)) {
/* Hm? Maybe we running against an older version of NetworkManager that
* doesn't support "preserve-external-ip" flags? Retry without the flag.
*
* Note that recent version would reject invalid flags with NM_DEVICE_ERROR_INVALID_ARGUMENT,
* but we want to detect old daemon versions here. */
reapply_flags = NM_DEVICE_REAPPLY_FLAGS_NONE;
goto again;
}
NM_SET_OUT(
out_version_id_changed,
g_error_matches(data.error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_VERSION_ID_MISMATCH));

View file

@ -136,6 +136,7 @@ gboolean nmcs_device_reapply(NMDevice *device,
GCancellable *sigterm_cancellable,
NMConnection *connection,
guint64 version_id,
gboolean maybe_no_preserved_external_ip,
gboolean *out_version_id_changed,
GError **error);