Merge lp:~wenjianhn/charm-helpers/get-addr-in-net into lp:charm-helpers

Proposed by Jian Wen
Status: Merged
Merged at revision: 109
Proposed branch: lp:~wenjianhn/charm-helpers/get-addr-in-net
Merge into: lp:charm-helpers
Diff against target: 169 lines (+152/-0)
3 files modified
charmhelpers/contrib/network/ip.py (+69/-0)
test_requirements.txt (+2/-0)
tests/contrib/network/test_ip.py (+81/-0)
To merge this branch: bzr merge lp:~wenjianhn/charm-helpers/get-addr-in-net
Reviewer Review Type Date Requested Status
James Page Approve
Review via email: mp+199390@code.launchpad.net

Description of the change

This patch adds a networking helper to get an IPv4 address in a
specified network.

To post a comment you must log in.
Revision history for this message
James Page (james-page) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'charmhelpers/contrib/network/ip.py'
--- charmhelpers/contrib/network/ip.py 1970-01-01 00:00:00 +0000
+++ charmhelpers/contrib/network/ip.py 2013-12-18 05:53:32 +0000
@@ -0,0 +1,69 @@
1import sys
2
3from charmhelpers.fetch import apt_install
4from charmhelpers.core.hookenv import (
5 ERROR, log,
6)
7
8try:
9 import netifaces
10except ImportError:
11 apt_install('python-netifaces')
12 import netifaces
13
14try:
15 import netaddr
16except ImportError:
17 apt_install('python-netaddr')
18 import netaddr
19
20
21def _validate_cidr(network):
22 try:
23 netaddr.IPNetwork(network)
24 except (netaddr.core.AddrFormatError, ValueError):
25 raise ValueError("Network (%s) is not in CIDR presentation format" %
26 network)
27
28
29def get_address_in_network(network, fallback=None, fatal=False):
30 """
31 Get an IPv4 address within the network from the host.
32
33 Args:
34 network (str): CIDR presentation format. For example,
35 '192.168.1.0/24'.
36 fallback (str): If no address is found, return fallback.
37 fatal (boolean): If no address is found, fallback is not
38 set and fatal is True then exit(1).
39 """
40
41 def not_found_error_out():
42 log("No IP address found in network: %s" % network,
43 level=ERROR)
44 sys.exit(1)
45
46 if network is None:
47 if fallback is not None:
48 return fallback
49 else:
50 if fatal:
51 not_found_error_out()
52
53 _validate_cidr(network)
54 for iface in netifaces.interfaces():
55 addresses = netifaces.ifaddresses(iface)
56 if netifaces.AF_INET in addresses:
57 addr = addresses[netifaces.AF_INET][0]['addr']
58 netmask = addresses[netifaces.AF_INET][0]['netmask']
59 cidr = netaddr.IPNetwork("%s/%s" % (addr, netmask))
60 if cidr in netaddr.IPNetwork(network):
61 return str(cidr.ip)
62
63 if fallback is not None:
64 return fallback
65
66 if fatal:
67 not_found_error_out()
68
69 return None
070
=== modified file 'test_requirements.txt'
--- test_requirements.txt 2013-05-22 08:12:53 +0000
+++ test_requirements.txt 2013-12-18 05:53:32 +0000
@@ -16,3 +16,5 @@
16testtools==0.9.3116testtools==0.9.31
17Tempita==0.5.117Tempita==0.5.1
18bzr+http://bazaar.launchpad.net/~yellow/python-shelltoolbox/trunk@17#egg=shelltoolbox18bzr+http://bazaar.launchpad.net/~yellow/python-shelltoolbox/trunk@17#egg=shelltoolbox
19netifaces==0.6
20netaddr==0.7.5
1921
=== added file 'tests/contrib/network/test_ip.py'
--- tests/contrib/network/test_ip.py 1970-01-01 00:00:00 +0000
+++ tests/contrib/network/test_ip.py 2013-12-18 05:53:32 +0000
@@ -0,0 +1,81 @@
1import subprocess
2import unittest
3
4import mock
5import netifaces
6
7import charmhelpers.contrib.network.ip as net_ip
8
9
10class IPTest(unittest.TestCase):
11
12 def test_get_address_in_network_with_invalid_net(self):
13 for net in ['192.168.300/22', '192.168.1.0/2a', '2.a']:
14 self.assertRaises(ValueError,
15 net_ip.get_address_in_network,
16 net)
17
18 def _test_get_address_in_network(self, expect_ip_addr,
19 network, fallback=None, fatal=False):
20
21 def side_effect(iface):
22 ffff = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'
23 results = {'lo': {17: [{'peer': '00:00:00:00:00:00',
24 'addr': '00:00:00:00:00:00'}],
25 2: [{'peer': '127.0.0.1', 'netmask':
26 '255.0.0.0', 'addr': '127.0.0.1'}],
27 10: [{'netmask': ffff,
28 'addr': '::1'}]
29 },
30 'eth0': {17: [{'broadcast': 'ff:ff:ff:ff:ff:ff',
31 'addr': '28:92:4a:19:8c:e8'}]
32 },
33 'eth2': {17: [{'broadcast': 'ff:ff:ff:ff:ff:ff',
34 'addr': 'e0:06:e6:41:dd:dd'}],
35 2: [{'broadcast': '192.168.1.255',
36 'netmask': '255.255.255.0',
37 'addr': '192.168.1.108'}],
38 10: [{'netmask': 'ffff:ffff:ffff:ffff::',
39 'addr': 'fe80::e206:e6ff:fe41:dddd%eth2'}
40 ]
41 },
42 }
43 return results[iface]
44
45 with mock.patch.object(netifaces, 'interfaces') as interfaces:
46 interfaces.return_value = ['lo', 'eth0', 'eth2']
47 with mock.patch.object(netifaces, 'ifaddresses') as ifaddresses:
48 ifaddresses.side_effect = side_effect
49 if not fatal:
50 self.assertEqual(expect_ip_addr,
51 net_ip.get_address_in_network(
52 network, fallback, fatal))
53 else:
54 net_ip.get_address_in_network(network, fallback, fatal)
55
56 @mock.patch.object(subprocess, 'call')
57 def test_get_address_in_network_with_none(self, popen):
58 fallback = '10.10.10.10'
59 self.assertEqual(fallback,
60 net_ip.get_address_in_network(None, fallback))
61
62 self.assertRaises(SystemExit, self._test_get_address_in_network,
63 None, None, fatal=True)
64
65 def test_get_address_in_network_works(self):
66 self._test_get_address_in_network('192.168.1.108', '192.168.1.0/24')
67
68 def test_get_address_in_network_with_non_existent_net(self):
69 self._test_get_address_in_network(None, '172.16.0.0/16')
70
71 def test_get_address_in_network_fallback_works(self):
72 fallback = '10.10.0.0'
73 self._test_get_address_in_network(fallback, '172.16.0.0/16', fallback)
74
75 @mock.patch.object(subprocess, 'call')
76 def test_get_address_in_network_not_found_fatal(self, popen):
77 self.assertRaises(SystemExit, self._test_get_address_in_network,
78 None, '172.16.0.0/16', fatal=True)
79
80 def test_get_address_in_network_not_found_not_fatal(self):
81 self._test_get_address_in_network(None, '172.16.0.0/16', fatal=False)

Subscribers

People subscribed via source and target branches