Skip to content

Commit 39d76a7

Browse files
committed
Merge pull request #26 from migibert/discover_mtu
Discover mtu
2 parents bccd873 + 09aa5b7 commit 39d76a7

File tree

5 files changed

+104
-79
lines changed

5 files changed

+104
-79
lines changed

pynetlib/device.py

+19-41
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from __future__ import absolute_import
2-
from .utils import execute_command, find_value
2+
from .utils import execute_command, get_devices_info
33
from .exceptions import ObjectAlreadyExistsException, ObjectNotFoundException
44

55

@@ -12,6 +12,7 @@ def __init__(self, id, name, flags=[], namespace=None):
1212
self.state = None
1313
self.inet = []
1414
self.inet6 = []
15+
self.mtu = None
1516

1617
def is_loopback(self):
1718
return 'LOOPBACK' in self.flags
@@ -54,53 +55,30 @@ def disable(self):
5455
self.refresh()
5556

5657
def refresh(self):
57-
output = execute_command('ip addr show %s' % self.name, namespace=self.namespace)
58-
devices = Device.parse_output(output, namespace=self.namespace)
59-
if len(devices) != 1:
60-
raise Exception('Only one device can be named %s inside the same namespace' % self.name)
61-
dev = devices[0]
62-
self.id = dev.id
63-
self.name = dev.name
64-
self.flags = dev.flags
65-
self.namespace = dev.namespace
66-
self.state = dev.state
67-
self.inet = dev.inet
68-
self.inet6 = dev.inet6
58+
devices = Device.discover(namespace=self.namespace)
59+
if self not in devices:
60+
raise ObjectNotFoundException(self)
61+
found = devices[devices.index(self)]
62+
self.flags = found.flags
63+
self.inet = found.inet
64+
self.inet6 = found.inet6
65+
self.mtu = found.mtu
6966

7067
@staticmethod
7168
def discover(namespace=None):
72-
output = execute_command('ip addr list', namespace=namespace)
73-
return Device.parse_output(output, namespace=namespace)
74-
75-
@staticmethod
76-
def parse_output(output, namespace=None):
7769
devices = []
78-
current_device = None
79-
for block in output.split('\n'):
80-
if block and block[0].isdigit():
81-
if current_device:
82-
devices.append(current_device)
83-
prefixes = block.split(':')
84-
id = prefixes[0]
85-
name = prefixes[1].strip()
86-
flags = block[block.index('<') + 1:block.index('>')].split(',')
87-
current_device = Device(id, name, flags=flags, namespace=namespace)
88-
words = block.strip().split(' ')
89-
state = find_value(words, 'state')
90-
if state is not None:
91-
current_device.state = state
92-
inet = find_value(words, 'inet')
93-
if inet is not None:
94-
current_device.inet.append(inet)
95-
inet6 = find_value(words, 'inet6')
96-
if inet6 is not None:
97-
current_device.inet6.append(inet6)
98-
if current_device:
99-
devices.append(current_device)
70+
output = execute_command('ip addr list', namespace=namespace)
71+
for id, name, flags, state, inet, inet6, mtu in get_devices_info(output):
72+
device = Device(id, name, flags=flags, namespace=namespace)
73+
device.state = state
74+
device.inet = inet
75+
device.inet6 = inet6
76+
device.mtu = mtu
77+
devices.append(device)
10078
return devices
10179

10280
def __eq__(self, other):
10381
return self.name == other.name and self.id == other.id
10482

10583
def __repr__(self):
106-
return '<' + ','.join([self.id, self.name, str(self.inet), str(self.inet6)]) + '>'
84+
return '<' + ','.join([self.id, self.name, str(self.inet), str(self.inet6), str(self.mtu)]) + '>'

pynetlib/tests/fixtures/ip_addr_show

-6
This file was deleted.

pynetlib/tests/test_device.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
from __future__ import absolute_import
2-
from nose_parameterized.parameterized import parameterized
3-
import os
42
import mock
53
import unittest
64
from . import read_file
@@ -14,7 +12,6 @@ class TestDevice(unittest.TestCase):
1412

1513
def setUp(self):
1614
self.ip_addr_list_output = read_file('ip_addr_list')
17-
self.ip_addr_show_output = read_file('ip_addr_show')
1815

1916
def test_init_device(self):
2017
id = '1'
@@ -26,6 +23,7 @@ def test_init_device(self):
2623
self.assertEqual(dev.inet, [])
2724
self.assertEqual(dev.inet6, [])
2825
self.assertIsNone(dev.state)
26+
self.assertIsNone(dev.mtu)
2927

3028
def test_init_loopback(self):
3129
id = '1'
@@ -207,17 +205,25 @@ def test_disable_down_device(self, execute_command):
207205

208206
@mock.patch('pynetlib.device.execute_command')
209207
def test_refresh_device(self, execute_command):
210-
execute_command.return_value = self.ip_addr_show_output
211-
device = Device('1', 'eth0', flags=[])
208+
execute_command.return_value = self.ip_addr_list_output
209+
device = Device('2', 'eth0', flags=[])
212210
device.refresh()
213-
execute_command.assert_called_once_with("ip addr show eth0", namespace=None)
211+
execute_command.assert_called_once_with("ip addr list", namespace=None)
214212
self.assertEqual(device.id, '2')
215213
self.assertEqual(device.name, "eth0")
216214
self.assertEqual(device.flags, ['BROADCAST', 'MULTICAST', 'UP', 'LOWER_UP'])
217-
self.assertEqual(device.inet, ['10.0.2.15/24'])
215+
self.assertEqual(device.inet, ['10.0.2.15/24', '10.0.2.16/24'])
218216
self.assertEqual(device.inet6, ['fe80::a00:27ff:feea:67cf/64'])
217+
self.assertEqual(device.mtu, '1500')
219218
self.assertIsNone(device.namespace)
220219

220+
@mock.patch('pynetlib.device.execute_command')
221+
def test_refresh_non_existing_device(self, execute_command):
222+
execute_command.return_value = self.ip_addr_list_output
223+
device = Device('id', 'device', flags=[])
224+
with self.assertRaises(ObjectNotFoundException):
225+
device.refresh()
226+
execute_command.assert_called_once_with("ip addr list", namespace=None)
221227

222228
if __name__ == '__main__':
223229
unittest.main()

pynetlib/tests/test_utils.py

+15-19
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from __future__ import absolute_import
22
import mock
33
import unittest
4-
from pynetlib.utils import execute_command, find_value
4+
from pynetlib.utils import execute_command, find_values_or_default_value
55
from pynetlib.namespace import Namespace
6-
from pynetlib.exceptions import ValueNotFoundException
6+
from nose_parameterized import parameterized
77

88

99
class TestUtils(unittest.TestCase):
@@ -31,25 +31,21 @@ def test_execute_command_in_string_namespace(self, check_output):
3131
execute_command('command arg1 arg2', namespace=namespace)
3232
check_output.assert_called_once_with('ip netns exec namespace command arg1 arg2', shell=True)
3333

34-
def test_find_value(self):
35-
values = ['key', 'value']
36-
value = find_value(values, 'key')
34+
def test_find_values(self):
35+
values = 'key value'
36+
value = find_values_or_default_value(values, 'key', single=True)
3737
self.assertEqual(value, 'value')
3838

39-
def test_find_non_existing_key(self):
40-
values = ['key', 'value']
41-
value = find_value(values, 'test')
42-
self.assertIsNone(value)
43-
44-
def test_find_non_existing_value(self):
45-
values = ['key']
46-
with self.assertRaises(ValueNotFoundException):
47-
find_value(values, 'key')
48-
49-
def test_find_two_values(self):
50-
values = ['key', 'value', 'key', 'other']
51-
value = find_value(values, 'key')
52-
self.assertEqual(value, 'value')
39+
@parameterized.expand([
40+
('key value', 'key', True, None, 'value'),
41+
('', 'test', True, 'value', 'value'),
42+
('key', 'key', True, None, None),
43+
('key', 'key', True, 'test', 'test'),
44+
('key value1 key value2', 'key', False, None, ['value1', 'value2'])
45+
])
46+
def test_find_values_or_default_value(self, values, key, single, default, expected):
47+
value = find_values_or_default_value(values, key, single=single, default_value=default)
48+
self.assertEqual(value, expected)
5349

5450

5551
if __name__ == '__main__':

pynetlib/utils.py

+57-6
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,60 @@ def execute_command(command, namespace=None):
1414
return subprocess.check_output(cmd, shell=True)
1515

1616

17-
def find_value(values, key):
18-
if key in values:
19-
if len(values) > values.index(key) + 1:
20-
return values[values.index(key) + 1]
21-
raise ValueNotFoundException('Value not found for key %s' % key)
22-
return None
17+
def get_devices_info(output):
18+
devices = []
19+
blocks = parse_output(output)
20+
for block in blocks:
21+
id, name, flags = parse_header(block)
22+
state = find_values_or_default_value(block, 'state', single=True, default_value='UNKNOWN')
23+
inet = find_values_or_default_value(block, 'inet', default_value=[])
24+
inet6 = find_values_or_default_value(block, 'inet6', default_value=[])
25+
mtu = find_values_or_default_value(block, 'mtu', default_value=None, single=True)
26+
devices.append((id, name, flags, state, inet, inet6, mtu))
27+
return devices
28+
29+
30+
def parse_output(output):
31+
blocks = []
32+
current_block = ''
33+
34+
for line in output.split('\n'):
35+
if line and line[0].isdigit():
36+
if current_block:
37+
blocks.append(current_block)
38+
current_block = ''
39+
current_block = current_block + line
40+
if current_block:
41+
blocks.append(current_block)
42+
return blocks
43+
44+
45+
def parse_header(header):
46+
prefixes = header.split(':')
47+
id = prefixes[0]
48+
name = prefixes[1].strip()
49+
flags = header[header.index('<') + 1:header.index('>')].split(',')
50+
return id, name, flags
51+
52+
53+
def find_values(data, key):
54+
result = []
55+
words = data.split(' ')
56+
indexes = [index for index, word in enumerate(words) if word == key]
57+
for index in indexes:
58+
value_index = index + 1
59+
if len(words) > value_index:
60+
result.append(words[value_index])
61+
else:
62+
raise ValueNotFoundException(key)
63+
if len(result) == 0:
64+
raise ValueNotFoundException(key)
65+
return result
66+
67+
68+
def find_values_or_default_value(data, key, default_value=None, single=False):
69+
try:
70+
result = find_values(data, key)
71+
return result[0] if single else result
72+
except ValueNotFoundException:
73+
return default_value

0 commit comments

Comments
 (0)