mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-02 17:36:36 +00:00
commit
353ee7bb81
@ -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'
|
||||
|
@ -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])
|
||||
|
49
gdb/lib.txt
49
gdb/lib.txt
@ -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
|
||||
|
62
lib/darr.c
62
lib/darr.c
@ -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;
|
||||
}
|
||||
|
||||
|
457
lib/darr.h
457
lib/darr.h
@ -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_ */
|
||||
|
21
lib/frrstr.c
21
lib/frrstr.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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
47
lib/mgmt_msg_native.c
Normal 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
380
lib/mgmt_msg_native.h
Normal 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_ */
|
370
lib/northbound.c
370
lib/northbound.c
@ -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();
|
||||
|
||||
|
100
lib/northbound.h
100
lib/northbound.h
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
1769
lib/northbound_oper.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
@ -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 \
|
||||
|
14
lib/vrf.c
14
lib/vrf.c
@ -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
334
lib/vty.c
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
156
lib/yang.c
156
lib/yang.c
@ -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;
|
||||
}
|
||||
|
71
lib/yang.h
71
lib/yang.h
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
433
mgmtd/mgmt_txn.c
433
mgmtd/mgmt_txn.c
@ -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;
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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
8
tests/lib/test_darr.py
Normal file
@ -0,0 +1,8 @@
|
||||
import frrtest
|
||||
|
||||
|
||||
class TestDarr(frrtest.TestMultiOut):
|
||||
program = "./test_darr"
|
||||
|
||||
|
||||
TestDarr.exit_cleanly()
|
103
tests/topotests/mgmt_fe_client/fe_client.py
Normal file
103
tests/topotests/mgmt_fe_client/fe_client.py
Normal 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()
|
1990
tests/topotests/mgmt_fe_client/mgmt_pb2.py
Normal file
1990
tests/topotests/mgmt_fe_client/mgmt_pb2.py
Normal file
File diff suppressed because one or more lines are too long
1
tests/topotests/mgmt_fe_client/oper.py
Symbolic link
1
tests/topotests/mgmt_fe_client/oper.py
Symbolic link
@ -0,0 +1 @@
|
||||
../mgmt_oper/oper.py
|
23
tests/topotests/mgmt_fe_client/r1/frr.conf
Normal file
23
tests/topotests/mgmt_fe_client/r1/frr.conf
Normal 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
|
49
tests/topotests/mgmt_fe_client/test_client.py
Normal file
49
tests/topotests/mgmt_fe_client/test_client.py
Normal 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, "")
|
@ -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]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
1145
tests/topotests/mgmt_oper/oper-results/result-lib-vrf-nokey.json
Normal file
1145
tests/topotests/mgmt_oper/oper-results/result-lib-vrf-nokey.json
Normal file
File diff suppressed because it is too large
Load Diff
576
tests/topotests/mgmt_oper/oper-results/result-lib-vrf-red.json
Normal file
576
tests/topotests/mgmt_oper/oper-results/result-lib-vrf-red.json
Normal 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]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
572
tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra.json
Normal file
572
tests/topotests/mgmt_oper/oper-results/result-lib-vrf-zebra.json
Normal 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]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
1145
tests/topotests/mgmt_oper/oper-results/result-lib.json
Normal file
1145
tests/topotests/mgmt_oper/oper-results/result-lib.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
258
tests/topotests/mgmt_oper/oper.py
Normal file
258
tests/topotests/mgmt_oper/oper.py
Normal 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
|
||||
)
|
||||
)
|
25
tests/topotests/mgmt_oper/r1/frr-scale.conf
Normal file
25
tests/topotests/mgmt_oper/r1/frr-scale.conf
Normal 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
|
23
tests/topotests/mgmt_oper/r1/frr-simple.conf
Normal file
23
tests/topotests/mgmt_oper/r1/frr-simple.conf
Normal 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
|
41
tests/topotests/mgmt_oper/r1/frr.conf
Normal file
41
tests/topotests/mgmt_oper/r1/frr.conf
Normal 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
|
@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"frr-interface:lib": {
|
||||
"interface": [
|
||||
{
|
||||
"name": "r1-eth0",
|
||||
"state": {
|
||||
"mtu": 1500
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
164
tests/topotests/mgmt_oper/simple-results/result-lib-vrf-red.json
Normal file
164
tests/topotests/mgmt_oper/simple-results/result-lib-vrf-red.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
350
tests/topotests/mgmt_oper/simple-results/result-lib.json
Normal file
350
tests/topotests/mgmt_oper/simple-results/result-lib.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
123
tests/topotests/mgmt_oper/test_oper.py
Normal file
123
tests/topotests/mgmt_oper/test_oper.py
Normal 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
|
103
tests/topotests/mgmt_oper/test_querying.py
Normal file
103
tests/topotests/mgmt_oper/test_querying.py
Normal 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)
|
67
tests/topotests/mgmt_oper/test_scale.py
Normal file
67
tests/topotests/mgmt_oper/test_scale.py
Normal 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")
|
140
tests/topotests/mgmt_oper/test_simple.py
Normal file
140
tests/topotests/mgmt_oper/test_simple.py
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user