blob: 7fbba28f530c1f68052e7a08be99efb5d7432e68 [file] [log] [blame]
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +01001import re
2import sys
3import subprocess
4import json
5
6
7def shell(command):
8 _ps = subprocess.Popen(
9 command.split(),
10 stdout=subprocess.PIPE
11 ).communicate()[0].decode()
12
13 return _ps
14
15
Alex Savatieievd79dde12019-03-13 19:07:46 -050016def cut_option(_param, _options_list, _option="n/a"):
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +010017 _result_list = []
18 if _param in _options_list:
19 _index = _options_list.index(_param)
20 _option = _options_list[_index+1]
21 _l1 = _options_list[:_index]
22 _l2 = _options_list[_index+2:]
23 _result_list = _l1 + _l2
24 else:
25 _result_list = _options_list
26 return _option, _result_list
27
28
Alex Savatieiev0137dad2019-01-25 16:18:42 +010029def get_linked_devices(if_name):
30 if '@' in if_name:
31 _name = if_name[:if_name.index('@')]
32 else:
33 _name = if_name
Alex Savatieiev5d1eebb2019-01-25 18:15:36 +010034 # identify device type
35 _dev_link_path = shell('readlink /sys/class/net/{}'.format(_name))
36 _type = "unknown"
37 if len(_dev_link_path) > 0:
38 _tmp = _dev_link_path.split('/')
39 _tmp = _tmp[_tmp.index("devices") + 1]
40 if _tmp.startswith("pci"):
41 _type = "physical"
42 elif _tmp.startswith("virtual"):
43 _type = "virtual"
44
45 # get linked devices if any
Alex Savatieiev0137dad2019-01-25 16:18:42 +010046 _links = shell(
47 "find /sys/class/net/{}/ -type l".format(_name)
48 )
49 # there can be only one parent device
50 _lower = None
51 # can be more than one child device
52 _upper = None
53 for line in _links.splitlines():
54 _line = line.rsplit('/', 1)[1]
55 if _line.startswith("upper_"):
Alex Savatieievbd256e82019-01-25 18:27:01 +010056 if not _upper:
57 _upper = []
58 _upper.append(_line[6:])
Alex Savatieiev0137dad2019-01-25 16:18:42 +010059 elif _line.startswith("lower_"):
60 if not _lower:
61 _lower = []
62 _lower.append(_line[6:])
63
Alex Savatieiev5d1eebb2019-01-25 18:15:36 +010064 return _lower, _upper, _type
Alex Savatieiev0137dad2019-01-25 16:18:42 +010065
66
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +010067def get_ifs_data():
Alex Savatieievd79dde12019-03-13 19:07:46 -050068 # Collect interface and IPs data
69 # Compile regexps for detecting IPs
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +010070 if_start = re.compile("^[0-9]+: .*: \<.*\> .*$")
Alex Savatieiev0137dad2019-01-25 16:18:42 +010071 if_link = re.compile("^\s{4}link\/ether\ .*$")
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +010072 if_ipv4 = re.compile("^\s{4}inet\ .*$")
Alex Savatieievd79dde12019-03-13 19:07:46 -050073 # variable prototypes
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +010074 _ifs = {}
75 _if_name = None
Alex Savatieievd79dde12019-03-13 19:07:46 -050076 # get the "ip a" output
77 _ifs_raw = shell('ip a')
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +010078 for line in _ifs_raw.splitlines():
79 _if_data = {}
80 if if_start.match(line):
81 _tmp = line.split(':')
82 _if_name = _tmp[1].strip()
83 _if_options = _tmp[2].strip().split(' ')
Alex Savatieiev5d1eebb2019-01-25 18:15:36 +010084 _lower, _upper, _type = get_linked_devices(_if_name)
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +010085 _if_data['order'] = _tmp[0]
86 _if_data['mtu'], _if_options = cut_option("mtu", _if_options)
87 _if_data['qlen'], _if_options = cut_option("qlen", _if_options)
88 _if_data['state'], _if_options = cut_option("state", _if_options)
89 _if_data['other'] = _if_options
90 _if_data['ipv4'] = {}
Alex Savatieiev0137dad2019-01-25 16:18:42 +010091 _if_data['mac'] = {}
Alex Savatieiev5d1eebb2019-01-25 18:15:36 +010092 _if_data['type'] = _type
Alex Savatieiev0137dad2019-01-25 16:18:42 +010093 _if_data['upper'] = _upper
94 _if_data['lower'] = _lower
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +010095 _ifs[_if_name] = _if_data
Alex Savatieiev0137dad2019-01-25 16:18:42 +010096 elif if_link.match(line):
97 if _if_name is None:
98 continue
99 else:
100 _tmp = line.strip().split(' ', 2)
101 _mac_addr = _tmp[1]
102 _options = _tmp[2].split(' ')
103 _brd, _options = cut_option("brd", _options)
104 _ifs[_if_name]['mac'][_mac_addr] = {}
105 _ifs[_if_name]['mac'][_mac_addr]['brd'] = _brd
106 _ifs[_if_name]['mac'][_mac_addr]['other'] = _options
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +0100107 elif if_ipv4.match(line):
108 if _if_name is None:
109 continue
110 else:
111 _tmp = line.strip().split(' ', 2)
112 _ip = _tmp[1]
113 _options = _tmp[2].split(' ')
114 _brd, _options = cut_option("brd", _options)
115 # TODO: Parse other options, mask, brd, etc...
116 _ifs[_if_name]['ipv4'][_ip] = {}
117 _ifs[_if_name]['ipv4'][_ip]['brd'] = _brd
118 _ifs[_if_name]['ipv4'][_ip]['other'] = _options
Alex Savatieievd79dde12019-03-13 19:07:46 -0500119
120 # Collect routes data and try to match it with network
121 # Compile regexp for detecting default route
122 _routes = {
123 'raw': []
124 }
125 _ip_route_raw = shell("ip -4 r")
126 for line in _ip_route_raw.splitlines():
127 _o = line.strip().split(' ')
128 if line.startswith("default"):
129 # default gateway found, prepare options and cut word 'default'
130 _gate, _o = cut_option('via', _o, _option="0.0.0.0")
131 _dev, _o = cut_option('dev', _o)
132 _routes[_o[0]] = {
133 'gateway': _gate,
134 'device': _dev,
135 'args': " ".join(_o[1:])
136 }
137 else:
138 # network specific gateway found
139 _gate, _o = cut_option('via', _o, _option=None)
140 _dev, _o = cut_option('dev', _o)
141 _src, _o = cut_option('src', _o)
142 _routes[_o[0]] = {
143 'gateway': _gate,
144 'device': _dev,
145 'source': _src,
146 'args': " ".join(_o[1:])
147 }
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +0100148
Alex Savatieievd79dde12019-03-13 19:07:46 -0500149 _ifs["routes"] = _routes
Oleksandr Savatieievfb9f9432018-11-23 17:39:12 +0100150 return _ifs
151
152
153ifs_data = get_ifs_data()
154
Alex Savatieiev0137dad2019-01-25 16:18:42 +0100155if len(sys.argv) > 1 and sys.argv[1] == 'json':
156 sys.stdout.write(json.dumps(ifs_data))
157else:
158 _ifs = sorted(ifs_data.keys())
159 _ifs.remove("lo")
Alex Savatieievd79dde12019-03-13 19:07:46 -0500160 _ifs.remove("routes")
Alex Savatieiev0137dad2019-01-25 16:18:42 +0100161 for _idx in range(len(_ifs)):
162 _linked = ""
163 if ifs_data[_ifs[_idx]]['lower']:
Alex Savatieiev5d1eebb2019-01-25 18:15:36 +0100164 _linked += "lower:{} ".format(
Alex Savatieiev0137dad2019-01-25 16:18:42 +0100165 ','.join(ifs_data[_ifs[_idx]]['lower'])
166 )
167 if ifs_data[_ifs[_idx]]['upper']:
Alex Savatieievbd256e82019-01-25 18:27:01 +0100168 _linked += "upper:{} ".format(
169 ','.join(ifs_data[_ifs[_idx]]['upper'])
170 )
Alex Savatieiev5d1eebb2019-01-25 18:15:36 +0100171 _linked = _linked.strip()
172 print("{0:8} {1:30} {2:18} {3:19} {4:5} {5:4} {6}".format(
173 ifs_data[_ifs[_idx]]['type'],
Alex Savatieiev0137dad2019-01-25 16:18:42 +0100174 _ifs[_idx],
175 ",".join(ifs_data[_ifs[_idx]]['mac'].keys()),
176 ",".join(ifs_data[_ifs[_idx]]['ipv4'].keys()),
177 ifs_data[_ifs[_idx]]['mtu'],
178 ifs_data[_ifs[_idx]]['state'],
179 _linked
180 ))
Alex Savatieievd79dde12019-03-13 19:07:46 -0500181
182 print("\n")
183 # default route
184 print("default via {} on {} ({})".format(
185 ifs_data["routes"]["default"]["gateway"],
186 ifs_data["routes"]["default"]["device"],
187 ifs_data["routes"]["default"]["args"]
188 ))
189 # detected routes
190 _routes = ifs_data["routes"].keys()
191 _routes.remove("raw")
192 _routes.remove("default")
193 _rt = ifs_data["routes"]
194 for idx in range(0, len(_routes)):
195 if _rt[_routes[idx]]["gateway"]:
196 print("{0:18} <- {1:16} -> {2:18} on {3:30} ({4})".format(
197 _routes[idx],
198 _rt[_routes[idx]]["gateway"],
199 _rt[_routes[idx]]["source"],
200 _rt[_routes[idx]]["device"],
201 _rt[_routes[idx]]["args"]
202 ))
203 else:
204 print("{0:18} <- -> {1:18} on {2:30} ({3})".format(
205 _routes[idx],
206 _rt[_routes[idx]]["source"],
207 _rt[_routes[idx]]["device"],
208 _rt[_routes[idx]]["args"]
209 ))