@@ -292,6 +292,15 @@ enum expr_type {
EXPR_T_AND, /* Logical AND of 2 or more subexpressions. */
EXPR_T_OR, /* Logical OR of 2 or more subexpressions. */
EXPR_T_BOOLEAN, /* True or false constant. */
+ EXPR_T_CONDITION, /* Conditional to be evaluated in the
+ * controller during expr_simplify(),
+ * prior to constructing OpenFlow matches. */
+};
+
+/* Expression condition type. */
+enum expr_cond_type {
+ EXPR_COND_CHASSIS_RESIDENT, /* Check if specified logical port name is
+ * resident on the controller chassis. */
};
/* Relational operator. */
@@ -349,6 +358,14 @@ struct expr {
/* EXPR_T_BOOLEAN. */
bool boolean;
+
+ /* EXPR_T_CONDITION. */
+ struct {
+ enum expr_cond_type type;
+ bool not;
+ /* XXX Should arguments for conditions be generic? */
+ char *string;
+ } cond;
};
};
@@ -375,7 +392,10 @@ void expr_destroy(struct expr *);
struct expr *expr_annotate(struct expr *, const struct shash *symtab,
char **errorp);
-struct expr *expr_simplify(struct expr *);
+struct expr *expr_simplify(struct expr *,
+ bool (*is_chassis_resident)(const void *c_aux,
+ const char *port_name),
+ const void *c_aux);
struct expr *expr_normalize(struct expr *);
bool expr_honors_invariants(const struct expr *);
@@ -50,12 +50,18 @@ struct lookup_port_aux {
const struct sbrec_datapath_binding *dp;
};
+struct condition_aux {
+ const struct lport_index *lports;
+ const struct sbrec_chassis *chassis;
+};
+
static void consider_logical_flow(const struct lport_index *lports,
const struct mcgroup_index *mcgroups,
const struct sbrec_logical_flow *lflow,
const struct hmap *local_datapaths,
struct group_table *group_table,
const struct simap *ct_zones,
+ const struct sbrec_chassis *chassis,
struct hmap *dhcp_opts,
struct hmap *dhcpv6_opts,
uint32_t *conj_id_ofs,
@@ -85,6 +91,16 @@ lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
}
static bool
+is_chassis_resident_cb(const void *c_aux_, const char *port_name)
+{
+ const struct condition_aux *c_aux = c_aux_;
+
+ const struct sbrec_port_binding *pb
+ = lport_lookup_by_name(c_aux->lports, port_name);
+ return pb && pb->chassis && pb->chassis == c_aux->chassis;
+}
+
+static bool
is_switch(const struct sbrec_datapath_binding *ldp)
{
return smap_get(&ldp->external_ids, "logical-switch") != NULL;
@@ -98,6 +114,7 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
const struct hmap *local_datapaths,
struct group_table *group_table,
const struct simap *ct_zones,
+ const struct sbrec_chassis *chassis,
const struct shash *addr_sets,
struct hmap *flow_table)
{
@@ -121,7 +138,7 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
SBREC_LOGICAL_FLOW_FOR_EACH (lflow, ctx->ovnsb_idl) {
consider_logical_flow(lports, mcgroups, lflow, local_datapaths,
- group_table, ct_zones,
+ group_table, ct_zones, chassis,
&dhcp_opts, &dhcpv6_opts, &conj_id_ofs,
addr_sets, flow_table);
}
@@ -137,6 +154,7 @@ consider_logical_flow(const struct lport_index *lports,
const struct hmap *local_datapaths,
struct group_table *group_table,
const struct simap *ct_zones,
+ const struct sbrec_chassis *chassis,
struct hmap *dhcp_opts,
struct hmap *dhcpv6_opts,
uint32_t *conj_id_ofs,
@@ -235,7 +253,8 @@ consider_logical_flow(const struct lport_index *lports,
return;
}
- expr = expr_simplify(expr);
+ struct condition_aux cond_aux = { lports, chassis };
+ expr = expr_simplify(expr, is_chassis_resident_cb, &cond_aux);
expr = expr_normalize(expr);
uint32_t n_conjs = expr_to_matches(expr, lookup_port_cb, &aux,
&matches);
@@ -356,7 +375,9 @@ add_neighbor_flows(struct controller_ctx *ctx,
/* Translates logical flows in the Logical_Flow table in the OVN_SB database
* into OpenFlow flows. See ovn-architecture(7) for more information. */
void
-lflow_run(struct controller_ctx *ctx, const struct lport_index *lports,
+lflow_run(struct controller_ctx *ctx,
+ const struct sbrec_chassis *chassis,
+ const struct lport_index *lports,
const struct mcgroup_index *mcgroups,
const struct hmap *local_datapaths,
struct group_table *group_table,
@@ -364,8 +385,8 @@ lflow_run(struct controller_ctx *ctx, const struct lport_index *lports,
const struct shash *addr_sets,
struct hmap *flow_table)
{
- add_logical_flows(ctx, lports, mcgroups, local_datapaths,
- group_table, ct_zones, addr_sets, flow_table);
+ add_logical_flows(ctx, lports, mcgroups, local_datapaths, group_table,
+ ct_zones, chassis, addr_sets, flow_table);
add_neighbor_flows(ctx, lports, flow_table);
}
@@ -40,6 +40,7 @@ struct group_table;
struct hmap;
struct lport_index;
struct mcgroup_index;
+struct sbrec_chassis;
struct simap;
struct uuid;
@@ -61,7 +62,9 @@ struct uuid;
#define LOG_PIPELINE_LEN 16
void lflow_init(void);
-void lflow_run(struct controller_ctx *, const struct lport_index *,
+void lflow_run(struct controller_ctx *,
+ const struct sbrec_chassis *chassis,
+ const struct lport_index *,
const struct mcgroup_index *,
const struct hmap *local_datapaths,
struct group_table *group_table,
@@ -621,8 +621,9 @@ main(int argc, char *argv[])
commit_ct_zones(br_int, &pending_ct_zones);
struct hmap flow_table = HMAP_INITIALIZER(&flow_table);
- lflow_run(&ctx, &lports, &mcgroups, &local_datapaths,
- &group_table, &ct_zones, &addr_sets, &flow_table);
+ lflow_run(&ctx, chassis, &lports, &mcgroups,
+ &local_datapaths, &group_table, &ct_zones,
+ &addr_sets, &flow_table);
physical_run(&ctx, mff_ovn_geneve,
br_int, chassis, &ct_zones, &lports,
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2015, 2016, 2017 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -233,6 +233,11 @@ expr_not(struct expr *expr)
case EXPR_T_BOOLEAN:
expr->boolean = !expr->boolean;
break;
+
+ case EXPR_T_CONDITION:
+ expr->cond.not = !expr->cond.not;
+ break;
+
default:
OVS_NOT_REACHED();
}
@@ -290,6 +295,9 @@ expr_fix(struct expr *expr)
case EXPR_T_BOOLEAN:
return expr;
+ case EXPR_T_CONDITION:
+ return expr;
+
default:
OVS_NOT_REACHED();
}
@@ -394,6 +402,21 @@ expr_format_andor(const struct expr *e, const char *op, struct ds *s)
}
}
+static void
+expr_format_condition(const struct expr *e, struct ds *s)
+{
+ if (e->cond.not) {
+ ds_put_char(s, '!');
+ }
+ switch (e->cond.type) {
+ case EXPR_COND_CHASSIS_RESIDENT:
+ ds_put_format(s, "is_chassis_resident(");
+ json_string_escape(e->cond.string, s);
+ ds_put_char(s, ')');
+ break;
+ }
+}
+
/* Appends a string form of 'e' to 's'. The string form is acceptable for
* parsing back into an equivalent expression. */
void
@@ -415,6 +438,10 @@ expr_format(const struct expr *e, struct ds *s)
case EXPR_T_BOOLEAN:
ds_put_char(s, e->boolean ? '1' : '0');
break;
+
+ case EXPR_T_CONDITION:
+ expr_format_condition(e, s);
+ break;
}
}
@@ -973,6 +1000,29 @@ expr_addr_sets_destroy(struct shash *addr_sets)
}
static struct expr *
+parse_chassis_resident(struct expr_context *ctx)
+{
+ if (ctx->lexer->token.type != LEX_T_STRING) {
+ lexer_syntax_error(ctx->lexer, "expecting string");
+ return NULL;
+ }
+
+ struct expr *e = xzalloc(sizeof *e);
+ e->type = EXPR_T_CONDITION;
+ e->cond.type = EXPR_COND_CHASSIS_RESIDENT;
+ e->cond.not = false;
+ e->cond.string = xstrdup(ctx->lexer->token.s);
+
+ lexer_get(ctx->lexer);
+ if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
+ expr_destroy(e);
+ return NULL;
+ }
+
+ return e;
+}
+
+static struct expr *
expr_parse_primary(struct expr_context *ctx, bool *atomic)
{
*atomic = false;
@@ -991,6 +1041,16 @@ expr_parse_primary(struct expr_context *ctx, bool *atomic)
enum expr_relop r;
struct expr_constant_set c;
+ if (lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
+ if (lexer_match_id(ctx->lexer, "is_chassis_resident")) {
+ lexer_get(ctx->lexer); /* Skip "(". */
+ *atomic = true;
+ return parse_chassis_resident(ctx);
+ }
+ lexer_error(ctx->lexer, "parsing function name");
+ return NULL;
+ }
+
if (!parse_field(ctx, &f)) {
return NULL;
}
@@ -1385,6 +1445,7 @@ expr_get_level(const struct expr *expr)
return level;
case EXPR_T_BOOLEAN:
+ case EXPR_T_CONDITION:
return EXPR_L_BOOLEAN;
default:
@@ -1467,6 +1528,14 @@ expr_clone_andor(struct expr *expr)
return new;
}
+static struct expr *
+expr_clone_condition(struct expr *expr)
+{
+ struct expr *new = xmemdup(expr, sizeof *expr);
+ new->cond.string = xstrdup(new->cond.string);
+ return new;
+}
+
/* Returns a clone of 'expr'. This is a "deep copy": neither the returned
* expression nor any of its substructure will be shared with 'expr'. */
struct expr *
@@ -1482,6 +1551,9 @@ expr_clone(struct expr *expr)
case EXPR_T_BOOLEAN:
return expr_create_boolean(expr->boolean);
+
+ case EXPR_T_CONDITION:
+ return expr_clone_condition(expr);
}
OVS_NOT_REACHED();
}
@@ -1513,6 +1585,10 @@ expr_destroy(struct expr *expr)
case EXPR_T_BOOLEAN:
break;
+
+ case EXPR_T_CONDITION:
+ free(expr->cond.string);
+ break;
}
free(expr);
}
@@ -1641,6 +1717,7 @@ expr_annotate__(struct expr *expr, const struct shash *symtab,
}
case EXPR_T_BOOLEAN:
+ case EXPR_T_CONDITION:
*errorp = NULL;
return expr;
@@ -1783,10 +1860,36 @@ expr_simplify_relational(struct expr *expr)
return new ? new : expr_create_boolean(false);
}
+/* Resolves condition and replaces the expression with a boolean. */
+static struct expr *
+expr_simplify_condition(struct expr *expr,
+ bool (*is_chassis_resident)(const void *c_aux,
+ const char *port_name),
+ const void *c_aux)
+{
+ bool result;
+
+ switch (expr->cond.type) {
+ case EXPR_COND_CHASSIS_RESIDENT:
+ result = is_chassis_resident(c_aux, expr->cond.string);
+ break;
+
+ default:
+ OVS_NOT_REACHED();
+ }
+
+ result ^= expr->cond.not;
+ expr_destroy(expr);
+ return expr_create_boolean(result);
+}
+
/* Takes ownership of 'expr' and returns an equivalent expression whose
* EXPR_T_CMP nodes use only tests for equality (EXPR_R_EQ). */
struct expr *
-expr_simplify(struct expr *expr)
+expr_simplify(struct expr *expr,
+ bool (*is_chassis_resident)(const void *c_aux,
+ const char *port_name),
+ const void *c_aux)
{
struct expr *sub, *next;
@@ -1801,12 +1904,16 @@ expr_simplify(struct expr *expr)
case EXPR_T_OR:
LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
ovs_list_remove(&sub->node);
- expr_insert_andor(expr, next, expr_simplify(sub));
+ expr_insert_andor(expr, next,
+ expr_simplify(sub, is_chassis_resident, c_aux));
}
return expr_fix(expr);
case EXPR_T_BOOLEAN:
return expr;
+
+ case EXPR_T_CONDITION:
+ return expr_simplify_condition(expr, is_chassis_resident, c_aux);
}
OVS_NOT_REACHED();
}
@@ -1834,6 +1941,7 @@ expr_is_cmp(const struct expr *expr)
}
case EXPR_T_BOOLEAN:
+ case EXPR_T_CONDITION:
return NULL;
default:
@@ -1930,6 +2038,8 @@ crush_and_string(struct expr *expr, const struct expr_symbol *symbol)
}
free(new);
break;
+ case EXPR_T_CONDITION:
+ OVS_NOT_REACHED();
}
}
@@ -2024,6 +2134,8 @@ crush_and_numeric(struct expr *expr, const struct expr_symbol *symbol)
}
expr_destroy(new);
break;
+ case EXPR_T_CONDITION:
+ OVS_NOT_REACHED();
}
}
if (ovs_list_is_empty(&expr->andor)) {
@@ -2236,6 +2348,10 @@ crush_cmps(struct expr *expr, const struct expr_symbol *symbol)
case EXPR_T_BOOLEAN:
return expr;
+ /* Should not hit expression type condition, since crush_cmps is only
+ * called during expr_normalize, after expr_simplify which resolves
+ * all conditions. */
+ case EXPR_T_CONDITION:
default:
OVS_NOT_REACHED();
}
@@ -2443,8 +2559,13 @@ expr_normalize(struct expr *expr)
case EXPR_T_BOOLEAN:
return expr;
+
+ /* Should not hit expression type condition, since expr_normalize is
+ * only called after expr_simplify, which resolves all conditions. */
+ case EXPR_T_CONDITION:
+ default:
+ OVS_NOT_REACHED();
}
- OVS_NOT_REACHED();
}
/* Creates, initializes, and returns a new 'struct expr_match'. If 'm' is
@@ -2611,6 +2732,8 @@ add_conjunction(const struct expr *and,
break;
case EXPR_T_AND:
case EXPR_T_BOOLEAN:
+ case EXPR_T_CONDITION:
+ default:
OVS_NOT_REACHED();
}
}
@@ -2742,6 +2865,12 @@ expr_to_matches(const struct expr *expr,
/* No match. */
}
break;
+
+ /* Should not hit expression type condition, since expr_to_matches is
+ * only called after expr_simplify, which resolves all conditions. */
+ case EXPR_T_CONDITION:
+ default:
+ OVS_NOT_REACHED();
}
return n_conjs;
}
@@ -2818,6 +2947,7 @@ expr_honors_invariants(const struct expr *expr)
return true;
case EXPR_T_BOOLEAN:
+ case EXPR_T_CONDITION:
return true;
default:
@@ -2866,6 +2996,9 @@ expr_is_normalized(const struct expr *expr)
case EXPR_T_BOOLEAN:
return true;
+ case EXPR_T_CONDITION:
+ return false;
+
default:
OVS_NOT_REACHED();
}
@@ -2958,6 +3091,11 @@ expr_evaluate(const struct expr *e, const struct flow *uflow,
case EXPR_T_BOOLEAN:
return e->boolean;
+ case EXPR_T_CONDITION:
+ /* Assume tests calling expr_evaluate are not chassis specific, so
+ * is_chassis_resident evaluates as true. */
+ return (e->cond.not ? false : true);
+
default:
OVS_NOT_REACHED();
}
@@ -3011,6 +3149,15 @@ expr_resolve_field(const struct expr_field *f)
return (struct mf_subfield) { symbol->field, ofs, n_bits };
}
+static bool
+microflow_is_chassis_resident_cb(const void *c_aux OVS_UNUSED,
+ const char *port_name OVS_UNUSED)
+{
+ /* Assume tests calling expr_parse_microflow are not chassis specific, so
+ * is_chassis_resident need not be supplied and should return true. */
+ return true;
+}
+
static struct expr *
expr_parse_microflow__(struct lexer *lexer,
const struct shash *symtab,
@@ -3031,7 +3178,7 @@ expr_parse_microflow__(struct lexer *lexer,
struct ds annotated = DS_EMPTY_INITIALIZER;
expr_format(e, &annotated);
- e = expr_simplify(e);
+ e = expr_simplify(e, microflow_is_chassis_resident_cb, NULL);
e = expr_normalize(e);
struct match m = MATCH_CATCHALL_INITIALIZER;
@@ -3067,6 +3214,9 @@ expr_parse_microflow__(struct lexer *lexer,
}
break;
+ /* Should not hit expression type condition, since
+ * expr_simplify was called above. */
+ case EXPR_T_CONDITION:
default:
OVS_NOT_REACHED();
}
@@ -534,6 +534,20 @@
packet.
</p>
+ <p>
+ Match expressions also support a kind of function syntax. The
+ following functions are supported:
+ </p>
+
+ <dl>
+ <dt><code>is_chassis_resident(<var>lport</var>)</code></dt>
+ <dd>
+ Evaluates to true on a chassis on which logical port <var>lport</var>
+ (a quoted string) resides, and to false elsewhere. This function was
+ introduced in OVN 2.7.
+ </dd>
+ </dl>
+
<p><em>Symbols</em></p>
<p>
@@ -585,6 +585,25 @@ read_address_sets(void)
}
}
+static bool
+ovntrace_is_chassis_resident(const void *aux OVS_UNUSED,
+ const char *port_name)
+{
+ if (port_name[0] == '\0') {
+ return true;
+ }
+
+ const struct ovntrace_port *port = shash_find_data(&ports, port_name);
+ if (port) {
+ /* Since ovntrace is not chassis specific, assume any port
+ * that exists is resident. */
+ return true;
+ }
+
+ VLOG_WARN("%s: unknown logical port\n", port_name);
+ return false;
+}
+
static int
compare_flow(const void *a_, const void *b_)
{
@@ -662,7 +681,7 @@ read_flows(void)
continue;
}
if (match) {
- match = expr_simplify(match);
+ match = expr_simplify(match, ovntrace_is_chassis_resident, NULL);
}
struct ovntrace_flow *flow = xzalloc(sizeof *flow);
@@ -453,6 +453,20 @@ AT_CHECK([simplify 'tcp.dst < 65535'], [0],
]])
AT_CLEANUP
+AT_SETUP([ovn -- is_chassis_resident simplification])
+simplify() {
+ echo "$1" | ovstest test-ovn simplify-expr
+}
+AT_CHECK([simplify 'is_chassis_resident("eth1")'], [0], [1
+])
+AT_CHECK([simplify 'is_chassis_resident("eth2")'], [0], [0
+])
+AT_CHECK([simplify '!is_chassis_resident("eth1")'], [0], [0
+])
+AT_CHECK([simplify '!is_chassis_resident("eth2")'], [0], [1
+])
+AT_CLEANUP
+
AT_SETUP([ovn -- 4-term numeric expression normalization])
AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=3 --svars=0 --bits=1 4], [0],
[Tested normalizing 1874026 expressions of 4 terminals with 3 numeric vars (each 1 bits) in terms of operators == != < <= > >=.
@@ -220,6 +220,17 @@ lookup_port_cb(const void *ports_, const char *port_name, unsigned int *portp)
return true;
}
+static bool
+is_chassis_resident_cb(const void *ports_, const char *port_name)
+{
+ const struct simap *ports = ports_;
+ const struct simap_node *node = simap_find(ports, port_name);
+ if (node) {
+ return true;
+ }
+ return false;
+}
+
static void
test_parse_expr__(int steps)
{
@@ -247,7 +258,7 @@ test_parse_expr__(int steps)
}
if (!error) {
if (steps > 1) {
- expr = expr_simplify(expr);
+ expr = expr_simplify(expr, is_chassis_resident_cb, &ports);
}
if (steps > 2) {
expr = expr_normalize(expr);
@@ -792,6 +803,13 @@ free_rule(struct test_rule *test_rule)
free(test_rule);
}
+static bool
+tree_shape_is_chassis_resident_cb(const void *c_aux OVS_UNUSED,
+ const char *port_name OVS_UNUSED)
+{
+ return true;
+}
+
static int
test_tree_shape_exhaustively(struct expr *expr, struct shash *symtab,
struct expr *terminals[], int n_terminals,
@@ -838,7 +856,9 @@ test_tree_shape_exhaustively(struct expr *expr, struct shash *symtab,
exit(EXIT_FAILURE);
}
} else if (operation >= OP_SIMPLIFY) {
- modified = expr_simplify(expr_clone(expr));
+ modified = expr_simplify(expr_clone(expr),
+ tree_shape_is_chassis_resident_cb,
+ NULL);
ovs_assert(expr_honors_invariants(modified));
if (operation >= OP_NORMALIZE) {