mirror of
https://github.com/openvswitch/ovs
synced 2025-09-01 23:05:29 +00:00
ovn: Add address set support.
Update the OVN expression parser to support address sets. Previously, you could have a set of IP or MAC addresses in this form: {addr1, addr2, ..., addrN} This patch adds support for a bit of indirection where we can define a set of addresses and refer to them by name. $name This '$name' can be used in the expresssions like {addr1, addr2, $name, ... } {$name} $name A future patch will expose the ability to define address sets for use. Signed-off-by: Russell Bryant <russell@ovn.org> Co-authored-by: Babu Shanmugam <bschanmu@redhat.com> Signed-off-by: Babu Shanmugam <bschanmu@redhat.com> [blp@ovn.org made numerous small changes] Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
committed by
Ben Pfaff
parent
eff49a5620
commit
2c5cbb15ff
@@ -306,7 +306,7 @@ add_logical_flows(struct controller_ctx *ctx, const struct lport_index *lports,
|
|||||||
struct hmap matches;
|
struct hmap matches;
|
||||||
struct expr *expr;
|
struct expr *expr;
|
||||||
|
|
||||||
expr = expr_parse_string(lflow->match, &symtab, &error);
|
expr = expr_parse_string(lflow->match, &symtab, NULL, &error);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
if (prereqs) {
|
if (prereqs) {
|
||||||
expr = expr_combine(EXPR_T_AND, expr, prereqs);
|
expr = expr_combine(EXPR_T_AND, expr, prereqs);
|
||||||
|
@@ -208,7 +208,7 @@ add_prerequisite(struct action_context *ctx, const char *prerequisite)
|
|||||||
struct expr *expr;
|
struct expr *expr;
|
||||||
char *error;
|
char *error;
|
||||||
|
|
||||||
expr = expr_parse_string(prerequisite, ctx->ap->symtab, &error);
|
expr = expr_parse_string(prerequisite, ctx->ap->symtab, NULL, &error);
|
||||||
ovs_assert(!error);
|
ovs_assert(!error);
|
||||||
ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr);
|
ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr);
|
||||||
}
|
}
|
||||||
|
113
ovn/lib/expr.c
113
ovn/lib/expr.c
@@ -409,6 +409,7 @@ expr_print(const struct expr *e)
|
|||||||
struct expr_context {
|
struct expr_context {
|
||||||
struct lexer *lexer; /* Lexer for pulling more tokens. */
|
struct lexer *lexer; /* Lexer for pulling more tokens. */
|
||||||
const struct shash *symtab; /* Symbol table. */
|
const struct shash *symtab; /* Symbol table. */
|
||||||
|
const struct shash *macros; /* Table of macros. */
|
||||||
char *error; /* Error, if any, otherwise NULL. */
|
char *error; /* Error, if any, otherwise NULL. */
|
||||||
bool not; /* True inside odd number of NOT operators. */
|
bool not; /* True inside odd number of NOT operators. */
|
||||||
};
|
};
|
||||||
@@ -728,6 +729,33 @@ assign_constant_set_type(struct expr_context *ctx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_macros(struct expr_context *ctx, struct expr_constant_set *cs,
|
||||||
|
size_t *allocated_values)
|
||||||
|
{
|
||||||
|
struct expr_constant_set *addr_set
|
||||||
|
= shash_find_data(ctx->macros, ctx->lexer->token.s);
|
||||||
|
if (!addr_set) {
|
||||||
|
expr_syntax_error(ctx, "expecting address set name.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!assign_constant_set_type(ctx, cs, EXPR_C_INTEGER)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t n_values = cs->n_values + addr_set->n_values;
|
||||||
|
if (n_values >= *allocated_values) {
|
||||||
|
cs->values = xrealloc(cs->values, n_values * sizeof *cs->values);
|
||||||
|
*allocated_values = n_values;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < addr_set->n_values; i++) {
|
||||||
|
cs->values[cs->n_values++] = addr_set->values[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
|
parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
|
||||||
size_t *allocated_values)
|
size_t *allocated_values)
|
||||||
@@ -759,6 +787,12 @@ parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
|
|||||||
}
|
}
|
||||||
lexer_get(ctx->lexer);
|
lexer_get(ctx->lexer);
|
||||||
return true;
|
return true;
|
||||||
|
} else if (ctx->lexer->token.type == LEX_T_MACRO) {
|
||||||
|
if (!parse_macros(ctx, cs, allocated_values)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lexer_get(ctx->lexer);
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
expr_syntax_error(ctx, "expecting constant.");
|
expr_syntax_error(ctx, "expecting constant.");
|
||||||
return false;
|
return false;
|
||||||
@@ -808,6 +842,69 @@ expr_constant_set_destroy(struct expr_constant_set *cs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Adds a macro named 'name' to 'macros', replacing any existing macro with the
|
||||||
|
* given name. */
|
||||||
|
void
|
||||||
|
expr_macros_add(struct shash *macros, const char *name,
|
||||||
|
const char *const *values, size_t n_values)
|
||||||
|
{
|
||||||
|
/* Replace any existing entry for this name. */
|
||||||
|
expr_macros_remove(macros, name);
|
||||||
|
|
||||||
|
struct expr_constant_set *cs = xzalloc(sizeof *cs);
|
||||||
|
cs->type = EXPR_C_INTEGER;
|
||||||
|
cs->in_curlies = true;
|
||||||
|
cs->n_values = 0;
|
||||||
|
cs->values = xmalloc(n_values * sizeof *cs->values);
|
||||||
|
for (size_t i = 0; i < n_values; i++) {
|
||||||
|
/* Use the lexer to convert each macro into the proper
|
||||||
|
* integer format. */
|
||||||
|
struct lexer lex;
|
||||||
|
lexer_init(&lex, values[i]);
|
||||||
|
lexer_get(&lex);
|
||||||
|
if (lex.token.type != LEX_T_INTEGER
|
||||||
|
&& lex.token.type != LEX_T_MASKED_INTEGER) {
|
||||||
|
VLOG_WARN("Invalid address set entry: '%s', token type: %d",
|
||||||
|
values[i], lex.token.type);
|
||||||
|
} else {
|
||||||
|
union expr_constant *c = &cs->values[cs->n_values++];
|
||||||
|
c->value = lex.token.value;
|
||||||
|
c->format = lex.token.format;
|
||||||
|
c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
|
||||||
|
if (c->masked) {
|
||||||
|
c->mask = lex.token.mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lexer_destroy(&lex);
|
||||||
|
}
|
||||||
|
|
||||||
|
shash_add(macros, name, cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
expr_macros_remove(struct shash *macros, const char *name)
|
||||||
|
{
|
||||||
|
struct expr_constant_set *cs = shash_find_and_delete(macros, name);
|
||||||
|
if (cs) {
|
||||||
|
expr_constant_set_destroy(cs);
|
||||||
|
free(cs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Destroy all contents of 'macros'. */
|
||||||
|
void
|
||||||
|
expr_macros_destroy(struct shash *macros)
|
||||||
|
{
|
||||||
|
struct shash_node *node, *next;
|
||||||
|
|
||||||
|
SHASH_FOR_EACH_SAFE (node, next, macros) {
|
||||||
|
struct expr_constant_set *cs = node->data;
|
||||||
|
|
||||||
|
shash_delete(macros, node);
|
||||||
|
expr_constant_set_destroy(cs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static struct expr *
|
static struct expr *
|
||||||
expr_parse_primary(struct expr_context *ctx, bool *atomic)
|
expr_parse_primary(struct expr_context *ctx, bool *atomic)
|
||||||
{
|
{
|
||||||
@@ -980,9 +1077,12 @@ expr_parse__(struct expr_context *ctx)
|
|||||||
* The caller must eventually free the returned expression (with
|
* The caller must eventually free the returned expression (with
|
||||||
* expr_destroy()) or error (with free()). */
|
* expr_destroy()) or error (with free()). */
|
||||||
struct expr *
|
struct expr *
|
||||||
expr_parse(struct lexer *lexer, const struct shash *symtab, char **errorp)
|
expr_parse(struct lexer *lexer, const struct shash *symtab,
|
||||||
|
const struct shash *macros, char **errorp)
|
||||||
{
|
{
|
||||||
struct expr_context ctx = { .lexer = lexer, .symtab = symtab };
|
struct expr_context ctx = { .lexer = lexer,
|
||||||
|
.symtab = symtab,
|
||||||
|
.macros = macros };
|
||||||
struct expr *e = expr_parse__(&ctx);
|
struct expr *e = expr_parse__(&ctx);
|
||||||
*errorp = ctx.error;
|
*errorp = ctx.error;
|
||||||
ovs_assert((ctx.error != NULL) != (e != NULL));
|
ovs_assert((ctx.error != NULL) != (e != NULL));
|
||||||
@@ -991,14 +1091,15 @@ expr_parse(struct lexer *lexer, const struct shash *symtab, char **errorp)
|
|||||||
|
|
||||||
/* Like expr_parse(), but the expression is taken from 's'. */
|
/* Like expr_parse(), but the expression is taken from 's'. */
|
||||||
struct expr *
|
struct expr *
|
||||||
expr_parse_string(const char *s, const struct shash *symtab, char **errorp)
|
expr_parse_string(const char *s, const struct shash *symtab,
|
||||||
|
const struct shash *macros, char **errorp)
|
||||||
{
|
{
|
||||||
struct lexer lexer;
|
struct lexer lexer;
|
||||||
struct expr *expr;
|
struct expr *expr;
|
||||||
|
|
||||||
lexer_init(&lexer, s);
|
lexer_init(&lexer, s);
|
||||||
lexer_get(&lexer);
|
lexer_get(&lexer);
|
||||||
expr = expr_parse(&lexer, symtab, errorp);
|
expr = expr_parse(&lexer, symtab, macros, errorp);
|
||||||
if (!*errorp && lexer.token.type != LEX_T_END) {
|
if (!*errorp && lexer.token.type != LEX_T_END) {
|
||||||
*errorp = xstrdup("Extra tokens at end of input.");
|
*errorp = xstrdup("Extra tokens at end of input.");
|
||||||
expr_destroy(expr);
|
expr_destroy(expr);
|
||||||
@@ -1149,7 +1250,7 @@ expr_get_level(const struct expr *expr)
|
|||||||
static enum expr_level
|
static enum expr_level
|
||||||
expr_parse_level(const char *s, const struct shash *symtab, char **errorp)
|
expr_parse_level(const char *s, const struct shash *symtab, char **errorp)
|
||||||
{
|
{
|
||||||
struct expr *expr = expr_parse_string(s, symtab, errorp);
|
struct expr *expr = expr_parse_string(s, symtab, NULL, errorp);
|
||||||
enum expr_level level = expr ? expr_get_level(expr) : EXPR_L_NOMINAL;
|
enum expr_level level = expr ? expr_get_level(expr) : EXPR_L_NOMINAL;
|
||||||
expr_destroy(expr);
|
expr_destroy(expr);
|
||||||
return level;
|
return level;
|
||||||
@@ -1292,7 +1393,7 @@ parse_and_annotate(const char *s, const struct shash *symtab,
|
|||||||
char *error;
|
char *error;
|
||||||
struct expr *expr;
|
struct expr *expr;
|
||||||
|
|
||||||
expr = expr_parse_string(s, symtab, &error);
|
expr = expr_parse_string(s, symtab, NULL, &error);
|
||||||
if (expr) {
|
if (expr) {
|
||||||
expr = expr_annotate__(expr, symtab, nesting, &error);
|
expr = expr_annotate__(expr, symtab, nesting, &error);
|
||||||
}
|
}
|
||||||
|
@@ -352,8 +352,10 @@ expr_from_node(const struct ovs_list *node)
|
|||||||
void expr_format(const struct expr *, struct ds *);
|
void expr_format(const struct expr *, struct ds *);
|
||||||
void expr_print(const struct expr *);
|
void expr_print(const struct expr *);
|
||||||
struct expr *expr_parse(struct lexer *, const struct shash *symtab,
|
struct expr *expr_parse(struct lexer *, const struct shash *symtab,
|
||||||
|
const struct shash *macros,
|
||||||
char **errorp);
|
char **errorp);
|
||||||
struct expr *expr_parse_string(const char *, const struct shash *symtab,
|
struct expr *expr_parse_string(const char *, const struct shash *symtab,
|
||||||
|
const struct shash *macros,
|
||||||
char **errorp);
|
char **errorp);
|
||||||
|
|
||||||
struct expr *expr_clone(struct expr *);
|
struct expr *expr_clone(struct expr *);
|
||||||
@@ -453,4 +455,20 @@ char *expr_parse_constant_set(struct lexer *, const struct shash *symtab,
|
|||||||
OVS_WARN_UNUSED_RESULT;
|
OVS_WARN_UNUSED_RESULT;
|
||||||
void expr_constant_set_destroy(struct expr_constant_set *cs);
|
void expr_constant_set_destroy(struct expr_constant_set *cs);
|
||||||
|
|
||||||
|
|
||||||
|
/* Address sets, aka "macros".
|
||||||
|
*
|
||||||
|
* Instead of referring to a set of value as:
|
||||||
|
* {addr1, addr2, ..., addrN}
|
||||||
|
* You can register a set of values and refer to them as:
|
||||||
|
* $name
|
||||||
|
* The macros should all have integer/masked-integer values.
|
||||||
|
* The values that don't qualify are ignored.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void expr_macros_add(struct shash *macros, const char *name,
|
||||||
|
const char * const *values, size_t n_values);
|
||||||
|
void expr_macros_remove(struct shash *macros, const char *name);
|
||||||
|
void expr_macros_destroy(struct shash *macros);
|
||||||
|
|
||||||
#endif /* ovn/expr.h */
|
#endif /* ovn/expr.h */
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015 Nicira, Inc.
|
* Copyright (c) 2015, 2016 Nicira, Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -227,6 +227,10 @@ lex_token_format(const struct lex_token *token, struct ds *s)
|
|||||||
lex_token_format_masked_integer(token, s);
|
lex_token_format_masked_integer(token, s);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case LEX_T_MACRO:
|
||||||
|
ds_put_format(s, "$%s", token->s);
|
||||||
|
break;
|
||||||
|
|
||||||
case LEX_T_LPAREN:
|
case LEX_T_LPAREN:
|
||||||
ds_put_cstr(s, "(");
|
ds_put_cstr(s, "(");
|
||||||
break;
|
break;
|
||||||
@@ -514,7 +518,7 @@ lex_is_idn(unsigned char c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
lex_parse_id(const char *p, struct lex_token *token)
|
lex_parse_id(const char *p, enum lex_type type, struct lex_token *token)
|
||||||
{
|
{
|
||||||
const char *start = p;
|
const char *start = p;
|
||||||
|
|
||||||
@@ -522,11 +526,23 @@ lex_parse_id(const char *p, struct lex_token *token)
|
|||||||
p++;
|
p++;
|
||||||
} while (lex_is_idn(*p));
|
} while (lex_is_idn(*p));
|
||||||
|
|
||||||
token->type = LEX_T_ID;
|
token->type = type;
|
||||||
lex_token_strcpy(token, start, p - start);
|
lex_token_strcpy(token, start, p - start);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
lex_parse_macro(const char *p, struct lex_token *token)
|
||||||
|
{
|
||||||
|
p++;
|
||||||
|
if (!lex_is_id1(*p)) {
|
||||||
|
lex_error(token, "`$' must be followed by a valid identifier.");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lex_parse_id(p, LEX_T_MACRO, token);
|
||||||
|
}
|
||||||
|
|
||||||
/* Initializes 'token' and parses the first token from the beginning of
|
/* Initializes 'token' and parses the first token from the beginning of
|
||||||
* null-terminated string 'p' into 'token'. Stores a pointer to the start of
|
* null-terminated string 'p' into 'token'. Stores a pointer to the start of
|
||||||
* the token (after skipping white space and comments, if any) into '*startp'.
|
* the token (after skipping white space and comments, if any) into '*startp'.
|
||||||
@@ -697,6 +713,10 @@ next:
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case '$':
|
||||||
|
p = lex_parse_macro(p, token);
|
||||||
|
break;
|
||||||
|
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
case '5': case '6': case '7': case '8': case '9':
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
case ':':
|
case ':':
|
||||||
@@ -715,12 +735,12 @@ next:
|
|||||||
* digits followed by a colon, but identifiers never do. */
|
* digits followed by a colon, but identifiers never do. */
|
||||||
p = (p[strspn(p, "0123456789abcdefABCDEF")] == ':'
|
p = (p[strspn(p, "0123456789abcdefABCDEF")] == ':'
|
||||||
? lex_parse_integer(p, token)
|
? lex_parse_integer(p, token)
|
||||||
: lex_parse_id(p, token));
|
: lex_parse_id(p, LEX_T_ID, token));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (lex_is_id1(*p)) {
|
if (lex_is_id1(*p)) {
|
||||||
p = lex_parse_id(p, token);
|
p = lex_parse_id(p, LEX_T_ID, token);
|
||||||
} else {
|
} else {
|
||||||
if (isprint((unsigned char) *p)) {
|
if (isprint((unsigned char) *p)) {
|
||||||
lex_error(token, "Invalid character `%c' in input.", *p);
|
lex_error(token, "Invalid character `%c' in input.", *p);
|
||||||
|
@@ -36,6 +36,7 @@ enum lex_type {
|
|||||||
LEX_T_STRING, /* "foo" */
|
LEX_T_STRING, /* "foo" */
|
||||||
LEX_T_INTEGER, /* 12345 or 1.2.3.4 or ::1 or 01:02:03:04:05 */
|
LEX_T_INTEGER, /* 12345 or 1.2.3.4 or ::1 or 01:02:03:04:05 */
|
||||||
LEX_T_MASKED_INTEGER, /* 12345/10 or 1.2.0.0/16 or ::2/127 or... */
|
LEX_T_MASKED_INTEGER, /* 12345/10 or 1.2.0.0/16 or ::2/127 or... */
|
||||||
|
LEX_T_MACRO, /* $NAME */
|
||||||
LEX_T_ERROR, /* invalid input */
|
LEX_T_ERROR, /* invalid input */
|
||||||
|
|
||||||
/* Bare tokens. */
|
/* Bare tokens. */
|
||||||
@@ -77,20 +78,32 @@ enum lex_format {
|
|||||||
};
|
};
|
||||||
const char *lex_format_to_string(enum lex_format);
|
const char *lex_format_to_string(enum lex_format);
|
||||||
|
|
||||||
/* A token.
|
/* A token. */
|
||||||
*
|
|
||||||
* 's' may point to 'buffer'; otherwise, it points to malloc()ed memory owned
|
|
||||||
* by the token. */
|
|
||||||
struct lex_token {
|
struct lex_token {
|
||||||
enum lex_type type; /* One of LEX_*. */
|
/* One of LEX_*. */
|
||||||
char *s; /* LEX_T_ID, LEX_T_STRING, LEX_T_ERROR only. */
|
enum lex_type type;
|
||||||
enum lex_format format; /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */
|
|
||||||
|
/* Meaningful for LEX_T_ID, LEX_T_STRING, LEX_T_ERROR, LEX_T_MACRO only.
|
||||||
|
* For these token types, 's' may point to 'buffer'; otherwise, it points
|
||||||
|
* to malloc()ed memory owned by the token.
|
||||||
|
*
|
||||||
|
* Must be NULL for other token types.
|
||||||
|
*
|
||||||
|
* For LEX_T_MACRO, 's' does not include the leading $. */
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
/* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */
|
||||||
|
enum lex_format format;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
/* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */
|
||||||
struct {
|
struct {
|
||||||
union mf_subvalue value; /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER. */
|
union mf_subvalue value; /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER. */
|
||||||
union mf_subvalue mask; /* LEX_T_MASKED_INTEGER only. */
|
union mf_subvalue mask; /* LEX_T_MASKED_INTEGER only. */
|
||||||
};
|
};
|
||||||
char buffer[256]; /* Buffer for LEX_T_ID/LEX_T_STRING. */
|
|
||||||
|
/* LEX_T_ID, LEX_T_STRING, LEX_T_ERROR, LEX_T_MACRO only. */
|
||||||
|
char buffer[256];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
55
tests/ovn.at
55
tests/ovn.at
@@ -8,6 +8,9 @@ foo bar baz quuxquuxquux _abcd_ a.b.c.d a123_.456
|
|||||||
"abc\u0020def" => "abc def"
|
"abc\u0020def" => "abc def"
|
||||||
" => error("Input ends inside quoted string.")dnl "
|
" => error("Input ends inside quoted string.")dnl "
|
||||||
|
|
||||||
|
$foo $bar $baz $quuxquuxquux $_abcd_ $a.b.c.d $a123_.456
|
||||||
|
$1 => error("`$' must be followed by a valid identifier.") 1
|
||||||
|
|
||||||
a/*b*/c => a c
|
a/*b*/c => a c
|
||||||
a//b c => a
|
a//b c => a
|
||||||
a/**/b => a b
|
a/**/b => a b
|
||||||
@@ -215,6 +218,8 @@ eth.type != 0x800 => Nominal field eth.type may only be tested for equality (tak
|
|||||||
|
|
||||||
123 == 123 => Syntax error at `123' expecting field name.
|
123 == 123 => Syntax error at `123' expecting field name.
|
||||||
|
|
||||||
|
$name => Syntax error at `$name' expecting address set name.
|
||||||
|
|
||||||
123 == xyzzy => Syntax error at `xyzzy' expecting field name.
|
123 == xyzzy => Syntax error at `xyzzy' expecting field name.
|
||||||
xyzzy == 1 => Syntax error at `xyzzy' expecting field name.
|
xyzzy == 1 => Syntax error at `xyzzy' expecting field name.
|
||||||
|
|
||||||
@@ -434,6 +439,56 @@ AT_CHECK([expr_to_flow 'inport == "eth0" && inport == "eth1"'], [0], [dnl
|
|||||||
])
|
])
|
||||||
AT_CLEANUP
|
AT_CLEANUP
|
||||||
|
|
||||||
|
AT_SETUP([ovn -- converting expressions to flows -- address sets])
|
||||||
|
expr_to_flow () {
|
||||||
|
echo "$1" | ovstest test-ovn expr-to-flows | sort
|
||||||
|
}
|
||||||
|
AT_CHECK([expr_to_flow 'ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3}'], [0], [dnl
|
||||||
|
ip,nw_src=10.0.0.1
|
||||||
|
ip,nw_src=10.0.0.2
|
||||||
|
ip,nw_src=10.0.0.3
|
||||||
|
])
|
||||||
|
AT_CHECK([expr_to_flow 'ip4.src == $set1'], [0], [dnl
|
||||||
|
ip,nw_src=10.0.0.1
|
||||||
|
ip,nw_src=10.0.0.2
|
||||||
|
ip,nw_src=10.0.0.3
|
||||||
|
])
|
||||||
|
AT_CHECK([expr_to_flow 'ip4.src == {1.2.3.4, $set1}'], [0], [dnl
|
||||||
|
ip,nw_src=1.2.3.4
|
||||||
|
ip,nw_src=10.0.0.1
|
||||||
|
ip,nw_src=10.0.0.2
|
||||||
|
ip,nw_src=10.0.0.3
|
||||||
|
])
|
||||||
|
AT_CHECK([expr_to_flow 'ip4.src == {1.2.0.0/20, 5.5.5.0/24, $set1}'], [0], [dnl
|
||||||
|
ip,nw_src=1.2.0.0/20
|
||||||
|
ip,nw_src=10.0.0.1
|
||||||
|
ip,nw_src=10.0.0.2
|
||||||
|
ip,nw_src=10.0.0.3
|
||||||
|
ip,nw_src=5.5.5.0/24
|
||||||
|
])
|
||||||
|
AT_CHECK([expr_to_flow 'ip6.src == {::1, ::2, ::3}'], [0], [dnl
|
||||||
|
ipv6,ipv6_src=::1
|
||||||
|
ipv6,ipv6_src=::2
|
||||||
|
ipv6,ipv6_src=::3
|
||||||
|
])
|
||||||
|
AT_CHECK([expr_to_flow 'ip6.src == {::1, $set2, ::4}'], [0], [dnl
|
||||||
|
ipv6,ipv6_src=::1
|
||||||
|
ipv6,ipv6_src=::2
|
||||||
|
ipv6,ipv6_src=::3
|
||||||
|
ipv6,ipv6_src=::4
|
||||||
|
])
|
||||||
|
AT_CHECK([expr_to_flow 'eth.src == {00:00:00:00:00:01, 00:00:00:00:00:02, 00:00:00:00:00:03}'], [0], [dnl
|
||||||
|
dl_src=00:00:00:00:00:01
|
||||||
|
dl_src=00:00:00:00:00:02
|
||||||
|
dl_src=00:00:00:00:00:03
|
||||||
|
])
|
||||||
|
AT_CHECK([expr_to_flow 'eth.src == {$set3}'], [0], [dnl
|
||||||
|
dl_src=00:00:00:00:00:01
|
||||||
|
dl_src=00:00:00:00:00:02
|
||||||
|
dl_src=00:00:00:00:00:03
|
||||||
|
])
|
||||||
|
AT_CLEANUP
|
||||||
|
|
||||||
AT_SETUP([ovn -- action parsing])
|
AT_SETUP([ovn -- action parsing])
|
||||||
dnl Text before => is input, text after => is expected output.
|
dnl Text before => is input, text after => is expected output.
|
||||||
AT_DATA([test-cases.txt], [[
|
AT_DATA([test-cases.txt], [[
|
||||||
|
@@ -268,6 +268,26 @@ create_dhcp_opts(struct hmap *dhcp_opts)
|
|||||||
dhcp_opt_add(dhcp_opts, "lease_time", 51, "uint32");
|
dhcp_opt_add(dhcp_opts, "lease_time", 51, "uint32");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
create_macros(struct shash *macros)
|
||||||
|
{
|
||||||
|
shash_init(macros);
|
||||||
|
|
||||||
|
static const char *const addrs1[] = {
|
||||||
|
"10.0.0.1", "10.0.0.2", "10.0.0.3",
|
||||||
|
};
|
||||||
|
static const char *const addrs2[] = {
|
||||||
|
"::1", "::2", "::3",
|
||||||
|
};
|
||||||
|
static const char *const addrs3[] = {
|
||||||
|
"00:00:00:00:00:01", "00:00:00:00:00:02", "00:00:00:00:00:03",
|
||||||
|
};
|
||||||
|
|
||||||
|
expr_macros_add(macros, "set1", addrs1, 3);
|
||||||
|
expr_macros_add(macros, "set2", addrs2, 3);
|
||||||
|
expr_macros_add(macros, "set3", addrs3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
lookup_port_cb(const void *ports_, const char *port_name, unsigned int *portp)
|
lookup_port_cb(const void *ports_, const char *port_name, unsigned int *portp)
|
||||||
{
|
{
|
||||||
@@ -284,10 +304,12 @@ static void
|
|||||||
test_parse_expr__(int steps)
|
test_parse_expr__(int steps)
|
||||||
{
|
{
|
||||||
struct shash symtab;
|
struct shash symtab;
|
||||||
|
struct shash macros;
|
||||||
struct simap ports;
|
struct simap ports;
|
||||||
struct ds input;
|
struct ds input;
|
||||||
|
|
||||||
create_symtab(&symtab);
|
create_symtab(&symtab);
|
||||||
|
create_macros(¯os);
|
||||||
|
|
||||||
simap_init(&ports);
|
simap_init(&ports);
|
||||||
simap_put(&ports, "eth0", 5);
|
simap_put(&ports, "eth0", 5);
|
||||||
@@ -299,7 +321,7 @@ test_parse_expr__(int steps)
|
|||||||
struct expr *expr;
|
struct expr *expr;
|
||||||
char *error;
|
char *error;
|
||||||
|
|
||||||
expr = expr_parse_string(ds_cstr(&input), &symtab, &error);
|
expr = expr_parse_string(ds_cstr(&input), &symtab, ¯os, &error);
|
||||||
if (!error && steps > 0) {
|
if (!error && steps > 0) {
|
||||||
expr = expr_annotate(expr, &symtab, &error);
|
expr = expr_annotate(expr, &symtab, &error);
|
||||||
}
|
}
|
||||||
@@ -336,6 +358,8 @@ test_parse_expr__(int steps)
|
|||||||
simap_destroy(&ports);
|
simap_destroy(&ports);
|
||||||
expr_symtab_destroy(&symtab);
|
expr_symtab_destroy(&symtab);
|
||||||
shash_destroy(&symtab);
|
shash_destroy(&symtab);
|
||||||
|
expr_macros_destroy(¯os);
|
||||||
|
shash_destroy(¯os);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -480,7 +504,7 @@ test_evaluate_expr(struct ovs_cmdl_context *ctx)
|
|||||||
struct expr *expr;
|
struct expr *expr;
|
||||||
char *error;
|
char *error;
|
||||||
|
|
||||||
expr = expr_parse_string(ds_cstr(&input), &symtab, &error);
|
expr = expr_parse_string(ds_cstr(&input), &symtab, NULL, &error);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
expr = expr_annotate(expr, &symtab, &error);
|
expr = expr_annotate(expr, &symtab, &error);
|
||||||
}
|
}
|
||||||
@@ -954,7 +978,7 @@ test_tree_shape_exhaustively(struct expr *expr, struct shash *symtab,
|
|||||||
expr_format(expr, &s);
|
expr_format(expr, &s);
|
||||||
|
|
||||||
char *error;
|
char *error;
|
||||||
modified = expr_parse_string(ds_cstr(&s), symtab, &error);
|
modified = expr_parse_string(ds_cstr(&s), symtab, NULL, &error);
|
||||||
if (error) {
|
if (error) {
|
||||||
fprintf(stderr, "%s fails to parse (%s)\n",
|
fprintf(stderr, "%s fails to parse (%s)\n",
|
||||||
ds_cstr(&s), error);
|
ds_cstr(&s), error);
|
||||||
|
Reference in New Issue
Block a user