summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/command.c7
-rw-r--r--lib/command.h21
-rw-r--r--lib/command_graph.h1
-rw-r--r--lib/defun_lex.l2
-rw-r--r--lib/filter_cli.c62
-rw-r--r--lib/if.c9
-rw-r--r--lib/ipaddr.h7
-rw-r--r--lib/northbound_cli.c132
-rw-r--r--lib/northbound_cli.h8
-rw-r--r--lib/routemap_cli.c87
-rw-r--r--lib/vrf.c5
-rw-r--r--lib/vty.c3
-rw-r--r--lib/vty.h8
13 files changed, 253 insertions, 99 deletions
diff --git a/lib/command.c b/lib/command.c
index 80b75d9b23..159ed07b38 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -904,6 +904,13 @@ static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter,
> vty->candidate_config->version)
nb_config_replace(vty->candidate_config,
running_config, true);
+
+ /*
+ * Perform pending commit (if any) before executing
+ * non-YANG command.
+ */
+ if (matched_element->attr != CMD_ATTR_YANG)
+ nb_cli_pending_commit_check(vty);
}
ret = matched_element->func(matched_element, vty, argc, argv);
diff --git a/lib/command.h b/lib/command.h
index 1acca90665..a7a2eaf868 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -257,6 +257,12 @@ struct cmd_node {
#define DEFPY_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+#define DEFPY_YANG(funcname, cmdname, cmdstr, helpstr) \
+ DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
+
+#define DEFPY_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \
+ DEFPY_YANG(funcname, cmdname, cmdstr, helpstr)
+
#define DEFUN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_FUNC_DECL(funcname) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \
@@ -270,10 +276,16 @@ struct cmd_node {
#define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN)
+#define DEFUN_YANG(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
+
/* DEFUN_NOSH for commands that vtysh should ignore */
#define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \
DEFUN(funcname, cmdname, cmdstr, helpstr)
+#define DEFUN_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_YANG(funcname, cmdname, cmdstr, helpstr)
+
/* DEFSH for vtysh. */
#define DEFSH(daemon, cmdname, cmdstr, helpstr) \
DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon)
@@ -282,6 +294,9 @@ struct cmd_node {
DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, \
daemon)
+#define DEFSH_YANG(daemon, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, CMD_ATTR_YANG, daemon)
+
/* DEFUN + DEFSH */
#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_FUNC_DECL(funcname) \
@@ -302,6 +317,9 @@ struct cmd_node {
DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, \
CMD_ATTR_DEPRECATED)
+#define DEFUNSH_YANG(daemon, funcname, cmdname, cmdstr, helpstr) \
+ DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG)
+
/* ALIAS macro which define existing command's alias. */
#define ALIAS(funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0)
@@ -317,6 +335,9 @@ struct cmd_node {
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, \
CMD_ATTR_DEPRECATED, 0)
+#define ALIAS_YANG(funcname, cmdname, cmdstr, helpstr) \
+ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG, 0)
+
#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon)
diff --git a/lib/command_graph.h b/lib/command_graph.h
index 1efe8b1803..179e104a57 100644
--- a/lib/command_graph.h
+++ b/lib/command_graph.h
@@ -74,6 +74,7 @@ enum cmd_token_type {
enum { CMD_ATTR_NORMAL,
CMD_ATTR_DEPRECATED,
CMD_ATTR_HIDDEN,
+ CMD_ATTR_YANG,
};
/* Comamand token struct. */
diff --git a/lib/defun_lex.l b/lib/defun_lex.l
index 19b06f51b8..bc5fbd24d9 100644
--- a/lib/defun_lex.l
+++ b/lib/defun_lex.l
@@ -140,6 +140,8 @@ SPECIAL [(),]
"DEFPY_NOSH" value = strdup(yytext); return DEFUNNY;
"DEFPY_ATTR" value = strdup(yytext); return DEFUNNY;
"DEFPY_HIDDEN" value = strdup(yytext); return DEFUNNY;
+"DEFPY_YANG" value = strdup(yytext); return DEFUNNY;
+"DEFPY_YANG_NOSH" value = strdup(yytext); return DEFUNNY;
"ALIAS" value = strdup(yytext); return DEFUNNY;
"ALIAS_HIDDEN" value = strdup(yytext); return DEFUNNY;
"install_element" value = strdup(yytext); return INSTALL;
diff --git a/lib/filter_cli.c b/lib/filter_cli.c
index 030039b3fe..8c7a515dc5 100644
--- a/lib/filter_cli.c
+++ b/lib/filter_cli.c
@@ -169,7 +169,7 @@ static long acl_get_seq(struct vty *vty, const char *xpath)
/*
* Cisco (legacy) access lists.
*/
-DEFPY(
+DEFPY_YANG(
access_list_std, access_list_std_cmd,
"access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>",
ACCESS_LIST_STR
@@ -220,7 +220,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_access_list_std, no_access_list_std_cmd,
"no access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>",
NO_STR
@@ -280,7 +280,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
access_list_ext, access_list_ext_cmd,
"access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
ACCESS_LIST_STR
@@ -351,7 +351,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_access_list_ext, no_access_list_ext_cmd,
"no access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>",
NO_STR
@@ -436,7 +436,7 @@ DEFPY(
/*
* Zebra access lists.
*/
-DEFPY(
+DEFPY_YANG(
access_list, access_list_cmd,
"access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
ACCESS_LIST_STR
@@ -482,7 +482,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_access_list, no_access_list_cmd,
"no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <A.B.C.D/M$prefix [exact-match$exact]|any>",
NO_STR
@@ -539,7 +539,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_access_list_all, no_access_list_all_cmd,
"no access-list WORD$name",
NO_STR
@@ -555,7 +555,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
access_list_remark, access_list_remark_cmd,
"access-list WORD$name remark LINE...",
ACCESS_LIST_STR
@@ -579,7 +579,7 @@ DEFPY(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_access_list_remark, no_access_list_remark_cmd,
"no access-list WORD$name remark",
NO_STR
@@ -606,7 +606,7 @@ ALIAS(
ACCESS_LIST_REMARK_STR
ACCESS_LIST_REMARK_LINE_STR)
-DEFPY(
+DEFPY_YANG(
ipv6_access_list, ipv6_access_list_cmd,
"ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
IPV6_STR
@@ -653,7 +653,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_access_list, no_ipv6_access_list_cmd,
"no ipv6 access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X::X:X/M$prefix [exact-match$exact]|any>",
NO_STR
@@ -711,7 +711,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_access_list_all, no_ipv6_access_list_all_cmd,
"no ipv6 access-list WORD$name",
NO_STR
@@ -728,7 +728,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
ipv6_access_list_remark, ipv6_access_list_remark_cmd,
"ipv6 access-list WORD$name remark LINE...",
IPV6_STR
@@ -753,7 +753,7 @@ DEFPY(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_access_list_remark, no_ipv6_access_list_remark_cmd,
"no ipv6 access-list WORD$name remark",
NO_STR
@@ -782,7 +782,7 @@ ALIAS(
ACCESS_LIST_REMARK_STR
ACCESS_LIST_REMARK_LINE_STR)
-DEFPY(
+DEFPY_YANG(
mac_access_list, mac_access_list_cmd,
"mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>",
MAC_STR
@@ -825,7 +825,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_mac_access_list, no_mac_access_list_cmd,
"no mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$prefix|any>",
NO_STR
@@ -882,7 +882,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_mac_access_list_all, no_mac_access_list_all_cmd,
"no mac access-list WORD$name",
NO_STR
@@ -899,7 +899,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
mac_access_list_remark, mac_access_list_remark_cmd,
"mac access-list WORD$name remark LINE...",
MAC_STR
@@ -924,7 +924,7 @@ DEFPY(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_mac_access_list_remark, no_mac_access_list_remark_cmd,
"no mac access-list WORD$name remark",
NO_STR
@@ -1182,7 +1182,7 @@ static int plist_remove(struct vty *vty, const char *iptype, const char *name,
return rv;
}
-DEFPY(
+DEFPY_YANG(
ip_prefix_list, ip_prefix_list_cmd,
"ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)$ge|le (0-32)$le}]>",
IP_STR
@@ -1239,7 +1239,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_ip_prefix_list, no_ip_prefix_list_cmd,
"no ip prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|A.B.C.D/M$prefix [{ge (0-32)|le (0-32)}]>",
NO_STR
@@ -1259,7 +1259,7 @@ DEFPY(
(struct prefix *)prefix, ge, le);
}
-DEFPY(
+DEFPY_YANG(
no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd,
"no ip prefix-list WORD$name seq (1-4294967295)$seq",
NO_STR
@@ -1271,7 +1271,7 @@ DEFPY(
return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, 0, 0);
}
-DEFPY(
+DEFPY_YANG(
no_ip_prefix_list_all, no_ip_prefix_list_all_cmd,
"no ip prefix-list WORD$name",
NO_STR
@@ -1288,7 +1288,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
ip_prefix_list_remark, ip_prefix_list_remark_cmd,
"ip prefix-list WORD$name description LINE...",
IP_STR
@@ -1313,7 +1313,7 @@ DEFPY(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_ip_prefix_list_remark, no_ip_prefix_list_remark_cmd,
"no ip prefix-list WORD$name description",
NO_STR
@@ -1342,7 +1342,7 @@ ALIAS(
ACCESS_LIST_REMARK_STR
ACCESS_LIST_REMARK_LINE_STR)
-DEFPY(
+DEFPY_YANG(
ipv6_prefix_list, ipv6_prefix_list_cmd,
"ipv6 prefix-list WORD$name [seq (1-4294967295)] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
IPV6_STR
@@ -1399,7 +1399,7 @@ DEFPY(
return nb_cli_apply_changes(vty, xpath_entry);
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_prefix_list, no_ipv6_prefix_list_cmd,
"no ipv6 prefix-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <any|X:X::X:X/M$prefix [{ge (0-128)$ge|le (0-128)$le}]>",
NO_STR
@@ -1419,7 +1419,7 @@ DEFPY(
(struct prefix *)prefix, ge, le);
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_prefix_list_seq, no_ipv6_prefix_list_seq_cmd,
"no ipv6 prefix-list WORD$name seq (1-4294967295)$seq",
NO_STR
@@ -1431,7 +1431,7 @@ DEFPY(
return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, 0, 0);
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_prefix_list_all, no_ipv6_prefix_list_all_cmd,
"no ipv6 prefix-list WORD$name",
NO_STR
@@ -1448,7 +1448,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
ipv6_prefix_list_remark, ipv6_prefix_list_remark_cmd,
"ipv6 prefix-list WORD$name description LINE...",
IPV6_STR
@@ -1473,7 +1473,7 @@ DEFPY(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_ipv6_prefix_list_remark, no_ipv6_prefix_list_remark_cmd,
"no ipv6 prefix-list WORD$name description",
NO_STR
diff --git a/lib/if.c b/lib/if.c
index 3620f20e4b..07e786c708 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -1321,7 +1321,7 @@ void if_link_params_free(struct interface *ifp)
/*
* XPath: /frr-interface:lib/interface
*/
-DEFPY_NOSH (interface,
+DEFPY_YANG_NOSH (interface,
interface_cmd,
"interface IFNAME [vrf NAME$vrf_name]",
"Select an interface to configure\n"
@@ -1384,6 +1384,7 @@ DEFPY_NOSH (interface,
* all interface-level commands are converted to the new
* northbound model.
*/
+ nb_cli_pending_commit_check(vty);
ifp = if_lookup_by_name(ifname, vrf_id);
if (ifp)
VTY_PUSH_CONTEXT(INTERFACE_NODE, ifp);
@@ -1392,7 +1393,7 @@ DEFPY_NOSH (interface,
return ret;
}
-DEFPY (no_interface,
+DEFPY_YANG (no_interface,
no_interface_cmd,
"no interface IFNAME [vrf NAME$vrf_name]",
NO_STR
@@ -1427,7 +1428,7 @@ static void cli_show_interface(struct vty *vty, struct lyd_node *dnode,
/*
* XPath: /frr-interface:lib/interface/description
*/
-DEFPY (interface_desc,
+DEFPY_YANG (interface_desc,
interface_desc_cmd,
"description LINE...",
"Interface specific description\n"
@@ -1444,7 +1445,7 @@ DEFPY (interface_desc,
return ret;
}
-DEFPY (no_interface_desc,
+DEFPY_YANG (no_interface_desc,
no_interface_desc_cmd,
"no description",
NO_STR
diff --git a/lib/ipaddr.h b/lib/ipaddr.h
index a96c9788bc..f2b75c1306 100644
--- a/lib/ipaddr.h
+++ b/lib/ipaddr.h
@@ -89,6 +89,13 @@ static inline char *ipaddr2str(const struct ipaddr *ip, char *buf, int size)
return buf;
}
+#define IS_MAPPED_IPV6(A) \
+ ((A)->s6_addr32[0] == 0x00000000 \
+ ? ((A)->s6_addr32[1] == 0x00000000 \
+ ? (ntohl((A)->s6_addr32[2]) == 0xFFFF ? 1 : 0) \
+ : 0) \
+ : 0)
+
/*
* Convert IPv4 address to IPv4-mapped IPv6 address which is of the
* form ::FFFF:<IPv4 address> (RFC 4291). This IPv6 address can then
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index 105fc83cef..534b5128ee 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -53,6 +53,106 @@ static void vty_show_nb_errors(struct vty *vty, int error, const char *errmsg)
vty_out(vty, "Error description: %s\n", errmsg);
}
+static int nb_cli_classic_commit(struct vty *vty)
+{
+ struct nb_context context = {};
+ char errmsg[BUFSIZ] = {0};
+ int ret;
+
+ context.client = NB_CLIENT_CLI;
+ context.user = vty;
+ ret = nb_candidate_commit(&context, vty->candidate_config, true, NULL,
+ NULL, errmsg, sizeof(errmsg));
+ if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
+ vty_out(vty, "%% Configuration failed.\n\n");
+ vty_show_nb_errors(vty, ret, errmsg);
+ if (vty->t_pending_commit)
+ vty_out(vty,
+ "The following commands were dynamically grouped into the same transaction and rejected:\n%s",
+ vty->pending_cmds_buf);
+
+ /* Regenerate candidate for consistency. */
+ nb_config_replace(vty->candidate_config, running_config, true);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void nb_cli_pending_commit_clear(struct vty *vty)
+{
+ THREAD_TIMER_OFF(vty->t_pending_commit);
+ vty->backoff_cmd_count = 0;
+ XFREE(MTYPE_TMP, vty->pending_cmds_buf);
+ vty->pending_cmds_buflen = 0;
+ vty->pending_cmds_bufpos = 0;
+}
+
+static int nb_cli_pending_commit_cb(struct thread *thread)
+{
+ struct vty *vty = THREAD_ARG(thread);
+
+ (void)nb_cli_classic_commit(vty);
+ nb_cli_pending_commit_clear(vty);
+
+ return 0;
+}
+
+void nb_cli_pending_commit_check(struct vty *vty)
+{
+ if (vty->t_pending_commit) {
+ (void)nb_cli_classic_commit(vty);
+ nb_cli_pending_commit_clear(vty);
+ }
+}
+
+static bool nb_cli_backoff_start(struct vty *vty)
+{
+ struct timeval now, delta;
+
+ /*
+ * Start the configuration backoff timer only if 100 YANG-modeled
+ * commands or more were entered within the last second.
+ */
+ monotime(&now);
+ if (monotime_since(&vty->backoff_start, &delta) >= 1000000) {
+ vty->backoff_start = now;
+ vty->backoff_cmd_count = 1;
+ return false;
+ }
+ if (++vty->backoff_cmd_count < 100)
+ return false;
+
+ return true;
+}
+
+static int nb_cli_schedule_command(struct vty *vty)
+{
+ /* Append command to dynamically sized buffer of scheduled commands. */
+ if (!vty->pending_cmds_buf) {
+ vty->pending_cmds_buflen = 4096;
+ vty->pending_cmds_buf =
+ XCALLOC(MTYPE_TMP, vty->pending_cmds_buflen);
+ }
+ if ((strlen(vty->buf) + 3)
+ > (vty->pending_cmds_buflen - vty->pending_cmds_bufpos)) {
+ vty->pending_cmds_buflen *= 2;
+ vty->pending_cmds_buf =
+ XREALLOC(MTYPE_TMP, vty->pending_cmds_buf,
+ vty->pending_cmds_buflen);
+ }
+ strlcat(vty->pending_cmds_buf, "- ", vty->pending_cmds_buflen);
+ vty->pending_cmds_bufpos = strlcat(vty->pending_cmds_buf, vty->buf,
+ vty->pending_cmds_buflen);
+
+ /* Schedule the commit operation. */
+ THREAD_TIMER_OFF(vty->t_pending_commit);
+ thread_add_timer_msec(master, nb_cli_pending_commit_cb, vty, 100,
+ &vty->t_pending_commit);
+
+ return CMD_SUCCESS;
+}
+
void nb_cli_enqueue_change(struct vty *vty, const char *xpath,
enum nb_operation operation, const char *value)
{
@@ -76,7 +176,6 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
{
char xpath_base[XPATH_MAXLEN] = {};
bool error = false;
- int ret;
VTY_CHECK_XPATH;
@@ -95,6 +194,7 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
struct nb_node *nb_node;
char xpath[XPATH_MAXLEN];
struct yang_data *data;
+ int ret;
/* Handle relative XPaths. */
memset(xpath, 0, sizeof(xpath));
@@ -158,25 +258,19 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
}
- /* Do an implicit "commit" when using the classic CLI mode. */
+ /*
+ * Do an implicit commit when using the classic CLI mode.
+ *
+ * NOTE: the implicit commit might be scheduled to run later when
+ * too many commands are being sent at the same time. This is a
+ * protection mechanism where multiple commands are grouped into the
+ * same configuration transaction, allowing them to be processed much
+ * faster.
+ */
if (frr_get_cli_mode() == FRR_CLI_CLASSIC) {
- struct nb_context context = {};
- char errmsg[BUFSIZ] = {0};
-
- context.client = NB_CLIENT_CLI;
- context.user = vty;
- ret = nb_candidate_commit(&context, vty->candidate_config,
- false, NULL, NULL, errmsg,
- sizeof(errmsg));
- if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) {
- vty_out(vty, "%% Configuration failed.\n\n");
- vty_show_nb_errors(vty, ret, errmsg);
-
- /* Regenerate candidate for consistency. */
- nb_config_replace(vty->candidate_config, running_config,
- true);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (vty->t_pending_commit || nb_cli_backoff_start(vty))
+ return nb_cli_schedule_command(vty);
+ return nb_cli_classic_commit(vty);
}
return CMD_SUCCESS;
diff --git a/lib/northbound_cli.h b/lib/northbound_cli.h
index b2d8c8f035..112d62efda 100644
--- a/lib/northbound_cli.h
+++ b/lib/northbound_cli.h
@@ -108,6 +108,14 @@ extern int nb_cli_rpc(const char *xpath, struct list *input,
extern void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
+/*
+ * Perform pending commit, if any.
+ *
+ * vty
+ * The vty context.
+ */
+extern void nb_cli_pending_commit_check(struct vty *vty);
+
/* Prototypes of internal functions. */
extern void nb_cli_show_config_prepare(struct nb_config *config,
bool with_defaults);
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index 10a958abed..7fa759252b 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -39,7 +39,7 @@
#define ROUTE_MAP_SEQUENCE_CMD_STR \
"Sequence to insert to/delete from existing route-map entry\n"
-DEFPY_NOSH(
+DEFPY_YANG_NOSH(
route_map, route_map_cmd,
"route-map WORD$name <deny|permit>$action (1-65535)$sequence",
ROUTE_MAP_CMD_STR
@@ -70,6 +70,7 @@ DEFPY_NOSH(
VTY_PUSH_XPATH(RMAP_NODE, xpath_index);
/* Add support for non-migrated route map users. */
+ nb_cli_pending_commit_check(vty);
rm = route_map_get(name);
action_type = (action[0] == 'p') ? RMAP_PERMIT : RMAP_DENY;
rmi = route_map_index_get(rm, action_type, sequence);
@@ -79,7 +80,7 @@ DEFPY_NOSH(
return rv;
}
-DEFPY(
+DEFPY_YANG(
no_route_map_all, no_route_map_all_cmd,
"no route-map WORD$name",
NO_STR
@@ -94,7 +95,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_route_map, no_route_map_cmd,
"no route-map WORD$name <deny|permit>$action (1-65535)$sequence",
NO_STR
@@ -179,7 +180,7 @@ void route_map_instance_show_end(struct vty *vty, struct lyd_node *dnode)
vty_out(vty, "!\n");
}
-DEFPY(
+DEFPY_YANG(
match_interface, match_interface_cmd,
"match interface IFNAME",
MATCH_STR
@@ -196,7 +197,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_interface, no_match_interface_cmd,
"no match interface [IFNAME]",
NO_STR
@@ -211,7 +212,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ip_address, match_ip_address_cmd,
"match ip address <(1-199)|(1300-2699)|WORD>$name",
MATCH_STR
@@ -231,7 +232,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_address, no_match_ip_address_cmd,
"no match ip address [<(1-199)|(1300-2699)|WORD>]",
NO_STR
@@ -249,7 +250,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ip_address_prefix_list,
match_ip_address_prefix_list_cmd,
"match ip address prefix-list WORD$name",
@@ -269,7 +270,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_address_prefix_list, no_match_ip_address_prefix_list_cmd,
"no match ip address prefix-list [WORD]",
NO_STR
@@ -286,7 +287,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ip_next_hop, match_ip_next_hop_cmd,
"match ip next-hop <(1-199)|(1300-2699)|WORD>$name",
MATCH_STR
@@ -306,7 +307,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_next_hop, no_match_ip_next_hop_cmd,
"no match ip next-hop [<(1-199)|(1300-2699)|WORD>]",
NO_STR
@@ -324,7 +325,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ip_next_hop_prefix_list,
match_ip_next_hop_prefix_list_cmd,
"match ip next-hop prefix-list WORD$name",
@@ -345,7 +346,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_next_hop_prefix_list,
no_match_ip_next_hop_prefix_list_cmd,
"no match ip next-hop prefix-list [WORD]",
@@ -364,7 +365,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ip_next_hop_type, match_ip_next_hop_type_cmd,
"match ip next-hop type <blackhole>$type",
MATCH_STR
@@ -384,7 +385,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ip_next_hop_type, no_match_ip_next_hop_type_cmd,
"no match ip next-hop type [<blackhole>]",
NO_STR MATCH_STR IP_STR
@@ -399,7 +400,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ipv6_address, match_ipv6_address_cmd,
"match ipv6 address WORD$name",
MATCH_STR
@@ -417,7 +418,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ipv6_address, no_match_ipv6_address_cmd,
"no match ipv6 address [WORD]",
NO_STR
@@ -433,7 +434,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ipv6_address_prefix_list, match_ipv6_address_prefix_list_cmd,
"match ipv6 address prefix-list WORD$name",
MATCH_STR
@@ -452,7 +453,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ipv6_address_prefix_list,
no_match_ipv6_address_prefix_list_cmd,
"no match ipv6 address prefix-list [WORD]",
@@ -470,7 +471,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd,
"match ipv6 next-hop type <blackhole>$type",
MATCH_STR IPV6_STR
@@ -489,7 +490,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_ipv6_next_hop_type, no_match_ipv6_next_hop_type_cmd,
"no match ipv6 next-hop type [<blackhole>]",
NO_STR MATCH_STR IPV6_STR
@@ -504,7 +505,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_metric, match_metric_cmd,
"match metric (0-4294967295)$metric",
MATCH_STR
@@ -521,7 +522,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_metric, no_match_metric_cmd,
"no match metric [(0-4294967295)]",
NO_STR
@@ -536,7 +537,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
match_tag, match_tag_cmd,
"match tag (1-4294967295)$tag",
MATCH_STR
@@ -553,7 +554,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_match_tag, no_match_tag_cmd,
"no match tag [(1-4294967295)]",
NO_STR
@@ -646,7 +647,7 @@ void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
}
}
-DEFPY(
+DEFPY_YANG(
set_ip_nexthop, set_ip_nexthop_cmd,
"set ip next-hop A.B.C.D$addr",
SET_STR
@@ -664,7 +665,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_set_ip_nexthop, no_set_ip_nexthop_cmd,
"no set ip next-hop [A.B.C.D]",
NO_STR
@@ -680,7 +681,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
set_ipv6_nexthop_local, set_ipv6_nexthop_local_cmd,
"set ipv6 next-hop local X:X::X:X$addr",
SET_STR
@@ -699,7 +700,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_set_ipv6_nexthop_local, no_set_ipv6_nexthop_local_cmd,
"no set ipv6 next-hop local [X:X::X:X]",
NO_STR
@@ -716,7 +717,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
set_metric, set_metric_cmd,
"set metric <(0-4294967295)$metric|rtt$rtt|+rtt$artt|-rtt$srtt|+metric$ametric|-metric$smetric>",
SET_STR
@@ -762,7 +763,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_set_metric, no_set_metric_cmd,
"no set metric [(0-4294967295)]",
NO_STR
@@ -776,7 +777,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
set_tag, set_tag_cmd,
"set tag (1-4294967295)$tag",
SET_STR
@@ -793,7 +794,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_set_tag, no_set_tag_cmd,
"no set tag [(1-4294967295)]",
NO_STR
@@ -853,7 +854,7 @@ void route_map_action_show(struct vty *vty, struct lyd_node *dnode,
}
}
-DEFPY(
+DEFPY_YANG(
rmap_onmatch_next, rmap_onmatch_next_cmd,
"on-match next",
"Exit policy on matches\n"
@@ -864,7 +865,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_rmap_onmatch_next,
no_rmap_onmatch_next_cmd,
"no on-match next",
@@ -877,7 +878,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
rmap_onmatch_goto, rmap_onmatch_goto_cmd,
"on-match goto (1-65535)$rm_num",
"Exit policy on matches\n"
@@ -890,7 +891,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_rmap_onmatch_goto, no_rmap_onmatch_goto_cmd,
"no on-match goto",
NO_STR
@@ -903,13 +904,13 @@ DEFPY(
}
/* Cisco/GNU Zebra compatibility aliases */
-ALIAS(
+ALIAS_YANG(
rmap_onmatch_goto, rmap_continue_cmd,
"continue (1-65535)$rm_num",
"Continue on a different entry within the route-map\n"
"Route-map entry sequence number\n")
-ALIAS(
+ALIAS_YANG(
no_rmap_onmatch_goto, no_rmap_continue_cmd,
"no continue [(1-65535)]",
NO_STR
@@ -935,7 +936,7 @@ void route_map_exit_policy_show(struct vty *vty, struct lyd_node *dnode,
}
}
-DEFPY(
+DEFPY_YANG(
rmap_call, rmap_call_cmd,
"call WORD$name",
"Jump to another Route-Map after match+set\n"
@@ -946,7 +947,7 @@ DEFPY(
return nb_cli_apply_changes(vty, NULL);
}
-DEFPY(
+DEFPY_YANG(
no_rmap_call, no_rmap_call_cmd,
"no call [NAME]",
NO_STR
@@ -963,7 +964,7 @@ void route_map_call_show(struct vty *vty, struct lyd_node *dnode,
vty_out(vty, " call %s\n", yang_dnode_get_string(dnode, NULL));
}
-DEFPY(
+DEFPY_YANG(
rmap_description, rmap_description_cmd,
"description LINE...",
"Route-map comment\n"
@@ -980,7 +981,7 @@ DEFPY(
return rv;
}
-DEFUN (no_rmap_description,
+DEFUN_YANG (no_rmap_description,
no_rmap_description_cmd,
"no description",
NO_STR
diff --git a/lib/vrf.c b/lib/vrf.c
index a7a10451bf..20e08b03d8 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -638,6 +638,7 @@ int vrf_handler_create(struct vty *vty, const char *vrfname,
ret = nb_cli_apply_changes(vty, xpath_list);
if (ret == CMD_SUCCESS) {
VTY_PUSH_XPATH(VRF_NODE, xpath_list);
+ nb_cli_pending_commit_check(vty);
vrfp = vrf_lookup_by_name(vrfname);
if (vrfp)
VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
@@ -731,7 +732,7 @@ DEFUN_NOSH(vrf_exit,
return CMD_SUCCESS;
}
-DEFUN_NOSH (vrf,
+DEFUN_YANG_NOSH (vrf,
vrf_cmd,
"vrf NAME",
"Select a VRF to configure\n"
@@ -743,7 +744,7 @@ DEFUN_NOSH (vrf,
return vrf_handler_create(vty, vrfname, NULL);
}
-DEFUN (no_vrf,
+DEFUN_YANG (no_vrf,
no_vrf_cmd,
"no vrf NAME",
NO_STR
diff --git a/lib/vty.c b/lib/vty.c
index 0d0e54af6c..184c7604b8 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -2631,6 +2631,9 @@ int vty_config_node_exit(struct vty *vty)
{
vty->xpath_index = 0;
+ /* Perform pending commit if any. */
+ nb_cli_pending_commit_check(vty);
+
/* Check if there's a pending confirmed commit. */
if (vty->t_confirmed_commit_timeout) {
vty_out(vty,
diff --git a/lib/vty.h b/lib/vty.h
index 5d5676199b..623f97ab03 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -134,6 +134,14 @@ struct vty {
/* Base candidate configuration. */
struct nb_config *candidate_config_base;
+ /* Dynamic transaction information. */
+ struct timeval backoff_start;
+ size_t backoff_cmd_count;
+ struct thread *t_pending_commit;
+ char *pending_cmds_buf;
+ size_t pending_cmds_buflen;
+ size_t pending_cmds_bufpos;
+
/* Confirmed-commit timeout and rollback configuration. */
struct thread *t_confirmed_commit_timeout;
struct nb_config *confirmed_commit_rollback;