confd,statd: add support for link aggregates

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2025-02-12 23:21:08 +01:00
committed by Tobias Waldekranz
parent ec2e161649
commit 78499757b0
9 changed files with 644 additions and 120 deletions
+1
View File
@@ -30,6 +30,7 @@ confd_plugin_la_SOURCES = \
ieee802-ethernet-interface.c \
ietf-ip.c \
infix-if-bridge.c \
infix-if-lag.c \
infix-if-bridge-mcd.c \
infix-if-bridge-port.c \
infix-if-veth.c \
+20 -5
View File
@@ -73,6 +73,10 @@ static int ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path)
inferred.data.string_val = "infix-if-type:ethernet";
else if (!fnmatch("br+([0-9])", ifname, FNM_EXTMATCH))
inferred.data.string_val = "infix-if-type:bridge";
else if (!fnmatch("bond+([0-9])", ifname, FNM_EXTMATCH))
inferred.data.string_val = "infix-if-type:lag";
else if (!fnmatch("lag+([0-9])", ifname, FNM_EXTMATCH))
inferred.data.string_val = "infix-if-type:lag";
else if (!fnmatch("docker+([0-9])", ifname, FNM_EXTMATCH))
inferred.data.string_val = "infix-if-type:bridge";
else if (!fnmatch("dummy+([0-9])", ifname, FNM_EXTMATCH))
@@ -299,6 +303,7 @@ static int netdag_gen_sysctl_setting(struct dagger *net, const char *ifname, FIL
return 0;
}
static int netdag_gen_sysctl(struct dagger *net,
struct lyd_node *cif,
struct lyd_node *dif)
@@ -355,6 +360,8 @@ static int netdag_gen_afspec_add(sr_session_ctx_t *session, struct dagger *net,
case IFT_GRE:
case IFT_GRETAP:
return gre_gen(NULL, cif, ip);
case IFT_LAG:
return lag_gen(dif, cif, ip, 1);
case IFT_VETH:
return veth_gen(NULL, cif, ip);
case IFT_VLAN:
@@ -368,7 +375,6 @@ static int netdag_gen_afspec_add(sr_session_ctx_t *session, struct dagger *net,
case IFT_UNKNOWN:
sr_session_set_error_message(net->session, "%s: unsupported interface type \"%s\"",
ifname, lydx_get_cattr(cif, "type"));
return -ENOSYS;
}
__builtin_unreachable();
@@ -383,6 +389,8 @@ static int netdag_gen_afspec_set(sr_session_ctx_t *session, struct dagger *net,
switch (iftype_from_iface(cif)) {
case IFT_BRIDGE:
return bridge_gen(dif, cif, ip, 0);
case IFT_LAG:
return lag_gen(dif, cif, ip, 0);
case IFT_VLAN:
return vlan_gen(dif, cif, ip);
@@ -420,6 +428,9 @@ static bool netdag_must_del(struct lyd_node *dif, struct lyd_node *cif)
case IFT_GRE:
case IFT_GRETAP:
return lydx_get_descendant(lyd_child(dif), "gre", NULL);
case IFT_LAG:
return lydx_get_child(dif, "custom-phys-address") ||
lydx_get_descendant(lyd_child(dif), "lag", "mode", NULL);
case IFT_VLAN:
return lydx_get_descendant(lyd_child(dif), "vlan", NULL);
case IFT_VETH:
@@ -505,6 +516,7 @@ static int netdag_gen_iface_del(struct dagger *net, struct lyd_node *dif,
case IFT_DUMMY:
case IFT_GRE:
case IFT_GRETAP:
case IFT_LAG:
case IFT_VLAN:
case IFT_VXLAN:
case IFT_UNKNOWN:
@@ -593,6 +605,10 @@ static sr_error_t netdag_gen_iface(sr_session_ctx_t *session, struct dagger *net
if (err)
goto err_close_ip;
err = lag_port_gen(dif, cif);
if (err)
goto err_close_ip;
/* Set type specific attributes */
if (!fixed && op != LYDX_OP_CREATE) {
err = netdag_gen_afspec_set(session, net, dif, cif, ip);
@@ -613,8 +629,7 @@ static sr_error_t netdag_gen_iface(sr_session_ctx_t *session, struct dagger *net
fprintf(ip, "link set alias \"%s\" dev %s\n", attr ?: "", ifname);
/* Bring interface back up, if enabled */
attr = lydx_get_cattr(cif, "enabled");
if (!attr || !strcmp(attr, "true"))
if (lydx_is_enabled(cif, "enabled"))
fprintf(ip, "link set dev %s up state up\n", ifname);
err = err ? : netdag_gen_sysctl(net, cif, dif);
@@ -641,8 +656,8 @@ static int netdag_init_iface(struct lyd_node *cif)
switch (iftype_from_iface(cif)) {
case IFT_BRIDGE:
return bridge_add_deps(cif);
/* case IFT_LAG: */
/* return lag_add_deps(cif); */
case IFT_LAG:
return lag_add_deps(cif);
case IFT_VLAN:
return vlan_add_deps(cif);
case IFT_VETH:
+23 -5
View File
@@ -28,6 +28,7 @@
_map(IFT_ETHISH, "infix-if-type:etherlike") \
_map(IFT_GRE, "infix-if-type:gre") \
_map(IFT_GRETAP, "infix-if-type:gretap") \
_map(IFT_LAG, "infix-if-type:lag") \
_map(IFT_LO, "infix-if-type:loopback") \
_map(IFT_VETH, "infix-if-type:veth") \
_map(IFT_VLAN, "infix-if-type:vlan") \
@@ -70,14 +71,26 @@ static inline const char *bridge_tagtype2str(const char *type)
return NULL;
}
static inline bool is_bridge_port(struct lyd_node *cif)
static inline struct lyd_node *get_master(struct lyd_node *cif)
{
struct lyd_node *node = lydx_get_descendant(lyd_child(cif), "bridge-port", NULL);
struct lyd_node *node;
if (!node || !lydx_get_child(node, "bridge"))
return false;
node = lydx_get_descendant(lyd_child(cif), "bridge-port", NULL);
if (node)
return lydx_get_child(node, "bridge");
return true;
node = lydx_get_descendant(lyd_child(cif), "lag-port", NULL);
if (node)
return lydx_get_child(node, "lag");
return NULL;
}
static inline bool is_member_port(struct lyd_node *cif)
{
if (get_master(cif))
return true;
return false;
}
@@ -108,6 +121,11 @@ int bridge_port_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip);
/* infix-if-gre.c */
int gre_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip);
/* infix-if-lag.c */
int lag_port_gen(struct lyd_node *dif, struct lyd_node *cif);
int lag_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip, int add);
int lag_add_deps(struct lyd_node *cif);
/* infix-if-veth.c */
bool veth_is_primary(struct lyd_node *cif);
int ifchange_cand_infer_veth(sr_session_ctx_t *session, const char *path);
+2 -2
View File
@@ -22,7 +22,7 @@ int netdag_gen_ipv6_autoconf(struct dagger *net, struct lyd_node *cif,
struct lyd_node *node;
FILE *fp;
if (!ipconf || !lydx_is_enabled(ipconf, "enabled") || is_bridge_port(cif)) {
if (!ipconf || !lydx_is_enabled(ipconf, "enabled") || is_member_port(cif)) {
fputs(" addrgenmode none", ip);
return 0;
}
@@ -86,7 +86,7 @@ int netdag_gen_ipv4_autoconf(struct dagger *net, struct lyd_node *cif,
snprintf(defaults, sizeof(defaults), "/etc/default/zeroconf-%s", ifname);
/* no ipv4 at all, ipv4 selectively disabled, or interface is a bridge port */
if (!ipconf || !lydx_is_enabled(ipconf, "enabled") || is_bridge_port(cif))
if (!ipconf || !lydx_is_enabled(ipconf, "enabled") || is_member_port(cif))
goto disable;
/*
+189
View File
@@ -0,0 +1,189 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include <fnmatch.h>
#include <stdbool.h>
#include <jansson.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <srx/common.h>
#include <srx/lyx.h>
#include <srx/srx_val.h>
#include "ietf-interfaces.h"
/*
* The lag-port noode is only in the dif at initial creation, or removal.
* When a lag is recreated, e.g., when changing mode, the lag-port is only
* in the cif.
*
* To simplify confd dependency handling we always call this function, so
* a lag member always has its lag master reset. This is cruicial in the
* case described above when the the lag is removed to change mode.
*/
int lag_port_gen(struct lyd_node *dif, struct lyd_node *cif)
{
const char *ifname = lydx_get_cattr(cif, "name");
struct lyd_node *node;
const char *lagname;
int err = 0;
FILE *fp;
node = lydx_get_descendant(lyd_child(dif), "lag-port", NULL);
if (node) {
struct lydx_diff lagdiff = { 0 };
if (!lydx_get_diff(lydx_get_child(node, "lag"), &lagdiff))
goto fail;
if (lagdiff.old) {
fp = dagger_fopen_net_exit(&confd.netdag, lagdiff.old,
NETDAG_EXIT_LOWERS, "delete-ports.ip");
if (!fp) {
err = -EIO;
goto fail;
}
fprintf(fp, "link set %s nomaster\n", ifname);
fclose(fp);
return 0;
}
lagname = lagdiff.new;
} else {
node = lydx_get_descendant(lyd_child(cif), "lag-port", NULL);
lagname = lydx_get_cattr(node, "lag");
if (!node || !lagname)
goto fail; /* done, nothing to do */
}
fp = dagger_fopen_net_init(&confd.netdag, lagname, NETDAG_INIT_LOWERS,
"add-ports.ip");
if (!fp)
return -EIO;
fprintf(fp, "link set %s down master %s\n", ifname, lagname);
if (lydx_is_enabled(cif, "enabled"))
fprintf(fp, "link set dev %s up state up\n", ifname);
fclose(fp);
err = dagger_add_dep(&confd.netdag, lagname, ifname);
if (err)
ERROR("%s: unable to add dep to %s", ifname, lagname);
fail:
return err;
}
int lag_gen(struct lyd_node *dif, struct lyd_node *cif, FILE *ip, int add)
{
const char *lagname = lydx_get_cattr(cif, "name");
const char *op = add ? "add" : "set";
struct lyd_node *lag, *mon;
const char *mode;
int err = 0;
lag = lydx_get_descendant(lyd_child(cif), "lag", NULL);
if (!lag)
return -EINVAL;
/* Must take lag down if changing mode */
if (!add)
fprintf(ip, "link set %s down\n", lagname);
fprintf(ip, "link %s dev %s", op, lagname);
if (add)
link_gen_address(cif, ip);
fprintf(ip, " type bond min_links 1");
mode = lydx_get_cattr(lag, "mode");
if (!strcmp(mode, "lacp")) {
struct lyd_node *lacp = lydx_get_child(lag, "lacp");
if (add)
fprintf(ip, " mode 802.3ad ad_select bandwidth");
mode = lydx_get_cattr(lacp, "mode");
if (!strcmp(mode, "active"))
mode = "on";
else
mode = "off";
fprintf(ip, " lacp_rate %s lacp_active %s",
lydx_get_cattr(lacp, "rate"), mode);
} else {
/* XXX: mode hard-coded for now for mv88e6xxxx operation */
if (add)
fprintf(ip, " mode balance-xor");
}
/*
* Required in lacp mode, we rely on it also in static mode.
* A previous attempt supported arp-monitor, but it does not
* work with mv88e6xxx link aggregates, unfortunately so was
* dropped for the final version.
*/
fprintf(ip, " miimon 100 use_carrier 1");
mon = lydx_get_descendant(lyd_child(lag), "link-monitor", NULL);
if (mon) {
const struct lyd_node *debounce;
debounce = lydx_get_descendant(lyd_child(mon), "debounce", NULL);
if (debounce) {
const char *msec;
msec = lydx_get_cattr(mon, "up");
if (msec)
fprintf(ip, " updelay %s", msec);
msec = lydx_get_cattr(mon, "down");
if (msec)
fprintf(ip, " downdelay %s", msec);
}
}
fputs("\n", ip);
/*
* When netdag_must_del() is triggered, this is when we reattach
* unmodified ports when recreating the lag.
*/
if (add) {
struct lyd_node *node, *cifs;
cifs = lydx_get_descendant(lyd_parent(cif), "interfaces", "interface", NULL);
LYX_LIST_FOR_EACH(cifs, node, "interface")
err += lag_port_gen(NULL, node);
}
return err;
}
int lag_add_deps(struct lyd_node *cif)
{
const char *lagname = lydx_get_cattr(cif, "name");
struct ly_set *ports;
const char *portname;
int err = 0;
uint32_t i;
ports = lydx_find_xpathf(cif, "../interface[lag-port/lag='%s']", lagname);
if (!ports)
return ERR_IFACE(cif, -ENOENT, "Unable to fetch lag ports");
for (i = 0; i < ports->count; i++) {
portname = lydx_get_cattr(ports->dnodes[i], "name");
err = dagger_add_dep(&confd.netdag, lagname, portname);
if (err) {
ERR_IFACE(cif, err, "Unable to depend on \"%s\"", portname);
break;
}
}
ly_set_free(ports, NULL);
return err;
}
+204 -100
View File
@@ -10,6 +10,12 @@ submodule infix-if-lag {
import iana-if-type {
prefix ianaift;
}
import ietf-inet-types {
prefix inet;
}
import ietf-yang-types {
prefix yang;
}
organization "KernelKit";
contact "kernelkit@googlegroups.com";
@@ -24,35 +30,93 @@ submodule infix-if-lag {
* Typedefs
*/
typedef lag-type {
description "Mode values for link aggregates.";
typedef lag-mode {
description "Mode values for link aggregates.";
/* Temporarily limited to 802.1AX and Balanced XOR */
type enumeration {
enum static {
description "Static mode (Balanced XOR).";
value 2;
}
enum lacp {
description "IEEE 802.3ad LACP mode.";
value 4;
}
type enumeration {
enum static {
description "Static mode (Balanced XOR).";
}
enum lacp {
description "IEEE 802.3ad LACP mode.";
}
}
}
typedef lag-type {
description "Static mode type of distribution (driver modes).";
type enumeration {
enum balance-rr;
enum active-backup;
enum balance-xor;
enum broadcast;
enum balance-tlb;
enum balance-alb;
}
}
typedef hash-policy {
description "Egress hash policy, note: offloading limitations!";
type enumeration {
enum layer2;
enum layer3-4;
enum layer2-3;
enum encap2-3;
enum encap3-4;
enum vlan-srcmac;
}
}
typedef member-state {
description "Lag port membership state, taking active part or backup.";
type enumeration {
enum backup;
enum active;
}
}
typedef lacp-mode {
description "LACP mode values.";
description "LACP mode values.";
type enumeration {
enum passive {
description "LACP active mode";
value 0;
}
enum active {
description "LACP passive mode.";
value 1;
}
type enumeration {
enum passive {
description "LACP active mode";
}
enum active {
description "LACP passive mode.";
}
}
}
typedef lacp-rate {
description "LACP rate values.";
type enumeration {
enum slow {
description "Send LACPDUs every 30 seconds (90-second timeout).";
}
enum fast {
description "Send LACPDUs every 1 second (3-second timeout).";
}
}
}
typedef lacp-state {
description "LACP port state flags.";
type enumeration {
enum active;
enum short_timeout;
enum aggregating;
enum in_sync;
enum collecting;
enum distributing;
enum defaulted;
enum expired;
}
}
/*
@@ -62,78 +126,8 @@ submodule infix-if-lag {
grouping hash {
leaf hash {
description "Transmit hash policy.";
type hash-policy;
config false; // For now, staically set to layer2 only
type string;
}
}
grouping lacp-settings {
description "LACP mode settings.";
container lacp {
description "Settings specific for LACP mode.";
uses hash;
leaf mode {
description "Operational mode of LACP, default: active.
- Active: initiates negotiation by sending LACPDUs.
- Passive: waits for the peer to initiate.
At least one end of the link must be in active mode. When both ends
are active, there is slightly more traffic, but the default ensures
fail-safe operation.
Passive mode is typically used for troubleshooting, in dynamic
setups (e.g., MLAG), or to minimize the risk of unintended
aggregation.
For most production scenarios, active mode is preferred to ensure
faster and more predictable link aggregation.";
type lacp-mode;
default "active";
}
leaf rate {
description "Rate of LACP keep-alives, default: slow.
Determines the frequency of LACPDU transmission and the associated
timeout for link failure detection.
- slow: Sends LACPDUs every 30 seconds. The associated timeout is 90
seconds, meaning a link is considered failed after 3 consecutive
missed LACPDUs.
- fast: Sends LACPDUs every 1 second. The associated timeout is 3
seconds, meaning a link is considered failed after 3 consecutive
missed LACPDUs.
The selected rate affects the responsiveness of link failure
detection and the amount of control traffic.";
type enumeration {
enum slow {
description "Send LACPDUs every 30 seconds (90-second timeout).";
}
enum fast {
description "Send LACPDUs every 1 second (3-second timeout).";
}
}
default "slow";
}
}
}
grouping static-settings {
container static {
config false; // For now, we need to read out mode and other status
uses hash;
leaf mode {
description "Active mode for static aggregates.";
type string;
}
}
}
@@ -153,12 +147,100 @@ submodule infix-if-lag {
leaf mode {
description "Link aggregation mode.";
type lag-type;
type lag-mode;
mandatory true;
}
uses lacp-settings;
uses static-settings;
container lacp {
description "Settings specific for LACP mode.";
uses hash;
leaf mode {
description "Operational mode of LACP, default: active.
- Active: initiates negotiation by sending LACPDUs.
- Passive: waits for the peer to initiate.
At least one end of the link must be in active mode. When both ends
are active, there is slightly more traffic, but the default ensures
fail-safe operation.
Passive mode is typically used for troubleshooting, in dynamic
setups (e.g., MLAG), or to minimize the risk of unintended
aggregation.
For most production scenarios, active mode is preferred to ensure
faster and more predictable link aggregation.";
type lacp-mode;
default active;
}
leaf rate {
description "Rate of LACP keep-alives, default: slow.
Determines the frequency of LACPDU transmission and the associated
timeout for link failure detection.
- slow: Sends LACPDUs every 30 seconds. The associated timeout is 90
seconds, meaning a link is considered failed after 3 consecutive
missed LACPDUs.
- fast: Sends LACPDUs every 1 second. The associated timeout is 3
seconds, meaning a link is considered failed after 3 consecutive
missed LACPDUs.
The selected rate affects the responsiveness of link failure
detection and the amount of control traffic.";
type lacp-rate;
default slow;
}
leaf system-priority {
description "Sytem priority used by the node on this LAG interface.
Lower value is higher priority for determining which node
is the controlling system.";
type uint16 {
range "1 .. 65535";
}
}
leaf aggregator-id {
description "Aggregator ID.";
config false;
type uint16;
}
leaf actor-key {
description "Actor key.";
config false;
type uint16;
}
leaf partner-key {
description "Partner key.";
config false;
type uint16;
}
leaf partner-mac {
description "Partner MAC address.";
config false;
type yang:phys-address;
}
}
container static {
config false; // For now, we need to read out mode and other status
leaf mode {
description "Active mode for static aggregates.";
type lag-type;
}
uses hash;
}
container link-monitor {
description "Link monitor properties.";
@@ -183,6 +265,16 @@ submodule infix-if-lag {
}
}
}
must "not(./mode = 'lacp' and ./monitor/arp-monitor/interval > 0)" {
error-message "ARP monitor cannot be enabled in LACP mode.";
description "The Linux bond driver disables miimon when arpmon != 0.";
}
must "not(./mode = 'lacp' and ./monitor/link-monitor/interval = 0)" {
error-message "Link monitor must be enabled in LACP mode.";
description "The Linux bond driver requires miimon in 802.3ad mode.";
}
}
}
@@ -206,24 +298,36 @@ submodule infix-if-lag {
error-message "Must refer to a valid LAG interface.";
}
}
leaf state {
description "Port state, active or backup member.";
description "Link state, active or backup member.";
type member-state;
config false;
}
leaf link-failures {
description "Link failure counter, cannot be reset.";
type uint32;
config false;
type string;
}
container lacp {
description "LACP port state, ours and partner.";
config false;
leaf aggregator-id {
description "Aggregator ID.";
type uint16;
}
leaf-list actor-state {
description "LACP state flags.";
type string;
type lacp-state;
}
leaf-list partner-state {
description "LACP state flags for link partner.";
type string;
type lacp-state;
}
}
}
+118 -1
View File
@@ -451,6 +451,34 @@ class Iface:
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', 'cist', 'state')
self.lag_mode = get_json_data('', self.data, 'infix-interfaces:lag', 'mode')
if self.lag_mode:
self.lag_type = get_json_data('', self.data, 'infix-interfaces:lag', 'static', 'mode')
if self.lag_mode == "lacp":
self.lag_hash = get_json_data('', self.data, 'infix-interfaces:lag', 'lacp', 'hash')
self.lacp_id = get_json_data('', self.data, 'infix-interfaces:lag', 'lacp', 'aggregator-id')
self.lacp_actor_key = get_json_data('', self.data, 'infix-interfaces:lag', 'lacp', 'actor-key')
self.lacp_partner_key = get_json_data('', self.data, 'infix-interfaces:lag', 'lacp', 'partner-key')
self.lacp_partner_mac = get_json_data('', self.data, 'infix-interfaces:lag', 'lacp', 'partner-mac')
self.lacp_sys_prio = get_json_data('', self.data, 'infix-interfaces:lag', 'lacp', 'system-priority')
else:
self.lag_hash = get_json_data('', self.data, 'infix-interfaces:lag', 'static', 'hash')
self.link_updelay = get_json_data('', self.data, 'infix-interfaces:lag', 'link-monitor', 'debounce', 'up')
self.link_downdelay = get_json_data('', self.data, 'infix-interfaces:lag', 'link-monitor', 'debounce', 'down')
self.lacp_mode = get_json_data('', self.data, 'infix-interfaces:lag', 'lacp', 'mode')
rate = get_json_data('', self.data, 'infix-interfaces:lag', 'lacp', 'rate')
self.lacp_rate = "fast (1s)" if rate == "fast" else "slow (30 sec)"
self.lag = get_json_data('', self.data, 'infix-interfaces:lag-port', 'lag')
if self.lag:
self.lag_state = get_json_data('', self.data, 'infix-interfaces:lag-port', 'state')
self.lacp_id = get_json_data('', self.data, 'infix-interfaces:lag-port', 'lacp', 'aggregator-id')
self.lacp_state = get_json_data('', self.data, 'infix-interfaces:lag-port', 'lacp', 'actor-state')
self.lacp_pstate = get_json_data('', self.data, 'infix-interfaces:lag-port', 'lacp', 'partner-state')
self.link_failures = get_json_data('', self.data, 'infix-interfaces:lag-port', 'link-failures')
self.containers = get_json_data('', self.data, 'infix-interfaces:container-network', 'containers')
@@ -491,6 +519,9 @@ class Iface:
def is_bridge(self):
return self.type == "infix-if-type:bridge"
def is_lag(self):
return self.type == "infix-if-type:lag"
def is_veth(self):
return self.data['type'] == "infix-if-type:veth"
@@ -635,6 +666,59 @@ class Iface:
self.pr_proto_ipv4()
self.pr_proto_ipv6()
def pr_proto_lag(self, member=True):
data_str = ""
row = f"{'lag':<{Pad.proto}}"
if member:
state = self.lag_state.upper()
if self.oper() == "up":
row += Decore.green(f"{state:<{Pad.state}}")
else:
row += Decore.yellow(f"{state:<{Pad.state}}")
if self.lacp_state:
lacp = ', '.join(self.lacp_state)
data_str += lacp
else:
dec = Decore.green if self.oper() == "up" else Decore.yellow
row += dec(f"{self.oper().upper():<{Pad.state}}")
data_str += f"{self.lag_mode}"
if self.lag_mode == "lacp":
data_str += f": {self.lacp_mode}"
data_str += f", rate: {self.lacp_rate}"
data_str += f", hash: {self.lag_hash}"
else:
data_str += f": {self.lag_type}"
data_str += f", hash: {self.lag_hash}"
if data_str:
row += f"{data_str:<{Pad.data}}"
print(row)
def pr_lag(self, _ifaces):
self.pr_name(pipe="")
self.pr_proto_lag(member=False)
lowers = []
for _iface in [Iface(data) for data in _ifaces]:
if _iface.lag and _iface.lag == self.name:
lowers.append(_iface)
if lowers:
self.pr_proto_eth(pipe='')
self.pr_proto_ipv4(pipe='')
self.pr_proto_ipv6(pipe='')
else:
self.pr_proto_eth(pipe=' ')
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_lag()
def pr_veth(self, _ifaces):
self.pr_name(pipe="")
self.pr_proto_veth()
@@ -716,6 +800,32 @@ class Iface:
if self.phys_address:
print(f"{'physical address':<{20}}: {self.phys_address}")
if self.lag_mode:
print(f"{'lag mode':<{20}}: {self.lag_mode}")
if self.lag_mode == "lacp":
print(f"{'lag hash':<{20}}: {self.lag_hash}")
print(f"{'lacp mode':<{20}}: {self.lacp_mode}")
print(f"{'lacp rate':<{20}}: {self.lacp_rate}")
print(f"{'lacp aggregate id':<{20}}: {self.lacp_id}")
print(f"{'lacp system priority':<{20}}: {self.lacp_sys_prio}")
print(f"{'lacp actor key':<{20}}: {self.lacp_actor_key}")
print(f"{'lacp partner key':<{20}}: {self.lacp_partner_key}")
print(f"{'lacp partner mac':<{20}}: {self.lacp_partner_mac}")
else:
print(f"{'lag type':<{20}}: {self.lag_type}")
print(f"{'lag hash':<{20}}: {self.lag_hash}")
print(f"{'link debounce up':<{20}}: {self.link_updelay} msec")
print(f"{'link debounce down':<{20}}: {self.link_downdelay} msec")
if self.lag:
print(f"{'lag member':<{20}}: {self.lag}")
print(f"{'lag member state':<{20}}: {self.lag_state}")
if self.lacp_state:
print(f"{'lacp aggregate id':<{20}}: {self.lacp_id}")
print(f"{'lacp actor state':<{20}}: {', '.join(self.lacp_state)}")
print(f"{'lacp partner state':<{20}}: {', '.join(self.lacp_pstate)}")
print(f"{'link failure count':<{20}}: {self.link_failures}")
if self.ipv4_addr:
first = True
for addr in self.ipv4_addr:
@@ -872,6 +982,10 @@ def pr_interface_list(json):
iface.pr_bridge(ifaces)
continue
if iface.is_lag():
iface.pr_lag(ifaces)
continue
if iface.is_veth():
iface.pr_veth(ifaces)
continue
@@ -892,11 +1006,14 @@ def pr_interface_list(json):
iface.pr_vlan(ifaces)
continue
# These interfaces are printed by there parent, such as bridge
# These interfaces are printed by their parent, such as bridge
if iface.lower_if:
continue
if iface.bridge:
continue
if iface.lag:
continue
print_interface(iface)
@@ -0,0 +1,79 @@
"""The lag/bond oper-status always follows the carrier"""
def lower(iplink):
"""Return a dictionary of the status of a lag member"""
port = {
"lag": iplink['master'],
}
info = iplink['linkinfo']['info_slave_data']
if info:
# active or backup link
port['state'] = info['state'].lower()
port['link-failures'] = info['link_failure_count']
# On etherlike interfaces and tap interfaces oper-status lies
# if info['mii_status'] == "DOWN":
# iface['oper-status'] = "down"
# Initialize lacp dict only if we encounter LACP-related fields
if 'ad_aggregator_id' in info:
port['lacp'] = {}
port['lacp']['aggregator-id'] = info['ad_aggregator_id']
port['lacp']['actor-state'] = info['ad_actor_oper_port_state_str']
port['lacp']['partner-state'] = info['ad_partner_oper_port_state_str']
else:
port['state'] = 'backup'
port['link-failures'] = 0
return port
def lag(iplink):
"""Return a dictionary of the status of the lag"""
mode = {
"balance-xor": "static",
"802.3ad": "lacp",
}
hash_policy = {
"layer2": "layer2",
"layer3+4": "layer3-4",
"layer2+3": "layer2-3",
"encap2+3": "encap2-3",
"encap3+4": "encap3-4",
"vlan+srcmac": "vlan-srcmac",
}
bond = {}
info = iplink["linkinfo"]["info_data"]
if info:
bond["mode"] = mode.get(info['mode'], "static")
if bond["mode"] == "lacp":
bond["lacp"] = {
"mode": 'active' if info['ad_lacp_active'] == "on" else 'passive',
"rate": info['ad_lacp_rate'],
"hash": hash_policy.get(info['xmit_hash_policy'], "layer2"),
}
if 'ad_info' in info:
bond["lacp"]["aggregator-id"] = info['ad_info']['aggregator']
bond["lacp"]["actor-key"] = info['ad_info']['actor_key']
bond["lacp"]["partner-key"] = info['ad_info']['partner_key']
bond["lacp"]["partner-mac"] = info['ad_info']['partner_mac']
if 'ad_actor_sys_prio' in info:
bond["lacp"]["system-priority"] = info['ad_actor_sys_prio']
else:
bond["static"] = {
"mode": info['mode'],
"hash": info['xmit_hash_policy']
}
bond["link-monitor"] = {
"debounce": {
"up": info['updelay'],
"down": info['downdelay']
}
}
return bond
@@ -3,6 +3,7 @@ from . import common
from . import bridge
from . import ethernet
from . import ip
from . import lag
from . import tun
from . import veth
from . import vlan
@@ -117,16 +118,16 @@ def interface(iplink, ipaddr):
interface["infix-interfaces:bridge"] = br
if brport := bridge.lower(iplink):
interface["infix-interfaces:bridge-port"] = brport
# case "infix-if-type:lag":
# if l := lag.lag(iplink):
# interface["infix-interfaces:lag"] = l
case "infix-if-type:lag":
if lg := lag.lag(iplink):
interface["infix-interfaces:lag"] = lg
case "infix-if-type:ethernet":
if eth := ethernet.ethernet(iplink):
interface["ieee802-ethernet-interface:ethernet"] = eth
case "infix-if-type:vxlan":
if vxlan := tun.vxlan(iplink):
interface["infix-interfaces:vxlan"] = vxlan
case "infix-if-type:gre"|"infix-if-type:gretap":
case "infix-if-type:gre" | "infix-if-type:gretap":
if gre := tun.gre(iplink):
interface["infix-interfaces:gre"] = gre
case "infix-if-type:veth":
@@ -140,9 +141,9 @@ def interface(iplink, ipaddr):
case "infix-interfaces:bridge-port":
if brport := bridge.lower(iplink):
interface["infix-interfaces:bridge-port"] = brport
# case "infix-interfaces:lag-port":
# if lagport := lag.lower(iplink):
# interface["infix-interfaces:lag-port"] = lagport
case "infix-interfaces:lag-port":
if lagport := lag.lower(iplink):
interface["infix-interfaces:lag-port"] = lagport
return interface