Skip to content

Commit

Permalink
cli: rework command registration
Browse files Browse the repository at this point in the history
Allow more than one level of command context.

Signed-off-by: Robin Jarry <[email protected]>
  • Loading branch information
rjarry committed Mar 29, 2024
1 parent a5ce189 commit af3ef11
Show file tree
Hide file tree
Showing 11 changed files with 197 additions and 223 deletions.
18 changes: 8 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ Welcome to the boring router CLI.
Use ? for help and <tab> for command completion.
br#
quit Exit the CLI.
route4 Manage IPv4 routes.
nh4 Manage IPv4 next hops.
ipv4 Manage IPv4 stack.
stats Manage stack statistics.
rxq Manage ports RX queues.
port Manage ports.
Expand All @@ -84,12 +83,12 @@ INDEX DEVICE RX_QUEUES RXQ_SIZE TX_QUEUES TXQ_SIZE MAC
PORT RXQ_ID CPU_ID ENABLED
0 0 7 1
1 0 27 1
+ nh4 add 172.16.0.1 mac b8:3f:d2:fa:53:7a port 0
+ nh4 add 172.16.1.1 mac b8:3f:d2:fa:53:7b port 1
+ route4 add 172.16.0.0/24 via 172.16.0.1
+ route4 add 172.16.1.0/24 via 172.16.1.1
+ route4 add 192.168.0.0/16 via 172.16.0.1
+ route4 add 0.0.0.0/0 via 172.16.1.1
+ ipv4 nexthop add 172.16.0.1 mac b8:3f:d2:fa:53:7a port 0
+ ipv4 nexthop add 172.16.1.1 mac b8:3f:d2:fa:53:7b port 1
+ ipv4 route add 172.16.0.0/24 via 172.16.0.1
+ ipv4 route add 172.16.1.0/24 via 172.16.1.1
+ ipv4 route add 192.168.0.0/16 via 172.16.0.1
+ ipv4 route add 0.0.0.0/0 via 172.16.1.1
```

The CLI can be used as a one-shot command (with bash completion built-in):
Expand All @@ -102,10 +101,9 @@ The CLI can be used as a one-shot command (with bash completion built-in):
graph (Get information about the packet processing graph.)
--help (Show usage help and exit.)
-h (Show usage help and exit.)
nh4 (Manage IPv4 next hops.)
ipv4 (Manage IPv4 stack.)
port (Manage ports.)
quit (Exit the CLI.)
route4 (Manage IPv4 routes.)
rxq (Manage ports RX queues.)
--socket (Path to the control plane API socket.)
-s (Path to the control plane API socket.)
Expand Down
28 changes: 28 additions & 0 deletions cli/ecoli.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,34 @@ struct ec_node *with_callback(cmd_cb_t *cb, struct ec_node *node) {
return node;
}

struct ec_node *cli_context(struct ec_node *root, const char *name, const char *help) {
struct ec_node *ctx = NULL, *or_node = NULL;

if (root == NULL || name == NULL || help == NULL) {
errno = EINVAL;
goto fail;
}

// if context is already present, return the OR node directly
or_node = ec_node_find(root, name);
if (or_node != NULL)
return or_node;

// else, create the context node
if ((or_node = ec_node("or", name)) == NULL)
goto fail;
ctx = EC_NODE_SEQ(EC_NO_ID, with_help(help, ec_node_str(EC_NO_ID, name)), or_node);
if (ctx == NULL)
goto fail;
if (ec_node_or_add(root, ctx) < 0)
goto fail;

return or_node;
fail:
ec_node_free(ctx);
return NULL;
}

const char *arg_str(const struct ec_pnode *p, const char *id) {
const struct ec_pnode *n = ec_pnode_find(p, id);
if (n == NULL) {
Expand Down
16 changes: 8 additions & 8 deletions cli/include/br_cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,19 @@ struct ec_node *with_help(const char *help, struct ec_node *node);

struct ec_node *with_callback(cmd_cb_t *cb, struct ec_node *node);

struct ec_node *cli_context(struct ec_node *root, const char *name, const char *help);

const char *arg_str(const struct ec_pnode *p, const char *id);
int arg_int(const struct ec_pnode *p, const char *id, int64_t *);
int arg_uint(const struct ec_pnode *p, const char *id, uint64_t *);
int arg_eth_addr(const struct ec_pnode *p, const char *id, struct eth_addr *);

#define CLI_COMMAND_CONTEXT(name, help, ...) \
EC_NODE_SEQ( \
EC_NO_ID, \
with_help(help, ec_node_str(name, name)), \
EC_NODE_OR(EC_NO_ID, __VA_ARGS__) \
#define CLI_COMMAND(ctx, cmd, cb, help, ...) \
ec_node_or_add( \
ctx, \
with_callback( \
cb, with_help(help, EC_NODE_CMD(cmd, cmd __VA_OPT__(, ) __VA_ARGS__)) \
) \
)

#define CLI_COMMAND(cmd, cb, help, ...) \
with_callback(cb, with_help(help, EC_NODE_CMD(cmd, cmd __VA_OPT__(, ) __VA_ARGS__)))

#endif
15 changes: 1 addition & 14 deletions cli/quit.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,7 @@ static cmd_status_t quit(const struct br_client *c, const struct ec_pnode *p) {
}

static int ctx_init(struct ec_node *root) {
struct ec_node *n = NULL;

n = CLI_COMMAND("quit", quit, "Exit the CLI.");
if (n == NULL)
goto fail;

if (ec_node_or_add(root, n) < 0)
goto fail;

return 0;

fail:
ec_node_free(n);
return -1;
return CLI_COMMAND(root, "quit", quit, "Exit the CLI.");
}

static struct br_cli_context ctx = {
Expand Down
36 changes: 13 additions & 23 deletions modules/infra/cli/graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,30 +88,20 @@ static cmd_status_t graph_stats(const struct br_client *c, const struct ec_pnode
}

static int ctx_init(struct ec_node *root) {
struct ec_node *node = NULL;

node = CLI_COMMAND_CONTEXT(
"graph",
"Get information about the packet processing graph.",
CLI_COMMAND("dump", graph_dump, "Dump the graph in DOT format."),
CLI_COMMAND(
"stats [zero]",
graph_stats,
"Print graph nodes statistics.",
with_help("Print stats with value 0.", ec_node_str("zero", "zero"))
)
struct ec_node *graph = cli_context(
root, "graph", "Get information about the packet processing graph."
);
if (graph == NULL)
return -1;
if (CLI_COMMAND(graph, "dump", graph_dump, "Dump the graph in DOT format.") < 0)
return -1;
return CLI_COMMAND(
graph,
"stats [zero]",
graph_stats,
"Print graph nodes statistics.",
with_help("Print stats with value 0.", ec_node_str("zero", "zero"))
);
if (node == NULL)
goto fail;

if (ec_node_or_add(root, node) < 0)
goto fail;

return 0;

fail:
ec_node_free(node);
return -1;
}

static struct br_cli_context ctx = {
Expand Down
89 changes: 42 additions & 47 deletions modules/infra/cli/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,54 +111,49 @@ static cmd_status_t port_list(const struct br_client *c, const struct ec_pnode *
}

static int ctx_init(struct ec_node *root) {
struct ec_node *node = NULL;

node = CLI_COMMAND_CONTEXT(
"port",
"Manage ports.",
CLI_COMMAND(
"add DEVARGS",
port_add,
"Create a new port.",
with_help("DPDK device args.", ec_node("devargs", "DEVARGS"))
),
CLI_COMMAND(
"set INDEX [rxqs N_RXQ] [qsize Q_SIZE]",
port_set,
"Modify port parameters.",
with_help("Port index.", ec_node_uint("INDEX", 0, UINT16_MAX - 1, 10)),
with_help(
"Number of Rx queues.", ec_node_uint("N_RXQ", 0, UINT16_MAX - 1, 10)
),
with_help(
"Rx/Tx queues size.", ec_node_uint("Q_SIZE", 0, UINT16_MAX - 1, 10)
)
),
CLI_COMMAND(
"del INDEX",
port_del,
"Delete an existing port.",
with_help("Port index.", ec_node_uint("INDEX", 0, UINT16_MAX - 1, 10))
),
CLI_COMMAND(
"show INDEX",
port_show,
"Show one port details.",
with_help("Port index.", ec_node_uint("INDEX", 0, UINT16_MAX - 1, 10))
),
CLI_COMMAND("list", port_list, "List all ports.")
struct ec_node *port = cli_context(root, "port", "Manage ports.");
int ret;

if (port == NULL)
return -1;

ret = CLI_COMMAND(
port,
"add DEVARGS",
port_add,
"Create a new port.",
with_help("DPDK device args.", ec_node("devargs", "DEVARGS"))
);
if (node == NULL)
goto fail;

if (ec_node_or_add(root, node) < 0)
goto fail;

return 0;

fail:
ec_node_free(node);
return -1;
if (ret < 0)
return ret;
ret = CLI_COMMAND(
port,
"set INDEX [rxqs N_RXQ] [qsize Q_SIZE]",
port_set,
"Modify port parameters.",
with_help("Port index.", ec_node_uint("INDEX", 0, UINT16_MAX - 1, 10)),
with_help("Number of Rx queues.", ec_node_uint("N_RXQ", 0, UINT16_MAX - 1, 10)),
with_help("Rx/Tx queues size.", ec_node_uint("Q_SIZE", 0, UINT16_MAX - 1, 10))
);
if (ret < 0)
return ret;
ret = CLI_COMMAND(
port,
"del INDEX",
port_del,
"Delete an existing port.",
with_help("Port index.", ec_node_uint("INDEX", 0, UINT16_MAX - 1, 10))
);
if (ret < 0)
return ret;
ret = CLI_COMMAND(
port,
"show INDEX",
port_show,
"Show one port details.",
with_help("Port index.", ec_node_uint("INDEX", 0, UINT16_MAX - 1, 10))
);
return CLI_COMMAND(port, "list", port_list, "List all ports.");
}

static struct br_cli_context ctx = {
Expand Down
37 changes: 14 additions & 23 deletions modules/infra/cli/rxq.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,33 +64,24 @@ static cmd_status_t rxq_set(const struct br_client *c, const struct ec_pnode *p)
}

static int ctx_init(struct ec_node *root) {
struct ec_node *rxq = cli_context(root, "rxq", "Manage port RX queues.");
unsigned ncpus = sysconf(_SC_NPROCESSORS_ONLN);
struct ec_node *node = NULL;

node = CLI_COMMAND_CONTEXT(
"rxq",
"Manage ports RX queues.",
CLI_COMMAND("list", rxq_list, "List all RX queues."),
CLI_COMMAND(
"set port PORT rxq RXQ cpu CPU",
rxq_set,
"Assign an RX queue to a given CPU.",
with_help("Port index.", ec_node_uint("PORT", 0, UINT16_MAX - 1, 10)),
with_help("RX queue ID.", ec_node_uint("RXQ", 0, UINT16_MAX - 1, 10)),
with_help("CPU ID.", ec_node_uint("CPU", 0, ncpus - 1, 10))
)
);
if (node == NULL)
goto fail;

if (ec_node_or_add(root, node) < 0)
goto fail;
if (rxq == NULL)
return -1;

return 0;
if (CLI_COMMAND(rxq, "list", rxq_list, "List all RX queues.") < 0)
return -1;

fail:
ec_node_free(node);
return -1;
return CLI_COMMAND(
rxq,
"set port PORT rxq RXQ cpu CPU",
rxq_set,
"Assign an RX queue to a given CPU.",
with_help("Port index.", ec_node_uint("PORT", 0, UINT16_MAX - 1, 10)),
with_help("RX queue ID.", ec_node_uint("RXQ", 0, UINT16_MAX - 1, 10)),
with_help("CPU ID.", ec_node_uint("CPU", 0, ncpus - 1, 10))
);
}

static struct br_cli_context ctx = {
Expand Down
47 changes: 20 additions & 27 deletions modules/infra/cli/stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,35 +64,28 @@ static cmd_status_t stats_reset(const struct br_client *c, const struct ec_pnode
}

static int ctx_init(struct ec_node *root) {
struct ec_node *node = NULL;

node = CLI_COMMAND_CONTEXT(
"stats",
"Manage stack statistics.",
CLI_COMMAND(
"software|hardware|xstats|all [zero] [pattern PATTERN]",
stats_get,
"Print statistics.",
with_help("Print software stats.", ec_node_str("software", "software")),
with_help("Print hardware stats.", ec_node_str("hardware", "hardware")),
with_help("Print extended driver stats.", ec_node_str("xstats", "xstats")),
with_help("Print all stats.", ec_node_str("all", "all")),
with_help("Print stats with value 0.", ec_node_str("zero", "zero")),
with_help("Filter by glob pattern.", ec_node("any", "PATTERN"))
),
CLI_COMMAND("reset", stats_reset, "Reset all stats to zero.")
struct ec_node *stats = cli_context(root, "stats", "Manage statistics.");
int ret;

if (stats == NULL)
return -1;

ret = CLI_COMMAND(
stats,
"software|hardware|xstats|all [zero] [pattern PATTERN]",
stats_get,
"Print statistics.",
with_help("Print software stats.", ec_node_str("software", "software")),
with_help("Print hardware stats.", ec_node_str("hardware", "hardware")),
with_help("Print extended driver stats.", ec_node_str("xstats", "xstats")),
with_help("Print all stats.", ec_node_str("all", "all")),
with_help("Print stats with value 0.", ec_node_str("zero", "zero")),
with_help("Filter by glob pattern.", ec_node("any", "PATTERN"))
);
if (node == NULL)
goto fail;
if (ret < 0)
return ret;

if (ec_node_or_add(root, node) < 0)
goto fail;

return 0;

fail:
ec_node_free(node);
return -1;
return CLI_COMMAND(stats, "reset", stats_reset, "Reset all stats to zero.");
}

static struct br_cli_context ctx = {
Expand Down
4 changes: 2 additions & 2 deletions modules/ip4/cli/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
# Copyright (c) 2024 Robin Jarry

cli_src += files(
'nh4.c',
'route4.c',
'nexthop.c',
'route.c',
)
Loading

0 comments on commit af3ef11

Please sign in to comment.