statd: refactor to autotools, same as confd

Fix issue #299

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2024-02-27 08:14:41 +01:00
parent 5762a73c45
commit b0b1a79aa5
15 changed files with 131 additions and 84 deletions
+6 -6
View File
@@ -274,9 +274,9 @@
sysrepocfg -d operational -X -f json -x /ietf-system:system-state/infix-system:software | \
jq -r '.[][].slot |= sort_by(.name)' > "$tmp"
if [ -n "$KLISH_PARAM_name" ]; then
cat "$tmp" | /libexec/infix/cli-pretty "infix-system" -n "$KLISH_PARAM_name"
cat "$tmp" | /usr/libexec/statd/cli-pretty "infix-system" -n "$KLISH_PARAM_name"
else
cat "$tmp" | /libexec/infix/cli-pretty "infix-system"
cat "$tmp" | /usr/libexec/statd/cli-pretty "infix-system"
fi
rm "$tmp"
</ACTION>
@@ -323,14 +323,14 @@
fi
fi
sysrepocfg -f json -X -d operational -x "/ietf-routing:routing/ribs" | \
/libexec/infix/cli-pretty "ietf-routing" -n "$KLISH_PARAM_name"
/usr/libexec/statd/cli-pretty "ietf-routing" -n "$KLISH_PARAM_name"
</ACTION>
</COMMAND>
<COMMAND name="hardware" help="Show hardware information">
<ACTION sym="script">
sysrepocfg -f json -X -d operational -x "/ietf-hardware:hardware" | \
/libexec/infix/cli-pretty "ietf-hardware"
/usr/libexec/statd/cli-pretty "ietf-hardware"
</ACTION>
</COMMAND>
@@ -346,10 +346,10 @@
if [ -n "$KLISH_PARAM_name" ]; then
sysrepocfg -f json -X -d operational -x \
"/ietf-interfaces:interfaces/interface[name='$KLISH_PARAM_name']" | \
/libexec/infix/cli-pretty "ietf-interfaces" -n "$KLISH_PARAM_name"
/usr/libexec/statd/cli-pretty "ietf-interfaces" -n "$KLISH_PARAM_name"
else
sysrepocfg -f json -X -d operational -m ietf-interfaces | \
/libexec/infix/cli-pretty "ietf-interfaces"
/usr/libexec/statd/cli-pretty "ietf-interfaces"
fi
</ACTION>
</COMMAND>
+25
View File
@@ -1,3 +1,28 @@
*~
*.o
statd
/aclocal.m4
/autom4te.cache/
/aux
/clixon.xml
/compile
/config.h
/config.h.in
/config.guess
/config.log
/config.status
/config.sub
/configure
/depcomp
/install-sh
/libtool
/ltmain.sh
/m4
/missing
GPATH
GRTAGS
GTAGS
Makefile
Makefile.in
-27
View File
@@ -1,27 +0,0 @@
CFLAGS += -Wall -Wextra -Werror
CPPFLAGS += -D_GNU_SOURCE
LDLIBS += -lsysrepo -lev -lyang -ljansson -lsrx -lite
TARGET = statd
HEADERS = shared.h
SRC = statd.c shared.c
OBJ = $(SRC:.c=.o)
all: $(TARGET)
.c.o:
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
$(OBJ): $(SRC) $(HEADERS)
$(TARGET): $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
clean:
-rm -f $(TARGET) $(OBJ)
distclean: clean
-rm *~
install: $(TARGET)
install -D $(TARGET) $(DESTDIR)/sbin/
+13
View File
@@ -0,0 +1,13 @@
DISTCLEANFILES = *~ *.d
ACLOCAL_AMFLAGS = -I m4
pkglibexec_SCRIPTS = cli-pretty yanger ospf-status
sbin_PROGRAMS = statd
statd_SOURCES = statd.c shared.c shared.h
statd_CPPFLAGS = -D_DEFAULT_SOURCE -D_GNU_SOURCE
statd_CFLAGS = -W -Wall -Wextra
statd_CFLAGS += $(jansson_CFLAGS) $(libite_CFLAGS) $(libyang_CFLAGS)
statd_CFLAGS += $(sysrepo_CFLAGS) $(libsrx_CFLAGS)
statd_LDADD = $(jansson_LIBS) $(libite_LIBS) $(libyang_LIBS)
statd_LDADD += $(sysrepo_LIBS) $(libsrx_LIBS)
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
autoreconf -W portability -vifm
+530
View File
@@ -0,0 +1,530 @@
#!/usr/bin/env python3
import json
import sys
import argparse
parser = argparse.ArgumentParser(description="JSON CLI Pretty Printer")
parser.add_argument("module", help="IETF Module")
parser.add_argument("-n", "--name", help="Focus on specific name")
args = parser.parse_args()
class Pad:
iface = 16
proto = 11
state = 12
data = 41
class PadRoute:
prefix = 30
protocol = 10
next_hop = 30
pref = 8
class PadSoftware:
name = 10
date = 25
hash = 64
state = 10
version = 23
class PadUsbPort:
title = 30
name = 20
state = 10
class Decore():
@staticmethod
def decorate(sgr, txt, restore="0"):
return f"\033[{sgr}m{txt}\033[{restore}m"
@staticmethod
def invert(txt):
return Decore.decorate("7", txt)
@staticmethod
def red(txt):
return Decore.decorate("31", txt, "39")
@staticmethod
def green(txt):
return Decore.decorate("32", txt, "39")
@staticmethod
def yellow(txt):
return Decore.decorate("33", txt, "39")
def get_json_data(default, indata, *args):
data = indata
for arg in args:
if arg in data:
data = data.get(arg)
else:
return default
return data
class Route:
def __init__(self,data,ip):
self.data = data
self.prefix = data.get(f'ietf-{ip}-unicast-routing:destination-prefix', '')
self.protocol = data.get('source-protocol','')[14:]
self.pref = data.get('route-preference','')
self.next_hop = []
next_hop_list=get_json_data(None, self.data, 'next-hop', 'next-hop-list')
if next_hop_list:
for nh in next_hop_list["next-hop"]:
if(nh.get(f"ietf-{ip}-unicast-routing:address")):
self.next_hop.append(nh[f"ietf-{ip}-unicast-routing:address"])
elif(nh.get("outgoing-interface")):
self.next_hop.append(nh["outgoing-interface"])
else:
self.next_hop.append("unspecified")
else:
interface = get_json_data(None, self.data, 'next-hop', 'outgoing-interface')
address = get_json_data(None, self.data, 'next-hop', f'ietf-{ip}-unicast-routing:next-hop-address')
special = get_json_data(None, self.data, 'next-hop', 'special-next-hop')
if address:
self.next_hop.append(address)
elif interface:
self.next_hop.append(interface)
elif special:
self.next_hop.append(special)
else:
self.next_hop.append("unspecified")
def print(self):
row = f"{self.prefix:<{PadRoute.prefix}}"
row += f"{self.next_hop[0]:<{PadRoute.next_hop}}"
row += f"{self.pref:>{PadRoute.pref}} "
row += f"{self.protocol:<{PadRoute.protocol}}"
print(row)
for nh in self.next_hop[1:]:
row = f"{'':<{PadRoute.prefix}}"
row += f"{nh:<{PadRoute.next_hop}}"
print(row)
class Software:
"""Software bundle class """
def __init__(self, data):
self.data = data
self.name = data.get('bootname', '')
self.size = data.get('size', '')
self.type = data.get('class', '')
self.hash = data.get('sha256', '')
self.state = data.get('state', '')
self.version = get_json_data('', self.data, 'bundle', 'version')
self.date = get_json_data('', self.data, 'installed', 'datetime')
def is_rootfs(self):
"""True if bundle type is 'rootfs'"""
return self.type == "rootfs"
def print(self):
"""Brief information about one bundle"""
row = f"{self.name:<{PadSoftware.name}}"
row += f"{self.state:<{PadSoftware.state}}"
row += f"{self.version:<{PadSoftware.version}}"
row += f"{self.date:<{PadSoftware.date}}"
print(row)
def detail(self):
"""Detailed information about one bundle"""
print(f"Name : {self.name}")
print(f"State : {self.state}")
print(f"Version : {self.version}")
print(f"Size : {self.size}")
print(f"SHA-256 : {self.hash}")
print(f"Installed : {self.date}")
class USBport:
def __init__(self, data):
self.data = data
self.name = data.get('name', '')
self.state = get_json_data('', self.data, 'state', 'admin-state')
def print(self):
#print(self.name)
row = f"{self.name:<{PadUsbPort.name}}"
row += f"{self.state:<{PadUsbPort.state}}"
print(row)
class Iface:
def __init__(self, data):
self.data = data
self.name = data.get('name', '')
self.type = data.get('type', '')
self.index = data.get('if-index', '')
self.oper_status = data.get('oper-status', '')
self.autoneg = get_json_data('unknown', self.data, 'ieee802-ethernet-interface:ethernet',
'auto-negotiation', 'enable')
self.duplex = get_json_data('', self.data,'ieee802-ethernet-interface:ethernet','duplex')
self.speed = get_json_data('', self.data, 'ieee802-ethernet-interface:ethernet', 'speed')
self.phys_address = data.get('phys-address', '')
self.br_vlans = get_json_data({}, self.data, 'infix-interfaces:bridge', 'vlans', "vlan")
self.bridge = get_json_data('', self.data, 'infix-interfaces:bridge-port', 'bridge')
self.pvid = get_json_data('', self.data, 'infix-interfaces:bridge-port', 'pvid')
self.stp_state = get_json_data('', self.data, 'infix-interfaces:bridge-port', 'stp-state')
if data.get('statistics'):
self.in_octets = data.get('statistics').get('in-octets', '')
self.out_octets = data.get('statistics').get('out-octets', '')
else:
self.in_octets = ''
self.out_octets = ''
if self.data.get('ietf-ip:ipv4'):
self.mtu = self.data.get('ietf-ip:ipv4').get('mtu', '')
self.ipv4_addr = self.data.get('ietf-ip:ipv4').get('address', '')
else:
self.mtu = ''
self.ipv4_addr = []
if self.data.get('ietf-ip:ipv6'):
self.ipv6_addr = self.data.get('ietf-ip:ipv6').get('address', '')
else:
self.ipv6_addr = []
if self.data.get('infix-interfaces:vlan'):
self.lower_if = self.data.get('infix-interfaces:vlan', None).get('lower-layer-if',None)
else:
self.lower_if = ''
def is_vlan(self):
return self.type == "infix-if-type:vlan"
def is_bridge(self):
return self.type == "infix-if-type:bridge"
def is_veth(self):
return self.data['type'] == "infix-if-type:veth"
def pr_name(self, pipe=""):
print(f"{pipe}{self.name:<{Pad.iface - len(pipe)}}", end="")
def pr_proto_ipv4(self, pipe=''):
for addr in self.ipv4_addr:
origin = f"({addr['origin']})" if addr.get('origin') else ""
row = f"{pipe:<{Pad.iface}}"
row += f"{'ipv4':<{Pad.proto}}"
row += f"{'':<{Pad.state}}{addr['ip']}/{addr['prefix-length']} {origin}"
print(row)
def pr_proto_ipv6(self, pipe=''):
for addr in self.ipv6_addr:
origin = f"({addr['origin']})" if addr.get('origin') else ""
row = f"{pipe:<{Pad.iface}}"
row += f"{'ipv6':<{Pad.proto}}"
row += f"{'':<{Pad.state}}{addr['ip']}/{addr['prefix-length']} {origin}"
print(row)
def pr_proto_eth(self):
row = f"{'ethernet':<{Pad.proto}}"
dec = Decore.green if self.oper_status == "up" else Decore.red
row += dec(f"{self.oper_status.upper():<{Pad.state}}")
row += f"{self.phys_address:<{Pad.data}}"
print(row)
def pr_proto_br(self, br_vlans):
data_str = ""
row = f"{'bridge':<{Pad.proto}}"
if self.oper_status == "up":
dec = Decore.green if self.stp_state == "forwarding" else Decore.yellow
row += dec(f"{self.stp_state.upper():<{Pad.state}}")
else:
row += Decore.red(f"{self.oper_status.upper():<{Pad.state}}")
for vlan in br_vlans:
if 'tagged' in vlan:
for tagged in vlan['tagged']:
if tagged == self.name:
if data_str:
data_str += f",{vlan['vid']}t"
else:
data_str += f"vlan:{vlan['vid']}t"
if 'untagged' in vlan:
for untagged in vlan['untagged']:
if untagged == self.name:
if data_str:
data_str += f",{vlan['vid']}u"
else:
data_str += f"vlan:{vlan['vid']}u"
if self.pvid:
data_str += f" pvid:{self.pvid}"
if data_str:
row += f"{data_str:<{Pad.data}}"
print(row)
def pr_bridge(self, _ifaces):
self.pr_name(pipe="")
self.pr_proto_br(self.br_vlans)
lowers = []
for _iface in [Iface(data) for data in _ifaces]:
if _iface.bridge and _iface.bridge == self.name:
lowers.append(_iface)
if lowers:
self.pr_proto_ipv4(pipe='│')
self.pr_proto_ipv6(pipe='│')
else:
self.pr_proto_ipv4()
self.pr_proto_ipv6()
for i, lower in enumerate(lowers):
pipe = '└ ' if (i == len(lowers) -1) else '├ '
lower.pr_name(pipe)
lower.pr_proto_br(self.br_vlans)
def pr_veth(self, _ifaces):
self.pr_name(pipe="")
self.pr_proto_eth()
if self.lower_if:
row = f"{'':<{Pad.iface}}"
row += f"{'veth':<{Pad.proto}}"
row += f"{'':<{Pad.state}}"
row += f"peer:{self.lower_if}"
print(row)
self.pr_proto_ipv4()
self.pr_proto_ipv6()
def pr_vlan(self, _ifaces):
self.pr_name(pipe="")
self.pr_proto_eth()
if self.lower_if:
self.pr_proto_ipv4(pipe='│')
self.pr_proto_ipv6(pipe='│')
else:
self.pr_proto_ipv4()
self.pr_proto_ipv6()
return
parent = find_iface(_ifaces, self.lower_if)
if not parent:
print(f"Error, didn't find parent interface for vlan {self.name}")
sys.exit(1)
parent.pr_name(pipe='└ ')
parent.pr_proto_eth()
def pr_iface(self):
print(f"{'name':<{20}}: {self.name}")
print(f"{'index':<{20}}: {self.index}")
if self.mtu:
print(f"{'mtu':<{20}}: {self.mtu}")
if self.oper_status:
print(f"{'operational status':<{20}}: {self.oper_status}")
if self.lower_if:
print(f"{'lower-layer-if':<{20}}: {self.lower_if}")
if self.autoneg != 'unknown':
val = "on" if self.autoneg else "off"
print(f"{'auto-negotiation':<{20}}: {val}")
if self.duplex:
print(f"{'duplex':<{20}}: {self.duplex}")
if self.speed:
mbs = float(self.speed) * 1000
print(f"{'speed':<{20}}: {int(mbs)}")
if self.phys_address:
print(f"{'physical address':<{20}}: {self.phys_address}")
if self.ipv4_addr:
first = True
for addr in self.ipv4_addr:
origin = f"({addr['origin']})" if addr.get('origin') else ""
key = 'ipv4 addresses' if first else ''
colon = ':' if first else ' '
row = f"{key:<{20}}{colon} "
row += f"{addr['ip']}/{addr['prefix-length']} {origin}"
print(row)
first = False
else:
print(f"{'ipv4 addresses':<{20}}:")
if self.ipv6_addr:
first = True
for addr in self.ipv6_addr:
origin = f"({addr['origin']})" if addr.get('origin') else ""
key = 'ipv6 addresses' if first else ''
colon = ':' if first else ' '
row = f"{key:<{20}}{colon} "
row += f"{addr['ip']}/{addr['prefix-length']} {origin}"
print(row)
first = False
else:
print(f"{'ipv6 addresses':<{20}}:")
if self.in_octets and self.out_octets:
print(f"{'in-octets':<{20}}: {self.in_octets}")
print(f"{'out-octets':<{20}}: {self.out_octets}")
frame = get_json_data([], self.data,'ieee802-ethernet-interface:ethernet',
'statistics', 'frame')
if frame:
print(f"")
for key, val in frame.items():
print(f"eth-{key:<{25}}: {val}")
def find_iface(_ifaces, name):
for _iface in [Iface(data) for data in _ifaces]:
if _iface.name == name:
return _iface
return False
def pr_interface_list(json):
hdr = (f"{'INTERFACE':<{Pad.iface}}"
f"{'PROTOCOL':<{Pad.proto}}"
f"{'STATE':<{Pad.state}}"
f"{'DATA':<{Pad.data}}")
print(Decore.invert(hdr))
ifaces = sorted(json["ietf-interfaces:interfaces"]["interface"], key=lambda x: x['name'])
for iface in [Iface(data) for data in ifaces]:
if iface.is_bridge():
iface.pr_bridge(ifaces)
continue
if iface.is_veth():
iface.pr_veth(ifaces)
continue
if iface.is_vlan():
iface.pr_vlan(ifaces)
continue
# These interfaces are printed by there parent, such as bridge
if iface.lower_if:
continue
if iface.bridge:
continue
iface.pr_name()
iface.pr_proto_eth()
iface.pr_proto_ipv4()
iface.pr_proto_ipv6()
def ietf_interfaces(json, name):
if name:
if not json.get("ietf-interfaces:interfaces"):
print(f"No interface data found for \"{name}\"")
sys.exit(1)
iface = find_iface(json["ietf-interfaces:interfaces"]["interface"], name)
if not iface:
print(f"Interface \"{name}\" not found")
sys.exit(1)
iface.pr_iface()
else:
if not json.get("ietf-interfaces:interfaces"):
print(f"Error, top level \"ietf-interfaces:interfaces\" missing")
sys.exit(1)
pr_interface_list(json)
def ietf_routing(json, ip="ipv4"):
if not json.get("ietf-routing:routing"):
print(f"Error, top level \"ietf-routing:routing\" missing")
sys.exit(1)
hdr = (f"{'PREFIX':<{PadRoute.prefix}}"
f"{'NEXT-HOP':<{PadRoute.next_hop}}"
f"{'PREF':>{PadRoute.pref}} "
f"{'PROTOCOL':<{PadRoute.protocol}}")
print(Decore.invert(hdr))
for rib in get_json_data({}, json, 'ietf-routing:routing','ribs', 'rib'):
if rib["name"] != ip:
continue;
routes = get_json_data(None, rib, "routes", "route")
if routes:
for r in routes:
route = Route(r, ip)
route.print()
def find_slot(_slots, name):
for _slot in [Software(data) for data in _slots]:
if _slot.name == name:
return _slot
return False
def infix_system(json, name):
if not json.get("ietf-system:system-state", "infix-system:software"):
print("Error, cannot find infix-system:software")
sys.exit(1)
slots = get_json_data({}, json, 'ietf-system:system-state', 'infix-system:software', 'slot')
if name:
slot = find_slot(slots, name)
if slot:
slot.detail()
else:
hdr = (f"{'NAME':<{PadSoftware.name}}"
f"{'STATE':<{PadSoftware.state}}"
f"{'VERSION':<{PadSoftware.version}}"
f"{'DATE':<{PadSoftware.date}}")
print(Decore.invert(hdr))
for _s in slots:
slot = Software(_s)
if slot.is_rootfs():
slot.print()
def ietf_hardware(json):
if not json.get("ietf-hardware:hardware"):
print(f"Error, top level \"ietf-hardware:component\" missing")
sys.exit(1)
hdr = (f"{'USB PORTS':<{PadUsbPort.title}}")
print(Decore.invert(hdr))
hdr = (f"{'NAME':<{PadUsbPort.name}}"
f"{'STATE':<{PadUsbPort.state}}")
print(Decore.invert(hdr))
components = get_json_data({}, json, "ietf-hardware:hardware", "component")
for component in components:
if component.get("class") == "infix-hardware:usb":
port = USBport(component)
port.print()
def main():
try:
json_data = json.load(sys.stdin)
except json.JSONDecodeError:
print("Error, invalid JSON input")
sys.exit(1)
except Exception as e:
print("Error, unexpected error parsing JSON")
sys.exit(1)
if args.module == "ietf-interfaces":
sys.exit(ietf_interfaces(json_data, args.name))
if args.module == "ietf-routing":
sys.exit(ietf_routing(json_data, args.name))
if args.module == "infix-system":
sys.exit(infix_system(json_data, args.name))
if args.module == "ietf-hardware":
sys.exit(ietf_hardware(json_data))
else:
print(f"Error, unknown module {args.module}")
sys.exit(1)
if __name__ == "__main__":
main()
+76
View File
@@ -0,0 +1,76 @@
AC_PREREQ(2.61)
AC_INIT([statd], [1.0.0], [https://github.com/kernelkit/infix/issues])
AM_INIT_AUTOMAKE(1.11 foreign subdir-objects)
AM_SILENT_RULES(yes)
AC_CONFIG_FILES([
Makefile
])
AC_PROG_CC
AC_PROG_INSTALL
# Check for pkg-config first, warn if it's not installed
PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES([jansson], [jansson >= 2.0.0])
PKG_CHECK_MODULES([libite], [libite >= 2.5.0])
PKG_CHECK_MODULES([libyang], [libyang >= 2.1.80])
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36])
PKG_CHECK_MODULES([libsrx], [libsrx >= 1.0.0])
AC_CHECK_HEADER([ev.h],
[AC_CHECK_LIB([ev], [ev_loop_new],
[],
[AC_MSG_ERROR("libev not found")] )],
[AC_MSG_ERROR("ev.h not found")]
)
test "x$prefix" = xNONE && prefix=$ac_default_prefix
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
DATAROOTDIR=`eval echo $datarootdir`
DATAROOTDIR=`eval echo $DATAROOTDIR`
AC_SUBST(DATAROOTDIR)
LIBDIR=`eval echo $libdir`
LIBDIR=`eval echo $LIBDIR`
AC_SUBST(LIBDIR)
LOCALSTATEDIR=`eval echo $localstatedir`
LOCALSTATEDIR=`eval echo $LOCALSTATEDIR`
AC_SUBST(LOCALSTATEDIR)
RUNSTATEDIR=`eval echo $runstatedir`
RUNSTATEDIR=`eval echo $RUNSTATEDIR`
AC_SUBST(RUNSTATEDIR)
SYSCONFDIR=`eval echo $sysconfdir`
SYSCONFDIR=`eval echo $SYSCONFDIR`
AC_SUBST(SYSCONFDIR)
AC_OUTPUT
cat <<EOF
------------------ Summary ------------------
$PACKAGE_NAME version $PACKAGE_VERSION
Prefix................: $prefix
Exec prefix...........: $eprefix
Sysconfdir............: `eval echo $sysconfdir`
Localstatedir.........: `eval echo $localstatedir`
Default fstab.........: `eval echo $fstab`
System environment....: ${sysconfig_path:-${sysconfig}}
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
Linker................: $LD $LLDP_LDFLAGS $LLDP_BIN_LDFLAGS $LDFLAGS $LIBS
------------- Compiler version --------------
$($CC --version || true)
-------------- Linker version ---------------
$($LD --version || true)
---------------------------------------------
Check the above options and compile with:
${MAKE-make}
EOF
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/python3
# This script is used to transform the output from the show ip ospf commands and order
# them to match the ietf-ospf YANG model. For example, interfaces is ordered under
# area but FRR has areas in interfaces.
#
# This makes the parsing for the operational parts of YANG model more easy
#
import json
import subprocess
iface_out=subprocess.check_output("vtysh -c 'show ip ospf interface json'", shell=True)
ospf_out=subprocess.check_output("vtysh -c 'show ip ospf json'", shell=True)
neighbor_out=subprocess.check_output("vtysh -c 'show ip ospf neighbor detail json'", shell=True)
interfaces=json.loads(iface_out)
ospf=json.loads(ospf_out)
neighbors=json.loads(neighbor_out)
for ifname,iface in interfaces["interfaces"].items():
iface["name"] = ifname
iface["neighbors"] = []
for area_id in ospf["areas"]:
area_type=""
stub=False
if("NSSA" in iface["area"]):
iface_area_id = iface["area"][:-7]
area_type = "nssa-area"
elif("Stub" in iface["area"]):
iface_area_id = iface["area"][:-7]
iface["areaId"] = iface["area"][:-7]
area_type = "stub-area"
else:
iface_area_id = iface["area"]
area_type = "normal-area"
if(iface_area_id != area_id):
continue
ospf["areas"][area_id]["area-type"] = area_type
iface["area"] = iface_area_id
for nbrAddress,nbrDatas in neighbors["neighbors"].items():
for nbrData in nbrDatas:
nbrIfname=nbrData["ifaceName"].split(":")[0]
if(("NSSA" in nbrData.get("areaId", {})) or ("Stub" in nbrData.get("areaId", {}))):
nbrData["areaId"] = nbrData["areaId"][:-7]
if ((nbrIfname != ifname) and (area_id != nbrData.get("areaId"))):
#print(f'Continute {ifname} {nbrData.get("areaId")}')
continue
nbrData["neighborIp"] = nbrAddress
iface["neighbors"].append(nbrData)
if(not ospf["areas"][area_id].get("interfaces", None)):
ospf["areas"][area_id]["interfaces"] = []
ospf["areas"][area_id]["interfaces"].append(iface)
print(json.dumps(ospf))
+1 -1
View File
@@ -134,7 +134,7 @@ static int ly_add_yanger_data(const struct ly_ctx *ctx, struct lyd_node **parent
const char *model, const char *arg)
{
char *yanger_args[5] = {
"/libexec/infix/yanger",
"/usr/libexec/statd/yanger",
(char *)model,
NULL,
NULL,
+840
View File
@@ -0,0 +1,840 @@
#!/usr/bin/env python3
import subprocess
import json
import sys # (built-in module)
import os
import argparse
from datetime import datetime
TESTPATH = ""
def json_get_yang_type(iface_in):
if iface_in['link_type'] == "loopback":
return "infix-if-type:loopback"
if iface_in['link_type'] != "ether":
return "infix-if-type:other"
if 'parentbus' in iface_in and iface_in['parentbus'] == "virtio":
return "infix-if-type:etherlike"
if not 'linkinfo' in iface_in:
return "infix-if-type:ethernet"
if not 'info_kind' in iface_in['linkinfo']:
return "infix-if-type:ethernet"
if iface_in['linkinfo']['info_kind'] == "veth":
return "infix-if-type:veth"
if iface_in['linkinfo']['info_kind'] == "vlan":
return "infix-if-type:vlan"
if iface_in['linkinfo']['info_kind'] == "bridge":
return "infix-if-type:bridge"
if iface_in['linkinfo']['info_kind'] == "dsa":
return "infix-if-type:ethernet"
# Fallback
return "infix-if-type:ethernet"
def json_get_yang_origin(addr):
"""Translate kernel IP address origin to YANG"""
xlate = {
"kernel_ll": "link-layer",
"kernel_ra": "link-layer",
"static": "static",
"dhcp": "dhcp",
"random": "random",
}
proto = addr['protocol']
if proto in ("kernel_ll", "kernel_ra"):
if "stable-privacy" in addr:
return "random"
return xlate.get(proto, "other")
def getitem(data, key):
"""Get sub-object from an object"""
while key:
data = data[key[0]]
key = key[1:]
return data
def get_proc_value(procfile):
"""Return contents of /proc file, or None"""
try:
with open(procfile, 'r') as file:
data = file.read().strip()
return data
except FileNotFoundError:
# This is considered OK
return None
except IOError:
print(f"Error: reading from {procfile}", file=sys.stderr)
def lookup(obj, *keys):
"""This function returns a value from a nested json object"""
curr = obj
for key in keys:
if isinstance(curr, dict) and key in curr:
curr = curr[key]
else:
return None
return curr
def insert(obj, *path_and_value):
""""This function inserts a value into a nested json object"""
if len(path_and_value) < 2:
raise ValueError("Error: insert() takes at least two args")
*path, value = path_and_value
curr = obj
for key in path[:-1]:
if key not in curr or not isinstance(curr[key], dict):
curr[key] = {}
curr = curr[key]
curr[path[-1]] = value
def run_cmd(cmd, testfile):
"""Run a command (array of args) and return an array of lines"""
if TESTPATH and testfile:
cmd = ['cat', os.path.join(TESTPATH, testfile)]
try:
output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, text=True)
return output.splitlines()
except subprocess.CalledProcessError:
print("Error: command returned error", file=sys.stderr)
sys.exit(1)
def run_json_cmd(cmd, testfile):
"""Run a command (array of args) that returns JSON text output and return JSON"""
if TESTPATH and testfile:
cmd = ['cat', os.path.join(TESTPATH, testfile)]
try:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, text=True)
output = result.stdout
data = json.loads(output)
except subprocess.CalledProcessError as err:
print("Error: unable to get data:", file=sys.stderr)
print(f"{err.stderr}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as err:
print(f"Error: parsing JSON output: {err.msg}", file=sys.stderr)
sys.exit(1)
return data
def iface_is_dsa(iface_in):
"""Check if interface is a DSA/intra-switch port"""
if not "linkinfo" in iface_in:
return False
if not "info_kind" in iface_in['linkinfo']:
return False
if iface_in['linkinfo']['info_kind'] != "dsa":
return False
return True
def get_vpd_vendor_extensions(data):
vendor_extensions=[]
for ext in data:
vendor_extension = {}
vendor_extension["iana-enterprise-number"] = ext[0]
vendor_extension["extension-data"] = ext[1]
vendor_extensions.append(vendor_extension)
return vendor_extensions
def get_vpd_data(vpd):
component={}
component["name"]=vpd.get("board")
component["infix-hardware:vpd-data"] = {}
if vpd.get("data"):
component["class"] = "infix-hardware:vpd"
if vpd["data"].get("manufacture-date"):
component["mfg-date"]=datetime.strptime(vpd["data"]["manufacture-date"],"%m/%d/%Y %H:%M:%S").strftime("%Y-%m-%-dT%H:%M:%SZ")
if vpd["data"].get("manufacter"):
component["mfg-name"]=vpd["data"]["manufacturer"]
if vpd["data"].get("product-name"):
component["model-name"]=vpd["data"]["product-name"]
if vpd["data"].get("serial-number"):
component["serial-num"]=vpd["data"]["serial-number"]
# Set VPD-data entrys
for k,v in vpd["data"].items():
if vpd["data"].get(k):
if k != "vendor-extension":
component["infix-hardware:vpd-data"][k] = v
else:
vendor_extensions=get_vpd_vendor_extensions(v)
component["infix-hardware:vpd-data"]["infix-hardware:vendor-extension"] = vendor_extensions
return component
def get_usb_ports(usb_ports):
ports=[]
names=[]
for usb_port in usb_ports:
port={}
if usb_port.get("path"):
if usb_port["name"] in names:
continue
path = usb_port["path"]
if os.path.basename(path) == "authorized_default":
if os.path.exists(path):
with open(path, "r") as f:
names.append(usb_port["name"])
data=int(f.readline().strip())
enabled="unlocked" if data == 1 else "locked"
port["state"] = {}
port["state"]["admin-state"] = enabled
port["name"] = usb_port["name"]
port["class"] = "infix-hardware:usb"
port["state"]["oper-state"] = "enabled"
ports.append(port)
return ports
def add_hardware(hw_out):
data = run_json_cmd(['cat', "/run/system.json"], "system.json")
components=[]
for _,vpd in data.get("vpd").items():
component=get_vpd_data(vpd)
components.append(component)
if data.get("usb-ports", None):
components.extend(get_usb_ports(data["usb-ports"]))
insert(hw_out, "component", components)
def get_routes(routes, proto, data):
"""Populate routes"""
out={}
out["route"] = []
if proto == "ipv4":
default = "0.0.0.0/0"
host_prefix_length="32"
else:
default = "::/0"
host_prefix_length="128"
for entry in data:
new = {}
if entry['dst'] == "default":
entry['dst'] = default
if entry['dst'].find('/') == -1:
entry['dst'] = entry['dst'] + "/" + host_prefix_length
new[f'ietf-{proto}-unicast-routing:destination-prefix'] = entry['dst']
new['source-protocol'] = "infix-routing:" + entry['protocol']
if entry.get("metric"):
new['route-preference'] = entry['metric']
else:
new['route-preference'] = 0
if entry.get('nexthops'):
next_hops = []
for hop in entry.get('nexthops'):
next_hop = {}
if hop.get("dev"):
next_hop['outgoing-interface'] = hop['dev']
if hop.get("gateway"):
next_hop[f'ietf-{proto}-unicast-routing:address'] = hop['gateway']
next_hops.append(next_hop)
insert(new, 'next-hop', 'next-hop-list', 'next-hop', next_hops)
else:
next_hop = {}
if entry['type'] == "blackhole":
next_hop['special-next-hop'] = "blackhole"
if entry['type'] == "unreachable":
next_hop['special-next-hop'] = "unreachable"
if entry['type'] == "unicast":
if entry.get("dev"):
next_hop['outgoing-interface'] = entry['dev']
if entry.get("gateway"):
next_hop[f'ietf-{proto}-unicast-routing:next-hop-address'] = entry['gateway']
new['next-hop'] = next_hop
out['route'].append(new)
insert(routes, 'routes', out)
def add_ipv4_route(routes):
"""Fetch IPv4 routes from kernel and populate tree"""
data = run_json_cmd(['ip', '-4', '-s', '-d', '-j', 'route'], "ip-4-route.json")
get_routes(routes, "ipv4", data)
def add_ipv6_route(routes):
"""Fetch IPv6 routes from kernel and populate tree"""
data = run_json_cmd(['ip', '-6', '-s', '-d', '-j', 'route'], "ip-6-route.json")
get_routes(routes, "ipv6", data)
def frr_to_ietf_neighbor_state(state):
"""Fetch OSPF neighbor state from Frr"""
state = state.split("/")[0]
if state == "TwoWay":
return "2-way"
return state.lower()
def add_ospf_routes(ospf):
"""Fetch OSPF routes from Frr"""
cmd = ['vtysh', '-c', "show ip ospf rout json"]
data = run_json_cmd(cmd, "")
routes=[]
for prefix,info in data.items():
if prefix.find("/") == -1: # Ignore router IDs
continue
route={}
route["prefix"] = prefix
nexthops=[]
routetype=info["routeType"].split(" ")
if len(routetype) > 1:
if routetype[1] == "E1":
route["route-type"] = "external-1"
elif routetype[1] == "E2":
route["route-type"] = "external-2"
elif routetype[1] == "IA":
route["route-type"] = "inter-area"
elif routetype[0] == "N":
route["route-type"] = "intra-area"
for hop in info["nexthops"]:
nexthop={}
if hop["ip"] != " ":
nexthop["next-hop"] = hop["ip"]
else:
nexthop["outgoing-interface"] = hop["directlyAttachedTo"]
nexthops.append(nexthop)
route["next-hops"] = {}
route["next-hops"]["next-hop"] = nexthops
routes.append(route)
insert(ospf, "ietf-ospf:local-rib", "ietf-ospf:route", routes)
def add_ospf(ospf):
"""Populate OSPF status"""
cmd = ['/usr/libexec/statd/ospf-status']
data = run_json_cmd(cmd, "")
ospf["ietf-ospf:router-id"] = data["routerId"]
ospf["ietf-ospf:address-family"] = "ipv4"
areas=[]
for area_id,values in data["areas"].items():
area={}
area["ietf-ospf:area-id"] = area_id
area["ietf-ospf:interfaces"] = {}
if values.get("area-type"):
area["ietf-ospf:area-type"] = values["area-type"]
interfaces=[]
for iface in values.get("interfaces", {}):
interface={}
interface["ietf-ospf:neighbors"] = {}
interface["name"] = iface["name"]
if iface.get("drId"):
interface["dr-router-id"] = iface["drId"]
if iface.get("drAddress"):
interface["dr-ip-addr"] = iface["drAddress"]
if iface.get("bdrId"):
interface["bdr-router-id"] = iface["bdrId"]
if iface.get("bdrAddress"):
interface["bdr-ip-addr"] = iface["bdrAddress"]
if iface.get("timerPassiveIface"):
interface["passive"] = True
else:
interface["passive"] = False
interface["enabled"] = iface["ospfEnabled"]
if iface["networkType"] == "POINTOPOINT":
interface["interface-type"] = "point-to-point"
if iface["networkType"] == "BROADCAST":
interface["interface-type"] = "broadcast"
if iface.get("state"):
# Wev've never seen "DependUpon", and has no entry in
# the YANG model, but is listed before down in Frr
xlate = {
"DependUpon": "down",
"Down": "down",
"Waiting": "waiting",
"Loopback": "loopback",
"Point-To-Point": "point-to-point",
"DROther": "dr-other",
"Backup": "bdr",
"DR": "dr"
}
val = xlate.get(iface["state"], "unknown")
interface["state"] = val
neighbors = []
for neigh in iface["neighbors"]:
neighbor={}
neighbor["neighbor-router-id"] = neigh["neighborIp"]
neighbor["address"] = neigh["ifaceAddress"]
neighbor["dr-router-id"] = neigh["routerDesignatedId"]
neighbor["bdr-router-id"] = neigh["routerDesignatedBackupId"]
neighbor["dead-timer"] = neigh["routerDeadIntervalTimerDueMsec"]
neighbor["state"]=frr_to_ietf_neighbor_state(neigh["nbrState"])
neighbors.append(neighbor)
interface["ietf-ospf:neighbors"] = {}
interface["ietf-ospf:neighbors"]["ietf-ospf:neighbor"] = neighbors
interfaces.append(interface)
area["ietf-ospf:interfaces"]["ietf-ospf:interface"] = interfaces
areas.append(area)
insert(ospf, "ietf-ospf:areas", "area", areas)
add_ospf_routes(ospf)
def get_bridge_port_pvid(ifname):
data = run_json_cmd(['bridge', '-j', 'vlan', 'show', 'dev', ifname],
f"bridge-vlan-show-dev-{ifname}.json")
if len(data) != 1:
return None
iface = data[0]
for vlan in iface['vlans']:
if 'flags' in vlan and 'PVID' in vlan['flags']:
return vlan['vlan']
return None
def get_bridge_port_stp_state(ifname):
data = run_json_cmd(['bridge', '-j', 'link', 'show', 'dev', ifname],
f"bridge-link-show-dev-{ifname}.json")
if len(data) != 1:
return None
iface = data[0]
states = [ 'disabled', 'listening', 'learning', 'forwarding', 'blocking' ]
if 'state' in iface and iface['state'] in states:
return iface['state']
return None
def container_inspect(name, key):
"""Call podman inspect {name}, return object at {path} or None"""
cmd = ['podman', 'inspect', name]
raw = run_json_cmd(cmd, "")
return getitem(raw[0], key)
def add_container(containers):
"""In container-state we list *all* containers, not just ones manged in configuration."""
cmd = ['podman', 'ps', '-a', '--format=json']
raw = run_json_cmd(cmd, "")
for entry in raw:
running = entry["State"] == "running"
container = {
"name": entry["Names"][0],
"id": entry["Id"],
"image": entry["Image"],
"image-id": entry["ImageID"],
"running": running,
"status": entry["Status"]
}
# Bonus information, may not be available
if entry["Command"]:
container["command"] = " ".join(entry["Command"])
# The 'podman ps' command lists ports even in host mode, but
# that's not applicable, so skip networks and port forwardings
networks = container_inspect(container["name"], ("NetworkSettings", "Networks"))
if "host" in networks:
container["network"] = { "host": True }
else:
container["network"] = {
"interface": [],
"publish": []
}
if entry["Networks"]:
for net in entry["Networks"]:
container["network"]["interface"].append({ "name": net })
if running and entry["Ports"]:
for port in entry["Ports"]:
addr = ""
if port["host_ip"]:
addr = f"{port['host_ip']}:"
pub = f"{addr}{port['host_port']}->{port['container_port']}/{port['protocol']}"
container["network"]["publish"].append(pub)
containers.append(container)
def add_ip_link(ifname, iface_out):
"""Fetch interface link information from kernel"""
data = run_json_cmd(['ip', '-s', '-d', '-j', 'link', 'show', 'dev', ifname],
f"ip-link-show-dev-{ifname}.json")
if len(data) != 1:
print("Error: expected ip link output to be array with length 1", file=sys.stderr)
sys.exit(1)
iface_in = data[0]
if 'ifname' in iface_in:
iface_out['name'] = iface_in['ifname']
if 'ifindex' in iface_in:
iface_out['if-index'] = iface_in['ifindex']
if 'address' in iface_in:
iface_out['phys-address'] = iface_in['address']
if 'master' in iface_in:
insert(iface_out, "infix-interfaces:bridge-port", "bridge", iface_in['master'])
pvid = get_bridge_port_pvid(ifname)
if pvid is not None:
insert(iface_out, "infix-interfaces:bridge-port", "pvid", pvid)
stp_state = get_bridge_port_stp_state(ifname)
if stp_state is not None:
insert(iface_out, "infix-interfaces:bridge-port", "stp-state", stp_state)
if 'link' in iface_in and not iface_is_dsa(iface_in):
insert(iface_out, "infix-interfaces:vlan", "lower-layer-if", iface_in['link'])
if 'operstate' in iface_in:
xlate = {
"DOWN": "down",
"UP": "up",
"DORMANT": "dormant",
"TESTING": "testing",
"LOWERLAYERDOWN": "lower-layer-down",
"NOTPRESENT": "not-present"
}
val = xlate.get(iface_in['operstate'], "unknown")
iface_out['oper-status'] = val
if 'link_type' in iface_in:
val = json_get_yang_type(iface_in)
iface_out['type'] = val
val = lookup(iface_in, "stats64", "rx", "bytes")
if val is not None:
insert(iface_out, "statistics", "out-octets", str(val))
val = lookup(iface_in, "stats64", "tx", "bytes")
if val is not None:
insert(iface_out, "statistics", "in-octets", str(val))
def add_ip_addr(ifname, iface_out):
"""Fetch interface address information from kernel"""
data = run_json_cmd(['ip', '-j', 'addr', 'show', 'dev', ifname],
f"ip-addr-show-dev-{ifname}.json")
if len(data) != 1:
print("Error: expected ip addr output to be array with length 1", file=sys.stderr)
sys.exit(1)
iface_in = data[0]
if 'mtu' in iface_in and ifname != "lo":
insert(iface_out, "ietf-ip:ipv4", "mtu", iface_in['mtu'])
# We avoid importing os to check if the file exists (for performance)
val = get_proc_value(f"/proc/sys/net/ipv6/conf/{ifname}/mtu")
if val is not None:
insert(iface_out, "ietf-ip:ipv6", "mtu", int(val))
if 'addr_info' in iface_in:
inet = []
inet6 = []
for addr in iface_in['addr_info']:
new = {}
if not 'family' in addr:
print("Error: 'family' missing from 'addr_info'", file=sys.stderr)
continue
if 'local' in addr:
new['ip'] = addr['local']
if 'prefixlen' in addr:
new['prefix-length'] = addr['prefixlen']
if 'protocol' in addr:
new['origin'] = json_get_yang_origin(addr)
if addr['family'] == "inet":
inet.append(new)
elif addr['family'] == "inet6":
inet6.append(new)
else:
print("Error: invalid 'family' in 'addr_info'", file=sys.stderr)
sys.exit(1)
insert(iface_out, "ietf-ip:ipv4", "address", inet)
insert(iface_out, "ietf-ip:ipv6", "address", inet6)
def add_ethtool_groups(ifname, iface_out):
"""Fetch interface counters from kernel"""
data = run_json_cmd(['ethtool', '--json', '-S', ifname, '--all-groups'],
f"ethtool-groups-{ifname}.json")
if len(data) != 1:
print("Error: expected ethtool groups output to be array with length 1", file=sys.stderr)
sys.exit(1)
iface_in = data[0]
# TODO: room for improvement here, the "frame" creation could be more dynamic.
if "eth-mac" in iface_in or "rmon" in iface_in:
insert(iface_out, "ieee802-ethernet-interface:ethernet", "statistics", "frame", {})
frame = iface_out['ieee802-ethernet-interface:ethernet']['statistics']['frame']
if "eth-mac" in iface_in:
mac_in = iface_in['eth-mac']
if "FramesTransmittedOK" in mac_in:
frame['out-frames'] = str(mac_in['FramesTransmittedOK'])
if "MulticastFramesXmittedOK" in mac_in:
frame['out-multicast-frames'] = str(mac_in['MulticastFramesXmittedOK'])
if "BroadcastFramesXmittedOK" in mac_in:
frame['out-broadcast-frames'] = str(mac_in['BroadcastFramesXmittedOK'])
if "FramesReceivedOK" in mac_in:
frame['in-frames'] = str(mac_in['FramesReceivedOK'])
if "MulticastFramesReceivedOK" in mac_in:
frame['in-multicast-frames'] = str(mac_in['MulticastFramesReceivedOK'])
if "BroadcastFramesReceivedOK" in mac_in:
frame['in-broadcast-frames'] = str(mac_in['BroadcastFramesReceivedOK'])
if "FrameCheckSequenceErrors" in mac_in:
frame['in-error-fcs-frames'] = str(mac_in['FrameCheckSequenceErrors'])
if "FramesLostDueToIntMACRcvError" in mac_in:
frame['in-error-mac-internal-frames'] = str(mac_in['FramesLostDueToIntMACRcvError'])
tot = 0
found = False
if "FramesReceivedOK" in mac_in:
tot += mac_in['FramesReceivedOK']
found = True
if "FrameCheckSequenceErrors" in mac_in:
tot += mac_in['FrameCheckSequenceErrors']
found = True
if "FramesLostDueToIntMACRcvError" in mac_in:
tot += mac_in['FramesLostDueToIntMACRcvError']
found = True
if "AlignmentErrors" in mac_in:
tot += mac_in['AlignmentErrors']
found = True
if "etherStatsOversizePkts" in mac_in:
tot += mac_in['etherStatsOversizePkts']
found = True
if "etherStatsJabbers" in mac_in:
tot += mac_in['etherStatsJabbers']
found = True
if found:
frame['in-total-frames'] = str(tot)
if "rmon" in iface_in:
rmon_in = iface_in['rmon']
if "undersize_pkts" in rmon_in:
frame['in-error-undersize-frames'] = str(rmon_in['undersize_pkts'])
tot = 0
found = False
if "etherStatsJabbers" in rmon_in:
tot += rmon_in['etherStatsJabbers']
found = True
if "etherStatsOversizePkts" in rmon_in:
tot += rmon_in['etherStatsOversizePkts']
found = True
if found:
frame['in-error-oversize-frames'] = str(tot)
def add_ethtool_std(ifname, iface_out):
"""Fetch interface speed/duplex/autoneg from kernel"""
keys = ['Speed', 'Duplex', 'Auto-negotiation']
result = {}
lines = run_cmd(['ethtool', ifname], f"ethtool-{ifname}.txt")
for line in lines:
line = line.strip()
key = line.split(':', 1)[0].strip()
if key in keys:
key, value = line.split(':', 1)
result[key.strip()] = value.strip()
if "Auto-negotiation" in result:
if result['Auto-negotiation'] == "on":
insert(iface_out, "ieee802-ethernet-interface:ethernet", "auto-negotiation", "enable", True)
else:
insert(iface_out, "ieee802-ethernet-interface:ethernet", "auto-negotiation", "enable", False)
if "Duplex" in result:
if result['Duplex'] == "Half":
insert(iface_out, "ieee802-ethernet-interface:ethernet", "duplex", "half")
elif result['Duplex'] == "Full":
insert(iface_out, "ieee802-ethernet-interface:ethernet", "duplex", "full")
else:
insert(iface_out, "ieee802-ethernet-interface:ethernet", "duplex", "unknown")
if "Speed" in result and result['Speed'] != "Unknown!":
# Avoid importing re (performance)
num = ''.join(filter(str.isdigit, result['Speed']))
if num:
num = round((int(num) / 1000), 3)
insert(iface_out, "ieee802-ethernet-interface:ethernet", "speed", str(num))
# Helper function to add tagged/untagged interfaces to a vlan dict in a list
def _add_vlan_iface(vlans, vid, key, val):
for d in vlans:
if d['vid'] == vid:
if key in d:
d[key].append(val)
else:
d[key] = [val]
return
vlans.append({'vid': vid, key: [val]})
def add_vlans_to_bridge(brname, iface_out):
slaves = [] # Contains all interfaces that has this bridge as 'master'
for iface in run_json_cmd(['bridge', '-j', 'link'], "bridge-link.json"):
if "master" in iface and iface['master'] == brname:
slaves.append(iface['ifname'])
vlans = [] # Contains all vlans and slaves belonging to this bridge
for iface in run_json_cmd(['bridge', '-j', 'vlan'], "bridge-vlan.json"):
if iface['ifname'] not in slaves and iface['ifname'] != brname:
continue
for vlan in iface['vlans']:
if 'flags' in vlan and 'Egress Untagged' in vlan['flags']:
_add_vlan_iface(vlans, vlan['vlan'], 'untagged', iface['ifname'])
else:
_add_vlan_iface(vlans, vlan['vlan'], 'tagged', iface['ifname'])
insert(iface_out, "infix-interfaces:bridge", "vlans", "vlan", vlans)
def main():
global TESTPATH
parser = argparse.ArgumentParser(description="YANG data creator")
parser.add_argument("model", help="YANG Model")
parser.add_argument("-p", "--param", default=None, help="Model dependant parameter")
parser.add_argument("-t", "--test", default=None, help="Test data base path")
args = parser.parse_args()
if args.test:
TESTPATH = args.test
else:
TESTPATH = ""
if args.model == 'ietf-interfaces':
# For now, we handle each interface separately, as this is how it's
# currently implemented in sysrepo. I.e sysrepo will subscribe to
# each individual interface and query it for YANG data.
if not args.param:
print("usage: yanger ietf-interfaces -p INTERFACE", file=sys.stderr)
sys.exit(1)
yang_data = {
"ietf-interfaces:interfaces": {
"interface": [{}]
}
}
ifname = args.param
iface_out = yang_data['ietf-interfaces:interfaces']['interface'][0]
add_ip_link(ifname, iface_out, )
add_ip_addr(ifname, iface_out)
if 'type' in iface_out and iface_out['type'] == "infix-if-type:ethernet":
add_ethtool_groups(ifname, iface_out)
add_ethtool_std(ifname, iface_out)
if 'type' in iface_out and iface_out['type'] == "infix-if-type:bridge":
add_vlans_to_bridge(ifname, iface_out)
elif args.model == 'ietf-routing':
yang_data = {
"ietf-routing:routing": {
"ribs": {
"rib": [{
"name": "ipv4",
"address-family": "ipv4"
},
{
"name": "ipv6",
"address-family": "ipv6"
}]
}
}
}
ipv4routes = yang_data['ietf-routing:routing']['ribs']['rib'][0]
ipv6routes = yang_data['ietf-routing:routing']['ribs']['rib'][1]
add_ipv4_route(ipv4routes)
add_ipv6_route(ipv6routes)
elif args.model == 'ietf-ospf':
yang_data = {
"ietf-routing:routing": {
"control-plane-protocols": {
"control-plane-protocol": [
{
"type": "ietf-ospf:ospfv2",
"name": "default",
"ietf-ospf:ospf": {
"ietf-ospf:areas":
{
}
}
}]
}
}
}
add_ospf(yang_data['ietf-routing:routing']['control-plane-protocols']['control-plane-protocol'][0]["ietf-ospf:ospf"])
elif args.model == 'ietf-hardware':
yang_data = {
"ietf-hardware:hardware": {
}
}
add_hardware(yang_data["ietf-hardware:hardware"])
elif args.model == 'infix-containers':
yang_data = {
"infix-containers:containers": {
"container": []
}
}
add_container(yang_data['infix-containers:containers']['container'])
else:
print(f"Unsupported model {args.model}", file=sys.stderr)
sys.exit(1)
print(json.dumps(yang_data, indent=2))
if __name__ == "__main__":
main()