2 * collectd - src/ovs_link.c
4 * Copyright(c) 2016 Intel Corporation. All rights reserved.
6 * Permission is hereby granted, free of charge, to any person obtaining a copy of
7 * this software and associated documentation files (the "Software"), to deal in
8 * the Software without restriction, including without limitation the rights to
9 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10 * of the Software, and to permit persons to whom the Software is furnished to do
11 * so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * Volodymyr Mytnyk <volodymyrx.mytnyk@intel.com>
28 #include "common.h" /* auxiliary functions */
29 #include "utils_ovs.h" /* OVS helpers */
31 #define OVS_LINK_PLUGIN "ovs_link"
32 #define OVS_LINK_DEFAULT_OVS_DB_SERVER_URL "tcp:127.0.0.1:6640"
33 #define CONFIG_LOCK for (int __i = config_lock(); __i != 0 ; \
34 __i = config_unlock())
37 char *name; /* interface name */
38 struct interface_s *next; /* next interface name */
40 typedef struct interface_s interface_t;
42 struct ovs_link_config_s {
43 pthread_mutex_t mutex; /* mutex to lock the config structure */
44 char *ovs_db_server_url; /* OVS DB server URL */
45 ovs_db_t *ovs_db; /* pointer to OVS DB instance */
46 interface_t *ifaces; /* interface names */
48 typedef struct ovs_link_config_s ovs_link_config_t;
53 ovs_link_config_t config = {PTHREAD_MUTEX_INITIALIZER, NULL, NULL, NULL};
55 /* This function is used only by "CONFIG_LOCK" defined above.
56 * It always returns 1 when the config is locked.
61 pthread_mutex_lock(&config.mutex);
65 /* This function is used only by "CONFIG_LOCK" defined above.
66 * It always returns 0 when config is unlocked.
71 pthread_mutex_unlock(&config.mutex);
75 /* Check if given interface name exists in configuration file. It
76 * returns 1 if exists otherwise 0. If no interfaces are configured,
80 ovs_link_config_iface_exists(const char *ifname)
84 if (!(rc = (config.ifaces == NULL))) {
85 for (interface_t *iface = config.ifaces; iface; iface = iface->next)
86 if (rc = (strcmp(ifname, iface->name) == 0))
93 /* Release memory allocated for configuration data */
95 ovs_link_config_free()
97 interface_t *del_iface = NULL;
99 sfree(config.ovs_db_server_url);
100 while (config.ifaces) {
101 del_iface = config.ifaces;
102 config.ifaces = config.ifaces->next;
103 free(del_iface->name);
109 /* Parse plugin configuration file and store the config
110 * in allocated memory. Returns negative value in case of error.
113 ovs_link_plugin_config(oconfig_item_t *ci)
115 interface_t *new_iface;
119 for (int i = 0; i < ci->children_num; i++) {
120 oconfig_item_t *child = ci->children + i;
121 if (strcasecmp("OvsDbServerUrl", child->key) == 0) {
122 if (cf_util_get_string(child, &ovs_db_url) < 0) {
123 ERROR(OVS_LINK_PLUGIN ": parse '%s' option failed", child->key);
126 config.ovs_db_server_url = ovs_db_url;
127 } else if (strcasecmp("Interfaces", child->key) == 0) {
128 for (int j = 0; j < child->values_num; j++) {
129 /* check value type */
130 if (child->values[j].type != OCONFIG_TYPE_STRING) {
131 ERROR(OVS_LINK_PLUGIN
132 ": given interface name is not a string [idx=%d]", j);
137 if ((if_name = strdup(child->values[j].value.string)) == NULL) {
138 ERROR(OVS_LINK_PLUGIN " strdup() copy interface name fail");
142 if ((new_iface = malloc(sizeof(*new_iface))) == NULL) {
143 ERROR(OVS_LINK_PLUGIN ": malloc () copy interface name fail");
146 /* store interface name */
147 new_iface->name = if_name;
148 new_iface->next = config.ifaces;
150 config.ifaces = new_iface;
152 DEBUG(OVS_LINK_PLUGIN ": found monitored interface \"%s\"",
157 ERROR(OVS_LINK_PLUGIN ": option '%s' is not allowed here", child->key);
164 ovs_link_config_free();
168 /* Dispatch OVS interface link status event to collectd */
170 ovs_link_dispatch_notification(const char *link_name, const char *link_state)
172 notification_t n = {NOTIF_FAILURE, time(NULL), "", "", OVS_LINK_PLUGIN,
175 /* fill the notification data */
176 if (link_state != NULL)
177 n.severity = ((strcmp(link_state, "up") == 0) ?
178 NOTIF_OKAY : NOTIF_WARNING);
180 link_state = "UNKNOWN";
182 sstrncpy(n.host, hostname_g, sizeof(n.host));
183 ssnprintf(n.message, sizeof(n.message),
184 "link state of \"%s\" interface has been changed to \"%s\"",
185 link_name, link_state);
187 /* send the notification */
188 return plugin_dispatch_notification(&n);
191 /* Process OVS DB update table event. It handles link status update event(s)
192 * and dispatches the value(s) to collectd if interface name matches one of
193 * interfaces specified in configuration file.
196 ovs_link_table_update_cb(yajl_val jupdates)
198 yajl_val jnew_val = NULL;
199 yajl_val jupdate = NULL;
200 yajl_val jrow_update = NULL;
201 yajl_val jlink_name = NULL;
202 yajl_val jlink_state = NULL;
203 const char *link_name = NULL;
205 /* JSON "Interface" table update example:
206 * ---------------------------------
209 * "9adf1db2-29ca-4140-ab22-ae347a4484de":
218 * "link_state":"down"
224 if (!YAJL_IS_OBJECT(jupdates) || !(YAJL_GET_OBJECT(jupdates)->len > 0)) {
225 ERROR(OVS_LINK_PLUGIN ": unexpected OVS DB update event received");
228 /* verify if this is a table event */
229 jupdate = YAJL_GET_OBJECT(jupdates)->values[0];
230 if (!YAJL_IS_OBJECT(jupdate)) {
231 ERROR(OVS_LINK_PLUGIN ": unexpected table update event received");
234 /* go through all row updates */
235 for (int row_index = 0; row_index < YAJL_GET_OBJECT(jupdate)->len;
237 jrow_update = YAJL_GET_OBJECT(jupdate)->values[row_index];
239 /* check row update */
240 jnew_val = ovs_utils_get_value_by_key(jrow_update, "new");
241 if (jnew_val == NULL) {
242 ERROR(OVS_LINK_PLUGIN ": unexpected row update received");
245 /* get link status update */
246 jlink_name = ovs_utils_get_value_by_key(jnew_val, "name");
247 jlink_state = ovs_utils_get_value_by_key(jnew_val, "link_state");
248 if (jlink_name && jlink_state) {
249 link_name = YAJL_GET_STRING(jlink_name);
250 if (link_name && ovs_link_config_iface_exists(link_name)) {
251 /* dispatch notification */
252 ovs_link_dispatch_notification(link_name,
253 YAJL_GET_STRING(jlink_state));
259 /* Process OVS DB result table callback. It handles init link status value
260 * and dispatches the value(s) to collectd. The logic to handle init status
261 * is same as 'ovs_link_table_update_cb'.
264 ovs_link_table_result_cb(yajl_val jresult, yajl_val jerror)
267 /* jerror is not used as it is the same all the time
268 (rfc7047, "Monitor" section, return value) */
269 ovs_link_table_update_cb(jresult);
272 /* Setup OVS DB table callback. It subscribes to 'Interface' tables
273 * to receive link status events.
276 ovs_link_initialize(ovs_db_t *pdb)
279 const char tb_name[] = "Interface";
280 const char *columns[] = {"name", "link_state", NULL};
282 /* register the update callback */
283 ret = ovs_db_table_cb_register(pdb, tb_name, columns,
284 ovs_link_table_update_cb,
285 ovs_link_table_result_cb,
286 OVS_DB_TABLE_CB_FLAG_MODIFY |
287 OVS_DB_TABLE_CB_FLAG_INITIAL);
289 ERROR(OVS_LINK_PLUGIN ": register OVS DB update callback failed");
293 DEBUG(OVS_LINK_PLUGIN ": OVS DB has been initialized");
296 /* Set default config values (update config) if some of them aren't
297 * specified in configuration file
300 ovs_link_config_set_default()
302 if (!config.ovs_db_server_url)
303 config.ovs_db_server_url = strdup(OVS_LINK_DEFAULT_OVS_DB_SERVER_URL);
304 return (config.ovs_db_server_url == NULL);
307 /* Initialize OVS plugin */
309 ovs_link_plugin_init(void)
311 ovs_db_t *ovs_db = NULL;
312 ovs_db_callback_t cb = {.init_cb = ovs_link_initialize};
314 if (ovs_link_config_set_default()) {
315 ERROR(OVS_LINK_PLUGIN ": fail to make configuration");
316 ovs_link_config_free();
320 /* initialize OVS DB */
321 if ((ovs_db = ovs_db_init(config.ovs_db_server_url, &cb)) == NULL) {
322 ERROR(OVS_LINK_PLUGIN ": fail to connect to OVS DB server");
323 ovs_link_config_free();
327 /* store OVSDB handler */
329 config.ovs_db = ovs_db;
332 DEBUG(OVS_LINK_PLUGIN ": plugin has been initialized");
336 /* Shutdown OVS plugin */
338 ovs_link_plugin_shutdown(void)
340 /* release memory allocated for config */
341 ovs_link_config_free();
344 if (ovs_db_destroy(config.ovs_db))
345 ERROR(OVS_LINK_PLUGIN ": OVSDB object destroy failed");
347 DEBUG(OVS_LINK_PLUGIN ": plugin has been destroyed");
351 /* Register OVS plugin callbacks */
353 module_register(void)
355 plugin_register_complex_config(OVS_LINK_PLUGIN, ovs_link_plugin_config);
356 plugin_register_init(OVS_LINK_PLUGIN, ovs_link_plugin_init);
357 plugin_register_shutdown(OVS_LINK_PLUGIN, ovs_link_plugin_shutdown);