NetworkManager/libnm-core/nm-team-utils.c

2324 lines
90 KiB
C
Raw Normal View History

/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
* Copyright 2019 Red Hat, Inc.
*/
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
#define NM_VALUE_TYPE_DEFINE_FUNCTIONS
#include "nm-default.h"
#include "nm-team-utils.h"
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
#include "nm-errors.h"
#include "nm-utils-private.h"
#include "nm-json.h"
#include "nm-glib-aux/nm-json-aux.h"
#include "nm-core-internal.h"
#include "nm-setting-team.h"
#include "nm-setting-team-port.h"
/*****************************************************************************/
/* we rely on "config" being the first. At various places we iterate over attribute types,
* starting after "config".*/
G_STATIC_ASSERT (_NM_TEAM_ATTRIBUTE_0 == 0);
G_STATIC_ASSERT (NM_TEAM_ATTRIBUTE_CONFIG == 1);
typedef struct {
const char *const*js_keys;
const char *dbus_name;
NMValueTypUnion default_val;
NMTeamAttribute team_attr;
NMValueType value_type;
guint8 field_offset;
guint8 js_keys_len;
bool for_master:1;
bool for_port:1;
} TeamAttrData;
#define TEAM_ATTR_IDX(_is_port, _team_attr) \
(( (!(_is_port) || (_team_attr) < _NM_TEAM_ATTRIBUTE_START) \
? (int) (_team_attr) \
: (((int) (_NM_TEAM_ATTRIBUTE_MASTER_NUM - _NM_TEAM_ATTRIBUTE_START)) + ((int) (_team_attr)))) - 1)
#define TEAM_ATTR_IDX_CONFIG (TEAM_ATTR_IDX (FALSE, NM_TEAM_ATTRIBUTE_CONFIG))
static const TeamAttrData team_attr_datas[] = {
#define _JS_KEYS(...) \
.js_keys = NM_MAKE_STRV (__VA_ARGS__), \
.js_keys_len = NM_NARG (__VA_ARGS__)
#define _INIT(_is_port, _team_attr, field, _value_type, _dbus_name, ...) \
[TEAM_ATTR_IDX (_is_port, _team_attr)] = { \
.for_master = (_team_attr) < _NM_TEAM_ATTRIBUTE_START || !(_is_port), \
.for_port = (_team_attr) < _NM_TEAM_ATTRIBUTE_START || (_is_port), \
.team_attr = (_team_attr), \
.field_offset = G_STRUCT_OFFSET (NMTeamSetting, _data_priv.field), \
.value_type = (_value_type), \
.dbus_name = ""_dbus_name"", \
__VA_ARGS__ \
}
_INIT (0, NM_TEAM_ATTRIBUTE_CONFIG, _js_str, NM_VALUE_TYPE_UNSPEC, NM_SETTING_TEAM_CONFIG, ),
_INIT (0, NM_TEAM_ATTRIBUTE_LINK_WATCHERS, link_watchers, NM_VALUE_TYPE_UNSPEC, NM_SETTING_TEAM_LINK_WATCHERS, _JS_KEYS ("link_watch"), ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT, master.notify_peers_count, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_NOTIFY_PEERS_COUNT, _JS_KEYS ("notify_peers", "count"), ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL, master.notify_peers_interval, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL, _JS_KEYS ("notify_peers", "interval"), ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT, master.mcast_rejoin_count, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_MCAST_REJOIN_COUNT, _JS_KEYS ("mcast_rejoin", "count"), ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL, master.mcast_rejoin_interval, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL, _JS_KEYS ("mcast_rejoin", "interval"), ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER, master.runner, NM_VALUE_TYPE_STRING, NM_SETTING_TEAM_RUNNER, _JS_KEYS ("runner", "name"), .default_val.v_string = NM_SETTING_TEAM_RUNNER_DEFAULT, ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY, master.runner_hwaddr_policy, NM_VALUE_TYPE_STRING, NM_SETTING_TEAM_RUNNER_HWADDR_POLICY, _JS_KEYS ("runner", "hwaddr_policy"), ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH, master.runner_tx_hash, NM_VALUE_TYPE_UNSPEC, NM_SETTING_TEAM_RUNNER_TX_HASH, _JS_KEYS ("runner", "tx_hash"), ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER, master.runner_tx_balancer, NM_VALUE_TYPE_STRING, NM_SETTING_TEAM_RUNNER_TX_BALANCER, _JS_KEYS ("runner", "tx_balancer", "name"), ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL, master.runner_tx_balancer_interval, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL, _JS_KEYS ("runner", "tx_balancer", "balancing_interval"), .default_val.v_int32 = -1 ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE, master.runner_active, NM_VALUE_TYPE_BOOL, NM_SETTING_TEAM_RUNNER_ACTIVE, _JS_KEYS ("runner", "active"), ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE, master.runner_fast_rate, NM_VALUE_TYPE_BOOL, NM_SETTING_TEAM_RUNNER_FAST_RATE, _JS_KEYS ("runner", "fast_rate"), ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO, master.runner_sys_prio, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_RUNNER_SYS_PRIO, _JS_KEYS ("runner", "sys_prio"), .default_val.v_int32 = -1, ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS, master.runner_min_ports, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_RUNNER_MIN_PORTS, _JS_KEYS ("runner", "min_ports"), .default_val.v_int32 = -1, ),
_INIT (0, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY, master.runner_agg_select_policy, NM_VALUE_TYPE_STRING, NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY, _JS_KEYS ("runner", "agg_select_policy"), ),
_INIT (1, NM_TEAM_ATTRIBUTE_PORT_QUEUE_ID, port.queue_id, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_PORT_QUEUE_ID, _JS_KEYS ("queue_id"), .default_val.v_int32 = NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT, ),
_INIT (1, NM_TEAM_ATTRIBUTE_PORT_PRIO, port.prio, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_PORT_PRIO, _JS_KEYS ("prio"), ),
_INIT (1, NM_TEAM_ATTRIBUTE_PORT_STICKY, port.sticky, NM_VALUE_TYPE_BOOL, NM_SETTING_TEAM_PORT_STICKY, _JS_KEYS ("sticky"), ),
_INIT (1, NM_TEAM_ATTRIBUTE_PORT_LACP_PRIO, port.lacp_prio, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_PORT_LACP_PRIO, _JS_KEYS ("lacp_prio"), .default_val.v_int32 = NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT, ),
_INIT (1, NM_TEAM_ATTRIBUTE_PORT_LACP_KEY, port.lacp_key, NM_VALUE_TYPE_INT32, NM_SETTING_TEAM_PORT_LACP_KEY, _JS_KEYS ("lacp_key"), ),
#undef _INIT
};
/*****************************************************************************/
typedef enum {
LINK_WATCHER_ATTRIBUTE_NAME,
LINK_WATCHER_ATTRIBUTE_TARGET_HOST,
LINK_WATCHER_ATTRIBUTE_SOURCE_HOST,
LINK_WATCHER_ATTRIBUTE_DELAY_UP,
LINK_WATCHER_ATTRIBUTE_DELAY_DOWN,
LINK_WATCHER_ATTRIBUTE_INIT_WAIT,
LINK_WATCHER_ATTRIBUTE_INTERVAL,
LINK_WATCHER_ATTRIBUTE_MISSED_MAX,
LINK_WATCHER_ATTRIBUTE_VLANID,
LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE,
LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE,
LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS,
} LinkWatcherAttribute;
#define _EXPECTED_LINK_WATCHER_ATTRIBUTES_ETHTOOL LINK_WATCHER_ATTRIBUTE_NAME, \
LINK_WATCHER_ATTRIBUTE_DELAY_UP, \
LINK_WATCHER_ATTRIBUTE_DELAY_DOWN
#define _EXPECTED_LINK_WATCHER_ATTRIBUTES_NSNA_PING LINK_WATCHER_ATTRIBUTE_NAME, \
LINK_WATCHER_ATTRIBUTE_INIT_WAIT, \
LINK_WATCHER_ATTRIBUTE_INTERVAL, \
LINK_WATCHER_ATTRIBUTE_MISSED_MAX, \
LINK_WATCHER_ATTRIBUTE_TARGET_HOST
#define _EXPECTED_LINK_WATCHER_ATTRIBUTES_ARP_PING LINK_WATCHER_ATTRIBUTE_NAME, \
LINK_WATCHER_ATTRIBUTE_INIT_WAIT, \
LINK_WATCHER_ATTRIBUTE_INTERVAL, \
LINK_WATCHER_ATTRIBUTE_MISSED_MAX, \
LINK_WATCHER_ATTRIBUTE_VLANID, \
LINK_WATCHER_ATTRIBUTE_TARGET_HOST, \
LINK_WATCHER_ATTRIBUTE_SOURCE_HOST, \
LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE, \
LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE, \
LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
typedef struct {
const char *js_key;
const char *dbus_name;
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
NMValueTypUnion default_val;
LinkWatcherAttribute link_watcher_attr;
NMValueType value_type;
} LinkWatcherAttrData;
static const LinkWatcherAttrData link_watcher_attr_datas[] = {
#define _INIT(_link_watcher_attr, _js_key, _dbus_name, _value_type, ...) \
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
[_link_watcher_attr] = { \
.link_watcher_attr = (_link_watcher_attr), \
.value_type = (_value_type), \
.js_key = (""_js_key""), \
.dbus_name = (""_dbus_name""), \
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
__VA_ARGS__ \
}
_INIT (LINK_WATCHER_ATTRIBUTE_NAME, "name", "name", NM_VALUE_TYPE_STRING, ),
_INIT (LINK_WATCHER_ATTRIBUTE_TARGET_HOST, "target_host", "target-host", NM_VALUE_TYPE_STRING, ),
_INIT (LINK_WATCHER_ATTRIBUTE_SOURCE_HOST, "source_host", "source-host", NM_VALUE_TYPE_STRING, ),
_INIT (LINK_WATCHER_ATTRIBUTE_DELAY_UP, "delay_up", "delay-up", NM_VALUE_TYPE_INT, ),
_INIT (LINK_WATCHER_ATTRIBUTE_DELAY_DOWN, "delay_down", "delay-down", NM_VALUE_TYPE_INT, ),
_INIT (LINK_WATCHER_ATTRIBUTE_INIT_WAIT, "init_wait", "init-wait", NM_VALUE_TYPE_INT, ),
_INIT (LINK_WATCHER_ATTRIBUTE_INTERVAL, "interval", "interval", NM_VALUE_TYPE_INT, ),
_INIT (LINK_WATCHER_ATTRIBUTE_MISSED_MAX, "missed_max", "missed-max", NM_VALUE_TYPE_INT, .default_val.v_int = 3, ),
_INIT (LINK_WATCHER_ATTRIBUTE_VLANID, "vlanid", "vlanid", NM_VALUE_TYPE_INT, .default_val.v_int = -1, ),
_INIT (LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE, "validate_active", "validate-active", NM_VALUE_TYPE_BOOL, ),
_INIT (LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE, "validate_inactive", "validate-inactive", NM_VALUE_TYPE_BOOL, ),
_INIT (LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS, "send_always", "send-always", NM_VALUE_TYPE_BOOL, ),
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
#undef _INIT
};
/*****************************************************************************/
static const TeamAttrData *_team_attr_data_get (gboolean is_port,
NMTeamAttribute team_attr);
static gpointer _team_setting_get_field (const NMTeamSetting *self,
const TeamAttrData *attr_data);
static gboolean _team_setting_verify (const NMTeamSetting *self,
GError **error);
static void _link_watcher_to_json (const NMTeamLinkWatcher *link_watcher,
GString *gstr);
/*****************************************************************************/
static void
_team_attr_data_ASSERT (const TeamAttrData *attr_data)
{
#if NM_MORE_ASSERTS > 5
nm_assert (attr_data);
if (attr_data->for_port)
nm_assert (attr_data == _team_attr_data_get (TRUE, attr_data->team_attr));
if (attr_data->for_master)
nm_assert (attr_data == _team_attr_data_get (FALSE, attr_data->team_attr));
nm_assert ((attr_data - team_attr_datas) == TEAM_ATTR_IDX (attr_data->for_port, attr_data->team_attr));
nm_assert (attr_data->value_type > 0);
nm_assert (attr_data->field_offset < sizeof (NMTeamSetting));
nm_assert (attr_data->js_keys_len == NM_PTRARRAY_LEN (attr_data->js_keys));
nm_assert (attr_data->dbus_name);
{
static int checked = 0;
if (checked == 0) {
checked = 1;
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++)
_team_attr_data_ASSERT (attr_data);
}
}
#endif
}
static gboolean
_team_attr_data_is_relevant (const TeamAttrData *attr_data,
gboolean is_port)
{
return is_port
? attr_data->for_port
: attr_data->for_master;
}
static const TeamAttrData *
_team_attr_data_get (gboolean is_port,
NMTeamAttribute team_attr)
{
const int idx = TEAM_ATTR_IDX (is_port, team_attr);
nm_assert ( idx >= 0
&& idx < G_N_ELEMENTS (team_attr_datas));
nm_assert (team_attr_datas[idx].team_attr == team_attr);
nm_assert (_team_attr_data_is_relevant (&team_attr_datas[idx], is_port));
return &team_attr_datas[idx];
}
static const TeamAttrData *
_team_attr_data_find_for_dbus_name (gboolean is_port,
const char *dbus_name)
{
const TeamAttrData *attr_data;
for (attr_data = team_attr_datas; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) {
if ( _team_attr_data_is_relevant (attr_data, is_port)
&& nm_streq (dbus_name, attr_data->dbus_name))
return attr_data;
}
return NULL;
}
static const NMValueTypUnion *
_team_attr_data_get_default (const TeamAttrData *attr_data,
gboolean is_port,
const char *v_master_runner,
NMValueTypUnion *value_tmp)
{
GPtrArray *v_ptrarray;
/* unfortunately, the default certain values depends on other values :(
*
* For examle, master attributes depend on the "runner" setting.
* and port settings default to the ethtool link-watcher. */
if (is_port) {
switch (attr_data->team_attr) {
case NM_TEAM_ATTRIBUTE_LINK_WATCHERS: {
static GPtrArray *volatile gl_arr = NULL;
again_port_link_watchers:
v_ptrarray = g_atomic_pointer_get (&gl_arr);
if (G_UNLIKELY (!v_ptrarray)) {
v_ptrarray = g_ptr_array_new_full (1, (GDestroyNotify) nm_team_link_watcher_unref);
g_ptr_array_add (v_ptrarray, nm_team_link_watcher_new_ethtool (0, 0, NULL));
if (!g_atomic_pointer_compare_and_exchange (&gl_arr, NULL, v_ptrarray)) {
g_ptr_array_unref (v_ptrarray);
goto again_port_link_watchers;
}
}
return NM_VALUE_TYP_UNION_SET (value_tmp, v_ptrarray, v_ptrarray);
}
default:
break;
}
} else {
if (NM_IN_STRSET (v_master_runner, NULL,
NM_SETTING_TEAM_RUNNER_DEFAULT)) {
/* a runner %NULL is the same as NM_SETTING_TEAM_RUNNER_DEFAULT ("roundrobin").
* In this case, the settings in attr_data are accurate. */
return &attr_data->default_val;
}
switch (attr_data->team_attr) {
case NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT:
if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP))
return NM_VALUE_TYP_UNION_SET (value_tmp, v_int32, NM_SETTING_TEAM_NOTIFY_PEERS_COUNT_ACTIVEBACKUP_DEFAULT);
break;
case NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT:
if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP))
return NM_VALUE_TYP_UNION_SET (value_tmp, v_int32, NM_SETTING_TEAM_NOTIFY_MCAST_COUNT_ACTIVEBACKUP_DEFAULT);
break;
case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY:
if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP))
return NM_VALUE_TYP_UNION_SET (value_tmp, v_string, "same_all");
break;
case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH:
if (NM_IN_STRSET (v_master_runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE,
NM_SETTING_TEAM_RUNNER_LACP)) {
static GPtrArray *volatile gl_arr = NULL;
again_master_runner_tx_hash:
v_ptrarray = g_atomic_pointer_get (&gl_arr);
if (G_UNLIKELY (!v_ptrarray)) {
v_ptrarray = g_ptr_array_sized_new (3);
g_ptr_array_add (v_ptrarray, "eth");
g_ptr_array_add (v_ptrarray, "ipv4");
g_ptr_array_add (v_ptrarray, "ipv6");
if (!g_atomic_pointer_compare_and_exchange (&gl_arr, NULL, v_ptrarray)) {
g_ptr_array_unref (v_ptrarray);
goto again_master_runner_tx_hash;
}
}
return NM_VALUE_TYP_UNION_SET (value_tmp, v_ptrarray, v_ptrarray);
}
break;
case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL:
if (NM_IN_STRSET (v_master_runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE,
NM_SETTING_TEAM_RUNNER_LACP))
return NM_VALUE_TYP_UNION_SET (value_tmp, v_int32, NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT);
break;
case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE:
if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_LACP))
return NM_VALUE_TYP_UNION_SET (value_tmp, v_bool, TRUE);
break;
case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO:
if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_LACP))
return NM_VALUE_TYP_UNION_SET (value_tmp, v_int32, NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT);
break;
case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS:
if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_LACP))
return NM_VALUE_TYP_UNION_SET (value_tmp, v_int32, 0);
break;
case NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY:
if (nm_streq (v_master_runner, NM_SETTING_TEAM_RUNNER_LACP))
return NM_VALUE_TYP_UNION_SET (value_tmp, v_string, NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT);
break;
default:
break;
}
}
return &attr_data->default_val;
}
static int
_team_attr_data_cmp (const TeamAttrData *attr_data,
gboolean is_port,
gconstpointer val_a,
gconstpointer val_b)
{
const GPtrArray *v_ptrarray_a;
const GPtrArray *v_ptrarray_b;
guint len;
_team_attr_data_ASSERT (attr_data);
nm_assert (val_a);
nm_assert (val_b);
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC)
NM_CMP_RETURN (nm_value_type_cmp (attr_data->value_type, val_a, val_b));
else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) {
v_ptrarray_a = *((const GPtrArray *const*) val_a);
v_ptrarray_b = *((const GPtrArray *const*) val_b);
len = v_ptrarray_a ? v_ptrarray_a->len : 0u;
NM_CMP_DIRECT (len, (v_ptrarray_b ? v_ptrarray_b->len : 0u));
if (len > 0) {
NM_CMP_RETURN (nm_team_link_watchers_cmp ((const NMTeamLinkWatcher *const*) v_ptrarray_a->pdata,
(const NMTeamLinkWatcher *const*) v_ptrarray_b->pdata,
len,
FALSE));
}
} else if ( !is_port
&& attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
v_ptrarray_a = *((const GPtrArray *const*) val_a);
v_ptrarray_b = *((const GPtrArray *const*) val_b);
NM_CMP_RETURN (_nm_utils_strv_cmp_n (v_ptrarray_a ? (const char *const*) v_ptrarray_a->pdata : NULL,
v_ptrarray_a ? v_ptrarray_a->len : 0u,
v_ptrarray_b ? (const char *const*) v_ptrarray_b->pdata : NULL,
v_ptrarray_b ? v_ptrarray_b->len : 0u));
} else
nm_assert_not_reached ();
return 0;
}
static gboolean
_team_attr_data_equal (const TeamAttrData *attr_data,
gboolean is_port,
gconstpointer val_a,
gconstpointer val_b)
{
return _team_attr_data_cmp (attr_data, is_port, val_a, val_b) == 0;
}
static void
_team_attr_data_copy (const TeamAttrData *attr_data,
gboolean is_port,
gpointer dst,
gconstpointer src)
{
GPtrArray *v_ptrarray_dst;
const GPtrArray *v_ptrarray_src;
GPtrArray *dst_array;
guint i, len;
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC)
nm_value_type_copy (attr_data->value_type, dst, src);
else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) {
v_ptrarray_src = *((const GPtrArray *const *) src);
v_ptrarray_dst = *((GPtrArray **) dst);
len = (v_ptrarray_src ? v_ptrarray_src->len : 0u);
if (len == 0) {
if (v_ptrarray_dst)
g_ptr_array_set_size (v_ptrarray_dst, 0);
} else {
dst_array = g_ptr_array_new_full (len, (GDestroyNotify) nm_team_link_watcher_unref);
for (i = 0; i < len; i++) {
if (v_ptrarray_src->pdata[i]) {
nm_team_link_watcher_ref (v_ptrarray_src->pdata[i]);
g_ptr_array_add (dst_array,v_ptrarray_src->pdata[i]);
}
}
if (v_ptrarray_dst)
g_ptr_array_unref (v_ptrarray_dst);
*((GPtrArray **) dst) = dst_array;
}
} else if ( !is_port
&& attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
v_ptrarray_src = *((const GPtrArray *const *) src);
v_ptrarray_dst = *((GPtrArray **) dst);
len = (v_ptrarray_src ? v_ptrarray_src->len : 0u);
if ( v_ptrarray_src
&& v_ptrarray_src->len > 0) {
dst_array = g_ptr_array_new_full (v_ptrarray_src->len, g_free);
for (i = 0; i < v_ptrarray_src->len; i++)
g_ptr_array_add (dst_array, g_strdup (v_ptrarray_src->pdata[i]));
} else
dst_array = NULL;
if (v_ptrarray_dst)
g_ptr_array_unref (v_ptrarray_dst);
*((GPtrArray **) dst) = dst_array;
} else
nm_assert_not_reached ();
}
static gboolean
_team_attr_data_is_default (const TeamAttrData *attr_data,
gboolean is_port,
const char *v_master_runner,
gconstpointer p_field)
{
const NMValueTypUnion *default_value;
NMValueTypUnion value_tmp;
_team_attr_data_ASSERT (attr_data);
nm_assert (p_field);
default_value = _team_attr_data_get_default (attr_data,
is_port,
v_master_runner,
&value_tmp);
if (_team_attr_data_equal (attr_data,
is_port,
default_value,
p_field))
return TRUE;
if ( attr_data->value_type == NM_VALUE_TYPE_STRING
&& default_value->v_string) {
const char *str0 = NULL;
/* this is a string value, whose default is not NULL. In such a case,
* NULL is also treated like the default. */
if (_team_attr_data_equal (attr_data,
is_port,
&str0,
p_field))
return TRUE;
}
return FALSE;
}
static void
_team_attr_data_to_json (const TeamAttrData *attr_data,
gboolean is_port,
GString *gstr,
gconstpointer p_field)
{
guint i;
_team_attr_data_ASSERT (attr_data);
nm_assert (p_field);
nm_json_aux_gstr_append_obj_name (gstr,
attr_data->js_keys[attr_data->js_keys_len - 1],
'\0');
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) {
nm_value_type_to_json (attr_data->value_type, gstr, p_field);
return;
}
if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) {
const GPtrArray *v_ptrarray = *((const GPtrArray *const*) p_field);
if (!v_ptrarray)
g_string_append (gstr, "null");
else if (v_ptrarray->len == 0)
g_string_append (gstr, "[ ]");
else if (v_ptrarray->len == 1)
_link_watcher_to_json (v_ptrarray->pdata[0], gstr);
else {
g_string_append (gstr, "[ ");
for (i = 0; i < v_ptrarray->len; i++) {
if (i > 0)
nm_json_aux_gstr_append_delimiter (gstr);
_link_watcher_to_json (v_ptrarray->pdata[i], gstr);
}
g_string_append (gstr, " ]");
}
return;
}
if ( !is_port
&& attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
const GPtrArray *v_ptrarray = *((const GPtrArray *const*) p_field);
if (!v_ptrarray)
g_string_append (gstr, "null");
else {
g_string_append (gstr, "[ ");
for (i = 0; i < v_ptrarray->len; i++) {
if (i > 0)
nm_json_aux_gstr_append_delimiter (gstr);
nm_json_aux_gstr_append_string (gstr, v_ptrarray->pdata[i]);
}
g_string_append (gstr, i > 0 ? " ]" : "]");
}
return;
}
nm_assert_not_reached ();
}
/*****************************************************************************/
static void
_team_setting_ASSERT (const NMTeamSetting *self)
{
nm_assert (self);
nm_assert (!self->d._js_str_need_synthetize || !self->d._js_str);
#if NM_MORE_ASSERTS > 2
if (!self->d.strict_validated) {
nm_assert (!self->d._js_str_need_synthetize);
nm_assert (self->d._js_str);
}
nm_assert (self->d.link_watchers);
nm_assert ( self->d.is_port
|| !self->d.master.runner_tx_hash
|| self->d.master.runner_tx_hash->len > 0);
#endif
}
static gpointer
_team_setting_get_field (const NMTeamSetting *self,
const TeamAttrData *attr_data)
{
_team_setting_ASSERT (self);
_team_attr_data_ASSERT (attr_data);
nm_assert (_team_attr_data_is_relevant (attr_data, self->d.is_port));
#if NM_MORE_ASSERTS > 5
if ( attr_data->for_master
&& attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO )
nm_assert ((gpointer) (((char *) self) + attr_data->field_offset) == &self->d.master.runner_sys_prio);
#endif
return (((char *) self) + attr_data->field_offset);
}
static guint32
_team_setting_attribute_changed (NMTeamSetting *self,
NMTeamAttribute team_attr,
gboolean changed)
{
guint32 changed_flags;
nm_assert (_team_attr_data_get (self->d.is_port, team_attr));
if (!changed) {
/* a regular attribute was set, but the value did not change.
*
* If we previously were in non-strict mode, then
*
* - switch to strict-mode. Clearly the user set a regular attribute
* and hence now we want to validate the setting.
*
* - clear the JSON string. We need to regenerate it.
*/
if (self->_data_priv.strict_validated)
return 0;
changed_flags = nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG);
} else {
changed_flags = nm_team_attribute_to_flags (team_attr)
| nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG);
}
nm_clear_g_free ((char **) &self->_data_priv._js_str);
self->_data_priv.strict_validated = TRUE;
self->_data_priv._js_str_need_synthetize = TRUE;
return changed_flags;
}
static void
_team_setting_field_to_json (const NMTeamSetting *self,
GString *gstr,
gboolean prepend_delimiter,
NMTeamAttribute team_attr)
{
const TeamAttrData *attr_data = _team_attr_data_get (self->d.is_port, team_attr);
if (prepend_delimiter)
nm_json_aux_gstr_append_delimiter (gstr);
_team_attr_data_to_json (attr_data,
self->d.is_port,
gstr,
_team_setting_get_field (self, attr_data));
}
static gboolean
_team_setting_fields_to_json_maybe (const NMTeamSetting *self,
GString *gstr,
gboolean prepend_delimiter,
const bool is_default_lst[static _NM_TEAM_ATTRIBUTE_NUM],
const NMTeamAttribute *team_attrs_lst,
gsize team_attrs_lst_len)
{
gsize i;
gboolean any_added = FALSE;
for (i = 0; i < team_attrs_lst_len; i++) {
NMTeamAttribute team_attr = team_attrs_lst[i];
if (is_default_lst[team_attr])
continue;
_team_setting_field_to_json (self, gstr, prepend_delimiter, team_attr);
any_added = TRUE;
prepend_delimiter = TRUE;
}
return any_added;
}
static guint32
_team_setting_set (NMTeamSetting *self,
gboolean modify,
const bool *has_lst,
const NMValueTypUnion *val_lst)
{
guint32 changed_flags = 0;
const TeamAttrData *attr_data;
const char *v_master_runner;
nm_assert ((!has_lst) == (!val_lst));
if (!self->d.is_port) {
if ( has_lst
&& has_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER])
v_master_runner = val_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER].v_string;
else {
nm_assert (nm_streq0 (_team_attr_data_get (FALSE, NM_TEAM_ATTRIBUTE_MASTER_RUNNER)->default_val.v_string,
NM_SETTING_TEAM_RUNNER_DEFAULT));
v_master_runner = NM_SETTING_TEAM_RUNNER_DEFAULT;
}
} else
v_master_runner = NULL;
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) {
NMValueTypUnion value_tmp;
const NMValueTypUnion *p_val;
gconstpointer p_field;
if (!_team_attr_data_is_relevant (attr_data, self->d.is_port))
continue;
if ( has_lst
&& has_lst[attr_data->team_attr])
p_val = &val_lst[attr_data->team_attr];
else {
p_val = _team_attr_data_get_default (attr_data,
self->d.is_port,
v_master_runner,
&value_tmp);
}
p_field = _team_setting_get_field (self, attr_data);
if (!_team_attr_data_equal (attr_data,
self->d.is_port,
p_val,
p_field)) {
if (modify) {
_team_attr_data_copy (attr_data,
self->d.is_port,
(gpointer) p_field,
p_val);
}
changed_flags |= nm_team_attribute_to_flags (attr_data->team_attr);
}
}
return changed_flags;
}
static guint32
_team_setting_check_default (const NMTeamSetting *self)
{
return _team_setting_set ((NMTeamSetting *) self, FALSE, NULL, NULL);
}
static guint32
_team_setting_set_default (NMTeamSetting *self)
{
return _team_setting_set (self, TRUE, NULL, NULL);
}
/*****************************************************************************/
gconstpointer
_nm_team_setting_value_get (const NMTeamSetting *self,
NMTeamAttribute team_attr,
NMValueType value_type)
{
const TeamAttrData *attr_data = _team_attr_data_get (self->d.is_port, team_attr);
nm_assert (value_type == attr_data->value_type);
return _team_setting_get_field (self, attr_data);
}
static guint32
_team_setting_value_set (NMTeamSetting *self,
NMTeamAttribute team_attr,
NMValueType value_type,
gconstpointer val)
{
const TeamAttrData *attr_data;
gpointer p_field;
nm_assert (self);
attr_data = _team_attr_data_get (self->d.is_port, team_attr);
nm_assert (val);
nm_assert (value_type == attr_data->value_type);
p_field = _team_setting_get_field (self, attr_data);
if (nm_value_type_equal (attr_data->value_type, p_field, val))
return 0u;
nm_value_type_copy (attr_data->value_type, p_field, val);
return nm_team_attribute_to_flags (team_attr);
}
guint32
_nm_team_setting_value_set (NMTeamSetting *self,
NMTeamAttribute team_attr,
NMValueType value_type,
gconstpointer val)
{
return _team_setting_attribute_changed (self,
team_attr,
(_team_setting_value_set (self,
team_attr,
value_type,
val) != 0u));
}
guint32
nm_team_setting_value_link_watchers_add (NMTeamSetting *self,
const NMTeamLinkWatcher *link_watcher)
{
guint i;
for (i = 0; i < self->d.link_watchers->len; i++) {
if (nm_team_link_watcher_equal (self->d.link_watchers->pdata[i], link_watcher))
return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_LINK_WATCHERS, FALSE);
}
g_ptr_array_add ((GPtrArray *) self->d.link_watchers,
_nm_team_link_watcher_ref ((NMTeamLinkWatcher *) link_watcher));
return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_LINK_WATCHERS, TRUE);
}
guint32
nm_team_setting_value_link_watchers_remove_by_value (NMTeamSetting *self,
const NMTeamLinkWatcher *link_watcher)
{
guint i;
for (i = 0; i < self->d.link_watchers->len; i++) {
if (nm_team_link_watcher_equal (self->d.link_watchers->pdata[i],
link_watcher))
return nm_team_setting_value_link_watchers_remove (self, i);
}
return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_LINK_WATCHERS, FALSE);
}
guint32
nm_team_setting_value_link_watchers_remove (NMTeamSetting *self,
guint idx)
{
g_ptr_array_remove_index ((GPtrArray *) self->d.link_watchers, idx);
return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_LINK_WATCHERS, TRUE);
}
static guint32
_team_setting_value_link_watchers_set_list (NMTeamSetting *self,
const NMTeamLinkWatcher *const*arr,
guint len)
{
if ( self->d.link_watchers->len == len
&& nm_team_link_watchers_cmp ((const NMTeamLinkWatcher *const*) self->d.link_watchers->pdata,
arr,
len,
FALSE) == 0)
return 0;
if (len == 0)
g_ptr_array_set_size ((GPtrArray *) self->d.link_watchers, 0);
else {
_nm_unused gs_unref_ptrarray GPtrArray *old_val_destroy = NULL;
guint i;
old_val_destroy = (GPtrArray *) g_steal_pointer (&self->_data_priv.link_watchers);
self->_data_priv.link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
for (i = 0; i < len; i++) {
if (arr[i]) {
g_ptr_array_add ((GPtrArray *) self->d.link_watchers,
_nm_team_link_watcher_ref ((NMTeamLinkWatcher *) arr[i]));
}
}
}
return nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_LINK_WATCHERS);
}
guint32
nm_team_setting_value_link_watchers_set_list (NMTeamSetting *self,
const NMTeamLinkWatcher *const*arr,
guint len)
{
return _team_setting_attribute_changed (self,
NM_TEAM_ATTRIBUTE_LINK_WATCHERS,
(_team_setting_value_link_watchers_set_list (self,
arr,
len) != 0u));
}
/*****************************************************************************/
guint32
nm_team_setting_value_master_runner_tx_hash_add (NMTeamSetting *self,
const char *txhash)
{
guint i;
if (!self->d.master.runner_tx_hash)
self->_data_priv.master.runner_tx_hash = g_ptr_array_new_with_free_func (g_free);
else {
for (i = 0; i < self->d.master.runner_tx_hash->len; i++) {
if (nm_streq (txhash, self->d.master.runner_tx_hash->pdata[i]))
return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH, FALSE);
}
}
g_ptr_array_add ((GPtrArray *) self->d.master.runner_tx_hash, g_strdup (txhash));
return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH, TRUE);
}
guint32
nm_team_setting_value_master_runner_tx_hash_remove (NMTeamSetting *self,
guint idx)
{
g_ptr_array_remove_index ((GPtrArray *) self->d.master.runner_tx_hash, idx);
return _team_setting_attribute_changed (self, NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH, TRUE);
}
static guint32
_team_setting_value_master_runner_tx_hash_set_list (NMTeamSetting *self,
const char *const*arr,
guint len)
{
_nm_unused gs_unref_ptrarray GPtrArray *old_val_destroy = NULL;
guint i;
if (_nm_utils_strv_cmp_n (self->d.master.runner_tx_hash ? (const char *const*) self->d.master.runner_tx_hash->pdata : NULL,
self->d.master.runner_tx_hash ? self->d.master.runner_tx_hash->len : 0u,
arr,
len) == 0)
return 0u;
old_val_destroy = (GPtrArray *) g_steal_pointer (&self->_data_priv.master.runner_tx_hash);
for (i = 0; i < len; i++) {
if (!arr[i])
continue;
if (!self->d.master.runner_tx_hash)
self->_data_priv.master.runner_tx_hash = g_ptr_array_new_with_free_func (g_free);
g_ptr_array_add ((GPtrArray *) self->d.master.runner_tx_hash, g_strdup (arr[i]));
}
return nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH);
}
guint32
nm_team_setting_value_master_runner_tx_hash_set_list (NMTeamSetting *self,
const char *const*arr,
guint len)
{
return _team_setting_attribute_changed (self,
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH,
(_team_setting_value_master_runner_tx_hash_set_list (self,
arr,
len) != 0u));
}
/*****************************************************************************/
#define _LINK_WATCHER_ATTR_GET(args, link_watcher_attribute, _value_type) \
({ \
const NMValueTypUnioMaybe *const _args = (args); \
\
nm_assert (link_watcher_attr_datas[(link_watcher_attribute)].value_type == (_value_type)); \
\
_args[(link_watcher_attribute)].has \
? &_args[(link_watcher_attribute)].val \
: &link_watcher_attr_datas[(link_watcher_attribute)].default_val; \
})
#define _LINK_WATCHER_ATTR_GET_BOOL(args, link_watcher_attribute) (_LINK_WATCHER_ATTR_GET (args, link_watcher_attribute, NM_VALUE_TYPE_BOOL )->v_bool)
#define _LINK_WATCHER_ATTR_GET_INT(args, link_watcher_attribute) (_LINK_WATCHER_ATTR_GET (args, link_watcher_attribute, NM_VALUE_TYPE_INT )->v_int)
#define _LINK_WATCHER_ATTR_GET_STRING(args, link_watcher_attribute) (_LINK_WATCHER_ATTR_GET (args, link_watcher_attribute, NM_VALUE_TYPE_STRING )->v_string)
#define _LINK_WATCHER_ATTR_SET(args, link_watcher_attribute, _value_type, c_type, val) \
({ \
nm_assert (link_watcher_attr_datas[(link_watcher_attribute)].value_type == (_value_type)); \
\
NM_VALUE_TYP_UNIO_MAYBE_SET (&(args)[(link_watcher_attribute)], c_type, (val)); \
})
#define _LINK_WATCHER_ATTR_SET_BOOL(args, link_watcher_attribute, val) _LINK_WATCHER_ATTR_SET((args), (link_watcher_attribute), NM_VALUE_TYPE_BOOL, v_bool, (val))
#define _LINK_WATCHER_ATTR_SET_INT(args, link_watcher_attribute, val) _LINK_WATCHER_ATTR_SET((args), (link_watcher_attribute), NM_VALUE_TYPE_INT, v_int, (val))
#define _LINK_WATCHER_ATTR_SET_STRING(args, link_watcher_attribute, val) _LINK_WATCHER_ATTR_SET((args), (link_watcher_attribute), NM_VALUE_TYPE_STRING, v_string, (val))
static void
_link_watcher_unpack (const NMTeamLinkWatcher *link_watcher,
NMValueTypUnioMaybe args[static G_N_ELEMENTS (link_watcher_attr_datas)])
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
{
const char *v_name = nm_team_link_watcher_get_name (link_watcher);
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
NMTeamLinkWatcherArpPingFlags v_arp_ping_flags;
memset (args, 0, sizeof (args[0]) * G_N_ELEMENTS (link_watcher_attr_datas));
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
_LINK_WATCHER_ATTR_SET_STRING (args, LINK_WATCHER_ATTRIBUTE_NAME, v_name);
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
if (nm_streq (v_name, NM_TEAM_LINK_WATCHER_ETHTOOL)) {
_LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_DELAY_UP, nm_team_link_watcher_get_delay_up (link_watcher));
_LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_DELAY_DOWN, nm_team_link_watcher_get_delay_down (link_watcher));
} else if (NM_IN_STRSET (v_name, NM_TEAM_LINK_WATCHER_NSNA_PING,
NM_TEAM_LINK_WATCHER_ARP_PING)) {
_LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_INIT_WAIT, nm_team_link_watcher_get_init_wait (link_watcher));
_LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_INTERVAL, nm_team_link_watcher_get_interval (link_watcher));
_LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_MISSED_MAX, nm_team_link_watcher_get_missed_max (link_watcher));
_LINK_WATCHER_ATTR_SET_STRING (args, LINK_WATCHER_ATTRIBUTE_TARGET_HOST, nm_team_link_watcher_get_target_host (link_watcher));
if (nm_streq (v_name, NM_TEAM_LINK_WATCHER_ARP_PING)) {
v_arp_ping_flags = nm_team_link_watcher_get_flags (link_watcher);
_LINK_WATCHER_ATTR_SET_INT (args, LINK_WATCHER_ATTRIBUTE_VLANID, nm_team_link_watcher_get_vlanid (link_watcher));
_LINK_WATCHER_ATTR_SET_STRING (args, LINK_WATCHER_ATTRIBUTE_SOURCE_HOST, nm_team_link_watcher_get_source_host (link_watcher));
_LINK_WATCHER_ATTR_SET_BOOL (args, LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE, NM_FLAGS_HAS (v_arp_ping_flags, NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE));
_LINK_WATCHER_ATTR_SET_BOOL (args, LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE, NM_FLAGS_HAS (v_arp_ping_flags, NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE));
_LINK_WATCHER_ATTR_SET_BOOL (args, LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS, NM_FLAGS_HAS (v_arp_ping_flags, NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS));
}
}
}
static void
_link_watcher_to_json (const NMTeamLinkWatcher *link_watcher,
GString *gstr)
{
NMValueTypUnioMaybe args[G_N_ELEMENTS (link_watcher_attr_datas)];
int i;
gboolean is_first = TRUE;
if (!link_watcher) {
g_string_append (gstr, "null");
return;
}
_link_watcher_unpack (link_watcher, args);
g_string_append (gstr, "{ ");
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
for (i = 0; i < (int) G_N_ELEMENTS (link_watcher_attr_datas); i++) {
const NMValueTypUnioMaybe *p_val = &args[i];
const LinkWatcherAttrData *attr_data = &link_watcher_attr_datas[i];
if (!p_val->has)
continue;
if (nm_value_type_equal (attr_data->value_type, &attr_data->default_val, &p_val->val))
continue;
if (is_first)
is_first = FALSE;
else
nm_json_aux_gstr_append_delimiter (gstr);
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
nm_json_aux_gstr_append_obj_name (gstr, attr_data->js_key, '\0');
nm_value_type_to_json (attr_data->value_type, gstr, &p_val->val);
}
g_string_append (gstr, " }");
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
}
#if WITH_JSON_VALIDATION
static NMTeamLinkWatcher *
_link_watcher_from_json (const json_t *root_js_obj,
gboolean *out_unrecognized_content)
{
NMValueTypUnioMaybe args[G_N_ELEMENTS (link_watcher_attr_datas)] = { };
const char *j_key;
json_t *j_val;
const char *v_name;
NMTeamLinkWatcher *result = NULL;
if (!json_is_object (root_js_obj))
goto fail;
json_object_foreach ((json_t *) root_js_obj, j_key, j_val) {
const LinkWatcherAttrData *attr_data = NULL;
NMValueTypUnioMaybe *parse_result;
if (j_key) {
int i;
for (i = 0; i < (int) G_N_ELEMENTS (link_watcher_attr_datas); i++) {
if (nm_streq (link_watcher_attr_datas[i].js_key, j_key)) {
attr_data = &link_watcher_attr_datas[i];
break;
}
}
}
if (!attr_data) {
*out_unrecognized_content = TRUE;
continue;
}
parse_result = &args[attr_data->link_watcher_attr];
if (parse_result->has)
*out_unrecognized_content = TRUE;
if (!nm_value_type_from_json (attr_data->value_type, j_val, &parse_result->val))
*out_unrecognized_content = TRUE;
else
parse_result->has = TRUE;
}
#define _PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES(_parse_results, ...) \
({ \
int _i; \
\
for (_i = 0; _i < (int) G_N_ELEMENTS ((_parse_results)); _i++) { \
if ( (_parse_results)[_i].has \
&& !NM_IN_SET ((LinkWatcherAttribute) _i, __VA_ARGS__)) \
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
break; \
} \
\
(_i == (int) G_N_ELEMENTS ((_parse_results))); \
})
v_name = _LINK_WATCHER_ATTR_GET_STRING (args, LINK_WATCHER_ATTRIBUTE_NAME);
if (nm_streq0 (v_name, NM_TEAM_LINK_WATCHER_ETHTOOL)) {
if (_PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES (args, _EXPECTED_LINK_WATCHER_ATTRIBUTES_ETHTOOL))
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
*out_unrecognized_content = TRUE;
result = nm_team_link_watcher_new_ethtool (_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_DELAY_UP),
_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_DELAY_DOWN),
NULL);
} else if (nm_streq0 (v_name, NM_TEAM_LINK_WATCHER_NSNA_PING)) {
if (_PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES (args, _EXPECTED_LINK_WATCHER_ATTRIBUTES_NSNA_PING))
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
*out_unrecognized_content = TRUE;
result = nm_team_link_watcher_new_nsna_ping (_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_INIT_WAIT),
_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_INTERVAL),
_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_MISSED_MAX),
_LINK_WATCHER_ATTR_GET_STRING (args, LINK_WATCHER_ATTRIBUTE_TARGET_HOST),
NULL);
} else if (nm_streq0 (v_name, NM_TEAM_LINK_WATCHER_ARP_PING)) {
NMTeamLinkWatcherArpPingFlags v_flags = NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE;
if (_PARSE_RESULT_HAS_UNEXPECTED_ATTRIBUTES (args, _EXPECTED_LINK_WATCHER_ATTRIBUTES_ARP_PING))
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
*out_unrecognized_content = TRUE;
if (_LINK_WATCHER_ATTR_GET_BOOL (args, LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE))
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE;
if (_LINK_WATCHER_ATTR_GET_BOOL (args, LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE))
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE;
if (_LINK_WATCHER_ATTR_GET_BOOL (args, LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS))
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS;
result = nm_team_link_watcher_new_arp_ping2 (_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_INIT_WAIT),
_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_INTERVAL),
_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_MISSED_MAX),
_LINK_WATCHER_ATTR_GET_INT (args, LINK_WATCHER_ATTRIBUTE_VLANID),
_LINK_WATCHER_ATTR_GET_STRING (args, LINK_WATCHER_ATTRIBUTE_TARGET_HOST),
_LINK_WATCHER_ATTR_GET_STRING (args, LINK_WATCHER_ATTRIBUTE_SOURCE_HOST),
v_flags,
NULL);
}
if (result)
return result;
fail:
*out_unrecognized_content = TRUE;
return NULL;
}
#endif
/*****************************************************************************/
static GVariant *
_link_watcher_to_variant (const NMTeamLinkWatcher *link_watcher)
{
NMValueTypUnioMaybe args[G_N_ELEMENTS (link_watcher_attr_datas)];
GVariantBuilder builder;
int i;
if (!link_watcher)
return NULL;
_link_watcher_unpack (link_watcher, args);
if (!args[LINK_WATCHER_ATTRIBUTE_NAME].has)
return NULL;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
for (i = 0; i < (int) G_N_ELEMENTS (link_watcher_attr_datas); i++) {
const NMValueTypUnioMaybe *p_val = &args[i];
const LinkWatcherAttrData *attr_data = &link_watcher_attr_datas[i];
GVariant *v;
if (!p_val->has)
continue;
if (nm_value_type_equal (attr_data->value_type, &attr_data->default_val, &p_val->val))
continue;
if (attr_data->value_type == NM_VALUE_TYPE_INT)
v = g_variant_new_int32 (p_val->val.v_int);
else {
v = nm_value_type_to_variant (attr_data->value_type,
&p_val->val);
}
if (!v)
continue;
nm_assert (g_variant_is_floating (v));
g_variant_builder_add (&builder,
"{sv}",
attr_data->dbus_name,
v);
}
return g_variant_builder_end (&builder);
}
#define _LINK_WATCHER_ATTR_VARGET(variants, link_watcher_attribute, _value_type, c_type, _cmd) \
({ \
GVariant *const*_variants = (variants); \
GVariant *_cc; \
\
nm_assert (link_watcher_attr_datas[(link_watcher_attribute)].value_type == (_value_type)); \
\
(_cc = _variants[(link_watcher_attribute)]) \
? (_cmd) \
: link_watcher_attr_datas[(link_watcher_attribute)].default_val.c_type; \
})
#define _LINK_WATCHER_ATTR_VARGET_BOOL(variants, link_watcher_attribute) (_LINK_WATCHER_ATTR_VARGET (variants, link_watcher_attribute, NM_VALUE_TYPE_BOOL, v_bool, g_variant_get_boolean (_cc) ))
#define _LINK_WATCHER_ATTR_VARGET_INT(variants, link_watcher_attribute) (_LINK_WATCHER_ATTR_VARGET (variants, link_watcher_attribute, NM_VALUE_TYPE_INT, v_int, g_variant_get_int32 (_cc) ))
#define _LINK_WATCHER_ATTR_VARGET_STRING(variants, link_watcher_attribute) (_LINK_WATCHER_ATTR_VARGET (variants, link_watcher_attribute, NM_VALUE_TYPE_STRING, v_string, g_variant_get_string (_cc, NULL) ))
static void
_variants_list_link_watcher_unref_auto (GVariant *(*p_variants)[])
{
int i;
for (i = 0; i < (int) G_N_ELEMENTS (link_watcher_attr_datas); i++)
nm_g_variant_unref ((*p_variants)[i]);
}
static NMTeamLinkWatcher *
_link_watcher_from_variant (GVariant *watcher_var,
gboolean strict_parsing,
GError **error)
{
nm_auto (_variants_list_link_watcher_unref_auto) GVariant *variants[G_N_ELEMENTS (link_watcher_attr_datas)] = { NULL, };
const char *v_key;
GVariant *v_val;
const char *v_name;
GVariantIter iter;
g_return_val_if_fail (g_variant_is_of_type (watcher_var, G_VARIANT_TYPE ("a{sv}")), NULL);
g_variant_iter_init (&iter, watcher_var);
while (g_variant_iter_next (&iter, "{&sv}", &v_key, &v_val)) {
_nm_unused gs_unref_variant GVariant *v_val_free = v_val;
const LinkWatcherAttrData *attr_data = NULL;
const GVariantType *variant_type;
int i;
for (i = 0; i < (int) G_N_ELEMENTS (link_watcher_attr_datas); i++) {
if (nm_streq (link_watcher_attr_datas[i].dbus_name, v_key)) {
attr_data = &link_watcher_attr_datas[i];
break;
}
}
if (!attr_data) {
if (strict_parsing) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid D-Bus property \"%s\""),
v_key);
return NULL;
}
continue;
}
if (attr_data->value_type == NM_VALUE_TYPE_INT)
variant_type = G_VARIANT_TYPE_INT32;
else
variant_type = nm_value_type_get_variant_type (attr_data->value_type);
if (!g_variant_is_of_type (v_val, variant_type)) {
if (strict_parsing) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid D-Bus property \"%s\""),
v_key);
return NULL;
}
continue;
}
if (variants[attr_data->link_watcher_attr]) {
if (strict_parsing) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("duplicate D-Bus property \"%s\""),
v_key);
return NULL;
}
g_variant_unref (variants[attr_data->link_watcher_attr]);
}
variants[attr_data->link_watcher_attr] = g_steal_pointer (&v_val_free);
}
#define _VARIANTS_HAVE_UNEXPECTED_ATTRIBUTES(_type, _variants, _error, ...) \
({ \
int _i; \
gboolean _has_error = FALSE; \
\
for (_i = 0; _i < (int) G_N_ELEMENTS ((_variants)); _i++) { \
if ( (_variants)[_i] \
&& !NM_IN_SET ((LinkWatcherAttribute) _i, __VA_ARGS__)) { \
_has_error = TRUE; \
g_set_error (_error, \
NM_CONNECTION_ERROR, \
NM_CONNECTION_ERROR_INVALID_PROPERTY, \
_("invalid D-Bus property \"%s\" for \"%s\""), \
link_watcher_attr_datas[_i].dbus_name, \
_type); \
break; \
} \
} \
\
_has_error; \
})
v_name = _LINK_WATCHER_ATTR_VARGET_STRING (variants, LINK_WATCHER_ATTRIBUTE_NAME);
if (nm_streq0 (v_name, NM_TEAM_LINK_WATCHER_ETHTOOL)) {
if ( strict_parsing
&& _VARIANTS_HAVE_UNEXPECTED_ATTRIBUTES (v_name, variants, error, _EXPECTED_LINK_WATCHER_ATTRIBUTES_ETHTOOL))
return NULL;
return nm_team_link_watcher_new_ethtool (_LINK_WATCHER_ATTR_VARGET_INT (variants, LINK_WATCHER_ATTRIBUTE_DELAY_UP),
_LINK_WATCHER_ATTR_VARGET_INT (variants, LINK_WATCHER_ATTRIBUTE_DELAY_DOWN),
strict_parsing ? error : NULL);
}
if (nm_streq0 (v_name, NM_TEAM_LINK_WATCHER_NSNA_PING)) {
if ( strict_parsing
&& _VARIANTS_HAVE_UNEXPECTED_ATTRIBUTES (v_name, variants, error, _EXPECTED_LINK_WATCHER_ATTRIBUTES_NSNA_PING))
return NULL;
return nm_team_link_watcher_new_nsna_ping (_LINK_WATCHER_ATTR_VARGET_INT (variants, LINK_WATCHER_ATTRIBUTE_INIT_WAIT),
_LINK_WATCHER_ATTR_VARGET_INT (variants, LINK_WATCHER_ATTRIBUTE_INTERVAL),
_LINK_WATCHER_ATTR_VARGET_INT (variants, LINK_WATCHER_ATTRIBUTE_MISSED_MAX),
_LINK_WATCHER_ATTR_VARGET_STRING (variants, LINK_WATCHER_ATTRIBUTE_TARGET_HOST),
strict_parsing ? error : NULL);
}
if (nm_streq0 (v_name, NM_TEAM_LINK_WATCHER_ARP_PING)) {
NMTeamLinkWatcherArpPingFlags v_flags = NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE;
if ( strict_parsing
&& _VARIANTS_HAVE_UNEXPECTED_ATTRIBUTES (v_name, variants, error, _EXPECTED_LINK_WATCHER_ATTRIBUTES_ARP_PING))
return NULL;
if (_LINK_WATCHER_ATTR_VARGET_BOOL (variants, LINK_WATCHER_ATTRIBUTE_VALIDATE_ACTIVE))
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE;
if (_LINK_WATCHER_ATTR_VARGET_BOOL (variants, LINK_WATCHER_ATTRIBUTE_VALIDATE_INACTIVE))
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE;
if (_LINK_WATCHER_ATTR_VARGET_BOOL (variants, LINK_WATCHER_ATTRIBUTE_SEND_ALWAYS))
v_flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS;
return nm_team_link_watcher_new_arp_ping2 (_LINK_WATCHER_ATTR_VARGET_INT (variants, LINK_WATCHER_ATTRIBUTE_INIT_WAIT),
_LINK_WATCHER_ATTR_VARGET_INT (variants, LINK_WATCHER_ATTRIBUTE_INTERVAL),
_LINK_WATCHER_ATTR_VARGET_INT (variants, LINK_WATCHER_ATTRIBUTE_MISSED_MAX),
_LINK_WATCHER_ATTR_VARGET_INT (variants, LINK_WATCHER_ATTRIBUTE_VLANID),
_LINK_WATCHER_ATTR_VARGET_STRING (variants, LINK_WATCHER_ATTRIBUTE_TARGET_HOST),
_LINK_WATCHER_ATTR_VARGET_STRING (variants, LINK_WATCHER_ATTRIBUTE_SOURCE_HOST),
v_flags,
strict_parsing ? error : NULL);
}
if (strict_parsing) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("unknown link-watcher name \"%s\""),
v_name);
}
return NULL;
}
/*****************************************************************************/
/**
* _nm_utils_team_link_watchers_to_variant:
* @link_watchers: (element-type NMTeamLinkWatcher): array of #NMTeamLinkWatcher
*
* Utility function to convert a #GPtrArray of #NMTeamLinkWatcher objects
* representing link watcher configuration for team devices into a #GVariant
* of type 'aa{sv}' representing an array of link watchers.
*
* Returns: (transfer full): a new floating #GVariant representing link watchers.
**/
GVariant *
_nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers)
{
GVariantBuilder builder;
guint i;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
if (link_watchers) {
for (i = 0; i < link_watchers->len; i++) {
g_variant_builder_add (&builder,
"@a{sv}",
_link_watcher_to_variant (link_watchers->pdata[i]));
}
}
return g_variant_builder_end (&builder);
}
/**
* _nm_utils_team_link_watchers_from_variant:
* @value: a #GVariant of type 'aa{sv}'
* @strict_parsing: whether to parse strictly or ignore everything invalid.
* @error: error reason.
*
* Utility function to convert a #GVariant representing a list of team link
* watchers int a #GPtrArray of #NMTeamLinkWatcher objects.
*
* Returns: (transfer full) (element-type NMTeamLinkWatcher): a newly allocated
* #GPtrArray of #NMTeamLinkWatcher objects.
*
* Note that if you provide an @error, then the function can only fail (and return %NULL)
* or succeed (and not return %NULL). If you don't provide an @error, then the function
* never returns %NULL.
**/
GPtrArray *
_nm_utils_team_link_watchers_from_variant (GVariant *value,
gboolean strict_parsing,
GError **error)
{
gs_unref_ptrarray GPtrArray *link_watchers = NULL;
GVariantIter iter;
GVariant *watcher_var;
g_return_val_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")), NULL);
link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
g_variant_iter_init (&iter, value);
while (g_variant_iter_next (&iter, "@a{sv}", &watcher_var)) {
_nm_unused gs_unref_variant GVariant *watcher_var_free = watcher_var;
NMTeamLinkWatcher *watcher;
watcher = _link_watcher_from_variant (watcher_var, strict_parsing, error);
if (error && *error)
return NULL;
if (watcher)
g_ptr_array_add (link_watchers, watcher);
}
return g_steal_pointer (&link_watchers);
}
/*****************************************************************************/
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
const char *
nm_team_setting_config_get (const NMTeamSetting *self)
{
char *js_str;
nm_assert (self);
if (G_LIKELY (!self->d._js_str_need_synthetize))
return self->d._js_str;
nm_assert (!self->d._js_str);
nm_assert (self->d.strict_validated);
if (_team_setting_check_default (self) == 0) {
/* the default is set. We signal this as a NULL JSON string.
* Nothing to do. */
js_str = NULL;
} else {
const TeamAttrData *attr_data;
GString *gstr;
bool is_default_lst[_NM_TEAM_ATTRIBUTE_NUM] = { FALSE, };
gboolean list_is_empty = TRUE;
const char *v_master_runner;
gstr = g_string_new (NULL);
g_string_append (gstr, "{ ");
v_master_runner = self->d.is_port
? NULL
: self->d.master.runner;
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) {
if (_team_attr_data_is_relevant (attr_data, self->d.is_port)) {
is_default_lst[attr_data->team_attr] = _team_attr_data_is_default (attr_data,
self->d.is_port,
v_master_runner,
_team_setting_get_field (self, attr_data));
}
}
if (self->d.is_port) {
static const NMTeamAttribute attr_lst_port[] = {
NM_TEAM_ATTRIBUTE_PORT_QUEUE_ID,
NM_TEAM_ATTRIBUTE_PORT_PRIO,
NM_TEAM_ATTRIBUTE_PORT_STICKY,
NM_TEAM_ATTRIBUTE_PORT_LACP_PRIO,
NM_TEAM_ATTRIBUTE_PORT_LACP_KEY,
};
if (_team_setting_fields_to_json_maybe (self, gstr, !list_is_empty, is_default_lst, attr_lst_port, G_N_ELEMENTS (attr_lst_port)))
list_is_empty = FALSE;
} else {
if ( !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY]) {
static const NMTeamAttribute attr_lst_runner_pt1[] = {
NM_TEAM_ATTRIBUTE_MASTER_RUNNER,
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_HWADDR_POLICY,
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH,
};
static const NMTeamAttribute attr_lst_runner_pt2[] = {
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER,
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL,
};
static const NMTeamAttribute attr_lst_runner_pt3[] = {
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_ACTIVE,
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_FAST_RATE,
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_SYS_PRIO,
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_MIN_PORTS,
NM_TEAM_ATTRIBUTE_MASTER_RUNNER_AGG_SELECT_POLICY,
};
gboolean list_is_empty2 = TRUE;
if (!list_is_empty)
nm_json_aux_gstr_append_delimiter (gstr);
nm_json_aux_gstr_append_obj_name (gstr, "runner", '{');
if (_team_setting_fields_to_json_maybe (self, gstr, !list_is_empty2, is_default_lst, attr_lst_runner_pt1, G_N_ELEMENTS (attr_lst_runner_pt1)))
list_is_empty2 = FALSE;
if ( !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_BALANCER_INTERVAL]) {
if (!list_is_empty2)
nm_json_aux_gstr_append_delimiter (gstr);
nm_json_aux_gstr_append_obj_name (gstr, "tx_balancer", '{');
if (!_team_setting_fields_to_json_maybe (self, gstr, FALSE, is_default_lst, attr_lst_runner_pt2, G_N_ELEMENTS (attr_lst_runner_pt2)))
nm_assert_not_reached ();
g_string_append (gstr, " }");
list_is_empty2 = FALSE;
}
if (_team_setting_fields_to_json_maybe (self, gstr, !list_is_empty2, is_default_lst, attr_lst_runner_pt3, G_N_ELEMENTS (attr_lst_runner_pt3)))
list_is_empty2 = FALSE;
nm_assert (!list_is_empty2);
g_string_append (gstr, " }");
list_is_empty = FALSE;
}
if ( !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL]) {
static const NMTeamAttribute attr_lst_notify_peers[] = {
NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_COUNT,
NM_TEAM_ATTRIBUTE_MASTER_NOTIFY_PEERS_INTERVAL,
};
if (!list_is_empty)
nm_json_aux_gstr_append_delimiter (gstr);
nm_json_aux_gstr_append_obj_name (gstr, "notify_peers", '{');
if (!_team_setting_fields_to_json_maybe (self, gstr, FALSE, is_default_lst, attr_lst_notify_peers, G_N_ELEMENTS (attr_lst_notify_peers)))
nm_assert_not_reached ();
g_string_append (gstr, " }");
list_is_empty = FALSE;
}
if ( !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT]
|| !is_default_lst[NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL]) {
static const NMTeamAttribute attr_lst_notify_peers[] = {
NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_COUNT,
NM_TEAM_ATTRIBUTE_MASTER_MCAST_REJOIN_INTERVAL,
};
if (!list_is_empty)
nm_json_aux_gstr_append_delimiter (gstr);
nm_json_aux_gstr_append_obj_name (gstr, "mcast_rejoin", '{');
if (!_team_setting_fields_to_json_maybe (self, gstr, FALSE, is_default_lst, attr_lst_notify_peers, G_N_ELEMENTS (attr_lst_notify_peers)))
nm_assert_not_reached ();
g_string_append (gstr, " }");
list_is_empty = FALSE;
}
}
if (!is_default_lst[NM_TEAM_ATTRIBUTE_LINK_WATCHERS]) {
_team_setting_field_to_json (self, gstr, !list_is_empty, NM_TEAM_ATTRIBUTE_LINK_WATCHERS);
list_is_empty = FALSE;
}
if (!list_is_empty)
g_string_append (gstr, " }");
js_str = g_string_free (gstr, list_is_empty);;
}
/* mutate the constant object. In C++ speak, these fields are "mutable".
* That is because we construct the JSON string lazily/on-demand. */
*((char **) &self->_data_priv._js_str) = js_str;
*((bool *) &self->_data_priv._js_str_need_synthetize) = FALSE;
return self->d._js_str;
}
/*****************************************************************************/
#if WITH_JSON_VALIDATION
static gboolean
_attr_data_match_keys (const TeamAttrData *attr_data,
const char *const*keys,
guint8 n_keys)
{
guint8 i;
_team_attr_data_ASSERT (attr_data);
nm_assert (keys);
nm_assert (n_keys > 0);
nm_assert (({
gboolean all_non_null = TRUE;
for (i = 0; i < n_keys; i++)
all_non_null = all_non_null && keys[i] && keys[i][0] != '\0';
all_non_null;
}));
if (attr_data->js_keys_len < n_keys)
return FALSE;
for (i = 0; i < n_keys; i++) {
if (!nm_streq (keys[i], attr_data->js_keys[i]))
return FALSE;
}
return TRUE;
}
static const TeamAttrData *
_attr_data_find_by_json_key (gboolean is_port,
const char *const*keys,
guint8 n_keys)
{
const TeamAttrData *attr_data;
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) {
if ( _team_attr_data_is_relevant (attr_data, is_port)
&& _attr_data_match_keys (attr_data, keys, n_keys))
return attr_data;
}
return NULL;
}
static void
_js_parse_locate_keys (NMTeamSetting *self,
json_t *root_js_obj,
json_t *found_keys[static _NM_TEAM_ATTRIBUTE_NUM],
gboolean *out_unrecognized_content)
{
const char *keys[3];
const char *cur_key1;
const char *cur_key2;
const char *cur_key3;
json_t *cur_val1;
json_t *cur_val2;
json_t *cur_val3;
#define _handle(_self, _cur_key, _cur_val, _keys, _level, _found_keys, _out_unrecognized_content) \
({ \
const TeamAttrData *_attr_data; \
gboolean _handled = FALSE; \
\
(_keys)[(_level) - 1] = (_cur_key); \
_attr_data = _attr_data_find_by_json_key ((_self)->d.is_port, (_keys), (_level)); \
if ( _attr_data \
&& _attr_data->js_keys_len == (_level)) { \
if ((_found_keys)[_attr_data->team_attr]) \
*(_out_unrecognized_content) = TRUE; \
(_found_keys)[_attr_data->team_attr] = (_cur_val); \
_handled = TRUE; \
} else if ( !_attr_data \
|| !json_is_object ((_cur_val))) { \
*(_out_unrecognized_content) = TRUE; \
_handled = TRUE; \
} \
_handled; \
})
json_object_foreach (root_js_obj, cur_key1, cur_val1) {
if (!_handle (self, cur_key1, cur_val1, keys, 1, found_keys, out_unrecognized_content)) {
json_object_foreach (cur_val1, cur_key2, cur_val2) {
if (!_handle (self, cur_key2, cur_val2, keys, 2, found_keys, out_unrecognized_content)) {
json_object_foreach (cur_val2, cur_key3, cur_val3) {
if (!_handle (self, cur_key3, cur_val3, keys, 3, found_keys, out_unrecognized_content))
*out_unrecognized_content = TRUE;
}
}
}
}
}
#undef _handle
}
static void
_js_parse_unpack (gboolean is_port,
json_t *found_keys[static _NM_TEAM_ATTRIBUTE_NUM],
bool out_has_lst[static _NM_TEAM_ATTRIBUTE_NUM],
NMValueTypUnion out_val_lst[static _NM_TEAM_ATTRIBUTE_NUM],
gboolean *out_unrecognized_content,
GPtrArray **out_ptr_array_link_watchers_free,
GPtrArray **out_ptr_array_master_runner_tx_hash_free)
{
const TeamAttrData *attr_data;
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) {
NMValueTypUnion *p_out_val;
gboolean valid = FALSE;
json_t *arg_js_obj;
if (!_team_attr_data_is_relevant (attr_data, is_port))
continue;
nm_assert (!out_has_lst[attr_data->team_attr]);
arg_js_obj = found_keys[attr_data->team_attr];
if (!arg_js_obj)
continue;
p_out_val = &out_val_lst[attr_data->team_attr];
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC)
valid = nm_value_type_from_json (attr_data->value_type, arg_js_obj, p_out_val);
else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) {
GPtrArray *link_watchers = NULL;
NMTeamLinkWatcher *link_watcher;
nm_assert (out_ptr_array_link_watchers_free && !*out_ptr_array_link_watchers_free);
if (json_is_array (arg_js_obj)) {
gsize i, len;
len = json_array_size (arg_js_obj);
link_watchers = g_ptr_array_new_full (len, (GDestroyNotify) nm_team_link_watcher_unref);
for (i = 0; i < len; i++) {
link_watcher = _link_watcher_from_json (json_array_get (arg_js_obj, i),
out_unrecognized_content);
if (link_watcher)
g_ptr_array_add (link_watchers, link_watcher);
}
} else {
link_watcher = _link_watcher_from_json (arg_js_obj,
out_unrecognized_content);
if (link_watcher) {
link_watchers = g_ptr_array_new_full (1, (GDestroyNotify) nm_team_link_watcher_unref);
g_ptr_array_add (link_watchers, link_watcher);
}
}
if (link_watchers) {
valid = TRUE;
p_out_val->v_ptrarray = link_watchers;
*out_ptr_array_link_watchers_free = link_watchers;
}
} else if ( !is_port
&& attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
GPtrArray *strv = NULL;
nm_assert (out_ptr_array_master_runner_tx_hash_free && !*out_ptr_array_master_runner_tx_hash_free);
if (json_is_array (arg_js_obj)) {
gsize i, len;
len = json_array_size (arg_js_obj);
if (len > 0) {
strv = g_ptr_array_sized_new (len);
for (i = 0; i < len; i++) {
const char *v_string;
if ( nm_jansson_json_as_string (json_array_get (arg_js_obj, i),
&v_string) <= 0
|| !v_string
|| v_string[0] == '\0') {
/* we remember that there was some invalid content, but parts of the
* list could still be parsed. */
*out_unrecognized_content = TRUE;
continue;
}
g_ptr_array_add (strv, (char *) v_string);
}
}
valid = TRUE;
*out_ptr_array_master_runner_tx_hash_free = strv;
}
p_out_val->v_ptrarray = strv;
} else
nm_assert_not_reached ();
out_has_lst[attr_data->team_attr] = valid;
if (!valid)
*out_unrecognized_content = TRUE;
}
}
#endif
guint32
nm_team_setting_config_set (NMTeamSetting *self, const char *js_str)
{
guint32 changed_flags = 0;
gboolean do_set_default = TRUE;
gboolean new_strict_validated = FALSE;
gboolean new_js_str_invalid = FALSE;
_team_setting_ASSERT (self);
if ( !js_str
|| js_str[0] == '\0') {
changed_flags = _team_setting_set_default (self);
if ( changed_flags != 0
|| !nm_streq0 (js_str, self->d._js_str))
changed_flags |= nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG);
nm_clear_g_free ((char **) &self->_data_priv._js_str);
self->_data_priv._js_str = g_strdup (js_str);
self->_data_priv._js_str_need_synthetize = FALSE;
self->_data_priv.strict_validated = TRUE;
self->_data_priv.js_str_invalid = FALSE;
return changed_flags;
}
if ( self->d._js_str
&& nm_streq (js_str, self->d._js_str))
return 0;
changed_flags |= nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG);
#if WITH_JSON_VALIDATION
if (js_str[0] != '\0') {
nm_auto_decref_json json_t *root_js_obj = NULL;
if (nm_jansson_load ())
root_js_obj = json_loads (js_str, 0, NULL);
if ( !root_js_obj
|| !json_is_object (root_js_obj))
new_js_str_invalid = TRUE;
else {
gboolean unrecognized_content = FALSE;
bool has_lst[_NM_TEAM_ATTRIBUTE_NUM] = { FALSE, };
NMValueTypUnion val_lst[_NM_TEAM_ATTRIBUTE_NUM];
json_t *found_keys[_NM_TEAM_ATTRIBUTE_NUM] = { NULL, };
gs_unref_ptrarray GPtrArray *ptr_array_master_runner_tx_hash_free = NULL;
gs_unref_ptrarray GPtrArray *ptr_array_link_watchers_free = NULL;
_js_parse_locate_keys (self,
root_js_obj,
found_keys,
&unrecognized_content);
_js_parse_unpack (self->d.is_port,
found_keys,
has_lst,
val_lst,
&unrecognized_content,
&ptr_array_link_watchers_free,
&ptr_array_master_runner_tx_hash_free);
do_set_default = FALSE;
changed_flags |= _team_setting_set (self,
TRUE,
has_lst,
val_lst);
if ( !unrecognized_content
&& _team_setting_verify (self, NULL)) {
/* if we could parse everything without unexpected/unknown data,
* we switch into strictly validating mode. */
new_strict_validated = TRUE;
}
}
}
#endif
if (do_set_default)
changed_flags |= _team_setting_set_default (self);
self->_data_priv.strict_validated = new_strict_validated;
self->_data_priv._js_str_need_synthetize = FALSE;
self->_data_priv.js_str_invalid = new_js_str_invalid;
g_free ((char *) self->_data_priv._js_str);
self->_data_priv._js_str = g_strdup (js_str);
return changed_flags;
}
/*****************************************************************************/
static void
_team_setting_prefix_error (const NMTeamSetting *self,
GError **error,
const char *prop_name_master,
const char *prop_name_port)
{
_team_setting_ASSERT (self);
nm_assert ( self->d.is_port
? (!!prop_name_port)
: (!!prop_name_master));
g_prefix_error (error,
"%s.%s: ",
self->d.is_port
? NM_SETTING_TEAM_PORT_SETTING_NAME
: NM_SETTING_TEAM_SETTING_NAME,
self->d.is_port
? prop_name_master
: prop_name_port);
}
static gboolean
_team_setting_verify (const NMTeamSetting *self,
GError **error)
{
guint i;
const char *js_str;
if (!self->d.is_port) {
if (!self->d.master.runner) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING,
_("missing runner"));
_team_setting_prefix_error (self, error, NM_SETTING_TEAM_RUNNER, NULL);
return FALSE;
}
if ( self->d.master.runner
&& g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_BROADCAST) != 0
&& g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_ROUNDROBIN) != 0
&& g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_RANDOM) != 0
&& g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP) != 0
&& g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_LOADBALANCE) != 0
&& g_ascii_strcasecmp (self->d.master.runner, NM_SETTING_TEAM_RUNNER_LACP) != 0) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING,
_("invalid runner \"%s\""), self->d.master.runner);
_team_setting_prefix_error (self, error, NM_SETTING_TEAM_RUNNER, NULL);
return FALSE;
}
if (self->d.master.runner_tx_hash) {
for (i = 0; i < self->d.master.runner_tx_hash->len; i++) {
const char *val = self->d.master.runner_tx_hash->pdata[i];
if (!val[0]) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING,
_("invalid runner.tx-hash"));
_team_setting_prefix_error (self, error, NM_SETTING_TEAM_RUNNER_TX_HASH, NULL);
return FALSE;
}
}
}
}
for (i = 0; i < self->d.link_watchers->len; i++) {
NMTeamLinkWatcher *link_watcher = self->d.link_watchers->pdata[i];
const char *name = nm_team_link_watcher_get_name (link_watcher);
if (!NM_IN_STRSET (name,
NM_TEAM_LINK_WATCHER_ETHTOOL,
NM_TEAM_LINK_WATCHER_ARP_PING,
NM_TEAM_LINK_WATCHER_NSNA_PING)) {
if (!name) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING,
_("missing link watcher name"));
} else {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING,
_("unknown link watcher \"%s\""), name);
}
_team_setting_prefix_error (self, error, NM_SETTING_TEAM_LINK_WATCHERS, NM_SETTING_TEAM_PORT_LINK_WATCHERS);
return FALSE;
}
if ( NM_IN_STRSET (name,
NM_TEAM_LINK_WATCHER_ARP_PING,
NM_TEAM_LINK_WATCHER_NSNA_PING)
&& !nm_team_link_watcher_get_target_host (link_watcher)) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING,
_("missing target host"));
_team_setting_prefix_error (self, error, NM_SETTING_TEAM_LINK_WATCHERS, NM_SETTING_TEAM_PORT_LINK_WATCHERS);
return FALSE;
}
if ( nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING)
&& !nm_team_link_watcher_get_source_host (link_watcher)) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_SETTING,
_("missing source address"));
_team_setting_prefix_error (self, error, NM_SETTING_TEAM_LINK_WATCHERS, NM_SETTING_TEAM_PORT_LINK_WATCHERS);
return FALSE;
}
}
/* we always materialize the JSON string. That is because we want to validate the
* string length of the resulting JSON. */
js_str = nm_team_setting_config_get (self);
if (js_str) {
if (strlen (js_str) > 1*1024*1024) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("team config exceeds size limit"));
_team_setting_prefix_error (self, error, NM_SETTING_TEAM_CONFIG, NM_SETTING_TEAM_PORT_CONFIG);
return FALSE;
}
if (!g_utf8_validate (js_str, -1, NULL)) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("team config is not valid UTF-8"));
_team_setting_prefix_error (self, error, NM_SETTING_TEAM_CONFIG, NM_SETTING_TEAM_PORT_CONFIG);
return FALSE;
}
if (self->d.js_str_invalid) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid json"));
_team_setting_prefix_error (self, error, NM_SETTING_TEAM_CONFIG, NM_SETTING_TEAM_PORT_CONFIG);
return FALSE;
}
}
return TRUE;
}
gboolean
nm_team_setting_verify (const NMTeamSetting *self,
GError **error)
{
return _team_setting_verify (self, error);
}
/*****************************************************************************/
int
nm_team_setting_cmp (const NMTeamSetting *self_a,
const NMTeamSetting *self_b,
gboolean ignore_js_str)
{
const TeamAttrData *attr_data;
NM_CMP_SELF (self_a, self_b);
NM_CMP_FIELD_UNSAFE (self_a, self_b, d.is_port);
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) {
if (_team_attr_data_is_relevant (attr_data, self_a->d.is_port)) {
NM_CMP_RETURN (_team_attr_data_cmp (attr_data,
self_a->d.is_port,
_team_setting_get_field (self_a, attr_data),
_team_setting_get_field (self_b, attr_data)));
}
}
if (!ignore_js_str) {
NM_CMP_DIRECT_STRCMP0 (nm_team_setting_config_get (self_a),
nm_team_setting_config_get (self_b));
}
return 0;
}
guint32
nm_team_setting_reset (NMTeamSetting *self,
const NMTeamSetting *src)
{
const TeamAttrData *attr_data;
guint32 changed;
_team_setting_ASSERT (self);
_team_setting_ASSERT (src);
nm_assert (self->d.is_port == src->d.is_port);
if (self == src)
return 0;
changed = 0;
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) {
if (!_team_attr_data_is_relevant (attr_data, self->d.is_port))
continue;
if (_team_attr_data_equal (attr_data,
self->d.is_port,
_team_setting_get_field (self, attr_data),
_team_setting_get_field (src, attr_data)))
continue;
_team_attr_data_copy (attr_data,
self->d.is_port,
_team_setting_get_field (self, attr_data),
_team_setting_get_field (src, attr_data));
changed |= nm_team_attribute_to_flags (attr_data->team_attr);
}
if (!nm_streq0 (self->d._js_str, src->d._js_str)) {
g_free ((char *) self->_data_priv._js_str);
self->_data_priv._js_str = g_strdup (src->d._js_str);
changed |= nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG);
} else if (changed != 0)
changed |= nm_team_attribute_to_flags (NM_TEAM_ATTRIBUTE_CONFIG);
self->_data_priv._js_str_need_synthetize = src->d._js_str_need_synthetize;
self->_data_priv.strict_validated = src->d.strict_validated;
self->_data_priv.js_str_invalid = src->d.js_str_invalid;
return changed;
}
static void
_variants_list_team_unref_auto (GVariant *(*p_variants)[])
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
{
int i;
for (i = 0; i < _NM_TEAM_ATTRIBUTE_NUM; i++)
nm_g_variant_unref ((*p_variants)[i]);
}
gboolean
nm_team_setting_reset_from_dbus (NMTeamSetting *self,
GVariant *setting_dict,
GHashTable *keys,
guint32 *out_changed,
guint /* NMSettingParseFlags */ parse_flags,
GError **error)
{
nm_auto (_variants_list_team_unref_auto) GVariant *variants[_NM_TEAM_ATTRIBUTE_NUM] = { NULL, };
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
gs_unref_ptrarray GPtrArray *v_link_watchers = NULL;
const TeamAttrData *attr_data;
GVariantIter iter;
const char *v_key;
GVariant *v_val;
*out_changed = 0;
g_variant_iter_init (&iter, setting_dict);
while (g_variant_iter_next (&iter, "{&sv}", &v_key, &v_val)) {
_nm_unused gs_unref_variant GVariant *v_val_free = v_val;
const GVariantType *variant_type = NULL;
attr_data = _team_attr_data_find_for_dbus_name (self->d.is_port, v_key);
if (!attr_data) {
/* _nm_setting_new_from_dbus() already checks for unknown keys. Don't
* do that here. */
continue;
}
if (keys)
g_hash_table_remove (keys, v_key);
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC)
variant_type = nm_value_type_get_variant_type (attr_data->value_type);
else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_CONFIG)
variant_type = G_VARIANT_TYPE_STRING;
else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS)
variant_type = G_VARIANT_TYPE ("aa{sv}");
else if ( !self->d.is_port
&& attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH)
variant_type = G_VARIANT_TYPE_STRING_ARRAY;
else
nm_assert_not_reached ();
if (!g_variant_is_of_type (v_val, variant_type)) {
if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid D-Bus type \"%s\""),
g_variant_get_type_string (v_val));
_team_setting_prefix_error (self,
error,
attr_data->dbus_name,
attr_data->dbus_name);
return FALSE;
}
continue;
}
/* _nm_setting_new_from_dbus() already checks for duplicate keys. Don't
* do that here. */
nm_g_variant_unref (variants[attr_data->team_attr]);
variants[attr_data->team_attr] = g_steal_pointer (&v_val_free);
}
if (variants[NM_TEAM_ATTRIBUTE_LINK_WATCHERS]) {
gs_free_error GError *local = NULL;
v_link_watchers = _nm_utils_team_link_watchers_from_variant (variants[NM_TEAM_ATTRIBUTE_LINK_WATCHERS],
NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT),
&local);
if (local) {
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("invalid link-watchers: %s"),
local->message);
_team_setting_prefix_error (self,
error,
NM_SETTING_TEAM_LINK_WATCHERS,
NM_SETTING_TEAM_PORT_LINK_WATCHERS);
return FALSE;
}
}
libnm: rework team handling of JSON config Completely refactor the team/JSON handling in libnm's NMSettingTeam and NMSettingTeamPort. - team handling was added as rh#1398925. The goal is to have a more convenient way to set properties than constructing JSON. This requires libnm to implement the hard task of parsing JSON (and exposing well-understood properties) and generating JSON (based on these "artificial" properties). But not only libnm. In particular nmcli and the D-Bus API must make this "simpler" API accessible. - since NMSettingTeam and NMSettingTeamPort are conceptually the same, add "libnm-core/nm-team-utils.h" and NMTeamSetting that tries to handle the similar code side-by-sdie. The setting classes now just delegate for everything to NMTeamSetting. - Previously, there was a very fuzzy understanding of the provided JSON config. Tighten that up, when setting a JSON config it regenerates/parses all other properties and tries to make the best of it. When modifying any abstraction property, the entire JSON config gets regenerated. In particular, don't try to merge existing JSON config with the new fields. If the user uses the abstraction API, then the entire JSON gets replaced. For example note that nm_setting_team_add_link_watcher() would not be reflected in the JSON config (a bug). That only accidentally worked because client would serializing the changed link watcher to GVariant/D-Bus, then NetworkManager would set it via g_object_set(), which would renerate the JSON, and finally persist it to disk. But as far as libnm is concerned, nm_setting_team_add_link_watcher() would bring the settings instance in an inconsistent state where JSON and the link watcher property disagree. Setting any property must immediately update both the JSON and the abstraction API. - when constucting a team setting from D-Bus, we would previously parse both "config" and abstraction properties. That is wrong. Since our settings plugins only support JSON, all information must be present in the JSON config anyway. So, when "config" is present, only the JSON must be parsed. In the best case, the other information is redudant and contributes nothing. In the worse case, they information differs (which might happen if the client version differs from the server version). As the settings plugin only supports JSON, it's wrong to consider redundant, differing information from D-Bus. - we now only convert string to JSON or back when needed. Previously, setting a property resulted in parsing several JSON multiple times (per property). All operations should now scale well and be reasonably efficient. - also the property-changed signals are now handled correctly. Since NMTeamSetting knows the current state of all attributes, it can emit the exact property changed signals for what changed. - we no longer use libjansson to generate the JSON. JSON is supposed to be a machine readable exchange format, hence a major goal is to be easily handled by applications. While parsing JSON is not so trivial, writing a well-known set of values to JSON is. The advantage is that when you build libnm without libjansson support, then we still can convert the artificial properties to JSON. - Requiring libjansson in libnm is a burden, because most of the time it is not needed (as most users don't create team configurations). With this change we only require it to parse the team settings (no longer to write them). It should be reasonably simple to use a more minimalistic JSON parser that is sufficient for us, so that we can get rid of the libjansson dependency (for libnm). This also avoids the pain that we have due to the symbol collision of libjansson and libjson-glib. https://bugzilla.redhat.com/show_bug.cgi?id=1691619
2019-05-06 12:36:41 +02:00
*out_changed |= nm_team_setting_config_set (self,
variants[NM_TEAM_ATTRIBUTE_CONFIG]
? g_variant_get_string (variants[NM_TEAM_ATTRIBUTE_CONFIG], NULL)
: NULL);
if ( variants[NM_TEAM_ATTRIBUTE_CONFIG]
&& WITH_JSON_VALIDATION) {
/* for team settings, the JSON must be able to express all possible options. That means,
* if the GVariant contains both the JSON "config" and other options, then the other options
* are silently ignored. */
} else {
guint32 extra_changed = 0u;
for (attr_data = &team_attr_datas[TEAM_ATTR_IDX_CONFIG + 1]; attr_data < &team_attr_datas[G_N_ELEMENTS (team_attr_datas)]; attr_data++) {
NMValueTypUnion val;
guint32 changed = 0u;
if (!_team_attr_data_is_relevant (attr_data, self->d.is_port))
continue;
if (!variants[attr_data->team_attr])
continue;
if (attr_data->value_type != NM_VALUE_TYPE_UNSPEC) {
nm_value_type_get_from_variant (attr_data->value_type, &val, variants[attr_data->team_attr], FALSE);
changed = _team_setting_value_set (self,
attr_data->team_attr,
attr_data->value_type,
&val);
} else if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_LINK_WATCHERS) {
changed = _team_setting_value_link_watchers_set_list (self,
v_link_watchers ? (const NMTeamLinkWatcher *const *) v_link_watchers->pdata : NULL,
v_link_watchers ? v_link_watchers->len : 0u);
} else if ( !self->d.is_port
&& attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
gs_free const char **strv = NULL;
gsize len;
strv = g_variant_get_strv (variants[attr_data->team_attr], &len);
changed = _team_setting_value_master_runner_tx_hash_set_list (self,
strv,
NM_MIN (len, (gsize) G_MAXUINT));
} else
nm_assert_not_reached ();
extra_changed |= changed;
}
if ( !variants[NM_TEAM_ATTRIBUTE_CONFIG]
&& extra_changed) {
/* clear the JSON string so it can be regenerated. But only if we didn't set
* it above. */
self->_data_priv.strict_validated = TRUE;
self->_data_priv._js_str_need_synthetize = TRUE;
}
*out_changed |= extra_changed;
}
return TRUE;
}
/*****************************************************************************/
gboolean
nm_team_setting_maybe_changed (NMSetting *source,
const GParamSpec *const*obj_properties,
guint32 changed_flags)
{
NMTeamAttribute team_attr;
int count_flags;
guint32 ch;
if (changed_flags == 0u)
return FALSE;
count_flags = 0;
for (ch = changed_flags; ch != 0u; ch >>= 1) {
if (NM_FLAGS_HAS (ch, 0x1u))
count_flags++;
}
if (count_flags > 1)
g_object_freeze_notify (G_OBJECT (source));
ch = changed_flags;
for (team_attr = 0; team_attr < _NM_TEAM_ATTRIBUTE_NUM; team_attr++) {
if (!NM_FLAGS_ANY (ch, nm_team_attribute_to_flags (team_attr)))
continue;
g_object_notify_by_pspec (G_OBJECT (source),
(GParamSpec *) obj_properties[team_attr]);
ch &= ~nm_team_attribute_to_flags (team_attr);
if (ch == 0)
break;
}
if (count_flags > 1)
g_object_thaw_notify (G_OBJECT (source));
return TRUE;
}
/*****************************************************************************/
NMTeamSetting *
nm_team_setting_new (gboolean is_port,
const char *js_str)
{
NMTeamSetting *self;
gsize l;
G_STATIC_ASSERT_EXPR (sizeof (*self) == sizeof (self->_data_priv));
G_STATIC_ASSERT_EXPR (sizeof (*self) == NM_CONST_MAX (nm_offsetofend (NMTeamSetting, d.master), nm_offsetofend (NMTeamSetting, d.port)));
l = is_port
? nm_offsetofend (NMTeamSetting, d.port)
: nm_offsetofend (NMTeamSetting, d.master);
self = g_malloc0 (l);
self->_data_priv.is_port = is_port;
self->_data_priv.strict_validated = TRUE;
self->_data_priv._js_str_need_synthetize = FALSE;
self->_data_priv.link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
_team_setting_ASSERT (self);
nm_team_setting_config_set (self, js_str);
_team_setting_ASSERT (self);
return self;
}
void
nm_team_setting_free (NMTeamSetting *self)
{
if (!self)
return;
_team_setting_ASSERT (self);
if (!self->d.is_port) {
nm_clear_pointer (((GPtrArray **) &self->_data_priv.master.runner_tx_hash), g_ptr_array_unref);
g_free ((char *) self->_data_priv.master.runner);
g_free ((char *) self->_data_priv.master.runner_hwaddr_policy);
g_free ((char *) self->_data_priv.master.runner_tx_balancer);
g_free ((char *) self->_data_priv.master.runner_agg_select_policy);
}
g_ptr_array_unref ((GPtrArray *) self->_data_priv.link_watchers);
g_free ((char *) self->_data_priv._js_str);
g_free (self);
}