Merge lp:~soren/vmbuilder/0.12.network-plugin into lp:vmbuilder
- 0.12.network-plugin
- Merge into 0.12
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Soren Hansen | Approve | ||
Review via email:
|
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Soren Hansen (soren) wrote : | # |
Revision history for this message
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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))) |
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.