mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-11-01 12:47:16 +00:00
commit
16ec3ea919
@ -49,6 +49,29 @@ DEFINE_MTYPE(LIB, HOST, "Host config")
|
||||
DEFINE_MTYPE(LIB, STRVEC, "String vector")
|
||||
DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
|
||||
|
||||
#define item(x) \
|
||||
{ \
|
||||
x, #x \
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
const struct message tokennames[] = {
|
||||
item(WORD_TKN),
|
||||
item(VARIABLE_TKN),
|
||||
item(RANGE_TKN),
|
||||
item(IPV4_TKN),
|
||||
item(IPV4_PREFIX_TKN),
|
||||
item(IPV6_TKN),
|
||||
item(IPV6_PREFIX_TKN),
|
||||
item(MAC_TKN),
|
||||
item(MAC_PREFIX_TKN),
|
||||
item(FORK_TKN),
|
||||
item(JOIN_TKN),
|
||||
item(START_TKN),
|
||||
item(END_TKN),
|
||||
{0},
|
||||
};
|
||||
|
||||
const char *node_names[] = {
|
||||
"auth", // AUTH_NODE,
|
||||
"view", // VIEW_NODE,
|
||||
@ -121,6 +144,7 @@ const char *node_names[] = {
|
||||
"bgp ipv6 flowspec", /* BGP_FLOWSPECV6_NODE
|
||||
*/
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
/* Command vector which includes some level of command lists. Normally
|
||||
each daemon maintains each own cmdvec. */
|
||||
@ -1566,6 +1590,21 @@ DEFUN (show_commandtree,
|
||||
return cmd_list_cmds(vty, argc == 3);
|
||||
}
|
||||
|
||||
DEFUN_HIDDEN(show_cli_graph,
|
||||
show_cli_graph_cmd,
|
||||
"show cli graph",
|
||||
SHOW_STR
|
||||
"CLI reflection\n"
|
||||
"Dump current command space as DOT graph\n")
|
||||
{
|
||||
struct cmd_node *cn = vector_slot(cmdvec, vty->node);
|
||||
char *dot = cmd_graph_dump_dot(cn->cmdgraph);
|
||||
|
||||
vty_out(vty, "%s\n", dot);
|
||||
XFREE(MTYPE_TMP, dot);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int vty_write_config(struct vty *vty)
|
||||
{
|
||||
size_t i;
|
||||
@ -2573,6 +2612,7 @@ void install_default(enum node_type node)
|
||||
install_element(node, &config_end_cmd);
|
||||
install_element(node, &config_help_cmd);
|
||||
install_element(node, &config_list_cmd);
|
||||
install_element(node, &show_cli_graph_cmd);
|
||||
install_element(node, &find_cmd);
|
||||
|
||||
install_element(node, &config_write_cmd);
|
||||
|
||||
@ -146,6 +146,7 @@ enum node_type {
|
||||
};
|
||||
|
||||
extern vector cmdvec;
|
||||
extern const struct message tokennames[];
|
||||
extern const char *node_names[];
|
||||
|
||||
/* Node which has some commands and prompt string and configuration
|
||||
|
||||
@ -25,6 +25,8 @@
|
||||
#include <zebra.h>
|
||||
|
||||
#include "command_graph.h"
|
||||
#include "command.h"
|
||||
#include "log.h"
|
||||
|
||||
DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens")
|
||||
DEFINE_MTYPE_STATIC(LIB, CMD_DESC, "Command Token Text")
|
||||
@ -457,3 +459,92 @@ void cmd_graph_names(struct graph *graph)
|
||||
|
||||
cmd_node_names(start, NULL, NULL);
|
||||
}
|
||||
|
||||
#ifndef BUILDING_CLIPPY
|
||||
|
||||
void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf)
|
||||
{
|
||||
static bool wasend;
|
||||
|
||||
char nbuf[512];
|
||||
struct cmd_token *tok = gn->data;
|
||||
const char *color;
|
||||
|
||||
if (wasend == true) {
|
||||
wasend = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (tok->type == END_TKN) {
|
||||
wasend = true;
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(nbuf, sizeof(nbuf), " n%p [ shape=box, label=<", gn);
|
||||
buffer_putstr(buf, nbuf);
|
||||
snprintf(nbuf, sizeof(nbuf), "<b>%s</b>",
|
||||
lookup_msg(tokennames, tok->type, NULL));
|
||||
buffer_putstr(buf, nbuf);
|
||||
if (tok->attr == CMD_ATTR_DEPRECATED)
|
||||
buffer_putstr(buf, " (d)");
|
||||
else if (tok->attr == CMD_ATTR_HIDDEN)
|
||||
buffer_putstr(buf, " (h)");
|
||||
if (tok->text) {
|
||||
if (tok->type == WORD_TKN)
|
||||
snprintf(
|
||||
nbuf, sizeof(nbuf),
|
||||
"<br/>\"<font color=\"#0055ff\" point-size=\"11\"><b>%s</b></font>\"",
|
||||
tok->text);
|
||||
else
|
||||
snprintf(nbuf, sizeof(nbuf), "<br/>%s", tok->text);
|
||||
buffer_putstr(buf, nbuf);
|
||||
}
|
||||
|
||||
switch (tok->type) {
|
||||
case START_TKN:
|
||||
color = "#ccffcc";
|
||||
break;
|
||||
case FORK_TKN:
|
||||
color = "#aaddff";
|
||||
break;
|
||||
case JOIN_TKN:
|
||||
color = "#ddaaff";
|
||||
break;
|
||||
case WORD_TKN:
|
||||
color = "#ffffff";
|
||||
break;
|
||||
default:
|
||||
color = "#ffffff";
|
||||
break;
|
||||
}
|
||||
snprintf(nbuf, sizeof(nbuf),
|
||||
">, style = filled, fillcolor = \"%s\" ];\n", color);
|
||||
buffer_putstr(buf, nbuf);
|
||||
|
||||
for (unsigned int i = 0; i < vector_active(gn->to); i++) {
|
||||
struct graph_node *adj = vector_slot(gn->to, i);
|
||||
|
||||
if (((struct cmd_token *)adj->data)->type == END_TKN) {
|
||||
snprintf(nbuf, sizeof(nbuf), " n%p -> end%p;\n", gn,
|
||||
adj);
|
||||
buffer_putstr(buf, nbuf);
|
||||
snprintf(
|
||||
nbuf, sizeof(nbuf),
|
||||
" end%p [ shape=box, label=<end>, style = filled, fillcolor = \"#ffddaa\" ];\n",
|
||||
adj);
|
||||
} else
|
||||
snprintf(nbuf, sizeof(nbuf), " n%p -> n%p;\n", gn,
|
||||
adj);
|
||||
|
||||
buffer_putstr(buf, nbuf);
|
||||
}
|
||||
}
|
||||
|
||||
char *cmd_graph_dump_dot(struct graph *cmdgraph)
|
||||
{
|
||||
struct graph_node *start = vector_slot(cmdgraph->nodes, 0);
|
||||
|
||||
return graph_dump_dot(cmdgraph, start, cmd_graph_node_print_cb);
|
||||
}
|
||||
|
||||
#endif /* BUILDING_CLIPPY */
|
||||
|
||||
@ -116,5 +116,21 @@ extern void cmd_graph_parse(struct graph *graph, struct cmd_element *cmd);
|
||||
extern void cmd_graph_names(struct graph *graph);
|
||||
extern void cmd_graph_merge(struct graph *old, struct graph *new,
|
||||
int direction);
|
||||
/*
|
||||
* Print callback for DOT dumping.
|
||||
*
|
||||
* See graph.h for more details.
|
||||
*/
|
||||
extern void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf);
|
||||
/*
|
||||
* Dump command graph to DOT.
|
||||
*
|
||||
* cmdgraph
|
||||
* A command graph to dump
|
||||
*
|
||||
* Returns:
|
||||
* String allocated with MTYPE_TMP representing this graph
|
||||
*/
|
||||
char *cmd_graph_dump_dot(struct graph *cmdgraph);
|
||||
|
||||
#endif /* _FRR_COMMAND_GRAPH_H */
|
||||
|
||||
@ -37,9 +37,6 @@ DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
|
||||
void grammar_sandbox_init(void);
|
||||
void pretty_print_graph(struct vty *vty, struct graph_node *, int, int,
|
||||
struct graph_node **, size_t);
|
||||
static void pretty_print_dot(FILE *ofd, unsigned opts, struct graph_node *start,
|
||||
struct graph_node **stack, size_t stackpos,
|
||||
struct graph_node **visited, size_t *visitpos);
|
||||
void init_cmdgraph(struct vty *, struct graph **);
|
||||
|
||||
/** shim interface commands **/
|
||||
@ -274,23 +271,19 @@ DEFUN (grammar_test_dot,
|
||||
".dot filename\n")
|
||||
{
|
||||
check_nodegraph();
|
||||
|
||||
struct graph_node *stack[CMD_ARGC_MAX];
|
||||
struct graph_node *visited[CMD_ARGC_MAX * CMD_ARGC_MAX];
|
||||
size_t vpos = 0;
|
||||
|
||||
FILE *ofd = fopen(argv[2]->arg, "w");
|
||||
|
||||
if (!ofd) {
|
||||
vty_out(vty, "%s: %s\r\n", argv[2]->arg, strerror(errno));
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
fprintf(ofd,
|
||||
"digraph {\n graph [ rankdir = LR ];\n node [ fontname = \"Fira Mono\", fontsize = 9 ];\n\n");
|
||||
pretty_print_dot(ofd, 0, vector_slot(nodegraph->nodes, 0), stack, 0,
|
||||
visited, &vpos);
|
||||
fprintf(ofd, "}\n");
|
||||
char *dot = cmd_graph_dump_dot(nodegraph);
|
||||
|
||||
fprintf(ofd, "%s", dot);
|
||||
fclose(ofd);
|
||||
XFREE(MTYPE_TMP, dot);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@ -489,24 +482,6 @@ void grammar_sandbox_init(void)
|
||||
install_element(ENABLE_NODE, &grammar_access_cmd);
|
||||
}
|
||||
|
||||
#define item(x) { x, #x }
|
||||
struct message tokennames[] = {item(WORD_TKN), // words
|
||||
item(VARIABLE_TKN), // almost anything
|
||||
item(RANGE_TKN), // integer range
|
||||
item(IPV4_TKN), // IPV4 addresses
|
||||
item(IPV4_PREFIX_TKN), // IPV4 network prefixes
|
||||
item(IPV6_TKN), // IPV6 prefixes
|
||||
item(IPV6_PREFIX_TKN), // IPV6 network prefixes
|
||||
item(MAC_TKN), // MAC address
|
||||
item(MAC_PREFIX_TKN), // MAC address w/ mask
|
||||
|
||||
/* plumbing types */
|
||||
item(FORK_TKN),
|
||||
item(JOIN_TKN),
|
||||
item(START_TKN), // first token in line
|
||||
item(END_TKN), // last token in line
|
||||
{0}};
|
||||
|
||||
/**
|
||||
* Pretty-prints a graph, assuming it is a tree.
|
||||
*
|
||||
@ -571,89 +546,6 @@ void pretty_print_graph(struct vty *vty, struct graph_node *start, int level,
|
||||
vty_out(vty, "\n");
|
||||
}
|
||||
|
||||
static void pretty_print_dot(FILE *ofd, unsigned opts, struct graph_node *start,
|
||||
struct graph_node **stack, size_t stackpos,
|
||||
struct graph_node **visited, size_t *visitpos)
|
||||
{
|
||||
// print this node
|
||||
char tokennum[32];
|
||||
struct cmd_token *tok = start->data;
|
||||
const char *color;
|
||||
|
||||
for (size_t i = 0; i < (*visitpos); i++)
|
||||
if (visited[i] == start)
|
||||
return;
|
||||
visited[(*visitpos)++] = start;
|
||||
if ((*visitpos) == CMD_ARGC_MAX * CMD_ARGC_MAX)
|
||||
return;
|
||||
|
||||
snprintf(tokennum, sizeof(tokennum), "%d?", tok->type);
|
||||
fprintf(ofd, " n%p [ shape=box, label=<", start);
|
||||
|
||||
fprintf(ofd, "<b>%s</b>", lookup_msg(tokennames, tok->type, NULL));
|
||||
if (tok->attr == CMD_ATTR_DEPRECATED)
|
||||
fprintf(ofd, " (d)");
|
||||
else if (tok->attr == CMD_ATTR_HIDDEN)
|
||||
fprintf(ofd, " (h)");
|
||||
if (tok->text) {
|
||||
if (tok->type == WORD_TKN)
|
||||
fprintf(ofd,
|
||||
"<br/>\"<font color=\"#0055ff\" point-size=\"11\"><b>%s</b></font>\"",
|
||||
tok->text);
|
||||
else
|
||||
fprintf(ofd, "<br/>%s", tok->text);
|
||||
}
|
||||
/* if (desc)
|
||||
fprintf(ofd, " ?'%s'", tok->desc); */
|
||||
switch (tok->type) {
|
||||
case START_TKN:
|
||||
color = "#ccffcc";
|
||||
break;
|
||||
case FORK_TKN:
|
||||
color = "#aaddff";
|
||||
break;
|
||||
case JOIN_TKN:
|
||||
color = "#ddaaff";
|
||||
break;
|
||||
case WORD_TKN:
|
||||
color = "#ffffff";
|
||||
break;
|
||||
default:
|
||||
color = "#ffffff";
|
||||
break;
|
||||
}
|
||||
fprintf(ofd, ">, style = filled, fillcolor = \"%s\" ];\n", color);
|
||||
|
||||
if (stackpos == CMD_ARGC_MAX)
|
||||
return;
|
||||
stack[stackpos++] = start;
|
||||
|
||||
for (unsigned int i = 0; i < vector_active(start->to); i++) {
|
||||
struct graph_node *adj = vector_slot(start->to, i);
|
||||
// if this node is a vararg, just print *
|
||||
if (adj == start) {
|
||||
fprintf(ofd, " n%p -> n%p;\n", start, start);
|
||||
} else if (((struct cmd_token *)adj->data)->type == END_TKN) {
|
||||
// struct cmd_token *et = adj->data;
|
||||
fprintf(ofd, " n%p -> end%p;\n", start, adj);
|
||||
fprintf(ofd,
|
||||
" end%p [ shape=box, label=<end>, style = filled, fillcolor = \"#ffddaa\" ];\n",
|
||||
adj);
|
||||
} else {
|
||||
fprintf(ofd, " n%p -> n%p;\n", start, adj);
|
||||
size_t k;
|
||||
for (k = 0; k < stackpos; k++)
|
||||
if (stack[k] == adj)
|
||||
break;
|
||||
if (k == stackpos) {
|
||||
pretty_print_dot(ofd, opts, adj, stack,
|
||||
stackpos, visited, visitpos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** stuff that should go in command.c + command.h */
|
||||
void init_cmdgraph(struct vty *vty, struct graph **graph)
|
||||
{
|
||||
|
||||
@ -43,6 +43,7 @@
|
||||
#include "ns.h"
|
||||
#include "vrf.h"
|
||||
#include "libfrr.h"
|
||||
#include "command_graph.h"
|
||||
|
||||
DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CMD, "Vtysh cmd copy")
|
||||
|
||||
@ -2957,10 +2958,26 @@ DEFUN(find,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN_HIDDEN(show_cli_graph_vtysh,
|
||||
show_cli_graph_vtysh_cmd,
|
||||
"show cli graph",
|
||||
SHOW_STR
|
||||
"CLI reflection\n"
|
||||
"Dump current command space as DOT graph\n")
|
||||
{
|
||||
struct cmd_node *cn = vector_slot(cmdvec, vty->node);
|
||||
char *dot = cmd_graph_dump_dot(cn->cmdgraph);
|
||||
|
||||
vty_out(vty, "%s\n", dot);
|
||||
XFREE(MTYPE_TMP, dot);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void vtysh_install_default(enum node_type node)
|
||||
{
|
||||
install_element(node, &config_list_cmd);
|
||||
install_element(node, &find_cmd);
|
||||
install_element(node, &show_cli_graph_vtysh_cmd);
|
||||
install_element(node, &vtysh_output_file_cmd);
|
||||
install_element(node, &no_vtysh_output_file_cmd);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user