The example of ifcfg-dcb is as followed: TYPE=Ethernet DEVICE=eth0 HWADDR=00:11:22:33:44:ee BOOTPROTO=none IPADDR=1.2.3.4 PREFIX=24 GATEWAY=1.1.1.1
DCB=on DCB_APP_OPTS="app:fcoe appcfg:08 e:1 a:1 w:1" DCB_PG_OPTS="pgid:0000111f pgpct:25,75,0,0,0,0,0,0 e:1 a:1 w:1" DCB_PFC_OPTS="pfcup:00010000 e:1 a:1 w:1" Note: 1 What forms a minimum valid ifcfg for DCB? DCB=on DCB_APP_OPTS="app:fcoe e:1 a:1 w:1" DCB_PFC_OPTS="e:1 a:1 w:1" 2 At present, I skip priority group settings, that is to say, DCB_PG_OPTS does not work. If you like the new format of ifcfg-dcb, then I will move on to priority group. Changelog: V1: use new format of ifcfg-dcb Signed-off-by: Weiping Pan <[email protected]> --- docs/api/generate-settings-spec.c | 2 + docs/libnm-util/libnm-util-docs.sgml | 1 + include/nm-dbus-glib-types.h | 2 + libnm-util/Makefile.am | 2 + libnm-util/libnm-util.ver | 12 + libnm-util/nm-connection.c | 24 +- libnm-util/nm-connection.h | 2 + libnm-util/nm-setting-dcb.c | 845 ++++++++++++++++++++ libnm-util/nm-setting-dcb.h | 135 ++++ src/settings/plugins/ifcfg-rh/reader.c | 64 ++- .../ifcfg-rh/tests/network-scripts/ifcfg-test-dcb | 14 + .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 44 + 12 files changed, 1145 insertions(+), 2 deletions(-) create mode 100644 libnm-util/nm-setting-dcb.c create mode 100644 libnm-util/nm-setting-dcb.h create mode 100644 src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb diff --git a/docs/api/generate-settings-spec.c b/docs/api/generate-settings-spec.c index 7a540e3..48b3be9 100644 --- a/docs/api/generate-settings-spec.c +++ b/docs/api/generate-settings-spec.c @@ -49,6 +49,7 @@ #include <nm-setting-infiniband.h> #include <nm-setting-bond.h> #include <nm-setting-vlan.h> +#include <nm-setting-dcb.h> #include <nm-utils.h> @@ -64,6 +65,7 @@ static SettingNewFunc funcs[] = { nm_setting_infiniband_new, nm_setting_ip4_config_new, nm_setting_ip6_config_new, + nm_setting_dcb_new, nm_setting_olpc_mesh_new, nm_setting_ppp_new, nm_setting_pppoe_new, diff --git a/docs/libnm-util/libnm-util-docs.sgml b/docs/libnm-util/libnm-util-docs.sgml index f389971..8da2d52 100644 --- a/docs/libnm-util/libnm-util-docs.sgml +++ b/docs/libnm-util/libnm-util-docs.sgml @@ -30,6 +30,7 @@ <xi:include href="xml/nm-setting-bond.xml"/> <xi:include href="xml/nm-setting-infiniband.xml"/> <xi:include href="xml/nm-setting-vlan.xml"/> + <xi:include href="xml/nm-setting-dcb.xml"/> <xi:include href="xml/nm-setting-olpc-mesh.xml"/> <xi:include href="xml/nm-setting-8021x.xml"/> <xi:include href="xml/nm-setting-ip4-config.xml"/> diff --git a/include/nm-dbus-glib-types.h b/include/nm-dbus-glib-types.h index 0f8c542..a4d7f12 100644 --- a/include/nm-dbus-glib-types.h +++ b/include/nm-dbus-glib-types.h @@ -39,5 +39,7 @@ #define DBUS_TYPE_G_IP6_ROUTE (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID)) #define DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_IP6_ROUTE)) +#define DBUS_TYPE_G_DCB_APP (dbus_g_type_get_struct ("GValueArray", G_TYPE_UINT, DBUS_TYPE_G_UCHAR_ARRAY, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_INVALID)) +#define DBUS_TYPE_G_ARRAY_OF_DCB_APP (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_DCB_APP)) #endif /* DBUS_GLIB_TYPES_H */ diff --git a/libnm-util/Makefile.am b/libnm-util/Makefile.am index 76acd99..9242918 100644 --- a/libnm-util/Makefile.am +++ b/libnm-util/Makefile.am @@ -23,6 +23,7 @@ libnm_util_include_HEADERS = \ nm-setting-infiniband.h \ nm-setting-ip4-config.h \ nm-setting-vlan.h \ + nm-setting-dcb.h \ nm-setting-ip6-config.h \ nm-setting-ppp.h \ nm-setting-pppoe.h \ @@ -57,6 +58,7 @@ libnm_util_la_csources = \ nm-setting-infiniband.c \ nm-setting-ip4-config.c \ nm-setting-vlan.c \ + nm-setting-dcb.c \ nm-setting-ip6-config.c \ nm-setting-ppp.c \ nm-setting-pppoe.c \ diff --git a/libnm-util/libnm-util.ver b/libnm-util/libnm-util.ver index 4f0c27c..735ad30 100644 --- a/libnm-util/libnm-util.ver +++ b/libnm-util/libnm-util.ver @@ -21,6 +21,7 @@ global: nm_connection_get_setting_by_name; nm_connection_get_setting_cdma; nm_connection_get_setting_connection; + nm_connection_get_setting_dcb; nm_connection_get_setting_gsm; nm_connection_get_setting_infiniband; nm_connection_get_setting_ip4_config; @@ -232,6 +233,17 @@ global: nm_setting_connection_new; nm_setting_connection_permissions_user_allowed; nm_setting_connection_remove_permission; + nm_setting_dcb_error_get_type; + nm_setting_dcb_error_quark; + nm_setting_dcb_get_app; + nm_setting_dcb_get_dcb_enabled; + nm_setting_dcb_get_interface_name; + nm_setting_dcb_get_num_pfc_eaw; + nm_setting_dcb_get_num_pfc_up; + nm_setting_dcb_get_pfc_eaw; + nm_setting_dcb_get_pfc_up; + nm_setting_dcb_get_type; + nm_setting_dcb_new; nm_setting_diff; nm_setting_diff_result_get_type; nm_setting_duplicate; diff --git a/libnm-util/nm-connection.c b/libnm-util/nm-connection.c index e24ec14..9eb1d48 100644 --- a/libnm-util/nm-connection.c +++ b/libnm-util/nm-connection.c @@ -49,6 +49,7 @@ #include "nm-setting-olpc-mesh.h" #include "nm-setting-bond.h" #include "nm-setting-vlan.h" +#include "nm-setting-dcb.h" #include "nm-setting-serial.h" #include "nm-setting-gsm.h" #include "nm-setting-cdma.h" @@ -118,7 +119,7 @@ static guint signals[LAST_SIGNAL] = { 0 }; static GHashTable *registered_settings = NULL; -#define DEFAULT_MAP_SIZE 20 +#define DEFAULT_MAP_SIZE 21 static struct SettingInfo { const char *name; @@ -240,6 +241,11 @@ register_default_settings (void) NM_SETTING_VLAN_ERROR, 1, TRUE); + register_one_setting (NM_SETTING_DCB_SETTING_NAME, + NM_TYPE_SETTING_DCB, + NM_SETTING_DCB_ERROR, + 1, TRUE); + register_one_setting (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_TYPE_SETTING_WIRELESS_SECURITY, NM_SETTING_WIRELESS_SECURITY_ERROR, @@ -1693,6 +1699,22 @@ nm_connection_get_setting_vlan (NMConnection *connection) return (NMSettingVlan *) nm_connection_get_setting (connection, NM_TYPE_SETTING_VLAN); } +/** + * nm_connection_get_setting_dcb: + * @connection: the #NMConnection + * + * A shortcut to return any #NMSettingDcb the connection might contain. + * + * Returns: (transfer none): an #NMSettingDcb if the connection contains one, otherwise NULL + **/ +NMSettingDcb * +nm_connection_get_setting_dcb (NMConnection *connection) +{ + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + return (NMSettingDcb *) nm_connection_get_setting (connection, NM_TYPE_SETTING_DCB); +} /*************************************************************/ static void diff --git a/libnm-util/nm-connection.h b/libnm-util/nm-connection.h index ca29d15..7deaa32 100644 --- a/libnm-util/nm-connection.h +++ b/libnm-util/nm-connection.h @@ -50,6 +50,7 @@ #include <nm-setting-wireless.h> #include <nm-setting-wireless-security.h> #include <nm-setting-vlan.h> +#include <nm-setting-dcb.h> G_BEGIN_DECLS @@ -205,6 +206,7 @@ NMSettingWired * nm_connection_get_setting_wired (NMConnec NMSettingWireless * nm_connection_get_setting_wireless (NMConnection *connection); NMSettingWirelessSecurity *nm_connection_get_setting_wireless_security (NMConnection *connection); NMSettingVlan * nm_connection_get_setting_vlan (NMConnection *connection); +NMSettingDcb * nm_connection_get_setting_dcb (NMConnection *connection); G_END_DECLS diff --git a/libnm-util/nm-setting-dcb.c b/libnm-util/nm-setting-dcb.c new file mode 100644 index 0000000..9325474 --- /dev/null +++ b/libnm-util/nm-setting-dcb.c @@ -0,0 +1,845 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * Weiping Pan <[email protected]> + * + * 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. + * + * (C) Copyright 2012 Red Hat, Inc. + */ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <glib/gprintf.h> +#include <dbus/dbus-glib.h> +#include "nm-setting-dcb.h" +#include "nm-param-spec-specialized.h" +#include "nm-utils.h" +#include "nm-dbus-glib-types.h" +#include "nm-setting-connection.h" + +/** + * SECTION:nm-setting-dcb + * @short_description: Describes properties for DCB setttings + * @include: nm-setting-dcb.h + * + * The #NMSettingDcb object is a #NMSetting subclass that describes properties + * necessary for DCB setttings. + **/ + +/** + * nm_setting_dcb_error_quark: + * + * Registers an error quark for #NMSettingDcb if necessary. + * + * Returns: the error quark used for #NMSettingDcb errors. + **/ +GQuark +nm_setting_dcb_error_quark (void) +{ + static GQuark quark; + + if (G_UNLIKELY (!quark)) + quark = g_quark_from_static_string ("nm-setting-dcb-error-quark"); + return quark; +} + +G_DEFINE_TYPE (NMSettingDcb, nm_setting_dcb, NM_TYPE_SETTING) + +#define NM_SETTING_DCB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_DCB, NMSettingDcbPrivate)) + +typedef struct { + char *interface_name; + gboolean dcb_enabled; + char *app_options; + char *pg_options; + char *pfc_options; + + dcb_app *app; + + GArray *pfc_eaw; + GArray *pfc_up; +} NMSettingDcbPrivate; + +enum { + PROP_0, + PROP_INTERFACE_NAME, + PROP_DCB_ENABLED, + PROP_APP_OPTIONS, + PROP_PG_OPTIONS, + PROP_PFC_OPTIONS, + LAST_PROP +}; + +static const char *dcb_commands[] = { + NM_SETTING_DCB_OPTION_EAW_E, + NM_SETTING_DCB_OPTION_EAW_A, + NM_SETTING_DCB_OPTION_EAW_W, + NM_SETTING_DCB_OPTION_APP, + NM_SETTING_DCB_OPTION_APPCFG, + NM_SETTING_DCB_OPTION_PFCUP +}; + +/** + * nm_setting_dcb_new: + * + * Creates a new #NMSettingDcb object with default values. + * + * Returns: (transfer full): the new empty #NMSettingDcb object + **/ +NMSetting * +nm_setting_dcb_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_DCB, NULL); +} + +/** + * nm_setting_dcb_get_interface_name + * @setting: the #NMSettingDcb + * + * Returns: the #NMSettingDcb:interface-name property of the setting + **/ +const char * +nm_setting_dcb_get_interface_name (NMSettingDcb *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + + return NM_SETTING_DCB_GET_PRIVATE (setting)->interface_name; +} + +gboolean +nm_setting_dcb_get_dcb_enabled (NMSettingDcb *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), FALSE); + return NM_SETTING_DCB_GET_PRIVATE (setting)->dcb_enabled; +} + +/** + * nm_setting_dcb_get_app: + * + * @setting: the #NMSettingDcb + * + * Returns: the dcb_app of #NMSettingDcb:app + **/ +dcb_app * +nm_setting_dcb_get_app (NMSettingDcb *setting) +{ + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), NULL); + + return priv->app; +} + +/** + * nm_setting_dcb_get_num_pfc_eaw: + * + * @setting: the #NMSettingDcb + * + * Returns: the number of #NMSettingDcb:pfc_eaw + **/ +guint32 +nm_setting_dcb_get_num_pfc_eaw (NMSettingDcb *setting) +{ + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + GArray *array = NULL; + + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + + array = priv->pfc_eaw; + g_return_val_if_fail (array != NULL, 0); + return priv->pfc_eaw->len; +} + +/** + * nm_setting_dcb_get_pfc_eaw: + * + * @setting: the #NMSettingDcb + * @idx: the index of pfc eaw array + * + * Returns: the value of #NMSettingDcb:pfc_eaw[idx] + **/ +guint8 +nm_setting_dcb_get_pfc_eaw (NMSettingDcb *setting, guint32 idx) +{ + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + GArray *array = NULL; + + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + g_return_val_if_fail (idx < nm_setting_dcb_get_num_pfc_eaw (setting), 0); + + array = priv->pfc_eaw; + g_return_val_if_fail (array != NULL, 0); + return g_array_index (array, guint8, idx); +} + +/** + * nm_setting_dcb_get_num_pfc_up: + * + * @setting: the #NMSettingDcb + * + * Returns: the number of #NMSettingDcb:pfc_up + **/ +guint32 +nm_setting_dcb_get_num_pfc_up (NMSettingDcb *setting) +{ + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + GArray *array = NULL; + + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + + array = priv->pfc_up; + g_return_val_if_fail (array != NULL, 0); + + return priv->pfc_up ? priv->pfc_up->len : 0; +} + +/** + * nm_setting_dcb_get_pfc_up: + * + * @setting: the #NMSettingDcb + * @idx: the index of pfc up array + * + * Returns: the value of #NMSettingDcb:pfc_up[idx] + **/ +guint8 +nm_setting_dcb_get_pfc_up (NMSettingDcb *setting, guint32 idx) +{ + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + GArray *array = NULL; + + g_return_val_if_fail (NM_IS_SETTING_DCB (setting), 0); + g_return_val_if_fail (idx < nm_setting_dcb_get_num_pfc_up (setting), 0); + + array = priv->pfc_up; + g_return_val_if_fail (array != NULL, 0); + return g_array_index (array, guint8, idx); +} + +/** + * dcb_app_malloc: + * + * Malloc a dcb_app + **/ +static dcb_app * +dcb_app_malloc (void) +{ + dcb_app *app = NULL; + + app = g_malloc0 (sizeof (dcb_app)); + if (!app) + return NULL; + + app->app_eaw = g_array_new (FALSE, TRUE, EAW_ELEMENT_SIZE); + if (!app->app_eaw) + goto free_app; + + app->app_cfg = g_array_new (FALSE, TRUE, APP_CFG_ELEMENT_SIZE); + if (!app->app_cfg) + goto free_app_eaw; + + return app; + +free_app_eaw: + g_array_free (app->app_eaw, TRUE); +free_app: + g_free (app); + + return NULL; +} + +/** + * dcb_app_free: + * @app: the #dcb_app + * + * Free a dcb_app + **/ +static void +dcb_app_free (dcb_app *app) +{ + if (app) { + if (app->app_eaw) + g_array_free (app->app_eaw, TRUE); + if (app->app_cfg) + g_array_free (app->app_cfg, TRUE); + } + + g_free (app); +} + +static gboolean +validate_option (const char *name) +{ + guint i; + + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (name[0] != '\0', FALSE); + + for (i = 0; i < G_N_ELEMENTS (dcb_commands); i++) { + if (g_strcmp0 (dcb_commands[i], name) == 0) + return TRUE; + } + return FALSE; +} + +static gboolean +nm_setting_dcb_add_option (GHashTable *table, const char *key, const char *value) +{ + size_t value_len; + + g_return_val_if_fail (table != NULL, FALSE); + g_return_val_if_fail (validate_option (key), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + + + value_len = strlen (value); + g_return_val_if_fail (value_len > 0 && value_len < 200, FALSE); + + g_hash_table_insert (table, g_strdup (key), g_strdup (value)); + + return TRUE; +} + +static GHashTable * +handle_dcb_options (const char *dcb_options_type, char *dcb_options) +{ + GHashTable *table = NULL; + char *value = dcb_options; + char **items, **iter; + int ret = 0; + + table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + items = g_strsplit_set (value, " ", -1); + for (iter = items; iter && *iter; iter++) { + if (strlen (*iter)) { + char **keys, *key, *val; + + keys = g_strsplit_set (*iter, ":", 2); + if (keys && *keys) { + key = *keys; + val = *(keys + 1); + if (val && strlen(key) && strlen(val)) + if (!nm_setting_dcb_add_option (table, key, val)) + ret = 1; + } + + g_strfreev (keys); + } + } + g_strfreev (items); + + if (ret) { + g_hash_table_destroy (table); + table = NULL; + } + + return table; +} + +/*********************************************************************/ + +static guint8 dcb_not_supplied = DCB_NOT_SUPPLIED; + +static void +nm_setting_dcb_init (NMSettingDcb *setting) +{ + g_object_set (setting, NM_SETTING_NAME, NM_SETTING_DCB_SETTING_NAME, NULL); +} + +static guint8 +dcb_get_eaw_value (const char *value) +{ + gchar *eaw_enable = "1", *eaw_disable = "0"; + if (!g_strcmp0 (eaw_enable, value)) + return 1; + else if (!g_strcmp0 (eaw_disable, value)) + return 0; + else + return EAW_ERROR; +} + +static gboolean +dcb_get_eaw (GArray *array, GHashTable *table) +{ + int i = 0; + char *value; + const char *eaw_options[3] = {"e", "a", "w"}; + + g_return_val_if_fail (array != NULL, FALSE); + g_return_val_if_fail (table != NULL, FALSE); + + for (i = 0; i < EAW_SIZE; ++i) { + guint8 eaw_value = DCB_NOT_SUPPLIED; + value = NULL; + value = g_hash_table_lookup (table, eaw_options[i]); + if (value) { + eaw_value = dcb_get_eaw_value (value); + if (eaw_value != EAW_ERROR) + g_array_append_val (array, eaw_value); + else + return FALSE; + } else + g_array_append_val (array, eaw_value); + } + + return TRUE; +} + +static gboolean +dcb_get_app_cfg (GArray *array, const char *value) +{ + const char *num1, *num2; + + g_return_val_if_fail (array != NULL, FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (strlen (value) == 2, FALSE); + + num1 = value; + num2 = num1 + 1; + if (isxdigit (*num1) && isxdigit (*num2)) { + guint8 num = *num1 - 0x30; + g_array_append_val (array, num); + num = *num2 - 0x30; + g_array_append_val (array, num); + return TRUE; + } else + return FALSE; +} + +static void +dcb_initialize_array (GArray *array, int size, guint8 value) +{ + int i; + + for (i = 0; i < size; ++i) + g_array_append_val (array, value); +} + +static gboolean +dcb_test_array_value (GArray *array, int size, guint8 value) +{ + int i; + + for (i = 0; i < size; ++i) + if (g_array_index (array, guint8, i) != value) + return FALSE; + + return TRUE; +} + +static gboolean +dcb_parse_app_options (NMSettingDcb *setting, char *options) +{ + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + int i = 0; + char *value; + const char *app_subtype_array[APP_STYPE_MAX+1] = {"fcoe", "iscsi", "fip"}; + GHashTable *table = NULL; + + if (!(table = handle_dcb_options (NM_SETTING_DCB_APP_OPTIONS, options))) + return FALSE; + + dcb_app_free (priv->app); + priv->app = dcb_app_malloc (); + if (!priv->app) + return FALSE; + + /* app subtype */ + priv->app->app_subtype = APP_STYPE_ERROR; + value = NULL; + value = g_hash_table_lookup (table, "app"); + if (!value) + return FALSE; + for (i = 0; i < APP_STYPE_MAX + 1; ++i) { + gchar str[2]; + g_sprintf(str, "%d", i); + if (!g_strcmp0 (value, app_subtype_array[i]) || !g_strcmp0 (value, str)) { + priv->app->app_subtype = i; + break; + } + } + + if (priv->app->app_subtype == APP_STYPE_ERROR) + return FALSE; + + /* app cfg */ + value = NULL; + value = g_hash_table_lookup (table, "appcfg"); + if (value) + dcb_get_app_cfg (priv->app->app_cfg, value); + else + dcb_initialize_array (priv->app->app_cfg, DCB_MAX_USER_PRIORITIES, dcb_not_supplied); + + /* app eaw */ + dcb_get_eaw (priv->app->app_eaw, table); + + g_hash_table_destroy (table); + return TRUE; +} + +/* Now we skip priority group settings */ +static gboolean +dcb_parse_pg_options (NMSettingDcb *setting, char *options) +{ + return TRUE; +} + +static gboolean +dcb_parse_pfc_options (NMSettingDcb *setting, char *options) +{ + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + int i = 0; + char *value; + GHashTable *table = NULL; + + if (!(table = handle_dcb_options (NM_SETTING_DCB_PFC_OPTIONS, options))) + return FALSE; + + if (priv->pfc_eaw) + g_array_free (priv->pfc_eaw, TRUE); + if (priv->pfc_up) + g_array_free (priv->pfc_up, TRUE); + priv->pfc_eaw = g_array_new (FALSE, TRUE, EAW_ELEMENT_SIZE); + if (!priv->pfc_eaw) + return FALSE; + + priv->pfc_up = g_array_new (FALSE, TRUE, PFC_UP_ELEMENT_SIZE); + if (!priv->pfc_up) { + g_array_free (priv->pfc_eaw, TRUE); + return FALSE; + } + + /* pfcup */ + value = NULL; + value = g_hash_table_lookup (table, "pfcup"); + if (value) { + if (value && strlen (value) == 8) { + const char *p = value; + for (i = 0; i < 8; ++i) { + if (*p >= 0) { + guint8 pfcup_value = *p - 0x30; + g_array_append_val (priv->pfc_up, pfcup_value); + } else + g_array_append_val (priv->pfc_up, dcb_not_supplied); + p++; + } + } + } else + dcb_initialize_array (priv->pfc_up, DCB_MAX_USER_PRIORITIES, dcb_not_supplied); + + /* pfc eaw*/ + dcb_get_eaw (priv->pfc_eaw, table); + + g_hash_table_destroy (table); + + return TRUE; +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingDcb *setting = NM_SETTING_DCB (object); + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_INTERFACE_NAME: + priv->interface_name = g_value_dup_string (value); + break; + + case PROP_DCB_ENABLED: + priv->dcb_enabled = g_value_get_boolean (value); + break; + + case PROP_APP_OPTIONS: + g_free (priv->app_options); + priv->app_options = g_value_dup_string (value); + if (!dcb_parse_app_options (setting, priv->app_options)) + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + + case PROP_PG_OPTIONS: + g_free (priv->pg_options); + priv->pg_options = g_value_dup_string (value); + if (!dcb_parse_pg_options (setting, priv->pg_options)) + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + + case PROP_PFC_OPTIONS: + g_free (priv->pfc_options); + priv->pfc_options = g_value_dup_string (value); + if (!dcb_parse_pfc_options (setting, priv->pfc_options)) + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingDcb *setting = NM_SETTING_DCB (object); + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + + switch (prop_id) { + case PROP_INTERFACE_NAME: + g_value_set_string (value, nm_setting_dcb_get_interface_name (setting)); + break; + + case PROP_DCB_ENABLED: + g_value_set_boolean (value, priv->dcb_enabled); + break; + + case PROP_APP_OPTIONS: + g_value_set_string (value, priv->app_options); + break; + + case PROP_PG_OPTIONS: + g_value_set_string (value, priv->pg_options); + break; + + case PROP_PFC_OPTIONS: + g_value_set_string (value, priv->pfc_options); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + NMSettingDcb *setting = NM_SETTING_DCB (object); + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + + dcb_app_free (priv->app); + if (priv->pfc_eaw) + g_array_free (priv->pfc_eaw, TRUE); + if (priv->pfc_up) + g_array_free (priv->pfc_up, TRUE); +} + +static gboolean +dcb_verify_eaw (GArray *arr) +{ + int i; + if (!arr) + return FALSE; + if (arr->len != EAW_SIZE) + return FALSE; + for (i = 0; i < EAW_SIZE; ++i) { + guint8 value; + value = g_array_index (arr, guint8, i); + if ((value != 0) && (value != 1) && (value != EAW_ERROR)) + return FALSE; + } + + return TRUE; +} + +static gboolean +dcb_verify_app_cfg (GArray *arr) +{ + if (!arr) + return FALSE; + + if (arr->len != APP_CFG_SIZE) + return FALSE; + + if ((g_array_index (arr, guint8, 0) > 15) + || (g_array_index (arr, guint8, 1) > 15)) + return FALSE; + + return TRUE; +} + +static gboolean +dcb_verify_app (dcb_app *app, GError **error) +{ + if (!app) { + g_set_error (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_MISSING_PROPERTY, + NM_SETTING_DCB_APP_OPTIONS); + return FALSE; + } + + if (app->app_subtype == APP_STYPE_ERROR) { + g_set_error (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + NM_SETTING_DCB_APP_OPTIONS); + return FALSE; + } + + if (!app->app_eaw) { + g_set_error (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_MISSING_PROPERTY, + NM_SETTING_DCB_APP_OPTIONS); + return FALSE; + } else if (!dcb_verify_eaw (app->app_eaw)) { + g_set_error (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + NM_SETTING_DCB_APP_OPTIONS); + return FALSE; + } + + if (!dcb_verify_app_cfg (app->app_cfg)) { + g_set_error (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + NM_SETTING_DCB_APP_OPTIONS); + return FALSE; + } + + /* missing appcfg and app eaw*/ + if (dcb_test_array_value (app->app_cfg, APP_CFG_SIZE, DCB_NOT_SUPPLIED) + && dcb_test_array_value (app->app_eaw, EAW_SIZE, DCB_NOT_SUPPLIED)) { + g_set_error (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_MISSING_PROPERTY, + NM_SETTING_DCB_APP_OPTIONS); + return FALSE; + } + + return TRUE; +} + +static gboolean +verify (NMSetting *setting, GSList *all_settings, GError **error) +{ + NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting); + int i; + + /* verify app setting */ + if (!dcb_verify_app (priv->app, error)) + return FALSE; + + /* verify pfc setting */ + if (!priv->pfc_eaw) { + g_set_error (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_MISSING_PROPERTY, + NM_SETTING_DCB_PFC_OPTIONS); + return FALSE; + } else if (!dcb_verify_eaw (priv->pfc_eaw)) { + g_set_error (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + NM_SETTING_DCB_PFC_OPTIONS); + return FALSE; + } + + if (priv->pfc_up) { + for (i = 0; i < DCB_MAX_USER_PRIORITIES; ++i) { + guint8 value = g_array_index (priv->pfc_up, guint8, i); + if (value != 0 && value != 1 && value != DCB_NOT_SUPPLIED) { + g_set_error (error, + NM_SETTING_DCB_ERROR, + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, + NM_SETTING_DCB_PFC_OPTIONS); + return FALSE; + } + } + } + + /* missing pfcup and pfc eaw*/ + if (dcb_test_array_value (priv->pfc_eaw, EAW_SIZE, DCB_NOT_SUPPLIED) + && dcb_test_array_value (priv->pfc_up, DCB_MAX_USER_PRIORITIES, DCB_NOT_SUPPLIED)) + return FALSE; + + return TRUE; +} + +static void +nm_setting_dcb_class_init (NMSettingDcbClass *setting_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (setting_class); + NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + + g_type_class_add_private (setting_class, sizeof (NMSettingDcbPrivate)); + + /* virtual methods */ + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + parent_class->verify = verify; + + /* Properties */ + /** + * NMSettingDcb:interface-name: + * + * The name of the network interface + **/ + g_object_class_install_property + (object_class, PROP_INTERFACE_NAME, + g_param_spec_string (NM_SETTING_DCB_INTERFACE_NAME, + "InterfaceName", + "The name of the network interface", + NULL, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + + g_object_class_install_property + (object_class, PROP_DCB_ENABLED, + g_param_spec_boolean (NM_SETTING_DCB_ENABLED, + "DCB enabled", + "Is DCB enabled", + TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_SERIALIZE)); + + g_object_class_install_property + (object_class, PROP_APP_OPTIONS, + g_param_spec_string (NM_SETTING_DCB_APP_OPTIONS, + "App Options", + "Dictionary of key/value pairs of DCB app options." + "Both keys and values must be strings." + "Option names must contain only " + "alphanumeric characters (ie, [a-zA-Z0-9]).", + NULL, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + + g_object_class_install_property + (object_class, PROP_PG_OPTIONS, + g_param_spec_string (NM_SETTING_DCB_PG_OPTIONS, + "Priority Group Options", + "Dictionary of key/value pairs of DCB priority group options." + "Both keys and values must be strings." + "Option names must contain only " + "alphanumeric characters (ie, [a-zA-Z0-9]).", + NULL, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); + + g_object_class_install_property + (object_class, PROP_PFC_OPTIONS, + g_param_spec_string (NM_SETTING_DCB_PFC_OPTIONS, + "Priority Flow Control Options", + "Dictionary of key/value pairs of DCB priority flow control options." + "Both keys and values must be strings." + "Option names must contain only " + "alphanumeric characters (ie, [a-zA-Z0-9]).", + NULL, + G_PARAM_READWRITE | NM_SETTING_PARAM_SERIALIZE)); +} diff --git a/libnm-util/nm-setting-dcb.h b/libnm-util/nm-setting-dcb.h new file mode 100644 index 0000000..6148a67 --- /dev/null +++ b/libnm-util/nm-setting-dcb.h @@ -0,0 +1,135 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * Weiping Pan <[email protected]> + * + * 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. + * + * (C) Copyright 2012 Red Hat, Inc. + */ + +#ifndef NM_SETTING_DCB_H +#define NM_SETTING_DCB_H + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_DCB (nm_setting_dcb_get_type ()) +#define NM_SETTING_DCB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_DCB, NMSettingDcb)) +#define NM_SETTING_DCB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_DCBCONFIG, NMSettingDcbClass)) +#define NM_IS_SETTING_DCB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_DCB)) +#define NM_IS_SETTING_DCB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SETTING_DCB)) +#define NM_SETTING_DCB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_DCB, NMSettingDcbClass)) + +#define NM_SETTING_DCB_SETTING_NAME "dcb" + +/** + * NMSettingDcbError: + * @NM_SETTING_DCB_ERROR_UNKNOWN: unknown or unclassified error + * @NM_SETTING_DCB_ERROR_INVALID_PROPERTY: the property was invalid + * @NM_SETTING_DCB_ERROR_MISSING_PROPERTY: the property was missing and is + * required + */ +typedef enum { + NM_SETTING_DCB_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/ + NM_SETTING_DCB_ERROR_INVALID_PROPERTY, /*< nick=InvalidProperty >*/ + NM_SETTING_DCB_ERROR_MISSING_PROPERTY /*< nick=MissingProperty >*/ +} NMSettingDcbError; + +#define NM_SETTING_DCB_ERROR nm_setting_dcb_error_quark () +GQuark nm_setting_dcb_error_quark (void); + +/* + * What does eaw (EAW) mean ? + * e:<0|1> + * controls feature enable + * + * a:<0|1> + * controls whether the feature is advertised via DCBX to peer + * + * w:<0|1> + * controls whether the feature is willing to change operational + * configuration based on what is received from the peer + * */ + +#define NM_SETTING_DCB_INTERFACE_NAME "interface-name" +#define NM_SETTING_DCB_ENABLED "dcb-enabled" +#define NM_SETTING_DCB_APP_OPTIONS "app-options" +#define NM_SETTING_DCB_PG_OPTIONS "pg-options" +#define NM_SETTING_DCB_PFC_OPTIONS "pfc-options" + +#define NM_SETTING_DCB_OPTION_EAW_E "e" +#define NM_SETTING_DCB_OPTION_EAW_A "a" +#define NM_SETTING_DCB_OPTION_EAW_W "w" +#define NM_SETTING_DCB_OPTION_APP "app" +#define NM_SETTING_DCB_OPTION_APPCFG "appcfg" +#define NM_SETTING_DCB_OPTION_PFCUP "pfcup" + +#define DCB_MAX_USER_PRIORITIES 8 +#define DCB_MAX_PRIORITY_GROUPS 8 +#define DCB_UNLIMITED_PRIORITY_GROUP 'F' +#define DCB_MAX_TRAFFIC_CLASSES 8 +#define DCB_LINK_STRICT_PGID 15 +#define DCB_NOT_SUPPLIED 'x' + +/* APP */ +#define APP_FCOE_STYPE 0 +#define APP_ISCSI_STYPE 1 +#define APP_FIP_STYPE 2 +#define APP_STYPE_MAX APP_FIP_STYPE +#define APP_STYPE_ERROR -1 + +#define EAW_ELEMENT_SIZE 1 +#define EAW_ERROR 'F' +#define EAW_SIZE 3 +#define APP_CFG_ELEMENT_SIZE 1 +#define APP_CFG_SIZE 2 +#define PFC_UP_ELEMENT_SIZE 1 + +typedef struct { + gint32 app_subtype; + GArray *app_eaw; + GArray *app_cfg; +} dcb_app; + +typedef struct { + NMSetting parent; +} NMSettingDcb; + +typedef struct { + NMSettingClass parent; + + /* Padding for future expansion */ + void (*_reserved1) (void); + void (*_reserved2) (void); + void (*_reserved3) (void); + void (*_reserved4) (void); +} NMSettingDcbClass; + +GType nm_setting_dcb_get_type (void); +NMSetting * nm_setting_dcb_new (void); + +const char * nm_setting_dcb_get_interface_name (NMSettingDcb *setting); +gboolean nm_setting_dcb_get_dcb_enabled (NMSettingDcb *setting); +dcb_app * nm_setting_dcb_get_app (NMSettingDcb *setting); +guint32 nm_setting_dcb_get_num_pfc_eaw (NMSettingDcb *setting); +guint8 nm_setting_dcb_get_pfc_eaw (NMSettingDcb *setting, guint32 idx); +guint32 nm_setting_dcb_get_num_pfc_up (NMSettingDcb *setting); +guint8 nm_setting_dcb_get_pfc_up (NMSettingDcb *setting, guint32 idx); + +G_END_DECLS +#endif /* NM_SETTING_DCB_H */ diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c index 6aa97f1..0e552d6 100644 --- a/src/settings/plugins/ifcfg-rh/reader.c +++ b/src/settings/plugins/ifcfg-rh/reader.c @@ -21,6 +21,7 @@ #include <config.h> #include <stdlib.h> #include <string.h> +#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> @@ -45,6 +46,7 @@ #include <nm-setting-wireless.h> #include <nm-setting-8021x.h> #include <nm-setting-bond.h> +#include <nm-setting-dcb.h> #include <nm-utils.h> #include "wifi-utils.h" @@ -55,6 +57,7 @@ #include "utils.h" #include "reader.h" +#include "nm-dbus-glib-types.h" #define PLUGIN_PRINT(pname, fmt, args...) \ { g_message (" " pname ": " fmt, ##args); } @@ -1647,6 +1650,53 @@ error: return NULL; } +static NMSetting * +make_dcb_setting (shvarFile *ifcfg, + const char *network_file, + GError **error) +{ + NMSettingDcb *s_dcb = NULL; + char *value; + char *iface_name = NULL; + gboolean dcb_enabled = TRUE; + int i; + const char *ifcfg_dcb_options[3] = {"DCB_APP_OPTS", "DCB_PG_OPTS", "DCB_PFC_OPTS"}; + const char *DCB_OPTIONS[3] = { NM_SETTING_DCB_APP_OPTIONS, + NM_SETTING_DCB_PG_OPTIONS, + NM_SETTING_DCB_PFC_OPTIONS }; + + s_dcb = (NMSettingDcb *) nm_setting_dcb_new (); + if (!s_dcb) { + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, + "Could not allocate DCB setting"); + return NULL; + } + + iface_name = svGetValue (ifcfg, "DEVICE", FALSE); + if (!iface_name) { + g_set_error (error, IFCFG_PLUGIN_ERROR, 0, + "Missing DEVICE property"); + goto error_return; + } + + g_object_set (s_dcb, NM_SETTING_DCB_INTERFACE_NAME, iface_name, NULL); + + g_object_set (s_dcb, NM_SETTING_DCB_ENABLED, dcb_enabled, NULL); + + for (i = 0; i < 3; ++i) { + value = svGetValue (ifcfg, ifcfg_dcb_options[i], FALSE); + if (value) + g_object_set (s_dcb, DCB_OPTIONS[i], value, NULL); + } + + return (NMSetting *) s_dcb; + +error_return: + g_object_unref (s_dcb); + + return NULL; +} + static gboolean add_one_wep_key (shvarFile *ifcfg, const char *shvar_key, @@ -3924,7 +3974,7 @@ connection_from_file (const char *filename, NMConnection *connection = NULL; shvarFile *parsed; char *type, *nmc = NULL, *bootproto, *tmp; - NMSetting *s_ip4, *s_ip6; + NMSetting *s_ip4, *s_ip6, *s_dcb; const char *ifcfg_name = NULL; gboolean nm_controlled = TRUE; gboolean can_disable_ip4 = FALSE; @@ -4110,6 +4160,18 @@ connection_from_file (const char *filename, } else if (s_ip4) nm_connection_add_setting (connection, s_ip4); + tmp = svGetValue (parsed, "DCB", FALSE); + if (tmp && !strcasecmp (tmp, "on")) { + g_free (tmp); + s_dcb = make_dcb_setting (parsed, network_file, &error); + if (error) { + g_object_unref (connection); + connection = NULL; + goto done; + } else if (s_dcb) { + nm_connection_add_setting (connection, s_dcb); + } + } /* iSCSI / ibft connections are read-only since their settings are * stored in NVRAM and can only be changed in BIOS. */ diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb new file mode 100644 index 0000000..5960ba7 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dcb @@ -0,0 +1,14 @@ +TYPE=Ethernet +DEVICE=eth0 +HWADDR=00:11:22:33:44:ee +BOOTPROTO=none +IPADDR=1.2.3.4 +PREFIX=24 +GATEWAY=1.1.1.1 + +# dcb global switch +# use DCB=off to turn it off +DCB=on +DCB_APP_OPTS="app:fcoe appcfg:08 e:1 a:1 w:1" +DCB_PG_OPTS="pgid:0000111f pgpct:25,75,0,0,0,0,0,0 e:1 a:1 w:1" +DCB_PFC_OPTS="pfcup:00010000 e:1 a:1 w:1" diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index f5cf32e..4e03530 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -45,6 +45,7 @@ #include <nm-setting-cdma.h> #include <nm-setting-serial.h> #include <nm-setting-vlan.h> +#include <nm-setting-dcb.h> #include "nm-test-helpers.h" @@ -12834,6 +12835,46 @@ test_write_infiniband (void) g_object_unref (reread); } +#define TEST_IFCFG_DCB_INTERFACE TEST_IFCFG_DIR"/network-scripts/ifcfg-test-dcb" + +static void +test_read_dcb_interface (void) +{ + NMConnection *connection; + char *unmanaged = NULL; + char *keyfile = NULL; + char *routefile = NULL; + char *route6file = NULL; + gboolean ignore_error = FALSE; + GError *error = NULL; + NMSettingDcb *s_dcb; + + connection = connection_from_file (TEST_IFCFG_DCB_INTERFACE, + NULL, + TYPE_ETHERNET, + NULL, + &unmanaged, + &keyfile, + &routefile, + &route6file, + &error, + &ignore_error); + + g_assert_no_error (error); + ASSERT (connection != NULL, + "infiniband-write-reread", "failed to read %s: %s", keyfile, error->message); + + g_free (unmanaged); + g_free (keyfile); + g_free (routefile); + g_free (route6file); + s_dcb = nm_connection_get_setting_dcb (connection); + g_assert (s_dcb); + + nm_connection_dump(connection); + g_object_unref (connection); +} + #define TEST_IFCFG_WIFI_OPEN_SSID_BAD_HEX TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-bad-hex" #define TEST_IFCFG_WIFI_OPEN_SSID_LONG_QUOTED TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-quoted" #define TEST_IFCFG_WIFI_OPEN_SSID_LONG_HEX TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wifi-open-ssid-long-hex" @@ -12861,6 +12902,9 @@ int main (int argc, char **argv) if (!nm_utils_init (&error)) FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message); + test_read_dcb_interface (); + return 0; + /* The tests */ test_read_unmanaged (); test_read_minimal (); -- 1.7.7.6 _______________________________________________ networkmanager-list mailing list [email protected] https://mail.gnome.org/mailman/listinfo/networkmanager-list
