@@ -83,7 +83,8 @@ struct ovn_extend_table;
OVNACT(ND_NS, ovnact_nest) \
OVNACT(SET_METER, ovnact_set_meter) \
OVNACT(OVNFIELD_LOAD, ovnact_load) \
- OVNACT(CHECK_PKT_LARGER, ovnact_check_pkt_larger)
+ OVNACT(CHECK_PKT_LARGER, ovnact_check_pkt_larger) \
+ OVNACT(TRIGGER_EVENT, ovnact_controller_event)
/* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
enum OVS_PACKED_ENUM ovnact_type {
@@ -318,6 +319,14 @@ struct ovnact_check_pkt_larger {
struct expr_field dst; /* 1-bit destination field. */
};
+/* OVNACT_EVENT. */
+struct ovnact_controller_event {
+ struct ovnact ovnact;
+ int event_type; /* controller event type */
+ struct ovnact_gen_option *options;
+ size_t n_options;
+};
+
/* Internal use by the helpers below. */
void ovnact_init(struct ovnact *, enum ovnact_type, size_t len);
void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len);
@@ -486,6 +495,9 @@ enum action_opcode {
* The actions, in OpenFlow 1.3 format, follow the action_header.
*/
ACTION_OPCODE_ICMP4_ERROR,
+
+ /* "trigger_event (event_type)" */
+ ACTION_OPCODE_EVENT,
};
/* Header. */
@@ -515,6 +527,10 @@ struct ovnact_parse_params {
/* hmap of 'struct gen_opts_map' to support 'put_nd_ra_opts' action */
const struct hmap *nd_ra_opts;
+ /* Array of hmap of 'struct gen_opts_map' to support 'trigger_event'
+ * action */
+ const struct controller_event_options *controller_event_opts;
+
/* Each OVN flow exists in a logical table within a logical pipeline.
* These parameters express this context for a set of OVN actions being
* parsed:
@@ -70,6 +70,7 @@ static bool consider_logical_flow(
struct hmap *dhcp_opts,
struct hmap *dhcpv6_opts,
struct hmap *nd_ra_opts,
+ struct controller_event_options *controller_event_opts,
const struct shash *addr_sets,
const struct shash *port_groups,
const struct sset *active_tunnels,
@@ -297,12 +298,16 @@ add_logical_flows(
struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
nd_ra_opts_init(&nd_ra_opts);
+ struct controller_event_options controller_event_opts;
+ controller_event_opts_init(&controller_event_opts);
+
SBREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, logical_flow_table) {
if (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
sbrec_port_binding_by_name,
lflow, local_datapaths,
chassis, &dhcp_opts, &dhcpv6_opts,
- &nd_ra_opts, addr_sets, port_groups,
+ &nd_ra_opts, &controller_event_opts,
+ addr_sets, port_groups,
active_tunnels, local_lport_ids,
flow_table, group_table, meter_table,
lfrr, conj_id_ofs)) {
@@ -315,6 +320,7 @@ add_logical_flows(
dhcp_opts_destroy(&dhcp_opts);
dhcp_opts_destroy(&dhcpv6_opts);
nd_ra_opts_destroy(&nd_ra_opts);
+ controller_event_opts_destroy(&controller_event_opts);
}
bool
@@ -371,6 +377,10 @@ lflow_handle_changed_flows(
lflow_resource_destroy_lflow(lfrr, &lflow->header_.uuid);
}
}
+
+ struct controller_event_options controller_event_opts;
+ controller_event_opts_init(&controller_event_opts);
+
SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_TRACKED (lflow, logical_flow_table) {
if (!sbrec_logical_flow_is_deleted(lflow)) {
/* Now, add/modify existing flows. If the logical
@@ -389,7 +399,8 @@ lflow_handle_changed_flows(
sbrec_port_binding_by_name,
lflow, local_datapaths,
chassis, &dhcp_opts, &dhcpv6_opts,
- &nd_ra_opts, addr_sets, port_groups,
+ &nd_ra_opts, &controller_event_opts,
+ addr_sets, port_groups,
active_tunnels, local_lport_ids,
flow_table, group_table, meter_table,
lfrr, conj_id_ofs)) {
@@ -401,6 +412,7 @@ lflow_handle_changed_flows(
dhcp_opts_destroy(&dhcp_opts);
dhcp_opts_destroy(&dhcpv6_opts);
nd_ra_opts_destroy(&nd_ra_opts);
+ controller_event_opts_destroy(&controller_event_opts);
return ret;
}
@@ -466,6 +478,9 @@ lflow_handle_changed_ref(
struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
nd_ra_opts_init(&nd_ra_opts);
+ struct controller_event_options controller_event_opts;
+ controller_event_opts_init(&controller_event_opts);
+
/* Re-parse the related lflows. */
LIST_FOR_EACH (lrln, ref_list, &rlfn->ref_lflow_head) {
const struct sbrec_logical_flow *lflow =
@@ -483,11 +498,13 @@ lflow_handle_changed_ref(
UUID_ARGS(&lrln->lflow_uuid),
ref_type, ref_name);
ofctrl_remove_flows(flow_table, &lrln->lflow_uuid);
+
if (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
sbrec_port_binding_by_name,
lflow, local_datapaths,
chassis, &dhcp_opts, &dhcpv6_opts,
- &nd_ra_opts, addr_sets, port_groups,
+ &nd_ra_opts, &controller_event_opts,
+ addr_sets, port_groups,
active_tunnels, local_lport_ids,
flow_table, group_table, meter_table,
lfrr, conj_id_ofs)) {
@@ -506,6 +523,7 @@ lflow_handle_changed_ref(
dhcp_opts_destroy(&dhcp_opts);
dhcp_opts_destroy(&dhcpv6_opts);
nd_ra_opts_destroy(&nd_ra_opts);
+ controller_event_opts_destroy(&controller_event_opts);
return ret;
}
@@ -530,6 +548,7 @@ consider_logical_flow(
struct hmap *dhcp_opts,
struct hmap *dhcpv6_opts,
struct hmap *nd_ra_opts,
+ struct controller_event_options *controller_event_opts,
const struct shash *addr_sets,
const struct shash *port_groups,
const struct sset *active_tunnels,
@@ -574,6 +593,7 @@ consider_logical_flow(
.dhcp_opts = dhcp_opts,
.dhcpv6_opts = dhcpv6_opts,
.nd_ra_opts = nd_ra_opts,
+ .controller_event_opts = controller_event_opts,
.pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
.n_tables = LOG_PIPELINE_LEN,
@@ -211,6 +211,10 @@ static void pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn,
struct ofputil_packet_in *pin,
struct ofpbuf *userdata,
struct ofpbuf *continuation);
+static void
+pinctrl_handle_event(struct ofpbuf *userdata)
+ OVS_REQUIRES(pinctrl_mutex);
+static void wait_controller_event(struct ovsdb_idl_txn *ovnsb_idl_txn);
static void init_ipv6_ras(void);
static void destroy_ipv6_ras(void);
static void ipv6_ra_wait(long long int send_ipv6_ra_time);
@@ -1897,6 +1901,12 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
&pin, &userdata, &continuation);
break;
+ case ACTION_OPCODE_EVENT:
+ ovs_mutex_lock(&pinctrl_mutex);
+ pinctrl_handle_event(&userdata);
+ ovs_mutex_unlock(&pinctrl_mutex);
+ break;
+
default:
VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
ntohl(ah->opcode));
@@ -2405,6 +2415,7 @@ void
pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn)
{
wait_put_mac_bindings(ovnsb_idl_txn);
+ wait_controller_event(ovnsb_idl_txn);
int64_t new_seq = seq_read(pinctrl_main_seq);
seq_wait(pinctrl_main_seq, new_seq);
}
@@ -3448,3 +3459,106 @@ exit:
dp_packet_delete(pkt_out);
}
}
+
+static void
+wait_controller_event(struct ovsdb_idl_txn *ovnsb_idl_txn)
+{
+ if (!ovnsb_idl_txn) {
+ return;
+ }
+
+ for (size_t i = 0; i < OVN_EVENT_MAX; i++) {
+ if (!hmap_is_empty(&event_table[i])) {
+ poll_immediate_wake();
+ break;
+ }
+ }
+}
+
+static bool
+pinctrl_handle_empty_lb_backends_opts(struct ofpbuf *userdata)
+{
+ struct controller_event_opt_header *userdata_opt;
+ uint32_t hash = 0;
+ char *vip = NULL;
+ char *protocol = NULL;
+ char *load_balancer = NULL;
+
+ while (userdata->size) {
+ userdata_opt = ofpbuf_try_pull(userdata, sizeof *userdata_opt);
+ if (!userdata_opt) {
+ return false;
+ }
+ size_t size = ntohs(userdata_opt->size);
+ char *userdata_opt_data = ofpbuf_try_pull(userdata, size);
+ if (!userdata_opt_data) {
+ return false;
+ }
+ switch (ntohs(userdata_opt->opt_code)) {
+ case EMPTY_LB_VIP:
+ vip = xmemdup0(userdata_opt_data, size);
+ break;
+ case EMPTY_LB_PROTOCOL:
+ protocol = xmemdup0(userdata_opt_data, size);
+ break;
+ case EMPTY_LB_LOAD_BALANCER:
+ load_balancer = xmemdup0(userdata_opt_data, size);
+ break;
+ default:
+ OVS_NOT_REACHED();
+ }
+ hash = hash_bytes(userdata_opt_data, size, hash);
+ }
+ if (!vip || !protocol || !load_balancer) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "missing lb parameters in userdata");
+ return false;
+ }
+
+ struct empty_lb_backends_event *event;
+
+ event = pinctrl_find_empty_lb_backends_event(vip, protocol,
+ load_balancer, hash);
+ if (!event) {
+ if (hmap_count(&event_table[OVN_EVENT_EMPTY_LB_BACKENDS]) >= 1000) {
+ COVERAGE_INC(pinctrl_drop_controller_event);
+ return false;
+ }
+
+ event = xzalloc(sizeof *event);
+ hmap_insert(&event_table[OVN_EVENT_EMPTY_LB_BACKENDS],
+ &event->hmap_node, hash);
+ event->vip = vip;
+ event->protocol = protocol;
+ event->load_balancer = load_balancer;
+ event->timestamp = time_msec();
+ notify_pinctrl_main();
+ } else {
+ free(vip);
+ free(protocol);
+ free(load_balancer);
+ }
+ return true;
+}
+
+static void
+pinctrl_handle_event(struct ofpbuf *userdata)
+ OVS_REQUIRES(pinctrl_mutex)
+{
+ ovs_be32 *pevent;
+
+ pevent = ofpbuf_try_pull(userdata, sizeof *pevent);
+ if (!pevent) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+ VLOG_WARN_RL(&rl, "event not present in the userdata");
+ return;
+ }
+
+ switch (ntohl(*pevent)) {
+ case OVN_EVENT_EMPTY_LB_BACKENDS:
+ pinctrl_handle_empty_lb_backends_opts(userdata);
+ break;
+ default:
+ return;
+ }
+}
@@ -38,6 +38,8 @@
#include "packets.h"
#include "openvswitch/shash.h"
#include "simap.h"
+#include "uuid.h"
+#include "socket-util.h"
VLOG_DEFINE_THIS_MODULE(actions);
@@ -1259,6 +1261,21 @@ format_CLONE(const struct ovnact_nest *nest, struct ds *s)
format_nested_action(nest, "clone", s);
}
+static void
+format_TRIGGER_EVENT(const struct ovnact_controller_event *event,
+ struct ds *s)
+{
+ ds_put_format(s, "trigger_event(event = \"%s\"",
+ event_to_string(event->event_type));
+ for (const struct ovnact_gen_option *o = event->options;
+ o < &event->options[event->n_options]; o++) {
+ ds_put_cstr(s, ", ");
+ ds_put_format(s, "%s = ", o->option->name);
+ expr_constant_set_format(&o->value, s);
+ }
+ ds_put_cstr(s, ");");
+}
+
static void
encode_nested_actions(const struct ovnact_nest *on,
const struct ovnact_encode_params *ep,
@@ -1362,6 +1379,52 @@ encode_CLONE(const struct ovnact_nest *on,
ofpact_finish_CLONE(ofpacts, &clone);
}
+static void
+encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
+ const struct ovnact_controller_event *event)
+{
+ for (const struct ovnact_gen_option *o = event->options;
+ o < &event->options[event->n_options]; o++) {
+ struct controller_event_opt_header *hdr =
+ ofpbuf_put_uninit(ofpacts, sizeof *hdr);
+ const union expr_constant *c = o->value.values;
+ size_t size;
+ hdr->opt_code = htons(o->option->code);
+ if (!strcmp(o->option->type, "str")) {
+ size = strlen(c->string);
+ hdr->size = htons(size);
+ ofpbuf_put(ofpacts, c->string, size);
+ } else {
+ /* All empty_lb_backends fields are of type 'str' */
+ OVS_NOT_REACHED();
+ }
+ }
+}
+
+static void
+encode_TRIGGER_EVENT(const struct ovnact_controller_event *event,
+ const struct ovnact_encode_params *ep OVS_UNUSED,
+ struct ofpbuf *ofpacts)
+{
+ size_t oc_offset;
+
+ oc_offset = encode_start_controller_op(ACTION_OPCODE_EVENT, false,
+ NX_CTLR_NO_METER, ofpacts);
+ ovs_be32 ofs = htonl(event->event_type);
+ ofpbuf_put(ofpacts, &ofs, sizeof ofs);
+
+ switch (event->event_type) {
+ case OVN_EVENT_EMPTY_LB_BACKENDS:
+ encode_event_empty_lb_backends_opts(ofpacts, event);
+ break;
+ case OVN_EVENT_MAX:
+ default:
+ OVS_NOT_REACHED();
+ }
+
+ encode_finish_controller_op(oc_offset, ofpacts);
+}
+
static void
ovnact_nest_free(struct ovnact_nest *on)
{
@@ -1576,6 +1639,117 @@ free_gen_options(struct ovnact_gen_option *options, size_t n)
free(options);
}
+static void
+validate_empty_lb_backends(struct action_context *ctx,
+ const struct ovnact_gen_option *options,
+ size_t n_options)
+{
+ for (const struct ovnact_gen_option *o = options;
+ o < &options[n_options]; o++) {
+ const union expr_constant *c = o->value.values;
+ struct sockaddr_storage ss;
+ struct uuid uuid;
+
+ if (o->value.n_values > 1 || !c->string) {
+ lexer_error(ctx->lexer, "Invalid value for \"%s\" option",
+ o->option->name);
+ return;
+ }
+
+ switch (o->option->code) {
+ case EMPTY_LB_VIP:
+ if (!inet_parse_active(c->string, 0, &ss, false)) {
+ lexer_error(ctx->lexer, "Invalid load balancer VIP '%s'",
+ c->string);
+ return;
+ }
+ break;
+ case EMPTY_LB_PROTOCOL:
+ if (strcmp(c->string, "tcp") && strcmp(c->string, "udp")) {
+ lexer_error(ctx->lexer,
+ "Load balancer protocol '%s' is not 'tcp' or 'udp'",
+ c->string);
+ return;
+ }
+ break;
+ case EMPTY_LB_LOAD_BALANCER:
+ if (!uuid_from_string(&uuid, c->string)) {
+ lexer_error(ctx->lexer, "Load balancer '%s' is not a UUID",
+ c->string);
+ return;
+ }
+ break;
+ }
+ }
+}
+
+static void
+parse_trigger_event(struct action_context *ctx,
+ struct ovnact_controller_event *event)
+{
+ int event_type = 0;
+
+ lexer_force_match(ctx->lexer, LEX_T_LPAREN);
+
+ /* Event type must be listed first */
+ if (!lexer_match_id(ctx->lexer, "event")) {
+ lexer_syntax_error(ctx->lexer, "Expecting 'event' option");
+ return;
+ }
+ if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
+ return;
+ }
+
+ if (ctx->lexer->token.type != LEX_T_STRING ||
+ strlen(ctx->lexer->token.s) >= 64) {
+ lexer_syntax_error(ctx->lexer, "Expecting string");
+ return;
+ }
+
+ event_type = string_to_event(ctx->lexer->token.s);
+ if (event_type < 0 || event_type >= OVN_EVENT_MAX) {
+ lexer_syntax_error(ctx->lexer, "Unknown event '%d'", event_type);
+ return;
+ }
+
+ event->event_type = event_type;
+ lexer_get(ctx->lexer);
+
+ lexer_match(ctx->lexer, LEX_T_COMMA);
+
+ size_t allocated_options = 0;
+ while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
+ if (event->n_options >= allocated_options) {
+ event->options = x2nrealloc(event->options, &allocated_options,
+ sizeof *event->options);
+ }
+
+ struct ovnact_gen_option *o = &event->options[event->n_options++];
+ memset(o, 0, sizeof *o);
+ parse_gen_opt(ctx, o,
+ &ctx->pp->controller_event_opts->event_opts[event_type],
+ event_to_string(event_type));
+ if (ctx->lexer->error) {
+ return;
+ }
+
+ lexer_match(ctx->lexer, LEX_T_COMMA);
+ }
+
+ switch (event_type) {
+ case OVN_EVENT_EMPTY_LB_BACKENDS:
+ validate_empty_lb_backends(ctx, event->options, event->n_options);
+ break;
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+static void
+ovnact_controller_event_free(struct ovnact_controller_event *event OVS_UNUSED)
+{
+}
+
static void
parse_put_opts(struct action_context *ctx, const struct expr_field *dst,
struct ovnact_put_opts *po, const struct hmap *gen_opts,
@@ -2514,6 +2688,8 @@ parse_action(struct action_context *ctx)
parse_LOG(ctx);
} else if (lexer_match_id(ctx->lexer, "set_meter")) {
parse_set_meter_action(ctx);
+ } else if (lexer_match_id(ctx->lexer, "trigger_event")) {
+ parse_trigger_event(ctx, ovnact_put_TRIGGER_EVENT(ctx->ovnacts));
} else {
lexer_syntax_error(ctx->lexer, "expecting action");
}
@@ -22,6 +22,7 @@
#include <netinet/icmp6.h>
#include "openvswitch/hmap.h"
#include "hash.h"
+#include "ovn/logical-fields.h"
/* Generic options map which is used to store dhcpv4 opts and dhcpv6 opts. */
struct gen_opts_map {
@@ -273,4 +274,49 @@ nd_ra_opts_init(struct hmap *nd_ra_opts)
nd_ra_opt_add(nd_ra_opts, "mtu", ND_OPT_MTU, "uint32");
}
+#define EMPTY_LB_VIP 1
+#define EMPTY_LB_PROTOCOL 2
+#define EMPTY_LB_LOAD_BALANCER 3
+
+/* Used in the OpenFlow PACKET_IN userdata */
+struct controller_event_opt_header {
+ ovs_be16 opt_code;
+ ovs_be16 size;
+};
+
+struct controller_event_options {
+ struct hmap event_opts[OVN_EVENT_MAX];
+};
+
+static inline void
+controller_event_opt_add(struct controller_event_options *event_opts,
+ enum ovn_controller_event event_type, char *opt_name,
+ size_t opt_code, char *opt_type)
+{
+ gen_opt_add(&event_opts->event_opts[event_type], opt_name, opt_code,
+ opt_type);
+}
+
+static inline void
+controller_event_opts_init(struct controller_event_options *opts)
+{
+ for (size_t i = 0; i < OVN_EVENT_MAX; i++) {
+ hmap_init(&opts->event_opts[i]);
+ }
+ controller_event_opt_add(opts, OVN_EVENT_EMPTY_LB_BACKENDS, "vip",
+ EMPTY_LB_VIP, "str");
+ controller_event_opt_add(opts, OVN_EVENT_EMPTY_LB_BACKENDS, "protocol",
+ EMPTY_LB_PROTOCOL, "str");
+ controller_event_opt_add(opts, OVN_EVENT_EMPTY_LB_BACKENDS,
+ "load_balancer", EMPTY_LB_LOAD_BALANCER, "str");
+}
+
+static inline void
+controller_event_opts_destroy(struct controller_event_options *opts)
+{
+ for (size_t i = 0; i < OVN_EVENT_MAX; i++) {
+ gen_opts_destroy(&opts->event_opts[i]);
+ }
+}
+
#endif /* OVN_DHCP_H */
@@ -1988,6 +1988,27 @@ tcp.flags = RST;
<p><b>Prerequisite:</b> <code>tcp</code></p>
</dd>
+
+ <dt><code>trigger_event;</code></dt>
+ <dd>
+ <p>
+ This action is used to allow ovs-vswitchd to report CMS related
+ events writing them in <ref table="Controller_Event"/> table.
+ Supported event:
+ </p>
+
+ <ul>
+ <li>
+ <p>
+ <dfn>empty_lb_backends</dfn>. This event is raised if a
+ received packet is destined for a load balancer VIP that has
+ no configured backend destinations. For this event, the event
+ info includes the load balancer VIP, the load balancer UUID,
+ and the transport protocol.
+ </p>
+ </li>
+ </ul>
+ </dd>
</dl>
</column>
@@ -2135,6 +2135,9 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
execute_ovnfield_load(ovnact_get_OVNFIELD_LOAD(a), super);
break;
+ case OVNACT_TRIGGER_EVENT:
+ break;
+
case OVNACT_CHECK_PKT_LARGER:
break;
}
@@ -1333,6 +1333,16 @@ tcp_reset { };
encodes as controller(userdata=00.00.00.0b.00.00.00.00)
has prereqs tcp
+# trigger_event
+trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c");
+ encodes as controller(userdata=00.00.00.0f.00.00.00.00.00.00.00.00.00.01.00.0b.31.30.2e.30.2e.30.2e.31.3a.38.30.00.02.00.03.74.63.70.00.03.00.24.31.32.33.34.35.36.37.38.2d.61.62.63.64.2d.39.38.37.36.2d.66.65.64.63.2d.31.31.31.31.39.66.38.65.37.64.36.63)
+
+# Testing invalid vip results in extra error messages from socket-util.c
+trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "sctp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c");
+ Load balancer protocol 'sctp' is not 'tcp' or 'udp'
+trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "bacon");
+ Load balancer 'bacon' is not a UUID
+
# Contradictionary prerequisites (allowed but not useful):
ip4.src = ip6.src[0..31];
encodes as move:NXM_NX_IPV6_SRC[0..31]->NXM_OF_IP_SRC[]
@@ -157,7 +157,8 @@ create_symtab(struct shash *symtab)
static void
create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
- struct hmap *nd_ra_opts)
+ struct hmap *nd_ra_opts,
+ struct controller_event_options *event_opts)
{
hmap_init(dhcp_opts);
dhcp_opt_add(dhcp_opts, "offerip", 0, "ipv4");
@@ -197,6 +198,9 @@ create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
/* IPv6 ND RA options. */
hmap_init(nd_ra_opts);
nd_ra_opts_init(nd_ra_opts);
+
+ /* OVN controller events options. */
+ controller_event_opts_init(event_opts);
}
static void
@@ -1229,12 +1233,13 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
struct hmap dhcp_opts;
struct hmap dhcpv6_opts;
struct hmap nd_ra_opts;
+ struct controller_event_options event_opts;
struct simap ports;
struct ds input;
bool ok = true;
create_symtab(&symtab);
- create_gen_opts(&dhcp_opts, &dhcpv6_opts, &nd_ra_opts);
+ create_gen_opts(&dhcp_opts, &dhcpv6_opts, &nd_ra_opts, &event_opts);
/* Initialize group ids. */
struct ovn_extend_table group_table;
@@ -1264,6 +1269,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
.dhcp_opts = &dhcp_opts,
.dhcpv6_opts = &dhcpv6_opts,
.nd_ra_opts = &nd_ra_opts,
+ .controller_event_opts = &event_opts,
.n_tables = 24,
.cur_ltable = 10,
};
@@ -1351,6 +1357,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
dhcp_opts_destroy(&dhcp_opts);
dhcp_opts_destroy(&dhcpv6_opts);
nd_ra_opts_destroy(&nd_ra_opts);
+ controller_event_opts_destroy(&event_opts);
ovn_extend_table_destroy(&group_table);
ovn_extend_table_destroy(&meter_table);
exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);