|
11 | 11 | from netbox_agent.config import config |
12 | 12 | from netbox_agent.config import netbox_instance as nb |
13 | 13 | from netbox_agent.ethtool import Ethtool |
| 14 | +from netbox_agent.ifconfig import Ifconfig |
14 | 15 | from netbox_agent.ipmi import IPMI |
15 | 16 | from netbox_agent.lldp import LLDP |
16 | 17 |
|
@@ -46,13 +47,69 @@ def __init__(self, server, *args, **kwargs): |
46 | 47 | def get_network_type(): |
47 | 48 | return NotImplementedError |
48 | 49 |
|
49 | | - def scan(self): |
50 | | - nics = [] |
51 | | - for interface in os.listdir("/sys/class/net/"): |
| 50 | + def _use_sysfs(self): |
| 51 | + """Whether Linux sysfs (/sys/class/net) is available. |
| 52 | +
|
| 53 | + When it isn't (e.g. *BSD), interface facts come from ``ifconfig`` instead. |
| 54 | + """ |
| 55 | + return os.path.isdir("/sys/class/net") |
| 56 | + |
| 57 | + def _ifconfig_interfaces(self): |
| 58 | + """Lazily parse ``ifconfig -a`` once, for the non-sysfs (BSD) code path.""" |
| 59 | + if not hasattr(self, "_ifconfig_cache"): |
| 60 | + self._ifconfig_cache = Ifconfig().interfaces |
| 61 | + return self._ifconfig_cache |
| 62 | + |
| 63 | + def _interface_names(self): |
| 64 | + if self._use_sysfs(): |
52 | 65 | # ignore if it's not a link (ie: bonding_masters etc) |
53 | | - if not os.path.islink("/sys/class/net/{}".format(interface)): |
54 | | - continue |
| 66 | + return [ |
| 67 | + i |
| 68 | + for i in os.listdir("/sys/class/net/") |
| 69 | + if os.path.islink("/sys/class/net/{}".format(i)) |
| 70 | + ] |
| 71 | + return list(self._ifconfig_interfaces().keys()) |
| 72 | + |
| 73 | + def _interface_mac(self, interface, ethtool): |
| 74 | + if config.network.primary_mac == "permanent" and ethtool and ethtool.get("mac_address"): |
| 75 | + mac = ethtool["mac_address"] |
| 76 | + elif self._use_sysfs(): |
| 77 | + mac = open("/sys/class/net/{}/address".format(interface), "r").read().strip() |
| 78 | + if mac == "00:00:00:00:00:00": |
| 79 | + mac = None |
| 80 | + else: |
| 81 | + mac = self._ifconfig_interfaces().get(interface, {}).get("mac") |
| 82 | + if mac == "00:00:00:00:00:00": |
| 83 | + mac = None |
| 84 | + if mac: |
| 85 | + mac = mac.upper() |
| 86 | + return mac |
| 87 | + |
| 88 | + def _interface_mtu(self, interface): |
| 89 | + if self._use_sysfs(): |
| 90 | + return int(open("/sys/class/net/{}/mtu".format(interface), "r").read().strip()) |
| 91 | + return self._ifconfig_interfaces().get(interface, {}).get("mtu") |
| 92 | + |
| 93 | + def _interface_bonding(self, interface): |
| 94 | + if self._use_sysfs() and os.path.isdir("/sys/class/net/{}/bonding".format(interface)): |
| 95 | + slaves = open("/sys/class/net/{}/bonding/slaves".format(interface)).read().split() |
| 96 | + return True, slaves |
| 97 | + return False, [] |
| 98 | + |
| 99 | + def _interface_virtual(self, interface): |
| 100 | + if self._use_sysfs(): |
| 101 | + return Path(f"/sys/class/net/{interface}").resolve().parent == VIRTUAL_NET_FOLDER |
| 102 | + # No sysfs (e.g. *BSD): fall back to a name-based heuristic for the common |
| 103 | + # virtual interface types. |
| 104 | + return bool( |
| 105 | + re.match( |
| 106 | + r"^(lo|tun|tap|bridge|vlan|gif|gre|epair|pflog|pfsync|enc|ipfw)\d*$", interface |
| 107 | + ) |
| 108 | + ) |
55 | 109 |
|
| 110 | + def scan(self): |
| 111 | + nics = [] |
| 112 | + for interface in self._interface_names(): |
56 | 113 | if config.network.ignore_interfaces and re.match( |
57 | 114 | config.network.ignore_interfaces, interface |
58 | 115 | ): |
@@ -90,33 +147,15 @@ def scan(self): |
90 | 147 | ip_addr.append(addr) |
91 | 148 |
|
92 | 149 | ethtool = Ethtool(interface).parse() |
93 | | - if ( |
94 | | - config.network.primary_mac == "permanent" |
95 | | - and ethtool |
96 | | - and ethtool.get("mac_address") |
97 | | - ): |
98 | | - mac = ethtool["mac_address"] |
99 | | - else: |
100 | | - mac = open("/sys/class/net/{}/address".format(interface), "r").read().strip() |
101 | | - if mac == "00:00:00:00:00:00": |
102 | | - mac = None |
103 | | - if mac: |
104 | | - mac = mac.upper() |
| 150 | + mac = self._interface_mac(interface, ethtool) |
| 151 | + mtu = self._interface_mtu(interface) |
105 | 152 |
|
106 | | - mtu = int(open("/sys/class/net/{}/mtu".format(interface), "r").read().strip()) |
107 | 153 | vlan = None |
108 | 154 | if len(interface.split(".")) > 1: |
109 | 155 | vlan = int(interface.split(".")[1]) |
110 | 156 |
|
111 | | - bonding = False |
112 | | - bonding_slaves = [] |
113 | | - if os.path.isdir("/sys/class/net/{}/bonding".format(interface)): |
114 | | - bonding = True |
115 | | - bonding_slaves = ( |
116 | | - open("/sys/class/net/{}/bonding/slaves".format(interface)).read().split() |
117 | | - ) |
118 | | - |
119 | | - virtual = Path(f"/sys/class/net/{interface}").resolve().parent == VIRTUAL_NET_FOLDER |
| 157 | + bonding, bonding_slaves = self._interface_bonding(interface) |
| 158 | + virtual = self._interface_virtual(interface) |
120 | 159 |
|
121 | 160 | nic = { |
122 | 161 | "name": interface, |
@@ -555,7 +594,7 @@ def batched(it, n): |
555 | 594 | nic_update += 1 |
556 | 595 |
|
557 | 596 | if hasattr(interface, "mtu"): |
558 | | - if nic["mtu"] != interface.mtu: |
| 597 | + if nic["mtu"] and nic["mtu"] != interface.mtu: |
559 | 598 | logging.info( |
560 | 599 | "Interface mtu is wrong, updating to: {mtu}".format(mtu=nic["mtu"]) |
561 | 600 | ) |
|
0 commit comments