Merge pull request #14492 from LabNConsulting/oper-state

oper state
This commit is contained in:
Igor Ryzhov 2023-12-29 07:08:30 +02:00 committed by GitHub
commit 353ee7bb81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 15492 additions and 473 deletions

View File

@ -80,6 +80,8 @@ ForEachMacros:
# libyang outliers:
- 'LY_FOR_KEYS'
- 'LY_LIST_FOR'
- 'LYD_LIST_FOR_INST'
- 'LYD_LIST_FOR_INST_SAFE'
- 'LY_TREE_FOR'
- 'LY_TREE_DFS_BEGIN'
- 'LYD_TREE_DFS_BEGIN'

View File

@ -379,6 +379,7 @@ else
fi
AC_C_FLAG([-Wno-unused-parameter])
AC_C_FLAG([-Wno-missing-field-initializers])
AC_C_FLAG([-Wno-microsoft-anon-tag])
AC_C_FLAG([-Wc++-compat], [], [CXX_COMPAT_CFLAGS="-Wc++-compat"])
AC_SUBST([CXX_COMPAT_CFLAGS])

View File

@ -306,8 +306,9 @@ define mq_walk
end
set $mg = $mg->next
end
end
document mg_walk
document mq_walk
Walk the memory data structures to show what is holding memory.
Arguments:
@ -315,3 +316,49 @@ Arguments:
sure where to start pass it mg_first, which is a global DS for
all memory allocated in FRR
end
define __darr_meta
set $_ = ((struct darr_metadata *)$arg0) - 1
end
document __darr_meta
Store a pointer to the struct darr_metadata in $_ for the given dynamic array.
Argument: a pointer to a darr dynamic array.
Returns: pointer to the struct darr_metadata in $_.
end
define darr_meta
__darr_meta $arg0
p *$_
end
document darr_meta
Print the struct darr_metadata for the given dynamic array. Store the value
in $_ as well.
Argument: a pointer to a darr dynamic array.
Returns: pointer to the struct darr_metadata in $_.
end
define darr_len
__darr_meta $arg0
set $_ = $_->len
p $_
end
document darr_len
Print the length of the given dynamic array, and store in $_.
Argument: a pointer to a darr dynamic array.
Returns: length of the array.
end
define darr_cap
__darr_meta $arg0
set $_ = $_->cap
p $_
end
document darr_len
Print the capacity of the given dynamic array, and store in $_.
Argument: a pointer to a darr dynamic array.
Returns: capacity of the array.
end

View File

@ -10,6 +10,7 @@
#include "memory.h"
DEFINE_MTYPE(LIB, DARR, "Dynamic Array");
DEFINE_MTYPE(LIB, DARR_STR, "Dynamic Array String");
static uint _msb(uint count)
{
@ -52,29 +53,72 @@ static size_t darr_size(uint count, size_t esize)
return count * esize + sizeof(struct darr_metadata);
}
void *__darr_resize(void *a, uint count, size_t esize)
char *__darr_in_vsprintf(char **sp, bool concat, const char *fmt, va_list ap)
{
size_t inlen = concat ? darr_strlen(*sp) : 0;
size_t capcount = strlen(fmt) + MIN(inlen + 64, 128);
ssize_t len;
darr_ensure_cap(*sp, capcount);
if (!concat)
darr_reset(*sp);
/* code below counts on having a NUL terminated string */
if (darr_len(*sp) == 0)
*darr_append(*sp) = 0;
again:
len = vsnprintf(darr_last(*sp), darr_avail(*sp), fmt, ap);
if (len < 0)
darr_in_strcat(*sp, fmt);
else if ((size_t)len < darr_avail(*sp))
_darr_len(*sp) += len;
else {
darr_ensure_cap(*sp, darr_len(*sp) + (size_t)len);
goto again;
}
return *sp;
}
char *__darr_in_sprintf(char **sp, bool concat, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
(void)__darr_in_vsprintf(sp, concat, fmt, ap);
va_end(ap);
return *sp;
}
void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mtype)
{
uint ncount = darr_next_count(count, esize);
size_t osz = (a == NULL) ? 0 : darr_size(darr_cap(a), esize);
size_t sz = darr_size(ncount, esize);
struct darr_metadata *dm = XREALLOC(MTYPE_DARR,
a ? _darr_meta(a) : NULL, sz);
struct darr_metadata *dm;
if (sz > osz)
memset((char *)dm + osz, 0, sz - osz);
if (a) {
dm = XREALLOC(_darr_meta(a)->mtype, _darr_meta(a), sz);
if (sz > osz)
memset((char *)dm + osz, 0, sz - osz);
} else {
dm = XCALLOC(mtype, sz);
dm->mtype = mtype;
}
dm->cap = ncount;
return (void *)(dm + 1);
}
void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero)
void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero,
struct memtype *mtype)
{
struct darr_metadata *dm;
uint olen, nlen;
if (!a)
a = __darr_resize(NULL, at + count, esize);
a = __darr_resize(NULL, at + count, esize, mtype);
dm = (struct darr_metadata *)a - 1;
olen = dm->len;
@ -89,7 +133,7 @@ void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero)
nlen = olen + count;
if (nlen > dm->cap) {
a = __darr_resize(a, nlen, esize);
a = __darr_resize(a, nlen, esize, mtype);
dm = (struct darr_metadata *)a - 1;
}

View File

@ -3,22 +3,37 @@
* June 23 2023, Christian Hopps <chopps@labn.net>
*
* Copyright (c) 2023, LabN Consulting, L.L.C.
*
*/
#ifndef _FRR_DARR_H_
#define _FRR_DARR_H_
/*
* API functions:
* ==============
* - darr_append
* - darr_append_mt
* - darr_append_n
* - darr_append_n_mt
* - darr_append_nz
* - darr_append_nz_mt
* - darr_cap
* - darr_ensure_avail
* - darr_ensure_avail_mt
* - darr_ensure_cap
* - darr_ensure_cap_mt
* - darr_ensure_i
* - darr_foreach_i
* - darr_foreach_p
* - darr_ensure_i_mt
* - darr_free
* - darr_insert
* - darr_insert_mt
* - darr_insertz
* - darr_insertz_mt
* - darr_insert_n
* - darr_insert_n_mt
* - darr_insert_nz
* - darr_insert_nz_mt
* - darr_last
* - darr_lasti
* - darr_len
* - darr_maxi
* - darr_pop
@ -28,41 +43,80 @@
* - darr_remove_n
* - darr_reset
* - darr_setlen
*
* Iteration
* ---------
* - darr_foreach_i
* - darr_foreach_p
*
* String Utilities
* ----------------
* - darr_in_strcat_tail
* - darr_in_strcatf, darr_in_vstrcatf
* - darr_in_strdup
* - darr_in_strdup_cap
* - darr_in_sprintf, darr_in_vsprintf
* - darr_set_strlen
* - darr_strdup
* - darr_strdup_cap
* - darr_strlen
* - darr_strnul
* - darr_sprintf, darr_vsprintf
*/
/*
* A few assured items
*
* - DAs will never have capacity 0 unless they are NULL pointers.
*/
/*
* NOTE: valgrind by default enables a "length64" heuristic (among others) which
* identifies "interior-pointer" 8 bytes forward of a "start-pointer" as a
* "start-pointer". This should cause what normally would be "possibly-lost"
* errors to instead be definite for dynamic arrays. This is b/c the header is 8 bytes
*/
#include <zebra.h>
#include "memory.h"
DECLARE_MTYPE(DARR);
DECLARE_MTYPE(DARR_STR);
struct darr_metadata {
uint len;
uint cap;
uint32_t len;
uint32_t cap;
struct memtype *mtype;
};
void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero);
void *__darr_resize(void *a, uint count, size_t esize);
#define _darr_esize(A) sizeof((A)[0])
#define darr_esize(A) sizeof((A)[0])
#define _darr_len(A) _darr_meta(A)->len
#define _darr_meta(A) (((struct darr_metadata *)(A)) - 1)
#define _darr_resize(A, C) ({ (A) = __darr_resize((A), C, _darr_esize(A)); })
void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero,
struct memtype *mt);
char *__darr_in_sprintf(char **sp, bool concat, const char *fmt, ...)
PRINTFRR(3, 4);
char *__darr_in_vsprintf(char **sp, bool concat, const char *fmt, va_list ap)
PRINTFRR(3, 0);
void *__darr_resize(void *a, uint count, size_t esize, struct memtype *mt);
#define _darr_esize(A) sizeof((A)[0])
#define darr_esize(A) sizeof((A)[0])
#define _darr_len(A) _darr_meta(A)->len
#define _darr_meta(A) (((struct darr_metadata *)(A)) - 1)
#define _darr_resize_mt(A, C, MT) \
({ (A) = __darr_resize(A, C, _darr_esize(A), MT); })
#define _darr_resize(A, C) _darr_resize_mt(A, C, MTYPE_DARR)
/* Get the current capacity of the array */
#define darr_cap(A) (((A) == NULL) ? 0 : _darr_meta(A)->cap)
/* Get the current available expansion space */
#define darr_avail(A) (((A) == NULL) ? 0 : (darr_cap(A) - darr_len(A)))
/* Get the largest possible index one can `darr_ensure_i` w/o resizing */
#define darr_maxi(A) ((int)darr_cap(A) - 1)
/**
* Get the current length of the array.
*
* As long as `A` is non-NULL, this macro may be used as an L-value to modify
* the length of the array.
* darr_len() - Get the current length of the array as a unsigned int.
* darr_ilen() - Get the current length of the array as an int.
*
* Args:
* A: The dynamic array, can be NULL.
@ -70,7 +124,19 @@ void *__darr_resize(void *a, uint count, size_t esize);
* Return:
* The current length of the array.
*/
#define darr_len(A) (((A) == NULL) ? 0 : _darr_meta(A)->len)
#define darr_len(A) (((A) == NULL) ? 0 : _darr_meta(A)->len)
#define darr_ilen(A) (((A) == NULL) ? 0 : (ssize_t)_darr_meta(A)->len)
/**
* darr_lasti() - Get the last element's index.
*
* Args:
* A: The dynamic array, can be NULL.
*
* Return:
* The current last element index, or -1 for none.
*/
#define darr_lasti(A) (darr_ilen(A) - 1)
/**
* Set the current length of the array `A` to 0.
@ -99,11 +165,41 @@ void *__darr_resize(void *a, uint count, size_t esize);
assert((A) || !(L)); \
if ((A)) { \
/* have to cast to avoid compiler warning for "0" */ \
assert((long long)darr_cap(A) >= (L)); \
assert((long long)darr_cap(A) >= (long long)(L)); \
_darr_len(A) = (L); \
} \
} while (0)
/**
* Set the string length of the array `S` to `L`, and NUL
* terminate the string at L. The dynamic array length will be `L` + 1.
*
* Thus after calling:
*
* darr_len(S) == L + 1
* darr_strlen(S) == L
* S[L] == 0
*
* This function does *not* guarantee the `L` + 1 memory is allocated to
* the array, use `darr_ensure` or `*_cap` functions for that.
*
* Args:
* S: The dynamic array, cannot be NULL.
* L: The new str length of the array, will set
*
* Return:
* A pointer to the end of S (i.e., pointing to the NUL byte).
*/
#define darr_set_strlen(S, L) \
({ \
assert((S)); \
/* have to cast to avoid compiler warning for "0" */ \
assert((long long)darr_cap(S) >= (long long)(L)); \
_darr_len(S) = (L) + 1; \
*darr_last(S) = 0; \
darr_last(S); \
})
/**
* Free memory allocated for the dynamic array `A`
*
@ -114,12 +210,38 @@ void *__darr_resize(void *a, uint count, size_t esize);
#define darr_free(A) \
do { \
if ((A)) { \
void *__ptr = _darr_meta(A); \
XFREE(MTYPE_DARR, __ptr); \
struct darr_metadata *__meta = _darr_meta(A); \
XFREE(__meta->mtype, __meta); \
(A) = NULL; \
} \
} while (0)
/**
* Make sure that there is room in the dynamic array `A` to add `C` elements.
*
* Available space is `darr_cap(a) - darr_len(a)`.
*
* The value `A` may be changed as a result of this call in which case any
* pointers into the previous memory block are no longer valid. The `A` value
* is guaranteed not to change if there is sufficient capacity in the array.
*
* Args:
* A: (IN/OUT) the dynamic array, can be NULL.
* S: Amount of free space to guarantee.
*
* Return:
* A pointer to the (possibly moved) array.
*/
#define darr_ensure_avail_mt(A, S, MT) \
({ \
ssize_t need = (ssize_t)(S) - \
(ssize_t)(darr_cap(A) - darr_len(A)); \
if (need > 0) \
_darr_resize_mt((A), darr_cap(A) + need, MT); \
(A); \
})
#define darr_ensure_avail(A, S) darr_ensure_avail_mt(A, S, MTYPE_DARR)
/**
* Make sure that there is room in the dynamic array `A` for `C` elements.
*
@ -129,17 +251,19 @@ void *__darr_resize(void *a, uint count, size_t esize);
*
* Args:
* A: (IN/OUT) the dynamic array, can be NULL.
* I: the index to guarantee memory exists for
* C: Total capacity to guarantee.
*
* Return:
* A pointer to the (possibly moved) array.
*/
#define darr_ensure_cap(A, C) \
#define darr_ensure_cap_mt(A, C, MT) \
({ \
if (darr_cap(A) < (C)) \
_darr_resize((A), (C)); \
/* Cast to avoid warning when C == 0 */ \
if ((ssize_t)darr_cap(A) < (ssize_t)(C)) \
_darr_resize_mt((A), (C), MT); \
(A); \
})
#define darr_ensure_cap(A, C) darr_ensure_cap_mt(A, C, MTYPE_DARR)
/**
* Return a pointer to the (I)th element of array `A`, making sure there is
@ -159,18 +283,19 @@ void *__darr_resize(void *a, uint count, size_t esize);
* Return:
* A pointer to the (I)th element in `A`
*/
#define darr_ensure_i(A, I) \
#define darr_ensure_i_mt(A, I, MT) \
({ \
if ((int)(I) > darr_maxi(A)) \
_darr_resize((A), (I) + 1); \
_darr_resize_mt((A), (I) + 1, MT); \
if ((I) + 1 > _darr_len(A)) \
_darr_len(A) = (I) + 1; \
&(A)[I]; \
})
#define darr_ensure_i(A, I) darr_ensure_i_mt(A, I, MTYPE_DARR)
#define _darr_insert_n(A, I, N, Z) \
#define _darr_insert_n(A, I, N, Z, MT) \
({ \
(A) = __darr_insert_n(A, I, N, _darr_esize(A), Z); \
(A) = __darr_insert_n(A, I, N, _darr_esize(A), Z, MT); \
&(A)[I]; \
})
/**
@ -191,8 +316,10 @@ void *__darr_resize(void *a, uint count, size_t esize);
* Return:
* A pointer to the first inserted element in the array.
*/
#define darr_insert_n(A, I, N) _darr_insert_n(A, I, N, false)
#define darr_insert_nz(A, I, N) _darr_insert_n(A, I, N, true)
#define darr_insert_n(A, I, N) _darr_insert_n(A, I, N, false, MTYPE_DARR)
#define darr_insert_n_mt(A, I, N) _darr_insert_n(A, I, N, false, MT)
#define darr_insert_nz(A, I, N) _darr_insert_n(A, I, N, true, MTYPE_DARR)
#define darr_insert_nz_mt(A, I, N) _darr_insert_n(A, I, N, true, MT)
/**
* Insert an uninitialized element in the array at index `I`.
@ -212,8 +339,10 @@ void *__darr_resize(void *a, uint count, size_t esize);
* Return:
* A pointer to the element in the array.
*/
#define darr_insert(A, I) _darr_insert_n(A, I, 1, false)
#define darr_insertz(A, I) _darr_insert_n(A, I, 1, true)
#define darr_insert(A, I) _darr_insert_n(A, I, 1, false, MTYPE_DARR)
#define darr_insert_mt(A, I) _darr_insert_n(A, I, 1, false, MT)
#define darr_insertz(A, I) _darr_insert_n(A, I, 1, true, MTYPE_DARR)
#define darr_insertz_mt(A, I) _darr_insert_n(A, I, 1, true, MT)
/**
* Remove `N` elements from the array starting at index `I`.
@ -251,10 +380,10 @@ void *__darr_resize(void *a, uint count, size_t esize);
#define darr_remove(A, I) darr_remove_n(A, I, 1)
#define _darr_append_n(A, N, Z) \
#define _darr_append_n(A, N, Z, MT) \
({ \
uint __len = darr_len(A); \
darr_ensure_cap(A, __len + (N)); \
darr_ensure_cap_mt(A, __len + (N), MT); \
_darr_len(A) = __len + (N); \
if (Z) \
memset(&(A)[__len], 0, (N)*_darr_esize(A)); \
@ -271,8 +400,10 @@ void *__darr_resize(void *a, uint count, size_t esize);
* Return:
* A pointer to the first of the added elements at the end of the array.
*/
#define darr_append_n(A, N) _darr_append_n(A, N, false)
#define darr_append_nz(A, N) _darr_append_n(A, N, true)
#define darr_append_n(A, N) _darr_append_n(A, N, false, MTYPE_DARR)
#define darr_append_n_mt(A, N, MT) _darr_append_n(A, N, false, MT)
#define darr_append_nz(A, N) _darr_append_n(A, N, true, MTYPE_DARR)
#define darr_append_nz_mt(A, N, MT) _darr_append_n(A, N, true, MT)
/**
* Extending the array's length by 1.
@ -285,8 +416,10 @@ void *__darr_resize(void *a, uint count, size_t esize);
* Return:
* A pointer to the new element at the end of the array.
*/
#define darr_append(A) _darr_append_n(A, 1, false)
#define darr_appendz(A) _darr_append_n(A, 1, true)
#define darr_append(A) _darr_append_n(A, 1, false, MTYPE_DARR)
#define darr_append_mt(A, MT) _darr_append_n(A, 1, false, MT)
#define darr_appendz(A) _darr_append_n(A, 1, true, MTYPE_DARR)
#define darr_appendz_mt(A, MT) _darr_append_n(A, 1, true, MT)
/**
* Append an element `E` onto the array `A`, extending it's length by 1.
@ -299,8 +432,10 @@ void *__darr_resize(void *a, uint count, size_t esize);
* Return:
* A pointer to the element in the array.
*/
#define darr_push(A, E) (*darr_append(A) = (E))
#define darr_pushz(A) (darr_appendz(A))
#define darr_push(A, E) (*darr_append(A) = (E))
#define darr_push_mt(A, E, MT) (*darr_append_mt(A, MT) = (E))
#define darr_pushz(A) (darr_appendz(A))
#define darr_pushz_mt(A, MT) (darr_appendz_mt(A, MT))
/**
@ -348,6 +483,246 @@ void *__darr_resize(void *a, uint count, size_t esize);
*/
#define darr_end(A) ((A) + darr_len(A))
/**
* darr_last() - Get a pointer to the last element of the array.
* darr_strnul() - Get a pointer to the NUL byte of the darr string or NULL.
*
* Args:
* A: The dynamic array, can be NULL.
*
* Return:
* A pointer to the last element of the array or NULL if the array is
* empty.
*/
#define darr_last(A) \
({ \
uint __len = darr_len(A); \
((__len > 0) ? &(A)[__len - 1] : NULL); \
})
#define darr_strnul(S) darr_last(S)
/**
* darr_in_sprintf() - sprintf into D.
*
* Args:
* D: The destination darr, D's value may be NULL.
* F: The format string
* ...: variable arguments for format string.
*
* Return:
* The dynamic_array D with the new string content.
*/
#define darr_in_sprintf(D, F, ...) __darr_in_sprintf(&(D), 0, F, __VA_ARGS__)
/**
* darr_in_strcat() - concat a string into a darr string.
*
* Args:
* D: The destination darr, D's value may be NULL.
* S: The string to concat onto D.
*
* Return:
* The dynamic_array D with the new string content.
*/
#define darr_in_strcat(D, S) \
({ \
uint __dlen = darr_strlen(D); \
uint __slen = strlen(S); \
darr_ensure_cap_mt(D, __dlen + __slen + 1, MTYPE_DARR_STR); \
if (darr_len(D) == 0) \
*darr_append(D) = 0; \
memcpy(darr_last(D), (S), __slen + 1); \
_darr_len(D) += __slen; \
D; \
})
/**
* darr_in_strcatf() - concat a formatted string into a darr string.
*
* Args:
* D: The destination darr, D's value may be NULL.
* F: The format string to concat onto D after adding arguments.
* ...: The arguments for the format string.
* Return:
* The dynamic_array D with the new string content.
*/
#define darr_in_strcatf(D, F, ...) \
__darr_in_sprintf(&(D), true, (F), __VA_ARGS__)
/**
* darr_in_strcat_tail() - copies end of one darr str to another.
*
* This is a rather specialized function, it takes 2 darr's, a destination and a
* source. If the source is not longer than the destination nothing is done.
* Otherwise the characters in the source that lie beyond the length of the dest
* are added to the dest. No checking is done to make sure the common prefix
* matches. For example:
*
* D: "/foo"
* S: "/foo/bar"
* -> D: "/foo/bar"
*
* perhaps surprising results:
* D: "/foo"
* S: "/zoo/bar"
* -> D: "/foo/bar"
*
* Args:
* D: The destination darr, D's value may be NULL.
* S: The string to copy the tail from.
*
* Return:
* The dynamic_array D with the extended string content.
*/
#define darr_in_strcat_tail(D, S) \
({ \
int __dsize, __ssize, __extra; \
\
if (darr_len(D) == 0) \
*darr_append(D) = 0; \
__dsize = darr_ilen(D); \
__ssize = darr_ilen(S); \
__extra = __ssize - __dsize; \
if (__extra > 0) { \
darr_ensure_cap_mt(D, (uint)__ssize, MTYPE_DARR_STR); \
memcpy(darr_last(D), (S) + __dsize - 1, __extra + 1); \
_darr_len(D) += __extra; \
} \
D; \
})
/**
* darr_in_strdup_cap() - duplicate the string into a darr reserving capacity.
* darr_in_strdup() - duplicate the string into a darr.
*
* Args:
* D: The destination darr, D's value may be NULL.
* S: The string to duplicate.
* C: The capacity to reserve.
*
* Return:
* The dynamic_array D with the duplicated string.
*/
#define darr_in_strdup_cap(D, S, C) \
({ \
size_t __size = strlen(S) + 1; \
darr_reset(D); \
darr_ensure_cap_mt(D, \
((size_t)(C) > __size) ? (size_t)(C) \
: __size, \
MTYPE_DARR_STR); \
strlcpy(D, (S), darr_cap(D)); \
darr_setlen((D), (size_t)__size); \
D; \
})
#define darr_in_strdup(D, S) darr_in_strdup_cap(D, S, 1)
/**
* darr_in_vsprintf() - vsprintf into D.
*
* Args:
* D: The destination darr, D's value may be NULL.
* F: The format string
* A: Varargs
*
* Return:
* The dynamic_array D with the new string content.
*/
#define darr_in_vsprintf(D, F, A) __darr_in_vsprintf(&(D), 0, F, A)
/**
* darr_in_vstrcatf() - concat a formatted string into a darr string.
*
* Args:
* D: The destination darr, D's value may be NULL.
* F: The format string to concat onto D after adding arguments.
* A: Varargs
*
* Return:
* The dynamic_array D with the new string content.
*/
#define darr_in_vstrcatf(D, F, A) __darr_in_vsprintf(&(D), true, (F), (A))
/**
* darr_sprintf() - sprintf into a new dynamic array.
*
* Args:
* F: The format string
* ...: variable arguments for format string.
*
* Return:
* A char * dynamic_array with the new string content.
*/
#define darr_sprintf(F, ...) \
({ \
char *d = NULL; \
__darr_in_sprintf(&d, false, F, __VA_ARGS__); \
d; \
})
/**
* darr_strdup_cap() - duplicate the string reserving capacity.
* darr_strdup() - duplicate the string into a dynamic array.
*
* Args:
* S: The string to duplicate.
* C: The capacity to reserve.
*
* Return:
* The dynamic_array with the duplicated string.
*/
#define darr_strdup_cap(S, C) \
({ \
size_t __size = strlen(S) + 1; \
char *__s = NULL; \
/* Cast to ssize_t to avoid warning when C == 0 */ \
darr_ensure_cap_mt(__s, \
((ssize_t)(C) > (ssize_t)__size) \
? (size_t)(C) \
: __size, \
MTYPE_DARR_STR); \
strlcpy(__s, (S), darr_cap(__s)); \
darr_setlen(__s, (size_t)__size); \
__s; \
})
#define darr_strdup(S) darr_strdup_cap(S, 0)
/**
* darr_strlen() - get the length of the NUL terminated string in a darr.
*
* Args:
* S: The string to measure, value may be NULL.
*
* Return:
* The length of the NUL terminated string in @S
*/
#define darr_strlen(S) \
({ \
uint __size = darr_len(S); \
if (__size) \
__size -= 1; \
assert(!(S) || ((char *)(S))[__size] == 0); \
__size; \
})
/**
* darr_vsprintf() - vsprintf into a new dynamic array.
*
* Args:
* F: The format string
* A: Varargs
*
* Return:
* The dynamic_array D with the new string content.
*/
#define darr_vsprintf(F, A) \
({ \
char *d = NULL; \
darr_in_vsprintf(d, F, A); \
d; \
})
/**
* Iterate over array `A` using a pointer to each element in `P`.
*
@ -365,3 +740,5 @@ void *__darr_resize(void *a, uint count, size_t esize);
* I: A uint variable to store the current element index in.
*/
#define darr_foreach_i(A, I) for ((I) = 0; (I) < darr_len(A); (I)++)
#endif /* _FRR_DARR_H_ */

View File

@ -249,3 +249,24 @@ const char *frrstr_skip_over_char(const char *s, int skipc)
}
return NULL;
}
/*
* Advance backward in string until reaching the char `toc`
* if beginning of string is reached w/o finding char return NULL
*
* /foo/bar'baz/booz'/foo
*/
const char *frrstr_back_to_char(const char *s, int toc)
{
const char *next = s;
const char *prev = NULL;
if (s[0] == 0)
return NULL;
if (!strpbrk(s, "'\"\\"))
return strrchr(s, toc);
while ((next = frrstr_skip_over_char(next, toc)))
prev = next - 1;
return prev;
}

View File

@ -167,13 +167,19 @@ int all_digit(const char *str);
*/
char *frrstr_hex(char *buff, size_t bufsiz, const uint8_t *str, size_t num);
/*
* Advance past a given char `skipc` in a string, while honoring quoting and
* backslash escapes (i.e., ignore `skipc` which occur in quoted sections).
*/
const char *frrstr_skip_over_char(const char *s, int skipc);
/*
* Advance back from end to a given char `toc` in a string, while honoring
* quoting and backslash escapes. `toc` chars inside quote or escaped are
* ignored.
*/
const char *frrstr_back_to_char(const char *s, int toc);
#ifdef __cplusplus
}
#endif

View File

@ -114,25 +114,11 @@ message BeCfgDataApplyReply {
optional string error_if_any = 3;
}
message BeOperDataGetReq {
required uint64 txn_id = 1;
required uint64 batch_id = 2;
repeated YangGetDataReq data = 3;
}
message YangDataReply {
repeated YangData data = 1;
required int64 next_indx = 2;
}
message BeOperDataGetReply {
required uint64 txn_id = 1;
required uint64 batch_id = 2;
required bool success = 3;
optional string error = 4;
optional YangDataReply data = 5;
}
//
// Any message on the MGMTD Backend Interface.
//
@ -146,8 +132,6 @@ message BeMessage {
BeCfgDataCreateReply cfg_data_reply = 7;
BeCfgDataApplyReq cfg_apply_req = 8;
BeCfgDataApplyReply cfg_apply_reply = 9;
BeOperDataGetReq get_req = 10;
BeOperDataGetReply get_reply = 11;
}
}

View File

@ -8,9 +8,12 @@
#include <zebra.h>
#include "debug.h"
#include "compiler.h"
#include "darr.h"
#include "libfrr.h"
#include "lib_errors.h"
#include "mgmt_be_client.h"
#include "mgmt_msg.h"
#include "mgmt_msg_native.h"
#include "mgmt_pb.h"
#include "network.h"
#include "northbound.h"
@ -23,11 +26,11 @@ DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_CLIENT, "backend client");
DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_CLIENT_NAME, "backend client name");
DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_BATCH, "backend transaction batch data");
DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_TXN, "backend transaction data");
DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_GT_CB_ARGS, "backend get-tree cb args");
enum mgmt_be_txn_event {
MGMTD_BE_TXN_PROC_SETCFG = 1,
MGMTD_BE_TXN_PROC_GETCFG,
MGMTD_BE_TXN_PROC_GETDATA
};
struct mgmt_be_set_cfg_req {
@ -35,19 +38,18 @@ struct mgmt_be_set_cfg_req {
uint16_t num_cfg_changes;
};
struct mgmt_be_get_data_req {
char *xpaths[MGMTD_MAX_NUM_DATA_REQ_IN_BATCH];
uint16_t num_xpaths;
};
struct mgmt_be_txn_req {
enum mgmt_be_txn_event event;
union {
struct mgmt_be_set_cfg_req set_cfg;
struct mgmt_be_get_data_req get_data;
} req;
};
struct be_oper_iter_arg {
struct lyd_node *root; /* the tree we are building */
struct lyd_node *hint; /* last node added */
};
PREDECL_LIST(mgmt_be_batches);
struct mgmt_be_batch_ctx {
struct mgmt_be_txn_req txn_req;
@ -119,6 +121,15 @@ struct debug mgmt_dbg_be_client = {
/* NOTE: only one client per proc for now. */
static struct mgmt_be_client *__be_client;
static int be_client_send_native_msg(struct mgmt_be_client *client_ctx,
void *msg, size_t len,
bool short_circuit_ok)
{
return msg_conn_send_msg(&client_ctx->client.conn,
MGMT_MSG_VERSION_NATIVE, msg, len, NULL,
short_circuit_ok);
}
static int mgmt_be_client_send_msg(struct mgmt_be_client *client_ctx,
Mgmtd__BeMessage *be_msg)
{
@ -190,7 +201,8 @@ mgmt_be_find_txn_by_id(struct mgmt_be_client *client_ctx, uint64_t txn_id,
if (txn->txn_id == txn_id)
return txn;
if (warn)
MGMTD_BE_CLIENT_ERR("Unknown txn-id: %" PRIu64, txn_id);
MGMTD_BE_CLIENT_ERR("client %s unkonwn txn-id: %" PRIu64,
client_ctx->name, txn_id);
return NULL;
}
@ -263,6 +275,41 @@ static void mgmt_be_cleanup_all_txns(struct mgmt_be_client *client_ctx)
}
}
/**
* Send an error back to MGMTD using native messaging.
*
* Args:
* client: the BE client.
* txn_id: the txn_id this error pertains to.
* short_circuit_ok: True if OK to short-circuit the call.
* error: An integer error value.
* errfmt: An error format string (i.e., printfrr)
* ...: args for use by the `errfmt` format string.
*
* Return:
* the return value from the underlying send message function.
*/
static int be_client_send_error(struct mgmt_be_client *client, uint64_t txn_id,
uint64_t req_id, bool short_circuit_ok,
int16_t error, const char *errfmt, ...)
PRINTFRR(6, 7);
static int be_client_send_error(struct mgmt_be_client *client, uint64_t txn_id,
uint64_t req_id, bool short_circuit_ok,
int16_t error, const char *errfmt, ...)
{
va_list ap;
int ret;
va_start(ap, errfmt);
ret = vmgmt_msg_native_send_error(&client->client.conn, txn_id, req_id,
short_circuit_ok, error, errfmt, ap);
va_end(ap);
return ret;
}
static int mgmt_be_send_txn_reply(struct mgmt_be_client *client_ctx,
uint64_t txn_id, bool create)
{
@ -702,19 +749,11 @@ static int mgmt_be_client_handle_msg(struct mgmt_be_client *client_ctx,
mgmt_be_process_cfg_apply(
client_ctx, (uint64_t)be_msg->cfg_apply_req->txn_id);
break;
case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ:
MGMTD_BE_CLIENT_ERR("Got unhandled message type %u",
be_msg->message_case);
/*
* TODO: Add handling code in future.
*/
break;
/*
* NOTE: The following messages are always sent from Backend
* clients to MGMTd only and/or need not be handled here.
*/
case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ:
case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY:
case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY:
case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY:
case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY:
@ -732,6 +771,119 @@ static int mgmt_be_client_handle_msg(struct mgmt_be_client *client_ctx,
return 0;
}
struct be_client_tree_data_batch_args {
struct mgmt_be_client *client;
uint64_t txn_id;
uint64_t req_id;
LYD_FORMAT result_type;
};
/*
* Process the get-tree request on our local oper state
*/
static enum nb_error be_client_send_tree_data_batch(const struct lyd_node *tree,
void *arg, enum nb_error ret)
{
struct be_client_tree_data_batch_args *args = arg;
struct mgmt_be_client *client = args->client;
struct mgmt_msg_tree_data *tree_msg = NULL;
bool more = false;
uint8_t **darrp;
LY_ERR err;
if (ret == NB_YIELD) {
more = true;
ret = NB_OK;
}
if (ret != NB_OK)
goto done;
tree_msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_tree_data, 0,
MTYPE_MSG_NATIVE_TREE_DATA);
tree_msg->refer_id = args->txn_id;
tree_msg->req_id = args->req_id;
tree_msg->code = MGMT_MSG_CODE_TREE_DATA;
tree_msg->result_type = args->result_type;
tree_msg->more = more;
darrp = mgmt_msg_native_get_darrp(tree_msg);
err = yang_print_tree_append(darrp, tree, args->result_type,
(LYD_PRINT_WD_EXPLICIT |
LYD_PRINT_WITHSIBLINGS));
if (err) {
ret = NB_ERR;
goto done;
}
(void)be_client_send_native_msg(client, tree_msg,
mgmt_msg_native_get_msg_len(tree_msg),
false);
done:
mgmt_msg_native_free_msg(tree_msg);
if (ret)
be_client_send_error(client, args->txn_id, args->req_id, false,
-EINVAL,
"FE cilent %s txn-id %" PRIu64
" error fetching oper state %d",
client->name, args->txn_id, ret);
if (ret != NB_OK || !more)
XFREE(MTYPE_MGMTD_BE_GT_CB_ARGS, args);
return ret;
}
/*
* Process the get-tree request on our local oper state
*/
static void be_client_handle_get_tree(struct mgmt_be_client *client,
uint64_t txn_id, void *msgbuf,
size_t msg_len)
{
struct mgmt_msg_get_tree *get_tree_msg = msgbuf;
struct be_client_tree_data_batch_args *args;
MGMTD_BE_CLIENT_DBG("Received get-tree request for client %s txn-id %" PRIu64
" req-id %" PRIu64,
client->name, txn_id, get_tree_msg->req_id);
/* NOTE: removed the translator, if put back merge with northbound_cli
* code
*/
args = XMALLOC(MTYPE_MGMTD_BE_GT_CB_ARGS, sizeof(*args));
args->client = client;
args->txn_id = get_tree_msg->refer_id;
args->req_id = get_tree_msg->req_id;
args->result_type = get_tree_msg->result_type;
nb_oper_walk(get_tree_msg->xpath, NULL, 0, true, NULL, NULL,
be_client_send_tree_data_batch, args);
}
/*
* Handle a native encoded message
*
* We don't create transactions with native messaging.
*/
static void be_client_handle_native_msg(struct mgmt_be_client *client,
struct mgmt_msg_header *msg,
size_t msg_len)
{
uint64_t txn_id = msg->refer_id;
switch (msg->code) {
case MGMT_MSG_CODE_GET_TREE:
be_client_handle_get_tree(client, txn_id, msg, msg_len);
break;
default:
MGMTD_BE_CLIENT_ERR("unknown native message txn-id %" PRIu64
" req-id %" PRIu64 " code %u to client %s",
txn_id, msg->req_id, msg->code,
client->name);
be_client_send_error(client, msg->refer_id, msg->req_id, false, -1,
"BE cilent %s recv msg unknown txn-id %" PRIu64,
client->name, txn_id);
break;
}
}
static void mgmt_be_client_process_msg(uint8_t version, uint8_t *data,
size_t len, struct msg_conn *conn)
{
@ -742,6 +894,17 @@ static void mgmt_be_client_process_msg(uint8_t version, uint8_t *data,
client = container_of(conn, struct msg_client, conn);
client_ctx = container_of(client, struct mgmt_be_client, client);
if (version == MGMT_MSG_VERSION_NATIVE) {
struct mgmt_msg_header *msg = (typeof(msg))data;
if (len >= sizeof(*msg))
be_client_handle_native_msg(client_ctx, msg, len);
else
MGMTD_BE_CLIENT_ERR("native message to client %s too short %zu",
client_ctx->name, len);
return;
}
be_msg = mgmtd__be_message__unpack(NULL, len, data);
if (!be_msg) {
MGMTD_BE_CLIENT_DBG("Failed to decode %zu bytes from server",
@ -775,10 +938,9 @@ int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx,
be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ;
be_msg.subscr_req = &subscr_req;
MGMTD_FE_CLIENT_DBG(
"Sending SUBSCR_REQ name: %s subscr_xpaths: %u num_xpaths: %zu",
subscr_req.client_name, subscr_req.subscribe_xpaths,
subscr_req.n_xpath_reg);
MGMTD_BE_CLIENT_DBG("Sending SUBSCR_REQ name: %s subscr_xpaths: %u num_xpaths: %zu",
subscr_req.client_name, subscr_req.subscribe_xpaths,
subscr_req.n_xpath_reg);
return mgmt_be_client_send_msg(client_ctx, &be_msg);
}
@ -922,6 +1084,7 @@ void mgmt_be_client_destroy(struct mgmt_be_client *client)
MGMTD_BE_CLIENT_DBG("Destroying MGMTD Backend Client '%s'",
client->name);
nb_oper_cancel_all_walks();
msg_client_cleanup(&client->client);
mgmt_be_cleanup_all_txns(client);
mgmt_be_txns_fini(&client->txn_head);

View File

@ -12,6 +12,7 @@
#include "libfrr.h"
#include "mgmt_fe_client.h"
#include "mgmt_msg.h"
#include "mgmt_msg_native.h"
#include "mgmt_pb.h"
#include "network.h"
#include "stream.h"
@ -304,6 +305,35 @@ int mgmt_fe_send_regnotify_req(struct mgmt_fe_client *client,
return mgmt_fe_client_send_msg(client, &fe_msg, false);
}
/*
* Send get-tree request.
*/
int mgmt_fe_send_get_tree_req(struct mgmt_fe_client *client,
uint64_t session_id, uint64_t req_id,
LYD_FORMAT result_type, const char *xpath)
{
struct mgmt_msg_get_tree *msg;
size_t xplen = strlen(xpath);
int ret;
msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_get_tree, xplen + 1,
MTYPE_MSG_NATIVE_GET_TREE);
msg->refer_id = session_id;
msg->req_id = req_id;
msg->code = MGMT_MSG_CODE_GET_TREE;
msg->result_type = result_type;
strlcpy(msg->xpath, xpath, xplen + 1);
MGMTD_FE_CLIENT_DBG("Sending GET_TREE_REQ session-id %" PRIu64
" req-id %" PRIu64 " xpath: %s",
session_id, req_id, xpath);
ret = mgmt_msg_native_send_msg(&client->client.conn, msg, false);
mgmt_msg_native_free_msg(msg);
return ret;
}
static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client,
Mgmtd__FeMessage *fe_msg)
{
@ -469,6 +499,73 @@ static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client,
return 0;
}
/*
* Handle a native encoded message
*/
static void fe_client_handle_native_msg(struct mgmt_fe_client *client,
struct mgmt_msg_header *msg,
size_t msg_len)
{
struct mgmt_fe_client_session *session;
struct mgmt_msg_tree_data *tree_msg;
struct mgmt_msg_error *err_msg;
MGMTD_FE_CLIENT_DBG("Got GET_TREE reply for session-id %" PRIu64,
msg->refer_id);
session = mgmt_fe_find_session_by_session_id(client, msg->refer_id);
if (!session || !session->client) {
MGMTD_FE_CLIENT_ERR("No session for received native msg session-id %" PRIu64,
msg->refer_id);
return;
}
switch (msg->code) {
case MGMT_MSG_CODE_ERROR:
if (!session->client->cbs.error_notify)
return;
err_msg = (typeof(err_msg))msg;
if (!MGMT_MSG_VALIDATE_NUL_TERM(err_msg, msg_len)) {
MGMTD_FE_CLIENT_ERR("Corrupt error msg recv");
return;
}
session->client->cbs.error_notify(client, client->user_data,
session->client_id,
msg->refer_id,
session->user_ctx,
msg->req_id, err_msg->error,
err_msg->errstr);
break;
case MGMT_MSG_CODE_TREE_DATA:
if (!session->client->cbs.get_tree_notify)
return;
tree_msg = (typeof(tree_msg))msg;
if (msg_len < sizeof(*tree_msg)) {
MGMTD_FE_CLIENT_ERR("Corrupt tree-data msg recv");
return;
}
session->client->cbs.get_tree_notify(client, client->user_data,
session->client_id,
msg->refer_id,
session->user_ctx,
msg->req_id,
MGMTD_DS_OPERATIONAL,
tree_msg->result_type,
tree_msg->result,
msg_len - sizeof(*tree_msg),
tree_msg->partial_error);
break;
default:
MGMTD_FE_CLIENT_ERR("unknown native message session-id %" PRIu64
" req-id %" PRIu64 " code %u",
msg->refer_id, msg->req_id, msg->code);
break;
}
}
static void mgmt_fe_client_process_msg(uint8_t version, uint8_t *data,
size_t len, struct msg_conn *conn)
{
@ -479,6 +576,17 @@ static void mgmt_fe_client_process_msg(uint8_t version, uint8_t *data,
msg_client = container_of(conn, struct msg_client, conn);
client = container_of(msg_client, struct mgmt_fe_client, client);
if (version == MGMT_MSG_VERSION_NATIVE) {
struct mgmt_msg_header *msg = (typeof(msg))data;
if (len >= sizeof(*msg))
fe_client_handle_native_msg(client, msg, len);
else
MGMTD_FE_CLIENT_ERR("native message to FE client %s too short %zu",
client->name, len);
return;
}
fe_msg = mgmtd__fe_message__unpack(NULL, len, data);
if (!fe_msg) {
MGMTD_FE_CLIENT_DBG("Failed to decode %zu bytes from server.",
@ -647,6 +755,11 @@ bool mgmt_fe_client_current_msg_short_circuit(struct mgmt_fe_client *client)
return client->client.conn.is_short_circuit;
}
const char *mgmt_fe_client_name(struct mgmt_fe_client *client)
{
return client->name;
}
/*
* Create a new Session for a Frontend Client connection.
*/

View File

@ -115,6 +115,20 @@ struct mgmt_fe_client_cbs {
uintptr_t user_data, uint64_t req_id,
Mgmtd__DatastoreId ds_id,
Mgmtd__YangData **yang_data, size_t num_data);
/* Called when get-tree result is returned */
int (*get_tree_notify)(struct mgmt_fe_client *client,
uintptr_t user_data, uint64_t client_id,
uint64_t session_id, uintptr_t session_ctx,
uint64_t req_id, Mgmtd__DatastoreId ds_id,
LYD_FORMAT result_type, void *result, size_t len,
int partial_error);
/* Called when new native error is returned */
int (*error_notify)(struct mgmt_fe_client *client, uintptr_t user_data,
uint64_t client_id, uint64_t session_id,
uintptr_t session_ctx, uint64_t req_id, int error,
const char *errstr);
};
extern struct debug mgmt_dbg_fe_client;
@ -363,6 +377,31 @@ extern int mgmt_fe_send_regnotify_req(struct mgmt_fe_client *client,
Mgmtd__YangDataXPath **data_req,
int num_reqs);
/*
* Send GET-TREE to MGMTD daemon.
*
* client
* Client object.
*
* session_id
* Client session ID.
*
* req_id
* Client request ID.
*
* result_type
* The LYD_FORMAT of the result.
*
* xpath
* the xpath to get.
*
* Returns:
* 0 on success, otherwise msg_conn_send_msg() return values.
*/
extern int mgmt_fe_send_get_tree_req(struct mgmt_fe_client *client,
uint64_t session_id, uint64_t req_id,
LYD_FORMAT result_type, const char *xpath);
/*
* Destroy library and cleanup everything.
*/
@ -379,6 +418,17 @@ extern uint mgmt_fe_client_session_count(struct mgmt_fe_client *client);
extern bool
mgmt_fe_client_current_msg_short_circuit(struct mgmt_fe_client *client);
/**
* Get the name of the client
*
* Args:
* The client object.
*
* Return:
* The name of the client.
*/
extern const char *mgmt_fe_client_name(struct mgmt_fe_client *client);
#ifdef __cplusplus
}
#endif

View File

@ -13,6 +13,7 @@
#include "stream.h"
#include "frrevent.h"
#include "mgmt_msg.h"
#include "mgmt_msg_native.h"
#define MGMT_MSG_DBG(dbgtag, fmt, ...) \
@ -84,7 +85,7 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
*/
assert(stream_get_getp(ms->ins) == 0);
left = stream_get_endp(ms->ins);
while (left > (long)sizeof(struct mgmt_msg_hdr)) {
while (left > (ssize_t)sizeof(struct mgmt_msg_hdr)) {
mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(ms->ins) + total);
if (!MGMT_MSG_IS_MARKER(mhdr->marker)) {
MGMT_MSG_DBG(dbgtag, "recv corrupt buffer, disconnect");
@ -99,8 +100,25 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
mcount++;
}
if (!mcount)
if (!mcount) {
/* Didn't manage to read a full message */
if (mhdr && avail == 0) {
struct stream *news;
/*
* Message was longer than what was left and we have no
* available space to read more in. B/c mcount == 0 the
* message starts at the beginning of the stream so
* therefor the stream is too small to fit the message..
* Resize the stream to fit.
*/
news = stream_new(mhdr->len);
stream_put(news, mhdr, left);
stream_set_endp(news, left);
stream_free(ms->ins);
ms->ins = news;
}
return MSR_SCHED_STREAM;
}
/*
* We have read at least one message into the stream, queue it up.
@ -108,7 +126,11 @@ enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd,
mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(ms->ins) + total);
stream_set_endp(ms->ins, total);
stream_fifo_push(&ms->inq, ms->ins);
ms->ins = stream_new(ms->max_msg_sz);
if (left < (ssize_t)sizeof(struct mgmt_msg_hdr))
ms->ins = stream_new(ms->max_msg_sz);
else
/* handle case where message is greater than max */
ms->ins = stream_new(MAX(ms->max_msg_sz, mhdr->len));
if (left) {
stream_put(ms->ins, mhdr, left);
stream_set_endp(ms->ins, left);
@ -292,23 +314,26 @@ int mgmt_msg_send_msg(struct mgmt_msg_state *ms, uint8_t version, void *msg,
size_t endp, n;
size_t mlen = len + sizeof(*mhdr);
if (mlen > ms->max_msg_sz) {
MGMT_MSG_ERR(ms, "Message %zu > max size %zu, dropping", mlen,
ms->max_msg_sz);
return -1;
}
if (mlen > ms->max_msg_sz)
MGMT_MSG_DBG(dbgtag, "Sending large msg size %zu > max size %zu",
mlen, ms->max_msg_sz);
if (!ms->outs) {
MGMT_MSG_DBG(dbgtag, "creating new stream for msg len %zu",
len);
ms->outs = stream_new(ms->max_msg_sz);
MGMT_MSG_DBG(dbgtag, "creating new stream for msg len %zu", mlen);
ms->outs = stream_new(MAX(ms->max_msg_sz, mlen));
} else if (mlen > ms->max_msg_sz && ms->outs->endp == 0) {
/* msg is larger than stream max size get a fit-to-size stream */
MGMT_MSG_DBG(dbgtag,
"replacing old stream with fit-to-size for msg len %zu",
mlen);
stream_free(ms->outs);
ms->outs = stream_new(mlen);
} else if (STREAM_WRITEABLE(ms->outs) < mlen) {
MGMT_MSG_DBG(
dbgtag,
"enq existing stream len %zu and creating new stream for msg len %zu",
STREAM_WRITEABLE(ms->outs), mlen);
MGMT_MSG_DBG(dbgtag,
"enq existing stream len %zu and creating new stream for msg len %zu",
STREAM_WRITEABLE(ms->outs), mlen);
stream_fifo_push(&ms->outq, ms->outs);
ms->outs = stream_new(ms->max_msg_sz);
ms->outs = stream_new(MAX(ms->max_msg_sz, mlen));
} else {
MGMT_MSG_DBG(
dbgtag,
@ -317,6 +342,16 @@ int mgmt_msg_send_msg(struct mgmt_msg_state *ms, uint8_t version, void *msg,
}
s = ms->outs;
if (dbgtag && version == MGMT_MSG_VERSION_NATIVE) {
struct mgmt_msg_header *native_msg = msg;
MGMT_MSG_DBG(
dbgtag,
"Sending native msg sess/txn-id %"PRIu64" req-id %"PRIu64" code %u",
native_msg->refer_id, native_msg->req_id, native_msg->code);
}
/* We have a stream with space, pack the message into it. */
mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(s) + s->endp);
mhdr->marker = MGMT_MSG_MARKER(version);
@ -672,6 +707,9 @@ static int msg_client_connect_short_circuit(struct msg_client *client)
/* server side */
memset(&su, 0, sizeof(union sockunion));
server_conn = server->create(sockets[1], &su);
server_conn->debug = DEBUG_MODE_CHECK(server->debug, DEBUG_MODE_ALL)
? true
: false;
client->conn.remote_conn = server_conn;
server_conn->remote_conn = &client->conn;
@ -765,8 +803,9 @@ void msg_client_cleanup(struct msg_client *client)
static void msg_server_accept(struct event *event)
{
struct msg_server *server = EVENT_ARG(event);
int fd;
struct msg_conn *conn;
union sockunion su;
int fd;
if (server->fd < 0)
return;
@ -789,7 +828,11 @@ static void msg_server_accept(struct event *event)
DEBUGD(server->debug, "Accepted new %s connection", server->idtag);
server->create(fd, &su);
conn = server->create(fd, &su);
if (conn)
conn->debug = DEBUG_MODE_CHECK(server->debug, DEBUG_MODE_ALL)
? true
: false;
}
int msg_server_init(struct msg_server *server, const char *sopath,

47
lib/mgmt_msg_native.c Normal file
View File

@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* June 29 2023, Christian Hopps <chopps@labn.net>
*
* Copyright (c) 2023, LabN Consulting, L.L.C.
*
*/
#include <zebra.h>
#include "mgmt_msg_native.h"
DEFINE_MGROUP(MSG_NATIVE, "Native message allocations");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_MSG, "native mgmt msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_ERROR, "native error msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_GET_TREE, "native get tree msg");
DEFINE_MTYPE(MSG_NATIVE, MSG_NATIVE_TREE_DATA, "native tree data msg");
int vmgmt_msg_native_send_error(struct msg_conn *conn, uint64_t sess_or_txn_id,
uint64_t req_id, bool short_circuit_ok,
int16_t error, const char *errfmt, va_list ap)
{
struct mgmt_msg_error *msg;
char *errstr;
ssize_t slen;
int ret;
errstr = darr_vsprintf(errfmt, ap);
slen = strlen(errstr);
msg = mgmt_msg_native_alloc_msg(typeof(*msg), slen + 1,
MTYPE_MSG_NATIVE_ERROR);
msg->refer_id = sess_or_txn_id;
msg->req_id = req_id;
msg->code = MGMT_MSG_CODE_ERROR;
msg->error = error;
strlcpy(msg->errstr, errstr, slen + 1);
darr_free(errstr);
if (conn->debug)
zlog_debug("Sending error %d session-id %" PRIu64
" req-id %" PRIu64 " scok %d errstr: %s",
error, sess_or_txn_id, req_id, short_circuit_ok,
msg->errstr);
ret = mgmt_msg_native_send_msg(conn, msg, short_circuit_ok);
mgmt_msg_native_free_msg(msg);
return ret;
}

380
lib/mgmt_msg_native.h Normal file
View File

@ -0,0 +1,380 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* June 29 2023, Christian Hopps <chopps@labn.net>
*
* Copyright (c) 2023, LabN Consulting, L.L.C.
*
*/
#ifndef _FRR_MGMT_MSG_NATIVE_H_
#define _FRR_MGMT_MSG_NATIVE_H_
#ifdef __cplusplus
extern "C" {
#elif 0
}
#endif
#include <zebra.h>
#include "compiler.h"
#include "darr.h"
#include "memory.h"
#include "mgmt_msg.h"
#include "mgmt_defines.h"
#include <stdalign.h>
/*
* ==================
* Native Message API
* ==================
*
* -----------------------
* Defining A New Message:
* -----------------------
*
* 1) Start with `struct mgmt_msg_header` as the first (unnamed) field.
*
* 2) Add fixed-width fields. Add on natural aligned boundaries (*)
*
* 3) [Optional] Add a zero-length variable field. Add aligned on a 64-bit
* boundary, this is done so that: `value = (HDR + 1)` works.
*
* 4) Define a new MTYPE for the new message type (see DECLARE_MTYPE below
* as well as the paired DEFINE_MTYPE in mgmt_msg_native.c)
*
* These rules are so the messages may be read from and written directly to
* "the wire", easily, using common programming languages (e.g., C, rust, go,
* python, ...)
*
* (*) Natrual aligned boundaries, i.e., uint16_t on 2-byte boundary, uint64_t
* on 8-byte boundaries, ...)
*
* ------------------------------
* Allocating New Native Messages
* ------------------------------
*
* For fixed-length and variable length messages one should allocate new
* messages with the mgmt_msg_native_alloc_msg() passing in the newly defined
* MTYPE. Likewise, to free the message one should use
* mgmt_msg_native_free_msg().
*
* Unknown Variable Length Messages:
* ---------------------------------
*
* If using a zero-length variable length field and the length is not known at
* message creation time, you can use the `native` API function
* mgmt_msg_native_append() to add data to the end of the message, or if a more
* full set of operations are required, the darr_xxxx() API is also available as
* in the Advanced section below.
*
* Notable API Functions:
* ---------------------------------
*
* mgmt_msg_native_alloc_msg() - Allocate a native msg.
* mgmt_msg_native_free_msg() - Free a native msg.
* mgmt_msg_native_append() - Append data to the end of the msg.
* mgmt_msg_native_get_msg_len() - Get the total length of the msg.
* mgmt_msg_native_send_msg() - Send the message.
*
*
* -------------------------------------
* [Advanced Use] Dynamic Array Messages
* -------------------------------------
*
* NOTE: Most users can simply use mgmt_msg_native_append() and skip this
* section.
*
* This section is only important to understand if you wish to utilize the fact
* that native messages allocated with mgmt_msg_native_alloc_msg are
* actually allocated as uint8_t dynamic arrays (`darr`).
*
* You can utilize all the darr_xxxx() API to manipulate the variable length
* message data in a native message. To do so you simply need to understand that
* the native message is actually a `uint8_t *` darr. So, for example, to append
* data to the end of a message one could do the following:
*
* void append_metric_path(struct mgmt_msg_my_msg *msg)
* {
* msg = (struct mggm_msg_my_msg *)
* darr_strcat((uint8_t *)msg, "/metric");
*
* // ...
* }
*
* NOTE: If reallocs happen the original passed in pointer will be updated;
* however, any other pointers into the message will become invalid, and so they
* should always be discarded or reinitialized after using any reallocating
* darr_xxx() API functions.
*
* void append_metric_path(struct mgmt_msg_my_msg *msg)
* {
* char *xpath = msg->xpath; // pointer into message
*
* darr_in_strcat((uint8_t *)msg, "/metric");
* // msg may have been updated to point at new memory
*
* xpath = NULL; // now invalid
* xpath = msg->xpath; // reinitialize
* // ...
* }
*
* Rather than worry about this, it's typical when using dynamic arrays to always
* work from the main pointer to the dynamic array, rather than caching multiple
* pointers into the data. Modern compilers will optimize the code so that it
* adds no extra execution cost.
*
* void append_metric_path(struct mgmt_msg_my_msg *msg)
* {
* darr_in_strcat((uint8_t *)msg, "/metric");
*
* // Use `msg->xpath` directly rather creating and using an
* // `xpath = msg->xpath` local variable.
*
* if (strcmp(msg->xpath, "foobar/metric")) {
* // ...
* }
* }
*
*/
DECLARE_MTYPE(MSG_NATIVE_MSG);
DECLARE_MTYPE(MSG_NATIVE_ERROR);
DECLARE_MTYPE(MSG_NATIVE_GET_TREE);
DECLARE_MTYPE(MSG_NATIVE_TREE_DATA);
/*
* Native message codes
*/
#define MGMT_MSG_CODE_ERROR 0
#define MGMT_MSG_CODE_GET_TREE 1
#define MGMT_MSG_CODE_TREE_DATA 2
/**
* struct mgmt_msg_header - Header common to all native messages.
*
* @code: the actual type of the message.
* @resv: Set to zero, ignore on receive.
* @vsplit: If a variable section is split in 2, the length of first part.
* @refer_id: the session, txn, conn, etc, this message is associated with.
* @req_id: the request this message is for.
*/
struct mgmt_msg_header {
uint16_t code;
uint16_t resv;
uint32_t vsplit;
uint64_t refer_id;
uint64_t req_id;
};
_Static_assert(sizeof(struct mgmt_msg_header) == 3 * 8, "Bad padding");
_Static_assert(sizeof(struct mgmt_msg_header) ==
offsetof(struct mgmt_msg_header, req_id) +
sizeof(((struct mgmt_msg_header *)0)->req_id),
"Size mismatch");
/**
* struct mgmt_msg_error - Common error message.
*
* @error: An error value.
* @errst: Description of error can be 0 length.
*
* This common error message can be used for replies for many msg requests
* (req_id).
*/
struct mgmt_msg_error {
struct mgmt_msg_header;
int16_t error;
uint8_t resv2[6];
alignas(8) char errstr[];
};
_Static_assert(sizeof(struct mgmt_msg_error) ==
offsetof(struct mgmt_msg_error, errstr),
"Size mismatch");
/**
* struct mgmt_msg_get_tree - Message carrying xpath query request.
*
* @result_type: ``LYD_FORMAT`` for the returned result.
* @xpath: the query for the data to return.
*/
struct mgmt_msg_get_tree {
struct mgmt_msg_header;
uint8_t result_type;
uint8_t resv2[7];
alignas(8) char xpath[];
};
_Static_assert(sizeof(struct mgmt_msg_get_tree) ==
offsetof(struct mgmt_msg_get_tree, xpath),
"Size mismatch");
/**
* struct mgmt_msg_tree_data - Message carrying tree data.
*
* @partial_error: If the full result could not be returned do to this error.
* @result_type: ``LYD_FORMAT`` for format of the @result value.
* @more: if this is a partial return and there will be more coming.
* @result: The tree data in @result_type format.
*
*/
struct mgmt_msg_tree_data {
struct mgmt_msg_header;
int8_t partial_error;
uint8_t result_type;
uint8_t more;
uint8_t resv2[5];
alignas(8) uint8_t result[];
};
_Static_assert(sizeof(struct mgmt_msg_tree_data) ==
offsetof(struct mgmt_msg_tree_data, result),
"Size mismatch");
#define MGMT_MSG_VALIDATE_NUL_TERM(msgp, len) \
((len) >= sizeof(*msg) + 1 && ((char *)msgp)[(len)-1] == 0)
/**
* Send a native message error to the other end of the connection.
*
* This function is normally used by the server-side to indicate a failure to
* process a client request. For this server side handling of client messages
* which expect a reply, either that reply or this error should be returned, as
* closing the connection is not allowed during message handling.
*
* Args:
* conn: the connection.
* sess_or_txn_id: Session ID (to FE client) or Txn ID (from BE client)
* req_id: which req_id this error is associated with.
* short_circuit_ok: if short circuit sending is OK.
* error: the error value
* errfmt: vprintfrr style format string
* ap: the variable args for errfmt.
*
* Return:
* The return value of ``msg_conn_send_msg``.
*/
extern int vmgmt_msg_native_send_error(struct msg_conn *conn,
uint64_t sess_or_txn_id, uint64_t req_id,
bool short_circuit_ok, int16_t error,
const char *errfmt, va_list ap)
PRINTFRR(6, 0);
/**
* mgmt_msg_native_alloc_msg() - Create a native appendable msg.
* @msg_type: The message structure type.
* @var_len: The initial additional length to add to the message.
* @mem_type: The initial additional length to add to the message.
*
* This function takes a C type (e.g., `struct mgmt_msg_get_tree`) as an
* argument and returns a new native message. The newly allocated message
* can be used with the other `native` functions.
*
* Importantly the mgmt_msg_native_append() function can be used to add data
* to the end of the message, and mgmt_msg_get_native_msg_len() can be used
* to obtain the total length of the message (i.e., the fixed sized header plus
* the variable length data that has been appended).
*
* Additionally, a dynamic array (darr) pointer can be obtained using
* mgmt_msg_get_native_darr() which allows adding and manipulating the
* variable data that follows the fixed sized header.
*
* Return: A `msg_type` object created using a dynamic_array.
*/
#define mgmt_msg_native_alloc_msg(msg_type, var_len, mem_type) \
({ \
uint8_t *buf = NULL; \
(msg_type *)darr_append_nz_mt(buf, \
sizeof(msg_type) + (var_len), \
mem_type); \
})
/**
* mgmt_msg_free_native_msg() - Free a native msg.
* @msg - pointer to message allocated by mgmt_msg_create_native_msg().
*/
#define mgmt_msg_native_free_msg(msg) darr_free(msg)
/**
* mgmt_msg_native_get_msg_len() - Get the total length of the msg.
* @msg: the native message.
*
* Return: the total length of the message, fixed + variable length.
*/
#define mgmt_msg_native_get_msg_len(msg) (darr_len((uint8_t *)(msg)))
/**
* mgmt_msg_native_append() - Append data to the end of the msg.
* @msg: (IN/OUT) Pointer to the native message, variable may be updated.
* @data: data to append.
* @len: length of data to append.
*
* Append @data of length @len to the native message @msg.
*
* NOTE: Be aware @msg pointer may change as a result of reallocating the
* message to fit the new data. Any other pointers into the old message should
* be discarded.
*
* Return: a pointer to the newly appended data.
*/
#define mgmt_msg_native_append(msg, data, len) \
memcpy(darr_append(*mgmt_msg_native_get_darrp(msg), len), data, len)
/**
* mgmt_msg_native_send_msg(msg, short_circuit_ok) - Send a native msg.
* @conn: the mgmt_msg connection.
* @msg: the native message.
* @short_circuit_ok: True if short-circuit sending is required.
*
* Return: The error return value of msg_conn_send_msg().
*/
#define mgmt_msg_native_send_msg(conn, msg, short_circuit_ok) \
msg_conn_send_msg(conn, MGMT_MSG_VERSION_NATIVE, msg, \
mgmt_msg_native_get_msg_len(msg), NULL, \
short_circuit_ok)
/**
* mgmt_msg_native_get_darrp() - Return a ptr to the dynamic array ptr.
* @msg: Pointer to the native message.
*
* NOTE: Most users can simply use mgmt_msg_native_append() instead of this.
*
* This function obtains a pointer to the dynamic byte array for this message,
* this array actually includes the message header if one is going to look at
* the length value. With that in mind any of the `darr_*()` functions/API may
* be used to manipulate the variable data at the end of the message.
*
* NOTE: The pointer returned is actually a pointer to the message pointer
* passed in to this function. This pointer to pointer is required so that
* realloc can be done inside the darr API.
*
* NOTE: If reallocs happen the original passed in pointer will be updated;
* however, any other pointers into the message will become invalid and so they
* should always be discarded after using the returned value.
*
* Example:
*
* void append_metric_path(struct mgmt_msg_my_msg *msg)
* {
* char *xpath = msg->xpath; // pointer into message
* uint8_t **darp;
*
* darrp = mgmt_msg_native_get_darrp(msg);
* darr_in_strcat(*darrp, "/metric");
*
* xpath = NULL; // now invalid
* xpath = msg->xpath;
* }
*
*
* Return: A pointer to the first argument -- which is a pointer to a pointer to
* a dynamic array.
*/
#define mgmt_msg_native_get_darrp(msg) ((uint8_t **)&(msg))
#ifdef __cplusplus
}
#endif
#endif /* _FRR_MGMT_MSG_NATIVE_H_ */

View File

@ -73,9 +73,9 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction,
static int nb_oper_data_iter_node(const struct lysc_node *snode,
const char *xpath, const void *list_entry,
const struct yang_list_keys *list_keys,
struct yang_translator *translator,
bool first, uint32_t flags,
nb_oper_data_cb cb, void *arg);
struct yang_translator *translator, bool first,
uint32_t flags, nb_oper_data_cb cb, void *arg,
struct lyd_node *pdnode);
static int nb_node_check_config_only(const struct lysc_node *snode, void *arg)
{
@ -1465,6 +1465,50 @@ const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
return nb_node->cbs.lookup_entry(&args);
}
const void *nb_callback_lookup_node_entry(struct lyd_node *node,
const void *parent_list_entry)
{
struct yang_list_keys keys;
struct nb_cb_lookup_entry_args args = {};
const struct nb_node *nb_node = node->schema->priv;
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
return NULL;
if (yang_get_node_keys(node, &keys)) {
flog_warn(EC_LIB_LIBYANG,
"%s: can't get keys for lookup from existing data node %s",
__func__, node->schema->name);
return NULL;
}
DEBUGD(&nb_dbg_cbs_state,
"northbound callback (lookup_node_entry): node [%s] parent_list_entry [%p]",
nb_node->xpath, parent_list_entry);
args.parent_list_entry = parent_list_entry;
args.keys = &keys;
return nb_node->cbs.lookup_entry(&args);
}
const void *nb_callback_lookup_next(const struct nb_node *nb_node,
const void *parent_list_entry,
const struct yang_list_keys *keys)
{
struct nb_cb_lookup_entry_args args = {};
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS))
return NULL;
DEBUGD(&nb_dbg_cbs_state,
"northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
nb_node->xpath, parent_list_entry);
args.parent_list_entry = parent_list_entry;
args.keys = keys;
return nb_node->cbs.lookup_next(&args);
}
int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
const struct list *input, struct list *output, char *errmsg,
size_t errmsg_len)
@ -1772,7 +1816,8 @@ static int nb_oper_data_iter_children(const struct lysc_node *snode,
const struct yang_list_keys *list_keys,
struct yang_translator *translator,
bool first, uint32_t flags,
nb_oper_data_cb cb, void *arg)
nb_oper_data_cb cb, void *arg,
struct lyd_node *pdnode)
{
const struct lysc_node *child;
@ -1781,7 +1826,7 @@ static int nb_oper_data_iter_children(const struct lysc_node *snode,
ret = nb_oper_data_iter_node(child, xpath, list_entry,
list_keys, translator, false,
flags, cb, arg);
flags, cb, arg, pdnode);
if (ret != NB_OK)
return ret;
}
@ -1793,15 +1838,19 @@ static int nb_oper_data_iter_leaf(const struct nb_node *nb_node,
const char *xpath, const void *list_entry,
const struct yang_list_keys *list_keys,
struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb, void *arg)
uint32_t flags, nb_oper_data_cb cb, void *arg,
struct lyd_node *pdnode)
{
const struct lysc_node *snode = nb_node->snode;
struct yang_data *data;
LY_ERR err = LY_SUCCESS;
if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return NB_OK;
/* Ignore list keys. */
if (lysc_is_key(nb_node->snode))
if (lysc_is_key(snode))
return NB_OK;
data = nb_callback_get_elem(nb_node, xpath, list_entry);
@ -1809,50 +1858,89 @@ static int nb_oper_data_iter_leaf(const struct nb_node *nb_node,
/* Leaf of type "empty" is not present. */
return NB_OK;
return (*cb)(nb_node->snode, translator, data, arg);
/*
* Add a dnode to our tree
*/
err = lyd_new_term(pdnode, snode->module, snode->name, data->value,
false, NULL);
if (err)
return NB_ERR_RESOURCE;
if (cb)
return (*cb)(nb_node->snode, translator, data, arg);
return NB_OK;
}
static int nb_oper_data_iter_container(const struct nb_node *nb_node,
const char *xpath,
const char *xpath, bool first,
const void *list_entry,
const struct yang_list_keys *list_keys,
struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb,
void *arg)
void *arg, struct lyd_node *pdnode)
{
const struct lysc_node *snode = nb_node->snode;
struct lyd_node *cnode = NULL;
bool presence = false;
LY_ERR err;
int ret;
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
return NB_OK;
if (pdnode->schema == snode)
assert(first);
else
assert(!first);
/* Read-only presence containers. */
if (nb_node->cbs.get_elem) {
struct yang_data *data;
int ret;
presence = true;
data = nb_callback_get_elem(nb_node, xpath, list_entry);
if (data == NULL)
/* Presence container is not present. */
return NB_OK;
ret = (*cb)(snode, translator, data, arg);
if (ret != NB_OK)
return ret;
if (!first) {
err = lyd_new_inner(pdnode, snode->module, snode->name,
false, &cnode);
if (err)
return NB_ERR_RESOURCE;
}
if (cb) {
ret = (*cb)(snode, translator, data, arg);
if (ret != NB_OK)
return ret;
}
}
/* Read-write presence containers. */
if (CHECK_FLAG(snode->flags, LYS_CONFIG_W)) {
struct lysc_node_container *scontainer;
scontainer = (struct lysc_node_container *)snode;
if (CHECK_FLAG(scontainer->flags, LYS_PRESENCE)
&& !yang_dnode_get(running_config->dnode, xpath))
return NB_OK;
if (first)
cnode = pdnode;
else if (!cnode) {
/* Add a node in for this container in-case we have children. */
err = lyd_new_inner(pdnode, snode->module, snode->name, false,
&cnode);
if (err)
return NB_ERR_RESOURCE;
}
/* Iterate over the child nodes. */
return nb_oper_data_iter_children(snode, xpath, list_entry, list_keys,
translator, false, flags, cb, arg);
ret = nb_oper_data_iter_children(snode, xpath, list_entry, list_keys,
translator, false, flags, cb, arg,
cnode);
/* TODO: here we are freeing only if we created; however, we may want to
* also free if pdnode was cnode on entry to cleanup the data tree
*/
/* If we aren't presence container and we gained no children remove */
if (!presence && !first && !lyd_child(cnode))
lyd_free_tree(cnode);
return ret;
}
static int
@ -1860,11 +1948,14 @@ nb_oper_data_iter_leaflist(const struct nb_node *nb_node, const char *xpath,
const void *parent_list_entry,
const struct yang_list_keys *parent_list_keys,
struct yang_translator *translator, uint32_t flags,
nb_oper_data_cb cb, void *arg)
nb_oper_data_cb cb, void *arg,
struct lyd_node *pdnode)
{
const struct lysc_node *snode = nb_node->snode;
const void *list_entry = NULL;
LY_ERR err;
if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return NB_OK;
do {
@ -1881,9 +1972,19 @@ nb_oper_data_iter_leaflist(const struct nb_node *nb_node, const char *xpath,
if (data == NULL)
continue;
ret = (*cb)(nb_node->snode, translator, data, arg);
if (ret != NB_OK)
return ret;
/*
* Add a dnode to our tree
*/
err = lyd_new_term(pdnode, snode->module, snode->name,
data->value, false, NULL);
if (err)
return NB_ERR_RESOURCE;
if (cb) {
ret = (*cb)(nb_node->snode, translator, data, arg);
if (ret != NB_OK)
return ret;
}
} while (list_entry);
return NB_OK;
@ -1894,21 +1995,24 @@ static int nb_oper_data_iter_list(const struct nb_node *nb_node,
const void *parent_list_entry,
const struct yang_list_keys *parent_list_keys,
struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb, void *arg)
uint32_t flags, nb_oper_data_cb cb, void *arg,
struct lyd_node *pdnode)
{
char xpath[XPATH_MAXLEN * 2];
const struct lysc_node *snode = nb_node->snode;
const void *list_entry = NULL;
struct lyd_node *list_node = NULL;
const char *key_preds = NULL;
uint32_t position = 1;
LY_ERR err;
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
return NB_OK;
/* Iterate over all list entries. */
do {
const struct lysc_node_leaf *skey;
struct yang_list_keys list_keys = {};
char xpath[XPATH_MAXLEN * 2];
int ret;
int len, ret;
/* Obtain list entry. */
list_entry = nb_callback_get_next(nb_node, parent_list_entry,
@ -1930,16 +2034,14 @@ static int nb_oper_data_iter_list(const struct nb_node *nb_node,
/* Build XPath of the list entry. */
strlcpy(xpath, xpath_list, sizeof(xpath));
unsigned int i = 0;
LY_FOR_KEYS (snode, skey) {
assert(i < list_keys.num);
snprintf(xpath + strlen(xpath),
sizeof(xpath) - strlen(xpath),
"[%s='%s']", skey->name,
list_keys.key[i]);
i++;
}
assert(i == list_keys.num);
len = strlen(xpath);
key_preds = &xpath[len];
uint n = yang_get_key_preds(xpath + len, snode,
&list_keys,
sizeof(xpath) - len);
assert(n == list_keys.num);
} else {
/*
* Keyless list - build XPath using a positional index.
@ -1949,10 +2051,20 @@ static int nb_oper_data_iter_list(const struct nb_node *nb_node,
position++;
}
/*
* `pdnode` needs to point at lib - and it does for
* "/frr-vrf:lib/vrf" need to test "/frr-vrf:lib" too though
*/
err = lyd_new_list2(pdnode, snode->module, snode->name,
key_preds, false, &list_node);
if (err)
return NB_ERR_RESOURCE;
/* Iterate over the child nodes. */
ret = nb_oper_data_iter_children(
nb_node->snode, xpath, list_entry, &list_keys,
translator, false, flags, cb, arg);
ret = nb_oper_data_iter_children(nb_node->snode, xpath,
list_entry, &list_keys,
translator, false, flags, cb,
arg, list_node);
if (ret != NB_OK)
return ret;
} while (list_entry);
@ -1960,13 +2072,12 @@ static int nb_oper_data_iter_list(const struct nb_node *nb_node,
return NB_OK;
}
static int nb_oper_data_iter_node(const struct lysc_node *snode,
const char *xpath_parent,
const void *list_entry,
const struct yang_list_keys *list_keys,
struct yang_translator *translator,
bool first, uint32_t flags,
nb_oper_data_cb cb, void *arg)
int nb_oper_data_iter_node(const struct lysc_node *snode,
const char *xpath_parent, const void *list_entry,
const struct yang_list_keys *list_keys,
struct yang_translator *translator, bool first,
uint32_t flags, nb_oper_data_cb cb, void *arg,
struct lyd_node *pdnode)
{
struct nb_node *nb_node;
char xpath[XPATH_MAXLEN];
@ -1976,6 +2087,10 @@ static int nb_oper_data_iter_node(const struct lysc_node *snode,
&& CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST))
return NB_OK;
/*
* would be nice to just be building a libyang data tree here as well
*/
/* Update XPath. */
strlcpy(xpath, xpath_parent, sizeof(xpath));
if (!first && snode->nodetype != LYS_USES) {
@ -2001,29 +2116,36 @@ static int nb_oper_data_iter_node(const struct lysc_node *snode,
nb_node = snode->priv;
switch (snode->nodetype) {
case LYS_CONTAINER:
ret = nb_oper_data_iter_container(nb_node, xpath, list_entry,
list_keys, translator, flags,
cb, arg);
/* does something, then walks children */
ret = nb_oper_data_iter_container(nb_node, xpath, first,
list_entry, list_keys,
translator, flags, cb, arg,
pdnode);
break;
case LYS_LEAF:
/* does something then returns */
ret = nb_oper_data_iter_leaf(nb_node, xpath, list_entry,
list_keys, translator, flags, cb,
arg);
arg, pdnode);
break;
case LYS_LEAFLIST:
/* walks leaf list doing things and returns */
ret = nb_oper_data_iter_leaflist(nb_node, xpath, list_entry,
list_keys, translator, flags,
cb, arg);
cb, arg, pdnode);
break;
case LYS_LIST:
/* walks children */
ret = nb_oper_data_iter_list(nb_node, xpath, list_entry,
list_keys, translator, flags, cb,
arg);
arg, pdnode);
break;
case LYS_USES:
/* walks children */
ret = nb_oper_data_iter_children(snode, xpath, list_entry,
list_keys, translator, false,
flags, cb, arg);
flags, cb, arg, pdnode);
break;
default:
break;
@ -2032,8 +2154,64 @@ static int nb_oper_data_iter_node(const struct lysc_node *snode,
return ret;
}
static int nb_xpath_dirname(char *xpath)
{
int len = strlen(xpath);
bool abs = xpath[0] == '/';
char *slash;
/* "//" or "/" => NULL */
if (abs && (len == 1 || (len == 2 && xpath[1] == '/')))
return NB_ERR_NOT_FOUND;
slash = (char *)frrstr_back_to_char(xpath, '/');
/* "/foo/bar/" or "/foo/bar//" => "/foo " */
if (slash && slash == &xpath[len - 1]) {
xpath[--len] = 0;
slash = (char *)frrstr_back_to_char(xpath, '/');
if (slash && slash == &xpath[len - 1]) {
xpath[--len] = 0;
slash = (char *)frrstr_back_to_char(xpath, '/');
}
}
if (!slash)
return NB_ERR_NOT_FOUND;
*slash = 0;
return NB_OK;
}
static int nb_oper_data_xpath_to_tree(const char *xpath_in,
struct lyd_node **dnode,
bool is_top_node_list)
{
/* Eventually this function will loop until it finds a concrete path */
char *xpath;
LY_ERR err;
int ret;
err = lyd_new_path2(NULL, ly_native_ctx, xpath_in, NULL, 0, 0,
LYD_NEW_PATH_UPDATE, NULL, dnode);
if (err == LY_SUCCESS)
return NB_OK;
if (!is_top_node_list)
return NB_ERR_NOT_FOUND;
xpath = XSTRDUP(MTYPE_TMP, xpath_in);
ret = nb_xpath_dirname(xpath);
if (ret != NB_OK)
goto done;
err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0,
LYD_NEW_PATH_UPDATE, NULL, dnode);
if (err != LY_SUCCESS)
ret = NB_ERR_NOT_FOUND;
done:
XFREE(MTYPE_TMP, xpath);
return ret;
}
int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb, void *arg)
uint32_t flags, nb_oper_data_cb cb, void *arg,
struct lyd_node **tree)
{
struct nb_node *nb_node;
const void *list_entry = NULL;
@ -2064,25 +2242,24 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
* all YANG lists (if any).
*/
LY_ERR err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0,
LYD_NEW_PATH_UPDATE, NULL, &dnode);
if (err || !dnode) {
const char *errmsg =
err ? ly_errmsg(ly_native_ctx) : "node not found";
flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed %s",
__func__, errmsg);
return NB_ERR;
ret = nb_oper_data_xpath_to_tree(xpath, &dnode,
nb_node->snode->nodetype == LYS_LIST);
if (ret) {
flog_warn(EC_LIB_LIBYANG,
"%s: can't instantiate concrete path using xpath: %s",
__func__, xpath);
return ret;
}
/*
* Create a linked list to sort the data nodes starting from the root.
*/
list_dnodes = list_new();
for (dn = dnode; dn; dn = lyd_parent(dn)) {
if (dn->schema->nodetype != LYS_LIST || !lyd_child(dn))
continue;
listnode_add_head(list_dnodes, dn);
}
for (dn = dnode; dn; dn = lyd_parent(dn))
if (dn->schema->nodetype == LYS_LIST)
listnode_add_head(list_dnodes, dn);
/*
* Use the northbound callbacks to find list entry pointer corresponding
* to the given XPath.
@ -2104,6 +2281,10 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
}
list_keys.num = n;
if (list_keys.num != yang_snode_num_keys(dn->schema)) {
flog_warn(
EC_LIB_NB_OPERATIONAL_DATA,
"%s: internal list entry '%s' missing required key values predicates in xpath: %s",
__func__, dn->schema->name, xpath);
list_delete(&list_dnodes);
yang_dnode_free(dnode);
return NB_ERR_NOT_FOUND;
@ -2121,6 +2302,11 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
return NB_ERR;
}
/* NOTE: To add support for multiple levels of unspecified keys
* we need to loop here using the list entry's get_next to work
* with each "existing in the data" list entry. It will be a bit
* tricky b/c we are inside a loop here.
*/
list_entry =
nb_callback_lookup_entry(nn, list_entry, &list_keys);
if (list_entry == NULL) {
@ -2130,18 +2316,33 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
}
}
/* If a list entry was given, iterate over that list entry only. */
if (dnode->schema->nodetype == LYS_LIST && lyd_child(dnode))
ret = nb_oper_data_iter_children(
nb_node->snode, xpath, list_entry, &list_keys,
translator, true, flags, cb, arg);
/* If a list entry was given with keys as the last node in the path,
* iterate over that list entry only.
*/
if (dnode->schema->nodetype == LYS_LIST && lyd_child(dnode)
&& dnode->schema == nb_node->snode)
ret = nb_oper_data_iter_children(nb_node->snode, xpath,
list_entry, &list_keys,
translator, true, flags, cb,
arg, dnode);
else
ret = nb_oper_data_iter_node(nb_node->snode, xpath, list_entry,
&list_keys, translator, true,
flags, cb, arg);
flags, cb, arg, dnode);
list_delete(&list_dnodes);
yang_dnode_free(dnode);
if (dnode) {
while (lyd_parent(dnode))
dnode = lyd_parent(dnode);
if (tree && ret == NB_OK)
*tree = dnode;
else {
lyd_free_all(dnode);
if (tree)
*tree = NULL;
}
}
return ret;
}
@ -2544,6 +2745,8 @@ const char *nb_err_name(enum nb_error error)
return "failed to allocate resource";
case NB_ERR_INCONSISTENCY:
return "internal inconsistency";
case NB_YIELD:
return "should yield";
}
assert(!"Reached end of function we should never hit");
@ -2665,10 +2868,15 @@ void nb_init(struct event_loop *tm,
/* Initialize the northbound CLI. */
nb_cli_init(tm);
/* Initialize oper-state */
nb_oper_init(tm);
}
void nb_terminate(void)
{
nb_oper_terminate();
/* Terminate the northbound CLI. */
nb_cli_terminate();

View File

@ -484,6 +484,22 @@ struct nb_callbacks {
*/
const void *(*lookup_entry)(struct nb_cb_lookup_entry_args *args);
/*
* Operational data callback for YANG lists.
*
* The callback function should return the next list entry that would
* follow a list entry with the keys given as a parameter. Keyless
* lists don't need to implement this callback.
*
* args
* Refer to the documentation comments of nb_cb_lookup_entry_args for
* details.
*
* Returns:
* Pointer to the list entry if found, or NULL if not found.
*/
const void *(*lookup_next)(struct nb_cb_lookup_entry_args *args);
/*
* RPC and action callback.
*
@ -644,6 +660,7 @@ enum nb_error {
NB_ERR_VALIDATION,
NB_ERR_RESOURCE,
NB_ERR_INCONSISTENCY,
NB_YIELD,
};
/* Default priority. */
@ -710,6 +727,29 @@ typedef int (*nb_oper_data_cb)(const struct lysc_node *snode,
struct yang_translator *translator,
struct yang_data *data, void *arg);
/**
* nb_oper_data_finish_cb() - finish a portion or all of a oper data walk.
* @tree - r/o copy of the tree created during this portion of the walk.
* @arg - finish arg passed to nb_op_iterate_yielding.
* @ret - NB_OK if done with walk, NB_YIELD if done with portion, otherwise an
* error.
*
* If nb_op_iterate_yielding() was passed with @should_batch set then this
* callback will be invoked during each portion (batch) of the walk.
*
* The @tree is read-only and should not be modified or freed.
*
* If this function returns anything but NB_OK then the walk will be terminated.
* and this function will not be called again regardless of if @ret was
* `NB_YIELD` or not.
*
* Return: NB_OK to continue or complete the walk normally, otherwise an error
* to immediately terminate the walk.
*/
/* Callback function used by nb_oper_data_iter_yielding(). */
typedef enum nb_error (*nb_oper_data_finish_cb)(const struct lyd_node *tree,
void *arg, enum nb_error ret);
/* Iterate over direct child nodes only. */
#define NB_OPER_DATA_ITER_NORECURSE 0x0001
@ -743,6 +783,11 @@ extern int nb_callback_get_keys(const struct nb_node *nb_node,
extern const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
const void *parent_list_entry,
const struct yang_list_keys *keys);
extern const void *nb_callback_lookup_node_entry(struct lyd_node *node,
const void *parent_list_entry);
extern const void *nb_callback_lookup_next(const struct nb_node *nb_node,
const void *parent_list_entry,
const struct yang_list_keys *keys);
extern int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
const struct list *input, struct list *output,
char *errmsg, size_t errmsg_len);
@ -1250,8 +1295,13 @@ extern int nb_running_unlock(enum nb_client client, const void *user);
*/
extern int nb_running_lock_check(enum nb_client client, const void *user);
extern int nb_oper_data_iterate(const char *xpath,
struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb, void *arg,
struct lyd_node **tree);
/*
* Iterate over operational data.
* Iterate over operational data -- deprecated.
*
* xpath
* Data path of the YANG data we want to iterate over.
@ -1262,18 +1312,57 @@ extern int nb_running_lock_check(enum nb_client client, const void *user);
* flags
* NB_OPER_DATA_ITER_ flags to control how the iteration is performed.
*
* should_batch
* Should call finish cb with partial results (i.e., creating batches)
*
* cb
* Function to call with each data node.
*
* arg
* Arbitrary argument passed as the fourth parameter in each call to 'cb'.
*
* tree
* If non-NULL will contain the data tree built from the walk.
*
* Returns:
* NB_OK on success, NB_ERR otherwise.
*/
extern int nb_oper_data_iterate(const char *xpath,
struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb, void *arg);
extern enum nb_error nb_oper_iterate_legacy(const char *xpath,
struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb,
void *arg, struct lyd_node **tree);
/**
* nb_op_walk() - walk the schema building operational state.
* @xpath -
* @translator -
* @flags -
* @should_batch - should allow yielding and processing portions of the tree.
* @cb - callback invoked for each non-list, non-container node.
* @arg - arg to pass to @cb.
* @finish - function to call when done with portion or all of walk.
* @finish_arg - arg to pass to @finish.
*
* Return: walk - a cookie that can be used to cancel the walk.
*/
extern void *nb_oper_walk(const char *xpath, struct yang_translator *translator,
uint32_t flags, bool should_batch, nb_oper_data_cb cb,
void *arg, nb_oper_data_finish_cb finish,
void *finish_arg);
/**
* nb_op_iterate_yielding_cancel() - cancel the in progress walk.
* @walk - value returned from nb_op_iterate_yielding()
*
* Should only be called on an in-progress walk. It is invalid to cancel and
* already finished walk. The walks `finish` callback will not be called.
*/
extern void nb_oper_cancel_walk(void *walk);
/**
* nb_op_cancel_all_walks() - cancel all in progress walks.
*/
extern void nb_oper_cancel_all_walks(void);
/*
* Validate if the northbound operation is valid for the given node.
@ -1481,6 +1570,9 @@ extern void nb_init(struct event_loop *tm,
*/
extern void nb_terminate(void);
extern void nb_oper_init(struct event_loop *loop);
extern void nb_oper_terminate(void);
#ifdef __cplusplus
}
#endif

View File

@ -1437,11 +1437,9 @@ static int nb_cli_oper_data_cb(const struct lysc_node *snode,
}
exit:
yang_data_free(data);
return NB_OK;
error:
yang_data_free(data);
return NB_ERR;
}
@ -1490,9 +1488,14 @@ DEFPY (show_yang_operational_data,
ly_ctx = ly_native_ctx;
/* Obtain data. */
dnode = yang_dnode_new(ly_ctx, false);
ret = nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb,
dnode);
if (translator) {
dnode = yang_dnode_new(ly_ctx, false);
ret = nb_oper_iterate_legacy(xpath, translator, 0,
nb_cli_oper_data_cb, dnode, NULL);
} else {
dnode = NULL;
ret = nb_oper_iterate_legacy(xpath, NULL, 0, NULL, NULL, &dnode);
}
if (ret != NB_OK) {
if (format == LYD_JSON)
vty_out(vty, "{}\n");
@ -1500,7 +1503,8 @@ DEFPY (show_yang_operational_data,
/* embed ly_last_errmsg() when we get newer libyang */
vty_out(vty, "<!-- Not found -->\n");
}
yang_dnode_free(dnode);
if (dnode)
yang_dnode_free(dnode);
return CMD_WARNING;
}

View File

@ -427,25 +427,11 @@ static struct lyd_node *get_dnode_config(const std::string &path)
return dnode;
}
static int get_oper_data_cb(const struct lysc_node *snode,
struct yang_translator *translator,
struct yang_data *data, void *arg)
{
struct lyd_node *dnode = static_cast<struct lyd_node *>(arg);
int ret = yang_dnode_edit(dnode, data->xpath, data->value);
yang_data_free(data);
return (ret == 0) ? NB_OK : NB_ERR;
}
static struct lyd_node *get_dnode_state(const std::string &path)
{
struct lyd_node *dnode = yang_dnode_new(ly_native_ctx, false);
if (nb_oper_data_iterate(path.c_str(), NULL, 0, get_oper_data_cb, dnode)
!= NB_OK) {
yang_dnode_free(dnode);
return NULL;
}
struct lyd_node *dnode = NULL;
(void)nb_oper_iterate_legacy(path.c_str(), NULL, 0, NULL, NULL, &dnode);
return dnode;
}

1769
lib/northbound_oper.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -118,6 +118,9 @@ static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data)
sr_data->type = SR_INT64_T;
sr_data->data.int64_val = yang_str2int64(frr_data->value);
break;
case LY_TYPE_LEAFREF:
sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value);
break;
case LY_TYPE_STRING:
sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value);
break;
@ -137,6 +140,11 @@ static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data)
sr_data->type = SR_UINT64_T;
sr_data->data.uint64_val = yang_str2uint64(frr_data->value);
break;
case LY_TYPE_UNION:
/* No way to deal with this using un-typed yang_data object */
sr_val_set_str_data(sr_data, SR_STRING_T, frr_data->value);
break;
case LY_TYPE_UNKNOWN:
default:
return -1;
}
@ -340,6 +348,8 @@ static int frr_sr_config_change_cb(sr_session_ctx_t *session, uint32_t sub_id,
return frr_sr_config_change_cb_apply(session, module_name);
case SR_EV_ABORT:
return frr_sr_config_change_cb_abort(session, module_name);
case SR_EV_RPC:
case SR_EV_UPDATE:
default:
flog_err(EC_LIB_LIBSYSREPO, "%s: unexpected sysrepo event: %u",
__func__, sr_ev);
@ -347,39 +357,16 @@ static int frr_sr_config_change_cb(sr_session_ctx_t *session, uint32_t sub_id,
}
}
static int frr_sr_state_data_iter_cb(const struct lysc_node *snode,
struct yang_translator *translator,
struct yang_data *data, void *arg)
{
struct lyd_node *dnode = arg;
LY_ERR ly_errno;
ly_errno = 0;
ly_errno = lyd_new_path(NULL, ly_native_ctx, data->xpath, data->value,
0, &dnode);
if (ly_errno) {
flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
__func__);
yang_data_free(data);
return NB_ERR;
}
yang_data_free(data);
return NB_OK;
}
/* Callback for state retrieval. */
static int frr_sr_state_cb(sr_session_ctx_t *session, uint32_t sub_id,
const char *module_name, const char *xpath,
const char *request_xpath, uint32_t request_id,
struct lyd_node **parent, void *private_ctx)
{
struct lyd_node *dnode;
struct lyd_node *dnode = NULL;
dnode = *parent;
if (nb_oper_data_iterate(request_xpath, NULL, 0,
frr_sr_state_data_iter_cb, dnode)
!= NB_OK) {
if (nb_oper_iterate_legacy(request_xpath, NULL, 0, NULL, NULL, &dnode)) {
flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
"%s: failed to obtain operational data [xpath %s]",
__func__, xpath);

View File

@ -68,6 +68,7 @@ lib_libfrr_la_SOURCES = \
lib/mgmt_be_client.c \
lib/mgmt_fe_client.c \
lib/mgmt_msg.c \
lib/mgmt_msg_native.c \
lib/mlag.c \
lib/module.c \
lib/mpls.c \
@ -80,6 +81,7 @@ lib_libfrr_la_SOURCES = \
lib/northbound.c \
lib/northbound_cli.c \
lib/northbound_db.c \
lib/northbound_oper.c \
lib/ntop.c \
lib/openbsd-tree.c \
lib/pid_output.c \
@ -256,6 +258,7 @@ pkginclude_HEADERS += \
lib/mgmt_defines.h \
lib/mgmt_fe_client.h \
lib/mgmt_msg.h \
lib/mgmt_msg_native.h \
lib/mgmt_pb.h \
lib/module.h \
lib/monotime.h \

View File

@ -987,6 +987,19 @@ static const void *lib_vrf_lookup_entry(struct nb_cb_lookup_entry_args *args)
return vrf;
}
static const void *lib_vrf_lookup_next(struct nb_cb_lookup_entry_args *args)
{
const char *vrfname = args->keys->key[0];
struct vrf vrfkey, *vrf;
strlcpy(vrfkey.name, vrfname, sizeof(vrfkey.name));
vrf = RB_FIND(vrf_name_head, &vrfs_by_name, &vrfkey);
if (!strcmp(vrf->name, vrfname))
vrf = RB_NEXT(vrf_name_head, vrf);
return vrf;
}
/*
* XPath: /frr-vrf:lib/vrf/id
*/
@ -1024,6 +1037,7 @@ const struct frr_yang_module_info frr_vrf_info = {
.get_next = lib_vrf_get_next,
.get_keys = lib_vrf_get_keys,
.lookup_entry = lib_vrf_lookup_entry,
.lookup_next = lib_vrf_lookup_next,
},
.priority = NB_DFLT_PRIORITY - 2,
},

334
lib/vty.c
View File

@ -157,10 +157,9 @@ static int vty_mgmt_unlock_running_inline(struct vty *vty)
return vty->mgmt_locked_running_ds ? -1 : 0;
}
void vty_mgmt_resume_response(struct vty *vty, bool success)
void vty_mgmt_resume_response(struct vty *vty, int ret)
{
uint8_t header[4] = {0, 0, 0, 0};
int ret = CMD_SUCCESS;
if (!vty->mgmt_req_pending_cmd) {
zlog_err(
@ -168,14 +167,10 @@ void vty_mgmt_resume_response(struct vty *vty, bool success)
return;
}
if (!success)
ret = CMD_WARNING_CONFIG_FAILED;
MGMTD_FE_CLIENT_DBG(
"resuming CLI cmd after %s on vty session-id: %" PRIu64
" with '%s'",
vty->mgmt_req_pending_cmd, vty->mgmt_session_id,
success ? "succeeded" : "failed");
MGMTD_FE_CLIENT_DBG("resuming CLI cmd after %s on vty session-id: %" PRIu64
" with '%s'",
vty->mgmt_req_pending_cmd, vty->mgmt_session_id,
ret == CMD_SUCCESS ? "success" : "failed");
vty->mgmt_req_pending_cmd = NULL;
@ -3560,7 +3555,8 @@ static void vty_mgmt_ds_lock_notified(struct mgmt_fe_client *client,
if (!is_short_circuit && vty->mgmt_req_pending_cmd) {
assert(!strcmp(vty->mgmt_req_pending_cmd, "MESSAGE_LOCKDS_REQ"));
vty_mgmt_resume_response(vty, success);
vty_mgmt_resume_response(vty,
success ? CMD_SUCCESS : CMD_WARNING);
}
}
@ -3592,7 +3588,8 @@ static void vty_mgmt_set_config_result_notified(
vty_mgmt_unlock_running_inline(vty);
}
vty_mgmt_resume_response(vty, success);
vty_mgmt_resume_response(vty, success ? CMD_SUCCESS
: CMD_WARNING_CONFIG_FAILED);
}
static void vty_mgmt_commit_config_result_notified(
@ -3620,7 +3617,8 @@ static void vty_mgmt_commit_config_result_notified(
vty_out(vty, "MGMTD: %s\n", errmsg_if_any);
}
vty_mgmt_resume_response(vty, success);
vty_mgmt_resume_response(vty, success ? CMD_SUCCESS
: CMD_WARNING_CONFIG_FAILED);
}
static int vty_mgmt_get_data_result_notified(
@ -3640,7 +3638,7 @@ static int vty_mgmt_get_data_result_notified(
client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
vty_out(vty, "ERROR: GET_DATA request failed, Error: %s\n",
errmsg_if_any ? errmsg_if_any : "Unknown");
vty_mgmt_resume_response(vty, success);
vty_mgmt_resume_response(vty, CMD_WARNING);
return -1;
}
@ -3659,12 +3657,293 @@ static int vty_mgmt_get_data_result_notified(
}
if (next_key < 0) {
vty_out(vty, "]\n");
vty_mgmt_resume_response(vty, success);
vty_mgmt_resume_response(vty,
success ? CMD_SUCCESS : CMD_WARNING);
}
return 0;
}
static ssize_t vty_mgmt_libyang_print(void *user_data, const void *buf,
size_t count)
{
struct vty *vty = user_data;
vty_out(vty, "%.*s", (int)count, (const char *)buf);
return count;
}
static void vty_out_yang_error(struct vty *vty, LYD_FORMAT format,
struct ly_err_item *ei)
{
bool have_apptag = ei->apptag && ei->apptag[0] != 0;
bool have_path = ei->path && ei->path[0] != 0;
bool have_msg = ei->msg && ei->msg[0] != 0;
const char *severity = NULL;
const char *evalid = NULL;
const char *ecode = NULL;
LY_ERR err = ei->no;
if (ei->level == LY_LLERR)
severity = "error";
else if (ei->level == LY_LLWRN)
severity = "warning";
switch (ei->no) {
case LY_SUCCESS:
ecode = "ok";
break;
case LY_EMEM:
ecode = "out of memory";
break;
case LY_ESYS:
ecode = "system error";
break;
case LY_EINVAL:
ecode = "invalid value given";
break;
case LY_EEXIST:
ecode = "item exists";
break;
case LY_ENOTFOUND:
ecode = "item not found";
break;
case LY_EINT:
ecode = "operation interrupted";
break;
case LY_EVALID:
ecode = "validation failed";
break;
case LY_EDENIED:
ecode = "access denied";
break;
case LY_EINCOMPLETE:
ecode = "incomplete";
break;
case LY_ERECOMPILE:
ecode = "compile error";
break;
case LY_ENOT:
ecode = "not";
break;
default:
case LY_EPLUGIN:
case LY_EOTHER:
ecode = "other";
break;
}
if (err == LY_EVALID) {
switch (ei->vecode) {
case LYVE_SUCCESS:
evalid = NULL;
break;
case LYVE_SYNTAX:
evalid = "syntax";
break;
case LYVE_SYNTAX_YANG:
evalid = "yang-syntax";
break;
case LYVE_SYNTAX_YIN:
evalid = "yin-syntax";
break;
case LYVE_REFERENCE:
evalid = "reference";
break;
case LYVE_XPATH:
evalid = "xpath";
break;
case LYVE_SEMANTICS:
evalid = "semantics";
break;
case LYVE_SYNTAX_XML:
evalid = "xml-syntax";
break;
case LYVE_SYNTAX_JSON:
evalid = "json-syntax";
break;
case LYVE_DATA:
evalid = "data";
break;
default:
case LYVE_OTHER:
evalid = "other";
break;
}
}
switch (format) {
case LYD_XML:
vty_out(vty,
"<rpc-error xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">");
vty_out(vty, "<error-type>application</error-type>");
if (severity)
vty_out(vty, "<error-severity>%s</error-severity>",
severity);
if (ecode)
vty_out(vty, "<error-code>%s</error-code>", ecode);
if (evalid)
vty_out(vty, "<error-validation>%s</error-validation>\n",
evalid);
if (have_path)
vty_out(vty, "<error-path>%s</error-path>\n", ei->path);
if (have_apptag)
vty_out(vty, "<error-app-tag>%s</error-app-tag>\n",
ei->apptag);
if (have_msg)
vty_out(vty, "<error-message>%s</error-message>\n",
ei->msg);
vty_out(vty, "</rpc-error>");
break;
case LYD_JSON:
vty_out(vty, "{ \"error-type\": \"application\"");
if (severity)
vty_out(vty, ", \"error-severity\": \"%s\"", severity);
if (ecode)
vty_out(vty, ", \"error-code\": \"%s\"", ecode);
if (evalid)
vty_out(vty, ", \"error-validation\": \"%s\"", evalid);
if (have_path)
vty_out(vty, ", \"error-path\": \"%s\"", ei->path);
if (have_apptag)
vty_out(vty, ", \"error-app-tag\": \"%s\"", ei->apptag);
if (have_msg)
vty_out(vty, ", \"error-message\": \"%s\"", ei->msg);
vty_out(vty, "}");
break;
case LYD_UNKNOWN:
case LYD_LYB:
default:
vty_out(vty, "%% error");
if (severity)
vty_out(vty, " severity: %s", severity);
if (evalid)
vty_out(vty, " invalid: %s", evalid);
if (have_path)
vty_out(vty, " path: %s", ei->path);
if (have_apptag)
vty_out(vty, " app-tag: %s", ei->apptag);
if (have_msg)
vty_out(vty, " msg: %s", ei->msg);
break;
}
}
static uint vty_out_yang_errors(struct vty *vty, LYD_FORMAT format)
{
struct ly_err_item *ei = ly_err_first(ly_native_ctx);
uint count;
if (!ei)
return 0;
if (format == LYD_JSON)
vty_out(vty, "\"ietf-restconf:errors\": [ ");
for (count = 0; ei; count++, ei = ei->next) {
if (count)
vty_out(vty, ", ");
vty_out_yang_error(vty, format, ei);
}
if (format == LYD_JSON)
vty_out(vty, " ]");
ly_err_clean(ly_native_ctx, NULL);
return count;
}
static int vty_mgmt_get_tree_result_notified(
struct mgmt_fe_client *client, uintptr_t user_data, uint64_t client_id,
uint64_t session_id, uintptr_t session_ctx, uint64_t req_id,
Mgmtd__DatastoreId ds_id, LYD_FORMAT result_type, void *result,
size_t len, int partial_error)
{
struct vty *vty;
struct lyd_node *dnode;
int ret = CMD_SUCCESS;
LY_ERR err;
vty = (struct vty *)session_ctx;
MGMTD_FE_CLIENT_DBG("GET_TREE request %ssucceeded, client 0x%" PRIx64
" req-id %" PRIu64,
partial_error ? "partially " : "", client_id,
req_id);
assert(result_type == LYD_LYB ||
result_type == vty->mgmt_req_pending_data);
if (vty->mgmt_req_pending_data == LYD_XML && partial_error)
vty_out(vty,
"<!-- some errors occurred gathering results -->\n");
if (result_type == LYD_LYB) {
/*
* parse binary into tree and print in the specified format
*/
result_type = vty->mgmt_req_pending_data;
err = lyd_parse_data_mem(ly_native_ctx, result, LYD_LYB, 0, 0,
&dnode);
if (!err)
err = lyd_print_clb(vty_mgmt_libyang_print, vty, dnode,
result_type, LYD_PRINT_WITHSIBLINGS);
lyd_free_all(dnode);
if (vty_out_yang_errors(vty, result_type) || err)
ret = CMD_WARNING;
} else {
/*
* Print the in-format result
*/
assert(result_type == LYD_XML || result_type == LYD_JSON);
vty_out(vty, "%.*s\n", (int)len - 1, (const char *)result);
}
vty_mgmt_resume_response(vty, ret);
return 0;
}
static int vty_mgmt_error_notified(struct mgmt_fe_client *client,
uintptr_t user_data, uint64_t client_id,
uint64_t session_id, uintptr_t session_ctx,
uint64_t req_id, int error,
const char *errstr)
{
struct vty *vty = (struct vty *)session_ctx;
const char *cname = mgmt_fe_client_name(client);
if (!vty->mgmt_req_pending_cmd) {
MGMTD_FE_CLIENT_DBG("Erorr with no pending command: %d returned for client %s 0x%" PRIx64
" session-id %" PRIu64 " req-id %" PRIu64
"error-str %s",
error, cname, client_id, session_id, req_id,
errstr);
vty_out(vty,
"%% Error %d from MGMTD for %s with no pending command: %s\n",
error, cname, errstr);
return CMD_WARNING;
}
MGMTD_FE_CLIENT_DBG("Erorr %d returned for client %s 0x%" PRIx64
" session-id %" PRIu64 " req-id %" PRIu64
"error-str %s",
error, cname, client_id, session_id, req_id, errstr);
vty_out(vty, "%% %s (for %s, client %s)\n", errstr,
vty->mgmt_req_pending_cmd, cname);
vty_mgmt_resume_response(vty, error ? CMD_WARNING : CMD_SUCCESS);
return 0;
}
static struct mgmt_fe_client_cbs mgmt_cbs = {
.client_connect_notify = vty_mgmt_server_connected,
.client_session_notify = vty_mgmt_session_notify,
@ -3672,6 +3951,9 @@ static struct mgmt_fe_client_cbs mgmt_cbs = {
.set_config_notify = vty_mgmt_set_config_result_notified,
.commit_config_notify = vty_mgmt_commit_config_result_notified,
.get_data_notify = vty_mgmt_get_data_result_notified,
.get_tree_notify = vty_mgmt_get_tree_result_notified,
.error_notify = vty_mgmt_error_notified,
};
void vty_init_mgmt_fe(void)
@ -3893,6 +4175,28 @@ int vty_mgmt_send_get_req(struct vty *vty, bool is_config,
return 0;
}
int vty_mgmt_send_get_tree_req(struct vty *vty, LYD_FORMAT result_type,
const char *xpath)
{
LYD_FORMAT intern_format = result_type;
vty->mgmt_req_id++;
if (mgmt_fe_send_get_tree_req(mgmt_fe_client, vty->mgmt_session_id,
vty->mgmt_req_id, intern_format, xpath)) {
zlog_err("Failed to send GET-TREE to MGMTD session-id: %" PRIu64
" req-id %" PRIu64 ".",
vty->mgmt_session_id, vty->mgmt_req_id);
vty_out(vty, "Failed to send GET-TREE to MGMTD!\n");
return -1;
}
vty->mgmt_req_pending_cmd = "MESSAGE_GET_TREE_REQ";
vty->mgmt_req_pending_data = result_type;
return 0;
}
/* Install vty's own commands like `who' command. */
void vty_init(struct event_loop *master_thread, bool do_command_logging)
{

View File

@ -229,6 +229,7 @@ struct vty {
* CLI command and we are waiting on the reply so we can respond to the
* vty user. */
const char *mgmt_req_pending_cmd;
uintptr_t mgmt_req_pending_data;
bool mgmt_locked_candidate_ds;
bool mgmt_locked_running_ds;
/* Need to track when we file-lock in vtysh to re-lock on end/conf t
@ -419,9 +420,11 @@ extern int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only,
extern int vty_mgmt_send_get_req(struct vty *vty, bool is_config,
Mgmtd__DatastoreId datastore,
const char **xpath_list, int num_req);
extern int vty_mgmt_send_get_tree_req(struct vty *vty, LYD_FORMAT result_type,
const char *xpath);
extern int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
bool lock, bool scok);
extern void vty_mgmt_resume_response(struct vty *vty, bool success);
extern void vty_mgmt_resume_response(struct vty *vty, int ret);
static inline bool vty_needs_implicit_commit(struct vty *vty)
{

View File

@ -6,6 +6,7 @@
#include <zebra.h>
#include "darr.h"
#include "log.h"
#include "lib_errors.h"
#include "yang.h"
@ -363,33 +364,10 @@ unsigned int yang_snode_num_keys(const struct lysc_node *snode)
return count;
}
void yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
size_t xpath_len)
char *yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
size_t xpath_len)
{
lyd_path(dnode, LYD_PATH_STD, xpath, xpath_len);
}
const char *yang_dnode_get_schema_name(const struct lyd_node *dnode,
const char *xpath_fmt, ...)
{
if (xpath_fmt) {
va_list ap;
char xpath[XPATH_MAXLEN];
va_start(ap, xpath_fmt);
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
va_end(ap);
dnode = yang_dnode_get(dnode, xpath);
if (!dnode) {
flog_err(EC_LIB_YANG_DNODE_NOT_FOUND,
"%s: couldn't find %s", __func__, xpath);
zlog_backtrace(LOG_ERR);
abort();
}
}
return dnode->schema->name;
return lyd_path(dnode, LYD_PATH_STD, xpath, xpath_len);
}
struct lyd_node *yang_dnode_get(const struct lyd_node *dnode, const char *xpath)
@ -673,6 +651,37 @@ static void ly_log_cb(LY_LOG_LEVEL level, const char *msg, const char *path)
zlog(priority, "libyang: %s", msg);
}
static ssize_t yang_print_darr(void *arg, const void *buf, size_t count)
{
uint8_t *dst = darr_append_n(*(uint8_t **)arg, count);
memcpy(dst, buf, count);
return count;
}
LY_ERR yang_print_tree_append(uint8_t **darr, const struct lyd_node *root,
LYD_FORMAT format, uint32_t options)
{
LY_ERR err;
err = lyd_print_clb(yang_print_darr, darr, root, format, options);
if (err)
zlog_err("Failed to save yang tree: %s", ly_last_errmsg());
else if (format != LYD_LYB)
*darr_append(*darr) = 0;
return err;
}
uint8_t *yang_print_tree(const struct lyd_node *root, LYD_FORMAT format,
uint32_t options)
{
uint8_t *darr = NULL;
if (yang_print_tree_append(&darr, root, format, options))
return NULL;
return darr;
}
const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len)
{
struct ly_err_item *ei;
@ -713,6 +722,7 @@ struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_compile)
{
struct ly_ctx *ctx = NULL;
const char *yang_models_path = YANG_MODELS_PATH;
uint options;
LY_ERR err;
if (access(yang_models_path, R_OK | X_OK)) {
@ -726,7 +736,7 @@ struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_compile)
YANG_MODELS_PATH);
}
uint options = LY_CTX_NO_YANGLIBRARY | LY_CTX_DISABLE_SEARCHDIR_CWD;
options = LY_CTX_NO_YANGLIBRARY | LY_CTX_DISABLE_SEARCHDIR_CWD;
if (explicit_compile)
options |= LY_CTX_EXPLICIT_COMPILE;
err = ly_ctx_new(yang_models_path, options, &ctx);
@ -917,3 +927,95 @@ uint32_t yang_get_list_elements_count(const struct lyd_node *node)
} while (node);
return count;
}
int yang_get_key_preds(char *s, const struct lysc_node *snode,
struct yang_list_keys *keys, ssize_t space)
{
const struct lysc_node_leaf *skey;
ssize_t len2, len = 0;
ssize_t i = 0;
LY_FOR_KEYS (snode, skey) {
assert(i < keys->num);
len2 = snprintf(s + len, space - len, "[%s='%s']", skey->name,
keys->key[i]);
if (len2 > space - len)
len = space;
else
len += len2;
i++;
}
assert(i == keys->num);
return i;
}
int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys)
{
struct lyd_node *child = lyd_child(node);
keys->num = 0;
for (; child && lysc_is_key(child->schema); child = child->next) {
const char *value = lyd_get_value(child);
if (!value)
return NB_ERR;
strlcpy(keys->key[keys->num], value,
sizeof(keys->key[keys->num]));
keys->num++;
}
return NB_OK;
}
LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent,
const struct lysc_node *snode,
const struct yang_list_keys *list_keys,
struct lyd_node_inner **node)
{
struct lyd_node *pnode = &parent->node;
struct lyd_node **nodepp = (struct lyd_node **)node;
const char(*keys)[LIST_MAXKEYLEN] = list_keys->key;
/*
* When
* https://github.com/CESNET/libyang/commit/2c1e327c7c2dd3ba12d466a4ebcf62c1c44116c4
* is released in libyang we should add a configure.ac check for the
* lyd_new_list3 function and use it here.
*/
switch (list_keys->num) {
case 0:
return lyd_new_list(pnode, snode->module, snode->name, false,
nodepp);
case 1:
return lyd_new_list(pnode, snode->module, snode->name, false,
nodepp, keys[0]);
case 2:
return lyd_new_list(pnode, snode->module, snode->name, false,
nodepp, keys[0], keys[1]);
case 3:
return lyd_new_list(pnode, snode->module, snode->name, false,
nodepp, keys[0], keys[1], keys[2]);
case 4:
return lyd_new_list(pnode, snode->module, snode->name, false,
nodepp, keys[0], keys[1], keys[2], keys[3]);
case 5:
return lyd_new_list(pnode, snode->module, snode->name, false,
nodepp, keys[0], keys[1], keys[2], keys[3],
keys[4]);
case 6:
return lyd_new_list(pnode, snode->module, snode->name, false,
nodepp, keys[0], keys[1], keys[2], keys[3],
keys[4], keys[5]);
case 7:
return lyd_new_list(pnode, snode->module, snode->name, false,
nodepp, keys[0], keys[1], keys[2], keys[3],
keys[4], keys[5], keys[6]);
case 8:
return lyd_new_list(pnode, snode->module, snode->name, false,
nodepp, keys[0], keys[1], keys[2], keys[3],
keys[4], keys[5], keys[6], keys[7]);
}
_Static_assert(LIST_MAXKEYS == 8, "max key mismatch in switch unroll");
/*NOTREACHED*/
return LY_EINVAL;
}

View File

@ -317,31 +317,17 @@ extern unsigned int yang_snode_num_keys(const struct lysc_node *snode);
* libyang data node to be processed.
*
* xpath
* Pointer to previously allocated buffer.
* Pointer to previously allocated buffer or NULL.
*
* xpath_len
* Size of the xpath buffer.
* Size of the xpath buffer if xpath non-NULL.
*
* If xpath is NULL, the returned string (if non-NULL) needs to be free()d by
* the caller.
*/
extern void yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
extern char *yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
size_t xpath_len);
/*
* Return the schema name of the given libyang data node.
*
* dnode
* libyang data node.
*
* xpath_fmt
* Optional XPath expression (absolute or relative) to specify a different
* data node to operate on in the same data tree.
*
* Returns:
* Schema name of the libyang data node.
*/
extern const char *yang_dnode_get_schema_name(const struct lyd_node *dnode,
const char *xpath_fmt, ...)
PRINTFRR(2, 3);
/*
* Find a libyang data node by its YANG data path.
*
@ -600,6 +586,39 @@ extern struct ly_ctx *yang_ctx_new_setup(bool embedded_modules,
*/
extern void yang_debugging_set(bool enable);
/*
* "Print" the yang tree in `root` into dynamic sized array.
*
* Args:
* root: root of the subtree to "print" along with siblings.
* format: LYD_FORMAT of output (see lyd_print_mem)
* options: printing options (see lyd_print_mem)
*
* Return:
* A darr dynamic array with the "printed" output or NULL on failure.
*/
extern uint8_t *yang_print_tree(const struct lyd_node *root, LYD_FORMAT format,
uint32_t options);
/*
* "Print" the yang tree in `root` into an existing dynamic sized array.
*
* This function does not initialize or free the dynamic array, the array can
* already existing data, the tree will be appended to this data.
*
* Args:
* darr: existing `uint8_t *`, dynamic array.
* root: root of the subtree to "print" along with siblings.
* format: LYD_FORMAT of output (see lyd_print_mem)
* options: printing options (see lyd_print_mem)
*
* Return:
* LY_ERR from underlying calls.
*/
extern LY_ERR yang_print_tree_append(uint8_t **darr, const struct lyd_node *root,
LYD_FORMAT format, uint32_t options);
/*
* Print libyang error messages into the provided buffer.
*
@ -693,6 +712,18 @@ bool yang_is_last_list_dnode(const struct lyd_node *dnode);
/* API to check if the given node is last node in the data tree level */
bool yang_is_last_level_dnode(const struct lyd_node *dnode);
/* Create a YANG predicate string based on the keys */
extern int yang_get_key_preds(char *s, const struct lysc_node *snode,
struct yang_list_keys *keys, ssize_t space);
/* Get YANG keys from an existing dnode */
extern int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys);
/* Create a new list lyd_node using `yang_list_keys` */
extern LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent,
const struct lysc_node *snode,
const struct yang_list_keys *keys,
struct lyd_node_inner **node);
#ifdef __cplusplus
}
#endif

View File

@ -15,6 +15,7 @@
#include "network.h"
#include "libfrr.h"
#include "mgmt_msg.h"
#include "mgmt_msg_native.h"
#include "mgmt_pb.h"
#include "mgmtd/mgmt.h"
#include "mgmtd/mgmt_memory.h"
@ -34,6 +35,7 @@
/* ---------- */
const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1] = {
[MGMTD_BE_CLIENT_ID_ZEBRA] = "zebra",
#ifdef HAVE_STATICD
[MGMTD_BE_CLIENT_ID_STATICD] = "staticd",
#endif
@ -72,7 +74,16 @@ static const char *const *be_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
#endif
};
static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {};
static const char *const zebra_oper_xpaths[] = {
"/frr-interface:lib/interface",
"/frr-vrf:lib/vrf/frr-zebra:zebra",
"/frr-zebra:zebra",
NULL,
};
static const char *const *be_client_oper_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
[MGMTD_BE_CLIENT_ID_ZEBRA] = zebra_oper_xpaths,
};
/*
* We would like to have a better ADT than one with O(n) comparisons
@ -102,8 +113,7 @@ mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter);
static bool be_is_client_interested(const char *xpath,
enum mgmt_be_client_id id, bool config);
static const char *mgmt_be_client_id2name(enum mgmt_be_client_id id)
const char *mgmt_be_client_id2name(enum mgmt_be_client_id id)
{
if (id > MGMTD_BE_CLIENT_ID_MAX)
return "invalid client id";
@ -287,7 +297,6 @@ mgmt_be_adapter_cleanup_old_conn(struct mgmt_be_client_adapter *adapter)
}
}
static int mgmt_be_adapter_send_msg(struct mgmt_be_client_adapter *adapter,
Mgmtd__BeMessage *be_msg)
{
@ -410,17 +419,11 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter,
be_msg->cfg_apply_reply->success,
be_msg->cfg_apply_reply->error_if_any, adapter);
break;
case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY:
/*
* TODO: Add handling code in future.
*/
break;
/*
* NOTE: The following messages are always sent from MGMTD to
* Backend clients only and/or need not be handled on MGMTd.
*/
case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY:
case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ:
case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ:
case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ:
case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ:
@ -503,12 +506,77 @@ int mgmt_be_send_cfgapply_req(struct mgmt_be_client_adapter *adapter,
return mgmt_be_adapter_send_msg(adapter, &be_msg);
}
int mgmt_be_send_native(enum mgmt_be_client_id id, void *msg)
{
struct mgmt_be_client_adapter *adapter = mgmt_be_get_adapter_by_id(id);
if (!adapter)
return -1;
return mgmt_msg_native_send_msg(adapter->conn, msg, false);
}
/*
* Handle a native encoded message
*/
static void be_adapter_handle_native_msg(struct mgmt_be_client_adapter *adapter,
struct mgmt_msg_header *msg,
size_t msg_len)
{
struct mgmt_msg_tree_data *tree_msg;
struct mgmt_msg_error *error_msg;
/* get the transaction */
switch (msg->code) {
case MGMT_MSG_CODE_ERROR:
error_msg = (typeof(error_msg))msg;
MGMTD_BE_ADAPTER_DBG("Got ERROR from '%s' txn-id %" PRIx64,
adapter->name, msg->refer_id);
/* Forward the reply to the txn module */
mgmt_txn_notify_error(adapter, msg->refer_id, msg->req_id,
error_msg->error, error_msg->errstr);
break;
case MGMT_MSG_CODE_TREE_DATA:
/* tree data from a backend client */
tree_msg = (typeof(tree_msg))msg;
MGMTD_BE_ADAPTER_DBG("Got TREE_DATA from '%s' txn-id %" PRIx64,
adapter->name, msg->refer_id);
/* Forward the reply to the txn module */
mgmt_txn_notify_tree_data_reply(adapter, tree_msg, msg_len);
break;
default:
MGMTD_BE_ADAPTER_ERR("unknown native message txn-id %" PRIu64
" req-id %" PRIu64
" code %u from BE client for adapter %s",
msg->refer_id, msg->req_id, msg->code,
adapter->name);
break;
}
}
static void mgmt_be_adapter_process_msg(uint8_t version, uint8_t *data,
size_t len, struct msg_conn *conn)
{
struct mgmt_be_client_adapter *adapter = conn->user;
Mgmtd__BeMessage *be_msg = mgmtd__be_message__unpack(NULL, len, data);
Mgmtd__BeMessage *be_msg;
if (version == MGMT_MSG_VERSION_NATIVE) {
struct mgmt_msg_header *msg = (typeof(msg))data;
if (len >= sizeof(*msg))
be_adapter_handle_native_msg(adapter, msg, len);
else
MGMTD_BE_ADAPTER_ERR("native message to adapter %s too short %zu",
adapter->name, len);
return;
}
be_msg = mgmtd__be_message__unpack(NULL, len, data);
if (!be_msg) {
MGMTD_BE_ADAPTER_DBG(
"Failed to decode %zu bytes for adapter: %s", len,
@ -662,11 +730,13 @@ struct msg_conn *mgmt_be_create_adapter(int conn_fd, union sockunion *from)
mgmt_be_adapters_add_tail(&mgmt_be_adapters, adapter);
RB_INIT(nb_config_cbs, &adapter->cfg_chgs);
adapter->conn = msg_server_conn_create(
mgmt_loop, conn_fd, mgmt_be_adapter_notify_disconnect,
mgmt_be_adapter_process_msg, MGMTD_BE_MAX_NUM_MSG_PROC,
MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN, adapter,
"BE-adapter");
adapter->conn = msg_server_conn_create(mgmt_loop, conn_fd,
mgmt_be_adapter_notify_disconnect,
mgmt_be_adapter_process_msg,
MGMTD_BE_MAX_NUM_MSG_PROC,
MGMTD_BE_MAX_NUM_MSG_WRITE,
MGMTD_BE_MSG_MAX_LEN, adapter,
"BE-adapter");
adapter->conn->debug = DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL);

View File

@ -30,6 +30,7 @@ enum mgmt_be_client_id {
#ifdef HAVE_STATICD
MGMTD_BE_CLIENT_ID_STATICD,
#endif
MGMTD_BE_CLIENT_ID_ZEBRA,
MGMTD_BE_CLIENT_ID_MAX
};
#define MGMTD_BE_CLIENT_ID_MIN 0
@ -149,6 +150,9 @@ mgmt_be_get_adapter_by_name(const char *name);
extern struct mgmt_be_client_adapter *
mgmt_be_get_adapter_by_id(enum mgmt_be_client_id id);
/* Get the client name given a client ID */
extern const char *mgmt_be_client_id2name(enum mgmt_be_client_id id);
/* Toggle debug on or off for connected clients. */
extern void mgmt_be_adapter_toggle_client_debug(bool set);
@ -211,6 +215,19 @@ extern void mgmt_be_adapter_status_write(struct vty *vty);
*/
extern void mgmt_be_xpath_register_write(struct vty *vty);
/**
* Send a native message to a backend client
*
* Args:
* adapter: the client to send the message to.
* msg: a native message from mgmt_msg_native_alloc_msg()
*
* Return:
* Any return value from msg_conn_send_msg().
*/
extern int mgmt_be_send_native(enum mgmt_be_client_id id, void *msg);
/**
* Lookup the clients which are subscribed to a given `xpath`
* and the way they are subscribed.

View File

@ -8,11 +8,13 @@
*/
#include <zebra.h>
#include "darr.h"
#include "sockopt.h"
#include "network.h"
#include "libfrr.h"
#include "mgmt_fe_client.h"
#include "mgmt_msg.h"
#include "mgmt_msg_native.h"
#include "mgmt_pb.h"
#include "hash.h"
#include "jhash.h"
@ -254,6 +256,15 @@ void mgmt_fe_adapter_toggle_client_debug(bool set)
adapter->conn->debug = set;
}
static struct mgmt_fe_session_ctx *fe_adapter_session_by_txn_id(uint64_t txn_id)
{
uint64_t session_id = mgmt_txn_get_session_id(txn_id);
if (session_id == MGMTD_SESSION_ID_NONE)
return NULL;
return mgmt_session_id2ctx(session_id);
}
static struct mgmt_fe_session_ctx *
mgmt_fe_create_session(struct mgmt_fe_client_adapter *adapter,
uint64_t client_id)
@ -281,6 +292,14 @@ mgmt_fe_create_session(struct mgmt_fe_client_adapter *adapter,
return session;
}
static int fe_adapter_send_native_msg(struct mgmt_fe_client_adapter *adapter,
void *msg, size_t len,
bool short_circuit_ok)
{
return msg_conn_send_msg(adapter->conn, MGMT_MSG_VERSION_NATIVE, msg,
len, NULL, short_circuit_ok);
}
static int fe_adapter_send_msg(struct mgmt_fe_client_adapter *adapter,
Mgmtd__FeMessage *fe_msg, bool short_circuit_ok)
{
@ -478,6 +497,28 @@ static int fe_adapter_send_get_reply(struct mgmt_fe_session_ctx *session,
return fe_adapter_send_msg(session->adapter, &fe_msg, false);
}
static int fe_adapter_send_error(struct mgmt_fe_session_ctx *session,
uint64_t req_id, bool short_circuit_ok,
int16_t error, const char *errfmt, ...)
PRINTFRR(5, 6);
static int fe_adapter_send_error(struct mgmt_fe_session_ctx *session,
uint64_t req_id, bool short_circuit_ok,
int16_t error, const char *errfmt, ...)
{
va_list ap;
int ret;
va_start(ap, errfmt);
ret = vmgmt_msg_native_send_error(session->adapter->conn,
session->session_id, req_id,
short_circuit_ok, error, errfmt, ap);
va_end(ap);
return ret;
}
static void mgmt_fe_session_cfg_txn_clnup(struct event *thread)
{
struct mgmt_fe_session_ctx *session;
@ -748,14 +789,8 @@ static int mgmt_fe_session_handle_get_req_msg(struct mgmt_fe_session_ctx *sessio
struct nb_config *cfg_root = NULL;
Mgmtd__DatastoreId ds_id = get_req->ds_id;
uint64_t req_id = get_req->req_id;
bool is_cfg = get_req->config;
bool ds_ok = true;
if (is_cfg && ds_id != MGMTD_DS_CANDIDATE && ds_id != MGMTD_DS_RUNNING)
ds_ok = false;
else if (!is_cfg && ds_id != MGMTD_DS_OPERATIONAL)
ds_ok = false;
if (!ds_ok) {
if (ds_id != MGMTD_DS_CANDIDATE && ds_id != MGMTD_DS_RUNNING) {
fe_adapter_send_get_reply(session, ds_id, req_id, false, NULL,
"get-req on unsupported datastore");
return 0;
@ -791,8 +826,7 @@ static int mgmt_fe_session_handle_get_req_msg(struct mgmt_fe_session_ctx *sessio
/*
* Get a copy of the datastore config root, avoids locking.
*/
if (is_cfg)
cfg_root = nb_config_dup(mgmt_ds_get_nb_config(ds_ctx));
cfg_root = nb_config_dup(mgmt_ds_get_nb_config(ds_ctx));
/*
* Create a GET request under the transaction.
@ -988,9 +1022,8 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter,
break;
case MGMTD__FE_MESSAGE__MESSAGE_GET_REQ:
session = mgmt_session_id2ctx(fe_msg->get_req->session_id);
MGMTD_FE_ADAPTER_DBG("Got GET_REQ (iscfg %d) for DS:%s (xpaths: %d) on session-id %" PRIu64
MGMTD_FE_ADAPTER_DBG("Got GET_REQ for DS:%s (xpaths: %d) on session-id %" PRIu64
" from '%s'",
(int)fe_msg->get_req->config,
mgmt_ds_id2name(fe_msg->get_req->ds_id),
(int)fe_msg->get_req->n_data,
fe_msg->get_req->session_id, adapter->name);
@ -1028,12 +1061,186 @@ mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter,
return 0;
}
/**
* Send result of get-tree request back to the FE client.
*
* Args:
* session: the session.
* req_id: the request ID.
* short_circuit_ok: if allowed to short circuit the message.
* result_format: LYD_FORMAT for the sent output.
* tree: the tree to send, can be NULL which will send an empty tree.
* partial_error: if an error occurred during gathering results.
*
* Return:
* Any error that occurs -- the message is likely not sent if non-zero.
*/
static int fe_adapter_send_tree_data(struct mgmt_fe_session_ctx *session,
uint64_t req_id, bool short_circuit_ok,
uint8_t result_type,
const struct lyd_node *tree,
int partial_error)
{
struct mgmt_msg_tree_data *msg;
struct lyd_node *empty = NULL;
uint8_t *buf = NULL;
int ret = 0;
darr_append_n(buf, offsetof(typeof(*msg), result));
msg = (typeof(msg))buf;
msg->refer_id = session->session_id;
msg->req_id = req_id;
msg->code = MGMT_MSG_CODE_TREE_DATA;
msg->partial_error = partial_error;
msg->result_type = result_type;
if (!tree) {
empty = yang_dnode_new(ly_native_ctx, false);
tree = empty;
}
ret = yang_print_tree_append(&buf, tree, result_type,
(LYD_PRINT_WD_EXPLICIT |
LYD_PRINT_WITHSIBLINGS));
/* buf may have been reallocated and moved */
msg = (typeof(msg))buf;
if (ret != LY_SUCCESS) {
MGMTD_FE_ADAPTER_ERR("Error building get-tree result for client %s session-id %" PRIu64
" req-id %" PRIu64
" scok %d result type %u",
session->adapter->name, session->session_id,
req_id, short_circuit_ok, result_type);
goto done;
}
MGMTD_FE_ADAPTER_DBG("Sending get-tree result from adapter %s to session-id %" PRIu64
" req-id %" PRIu64 " scok %d result type %u len %u",
session->adapter->name, session->session_id, req_id,
short_circuit_ok, result_type, darr_len(buf));
ret = fe_adapter_send_native_msg(session->adapter, buf, darr_len(buf),
short_circuit_ok);
done:
if (empty)
yang_dnode_free(empty);
darr_free(buf);
return ret;
}
/**
* Handle a get-tree message from the client.
*/
static void fe_adapter_handle_get_tree(struct mgmt_fe_session_ctx *session,
void *data, size_t len)
{
struct mgmt_msg_get_tree *msg = data;
uint64_t req_id = msg->req_id;
uint64_t clients;
int ret;
MGMTD_FE_ADAPTER_DBG("Received get-tree request from client %s for session-id %" PRIu64
" req-id %" PRIu64,
session->adapter->name, session->session_id,
msg->req_id);
if (session->txn_id != MGMTD_TXN_ID_NONE) {
fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
"Transaction in progress txn-id: %" PRIu64
" for session-id: %" PRIu64,
session->txn_id, session->session_id);
return;
}
clients = mgmt_be_interested_clients(msg->xpath, false);
if (!clients) {
MGMTD_FE_ADAPTER_DBG("No backends provide xpath: %s for txn-id: %" PRIu64
" session-id: %" PRIu64,
msg->xpath, session->txn_id,
session->session_id);
fe_adapter_send_tree_data(session, req_id, false,
msg->result_type, NULL, 0);
return;
}
/* Start a SHOW Transaction */
session->txn_id = mgmt_create_txn(session->session_id,
MGMTD_TXN_TYPE_SHOW);
if (session->txn_id == MGMTD_SESSION_ID_NONE) {
fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
"failed to create a 'show' txn");
return;
}
MGMTD_FE_ADAPTER_DBG("Created new show txn-id: %" PRIu64
" for session-id: %" PRIu64,
session->txn_id, session->session_id);
/* Create a GET-TREE request under the transaction */
ret = mgmt_txn_send_get_tree_oper(session->txn_id, req_id, clients,
msg->result_type, msg->xpath);
if (ret) {
/* destroy the just created txn */
mgmt_destroy_txn(&session->txn_id);
fe_adapter_send_error(session, req_id, false, -EINPROGRESS,
"failed to create a 'show' txn");
}
}
/**
* Handle a native encoded message from the FE client.
*/
static void fe_adapter_handle_native_msg(struct mgmt_fe_client_adapter *adapter,
struct mgmt_msg_header *msg,
size_t msg_len)
{
struct mgmt_fe_session_ctx *session;
session = mgmt_session_id2ctx(msg->refer_id);
if (!session) {
MGMTD_FE_ADAPTER_ERR("adapter %s: recv msg unknown session-id %" PRIu64,
adapter->name, msg->refer_id);
return;
}
assert(session->adapter == adapter);
switch (msg->code) {
case MGMT_MSG_CODE_GET_TREE:
fe_adapter_handle_get_tree(session, msg, msg_len);
break;
default:
MGMTD_FE_ADAPTER_ERR("unknown native message session-id %" PRIu64
" req-id %" PRIu64
" code %u to FE adapter %s",
msg->refer_id, msg->req_id, msg->code,
adapter->name);
break;
}
}
static void mgmt_fe_adapter_process_msg(uint8_t version, uint8_t *data,
size_t len, struct msg_conn *conn)
{
struct mgmt_fe_client_adapter *adapter = conn->user;
Mgmtd__FeMessage *fe_msg = mgmtd__fe_message__unpack(NULL, len, data);
Mgmtd__FeMessage *fe_msg;
if (version == MGMT_MSG_VERSION_NATIVE) {
struct mgmt_msg_header *msg = (typeof(msg))data;
if (len >= sizeof(*msg))
fe_adapter_handle_native_msg(adapter, msg, len);
else
MGMTD_FE_ADAPTER_ERR("native message to adapter %s too short %zu",
adapter->name, len);
return;
}
fe_msg = mgmtd__fe_message__unpack(NULL, len, data);
if (!fe_msg) {
MGMTD_FE_ADAPTER_DBG(
"Failed to decode %zu bytes for adapter: %s", len,
@ -1209,8 +1416,54 @@ int mgmt_fe_send_get_reply(uint64_t session_id, uint64_t txn_id,
error_if_any);
}
struct mgmt_setcfg_stats *
mgmt_fe_get_session_setcfg_stats(uint64_t session_id)
int mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id,
uint64_t req_id, LYD_FORMAT result_type,
const struct lyd_node *tree,
int partial_error, bool short_circuit_ok)
{
struct mgmt_fe_session_ctx *session;
int ret;
session = mgmt_session_id2ctx(session_id);
if (!session || session->txn_id != txn_id)
return -1;
ret = fe_adapter_send_tree_data(session, req_id, short_circuit_ok,
result_type, tree, partial_error);
mgmt_destroy_txn(&session->txn_id);
return ret;
}
/**
* Send an error back to the FE client and cleanup any in-progress txn.
*/
int mgmt_fe_adapter_txn_error(uint64_t txn_id, uint64_t req_id,
bool short_circuit_ok, int16_t error,
const char *errstr)
{
struct mgmt_fe_session_ctx *session;
int ret;
session = fe_adapter_session_by_txn_id(txn_id);
if (!session) {
MGMTD_FE_ADAPTER_ERR("failed sending error for txn-id %" PRIu64
" session not found",
txn_id);
return -ENOENT;
}
ret = fe_adapter_send_error(session, req_id, false, error, "%s", errstr);
mgmt_destroy_txn(&session->txn_id);
return ret;
}
struct mgmt_setcfg_stats *mgmt_fe_get_session_setcfg_stats(uint64_t session_id)
{
struct mgmt_fe_session_ctx *session;

View File

@ -138,6 +138,52 @@ extern int mgmt_fe_send_get_reply(uint64_t session_id, uint64_t txn_id,
Mgmtd__YangDataReply *data_resp,
const char *error_if_any);
/**
* Send get-tree data reply back to client.
*
* This also cleans up and frees the transaction.
*
* Args:
* session_id: the session.
* txn_id: the txn_id this data pertains to
* req_id: the req id for the get_tree message
* result_type: the format of the result data.
* tree: the results.
* partial_error: if there were errors while gather results.
* short_circuit_ok: True if OK to short-circuit the call.
*
* Return:
* the return value from the underlying send function.
*
*/
extern int mgmt_fe_adapter_send_tree_data(uint64_t session_id, uint64_t txn_id,
uint64_t req_id,
LYD_FORMAT result_type,
const struct lyd_node *tree,
int partial_error,
bool short_circuit_ok);
/**
* Send an error back to the FE client using native messaging.
*
* This also cleans up and frees the transaction.
*
* Args:
* txn_id: the txn_id this error pertains to.
* short_circuit_ok: True if OK to short-circuit the call.
* error: An integer error value.
* errfmt: An error format string (i.e., printfrr)
* ...: args for use by the `errfmt` format string.
*
* Return:
* the return value from the underlying send function.
*
*/
extern int mgmt_fe_adapter_txn_error(uint64_t txn_id, uint64_t req_id,
bool short_circuit_ok, int16_t error,
const char *errstr);
/* Fetch frontend client session set-config stats */
extern struct mgmt_setcfg_stats *
mgmt_fe_get_session_setcfg_stats(uint64_t session_id);

View File

@ -261,7 +261,9 @@ failed_unlock:
void mgmt_history_rollback_complete(bool success)
{
vty_mgmt_resume_response(rollback_vty, success);
vty_mgmt_resume_response(rollback_vty,
success ? CMD_SUCCESS
: CMD_WARNING_CONFIG_FAILED);
rollback_vty = NULL;
}

View File

@ -189,12 +189,33 @@ static void mgmt_vrf_terminate(void)
extern const struct frr_yang_module_info frr_staticd_info;
#endif
/*
* These are stub info structs that are used to load the modules used by backend
* clients into mgmtd. The modules are used by libyang in order to support
* parsing binary data returns from the backend.
*/
const struct frr_yang_module_info zebra_info = {
.name = "frr-zebra",
.ignore_cbs = true,
.nodes = { { .xpath = NULL } },
};
const struct frr_yang_module_info affinity_map_info = {
.name = "frr-affinity-map",
.ignore_cbs = true,
.nodes = { { .xpath = NULL } },
};
const struct frr_yang_module_info zebra_route_map_info = {
.name = "frr-zebra-route-map",
.ignore_cbs = true,
.nodes = { { .xpath = NULL } },
};
/*
* List of YANG modules to be loaded in the process context of
* MGMTd.
*
* NOTE: In future this will also include the YANG modules of
* all individual Backend clients.
*/
static const struct frr_yang_module_info *const mgmt_yang_modules[] = {
&frr_filter_info,
@ -202,11 +223,15 @@ static const struct frr_yang_module_info *const mgmt_yang_modules[] = {
&frr_route_map_info,
&frr_routing_info,
&frr_vrf_info,
/*
* YANG module info supported by backend clients get added here.
* NOTE: Always set .ignore_cbs true for to avoid validating
* backend configuration northbound callbacks during loading.
*/
/*
* YANG module info used by backend clients get added here.
*/
&zebra_info,
&affinity_map_info,
&zebra_route_map_info,
#ifdef HAVE_STATICD
&frr_staticd_info,
#endif

View File

@ -29,5 +29,6 @@ DEFINE_MTYPE(MGMTD, MGMTD_TXN_SETCFG_REQ, "txn set-config requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_COMMCFG_REQ, "txn commit-config requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REQ, "txn get-data requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REPLY, "txn get-data replies");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETTREE_REQ, "txn get-tree requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_CFG_BATCH, "txn config batches");
DEFINE_MTYPE(MGMTD, MGMTD_CMT_INFO, "commit info");

View File

@ -23,6 +23,7 @@ DECLARE_MTYPE(MGMTD_TXN_SETCFG_REQ);
DECLARE_MTYPE(MGMTD_TXN_COMMCFG_REQ);
DECLARE_MTYPE(MGMTD_TXN_GETDATA_REQ);
DECLARE_MTYPE(MGMTD_TXN_GETDATA_REPLY);
DECLARE_MTYPE(MGMTD_TXN_GETTREE_REQ);
DECLARE_MTYPE(MGMTD_TXN_CFG_BATCH);
DECLARE_MTYPE(MGMTD_BE_ADAPTER_MSG_BUF);
DECLARE_MTYPE(MGMTD_CMT_INFO);

View File

@ -7,9 +7,12 @@
*/
#include <zebra.h>
#include "darr.h"
#include "hash.h"
#include "jhash.h"
#include "libfrr.h"
#include "mgmt_msg.h"
#include "mgmt_msg_native.h"
#include "mgmtd/mgmt.h"
#include "mgmtd/mgmt_memory.h"
#include "mgmtd/mgmt_txn.h"
@ -26,8 +29,9 @@ enum mgmt_txn_event {
MGMTD_TXN_PROC_SETCFG = 1,
MGMTD_TXN_PROC_COMMITCFG,
MGMTD_TXN_PROC_GETCFG,
MGMTD_TXN_PROC_GETDATA,
MGMTD_TXN_PROC_GETTREE,
MGMTD_TXN_COMMITCFG_TIMEOUT,
MGMTD_TXN_GETTREE_TIMEOUT,
MGMTD_TXN_CLEANUP
};
@ -166,6 +170,16 @@ struct mgmt_get_data_req {
int total_reply;
};
struct txn_req_get_tree {
char *xpath; /* xpath of tree to get */
uint8_t result_type; /* LYD_FORMAT for results */
uint64_t sent_clients; /* Bitmask of clients sent req to */
uint64_t recv_clients; /* Bitmask of clients recv reply from */
int32_t partial_error; /* an error while gather results */
struct lyd_node *client_results; /* result tree from clients */
};
struct mgmt_txn_req {
struct mgmt_txn_ctx *txn;
enum mgmt_txn_event req_event;
@ -173,6 +187,7 @@ struct mgmt_txn_req {
union {
struct mgmt_set_cfg_req *set_cfg;
struct mgmt_get_data_req *get_data;
struct txn_req_get_tree *get_tree;
struct mgmt_commit_cfg_req commit_cfg;
} req;
@ -196,7 +211,9 @@ struct mgmt_txn_ctx {
struct event *proc_comm_cfg;
struct event *proc_get_cfg;
struct event *proc_get_data;
struct event *proc_get_tree;
struct event *comm_cfg_timeout;
struct event *get_tree_timeout;
struct event *clnup;
/* List of backend adapters involved in this transaction */
@ -206,6 +223,10 @@ struct mgmt_txn_ctx {
struct mgmt_txns_item list_linkage;
/* TODO: why do we need unique lists for each type of transaction since
* a transaction is of only 1 type?
*/
/*
* List of pending set-config requests for a given
* transaction/session. Just one list for requests
@ -221,13 +242,9 @@ struct mgmt_txn_ctx {
*/
struct mgmt_txn_reqs_head get_cfg_reqs;
/*
* List of pending get-data requests for a given
* transaction/session Two lists, one for requests
* not processed at all, and one for requests that
* has been sent to backend for processing.
* List of pending get-tree requests.
*/
struct mgmt_txn_reqs_head get_data_reqs;
struct mgmt_txn_reqs_head pending_get_datas;
struct mgmt_txn_reqs_head get_tree_reqs;
/*
* There will always be one commit-config allowed for a given
* transaction/session. No need to maintain lists for it.
@ -386,17 +403,16 @@ static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn,
" txn-id: %" PRIu64 " session-id: %" PRIu64,
txn_req->req_id, txn->txn_id, txn->session_id);
break;
case MGMTD_TXN_PROC_GETDATA:
txn_req->req.get_data =
XCALLOC(MTYPE_MGMTD_TXN_GETDATA_REQ,
sizeof(struct mgmt_get_data_req));
assert(txn_req->req.get_data);
mgmt_txn_reqs_add_tail(&txn->get_data_reqs, txn_req);
MGMTD_TXN_DBG("Added a new GETDATA req-id: %" PRIu64
case MGMTD_TXN_PROC_GETTREE:
txn_req->req.get_tree = XCALLOC(MTYPE_MGMTD_TXN_GETTREE_REQ,
sizeof(struct txn_req_get_tree));
mgmt_txn_reqs_add_tail(&txn->get_tree_reqs, txn_req);
MGMTD_TXN_DBG("Added a new GETTREE req-id: %" PRIu64
" txn-id: %" PRIu64 " session-id: %" PRIu64,
txn_req->req_id, txn->txn_id, txn->session_id);
break;
case MGMTD_TXN_COMMITCFG_TIMEOUT:
case MGMTD_TXN_GETTREE_TIMEOUT:
case MGMTD_TXN_CLEANUP:
break;
}
@ -496,24 +512,17 @@ static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req)
XFREE(MTYPE_MGMTD_TXN_GETDATA_REQ, (*txn_req)->req.get_data);
break;
case MGMTD_TXN_PROC_GETDATA:
for (indx = 0; indx < (*txn_req)->req.get_data->num_xpaths;
indx++) {
if ((*txn_req)->req.get_data->xpaths[indx])
free((void *)(*txn_req)
->req.get_data->xpaths[indx]);
}
pending_list = &(*txn_req)->txn->pending_get_datas;
req_list = &(*txn_req)->txn->get_data_reqs;
MGMTD_TXN_DBG("Deleting GETDATA req-id: %" PRIu64
" txn-id: %" PRIu64,
case MGMTD_TXN_PROC_GETTREE:
MGMTD_TXN_DBG("Deleting GETTREE req-id: %" PRIu64
" of txn-id: %" PRIu64,
(*txn_req)->req_id, (*txn_req)->txn->txn_id);
if ((*txn_req)->req.get_data->reply)
XFREE(MTYPE_MGMTD_TXN_GETDATA_REPLY,
(*txn_req)->req.get_data->reply);
XFREE(MTYPE_MGMTD_TXN_GETDATA_REQ, (*txn_req)->req.get_data);
req_list = &(*txn_req)->txn->get_tree_reqs;
lyd_free_all((*txn_req)->req.get_tree->client_results);
XFREE(MTYPE_MGMTD_XPATH, (*txn_req)->req.get_tree->xpath);
XFREE(MTYPE_MGMTD_TXN_GETTREE_REQ, (*txn_req)->req.get_tree);
break;
case MGMTD_TXN_COMMITCFG_TIMEOUT:
case MGMTD_TXN_GETTREE_TIMEOUT:
case MGMTD_TXN_CLEANUP:
break;
}
@ -1260,6 +1269,66 @@ static void mgmt_txn_cfg_commit_timedout(struct event *thread)
"Operation on the backend timed-out. Aborting commit!");
}
static int txn_get_tree_data_done(struct mgmt_txn_ctx *txn,
struct mgmt_txn_req *txn_req)
{
struct txn_req_get_tree *get_tree = txn_req->req.get_tree;
int ret = 0;
/* cancel timer and send reply onward */
EVENT_OFF(txn->get_tree_timeout);
ret = mgmt_fe_adapter_send_tree_data(txn->session_id, txn->txn_id,
txn_req->req_id,
get_tree->result_type,
get_tree->client_results,
get_tree->partial_error, false);
/* we're done with the request */
mgmt_txn_req_free(&txn_req);
if (ret) {
MGMTD_TXN_ERR("Error saving the results of GETTREE for txn-id %" PRIu64
" req_id %" PRIu64 " to requested type %u",
txn->txn_id, txn_req->req_id,
get_tree->result_type);
(void)mgmt_fe_adapter_txn_error(txn->txn_id, txn_req->req_id,
false, ret,
"Error converting results of GETTREE");
}
return ret;
}
static void txn_get_tree_timeout(struct event *thread)
{
struct mgmt_txn_ctx *txn;
struct mgmt_txn_req *txn_req;
txn_req = (struct mgmt_txn_req *)EVENT_ARG(thread);
txn = txn_req->txn;
assert(txn);
assert(txn->type == MGMTD_TXN_TYPE_SHOW);
MGMTD_TXN_ERR("Backend timeout txn-id: %" PRIu64 " ending get-tree",
txn->txn_id);
/*
* Send a get-tree data reply.
*
* NOTE: The transaction cleanup will be triggered from Front-end
* adapter.
*/
txn_req->req.get_tree->partial_error = -ETIMEDOUT;
txn_get_tree_data_done(txn, txn_req);
}
/*
* Send CFG_APPLY_REQs to all the backend client.
*
@ -1474,20 +1543,10 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req,
txn_req->txn->session_id, txn_req->req_id);
}
break;
case MGMTD_TXN_PROC_GETDATA:
if (mgmt_fe_send_get_reply(txn_req->txn->session_id,
txn_req->txn->txn_id, get_req->ds_id,
txn_req->req_id, MGMTD_SUCCESS,
data_reply, NULL) != 0) {
MGMTD_TXN_ERR("Failed to send GET-DATA-REPLY txn-id: %" PRIu64
" session-id: %" PRIu64
" req-id: %" PRIu64,
txn_req->txn->txn_id,
txn_req->txn->session_id, txn_req->req_id);
}
break;
case MGMTD_TXN_PROC_SETCFG:
case MGMTD_TXN_PROC_COMMITCFG:
case MGMTD_TXN_PROC_GETTREE:
case MGMTD_TXN_GETTREE_TIMEOUT:
case MGMTD_TXN_COMMITCFG_TIMEOUT:
case MGMTD_TXN_CLEANUP:
MGMTD_TXN_ERR("Invalid Txn-Req-Event %u", txn_req->req_event);
@ -1500,10 +1559,8 @@ static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req,
mgmt_reset_get_data_reply_buf(get_req);
}
static void mgmt_txn_iter_and_send_get_cfg_reply(const char *xpath,
struct lyd_node *node,
struct nb_node *nb_node,
void *ctx)
static void txn_iter_get_config_data_cb(const char *xpath, struct lyd_node *node,
struct nb_node *nb_node, void *ctx)
{
struct mgmt_txn_req *txn_req;
struct mgmt_get_data_req *get_req;
@ -1518,8 +1575,7 @@ static void mgmt_txn_iter_and_send_get_cfg_reply(const char *xpath,
if (!(node->schema->nodetype & LYD_NODE_TERM))
return;
assert(txn_req->req_event == MGMTD_TXN_PROC_GETCFG ||
txn_req->req_event == MGMTD_TXN_PROC_GETDATA);
assert(txn_req->req_event == MGMTD_TXN_PROC_GETCFG);
get_req = txn_req->req.get_data;
assert(get_req);
@ -1581,7 +1637,7 @@ static int mgmt_txn_get_config(struct mgmt_txn_ctx *txn,
*/
if (mgmt_ds_iter_data(get_data->ds_id, root,
get_data->xpaths[indx],
mgmt_txn_iter_and_send_get_cfg_reply,
txn_iter_get_config_data_cb,
(void *)txn_req) == -1) {
MGMTD_TXN_DBG("Invalid Xpath '%s",
get_data->xpaths[indx]);
@ -1664,54 +1720,6 @@ static void mgmt_txn_process_get_cfg(struct event *thread)
}
}
static void mgmt_txn_process_get_data(struct event *thread)
{
struct mgmt_txn_ctx *txn;
struct mgmt_txn_req *txn_req;
int num_processed = 0;
txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread);
assert(txn);
MGMTD_TXN_DBG("Processing %zu GET_DATA requests txn-id: %" PRIu64
" session-id: %" PRIu64,
mgmt_txn_reqs_count(&txn->get_data_reqs), txn->txn_id,
txn->session_id);
FOREACH_TXN_REQ_IN_LIST (&txn->get_data_reqs, txn_req) {
assert(txn_req->req_event == MGMTD_TXN_PROC_GETDATA);
/*
* TODO: Trigger GET procedures for Backend
* For now return back error.
*/
mgmt_fe_send_get_reply(txn->session_id, txn->txn_id,
txn_req->req.get_data->ds_id,
txn_req->req_id, MGMTD_INTERNAL_ERROR,
NULL, "GET-DATA is not supported yet!");
/*
* Delete the txn request.
* Note: The following will remove it from the list
* as well.
*/
mgmt_txn_req_free(&txn_req);
/*
* Else the transaction would have been already deleted or
* moved to corresponding pending list. No need to delete it.
*/
num_processed++;
if (num_processed == MGMTD_TXN_MAX_NUM_GETDATA_PROC)
break;
}
if (mgmt_txn_reqs_count(&txn->get_data_reqs)) {
MGMTD_TXN_DBG("Processed maximum number of Get-Data requests (%d/%d). Rescheduling for rest.",
num_processed, MGMTD_TXN_MAX_NUM_GETDATA_PROC);
mgmt_txn_register_event(txn, MGMTD_TXN_PROC_GETDATA);
}
}
static struct mgmt_txn_ctx *
mgmt_fe_find_txn_by_session_id(struct mgmt_master *cm, uint64_t session_id,
enum mgmt_txn_type type)
@ -1733,7 +1741,7 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id,
/*
* For 'CONFIG' transaction check if one is already created
* or not.
* or not. TODO: figure out what code counts on this and fix it.
*/
if (type == MGMTD_TXN_TYPE_CONFIG && mgmt_txn_mm->cfg_txn) {
if (mgmt_config_txn_in_progress() == session_id)
@ -1749,10 +1757,10 @@ static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id,
txn->session_id = session_id;
txn->type = type;
mgmt_txns_add_tail(&mgmt_txn_mm->txn_list, txn);
/* TODO: why do we need N lists for one transaction */
mgmt_txn_reqs_init(&txn->set_cfg_reqs);
mgmt_txn_reqs_init(&txn->get_cfg_reqs);
mgmt_txn_reqs_init(&txn->get_data_reqs);
mgmt_txn_reqs_init(&txn->pending_get_datas);
mgmt_txn_reqs_init(&txn->get_tree_reqs);
txn->commit_cfg_req = NULL;
txn->refcount = 0;
if (!mgmt_txn_mm->next_txn_id)
@ -1834,6 +1842,13 @@ static inline struct mgmt_txn_ctx *mgmt_txn_id2ctx(uint64_t txn_id)
return txn;
}
uint64_t mgmt_txn_get_session_id(uint64_t txn_id)
{
struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id);
return txn ? txn->session_id : MGMTD_SESSION_ID_NONE;
}
static void mgmt_txn_lock(struct mgmt_txn_ctx *txn, const char *file, int line)
{
txn->refcount++;
@ -1859,6 +1874,7 @@ static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file,
EVENT_OFF((*txn)->proc_get_data);
EVENT_OFF((*txn)->proc_comm_cfg);
EVENT_OFF((*txn)->comm_cfg_timeout);
EVENT_OFF((*txn)->get_tree_timeout);
hash_release(mgmt_txn_mm->txn_hash, *txn);
mgmt_txns_del(&mgmt_txn_mm->txn_list, *txn);
@ -1922,19 +1938,24 @@ static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn,
event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_get_cfg, txn,
&tv, &txn->proc_get_cfg);
break;
case MGMTD_TXN_PROC_GETDATA:
event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_get_data, txn,
&tv, &txn->proc_get_data);
break;
case MGMTD_TXN_COMMITCFG_TIMEOUT:
event_add_timer_msec(mgmt_txn_tm, mgmt_txn_cfg_commit_timedout,
txn, MGMTD_TXN_CFG_COMMIT_MAX_DELAY_MSEC,
&txn->comm_cfg_timeout);
event_add_timer(mgmt_txn_tm, mgmt_txn_cfg_commit_timedout, txn,
MGMTD_TXN_CFG_COMMIT_MAX_DELAY_SEC,
&txn->comm_cfg_timeout);
break;
case MGMTD_TXN_GETTREE_TIMEOUT:
event_add_timer(mgmt_txn_tm, txn_get_tree_timeout, txn,
MGMTD_TXN_GET_TREE_MAX_DELAY_SEC,
&txn->get_tree_timeout);
break;
case MGMTD_TXN_CLEANUP:
tv.tv_usec = MGMTD_TXN_CLEANUP_DELAY_USEC;
event_add_timer_tv(mgmt_txn_tm, mgmt_txn_cleanup, txn, &tv,
&txn->clnup);
break;
case MGMTD_TXN_PROC_GETTREE:
assert(!"code bug do not register this event");
break;
}
}
@ -2314,8 +2335,7 @@ int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id,
if (!txn)
return -1;
req_event = cfg_root ? MGMTD_TXN_PROC_GETCFG : MGMTD_TXN_PROC_GETDATA;
req_event = MGMTD_TXN_PROC_GETCFG;
txn_req = mgmt_txn_req_alloc(txn, req_id, req_event);
txn_req->req.get_data->ds_id = ds_id;
txn_req->req.get_data->cfg_root = cfg_root;
@ -2333,6 +2353,203 @@ int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id,
return 0;
}
/**
* Send get-tree requests to each client indicated in `clients` bitmask, which
* has registered operational state that matches the given `xpath`
*/
int mgmt_txn_send_get_tree_oper(uint64_t txn_id, uint64_t req_id,
uint64_t clients, LYD_FORMAT result_type,
const char *xpath)
{
struct mgmt_msg_get_tree *msg;
struct mgmt_txn_ctx *txn;
struct mgmt_txn_req *txn_req;
struct txn_req_get_tree *get_tree;
enum mgmt_be_client_id id;
ssize_t slen = strlen(xpath);
int ret;
txn = mgmt_txn_id2ctx(txn_id);
if (!txn)
return -1;
/* If error in this function below here, be sure to free the req */
txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_GETTREE);
get_tree = txn_req->req.get_tree;
get_tree->result_type = result_type;
get_tree->xpath = XSTRDUP(MTYPE_MGMTD_XPATH, xpath);
msg = mgmt_msg_native_alloc_msg(struct mgmt_msg_get_tree, slen + 1,
MTYPE_MSG_NATIVE_GET_TREE);
msg->refer_id = txn_id;
msg->req_id = req_id;
msg->code = MGMT_MSG_CODE_GET_TREE;
/* Always operate with the binary format in the backend */
msg->result_type = LYD_LYB;
strlcpy(msg->xpath, xpath, slen + 1);
assert(clients);
FOREACH_BE_CLIENT_BITS (id, clients) {
ret = mgmt_be_send_native(id, msg);
if (ret) {
MGMTD_TXN_ERR("Could not send get-tree message to backend client %s",
mgmt_be_client_id2name(id));
continue;
}
MGMTD_TXN_DBG("Sent get-tree req to backend client %s",
mgmt_be_client_id2name(id));
/* record that we sent the request to the client */
get_tree->sent_clients |= (1u << id);
}
mgmt_msg_native_free_msg(msg);
/* Start timeout timer - pulled out of register event code so we can
* pass a different arg
*/
event_add_timer(mgmt_txn_tm, txn_get_tree_timeout, txn_req,
MGMTD_TXN_GET_TREE_MAX_DELAY_SEC,
&txn->get_tree_timeout);
return 0;
}
/*
* Error reply from the backend client.
*/
int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter,
uint64_t txn_id, uint64_t req_id, int error,
const char *errstr)
{
enum mgmt_be_client_id id = adapter->id;
struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id);
struct txn_req_get_tree *get_tree;
struct mgmt_txn_req *txn_req;
if (!txn) {
MGMTD_TXN_ERR("Error reply from %s cannot find txn-id %" PRIu64,
adapter->name, txn_id);
return -1;
}
/* Find the request. */
FOREACH_TXN_REQ_IN_LIST (&txn->get_tree_reqs, txn_req)
if (txn_req->req_id == req_id)
break;
if (!txn_req) {
MGMTD_TXN_ERR("Error reply from %s for txn-id %" PRIu64
" cannot find req_id %" PRIu64,
adapter->name, txn_id, req_id);
return -1;
}
MGMTD_TXN_ERR("Error reply from %s for txn-id %" PRIu64
" req_id %" PRIu64,
adapter->name, txn_id, req_id);
switch (txn_req->req_event) {
case MGMTD_TXN_PROC_GETTREE:
get_tree = txn_req->req.get_tree;
get_tree->recv_clients |= (1u << id);
get_tree->partial_error = error;
/* check if done yet */
if (get_tree->recv_clients != get_tree->sent_clients)
return 0;
return txn_get_tree_data_done(txn, txn_req);
/* non-native message events */
case MGMTD_TXN_PROC_SETCFG:
case MGMTD_TXN_PROC_COMMITCFG:
case MGMTD_TXN_PROC_GETCFG:
case MGMTD_TXN_COMMITCFG_TIMEOUT:
case MGMTD_TXN_GETTREE_TIMEOUT:
case MGMTD_TXN_CLEANUP:
default:
assert(!"non-native req event in native erorr path");
return -1;
}
}
/*
* Get-tree data from the backend client.
*/
int mgmt_txn_notify_tree_data_reply(struct mgmt_be_client_adapter *adapter,
struct mgmt_msg_tree_data *data_msg,
size_t msg_len)
{
uint64_t txn_id = data_msg->refer_id;
uint64_t req_id = data_msg->req_id;
enum mgmt_be_client_id id = adapter->id;
struct mgmt_txn_ctx *txn = mgmt_txn_id2ctx(txn_id);
struct mgmt_txn_req *txn_req;
struct txn_req_get_tree *get_tree;
struct lyd_node *tree = NULL;
LY_ERR err;
if (!txn) {
MGMTD_TXN_ERR("GETTREE reply from %s for a missing txn-id %" PRIu64,
adapter->name, txn_id);
return -1;
}
/* Find the request. */
FOREACH_TXN_REQ_IN_LIST (&txn->get_tree_reqs, txn_req)
if (txn_req->req_id == req_id)
break;
if (!txn_req) {
MGMTD_TXN_ERR("GETTREE reply from %s for txn-id %" PRIu64
" missing req_id %" PRIu64,
adapter->name, txn_id, req_id);
return -1;
}
get_tree = txn_req->req.get_tree;
/* store the result */
err = lyd_parse_data_mem(ly_native_ctx, (const char *)data_msg->result,
data_msg->result_type,
LYD_PARSE_STRICT | LYD_PARSE_ONLY,
0 /*LYD_VALIDATE_OPERATIONAL*/, &tree);
if (err) {
MGMTD_TXN_ERR("GETTREE reply from %s for txn-id %" PRIu64
" req_id %" PRIu64
" error parsing result of type %u",
adapter->name, txn_id, req_id,
data_msg->result_type);
}
if (!err) {
/* TODO: we could merge ly_errs here if it's not binary */
if (!get_tree->client_results)
get_tree->client_results = tree;
else
err = lyd_merge_siblings(&get_tree->client_results,
tree, LYD_MERGE_DESTRUCT);
if (err) {
MGMTD_TXN_ERR("GETTREE reply from %s for txn-id %" PRIu64
" req_id %" PRIu64 " error merging result",
adapter->name, txn_id, req_id);
}
}
if (!get_tree->partial_error)
get_tree->partial_error = (data_msg->partial_error
? data_msg->partial_error
: (int)err);
if (!data_msg->more)
get_tree->recv_clients |= (1u << id);
/* check if done yet */
if (get_tree->recv_clients != get_tree->sent_clients)
return 0;
return txn_get_tree_data_done(txn, txn_req);
}
void mgmt_txn_status_write(struct vty *vty)
{
struct mgmt_txn_ctx *txn;

View File

@ -9,21 +9,19 @@
#ifndef _FRR_MGMTD_TXN_H_
#define _FRR_MGMTD_TXN_H_
#include "lib/mgmt_msg_native.h"
#include "mgmtd/mgmt_be_adapter.h"
#include "mgmtd/mgmt.h"
#include "mgmtd/mgmt_ds.h"
#define MGMTD_TXN_PROC_DELAY_MSEC 5
#define MGMTD_TXN_PROC_DELAY_USEC 10
#define MGMTD_TXN_MAX_NUM_SETCFG_PROC 128
#define MGMTD_TXN_MAX_NUM_GETCFG_PROC 128
#define MGMTD_TXN_MAX_NUM_GETDATA_PROC 128
#define MGMTD_TXN_SEND_CFGVALIDATE_DELAY_MSEC 100
#define MGMTD_TXN_SEND_CFGAPPLY_DELAY_MSEC 100
#define MGMTD_TXN_CFG_COMMIT_MAX_DELAY_MSEC 30000 /* 30 seconds */
#define MGMTD_TXN_CFG_COMMIT_MAX_DELAY_SEC 600
#define MGMTD_TXN_GET_TREE_MAX_DELAY_SEC 600
#define MGMTD_TXN_CLEANUP_DELAY_MSEC 100
#define MGMTD_TXN_CLEANUP_DELAY_USEC 10
#define MGMTD_TXN_ID_NONE 0
@ -80,6 +78,12 @@ extern void mgmt_txn_destroy(void);
*/
extern uint64_t mgmt_config_txn_in_progress(void);
/**
* Get the session ID associated with the given ``txn-id``.
*
*/
extern uint64_t mgmt_txn_get_session_id(uint64_t txn_id);
/*
* Create transaction.
*
@ -190,6 +194,23 @@ extern int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id,
Mgmtd__YangGetDataReq **data_req,
size_t num_reqs);
/**
* Send get-tree to the backend `clients`.
*
* Args:
* txn_id: Transaction identifier.
* req_id: FE client request identifier.
* clients: Bitmask of clients to send get-tree to.
* result_type: LYD_FORMAT result format.
* xpath: The xpath to get the tree from.
* Return:
* 0 on success.
*/
extern int mgmt_txn_send_get_tree_oper(uint64_t txn_id, uint64_t req_id,
uint64_t clients, LYD_FORMAT result_type,
const char *xpath);
/*
* Notifiy backend adapter on connection.
*/
@ -228,6 +249,34 @@ mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success,
char *error_if_any,
struct mgmt_be_client_adapter *adapter);
/**
* Process a reply from a backend client to our get-tree request
*
* Args:
* adapter: The adapter that received the result.
* txn_id: The transaction for this get-tree request.
* req_id: The request ID for this transaction.
* error: the integer error value (negative)
* errstr: the string description of the error.
*/
int mgmt_txn_notify_error(struct mgmt_be_client_adapter *adapter,
uint64_t txn_id, uint64_t req_id, int error,
const char *errstr);
/**
* Process a reply from a backend client to our get-tree request
*
* Args:
* adapter: The adapter that received the result.
* data_msg: The message from the backend.
* msg_len: Total length of the message.
*/
extern int mgmt_txn_notify_tree_data_reply(struct mgmt_be_client_adapter *adapter,
struct mgmt_msg_tree_data *data_msg,
size_t msg_len);
/*
* Dump transaction status to vty.
*/

View File

@ -199,22 +199,32 @@ DEFPY(show_mgmt_get_config, show_mgmt_get_config_cmd,
}
DEFPY(show_mgmt_get_data, show_mgmt_get_data_cmd,
"show mgmt get-data [candidate|operational|running]$dsname WORD$path",
SHOW_STR MGMTD_STR
"Get data from a specific datastore\n"
"Candidate datastore\n"
"Operational datastore (default)\n"
"Running datastore\n"
"XPath expression specifying the YANG data path\n")
"show mgmt get-data WORD$path [json|xml]$fmt",
SHOW_STR
MGMTD_STR
"Get a data from the operational datastore\n"
"XPath expression specifying the YANG data root\n"
"JSON output format\n"
"XML output format\n")
{
const char *xpath_list[VTY_MAXCFGCHANGES] = {0};
Mgmtd__DatastoreId datastore = MGMTD_DS_OPERATIONAL;
LYD_FORMAT format = (fmt && fmt[0] == 'x') ? LYD_XML : LYD_JSON;
int plen = strlen(path);
char *xpath = NULL;
if (dsname)
datastore = mgmt_ds_name2id(dsname);
/* get rid of extraneous trailing slash-* or single '/' unless root */
if (plen > 2 && ((path[plen - 2] == '/' && path[plen - 1] == '*') ||
(path[plen - 2] != '/' && path[plen - 1] == '/'))) {
plen = path[plen - 1] == '/' ? plen - 1 : plen - 2;
xpath = XSTRDUP(MTYPE_TMP, path);
xpath[plen] = 0;
path = xpath;
}
vty_mgmt_send_get_tree_req(vty, format, path);
if (xpath)
XFREE(MTYPE_TMP, xpath);
xpath_list[0] = path;
vty_mgmt_send_get_req(vty, false, datastore, xpath_list, 1);
return CMD_SUCCESS;
}

View File

@ -37,7 +37,7 @@ daemon_flags = {
"lib/filter_cli.c": "VTYSH_ACL",
"lib/if.c": "VTYSH_INTERFACE",
"lib/keychain.c": "VTYSH_KEYS",
"lib/mgmt_be_client.c": "VTYSH_STATICD",
"lib/mgmt_be_client.c": "VTYSH_STATICD|VTYSH_ZEBRA",
"lib/mgmt_fe_client.c": "VTYSH_MGMTD",
"lib/lib_vty.c": "VTYSH_ALL",
"lib/log_vty.c": "VTYSH_ALL",

View File

@ -162,6 +162,7 @@ tests_lib_test_darr_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_darr_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_darr_LDADD = $(ALL_TESTS_LDADD)
tests_lib_test_darr_SOURCES = tests/lib/test_darr.c
EXTRA_DIST += tests/lib/test_darr.py
check_PROGRAMS += tests/lib/test_graph

View File

@ -14,7 +14,8 @@
* [x] - darr_append_n
* [x] - darr_append_nz
* [x] - darr_cap
* [-] - darr_ensure_cap
* [x] - darr_ensure_avail
* [x] - darr_ensure_cap
* [x] - darr_ensure_i
* [x] - darr_foreach_i
* [x] - darr_foreach_p
@ -23,6 +24,15 @@
* [ ] - darr_insertz
* [x] - darr_insert_n
* [x] - darr_insert_nz
* [x] - darr_in_sprintf
* [x] - darr_in_strcat
* [x] - darr_in_strcat_tail
* [ ] - darr_in_strcatf
* [ ] - darr_in_vstrcatf
* [x] - darr_in_strdup
* [x] - darr_in_strdup_cap
* [-] - darr_in_vsprintf
* [x] - darr_lasti
* [x] - darr_maxi
* [x] - darr_pop
* [x] - darr_push
@ -31,6 +41,13 @@
* [x] - darr_remove_n
* [x] - darr_reset
* [x] - darr_setlen
* [x] - darr_set_strlen
* [x] - darr_sprintf
* [x] - darr_strdup
* [x] - darr_strdup_cap
* [x] - darr_strlen
* [x] - darr_strnul
* [ ] - darr_vsprintf
*/
static void test_int(void)
@ -43,6 +60,11 @@ static void test_int(void)
int *dap;
uint i;
assert(darr_len(da1) == 0);
assert(darr_lasti(da1) == -1);
assert(darr_last(da1) == NULL);
assert(darr_end(da1) == NULL);
darr_ensure_i(da1, 0);
da1[0] = 0;
assert(darr_len(da1) == 1);
@ -57,9 +79,11 @@ static void test_int(void)
da1[i] = i;
assert(darr_len(da1) == 5);
assert(darr_lasti(da1) == 4);
/* minimum non-pow2 array size for long long and smaller */
assert(darr_cap(da1) == 8);
assert(!memcmp(da1, a1, sizeof(a1)));
assert(&da1[darr_lasti(da1)] == darr_last(da1));
/* reverse the numbers */
darr_foreach_p (da1, dap)
@ -185,6 +209,20 @@ static void test_struct(void)
assert(darr_cap(da1) == 8);
assert(!memcmp(da1, a1, sizeof(a1)));
assert(darr_cap(da1) - darr_len(da1) == 3);
darr_ensure_avail(da1, 2);
assert(darr_cap(da1) == 8);
darr_ensure_avail(da1, 3);
assert(darr_cap(da1) == 8);
darr_ensure_avail(da1, 4);
assert(darr_cap(da1) == 16);
darr_ensure_cap(da1, 16);
assert(darr_cap(da1) == 16);
darr_ensure_cap(da1, 20);
assert(darr_cap(da1) == 32);
darr_append_n(da1, 100);
assert(darr_len(da1) == 105);
@ -272,8 +310,113 @@ static void test_struct(void)
darr_free(da2);
}
static void test_string(void)
{
const char *src = "ABCDE";
const char *add = "FGHIJ";
uint srclen = strlen(src);
uint addlen = strlen(add);
char *da1 = NULL;
char *da2 = NULL;
assert(darr_strlen(da1) == 0);
da1 = darr_strdup(src);
assert(darr_strlen(da1) == strlen(da1));
assert(darr_strlen(da1) == srclen);
assert(darr_len(da1) == srclen + 1);
assert(darr_ilen(da1) == (int)srclen + 1);
assert(darr_cap(da1) >= 8);
assert(darr_last(da1) == darr_strnul(da1));
assert(darr_strnul(da1) == da1 + darr_strlen(da1));
da2 = da1;
darr_in_strdup(da1, src);
assert(da1 == da2);
assert(darr_strlen(da1) == strlen(da1));
assert(darr_strlen(da1) == srclen);
assert(darr_len(da1) == srclen + 1);
darr_free(da1);
assert(da1 == NULL);
da1 = darr_strdup_cap(src, 128);
assert(darr_strlen(da1) == srclen);
assert(darr_cap(da1) >= 128);
darr_in_strdup_cap(da1, src, 256);
assert(darr_strlen(da1) == srclen);
assert(darr_cap(da1) >= 256);
darr_free(da1);
da1 = darr_strdup_cap(add, 2);
assert(darr_strlen(da1) == addlen);
assert(darr_cap(da1) >= 8);
darr_in_strdup(da1, "ab");
darr_in_strcat(da1, "/");
darr_in_strcat(da1, "foo");
assert(!strcmp("ab/foo", da1));
darr_free(da1);
da1 = darr_in_strcat(da1, "ab");
darr_in_strcat(da1, "/");
darr_in_strcat(da1, "foo");
assert(!strcmp("ab/foo", da1));
darr_set_strlen(da1, 5);
assert(!strcmp("ab/fo", da1));
darr_set_strlen(da1, 1);
assert(!strcmp("a", da1));
darr_in_strdup(da1, "ab");
da2 = darr_strdup(add);
darr_in_strcat_tail(da1, da2);
assert(!strcmp("abHIJ", da1));
assert(darr_strlen(da1) == 5);
assert(darr_len(da1) == 6);
darr_free(da1);
darr_free(da2);
da1 = darr_strdup("abcde");
da2 = darr_strdup(add);
darr_in_strcat_tail(da1, da2);
assert(!strcmp("abcde", da1));
assert(darr_strlen(da1) == 5);
assert(darr_len(da1) == 6);
darr_free(da1);
darr_free(da2);
da1 = darr_sprintf("0123456789: %08X", 0xDEADBEEF);
assert(!strcmp(da1, "0123456789: DEADBEEF"));
assert(darr_strlen(da1) == 20);
assert(darr_cap(da1) == 128);
da2 = da1;
darr_in_sprintf(da1, "9876543210: %08x", 0x0BADF00D);
assert(da1 == da2);
assert(!strcmp("9876543210: 0badf00d", da2));
darr_free(da1);
da2 = NULL;
da1 = NULL;
darr_in_sprintf(da1, "0123456789: %08X", 0xDEADBEEF);
assert(!strcmp(da1, "0123456789: DEADBEEF"));
assert(darr_strlen(da1) == 20);
assert(darr_cap(da1) == 128);
darr_free(da1);
da1 = darr_sprintf("0123456789: %08x", 0xDEADBEEF);
darr_in_strcatf(da1, " 9876543210: %08x", 0x0BADF00D);
assert(!strcmp("0123456789: deadbeef 9876543210: 0badf00d", da1));
darr_free(da1);
da1 = darr_in_strcatf(da1, "0123456789: %08x", 0xDEADBEEF);
assert(!strcmp("0123456789: deadbeef", da1));
darr_free(da1);
}
int main(int argc, char **argv)
{
test_int();
test_struct();
test_string();
}

8
tests/lib/test_darr.py Normal file
View File

@ -0,0 +1,8 @@
import frrtest
class TestDarr(frrtest.TestMultiOut):
program = "./test_darr"
TestDarr.exit_cleanly()

View File

@ -0,0 +1,103 @@
#!/usr/bin/env python
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: GPL-2.0-or-later
#
# November 27 2023, Christian Hopps <chopps@labn.net>
#
# Copyright (c) 2023, LabN Consulting, L.L.C.
#
# noqa: E501
#
import argparse
import errno
import logging
import os
import socket
import sys
import time
from pathlib import Path
import mgmt_pb2
MGMT_MSG_MARKER_PROTOBUF = b"\000###"
MGMT_MSG_MARKER_NATIVE = b"\001###"
def __parse_args():
MPATH = "/var/run/frr/mgmtd_fe.sock"
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", action="store_true", help="Be verbose")
parser.add_argument("--server", default=MPATH, help="path to server socket")
args = parser.parse_args()
level = logging.DEBUG if args.verbose else logging.INFO
logging.basicConfig(level=level, format="%(asctime)s %(levelname)s: %(message)s")
return args
def __server_connect(spath):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
logging.debug("Connecting to server on %s", spath)
while ec := sock.connect_ex(str(spath)):
logging.warn("retry server connection in .5s (%s)", os.strerror(ec))
time.sleep(0.5)
logging.info("Connected to server on %s", spath)
return sock
def mgmt_pb_recv_msg(sock, msg):
"""Receive a mgmtd protobuf message from a stream socket."""
marker = sock.recv(4)
assert marker in (MGMT_MSG_MARKER_PROTOBUF, MGMT_MSG_MARKER_NATIVE)
msize = int.from_bytes(sock.recv(4), byteorder="big")
mdata = sock.recv(msize)
msg.ParseFromString(mdata)
return msg
def mgmt_pb_send_msg(sock, msg):
"""Send a mgmtd protobuf message from a stream socket."""
marker = MGMT_MSG_MARKER_PROTOBUF
mdata = msg.SerializeToString()
msize = int.to_bytes(len(mdata), byteorder="big", length=4)
sock.send(marker)
sock.send(msize)
sock.send(mdata)
def create_session(sock):
req = mgmt_pb2.FeRegisterReq()
req.client_name = "test-client"
mgmt_pb_send_msg(sock, req)
logging.debug("Sent FeRegisterReq: %s", req)
req = mgmt_pb2.FeSessionReq()
req.create = 1
req.client_conn_id = 1
mgmt_pb_send_msg(sock, req)
logging.debug("Sent FeSessionReq: %s", req)
reply = mgmt_pb_recv_msg(sock, mgmt_pb2.FeSessionReply())
logging.debug("Received FeSessionReply: %s", reply)
def __main():
args = __parse_args()
sock = __server_connect(Path(args.server))
create_session(sock)
def main():
try:
__main()
except KeyboardInterrupt:
logging.info("Exiting")
except Exception as error:
logging.error("Unexpected error exiting: %s", error, exc_info=True)
if __name__ == "__main__":
main()

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
../mgmt_oper/oper.py

View File

@ -0,0 +1,23 @@
log timestamp precision 6
log file frr.log
no debug memstats-at-exit
debug northbound notifications
debug northbound libyang
debug northbound events
debug northbound callbacks
debug mgmt backend datastore frontend transaction
debug mgmt client frontend
debug mgmt client backend
interface r1-eth0
ip address 1.1.1.1/24
exit
interface r1-eth1 vrf red
ip address 3.3.3.1/24
exit
ip route 11.11.11.11/32 1.1.1.2
!ip route 13.13.13.13/32 3.3.3.2 vrf red

View File

@ -0,0 +1,49 @@
#!/usr/bin/env python
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: ISC
#
# Copyright (c) 2021, LabN Consulting, L.L.C.
# Copyright (c) 2019-2020 by
# Donatas Abraitis <donatas.abraitis@gmail.com>
#
# noqa: E501
#
"""
Test static route functionality
"""
import pytest
from lib.topogen import Topogen
from oper import check_kernel_32
pytestmark = [pytest.mark.staticd]
@pytest.fixture(scope="module")
def tgen(request):
"Setup/Teardown the environment and provide tgen argument to tests"
topodef = {"s1": ("r1",), "s2": ("r1",)}
tgen = Topogen(topodef, request.module.__name__)
tgen.start_topology()
router_list = tgen.routers()
for rname, router in router_list.items():
# Setup VRF red
router.net.add_l3vrf("red", 10)
router.net.add_loop("lo-red")
router.net.attach_iface_to_l3vrf("lo-red", "red")
router.net.attach_iface_to_l3vrf(rname + "-eth1", "red")
router.load_frr_config("frr.conf")
tgen.start_router()
yield tgen
tgen.stop_topology()
def test_oper_simple(tgen):
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
r1 = tgen.gears["r1"].net
check_kernel_32(r1, "11.11.11.11", 1, "")

View File

@ -0,0 +1,576 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"state": {
"id": "rubout",
"active": true
},
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.0.0.0/8",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "blackhole",
"vrf": "rubout",
"gateway": "",
"interface": " ",
"bh-type": "null",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "12.12.12.12/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "2.2.2.2",
"interface": "r1-eth1",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
},
{
"prefix": "2001:1111::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2001:1111::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
},
{
"prefix": "2001:1111::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2001:1111::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
}
]
}
}
}
]
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,576 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "red",
"state": {
"id": "rubout",
"active": true
},
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 10,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "3.3.3.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth2",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "3.3.3.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth2",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "4.4.4.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth3",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "4.4.4.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth3",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "13.0.0.0/8",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "blackhole",
"vrf": "rubout",
"gateway": "",
"interface": " ",
"bh-type": "null",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "13.13.13.13/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "3.3.3.2",
"interface": "r1-eth2",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "14.14.14.14/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "4.4.4.2",
"interface": "r1-eth3",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 10,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "3.3.3.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth2",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "3.3.3.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth2",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "4.4.4.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth3",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "4.4.4.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth3",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 10,
"route": [
{
"prefix": "::/0"
},
{
"prefix": "2003:333::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth2",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2003:333::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth2",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2004:4444::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth3",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2004:4444::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth3",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 10,
"route": [
{
"prefix": "::/0"
},
{
"prefix": "2003:333::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth2",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2003:333::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth2",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2004:4444::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth3",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2004:4444::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth3",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,572 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.0.0.0/8",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "blackhole",
"vrf": "rubout",
"gateway": "",
"interface": " ",
"bh-type": "null",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "12.12.12.12/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "2.2.2.2",
"interface": "r1-eth1",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
},
{
"prefix": "2001:1111::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2001:1111::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
},
{
"prefix": "2001:1111::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2001:1111::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,572 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.0.0.0/8",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "blackhole",
"vrf": "rubout",
"gateway": "",
"interface": " ",
"bh-type": "null",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "12.12.12.12/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "2.2.2.2",
"interface": "r1-eth1",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
},
{
"prefix": "2001:1111::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2001:1111::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
},
{
"prefix": "2001:1111::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2001:1111::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
}
]
}
}
}
]
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,225 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.0.0.0/8",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "blackhole",
"vrf": "rubout",
"gateway": "",
"interface": " ",
"bh-type": "null",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "12.12.12.12/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "2.2.2.2",
"interface": "r1-eth1",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,572 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.0.0.0/8",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "blackhole",
"vrf": "rubout",
"gateway": "",
"interface": " ",
"bh-type": "null",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
},
{
"prefix": "12.12.12.12/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "2.2.2.2",
"interface": "r1-eth1",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2.2.2.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
},
{
"prefix": "2001:1111::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2001:1111::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
},
{
"prefix": "2001:1111::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2001:1111::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::/64",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "2002:2222::1/128",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,258 @@
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: ISC
#
# October 29 2023, Christian Hopps <chopps@labn.net>
#
# Copyright (c) 2023, LabN Consulting, L.L.C.
#
import datetime
import ipaddress
import json
import logging
import math
import os
import pprint
import re
from lib.common_config import retry, step
from lib.topolog import logger
from lib.topotest import json_cmp as tt_json_cmp
try:
from deepdiff import DeepDiff as dd_json_cmp
except ImportError:
dd_json_cmp = None
def json_cmp(got, expect, exact_match):
if dd_json_cmp:
if exact_match:
deep_diff = dd_json_cmp(expect, got)
# Convert DeepDiff completely into dicts or lists at all levels
json_diff = json.loads(deep_diff.to_json())
else:
json_diff = dd_json_cmp(expect, got, ignore_order=True)
# Convert DeepDiff completely into dicts or lists at all levels
# json_diff = json.loads(deep_diff.to_json())
# Remove new fields in json object from diff
if json_diff.get("dictionary_item_added") is not None:
del json_diff["dictionary_item_added"]
# Remove new json objects in json array from diff
if (new_items := json_diff.get("iterable_item_added")) is not None:
new_item_paths = list(new_items.keys())
for path in new_item_paths:
if type(new_items[path]) is dict:
del new_items[path]
if len(new_items) == 0:
del json_diff["iterable_item_added"]
if not json_diff:
json_diff = None
else:
json_diff = tt_json_cmp(got, expect, exact_match)
json_diff = str(json_diff)
return json_diff
def enable_debug(router):
router.vtysh_cmd("debug northbound callbacks configuration")
def disable_debug(router):
router.vtysh_cmd("no debug northbound callbacks configuration")
def do_oper_test(tgen, query_results):
r1 = tgen.gears["r1"].net
qcmd = (
r"vtysh -c 'show mgmt get-data {}' "
r"""| sed -e 's/"phy-address": ".*"/"phy-address": "rubout"/'"""
r"""| sed -e 's/"uptime": ".*"/"uptime": "rubout"/'"""
r"""| sed -e 's/"vrf": "[0-9]*"/"vrf": "rubout"/'"""
r"""| sed -e 's/"if-index": [0-9][0-9]*/"if-index": "rubout"/'"""
r"""| sed -e 's/"id": [0-9][0-9]*/"id": "rubout"/'"""
)
doreset = True
dd_json_cmp = None
for qr in query_results:
step(f"Perform query '{qr[0]}'", reset=doreset)
if doreset:
doreset = False
expected = open(qr[1], encoding="ascii").read()
output = r1.cmd_nostatus(qcmd.format(qr[0]))
try:
ojson = json.loads(output)
except json.decoder.JSONDecodeError as error:
logging.error("Error decoding json: %s\noutput:\n%s", error, output)
raise
try:
ejson = json.loads(expected)
except json.decoder.JSONDecodeError as error:
logging.error(
"Error decoding json exp result: %s\noutput:\n%s", error, expected
)
raise
if dd_json_cmp:
cmpout = json_cmp(ojson, ejson, exact_match=True)
if cmpout:
logging.warning(
"-------DIFF---------\n%s\n---------DIFF----------",
pprint.pformat(cmpout),
)
else:
cmpout = tt_json_cmp(ojson, ejson, exact=True)
if cmpout:
logging.warning(
"-------EXPECT--------\n%s\n------END-EXPECT------",
json.dumps(ejson, indent=4),
)
logging.warning(
"--------GOT----------\n%s\n-------END-GOT--------",
json.dumps(ojson, indent=4),
)
assert cmpout is None
def get_ip_networks(super_prefix, count):
count_log2 = math.log(count, 2)
if count_log2 != int(count_log2):
count_log2 = int(count_log2) + 1
else:
count_log2 = int(count_log2)
network = ipaddress.ip_network(super_prefix)
return tuple(network.subnets(count_log2))[0:count]
@retry(retry_timeout=30, initial_wait=0.1)
def check_kernel(r1, super_prefix, count, add, is_blackhole, vrf, matchvia):
network = ipaddress.ip_network(super_prefix)
vrfstr = f" vrf {vrf}" if vrf else ""
if network.version == 6:
kernel = r1.cmd_raises(f"ip -6 route show{vrfstr}")
else:
kernel = r1.cmd_raises(f"ip -4 route show{vrfstr}")
# logger.debug("checking kernel routing table%s:\n%s", vrfstr, kernel)
for i, net in enumerate(get_ip_networks(super_prefix, count)):
if not add:
assert str(net) not in kernel
continue
if is_blackhole:
route = f"blackhole {str(net)} proto (static|196) metric 20"
else:
route = (
f"{str(net)}(?: nhid [0-9]+)? {matchvia} "
"proto (static|196) metric 20"
)
assert re.search(route, kernel), f"Failed to find \n'{route}'\n in \n'{kernel}'"
def addrgen(a, count, step=1):
for _ in range(0, count, step):
yield a
a += step
@retry(retry_timeout=30, initial_wait=0.1)
def check_kernel_32(r1, start_addr, count, vrf, step=1):
start = ipaddress.ip_address(start_addr)
vrfstr = f" vrf {vrf}" if vrf else ""
if start.version == 6:
kernel = r1.cmd_raises(f"ip -6 route show{vrfstr}")
else:
kernel = r1.cmd_raises(f"ip -4 route show{vrfstr}")
nentries = len(re.findall("\n", kernel))
logging.info("checking kernel routing table%s: (%s entries)", vrfstr, nentries)
for addr in addrgen(start, count, step):
assert str(addr) in kernel, f"Failed to find '{addr}' in {nentries} entries"
def do_config(
r1,
count,
add=True,
do_ipv6=False,
via=None,
vrf=None,
use_cli=False,
):
optype = "adding" if add else "removing"
iptype = "IPv6" if do_ipv6 else "IPv4"
#
# Set the route details
#
if vrf:
super_prefix = "2111::/48" if do_ipv6 else "111.0.0.0/8"
else:
super_prefix = "2055::/48" if do_ipv6 else "55.0.0.0/8"
matchvia = ""
if via == "blackhole":
pass
elif via:
matchvia = f"dev {via}"
else:
if vrf:
via = "2102::2" if do_ipv6 else "3.3.3.2"
matchvia = f"via {via} dev r1-eth1"
else:
via = "2101::2" if do_ipv6 else "1.1.1.2"
matchvia = f"via {via} dev r1-eth0"
vrfdbg = " in vrf {}".format(vrf) if vrf else ""
logger.debug("{} {} static {} routes{}".format(optype, count, iptype, vrfdbg))
#
# Generate config file in a retrievable place
#
config_file = os.path.join(
r1.logdir, r1.name, "{}-routes-{}.conf".format(iptype.lower(), optype)
)
with open(config_file, "w") as f:
if use_cli:
f.write("configure terminal\n")
if vrf:
f.write("vrf {}\n".format(vrf))
for i, net in enumerate(get_ip_networks(super_prefix, count)):
if add:
f.write("ip route {} {}\n".format(net, via))
else:
f.write("no ip route {} {}\n".format(net, via))
#
# Load config file.
#
if use_cli:
load_command = 'vtysh < "{}"'.format(config_file)
else:
load_command = 'vtysh -f "{}"'.format(config_file)
tstamp = datetime.datetime.now()
output = r1.cmd_raises(load_command)
delta = (datetime.datetime.now() - tstamp).total_seconds()
#
# Verify the results are in the kernel
#
check_kernel(r1, super_prefix, count, add, via == "blackhole", vrf, matchvia)
optyped = "added" if add else "removed"
logger.debug(
"{} {} {} static routes under {}{} in {}s".format(
optyped, count, iptype.lower(), super_prefix, vrfdbg, delta
)
)

View File

@ -0,0 +1,25 @@
log timestamp precision 6
log file frr.log
no debug memstats-at-exit
! debug northbound libyang
! debug northbound callbacks
debug northbound notifications
debug northbound events
debug mgmt backend datastore frontend transaction
debug mgmt client frontend
debug mgmt client backend
interface r1-eth0
ip address 1.1.1.1/24
exit
interface r1-eth1 vrf red
ip address 3.3.3.1/24
exit
ip route 11.11.11.11/32 1.1.1.2
ip route 13.13.13.13/32 3.3.3.2 vrf red

View File

@ -0,0 +1,23 @@
log timestamp precision 6
log file frr.log
no debug memstats-at-exit
debug northbound notifications
debug northbound libyang
debug northbound events
debug northbound callbacks
debug mgmt backend datastore frontend transaction
debug mgmt client frontend
debug mgmt client backend
interface r1-eth0
ip address 1.1.1.1/24
exit
interface r1-eth1 vrf red
ip address 3.3.3.1/24
exit
ip route 11.11.11.11/32 1.1.1.2
!ip route 13.13.13.13/32 3.3.3.2 vrf red

View File

@ -0,0 +1,41 @@
log timestamp precision 6
log file frr.log
no debug memstats-at-exit
debug northbound notifications
debug northbound libyang
debug northbound events
debug northbound callbacks
debug mgmt backend datastore frontend transaction
debug mgmt client frontend
debug mgmt client backend
interface r1-eth0
ip address 1.1.1.1/24
ipv6 address 2001:1111::1/64
exit
interface r1-eth1
ip address 2.2.2.1/24
ipv6 address 2002:2222::1/64
exit
interface r1-eth2 vrf red
ip address 3.3.3.1/24
ipv6 address 2003:333::1/64
exit
interface r1-eth3 vrf red
ip address 4.4.4.1/24
ipv6 address 2004:4444::1/64
exit
ip route 11.0.0.0/8 Null0
ip route 11.11.11.11/32 1.1.1.2
ip route 12.12.12.12/32 2.2.2.2
ip route 13.0.0.0/8 Null0 vrf red
ip route 13.13.13.13/32 3.3.3.2 vrf red
ip route 14.14.14.14/32 4.4.4.2 vrf red

View File

@ -0,0 +1,2 @@
{
}

View File

@ -0,0 +1,12 @@
{
"frr-interface:lib": {
"interface": [
{
"name": "r1-eth0",
"state": {
"mtu": 1500
}
}
]
}
}

View File

@ -0,0 +1,17 @@
{
"frr-interface:lib": {
"interface": [
{
"name": "r1-eth0",
"state": {
"if-index": "rubout",
"mtu": 1500,
"mtu6": 1500,
"speed": 10000,
"metric": 0,
"phy-address": "rubout"
}
}
]
}
}

View File

@ -0,0 +1,193 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"state": {
"id": "rubout",
"active": true
},
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,350 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"state": {
"id": "rubout",
"active": true
},
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
}
]
}
}
},
{
"name": "red",
"state": {
"id": "rubout",
"active": true
},
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 10,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "3.3.3.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "3.3.3.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 10,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "3.3.3.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "3.3.3.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 10,
"route": [
{
"prefix": "::/0"
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 10,
"route": [
{
"prefix": "::/0"
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,164 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "red",
"state": {
"id": "rubout",
"active": true
},
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 10,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "3.3.3.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "3.3.3.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 10,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "3.3.3.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "3.3.3.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 10,
"route": [
{
"prefix": "::/0"
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 10,
"route": [
{
"prefix": "::/0"
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,189 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,189 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,350 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"state": {
"id": "rubout",
"active": true
},
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
}
]
}
}
},
{
"name": "red",
"state": {
"id": "rubout",
"active": true
},
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 10,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "3.3.3.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "3.3.3.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 10,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "3.3.3.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "3.3.3.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth1",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 10,
"route": [
{
"prefix": "::/0"
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 10,
"route": [
{
"prefix": "::/0"
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,110 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,189 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv4-multicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"internal-flags": 8,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-unicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
},
{
"afi-safi-name": "frr-routing:ipv6-multicast",
"table-id": 254,
"route": [
{
"prefix": "::/0"
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,110 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "0.0.0.0/0"
},
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "1.1.1.1/32",
"route-entry": [
{
"protocol": "local",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
},
{
"prefix": "11.11.11.11/32",
"route-entry": [
{
"protocol": "static",
"distance": 1,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 73,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ip4-ifindex",
"vrf": "rubout",
"gateway": "1.1.1.2",
"interface": "r1-eth0",
"active": [null],
"fib": [null],
"weight": 1
}
]
}
}
]
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,50 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"distance": 0,
"metric": 0,
"selected": [null],
"installed": [null],
"internal-flags": 8,
"internal-status": 16,
"uptime": "rubout",
"nexthop-group": {
"id": "rubout",
"nexthop": [
{
"nh-type": "ifindex",
"vrf": "rubout",
"gateway": "",
"interface": "r1-eth0",
"active": [null],
"fib": [null]
}
]
}
}
]
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,30 @@
{
"frr-vrf:lib": {
"vrf": [
{
"name": "default",
"frr-zebra:zebra": {
"ribs": {
"rib": [
{
"afi-safi-name": "frr-routing:ipv4-unicast",
"table-id": 254,
"route": [
{
"prefix": "1.1.1.0/24",
"route-entry": [
{
"protocol": "connected",
"metric": 0
}
]
}
]
}
]
}
}
}
]
}
}

View File

@ -0,0 +1,123 @@
#!/usr/bin/env python
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: ISC
#
# Copyright (c) 2021, LabN Consulting, L.L.C.
# Copyright (c) 2019-2020 by
# Donatas Abraitis <donatas.abraitis@gmail.com>
#
"""
Test static route functionality
"""
import ipaddress
import math
import time
import pytest
from lib.topogen import Topogen
from oper import check_kernel_32, do_oper_test
try:
from deepdiff import DeepDiff as dd_json_cmp
except ImportError:
dd_json_cmp = None
pytestmark = [pytest.mark.staticd]
@pytest.fixture(scope="module")
def tgen(request):
"Setup/Teardown the environment and provide tgen argument to tests"
topodef = {"s1": ("r1",), "s2": ("r1",), "s3": ("r1",), "s4": ("r1",)}
tgen = Topogen(topodef, request.module.__name__)
tgen.start_topology()
router_list = tgen.routers()
for rname, router in router_list.items():
# Setup VRF red
router.net.add_l3vrf("red", 10)
router.net.add_loop("lo-red")
router.net.attach_iface_to_l3vrf("lo-red", "red")
router.net.attach_iface_to_l3vrf(rname + "-eth2", "red")
router.net.attach_iface_to_l3vrf(rname + "-eth3", "red")
router.load_frr_config("frr.conf")
tgen.start_router()
yield tgen
tgen.stop_topology()
def get_ip_networks(super_prefix, count):
count_log2 = math.log(count, 2)
if count_log2 != int(count_log2):
count_log2 = int(count_log2) + 1
else:
count_log2 = int(count_log2)
network = ipaddress.ip_network(super_prefix)
return tuple(network.subnets(count_log2))[0:count]
def test_oper(tgen):
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
query_results = [
("/frr-vrf:lib", "oper-results/result-lib.json"),
("/frr-vrf:lib/vrf", "oper-results/result-lib-vrf-nokey.json"),
(
'/frr-vrf:lib/vrf[name="default"]',
"oper-results/result-lib-vrf-default.json",
),
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra',
"oper-results/result-lib-vrf-zebra.json",
),
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs',
"oper-results/result-lib-vrf-zebra-ribs.json",
),
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib',
"oper-results/result-ribs-rib-nokeys.json",
),
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/'
'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]',
"oper-results/result-ribs-rib-ipv4-unicast.json",
),
]
r1 = tgen.gears["r1"].net
check_kernel_32(r1, "11.11.11.11", 1, "")
check_kernel_32(r1, "12.12.12.12", 1, "")
check_kernel_32(r1, "13.13.13.13", 1, "red")
check_kernel_32(r1, "14.14.14.14", 1, "red")
time.sleep(2)
do_oper_test(tgen, query_results)
to_gen_new_results = """
scriptdir=~chopps/w/frr/tests/topotests/mgmt_oper
resdir=${scriptdir}/oper-results
vtysh -c 'show mgmt get-data /frr-vrf:lib' > ${resdir}/result-lib.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf' > ${resdir}/result-lib-vrf-nokey.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]' > ${resdir}/result-lib-vrf-default.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="red"]' > ${resdir}/result-lib-vrf-red.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra' > ${resdir}/result-lib-vrf-zebra.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs' > ${resdir}/result-lib-vrf-zebra-ribs.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib' > ${resdir}/result-ribs-rib-nokeys.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]' > ${resdir}/result-ribs-rib-ipv4-unicast.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route' > ${resdir}/result-ribs-rib-route-nokey.json
for f in ${resdir}/result-*; do
sed -i -e 's/"uptime": ".*"/"uptime": "rubout"/;s/"id": [0-9][0-9]*/"id": "rubout"/' $f
sed -i -e 's/"if-index": [0-9][0-9]*/"if-index": "rubout"/' $f
sed -i -e 's,"vrf": "[0-9]*","vrf": "rubout",' $f
done
""" # noqa: 501
# should not differ
# diff result-lib.json result-lib-vrf-nokey.json
# diff result-lib-vrf-zebra.json result-lib-vrf-zebra-ribs.json

View File

@ -0,0 +1,103 @@
#!/usr/bin/env python
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: ISC
#
# Copyright (c) 2021, LabN Consulting, L.L.C.
# Copyright (c) 2019-2020 by
# Donatas Abraitis <donatas.abraitis@gmail.com>
#
# noqa: E501
#
"""
Test various query types
"""
import json
import logging
import pytest
from lib.common_config import step
from lib.topogen import Topogen
from oper import check_kernel_32
pytestmark = [pytest.mark.staticd]
@pytest.fixture(scope="module")
def tgen(request):
"Setup/Teardown the environment and provide tgen argument to tests"
topodef = {"s1": ("r1",), "s2": ("r1",)}
tgen = Topogen(topodef, request.module.__name__)
tgen.start_topology()
router_list = tgen.routers()
for rname, router in router_list.items():
# Setup VRF red
router.net.add_l3vrf("red", 10)
router.net.add_loop("lo-red")
router.net.attach_iface_to_l3vrf("lo-red", "red")
router.net.attach_iface_to_l3vrf(rname + "-eth1", "red")
router.load_frr_config("frr-simple.conf")
tgen.start_router()
yield tgen
tgen.stop_topology()
def test_oper_simple(tgen):
"""This test is useful for doing manual testing"""
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
query_results = [
# Specific list entry after non-specific lists
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/'
'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/'
'route/route-entry[protocol="connected"]',
# crashes: All specific until the end, then walk
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/'
'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/'
'route[prefix="1.1.1.0/24"]/route-entry[protocol="connected"]',
# Does nothing: Root level query
"//metric",
# specific leaf after non-specific lists
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/'
'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/'
"route/route-entry/metric",
# All specific until the end generic.
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/'
'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/'
'route[prefix="1.1.1.0/24"]/route-entry',
# All specific until the penultimate generic with a specific leaf child.
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/'
'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/'
'route[prefix="1.1.1.0/24"]/route-entry/metric',
# All generic until the end (middle) specific with unspecified
# children below to walk.
'/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route[prefix="1.1.1.0/24"]',
# All generic until the end which is a specific leaf.
"/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/metric",
]
# query_results = [
# '/frr-interface:lib/frr-interface:interface/frr-zebra:zebra/ip-addrs[frr-rt:address-family="frr-rt:ipv4"][prefix="1.1.1.1/24"]'
# ]
r1 = tgen.gears["r1"].net
check_kernel_32(r1, "11.11.11.11", 1, "")
step("Oper test start", reset=True)
for qr in query_results:
step(f"Perform query '{qr}'")
try:
output = r1.cmd_nostatus(f"vtysh -c 'show mgmt get-data {qr}'")
except Exception as error:
logging.error("Error sending query: %s: %s", qr, error)
continue
try:
ojson = json.loads(output)
logging.info("'%s': generates:\n%s", qr, ojson)
except json.decoder.JSONDecodeError as error:
logging.error("Error decoding json: %s\noutput:\n%s", error, output)

View File

@ -0,0 +1,67 @@
#!/usr/bin/env python
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: ISC
#
# Copyright (c) 2021, LabN Consulting, L.L.C.
# Copyright (c) 2019-2020 by
# Donatas Abraitis <donatas.abraitis@gmail.com>
#
# noqa: E501
#
"""
Test static route functionality
"""
import logging
import time
import pytest
from lib.common_config import step
from lib.topogen import Topogen, TopoRouter
from oper import check_kernel_32
pytestmark = [pytest.mark.staticd]
@pytest.fixture(scope="module")
def tgen(request):
"Setup/Teardown the environment and provide tgen argument to tests"
topodef = {"s1": ("r1",), "s2": ("r1",)}
tgen = Topogen(topodef, request.module.__name__)
tgen.start_topology()
router_list = tgen.routers()
for rname, router in router_list.items():
# Setup VRF red
router.net.add_l3vrf("red", 10)
router.net.add_loop("lo-red")
router.net.attach_iface_to_l3vrf("lo-red", "red")
router.net.attach_iface_to_l3vrf(rname + "-eth1", "red")
router.load_frr_config("frr-scale.conf")
router.load_config(TopoRouter.RD_SHARP, "")
tgen.start_router()
yield tgen
tgen.stop_topology()
def test_oper_simple(tgen):
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
r1 = tgen.gears["r1"].net
time.sleep(2)
count = 20 * 1000
vrf = None # "red"
check_kernel_32(r1, "11.11.11.11", 1, vrf)
step("Found 11.11.11.11 in kernel adding sharpd routes")
r1.cmd_raises(f"vtysh -c 'sharp install routes 20.0.0.0 nexthop 1.1.1.2 {count}'")
check_kernel_32(r1, "20.0.0.0", count, vrf, 1000)
step(f"All {count} routes installed in kernel, continuing")
output = r1.cmd_raises("vtysh -c 'show mgmt get-data /frr-vrf:lib'")
step("Got output: output")

View File

@ -0,0 +1,140 @@
#!/usr/bin/env python
# -*- coding: utf-8 eval: (blacken-mode 1) -*-
# SPDX-License-Identifier: ISC
#
# Copyright (c) 2021, LabN Consulting, L.L.C.
# Copyright (c) 2019-2020 by
# Donatas Abraitis <donatas.abraitis@gmail.com>
#
# noqa: E501
#
"""
Test static route functionality
"""
import pytest
from lib.topogen import Topogen
from oper import check_kernel_32, do_oper_test
pytestmark = [pytest.mark.staticd]
@pytest.fixture(scope="module")
def tgen(request):
"Setup/Teardown the environment and provide tgen argument to tests"
topodef = {"s1": ("r1",), "s2": ("r1",)}
tgen = Topogen(topodef, request.module.__name__)
tgen.start_topology()
router_list = tgen.routers()
for rname, router in router_list.items():
# Setup VRF red
router.net.add_l3vrf("red", 10)
router.net.add_loop("lo-red")
router.net.attach_iface_to_l3vrf("lo-red", "red")
router.net.attach_iface_to_l3vrf(rname + "-eth1", "red")
router.load_frr_config("frr-simple.conf")
tgen.start_router()
yield tgen
tgen.stop_topology()
def test_oper_simple(tgen):
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
query_results = [
("/frr-vrf:lib", "simple-results/result-lib.json"),
("/frr-vrf:lib/vrf", "simple-results/result-lib-vrf-nokey.json"),
(
'/frr-vrf:lib/vrf[name="default"]',
"simple-results/result-lib-vrf-default.json",
),
('/frr-vrf:lib/vrf[name="red"]', "simple-results/result-lib-vrf-red.json"),
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra',
"simple-results/result-lib-vrf-zebra.json",
),
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs',
"simple-results/result-lib-vrf-zebra-ribs.json",
),
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib',
"simple-results/result-ribs-rib-nokeys.json",
),
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/'
'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]',
"simple-results/result-ribs-rib-ipv4-unicast.json",
),
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/'
'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route',
"simple-results/result-ribs-rib-route-nokey.json",
),
# Missing entry
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/'
'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/'
'route[prefix="1.1.0.0/24"]',
"simple-results/result-empty.json",
),
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/'
'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/'
'route[prefix="1.1.1.0/24"]',
"simple-results/result-ribs-rib-route-prefix.json",
),
# Leaf reference
(
'/frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/'
'rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/'
'route[prefix="1.1.1.0/24"]/route-entry[protocol="connected"]/metric',
"simple-results/result-singleton-metric.json",
),
# Interface state
(
'/frr-interface:lib/interface[name="r1-eth0"]/state',
"simple-results/result-intf-state.json",
),
(
'/frr-interface:lib/interface[name="r1-eth0"]/state/mtu',
"simple-results/result-intf-state-mtu.json",
),
]
r1 = tgen.gears["r1"].net
check_kernel_32(r1, "11.11.11.11", 1, "")
do_oper_test(tgen, query_results)
to_gen_new_results = """
scriptdir=~chopps/w/frr/tests/topotests/mgmt_oper
resdir=${scriptdir}/simple-results
vtysh -c 'show mgmt get-data /frr-vrf:lib' > ${resdir}/result-lib.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf' > ${resdir}/result-lib-vrf-nokey.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]' > ${resdir}/result-lib-vrf-default.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="red"]' > ${resdir}/result-lib-vrf-red.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra' > ${resdir}/result-lib-vrf-zebra.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs' > ${resdir}/result-lib-vrf-zebra-ribs.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib' > ${resdir}/result-ribs-rib-nokeys.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]' > ${resdir}/result-ribs-rib-ipv4-unicast.json
vtysh -c 'show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route' > ${resdir}/result-ribs-rib-route-nokey.json
vtysh -c 'show mgmt get-data /frr-interface:lib/interface[name="r1-eth0"]/state' > ${resdir}/result-intf-state.json
vtysh -c 'show mgmt get-data /frr-interface:lib/interface[name="r1-eth0"]/state/mtu' > ${resdir}/result-intf-state-mtu.json
for f in ${resdir}/result-*; do
sed -i -e 's/"uptime": ".*"/"uptime": "rubout"/;s/"id": [0-9][0-9]*/"id": "rubout"/' $f
sed -i -e 's/"phy-address": ".*"/"phy-address": "rubout"/' $f
sed -i -e 's/"if-index": [0-9][0-9]*/"if-index": "rubout"/' $f
sed -i -e 's,"vrf": "[0-9]*","vrf": "rubout",' $f
done
""" # noqa: 501
# Example commands:
# show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route[prefix="1.1.0.0/24"] # noqa: E501
# show mgmt get-data /frr-vrf:lib/vrf[name="default"]/frr-zebra:zebra/ribs/rib[afi-safi-name="frr-routing:ipv4-unicast"][table-id="254"]/route[prefix="1.1.1.0/24"] # noqa: E501

View File

@ -1 +1,11 @@
log timestamp precision 3
! way too noisy
! debug northbound libyang
debug northbound notifications
debug northbound events
debug northbound callbacks
debug mgmt backend datastore frontend transaction
debug mgmt client frontend
debug mgmt client backend

View File

@ -1,5 +1,15 @@
log timestamp precision 3
! way too noisy
! debug northbound libyang
debug northbound notifications
debug northbound events
debug northbound callbacks
debug mgmt backend datastore frontend transaction
debug mgmt client frontend
debug mgmt client backend
interface r1-eth0
ip address 101.0.0.1/24
ipv6 address 2101::1/64

View File

@ -40,6 +40,8 @@ def tgen(request):
router.net.add_loop("lo-red")
router.net.attach_iface_to_l3vrf("lo-red", "red")
router.net.attach_iface_to_l3vrf(rname + "-eth1", "red")
#
# router.load_frr_config("frr.conf")
# and select daemons to run
router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
router.load_config(TopoRouter.RD_MGMTD)
@ -181,10 +183,11 @@ def guts(tgen, vrf, use_cli):
r1 = tgen.routers()["r1"]
step("add via gateway", reset=True)
do_config(r1, 1, True, False, vrf=vrf, use_cli=use_cli)
step("remove via gateway")
do_config(r1, 1, False, False, vrf=vrf, use_cli=use_cli)
count = 10
step(f"add {count} via gateway", reset=True)
do_config(r1, count, True, False, vrf=vrf, use_cli=use_cli)
step(f"remove {count} via gateway")
do_config(r1, count, False, False, vrf=vrf, use_cli=use_cli)
via = f"lo-{vrf}" if vrf else "lo"
step("add via loopback")

View File

@ -7,6 +7,7 @@
#include <zebra.h>
#include "command.h"
#include "debug.h"
#include "mgmt_be_client.h"
#include "zebra/debug_clippy.c"
@ -846,4 +847,7 @@ void zebra_debug_init(void)
install_element(CONFIG_NODE, &no_debug_zebra_pbr_cmd);
install_element(CONFIG_NODE, &debug_zebra_mlag_cmd);
install_element(CONFIG_NODE, &debug_zebra_evpn_mh_cmd);
/* Init mgmtd backend client debug commands. */
mgmt_be_client_lib_vty_init();
}

View File

@ -25,6 +25,7 @@
#include "affinitymap.h"
#include "routemap.h"
#include "routing_nb.h"
#include "mgmt_be_client.h"
#include "zebra/zebra_router.h"
#include "zebra/zebra_errors.h"
@ -58,6 +59,8 @@ pid_t pid;
/* Pacify zclient.o in libfrr, which expects this variable. */
struct event_loop *master;
struct mgmt_be_client *mgmt_be_client;
/* Route retain mode flag. */
int retain_mode = 0;
@ -142,6 +145,10 @@ static void sigint(void)
zlog_notice("Terminating on signal");
nb_oper_cancel_all_walks();
mgmt_be_client_destroy(mgmt_be_client);
mgmt_be_client = NULL;
atomic_store_explicit(&zrouter.in_shutdown, true,
memory_order_relaxed);
@ -430,6 +437,8 @@ int main(int argc, char **argv)
zebra_ns_init();
router_id_cmd_init();
zebra_vty_init();
mgmt_be_client = mgmt_be_client_create("zebra", NULL, 0,
zrouter.master);
access_list_init();
prefix_list_init();

View File

@ -434,6 +434,7 @@ const struct frr_yang_module_info frr_zebra_info = {
.get_next = lib_vrf_zebra_ribs_rib_get_next,
.get_keys = lib_vrf_zebra_ribs_rib_get_keys,
.lookup_entry = lib_vrf_zebra_ribs_rib_lookup_entry,
.lookup_next = lib_vrf_zebra_ribs_rib_lookup_next,
}
},
{
@ -454,6 +455,7 @@ const struct frr_yang_module_info frr_zebra_info = {
.get_next = lib_vrf_zebra_ribs_rib_route_get_next,
.get_keys = lib_vrf_zebra_ribs_rib_route_get_keys,
.lookup_entry = lib_vrf_zebra_ribs_rib_route_lookup_entry,
.lookup_next = lib_vrf_zebra_ribs_rib_route_lookup_next,
}
},
{

View File

@ -125,6 +125,8 @@ const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args);
int lib_vrf_zebra_ribs_rib_get_keys(struct nb_cb_get_keys_args *args);
const void *
lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args);
const void *
lib_vrf_zebra_ribs_rib_lookup_next(struct nb_cb_lookup_entry_args *args);
struct yang_data *
lib_vrf_zebra_ribs_rib_afi_safi_name_get_elem(struct nb_cb_get_elem_args *args);
struct yang_data *
@ -134,6 +136,8 @@ lib_vrf_zebra_ribs_rib_route_get_next(struct nb_cb_get_next_args *args);
int lib_vrf_zebra_ribs_rib_route_get_keys(struct nb_cb_get_keys_args *args);
const void *
lib_vrf_zebra_ribs_rib_route_lookup_entry(struct nb_cb_lookup_entry_args *args);
const void *
lib_vrf_zebra_ribs_rib_route_lookup_next(struct nb_cb_lookup_entry_args *args);
struct yang_data *
lib_vrf_zebra_ribs_rib_route_prefix_get_elem(struct nb_cb_get_elem_args *args);
struct yang_data *lib_vrf_zebra_ribs_rib_route_protocol_get_elem(

View File

@ -156,6 +156,8 @@ const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args)
safi_t safi;
zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id);
if (!zvrf)
return NULL;
if (args->list_entry == NULL) {
afi = AFI_IP;
@ -167,7 +169,8 @@ const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args)
} else {
zrt = RB_NEXT(zebra_router_table_head, zrt);
/* vrf_id/ns_id do not match, only walk for the given VRF */
while (zrt && zrt->ns_id != zvrf->zns->ns_id)
while (zrt && (zrt->tableid != zvrf->table_id ||
zrt->ns_id != zvrf->zns->ns_id))
zrt = RB_NEXT(zebra_router_table_head, zrt);
}
@ -198,6 +201,8 @@ lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args)
uint32_t table_id = 0;
zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id);
if (!zvrf)
return NULL;
yang_afi_safi_identity2value(args->keys->key[0], &afi, &safi);
table_id = yang_str2uint32(args->keys->key[1]);
@ -208,6 +213,28 @@ lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args)
return zebra_router_find_zrt(zvrf, table_id, afi, safi);
}
const void *
lib_vrf_zebra_ribs_rib_lookup_next(struct nb_cb_lookup_entry_args *args)
{
struct vrf *vrf = (struct vrf *)args->parent_list_entry;
struct zebra_vrf *zvrf;
afi_t afi;
safi_t safi;
uint32_t table_id = 0;
zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id);
if (!zvrf)
return NULL;
yang_afi_safi_identity2value(args->keys->key[0], &afi, &safi);
table_id = yang_str2uint32(args->keys->key[1]);
/* table_id 0 assume vrf's table_id. */
if (!table_id)
table_id = zvrf->table_id;
return zebra_router_find_next_zrt(zvrf, table_id, afi, safi);
}
/*
* XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/afi-safi-name
*/
@ -285,6 +312,25 @@ lib_vrf_zebra_ribs_rib_route_lookup_entry(struct nb_cb_lookup_entry_args *args)
return rn;
}
const void *
lib_vrf_zebra_ribs_rib_route_lookup_next(struct nb_cb_lookup_entry_args *args)
{
const struct zebra_router_table *zrt = args->parent_list_entry;
struct prefix p;
struct route_node *rn;
yang_str2prefix(args->keys->key[0], &p);
rn = route_table_get_next(zrt->table, &p);
if (!rn)
return NULL;
route_unlock_node(rn);
return rn;
}
/*
* XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/prefix
*/

View File

@ -70,6 +70,26 @@ struct zebra_router_table *zebra_router_find_zrt(struct zebra_vrf *zvrf,
return zrt;
}
struct zebra_router_table *zebra_router_find_next_zrt(struct zebra_vrf *zvrf,
uint32_t tableid,
afi_t afi, safi_t safi)
{
struct zebra_router_table finder;
struct zebra_router_table *zrt;
memset(&finder, 0, sizeof(finder));
finder.afi = afi;
finder.safi = safi;
finder.tableid = tableid;
finder.ns_id = zvrf->zns->ns_id;
zrt = RB_NFIND(zebra_router_table_head, &zrouter.tables, &finder);
if (zrt->afi == afi && zrt->safi == safi && zrt->tableid == tableid &&
zrt->ns_id == finder.ns_id)
zrt = RB_NEXT(zebra_router_table_head, zrt);
return zrt;
}
struct route_table *zebra_router_find_table(struct zebra_vrf *zvrf,
uint32_t tableid, afi_t afi,
safi_t safi)

View File

@ -250,6 +250,9 @@ extern void zebra_router_terminate(void);
extern struct zebra_router_table *zebra_router_find_zrt(struct zebra_vrf *zvrf,
uint32_t tableid,
afi_t afi, safi_t safi);
extern struct zebra_router_table *
zebra_router_find_next_zrt(struct zebra_vrf *zvrf, uint32_t tableid, afi_t afi,
safi_t safi);
extern struct route_table *zebra_router_find_table(struct zebra_vrf *zvrf,
uint32_t tableid, afi_t afi,
safi_t safi);