Merge lp:~soren/vmbuilder/0.12.network-plugin into lp:vmbuilder

Proposed by Soren Hansen
Status: Merged
Merged at revision: not available
Proposed branch: lp:~soren/vmbuilder/0.12.network-plugin
Merge into: lp:vmbuilder
Diff against target: 295 lines (+210/-57)
3 files modified
VMBuilder/plugins/network/__init__.py (+115/-0)
VMBuilder/vm.py (+0/-57)
test/network_tests.py (+95/-0)
To merge this branch: bzr merge lp:~soren/vmbuilder/0.12.network-plugin
Reviewer Review Type Date Requested Status
Soren Hansen Approve
Review via email: mp+18455@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Soren Hansen (soren) wrote :

Is the lack of feedback because there are no objections, or because it's so hideous that you don't know where to start? :)

Let's put it this way: If there are no objections by tomorrow (Friday) at 1600 UTC, I'll take the liberty of just going ahead and merging it.

Revision history for this message
Soren Hansen (soren) wrote :

Merged, since there were no objections.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'VMBuilder/plugins/network'
2=== added file 'VMBuilder/plugins/network/__init__.py'
3--- VMBuilder/plugins/network/__init__.py 1970-01-01 00:00:00 +0000
4+++ VMBuilder/plugins/network/__init__.py 2010-02-02 16:27:13 +0000
5@@ -0,0 +1,115 @@
6+#
7+# Uncomplicated VM Builder
8+# Copyright (C) 2007-2009 Canonical Ltd.
9+#
10+# See AUTHORS for list of contributors
11+#
12+# This program is free software: you can redistribute it and/or modify
13+# it under the terms of the GNU General Public License version 3, as
14+# published by the Free Software Foundation.
15+#
16+# This program is distributed in the hope that it will be useful,
17+# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+# GNU General Public License for more details.
20+#
21+# You should have received a copy of the GNU General Public License
22+# along with this program. If not, see <http://www.gnu.org/licenses/>.
23+#
24+# Virtual network management
25+
26+import logging
27+import re
28+import struct
29+import socket
30+
31+from VMBuilder import register_plugin
32+from VMBuilder.plugins import Plugin
33+from VMBuilder.exception import VMBuilderUserError
34+
35+def validate_mac(mac):
36+ valid_mac_address = re.compile("^([0-9a-f]{2}:){5}([0-9a-f]{2})$", re.IGNORECASE)
37+ if not valid_mac_address.match(mac):
38+ return False
39+ else:
40+ return True
41+
42+def numeric_to_dotted_ip(numeric_ip):
43+ return socket.inet_ntoa(struct.pack('I', numeric_ip))
44+
45+def dotted_to_numeric_ip(dotted_ip):
46+ try:
47+ return struct.unpack('I', socket.inet_aton(dotted_ip))[0]
48+ except socket.error:
49+ raise VMBuilderUserError('%s is not a valid ip address' % dotted_ip)
50+
51+def guess_mask_from_ip(numip):
52+ first_octet = numip & 0xFF
53+
54+ if (first_octet > 0) and (first_octet <= 127):
55+ return 0xFF
56+ elif (first_octet > 128) and (first_octet < 192):
57+ return 0xFFFF
58+ elif (first_octet < 224):
59+ return 0xFFFFFF
60+ else:
61+ raise VMBuilderUserError('Could not guess network class of: %s' % numeric_to_dotted_ip(numip))
62+
63+def calculate_net_address_from_ip_and_netmask(ip, netmask):
64+ return ip & netmask
65+
66+def calculate_broadcast_address_from_ip_and_netmask(net, mask):
67+ return net + (mask ^ 0xFFFFFFFF)
68+
69+def guess_gw_from_ip(ip):
70+ return ip + 0x01000000
71+
72+class NetworkPlugin(Plugin):
73+ def preflight_check(self):
74+ """
75+ Validate the ip configuration given and set defaults
76+ """
77+
78+ logging.debug("ip: %s" % self.vm.ip)
79+
80+ if self.vm.mac:
81+ if not validate_mac(mac):
82+ raise VMBuilderUserError("Malformed MAC address entered: %s" % mac)
83+
84+ if self.vm.ip != 'dhcp':
85+ if self.vm.domain == '':
86+ raise VMBuilderUserError('Domain is undefined and host has no domain set.')
87+
88+ # num* are numeric representations
89+ numip = dotted_to_numeric_ip(self.vm.ip)
90+
91+ if not self.vm.mask:
92+ nummask = guess_mask_from_ip(numip)
93+ else:
94+ nummask = dotted_to_numeric_ip(self.vm.mask)
95+
96+ numnet = calculate_net_address_from_ip_and_netmask(numip, nummask)
97+
98+ if not self.vm.net:
99+ self.vm.net = numeric_to_dotted_ip(numnet)
100+
101+ if not self.vm.bcast:
102+ numbcast = calculate_broadcast_address_from_ip_and_netmask(numnet, nummask)
103+ self.vm.bcast = numeric_to_dotted_ip(numbcast)
104+
105+ if not self.vm.gw:
106+ numgw = guess_gw_from_ip(numip)
107+ self.vm.gw = numeric_to_dotted_ip(numgw)
108+
109+ if not self.vm.dns:
110+ self.vm.dns = self.vm.gw
111+
112+ self.vm.mask = numeric_to_dotted_ip(nummask)
113+
114+ logging.debug("net: %s" % self.vm.net)
115+ logging.debug("netmask: %s" % self.vm.mask)
116+ logging.debug("broadcast: %s" % self.vm.bcast)
117+ logging.debug("gateway: %s" % self.vm.gw)
118+ logging.debug("dns: %s" % self.vm.dns)
119+
120+register_plugin(NetworkPlugin)
121
122=== modified file 'VMBuilder/vm.py'
123--- VMBuilder/vm.py 2009-12-08 22:27:46 +0000
124+++ VMBuilder/vm.py 2010-02-02 16:27:13 +0000
125@@ -272,62 +272,6 @@
126 self.distro.set_defaults()
127 self.hypervisor.set_defaults()
128
129-
130- def ip_defaults(self):
131- """
132- is called to validate the ip configuration given and set defaults
133- """
134-
135- logging.debug("ip: %s" % self.ip)
136-
137- if self.mac:
138- valid_mac_address = re.compile("([0-9a-f]{2}:){5}([0-9a-f]{2})", re.IGNORECASE)
139- if not valid_mac_address.search(self.mac):
140- raise VMBuilderUserError("Malformed MAC address entered: %s" % self.mac)
141- else:
142- logging.debug("Valid mac given: %s" % self.mac)
143-
144- if self.ip != 'dhcp':
145- if self.domain == '':
146- raise VMBuilderUserError('Domain is undefined and host has no domain set.')
147-
148- try:
149- numip = struct.unpack('I', socket.inet_aton(self.ip))[0]
150- except socket.error:
151- raise VMBuilderUserError('%s is not a valid ip address' % self.ip)
152-
153- if not self.mask:
154- ipclass = numip & 0xFF
155- if (ipclass > 0) and (ipclass <= 127):
156- mask = 0xFF
157- elif (ipclass > 128) and (ipclass < 192):
158- mask = 0xFFFF
159- elif (ipclass < 224):
160- mask = 0xFFFFFF
161- else:
162- raise VMBuilderUserError('The class of the ip address specified (%s) does not seem right' % self.ip)
163- else:
164- mask = struct.unpack('I', socket.inet_aton(self.mask))[0]
165-
166- numnet = numip & mask
167-
168- if not self.net:
169- self.net = socket.inet_ntoa( struct.pack('I', numnet ) )
170- if not self.bcast:
171- self.bcast = socket.inet_ntoa( struct.pack('I', numnet + (mask ^ 0xFFFFFFFF)))
172- if not self.gw:
173- self.gw = socket.inet_ntoa( struct.pack('I', numnet + 0x01000000 ) )
174- if not self.dns:
175- self.dns = self.gw
176-
177- self.mask = socket.inet_ntoa( struct.pack('I', mask ) )
178-
179- logging.debug("net: %s" % self.net)
180- logging.debug("netmask: %s" % self.mask)
181- logging.debug("broadcast: %s" % self.bcast)
182- logging.debug("gateway: %s" % self.gw)
183- logging.debug("dns: %s" % self.dns)
184-
185 def create_directory_structure(self):
186 """Creates the directory structure where we'll be doing all the work
187
188@@ -423,7 +367,6 @@
189 if '-' in opt:
190 raise VMBuilderUserError('You specified a "%s" config option in a config file, but that is not valid. Perhaps you meant "%s"?' % (opt, opt.replace('-', '_')))
191
192- self.ip_defaults()
193 self.call_hooks('preflight_check')
194
195 # Check repository availability
196
197=== added file 'test/network_tests.py'
198--- test/network_tests.py 1970-01-01 00:00:00 +0000
199+++ test/network_tests.py 2010-02-02 16:27:13 +0000
200@@ -0,0 +1,95 @@
201+#
202+# Uncomplicated VM Builder
203+# Copyright (C) 2007-2009 Canonical Ltd.
204+#
205+# See AUTHORS for list of contributors
206+#
207+# This program is free software: you can redistribute it and/or modify
208+# it under the terms of the GNU General Public License version 3, as
209+# published by the Free Software Foundation.
210+#
211+# This program is distributed in the hope that it will be useful,
212+# but WITHOUT ANY WARRANTY; without even the implied warranty of
213+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
214+# GNU General Public License for more details.
215+#
216+# You should have received a copy of the GNU General Public License
217+# along with this program. If not, see <http://www.gnu.org/licenses/>.
218+import unittest
219+
220+from VMBuilder.exception import VMBuilderUserError
221+from VMBuilder.plugins import network
222+
223+class TestNetworkPlugin(unittest.TestCase):
224+ def test_validate_mac(self):
225+ valid_macs = ['11:22:33:44:55:66',
226+ 'ff:ff:ff:ff:ff:ff',
227+ '00:00:00:00:00:00']
228+ invalid_macs = ['g1:22:33:44:55:66',
229+ '11:ff:ff:ff:ff:ff:ff',
230+ 'ffffffffffff']
231+ for mac in valid_macs:
232+ self.assertTrue(network.validate_mac(mac), '%s was not considered a valid MAC address' % mac)
233+ for mac in invalid_macs:
234+ self.assertFalse(network.validate_mac(mac), '%s was not considered an invalid MAC address' % mac)
235+
236+ def test_dotted_to_numeric_ip(self):
237+ valid_ips = ['192.168.1.1',
238+ '1.1.1.1',
239+ '10.0.0.1',
240+ '255.255.255.255']
241+
242+ invalid_ips = ['this is not a valid IP',
243+ '256.1.1.1']
244+
245+ for ip in valid_ips:
246+ self.assertTrue(network.dotted_to_numeric_ip(ip), '%s was considered a valid IP address' % ip)
247+ for ip in invalid_ips:
248+ self.assertRaises(VMBuilderUserError, network.dotted_to_numeric_ip, ip)
249+
250+ def test_guess_mask_from_ip(self):
251+ known_correct_values = [('10.0.0.1', 0xFF),
252+ ('127.0.0.1', 0xFF),
253+ ('172.17.0.1', 0xFFFF),
254+ ('192.168.1.1', 0xFFFFFF)]
255+
256+ for ip, nummask in known_correct_values:
257+ numip = network.dotted_to_numeric_ip(ip)
258+ self.assertEqual(network.guess_mask_from_ip(numip), nummask, "Incorrect netmask guessed")
259+
260+ self.assertRaises(VMBuilderUserError, network.guess_mask_from_ip, network.dotted_to_numeric_ip('230.0.0.0'))
261+
262+ def test_calculate_net_address_from_ip_and_netmask(self):
263+ known_correct_values = [(('192.168.1.1', '255.255.255.0'), '192.168.1.0'),
264+ (('192.168.1.1', '255.255.0.0'), '192.168.0.0'),
265+ (('192.168.1.1', '255.0.0.0'), '192.0.0.0'),
266+ (('192.168.1.1', '255.242.255.0'), '192.160.1.0'),
267+ (('192.168.1.1', '0.255.255.0'), '0.168.1.0')]
268+
269+ for ((ip, netmask), expected_network) in known_correct_values:
270+ numip = network.dotted_to_numeric_ip(ip)
271+ numnetmask = network.dotted_to_numeric_ip(netmask)
272+ self.assertEqual(network.calculate_net_address_from_ip_and_netmask(numip, numnetmask),
273+ network.dotted_to_numeric_ip(expected_network))
274+
275+ def test_calculate_broadcast_address_from_ip_and_netmask(self):
276+ known_correct_values = [(('192.168.1.0', '255.255.255.0'), '192.168.1.255'),
277+ (('192.168.0.0', '255.255.0.0'), '192.168.255.255'),
278+ (('192.0.0.0', '255.0.0.0'), '192.255.255.255'),
279+ (('192.160.1.0', '255.242.255.0'), '192.173.1.255'),
280+ (('0.168.1.0', '0.255.255.0'), '255.168.1.255')]
281+
282+ for ((ip, netmask), expected_bcast) in known_correct_values:
283+ numip = network.dotted_to_numeric_ip(ip)
284+ numnetmask = network.dotted_to_numeric_ip(netmask)
285+ guessed_broadcast = network.calculate_broadcast_address_from_ip_and_netmask(numip, numnetmask)
286+ self.assertEqual(guessed_broadcast,
287+ network.dotted_to_numeric_ip(expected_bcast),
288+ "%s %s made %s, but expected %s" % (ip,
289+ netmask,
290+ network.numeric_to_dotted_ip(guessed_broadcast),
291+ expected_bcast))
292+
293+ def test_ip_conversion(self):
294+ for x in xrange(256*256):
295+ self.assertEqual(x*x, network.dotted_to_numeric_ip(network.numeric_to_dotted_ip(x*x)))

Subscribers

People subscribed via source and target branches