2 * collectd - src/ovs_events.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>
30 #include "common.h" /* auxiliary functions */
32 #include "utils_ovs.h" /* OVS helpers */
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())
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;
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 */
55 typedef struct ovs_events_iface_info_s ovs_events_iface_info_t;
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 */
62 typedef struct ovs_events_iface_list_s ovs_events_iface_list_t;
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 */
72 typedef struct ovs_events_config_s ovs_events_config_t;
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 */
82 typedef struct ovs_events_ctx_s ovs_events_ctx_t;
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 */
93 /* This function is used only by "OVS_EVENTS_CTX_LOCK" define (see above).
94 * It always returns 1 when context is locked.
96 static int ovs_events_ctx_lock() {
97 pthread_mutex_lock(&ovs_events_ctx.mutex);
101 /* This function is used only by "OVS_EVENTS_CTX_LOCK" define (see above).
102 * It always returns 0 when context is unlocked.
104 static int ovs_events_ctx_unlock() {
105 pthread_mutex_unlock(&ovs_events_ctx.mutex);
109 /* Check if given interface name exists in configuration file. It
110 * returns 1 if exists otherwise 0. If no interfaces are configured,
113 static int ovs_events_config_iface_exists(const char *ifname) {
114 if (ovs_events_ctx.config.ifaces == NULL)
117 /* check if given interface exists */
118 for (ovs_events_iface_list_t *iface = ovs_events_ctx.config.ifaces; iface;
120 if (strcmp(ifname, iface->name) == 0)
126 /* Get OVS DB select parameter request based on rfc7047,
127 * "Transact" & "Select" section
129 static char *ovs_events_get_select_params() {
130 size_t buff_size = 0;
132 char *opt_buff = NULL;
133 static const char params_fmt[] = "[\"Open_vSwitch\"%s]";
134 static const char option_fmt[] =
135 ",{\"op\":\"select\",\"table\":\"Interface\","
136 "\"where\":[[\"name\",\"==\",\"%s\"]],"
137 "\"columns\":[\"link_state\",\"external_ids\","
138 "\"name\",\"_uuid\"]}";
139 static const char default_opt[] =
140 ",{\"op\":\"select\",\"table\":\"Interface\","
141 "\"where\":[],\"columns\":[\"link_state\","
142 "\"external_ids\",\"name\",\"_uuid\"]}";
143 /* setup OVS DB interface condition */
144 for (ovs_events_iface_list_t *iface = ovs_events_ctx.config.ifaces; iface;
145 iface = iface->next) {
146 /* allocate new buffer (format size + ifname len is good enough) */
147 buff_size += sizeof(option_fmt) + strlen(iface->name);
148 char *new_buff = realloc(opt_buff, buff_size);
149 if (new_buff == NULL) {
154 int ret = ssnprintf(opt_buff + buff_off, buff_size - buff_off, option_fmt,
162 /* if no interfaces are configured, use default params */
163 if (opt_buff == NULL)
164 if ((opt_buff = strdup(default_opt)) == NULL)
167 /* allocate memory for OVS DB select params */
168 size_t params_size = sizeof(params_fmt) + strlen(opt_buff);
169 char *params_buff = calloc(1, params_size);
170 if (params_buff == NULL) {
175 /* create OVS DB select params */
176 if (ssnprintf(params_buff, params_size, params_fmt, opt_buff) < 0)
183 /* Release memory allocated for configuration data */
184 static void ovs_events_config_free() {
185 ovs_events_iface_list_t *del_iface = NULL;
186 sfree(ovs_events_ctx.ovs_db_select_params);
187 while (ovs_events_ctx.config.ifaces) {
188 del_iface = ovs_events_ctx.config.ifaces;
189 ovs_events_ctx.config.ifaces = ovs_events_ctx.config.ifaces->next;
194 /* Parse/process "Interfaces" configuration option. Returns 0 if success
195 * otherwise -1 (error)
197 static int ovs_events_config_get_interfaces(const oconfig_item_t *ci) {
198 for (int j = 0; j < ci->values_num; j++) {
199 /* check interface name type */
200 if (ci->values[j].type != OCONFIG_TYPE_STRING) {
201 ERROR(OVS_EVENTS_PLUGIN
202 ": given interface name is not a string [idx=%d]", j);
205 /* allocate memory for configured interface */
206 ovs_events_iface_list_t *new_iface = calloc(1, sizeof(*new_iface));
207 if (new_iface == NULL) {
208 ERROR(OVS_EVENTS_PLUGIN ": calloc () copy interface name fail");
211 /* store interface name */
212 sstrncpy(new_iface->name, ci->values[j].value.string,
213 sizeof(new_iface->name));
214 new_iface->next = ovs_events_ctx.config.ifaces;
215 ovs_events_ctx.config.ifaces = new_iface;
216 DEBUG(OVS_EVENTS_PLUGIN ": found monitored interface \"%s\"",
223 /* Parse plugin configuration file and store the config
224 * in allocated memory. Returns negative value in case of error.
226 static int ovs_events_plugin_config(oconfig_item_t *ci) {
227 for (int i = 0; i < ci->children_num; i++) {
228 oconfig_item_t *child = ci->children + i;
229 if (strcasecmp("SendNotification", child->key) == 0) {
230 if (cf_util_get_boolean(child,
231 &ovs_events_ctx.config.send_notification) != 0) {
232 ovs_events_config_free();
235 } else if (strcasecmp("Address", child->key) == 0) {
236 if (cf_util_get_string_buffer(
237 child, ovs_events_ctx.config.ovs_db_node,
238 sizeof(ovs_events_ctx.config.ovs_db_node)) != 0) {
239 ovs_events_config_free();
242 } else if (strcasecmp("Port", child->key) == 0) {
243 char *service = NULL;
244 if (cf_util_get_service(child, &service) != 0) {
245 ovs_events_config_free();
248 strncpy(ovs_events_ctx.config.ovs_db_serv, service,
249 sizeof(ovs_events_ctx.config.ovs_db_serv));
251 } else if (strcasecmp("Socket", child->key) == 0) {
252 if (cf_util_get_string_buffer(
253 child, ovs_events_ctx.config.ovs_db_unix,
254 sizeof(ovs_events_ctx.config.ovs_db_unix)) != 0) {
255 ovs_events_config_free();
258 } else if (strcasecmp("Interfaces", child->key) == 0) {
259 if (ovs_events_config_get_interfaces(child) != 0) {
260 ovs_events_config_free();
264 ERROR(OVS_EVENTS_PLUGIN ": option '%s' is not allowed here", child->key);
265 ovs_events_config_free();
272 /* Dispatch OVS interface link status event to collectd */
273 static void ovs_events_dispatch_notification(const ovs_events_iface_info_t *ifinfo) {
274 const char *msg_link_status = NULL;
276 NOTIF_FAILURE, cdtime(), "", "", OVS_EVENTS_PLUGIN, "", "", "", NULL};
278 /* convert link status to message string */
279 switch (ifinfo->link_status) {
281 msg_link_status = "UP";
282 n.severity = NOTIF_OKAY;
285 msg_link_status = "DOWN";
286 n.severity = NOTIF_WARNING;
289 ERROR(OVS_EVENTS_PLUGIN ": unknown interface link status");
293 /* add interface metadata to the notification */
294 if (plugin_notification_meta_add_string(&n, "uuid", ifinfo->uuid) < 0) {
295 ERROR(OVS_EVENTS_PLUGIN ": add interface uuid meta data failed");
299 if (strlen(ifinfo->ext_vm_uuid) > 0) {
300 if (plugin_notification_meta_add_string(&n, "vm-uuid",
301 ifinfo->ext_vm_uuid) < 0) {
302 ERROR(OVS_EVENTS_PLUGIN ": add interface vm-uuid meta data failed");
307 if (strlen(ifinfo->ext_iface_id) > 0) {
308 if (plugin_notification_meta_add_string(&n, "iface-id",
309 ifinfo->ext_iface_id) < 0) {
310 ERROR(OVS_EVENTS_PLUGIN ": add interface iface-id meta data failed");
315 /* fill the notification data */
316 ssnprintf(n.message, sizeof(n.message),
317 "link state of \"%s\" interface has been changed to \"%s\"",
318 ifinfo->name, msg_link_status);
319 sstrncpy(n.host, hostname_g, sizeof(n.host));
320 sstrncpy(n.plugin_instance, ifinfo->name, sizeof(n.plugin_instance));
321 sstrncpy(n.type, "gauge", sizeof(n.type));
322 sstrncpy(n.type_instance, "link_status", sizeof(n.type_instance));
323 plugin_dispatch_notification(&n);
326 /* Dispatch OVS interface link status value to collectd */
327 static void ovs_events_link_status_submit(const ovs_events_iface_info_t *ifinfo) {
328 value_list_t vl = VALUE_LIST_INIT;
329 meta_data_t *meta = NULL;
331 /* add interface metadata to the submit value */
332 if ((meta = meta_data_create()) != NULL) {
333 if (meta_data_add_string(meta, "uuid", ifinfo->uuid) < 0)
334 ERROR(OVS_EVENTS_PLUGIN ": add interface uuid meta data failed");
336 if (strlen(ifinfo->ext_vm_uuid) > 0)
337 if (meta_data_add_string(meta, "vm-uuid", ifinfo->ext_vm_uuid) < 0)
338 ERROR(OVS_EVENTS_PLUGIN ": add interface vm-uuid meta data failed");
340 if (strlen(ifinfo->ext_iface_id) > 0)
341 if (meta_data_add_string(meta, "iface-id", ifinfo->ext_iface_id) < 0)
342 ERROR(OVS_EVENTS_PLUGIN ": add interface iface-id meta data failed");
345 ERROR(OVS_EVENTS_PLUGIN ": create metadata failed");
348 vl.values = &(value_t){.gauge = (gauge_t)ifinfo->link_status};
350 sstrncpy(vl.plugin, OVS_EVENTS_PLUGIN, sizeof(vl.plugin));
351 sstrncpy(vl.plugin_instance, ifinfo->name, sizeof(vl.plugin_instance));
352 sstrncpy(vl.type, "gauge", sizeof(vl.type));
353 sstrncpy(vl.type_instance, "link_status", sizeof(vl.type_instance));
354 plugin_dispatch_values(&vl);
355 meta_data_destroy(meta);
358 /* Dispatch OVS DB terminate connection event to collectd */
359 static void ovs_events_dispatch_terminate_notification(const char *msg) {
361 NOTIF_FAILURE, cdtime(), "", "", OVS_EVENTS_PLUGIN, "", "", "", NULL};
362 sstrncpy(n.message, msg, sizeof(n.message));
363 sstrncpy(n.host, hostname_g, sizeof(n.host));
364 plugin_dispatch_notification(&n);
367 /* Get OVS DB interface information and stores it into
368 * ovs_events_iface_info_t structure */
369 static int ovs_events_get_iface_info(yajl_val jobject,
370 ovs_events_iface_info_t *ifinfo) {
371 yajl_val jexternal_ids = NULL;
372 yajl_val jvalue = NULL;
373 yajl_val juuid = NULL;
374 const char *state = NULL;
376 /* check YAJL type */
377 if (!YAJL_IS_OBJECT(jobject))
380 /* zero the interface info structure */
381 memset(ifinfo, 0, sizeof(*ifinfo));
383 /* try to find external_ids, name and link_state fields */
384 jexternal_ids = ovs_utils_get_value_by_key(jobject, "external_ids");
385 if (jexternal_ids == NULL || ifinfo == NULL)
388 /* get iface-id from external_ids field */
389 jvalue = ovs_utils_get_map_value(jexternal_ids, "iface-id");
390 if (jvalue != NULL && YAJL_IS_STRING(jvalue))
391 sstrncpy(ifinfo->ext_iface_id, YAJL_GET_STRING(jvalue),
392 sizeof(ifinfo->ext_iface_id));
394 /* get vm-uuid from external_ids field */
395 jvalue = ovs_utils_get_map_value(jexternal_ids, "vm-uuid");
396 if (jvalue != NULL && YAJL_IS_STRING(jvalue))
397 sstrncpy(ifinfo->ext_vm_uuid, YAJL_GET_STRING(jvalue),
398 sizeof(ifinfo->ext_vm_uuid));
400 /* get interface uuid */
401 jvalue = ovs_utils_get_value_by_key(jobject, "_uuid");
402 if (jvalue == NULL || !YAJL_IS_ARRAY(jvalue) ||
403 YAJL_GET_ARRAY(jvalue)->len != 2)
405 juuid = YAJL_GET_ARRAY(jvalue)->values[1];
406 if (juuid == NULL || !YAJL_IS_STRING(juuid))
408 sstrncpy(ifinfo->uuid, YAJL_GET_STRING(juuid), sizeof(ifinfo->uuid));
410 /* get interface name */
411 jvalue = ovs_utils_get_value_by_key(jobject, "name");
412 if (jvalue == NULL || !YAJL_IS_STRING(jvalue))
414 sstrncpy(ifinfo->name, YAJL_GET_STRING(jvalue), sizeof(ifinfo->name));
416 /* get OVS DB interface link status */
417 jvalue = ovs_utils_get_value_by_key(jobject, "link_state");
418 if (jvalue != NULL && ((state = YAJL_GET_STRING(jvalue)) != NULL)) {
419 /* convert OVS table link state to link status */
420 if (strcmp(state, "up") == 0)
421 ifinfo->link_status = UP;
422 else if (strcmp(state, "down") == 0)
423 ifinfo->link_status = DOWN;
428 /* Process OVS DB update table event. It handles link status update event(s)
429 * and dispatches the value(s) to collectd if interface name matches one of
430 * interfaces specified in configuration file.
432 static void ovs_events_table_update_cb(yajl_val jupdates) {
433 yajl_val jnew_val = NULL;
434 yajl_val jupdate = NULL;
435 yajl_val jrow_update = NULL;
436 ovs_events_iface_info_t ifinfo;
438 /* JSON "Interface" table update example:
439 * ---------------------------------
442 * "9adf1db2-29ca-4140-ab22-ae347a4484de":
451 * "link_state":"down"
457 if (!YAJL_IS_OBJECT(jupdates) || !(YAJL_GET_OBJECT(jupdates)->len > 0)) {
458 ERROR(OVS_EVENTS_PLUGIN ": unexpected OVS DB update event received");
461 /* verify if this is a table event */
462 jupdate = YAJL_GET_OBJECT(jupdates)->values[0];
463 if (!YAJL_IS_OBJECT(jupdate)) {
464 ERROR(OVS_EVENTS_PLUGIN ": unexpected table update event received");
467 /* go through all row updates */
468 for (size_t row_index = 0; row_index < YAJL_GET_OBJECT(jupdate)->len;
470 jrow_update = YAJL_GET_OBJECT(jupdate)->values[row_index];
472 /* check row update */
473 jnew_val = ovs_utils_get_value_by_key(jrow_update, "new");
474 if (jnew_val == NULL) {
475 ERROR(OVS_EVENTS_PLUGIN ": unexpected row update received");
478 /* get OVS DB interface information */
479 if (ovs_events_get_iface_info(jnew_val, &ifinfo) < 0) {
480 ERROR(OVS_EVENTS_PLUGIN
481 " :unexpected interface information data received");
484 if (ovs_events_config_iface_exists(ifinfo.name) != 0) {
485 DEBUG("name=%s, uuid=%s, ext_iface_id=%s, ext_vm_uuid=%s", ifinfo.name,
486 ifinfo.uuid, ifinfo.ext_iface_id, ifinfo.ext_vm_uuid);
487 /* dispatch notification */
488 ovs_events_dispatch_notification(&ifinfo);
493 /* OVS DB reply callback. It parses reply, receives
494 * interface information and dispatches the info to
497 static void ovs_events_poll_result_cb(yajl_val jresult, yajl_val jerror) {
498 yajl_val *jvalues = NULL;
499 yajl_val jvalue = NULL;
500 ovs_events_iface_info_t ifinfo;
502 if (!YAJL_IS_NULL(jerror)) {
503 ERROR(OVS_EVENTS_PLUGIN "error received by OVS DB server");
507 /* result should be an array */
508 if (!YAJL_IS_ARRAY(jresult)) {
509 ERROR(OVS_EVENTS_PLUGIN "invalid data (array is expected)");
513 /* go through all rows and get interface info */
514 jvalues = YAJL_GET_ARRAY(jresult)->values;
515 for (size_t i = 0; i < YAJL_GET_ARRAY(jresult)->len; i++) {
516 jvalue = ovs_utils_get_value_by_key(jvalues[i], "rows");
517 if (jvalue == NULL || !YAJL_IS_ARRAY(jvalue)) {
518 ERROR(OVS_EVENTS_PLUGIN "invalid data (array of rows is expected)");
521 /* get interfaces info */
522 for (size_t j = 0; j < YAJL_GET_ARRAY(jvalue)->len; j++) {
523 if (ovs_events_get_iface_info(YAJL_GET_ARRAY(jvalue)->values[j],
525 ERROR(OVS_EVENTS_PLUGIN
526 "unexpected interface information data received");
529 DEBUG("name=%s, uuid=%s, ext_iface_id=%s, ext_vm_uuid=%s", ifinfo.name,
530 ifinfo.uuid, ifinfo.ext_iface_id, ifinfo.ext_vm_uuid);
531 ovs_events_link_status_submit(&ifinfo);
536 /* Setup OVS DB table callback. It subscribes to OVS DB 'Interface' table
537 * to receive link status event(s).
539 static void ovs_events_conn_initialize(ovs_db_t *pdb) {
540 const char tb_name[] = "Interface";
541 const char *columns[] = {"_uuid", "external_ids", "name", "link_state", NULL};
543 /* register update link status event if needed */
544 if (ovs_events_ctx.config.send_notification) {
545 int ret = ovs_db_table_cb_register(pdb, tb_name, columns,
546 ovs_events_table_update_cb, NULL,
547 OVS_DB_TABLE_CB_FLAG_MODIFY);
549 ERROR(OVS_EVENTS_PLUGIN ": register OVS DB update callback failed");
553 OVS_EVENTS_CTX_LOCK { ovs_events_ctx.is_db_available = 1; }
554 DEBUG(OVS_EVENTS_PLUGIN ": OVS DB connection has been initialized");
557 /* OVS DB terminate connection notification callback */
558 static void ovs_events_conn_terminate() {
559 const char msg[] = "OVS DB connection has been lost";
560 if (ovs_events_ctx.config.send_notification)
561 ovs_events_dispatch_terminate_notification(msg);
562 WARNING(OVS_EVENTS_PLUGIN ": %s", msg);
563 OVS_EVENTS_CTX_LOCK { ovs_events_ctx.is_db_available = 0; }
566 /* Read OVS DB interface link status callback */
567 static int ovs_events_plugin_read(__attribute__((unused)) user_data_t *u) {
568 _Bool is_connected = 0;
569 OVS_EVENTS_CTX_LOCK { is_connected = ovs_events_ctx.is_db_available; }
571 if (ovs_db_send_request(ovs_events_ctx.ovs_db, "transact",
572 ovs_events_ctx.ovs_db_select_params,
573 ovs_events_poll_result_cb) < 0) {
574 ERROR(OVS_EVENTS_PLUGIN ": get interface info failed");
580 /* Initialize OVS plugin */
581 static int ovs_events_plugin_init(void) {
582 ovs_db_t *ovs_db = NULL;
583 ovs_db_callback_t cb = {.post_conn_init = ovs_events_conn_initialize,
584 .post_conn_terminate = ovs_events_conn_terminate};
586 DEBUG(OVS_EVENTS_PLUGIN ": OVS DB address=%s, service=%s, unix=%s",
587 ovs_events_ctx.config.ovs_db_node, ovs_events_ctx.config.ovs_db_serv,
588 ovs_events_ctx.config.ovs_db_unix);
590 /* generate OVS DB select condition based on list on configured interfaces */
591 ovs_events_ctx.ovs_db_select_params = ovs_events_get_select_params();
592 if (ovs_events_ctx.ovs_db_select_params == NULL) {
593 ERROR(OVS_EVENTS_PLUGIN ": fail to get OVS DB select condition");
594 goto ovs_events_failure;
597 /* initialize OVS DB */
598 ovs_db = ovs_db_init(ovs_events_ctx.config.ovs_db_node,
599 ovs_events_ctx.config.ovs_db_serv,
600 ovs_events_ctx.config.ovs_db_unix, &cb);
601 if (ovs_db == NULL) {
602 ERROR(OVS_EVENTS_PLUGIN ": fail to connect to OVS DB server");
603 goto ovs_events_failure;
606 /* store OVS DB handler */
607 OVS_EVENTS_CTX_LOCK { ovs_events_ctx.ovs_db = ovs_db; }
609 DEBUG(OVS_EVENTS_PLUGIN ": plugin has been initialized");
613 ERROR(OVS_EVENTS_PLUGIN ": plugin initialize failed");
614 /* release allocated memory */
615 ovs_events_config_free();
619 /* Shutdown OVS plugin */
620 static int ovs_events_plugin_shutdown(void) {
622 if (ovs_db_destroy(ovs_events_ctx.ovs_db))
623 ERROR(OVS_EVENTS_PLUGIN ": OVSDB object destroy failed");
625 /* release memory allocated for config */
626 ovs_events_config_free();
628 DEBUG(OVS_EVENTS_PLUGIN ": plugin has been destroyed");
632 /* Register OVS plugin callbacks */
633 void module_register(void) {
634 plugin_register_complex_config(OVS_EVENTS_PLUGIN, ovs_events_plugin_config);
635 plugin_register_init(OVS_EVENTS_PLUGIN, ovs_events_plugin_init);
636 plugin_register_complex_read(NULL, OVS_EVENTS_PLUGIN, ovs_events_plugin_read,
638 plugin_register_shutdown(OVS_EVENTS_PLUGIN, ovs_events_plugin_shutdown);