Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Multiple inet4 address for a single interface #27

Merged
merged 2 commits into from
May 24, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ Usage
for name, interface in ifcfg.interfaces().items():
# do something with interface
print interface['device']
print interface['inet']
print interface['inet'] # First IPv4 found
print interface['inet4'] # List of ips
print interface['inet6']
print interface['netmask']
print interface['broadcast']
Expand All @@ -43,6 +44,7 @@ following:
"flags": "4163<up,broadcast,running,multicast> ",
"hostname": "derks-vm.local",
"inet": "172.16.217.10",
"inet4": ["172.16.217.10"],
"inet6": ["fe80::20c:29ff:fe0c:da5d"],
"mtu": "1500",
"name": "eth0",
Expand All @@ -53,6 +55,7 @@ following:
"flags": "73<up,loopback,running> ",
"hostname": "localhost",
"inet": "127.0.0.1",
"inet4": ["127.0.0.1"],
"inet6": ["::1"],
"mtu": "16436",
"name": "lo",
Expand All @@ -64,6 +67,7 @@ following:
"flags": "4099<up,broadcast,multicast> ",
"hostname": "derks-vm.local",
"inet": "192.168.122.1",
"inet4": ["192.168.122.1"],
"inet6": [],
"mtu": "1500",
"name": "virbr0",
Expand All @@ -75,6 +79,8 @@ following:
Release notes
-------------

* Support for multiple IPv4 addresses in the new 'inet4' field

0.15
____

Expand Down
20 changes: 16 additions & 4 deletions src/ifcfg/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#: These will always be present for each device (even if they are None)
DEVICE_PROPERTY_DEFAULTS = {
'inet': None,
'inet4': "LIST",
'ether': None,
'inet6': "LIST", # Because lists are mutable
'netmask': None,
Expand Down Expand Up @@ -78,6 +79,10 @@ def parse(self, ifconfig=None): # noqa: max-complexity=12
self._interfaces[cur][k] = v
elif hasattr(self._interfaces[cur][k], 'append'):
self._interfaces[cur][k].append(v)
elif self._interfaces[cur][k] == v:
# Silently ignore if the it's the same value as last. Example: Multiple
# inet4 addresses, result in multiple netmasks. Cardinality mismatch
continue
else:
raise RuntimeError(
"Tried to add {}={} multiple times to {}, it was already: {}".format(
Expand All @@ -90,6 +95,11 @@ def parse(self, ifconfig=None): # noqa: max-complexity=12
else:
self._interfaces[cur][k] = v

# Copy the first 'inet4' ip address to 'inet' for backwards compatibility
for device, device_dict in self._interfaces.items():
if len(device_dict['inet4']) > 0:
device_dict['inet'] = device_dict['inet4'][0]

# fix it up
self._interfaces = self.alter(self._interfaces)

Expand All @@ -108,6 +118,8 @@ def alter(self, interfaces):
"""
# fixup some things
for device, device_dict in interfaces.items():
if len(device_dict['inet4']) > 0:
device_dict['inet'] = device_dict['inet4'][0]
if 'inet' in device_dict and not device_dict['inet'] is None:
try:
host = socket.gethostbyaddr(device_dict['inet'])[0]
Expand Down Expand Up @@ -174,7 +186,7 @@ def get_patterns(cls):
return [
r"^(?P<device>\w.+):",
r"^ Physical Address. . . . . . . . . : (?P<ether>[ABCDEFabcdef\d-]+)",
r"^ IPv4 Address. . . . . . . . . . . : (?P<inet>[^\s\(]+)",
r"^ IPv4 Address. . . . . . . . . . . : (?P<inet4>[^\s\(]+)",
r"^ IPv6 Address. . . . . . . . . . . : (?P<inet6>[ABCDEFabcdef\d\:\%]+)",
]

Expand Down Expand Up @@ -210,7 +222,7 @@ def get_command(cls):
def get_patterns(cls):
return [
'(?P<device>^[-a-zA-Z0-9:]+): flags=(?P<flags>.*) mtu (?P<mtu>.*)',
'.*inet\s+(?P<inet>[\d\.]+).*',
'.*inet\s+(?P<inet4>[\d\.]+).*',
'.*inet6\s+(?P<inet6>[\d\:abcdef]+).*',
'.*broadcast (?P<broadcast>[^\s]*).*',
'.*netmask (?P<netmask>[^\s]*).*',
Expand Down Expand Up @@ -257,7 +269,7 @@ def get_patterns(cls):
return super(LinuxParser, cls).get_patterns() + [
'(?P<device>^[a-zA-Z0-9:_-]+)(.*)Link encap:(.*).*',
'(.*)Link encap:(.*)(HWaddr )(?P<ether>[^\s]*).*',
'.*(inet addr:\s*)(?P<inet>[^\s]+).*',
'.*(inet addr:\s*)(?P<inet4>[^\s]+).*',
'.*(inet6 addr:\s*)(?P<inet6>[^\s\/]+)',
'.*(P-t-P:)(?P<ptp>[^\s]*).*',
'.*(Bcast:)(?P<broadcast>[^\s]*).*',
Expand Down Expand Up @@ -288,7 +300,7 @@ def get_command(cls):
def get_patterns(cls):
return [
'\s*[0-9]+:\s+(?P<device>[a-zA-Z0-9]+):.*mtu (?P<mtu>.*)',
'.*(inet\s)(?P<inet>[\d\.]+)',
'.*(inet\s)(?P<inet4>[\d\.]+)',
'.*(inet6 )(?P<inet6>[^/]*).*',
'.*(ether )(?P<ether>[^\s]*).*',
'.*inet\s.*(brd )(?P<broadcast>[^\s]*).*',
Expand Down
66 changes: 66 additions & 0 deletions tests/ifconfig_out.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,72 @@
""" # noqa


MACOSX2 = """
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
inet 127.0.0.1 netmask 0xff000000
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
inet 127.0.1.99 netmask 0xff000000
nd6 options=201<PERFORMNUD,DAD>
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether 8c:85:90:00:aa:bb
inet6 fe80::1c8b:e7cc:c695:a6de%en0 prefixlen 64 secured scopeid 0x6
inet 10.0.1.12 netmask 0xffff0000 broadcast 10.0.255.255
inet6 2601:980:c000:7c5a:da:6a59:1e94:b03 prefixlen 64 autoconf secured
inet6 2601:980:c000:7c5a:fdb3:b90c:80d6:abcd prefixlen 64 deprecated autoconf temporary
inet6 2601:980:c000:7c5a:34b0:676d:93f7:0123 prefixlen 64 autoconf temporary
nd6 options=201<PERFORMNUD,DAD>
media: autoselect
status: active
en1: flags=963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX> mtu 1500
options=60<TSO4,TSO6>
ether 7a:00:48:a1:b2:00
media: autoselect <full-duplex>
status: inactive
p2p0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 2304
ether 0e:85:90:0f:ab:cd
media: autoselect
status: inactive
awdl0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1484
ether 2e:51:48:37:99:0a
inet6 fe80::2c51:48ff:fe37:86f8%awdl0 prefixlen 64 scopeid 0xc
nd6 options=201<PERFORMNUD,DAD>
media: autoselect
status: active
bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=63<RXCSUM,TXCSUM,TSO4,TSO6>
ether 7a:00:48:c8:aa:bd
Configuration:
id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
ipfilter disabled flags 0x2
member: en1 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 7 priority 0 path cost 0
member: en2 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 8 priority 0 path cost 0
member: en3 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 9 priority 0 path cost 0
member: en4 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 10 priority 0 path cost 0
nd6 options=201<PERFORMNUD,DAD>
media: <unknown type>
status: inactive
utun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 2000
inet6 fe80::ebb7:7d4a:49e8:e4fc%utun0 prefixlen 64 scopeid 0xe
nd6 options=201<PERFORMNUD,DAD>
vboxnet0: flags=8842<BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether 0a:00:27:00:00:00
en5: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether ac:de:48:00:22:11
inet6 fe80::aede:48ff:fe00:2211%en5 prefixlen 64 scopeid 0x5
nd6 options=281<PERFORMNUD,INSECURE,DAD>
media: autoselect
status: active
"""


ROUTE_OUTPUT = """
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
Expand Down
10 changes: 10 additions & 0 deletions tests/ifconfig_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ def test_macosx(self):
eq_(interfaces['en0']['broadcast'], '192.168.0.255')
eq_(interfaces['en0']['netmask'], '255.255.255.0')

def test_macosx2(self):
ifcfg.distro = 'MacOSX'
ifcfg.Parser = ifcfg.get_parser_class()
parser = ifcfg.get_parser(ifconfig=ifconfig_out.MACOSX2)
interfaces = parser.interfaces
self.assertEqual(len(interfaces.keys()), 9)
eq_(interfaces['lo0']['inet'], '127.0.0.1')
eq_(interfaces['lo0']['inet4'], ['127.0.0.1', '127.0.1.99'])
eq_(interfaces['lo0']['netmask'], '255.0.0.0')

def test_default_interface(self):
ifcfg.distro = 'Linux'
ifcfg.Parser = ifcfg.get_parser_class()
Expand Down
12 changes: 12 additions & 0 deletions tests/ip_out.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@
link/ether a0:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
"""

LINUX_MULTI_IPV4 = """
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether b8:27:eb:50:39:69 brd ff:ff:ff:ff:ff:ff
inet 192.168.13.1/24 brd 192.168.13.255 scope global eth0
valid_lft forever preferred_lft forever
inet 192.168.10.3/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::ba27:ebff:fe50:3969/64 scope link
valid_lft forever preferred_lft forever
"""


ROUTE_OUTPUT = """
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
Expand Down
11 changes: 11 additions & 0 deletions tests/ip_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ def test_linux(self):
eq_(interfaces['wlp3s0']['broadcast'], '192.168.12.255')
eq_(interfaces['wlp3s0']['netmask'], '/24')

def test_linux_multi_inet4(self):
ifcfg.Parser = UnixIPParser
parser = ifcfg.get_parser(ifconfig=ip_out.LINUX_MULTI_IPV4)
interfaces = parser.interfaces
# Connected interface
eq_(interfaces['eth0']['ether'], 'b8:27:eb:50:39:69')
eq_(interfaces['eth0']['inet'], '192.168.13.1')
eq_(interfaces['eth0']['inet4'], ['192.168.13.1', '192.168.10.3'])
eq_(interfaces['eth0']['broadcast'], '192.168.13.255')
eq_(interfaces['eth0']['netmask'], '/24')

def test_default_interface(self):
ifcfg.distro = 'Linux'
ifcfg.Parser = UnixIPParser
Expand Down