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