ovs_events: Fix plugin collectd config file
[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   sstrncpy(n.message, msg, sizeof(n.message));
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   const char *state = NULL;
371
372   /* check YAJL type */
373   if (!YAJL_IS_OBJECT(jobject))
374     return (-1);
375
376   /* try to find external_ids, name and link_state fields */
377   jexternal_ids = ovs_utils_get_value_by_key(jobject, "external_ids");
378   if (jexternal_ids == NULL || ifinfo == NULL)
379     return (-1);
380
381   /* get iface-id from external_ids field */
382   jvalue = ovs_utils_get_map_value(jexternal_ids, "iface-id");
383   if (jvalue != NULL && YAJL_IS_STRING(jvalue))
384     sstrncpy(ifinfo->ext_iface_id, YAJL_GET_STRING(jvalue),
385              sizeof(ifinfo->ext_iface_id));
386
387   /* get vm-uuid from external_ids field */
388   jvalue = ovs_utils_get_map_value(jexternal_ids, "vm-uuid");
389   if (jvalue != NULL && YAJL_IS_STRING(jvalue))
390     sstrncpy(ifinfo->ext_vm_uuid, YAJL_GET_STRING(jvalue),
391              sizeof(ifinfo->ext_vm_uuid));
392
393   /* get interface uuid */
394   jvalue = ovs_utils_get_value_by_key(jobject, "_uuid");
395   if (jvalue == NULL || !YAJL_IS_ARRAY(jvalue) ||
396       YAJL_GET_ARRAY(jvalue)->len != 2)
397     return (-1);
398   juuid = YAJL_GET_ARRAY(jvalue)->values[1];
399   if (juuid == NULL || !YAJL_IS_STRING(juuid))
400     return (-1);
401   sstrncpy(ifinfo->uuid, YAJL_GET_STRING(juuid), sizeof(ifinfo->uuid));
402
403   /* get interface name */
404   jvalue = ovs_utils_get_value_by_key(jobject, "name");
405   if (jvalue == NULL || !YAJL_IS_STRING(jvalue))
406     return (-1);
407   sstrncpy(ifinfo->name, YAJL_GET_STRING(jvalue), sizeof(ifinfo->name));
408
409   /* get OVS DB interface link status */
410   jvalue = ovs_utils_get_value_by_key(jobject, "link_state");
411   if (jvalue != NULL && ((state = YAJL_GET_STRING(jvalue)) != NULL)) {
412     /* convert OVS table link state to link status */
413     if (strcmp(state, "up") == 0)
414       ifinfo->link_status = UP;
415     else if (strcmp(state, "down") == 0)
416       ifinfo->link_status = DOWN;
417   }
418   return (0);
419 }
420
421 /* Process OVS DB update table event. It handles link status update event(s)
422  * and dispatches the value(s) to collectd if interface name matches one of
423  * interfaces specified in configuration file.
424  */
425 static void
426 ovs_events_table_update_cb(yajl_val jupdates)
427 {
428   yajl_val jnew_val = NULL;
429   yajl_val jupdate = NULL;
430   yajl_val jrow_update = NULL;
431   ovs_events_iface_info_t ifinfo;
432
433   /* JSON "Interface" table update example:
434    * ---------------------------------
435    * {"Interface":
436    *  {
437    *   "9adf1db2-29ca-4140-ab22-ae347a4484de":
438    *    {
439    *     "new":
440    *      {
441    *       "name":"br0",
442    *       "link_state":"up"
443    *      },
444    *     "old":
445    *      {
446    *       "link_state":"down"
447    *      }
448    *    }
449    *  }
450    * }
451    */
452   if (!YAJL_IS_OBJECT(jupdates) || !(YAJL_GET_OBJECT(jupdates)->len > 0)) {
453     ERROR(OVS_EVENTS_PLUGIN ": unexpected OVS DB update event received");
454     return;
455   }
456   /* verify if this is a table event */
457   jupdate = YAJL_GET_OBJECT(jupdates)->values[0];
458   if (!YAJL_IS_OBJECT(jupdate)) {
459     ERROR(OVS_EVENTS_PLUGIN ": unexpected table update event received");
460     return;
461   }
462   /* go through all row updates  */
463   for (int row_index = 0; row_index < YAJL_GET_OBJECT(jupdate)->len;
464        ++row_index) {
465     jrow_update = YAJL_GET_OBJECT(jupdate)->values[row_index];
466
467     /* check row update */
468     jnew_val = ovs_utils_get_value_by_key(jrow_update, "new");
469     if (jnew_val == NULL) {
470       ERROR(OVS_EVENTS_PLUGIN ": unexpected row update received");
471       return;
472     }
473     /* get OVS DB interface information */
474     if (ovs_events_get_iface_info(jnew_val, &ifinfo) < 0) {
475       ERROR(OVS_EVENTS_PLUGIN
476             " :unexpected interface information data received");
477       return;
478     }
479     if (ovs_events_config_iface_exists(ifinfo.name) != 0)
480       /* dispatch notification */
481       ovs_events_dispatch_notification(&ifinfo);
482   }
483 }
484
485 /* OVD DB reply callback. It parses reply, receives
486  * interface information and dispatches the info to
487  * collecd
488  */
489 static void
490 ovs_events_poll_result_cb(yajl_val jresult, yajl_val jerror)
491 {
492   yajl_val *jvalues = NULL;
493   yajl_val jvalue = NULL;
494   ovs_events_iface_info_t ifinfo;
495
496   if (!YAJL_IS_NULL(jerror)) {
497     ERROR(OVS_EVENTS_PLUGIN "error received by OVS DB server");
498     return;
499   }
500
501   /* result should be an array */
502   if (!YAJL_IS_ARRAY(jresult)) {
503     ERROR(OVS_EVENTS_PLUGIN "invalid data (array is expected)");
504     return;
505   }
506
507   /* go through all rows and get interface info */
508   jvalues = YAJL_GET_ARRAY(jresult)->values;
509   for (int i = 0; i < YAJL_GET_ARRAY(jresult)->len; i++) {
510     jvalue = ovs_utils_get_value_by_key(jvalues[i], "rows");
511     if (jvalue == NULL || !YAJL_IS_ARRAY(jvalue)) {
512       ERROR(OVS_EVENTS_PLUGIN "invalid data (array of rows is expected)");
513       return;
514     }
515     /* get interfaces info */
516     for (int j = 0; j < YAJL_GET_ARRAY(jvalue)->len; j++) {
517       memset(&ifinfo, 0, sizeof(ifinfo));
518       if (ovs_events_get_iface_info
519           (YAJL_GET_ARRAY(jvalue)->values[j], &ifinfo) < 0) {
520         ERROR(OVS_EVENTS_PLUGIN
521               "unexpected interface information data received");
522         return;
523       }
524       DEBUG("name=%s, uuid=%s, ext_iface_id=%s, ext_vm_uuid=%s",
525             ifinfo.name, ifinfo.uuid, ifinfo.ext_iface_id,
526             ifinfo.ext_vm_uuid);
527       ovs_events_link_status_submit(&ifinfo);
528     }
529   }
530 }
531
532 /* Setup OVS DB table callback. It subscribes to OVS DB 'Interface' table
533  * to receive link status event(s).
534  */
535 static void
536 ovs_events_conn_initialize(ovs_db_t *pdb)
537 {
538   int ret = 0;
539   const char tb_name[] = "Interface";
540   const char *columns[] = {"_uuid", "external_ids",
541                            "name", "link_state", NULL};
542
543   /* register update link status event if needed */
544   if (ovs_events_ctx.config.send_notification) {
545     ret = ovs_db_table_cb_register(pdb, tb_name, columns,
546                                    ovs_events_table_update_cb, NULL,
547                                    OVS_DB_TABLE_CB_FLAG_MODIFY);
548     if (ret < 0) {
549       ERROR(OVS_EVENTS_PLUGIN ": register OVS DB update callback failed");
550       return;
551     }
552   }
553   OVS_EVENTS_CTX_LOCK {
554     ovs_events_ctx.is_db_available = 1;
555   }
556   DEBUG(OVS_EVENTS_PLUGIN ": OVS DB has been initialized");
557 }
558
559 /* OVS DB terminate connection notification callback */
560 static void
561 ovs_events_conn_terminate()
562 {
563   const char msg[] = "OVS DB connection has been lost";
564   if (ovs_events_ctx.config.send_notification)
565     ovs_events_dispatch_terminate_notification(msg);
566   WARNING(OVS_EVENTS_PLUGIN ": %s", msg);
567   OVS_EVENTS_CTX_LOCK {
568     ovs_events_ctx.is_db_available = 0;
569   }
570 }
571
572 /* Read OVS DB interface link status callback */
573 static int
574 ovs_events_plugin_read(user_data_t *ud)
575 {
576   (void)ud;                     /* unused argument */
577   _Bool is_connected = 0;
578   OVS_EVENTS_CTX_LOCK {
579     is_connected = ovs_events_ctx.is_db_available;
580   }
581   if (is_connected)
582     if (ovs_db_send_request(ovs_events_ctx.ovs_db, "transact",
583                             ovs_events_ctx.ovs_db_select_params,
584                             ovs_events_poll_result_cb) < 0) {
585       ERROR(OVS_EVENTS_PLUGIN ": get interface info failed");
586       return (-1);
587     }
588   return (0);
589 }
590
591 /* Initialize OVS plugin */
592 static int
593 ovs_events_plugin_init(void)
594 {
595   ovs_db_t *ovs_db = NULL;
596   ovs_db_callback_t cb = {.post_conn_init = ovs_events_conn_initialize,
597                           .post_conn_terminate = ovs_events_conn_terminate};
598
599   DEBUG(OVS_EVENTS_PLUGIN ": OVS DB url = %s",
600         ovs_events_ctx.config.ovs_db_server_url);
601
602   /* generate OVS DB select condition based on list on configured interfaces */
603   ovs_events_ctx.ovs_db_select_params = ovs_events_get_select_params();
604   if (ovs_events_ctx.ovs_db_select_params == NULL) {
605     ERROR(OVS_EVENTS_PLUGIN ": fail to get OVS DB select condition");
606     goto ovs_events_failure;
607   }
608
609   /* initialize OVS DB */
610   ovs_db = ovs_db_init(ovs_events_ctx.config.ovs_db_server_url, &cb);
611   if (ovs_db == NULL) {
612     ERROR(OVS_EVENTS_PLUGIN ": fail to connect to OVS DB server");
613     goto ovs_events_failure;
614   }
615
616   /* store OVS DB handler */
617   OVS_EVENTS_CTX_LOCK {
618     ovs_events_ctx.ovs_db = ovs_db;
619   }
620
621   DEBUG(OVS_EVENTS_PLUGIN ": plugin has been initialized");
622   return (0);
623
624 ovs_events_failure:
625   ERROR(OVS_EVENTS_PLUGIN ": plugin initialize failed");
626   /* release allocated memory */
627   ovs_events_config_free();
628   return (-1);
629 }
630
631 /* Shutdown OVS plugin */
632 static int
633 ovs_events_plugin_shutdown(void)
634 {
635   /* destroy OVS DB */
636   if (ovs_db_destroy(ovs_events_ctx.ovs_db))
637     ERROR(OVS_EVENTS_PLUGIN ": OVSDB object destroy failed");
638
639   /* release memory allocated for config */
640   ovs_events_config_free();
641
642   DEBUG(OVS_EVENTS_PLUGIN ": plugin has been destroyed");
643   return (0);
644 }
645
646 /* Register OVS plugin callbacks */
647 void
648 module_register(void)
649 {
650   plugin_register_complex_config(OVS_EVENTS_PLUGIN, ovs_events_plugin_config);
651   plugin_register_init(OVS_EVENTS_PLUGIN, ovs_events_plugin_init);
652   plugin_register_complex_read(NULL, OVS_EVENTS_PLUGIN,
653                                ovs_events_plugin_read, 0, NULL);
654   plugin_register_shutdown(OVS_EVENTS_PLUGIN, ovs_events_plugin_shutdown);
655 }