8c2cd12cb296f8ac3fc22ada9a6b2b8d266b0f9e
[collectd.git] / src / ovs_events.c
1 /**
2  * collectd - src/ovs_events.c
3  *
4  * Copyright(c) 2016 Intel Corporation. All rights reserved.
5  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
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
22  * SOFTWARE.
23  *
24  * Authors:
25  *   Volodymyr Mytnyk <volodymyrx.mytnyk@intel.com>
26  **/
27
28 #include "collectd.h"
29
30 #include "common.h" /* auxiliary functions */
31
32 #include "utils_ovs.h" /* OVS helpers */
33
34 #define OVS_EVENTS_IFACE_NAME_SIZE 128
35 #define OVS_EVENTS_IFACE_UUID_SIZE 64
36 #define OVS_EVENTS_EXT_IFACE_ID_SIZE 64
37 #define OVS_EVENTS_EXT_VM_UUID_SIZE 64
38 #define OVS_EVENTS_PLUGIN "ovs_events"
39 #define OVS_EVENTS_CTX_LOCK                                                    \
40   for (int __i = ovs_events_ctx_lock(); __i != 0; __i = ovs_events_ctx_unlock())
41
42 /* Link status type */
43 enum ovs_events_link_status_e { DOWN, UP };
44 typedef enum ovs_events_link_status_e ovs_events_link_status_t;
45
46 /* Interface info */
47 struct ovs_events_iface_info_s {
48   char name[OVS_EVENTS_IFACE_NAME_SIZE];           /* interface name */
49   char uuid[OVS_EVENTS_IFACE_UUID_SIZE];           /* interface UUID */
50   char ext_iface_id[OVS_EVENTS_EXT_IFACE_ID_SIZE]; /* external interface id */
51   char ext_vm_uuid[OVS_EVENTS_EXT_VM_UUID_SIZE];   /* external VM UUID */
52   ovs_events_link_status_t link_status;            /* interface link status */
53   struct ovs_events_iface_info_s *next;            /* next interface info */
54 };
55 typedef struct ovs_events_iface_info_s ovs_events_iface_info_t;
56
57 /* Interface list */
58 struct ovs_events_iface_list_s {
59   char name[OVS_EVENTS_IFACE_NAME_SIZE]; /* interface name */
60   struct ovs_events_iface_list_s *next;  /* next interface info */
61 };
62 typedef struct ovs_events_iface_list_s ovs_events_iface_list_t;
63
64 /* OVS events configuration data */
65 struct ovs_events_config_s {
66   _Bool send_notification;                 /* sent notification to collectd? */
67   char ovs_db_node[OVS_DB_ADDR_NODE_SIZE]; /* OVS DB node */
68   char ovs_db_serv[OVS_DB_ADDR_SERVICE_SIZE]; /* OVS DB service */
69   char ovs_db_unix[OVS_DB_ADDR_UNIX_SIZE];    /* OVS DB unix socket path */
70   ovs_events_iface_list_t *ifaces;            /* interface info */
71 };
72 typedef struct ovs_events_config_s ovs_events_config_t;
73
74 /* OVS events context type */
75 struct ovs_events_ctx_s {
76   pthread_mutex_t mutex;      /* mutex to lock the context */
77   ovs_db_t *ovs_db;           /* pointer to OVS DB instance */
78   ovs_events_config_t config; /* plugin config */
79   char *ovs_db_select_params; /* OVS DB select parameter request */
80   _Bool is_db_available;      /* specify whether OVS DB is available */
81 };
82 typedef struct ovs_events_ctx_s ovs_events_ctx_t;
83
84 /*
85  * Private variables
86  */
87 static ovs_events_ctx_t ovs_events_ctx = {
88     .mutex = PTHREAD_MUTEX_INITIALIZER,
89     .config = {.ovs_db_node = "localhost", /* use default OVS DB node */
90                .ovs_db_serv = "6640"}      /* use default OVS DB service */
91 };
92
93 /* Forward declaration */
94 static int ovs_events_plugin_read(user_data_t *u);
95
96 /* This function is used only by "OVS_EVENTS_CTX_LOCK" define (see above).
97  * It always returns 1 when context is locked.
98  */
99 static int ovs_events_ctx_lock() {
100   pthread_mutex_lock(&ovs_events_ctx.mutex);
101   return (1);
102 }
103
104 /* This function is used only by "OVS_EVENTS_CTX_LOCK" define (see above).
105  * It always returns 0 when context is unlocked.
106  */
107 static int ovs_events_ctx_unlock() {
108   pthread_mutex_unlock(&ovs_events_ctx.mutex);
109   return (0);
110 }
111
112 /* Check if given interface name exists in configuration file. It
113  * returns 1 if exists otherwise 0. If no interfaces are configured,
114  * -1 is returned
115  */
116 static int ovs_events_config_iface_exists(const char *ifname) {
117   if (ovs_events_ctx.config.ifaces == NULL)
118     return (-1);
119
120   /* check if given interface exists */
121   for (ovs_events_iface_list_t *iface = ovs_events_ctx.config.ifaces; iface;
122        iface = iface->next)
123     if (strcmp(ifname, iface->name) == 0)
124       return (1);
125
126   return (0);
127 }
128
129 /* Get OVS DB select parameter request based on rfc7047,
130  * "Transact" & "Select" section
131  */
132 static char *ovs_events_get_select_params() {
133   size_t buff_size = 0;
134   size_t buff_off = 0;
135   char *opt_buff = NULL;
136   static const char params_fmt[] = "[\"Open_vSwitch\"%s]";
137   static const char option_fmt[] =
138       ",{\"op\":\"select\",\"table\":\"Interface\","
139       "\"where\":[[\"name\",\"==\",\"%s\"]],"
140       "\"columns\":[\"link_state\",\"external_ids\","
141       "\"name\",\"_uuid\"]}";
142   static const char default_opt[] =
143       ",{\"op\":\"select\",\"table\":\"Interface\","
144       "\"where\":[],\"columns\":[\"link_state\","
145       "\"external_ids\",\"name\",\"_uuid\"]}";
146   /* setup OVS DB interface condition */
147   for (ovs_events_iface_list_t *iface = ovs_events_ctx.config.ifaces; iface;
148        iface = iface->next) {
149     /* allocate new buffer (format size + ifname len is good enough) */
150     buff_size += sizeof(option_fmt) + strlen(iface->name);
151     char *new_buff = realloc(opt_buff, buff_size);
152     if (new_buff == NULL) {
153       sfree(opt_buff);
154       return NULL;
155     }
156     opt_buff = new_buff;
157     int ret = ssnprintf(opt_buff + buff_off, buff_size - buff_off, option_fmt,
158                         iface->name);
159     if (ret < 0) {
160       sfree(opt_buff);
161       return NULL;
162     }
163     buff_off += ret;
164   }
165   /* if no interfaces are configured, use default params */
166   if (opt_buff == NULL)
167     if ((opt_buff = strdup(default_opt)) == NULL)
168       return NULL;
169
170   /* allocate memory for OVS DB select params */
171   size_t params_size = sizeof(params_fmt) + strlen(opt_buff);
172   char *params_buff = calloc(1, params_size);
173   if (params_buff == NULL) {
174     sfree(opt_buff);
175     return NULL;
176   }
177
178   /* create OVS DB select params */
179   if (ssnprintf(params_buff, params_size, params_fmt, opt_buff) < 0)
180     sfree(params_buff);
181
182   sfree(opt_buff);
183   return params_buff;
184 }
185
186 /* Release memory allocated for configuration data */
187 static void ovs_events_config_free() {
188   ovs_events_iface_list_t *del_iface = NULL;
189   sfree(ovs_events_ctx.ovs_db_select_params);
190   while (ovs_events_ctx.config.ifaces) {
191     del_iface = ovs_events_ctx.config.ifaces;
192     ovs_events_ctx.config.ifaces = ovs_events_ctx.config.ifaces->next;
193     sfree(del_iface);
194   }
195 }
196
197 /* Parse/process "Interfaces" configuration option. Returns 0 if success
198  * otherwise -1 (error)
199  */
200 static int ovs_events_config_get_interfaces(const oconfig_item_t *ci) {
201   for (int j = 0; j < ci->values_num; j++) {
202     /* check interface name type */
203     if (ci->values[j].type != OCONFIG_TYPE_STRING) {
204       ERROR(OVS_EVENTS_PLUGIN
205             ": given interface name is not a string [idx=%d]", j);
206       return (-1);
207     }
208     /* allocate memory for configured interface */
209     ovs_events_iface_list_t *new_iface = calloc(1, sizeof(*new_iface));
210     if (new_iface == NULL) {
211       ERROR(OVS_EVENTS_PLUGIN ": calloc () copy interface name fail");
212       return (-1);
213     } else {
214       /* store interface name */
215       sstrncpy(new_iface->name, ci->values[j].value.string,
216                sizeof(new_iface->name));
217       new_iface->next = ovs_events_ctx.config.ifaces;
218       ovs_events_ctx.config.ifaces = new_iface;
219       DEBUG(OVS_EVENTS_PLUGIN ": found monitored interface \"%s\"",
220             new_iface->name);
221     }
222   }
223   return (0);
224 }
225
226 /* Parse plugin configuration file and store the config
227  * in allocated memory. Returns negative value in case of error.
228  */
229 static int ovs_events_plugin_config(oconfig_item_t *ci) {
230   _Bool dispatch_values = 1;
231   for (int i = 0; i < ci->children_num; i++) {
232     oconfig_item_t *child = ci->children + i;
233     if (strcasecmp("SendNotification", child->key) == 0) {
234       if (cf_util_get_boolean(child,
235                               &ovs_events_ctx.config.send_notification) != 0) {
236         ovs_events_config_free();
237         return (-1);
238       }
239     } else if (strcasecmp("Address", child->key) == 0) {
240       if (cf_util_get_string_buffer(
241               child, ovs_events_ctx.config.ovs_db_node,
242               sizeof(ovs_events_ctx.config.ovs_db_node)) != 0) {
243         ovs_events_config_free();
244         return (-1);
245       }
246     } else if (strcasecmp("Port", child->key) == 0) {
247       char *service = NULL;
248       if (cf_util_get_service(child, &service) != 0) {
249         ovs_events_config_free();
250         return (-1);
251       }
252       strncpy(ovs_events_ctx.config.ovs_db_serv, service,
253               sizeof(ovs_events_ctx.config.ovs_db_serv));
254       sfree(service);
255     } else if (strcasecmp("Socket", child->key) == 0) {
256       if (cf_util_get_string_buffer(
257               child, ovs_events_ctx.config.ovs_db_unix,
258               sizeof(ovs_events_ctx.config.ovs_db_unix)) != 0) {
259         ovs_events_config_free();
260         return (-1);
261       }
262     } else if (strcasecmp("Interfaces", child->key) == 0) {
263       if (ovs_events_config_get_interfaces(child) != 0) {
264         ovs_events_config_free();
265         return (-1);
266       }
267     } else if (strcasecmp("DispatchValues", child->key) == 0) {
268       if (cf_util_get_boolean(child, &dispatch_values) != 0) {
269         ovs_events_config_free();
270         return (-1);
271       }
272     } else {
273       ERROR(OVS_EVENTS_PLUGIN ": option '%s' is not allowed here", child->key);
274       ovs_events_config_free();
275       return (-1);
276     }
277   }
278   /* Check and warn about invalid configuration */
279   if (!ovs_events_ctx.config.send_notification && !dispatch_values) {
280       WARNING(OVS_EVENTS_PLUGIN ": send notification and dispatch values "
281               "options are disabled. No information will be dispatched by the "
282               "plugin. Please check your configuration");
283   }
284   /* Dispatch link status values if configured */
285   if (dispatch_values)
286     return plugin_register_complex_read(NULL, OVS_EVENTS_PLUGIN,
287                                         ovs_events_plugin_read, 0, NULL);
288
289   return (0);
290 }
291
292 /* Dispatch OVS interface link status event to collectd */
293 static void ovs_events_dispatch_notification(const ovs_events_iface_info_t *ifinfo) {
294   const char *msg_link_status = NULL;
295   notification_t n = {
296       NOTIF_FAILURE, cdtime(), "", "", OVS_EVENTS_PLUGIN, "", "", "", NULL};
297
298   /* convert link status to message string */
299   switch (ifinfo->link_status) {
300   case UP:
301     msg_link_status = "UP";
302     n.severity = NOTIF_OKAY;
303     break;
304   case DOWN:
305     msg_link_status = "DOWN";
306     n.severity = NOTIF_WARNING;
307     break;
308   default:
309     ERROR(OVS_EVENTS_PLUGIN ": unknown interface link status");
310     return;
311   }
312
313   /* add interface metadata to the notification */
314   if (plugin_notification_meta_add_string(&n, "uuid", ifinfo->uuid) < 0) {
315     ERROR(OVS_EVENTS_PLUGIN ": add interface uuid meta data failed");
316     return;
317   }
318
319   if (strlen(ifinfo->ext_vm_uuid) > 0) {
320     if (plugin_notification_meta_add_string(&n, "vm-uuid",
321                                             ifinfo->ext_vm_uuid) < 0) {
322       ERROR(OVS_EVENTS_PLUGIN ": add interface vm-uuid meta data failed");
323       return;
324     }
325   }
326
327   if (strlen(ifinfo->ext_iface_id) > 0) {
328     if (plugin_notification_meta_add_string(&n, "iface-id",
329                                             ifinfo->ext_iface_id) < 0) {
330       ERROR(OVS_EVENTS_PLUGIN ": add interface iface-id meta data failed");
331       return;
332     }
333   }
334
335   /* fill the notification data */
336   ssnprintf(n.message, sizeof(n.message),
337             "link state of \"%s\" interface has been changed to \"%s\"",
338             ifinfo->name, msg_link_status);
339   sstrncpy(n.host, hostname_g, sizeof(n.host));
340   sstrncpy(n.plugin_instance, ifinfo->name, sizeof(n.plugin_instance));
341   sstrncpy(n.type, "gauge", sizeof(n.type));
342   sstrncpy(n.type_instance, "link_status", sizeof(n.type_instance));
343   plugin_dispatch_notification(&n);
344 }
345
346 /* Dispatch OVS interface link status value to collectd */
347 static void ovs_events_link_status_submit(const ovs_events_iface_info_t *ifinfo) {
348   value_list_t vl = VALUE_LIST_INIT;
349   meta_data_t *meta = NULL;
350
351   /* add interface metadata to the submit value */
352   if ((meta = meta_data_create()) != NULL) {
353     if (meta_data_add_string(meta, "uuid", ifinfo->uuid) < 0)
354       ERROR(OVS_EVENTS_PLUGIN ": add interface uuid meta data failed");
355
356     if (strlen(ifinfo->ext_vm_uuid) > 0)
357       if (meta_data_add_string(meta, "vm-uuid", ifinfo->ext_vm_uuid) < 0)
358         ERROR(OVS_EVENTS_PLUGIN ": add interface vm-uuid meta data failed");
359
360     if (strlen(ifinfo->ext_iface_id) > 0)
361       if (meta_data_add_string(meta, "iface-id", ifinfo->ext_iface_id) < 0)
362         ERROR(OVS_EVENTS_PLUGIN ": add interface iface-id meta data failed");
363     vl.meta = meta;
364   } else
365     ERROR(OVS_EVENTS_PLUGIN ": create metadata failed");
366
367   vl.time = cdtime();
368   vl.values = &(value_t){.gauge = (gauge_t)ifinfo->link_status};
369   vl.values_len = 1;
370   sstrncpy(vl.plugin, OVS_EVENTS_PLUGIN, sizeof(vl.plugin));
371   sstrncpy(vl.plugin_instance, ifinfo->name, sizeof(vl.plugin_instance));
372   sstrncpy(vl.type, "gauge", sizeof(vl.type));
373   sstrncpy(vl.type_instance, "link_status", sizeof(vl.type_instance));
374   plugin_dispatch_values(&vl);
375   meta_data_destroy(meta);
376 }
377
378 /* Dispatch OVS DB terminate connection event to collectd */
379 static void ovs_events_dispatch_terminate_notification(const char *msg) {
380   notification_t n = {
381       NOTIF_FAILURE, cdtime(), "", "", OVS_EVENTS_PLUGIN, "", "", "", NULL};
382   sstrncpy(n.message, msg, sizeof(n.message));
383   sstrncpy(n.host, hostname_g, sizeof(n.host));
384   plugin_dispatch_notification(&n);
385 }
386
387 /* Get OVS DB interface information and stores it into
388  * ovs_events_iface_info_t structure */
389 static int ovs_events_get_iface_info(yajl_val jobject,
390                                      ovs_events_iface_info_t *ifinfo) {
391   yajl_val jexternal_ids = NULL;
392   yajl_val jvalue = NULL;
393   yajl_val juuid = NULL;
394   const char *state = NULL;
395
396   /* check YAJL type */
397   if (!YAJL_IS_OBJECT(jobject))
398     return (-1);
399
400   /* zero the interface info structure */
401   memset(ifinfo, 0, sizeof(*ifinfo));
402
403   /* try to find external_ids, name and link_state fields */
404   jexternal_ids = ovs_utils_get_value_by_key(jobject, "external_ids");
405   if (jexternal_ids == NULL || ifinfo == NULL)
406     return (-1);
407
408   /* get iface-id from external_ids field */
409   jvalue = ovs_utils_get_map_value(jexternal_ids, "iface-id");
410   if (jvalue != NULL && YAJL_IS_STRING(jvalue))
411     sstrncpy(ifinfo->ext_iface_id, YAJL_GET_STRING(jvalue),
412              sizeof(ifinfo->ext_iface_id));
413
414   /* get vm-uuid from external_ids field */
415   jvalue = ovs_utils_get_map_value(jexternal_ids, "vm-uuid");
416   if (jvalue != NULL && YAJL_IS_STRING(jvalue))
417     sstrncpy(ifinfo->ext_vm_uuid, YAJL_GET_STRING(jvalue),
418              sizeof(ifinfo->ext_vm_uuid));
419
420   /* get interface uuid */
421   jvalue = ovs_utils_get_value_by_key(jobject, "_uuid");
422   if (jvalue == NULL || !YAJL_IS_ARRAY(jvalue) ||
423       YAJL_GET_ARRAY(jvalue)->len != 2)
424     return (-1);
425   juuid = YAJL_GET_ARRAY(jvalue)->values[1];
426   if (juuid == NULL || !YAJL_IS_STRING(juuid))
427     return (-1);
428   sstrncpy(ifinfo->uuid, YAJL_GET_STRING(juuid), sizeof(ifinfo->uuid));
429
430   /* get interface name */
431   jvalue = ovs_utils_get_value_by_key(jobject, "name");
432   if (jvalue == NULL || !YAJL_IS_STRING(jvalue))
433     return (-1);
434   sstrncpy(ifinfo->name, YAJL_GET_STRING(jvalue), sizeof(ifinfo->name));
435
436   /* get OVS DB interface link status */
437   jvalue = ovs_utils_get_value_by_key(jobject, "link_state");
438   if (jvalue != NULL && ((state = YAJL_GET_STRING(jvalue)) != NULL)) {
439     /* convert OVS table link state to link status */
440     if (strcmp(state, "up") == 0)
441       ifinfo->link_status = UP;
442     else if (strcmp(state, "down") == 0)
443       ifinfo->link_status = DOWN;
444   }
445   return (0);
446 }
447
448 /* Process OVS DB update table event. It handles link status update event(s)
449  * and dispatches the value(s) to collectd if interface name matches one of
450  * interfaces specified in configuration file.
451  */
452 static void ovs_events_table_update_cb(yajl_val jupdates) {
453   yajl_val jnew_val = NULL;
454   yajl_val jupdate = NULL;
455   yajl_val jrow_update = NULL;
456   ovs_events_iface_info_t ifinfo;
457
458   /* JSON "Interface" table update example:
459    * ---------------------------------
460    * {"Interface":
461    *  {
462    *   "9adf1db2-29ca-4140-ab22-ae347a4484de":
463    *    {
464    *     "new":
465    *      {
466    *       "name":"br0",
467    *       "link_state":"up"
468    *      },
469    *     "old":
470    *      {
471    *       "link_state":"down"
472    *      }
473    *    }
474    *  }
475    * }
476    */
477   if (!YAJL_IS_OBJECT(jupdates) || !(YAJL_GET_OBJECT(jupdates)->len > 0)) {
478     ERROR(OVS_EVENTS_PLUGIN ": unexpected OVS DB update event received");
479     return;
480   }
481   /* verify if this is a table event */
482   jupdate = YAJL_GET_OBJECT(jupdates)->values[0];
483   if (!YAJL_IS_OBJECT(jupdate)) {
484     ERROR(OVS_EVENTS_PLUGIN ": unexpected table update event received");
485     return;
486   }
487   /* go through all row updates  */
488   for (size_t row_index = 0; row_index < YAJL_GET_OBJECT(jupdate)->len;
489        ++row_index) {
490     jrow_update = YAJL_GET_OBJECT(jupdate)->values[row_index];
491
492     /* check row update */
493     jnew_val = ovs_utils_get_value_by_key(jrow_update, "new");
494     if (jnew_val == NULL) {
495       ERROR(OVS_EVENTS_PLUGIN ": unexpected row update received");
496       return;
497     }
498     /* get OVS DB interface information */
499     if (ovs_events_get_iface_info(jnew_val, &ifinfo) < 0) {
500       ERROR(OVS_EVENTS_PLUGIN
501             " :unexpected interface information data received");
502       return;
503     }
504     if (ovs_events_config_iface_exists(ifinfo.name) != 0) {
505       DEBUG("name=%s, uuid=%s, ext_iface_id=%s, ext_vm_uuid=%s", ifinfo.name,
506             ifinfo.uuid, ifinfo.ext_iface_id, ifinfo.ext_vm_uuid);
507       /* dispatch notification */
508       ovs_events_dispatch_notification(&ifinfo);
509     }
510   }
511 }
512
513 /* OVS DB reply callback. It parses reply, receives
514  * interface information and dispatches the info to
515  * collectd
516  */
517 static void ovs_events_poll_result_cb(yajl_val jresult, yajl_val jerror) {
518   yajl_val *jvalues = NULL;
519   yajl_val jvalue = NULL;
520   ovs_events_iface_info_t ifinfo;
521
522   if (!YAJL_IS_NULL(jerror)) {
523     ERROR(OVS_EVENTS_PLUGIN "error received by OVS DB server");
524     return;
525   }
526
527   /* result should be an array */
528   if (!YAJL_IS_ARRAY(jresult)) {
529     ERROR(OVS_EVENTS_PLUGIN "invalid data (array is expected)");
530     return;
531   }
532
533   /* go through all rows and get interface info */
534   jvalues = YAJL_GET_ARRAY(jresult)->values;
535   for (size_t i = 0; i < YAJL_GET_ARRAY(jresult)->len; i++) {
536     jvalue = ovs_utils_get_value_by_key(jvalues[i], "rows");
537     if (jvalue == NULL || !YAJL_IS_ARRAY(jvalue)) {
538       ERROR(OVS_EVENTS_PLUGIN "invalid data (array of rows is expected)");
539       return;
540     }
541     /* get interfaces info */
542     for (size_t j = 0; j < YAJL_GET_ARRAY(jvalue)->len; j++) {
543       if (ovs_events_get_iface_info(YAJL_GET_ARRAY(jvalue)->values[j],
544                                     &ifinfo) < 0) {
545         ERROR(OVS_EVENTS_PLUGIN
546               "unexpected interface information data received");
547         return;
548       }
549       DEBUG("name=%s, uuid=%s, ext_iface_id=%s, ext_vm_uuid=%s", ifinfo.name,
550             ifinfo.uuid, ifinfo.ext_iface_id, ifinfo.ext_vm_uuid);
551       ovs_events_link_status_submit(&ifinfo);
552     }
553   }
554 }
555
556 /* Setup OVS DB table callback. It subscribes to OVS DB 'Interface' table
557  * to receive link status event(s).
558  */
559 static void ovs_events_conn_initialize(ovs_db_t *pdb) {
560   const char tb_name[] = "Interface";
561   const char *columns[] = {"_uuid", "external_ids", "name", "link_state", NULL};
562
563   /* register update link status event if needed */
564   if (ovs_events_ctx.config.send_notification) {
565     int ret = ovs_db_table_cb_register(pdb, tb_name, columns,
566                                        ovs_events_table_update_cb, NULL,
567                                        OVS_DB_TABLE_CB_FLAG_MODIFY);
568     if (ret < 0) {
569       ERROR(OVS_EVENTS_PLUGIN ": register OVS DB update callback failed");
570       return;
571     }
572   }
573   OVS_EVENTS_CTX_LOCK { ovs_events_ctx.is_db_available = 1; }
574   DEBUG(OVS_EVENTS_PLUGIN ": OVS DB connection has been initialized");
575 }
576
577 /* OVS DB terminate connection notification callback */
578 static void ovs_events_conn_terminate() {
579   const char msg[] = "OVS DB connection has been lost";
580   if (ovs_events_ctx.config.send_notification)
581     ovs_events_dispatch_terminate_notification(msg);
582   WARNING(OVS_EVENTS_PLUGIN ": %s", msg);
583   OVS_EVENTS_CTX_LOCK { ovs_events_ctx.is_db_available = 0; }
584 }
585
586 /* Read OVS DB interface link status callback */
587 static int ovs_events_plugin_read(__attribute__((unused)) user_data_t *u) {
588   _Bool is_connected = 0;
589   OVS_EVENTS_CTX_LOCK { is_connected = ovs_events_ctx.is_db_available; }
590   if (is_connected)
591     if (ovs_db_send_request(ovs_events_ctx.ovs_db, "transact",
592                             ovs_events_ctx.ovs_db_select_params,
593                             ovs_events_poll_result_cb) < 0) {
594       ERROR(OVS_EVENTS_PLUGIN ": get interface info failed");
595       return (-1);
596     }
597   return (0);
598 }
599
600 /* Initialize OVS plugin */
601 static int ovs_events_plugin_init(void) {
602   ovs_db_t *ovs_db = NULL;
603   ovs_db_callback_t cb = {.post_conn_init = ovs_events_conn_initialize,
604                           .post_conn_terminate = ovs_events_conn_terminate};
605
606   DEBUG(OVS_EVENTS_PLUGIN ": OVS DB address=%s, service=%s, unix=%s",
607         ovs_events_ctx.config.ovs_db_node, ovs_events_ctx.config.ovs_db_serv,
608         ovs_events_ctx.config.ovs_db_unix);
609
610   /* generate OVS DB select condition based on list on configured interfaces */
611   ovs_events_ctx.ovs_db_select_params = ovs_events_get_select_params();
612   if (ovs_events_ctx.ovs_db_select_params == NULL) {
613     ERROR(OVS_EVENTS_PLUGIN ": fail to get OVS DB select condition");
614     goto ovs_events_failure;
615   }
616
617   /* initialize OVS DB */
618   ovs_db = ovs_db_init(ovs_events_ctx.config.ovs_db_node,
619                        ovs_events_ctx.config.ovs_db_serv,
620                        ovs_events_ctx.config.ovs_db_unix, &cb);
621   if (ovs_db == NULL) {
622     ERROR(OVS_EVENTS_PLUGIN ": fail to connect to OVS DB server");
623     goto ovs_events_failure;
624   }
625
626   /* store OVS DB handler */
627   OVS_EVENTS_CTX_LOCK { ovs_events_ctx.ovs_db = ovs_db; }
628
629   DEBUG(OVS_EVENTS_PLUGIN ": plugin has been initialized");
630   return (0);
631
632 ovs_events_failure:
633   ERROR(OVS_EVENTS_PLUGIN ": plugin initialize failed");
634   /* release allocated memory */
635   ovs_events_config_free();
636   return (-1);
637 }
638
639 /* Shutdown OVS plugin */
640 static int ovs_events_plugin_shutdown(void) {
641   /* destroy OVS DB */
642   if (ovs_db_destroy(ovs_events_ctx.ovs_db))
643     ERROR(OVS_EVENTS_PLUGIN ": OVSDB object destroy failed");
644
645   /* release memory allocated for config */
646   ovs_events_config_free();
647
648   DEBUG(OVS_EVENTS_PLUGIN ": plugin has been destroyed");
649   return (0);
650 }
651
652 /* Register OVS plugin callbacks */
653 void module_register(void) {
654   plugin_register_complex_config(OVS_EVENTS_PLUGIN, ovs_events_plugin_config);
655   plugin_register_init(OVS_EVENTS_PLUGIN, ovs_events_plugin_init);
656   plugin_register_shutdown(OVS_EVENTS_PLUGIN, ovs_events_plugin_shutdown);
657 }