Merge lp:~mpontillo/maas/maas-rack--send-beacons into lp:~maas-committers/maas/trunk
- maas-rack--send-beacons
- Merge into trunk
Proposed by
Mike Pontillo
Status: | Rejected |
---|---|
Rejected by: | MAAS Lander |
Proposed branch: | lp:~mpontillo/maas/maas-rack--send-beacons |
Merge into: | lp:~maas-committers/maas/trunk |
Prerequisite: | lp:~mpontillo/maas/beaconing-packet-format |
Diff against target: |
429 lines (+166/-10) 5 files modified
src/provisioningserver/__main__.py (+2/-0) src/provisioningserver/utils/beaconing.py (+2/-2) src/provisioningserver/utils/network.py (+1/-0) src/provisioningserver/utils/send_beacons.py (+123/-0) src/provisioningserver/utils/tests/test_network.py (+38/-8) |
To merge this branch: | bzr merge lp:~mpontillo/maas/maas-rack--send-beacons |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
MAAS Maintainers | Pending | ||
Review via email: mp+325605@code.launchpad.net |
Commit message
Add test command for sending out beacons.
Description of the change
To post a comment you must log in.
- 6090. By Mike Pontillo
-
Add tests for interface index.
- 6091. By Mike Pontillo
-
Merge branch: lp:~mpontillo/maas/get-all-interfaces--add-ifindex
Revision history for this message
MAAS Lander (maas-lander) wrote : | # |
Unmerged revisions
- 6091. By Mike Pontillo
-
Merge branch: lp:~mpontillo/maas/get-all-interfaces--add-ifindex
- 6090. By Mike Pontillo
-
Add tests for interface index.
- 6089. By Mike Pontillo
-
Add test command to send beacons out.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/provisioningserver/__main__.py' |
2 | --- src/provisioningserver/__main__.py 2017-06-08 20:41:27 +0000 |
3 | +++ src/provisioningserver/__main__.py 2017-06-14 15:45:59 +0000 |
4 | @@ -16,6 +16,7 @@ |
5 | import provisioningserver.utils.dhcp |
6 | import provisioningserver.utils.scan_network |
7 | from provisioningserver.utils.script import MainScript |
8 | +import provisioningserver.utils.send_beacons |
9 | |
10 | |
11 | script_commands = { |
12 | @@ -27,6 +28,7 @@ |
13 | 'observe-beacons': provisioningserver.utils.beaconing, |
14 | 'observe-mdns': provisioningserver.utils.avahi, |
15 | 'observe-dhcp': provisioningserver.utils.dhcp, |
16 | + 'send-beacons': provisioningserver.utils.send_beacons, |
17 | 'scan-network': provisioningserver.utils.scan_network, |
18 | 'register': provisioningserver.register_command, |
19 | 'support-dump': provisioningserver.support_dump, |
20 | |
21 | === modified file 'src/provisioningserver/utils/beaconing.py' |
22 | --- src/provisioningserver/utils/beaconing.py 2017-06-14 15:45:58 +0000 |
23 | +++ src/provisioningserver/utils/beaconing.py 2017-06-14 15:45:59 +0000 |
24 | @@ -53,7 +53,7 @@ |
25 | class BEACON_ENCAPSULATION: |
26 | """Enumeration to describe beacon encapsulation types.""" |
27 | |
28 | - FERNET_PSK = "fernet_psk" |
29 | + FERNET_PSK = "fernet-psk" |
30 | |
31 | |
32 | BeaconPayload = namedtuple('BeaconPayload', ( |
33 | @@ -63,7 +63,7 @@ |
34 | )) |
35 | |
36 | |
37 | -def create_beacon_payload(beacon_type, data=None): |
38 | +def create_beacon_payload(beacon_type, data=None) -> BeaconPayload: |
39 | """Creates a beacon payload of the specified type, with the given data. |
40 | |
41 | :param beacon_type: The beacon packet type. Indicates the purpose of the |
42 | |
43 | === modified file 'src/provisioningserver/utils/network.py' |
44 | --- src/provisioningserver/utils/network.py 2017-04-26 21:15:06 +0000 |
45 | +++ src/provisioningserver/utils/network.py 2017-06-14 15:45:59 +0000 |
46 | @@ -1159,6 +1159,7 @@ |
47 | # Create the interface definition will links for both IPv4 and IPv6. |
48 | interface = { |
49 | "type": iface_type, |
50 | + "index": ipaddr['index'], |
51 | "links": [], |
52 | "enabled": True if 'UP' in ipaddr['flags'] else False, |
53 | "parents": parents, |
54 | |
55 | === added file 'src/provisioningserver/utils/send_beacons.py' |
56 | --- src/provisioningserver/utils/send_beacons.py 1970-01-01 00:00:00 +0000 |
57 | +++ src/provisioningserver/utils/send_beacons.py 2017-06-14 15:45:59 +0000 |
58 | @@ -0,0 +1,123 @@ |
59 | +# Copyright 2016 Canonical Ltd. This software is licensed under the |
60 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
61 | + |
62 | +"""Utilities for scanning attached networks.""" |
63 | +import struct |
64 | + |
65 | + |
66 | +__all__ = [ |
67 | + "add_arguments", |
68 | + "run" |
69 | +] |
70 | + |
71 | +import select |
72 | +import socket |
73 | +import sys |
74 | +from textwrap import dedent |
75 | +import time |
76 | + |
77 | +from provisioningserver.utils.beaconing import ( |
78 | + BEACON_PORT, |
79 | + create_beacon_payload, |
80 | + read_beacon_payload, |
81 | +) |
82 | +from provisioningserver.utils.network import get_all_interfaces_definition |
83 | +from pprint import pformat |
84 | + |
85 | + |
86 | +def add_arguments(parser): |
87 | + """Add this command's options to the `ArgumentParser`. |
88 | + |
89 | + Specified by the `ActionScript` interface. |
90 | + """ |
91 | + parser.description = dedent("""\ |
92 | + Send solicitation beacons to a particular address. |
93 | + """) |
94 | + parser.add_argument( |
95 | + '-v', '--verbose', action='store_true', required=False, |
96 | + help='Verbose packet output.') |
97 | + parser.add_argument( |
98 | + '-s', '--source', type=str, required=False, |
99 | + help='Source address to send beacons from.') |
100 | + parser.add_argument( |
101 | + 'destination', type=str, nargs='?', |
102 | + help="Destination to send beacon to. If not specified, will use the " |
103 | + "MAAS multicast group (224.0.0.118).") |
104 | + |
105 | + |
106 | +def do_beaconing(args, source_ip, destination_ip, interfaces, stdout): |
107 | + """Interprets the specified `args` and `to_scan` dict to perform the scan. |
108 | + |
109 | + Uses the specified `stdout` and `stderr` for output. |
110 | + """ |
111 | + clock = time.monotonic() |
112 | + transport = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) |
113 | + if source_ip is not None: |
114 | + if ':' not in source_ip: |
115 | + source_ip = "::ffff:" + source_ip |
116 | + transport.bind((source_ip, 0)) |
117 | + if destination_ip is None: |
118 | + destination_ip = "::ffff:224.0.0.118" |
119 | + elif ':' not in destination_ip: |
120 | + destination_ip = "::ffff:" + destination_ip |
121 | + beacon = create_beacon_payload("solicitation") |
122 | + if "224.0.0.118" in destination_ip: |
123 | + for ifdata in interfaces.values(): |
124 | + for link in ifdata["links"]: |
125 | + address = link["address"] |
126 | + # Strip off CIDRs. |
127 | + address = address.split("/")[0] |
128 | + if ':' not in address: |
129 | + transport.setsockopt( |
130 | + socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1) |
131 | + transport.setsockopt( |
132 | + socket.IPPROTO_IP, socket.IP_MULTICAST_IF, |
133 | + socket.inet_aton(address)) |
134 | + destination_ip = "::ffff:224.0.0.118" |
135 | + transport.sendto( |
136 | + beacon.packet_bytes, (destination_ip, BEACON_PORT)) |
137 | + else: |
138 | + # We're sending to an IPv4 multicast address, so we can't |
139 | + # actually use IPv6 addresses here. |
140 | + transport.setsockopt( |
141 | + socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_HOPS, 1) |
142 | + packed_ifindex = struct.pack('I', ifdata['index']) |
143 | + transport.setsockopt( |
144 | + socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, |
145 | + packed_ifindex) |
146 | + destination_ip = "ff02::15a" |
147 | + transport.sendto( |
148 | + beacon.packet_bytes, (destination_ip, BEACON_PORT)) |
149 | + else: |
150 | + transport.sendto(beacon.packet_bytes, (destination_ip, BEACON_PORT)) |
151 | + transport.setblocking(0) |
152 | + clock_diff = 0 |
153 | + timeout = 5 |
154 | + while clock_diff < timeout: |
155 | + ready = select.select([transport], [], [], timeout) |
156 | + if ready[0]: |
157 | + reply, reply_address = transport.recvfrom(16384) |
158 | + if args.verbose: |
159 | + print("%s: %s" % ( |
160 | + pformat(reply), pformat(reply_address)), file=stdout) |
161 | + reply = read_beacon_payload(reply) |
162 | + print("%s" % pformat(reply), file=stdout) |
163 | + clock_diff = time.monotonic() - clock |
164 | + |
165 | + |
166 | +def run(args, stdout=sys.stdout): |
167 | + """Scan local networks for on-link hosts. |
168 | + |
169 | + :param args: Parsed output of the arguments added in `add_arguments()`. |
170 | + :param stdout: Standard output stream to write to. |
171 | + :param stderr: Standard error stream to write to. |
172 | + """ |
173 | + # Record the current time so we can figure out how long it took us to |
174 | + # do all this scanning. |
175 | + source_ip = args.source |
176 | + destination_ip = args.destination |
177 | + interfaces = get_all_interfaces_definition(annotate_with_monitored=False) |
178 | + if args.verbose: |
179 | + print("%s" % pformat(interfaces), file=stdout) |
180 | + do_beaconing( |
181 | + args, source_ip, destination_ip, interfaces, stdout) |
182 | |
183 | === modified file 'src/provisioningserver/utils/tests/test_network.py' |
184 | --- src/provisioningserver/utils/tests/test_network.py 2017-04-26 02:29:43 +0000 |
185 | +++ src/provisioningserver/utils/tests/test_network.py 2017-06-14 15:45:59 +0000 |
186 | @@ -5,6 +5,7 @@ |
187 | |
188 | __all__ = [] |
189 | |
190 | +import random |
191 | import socket |
192 | from socket import ( |
193 | EAI_BADFLAGS, |
194 | @@ -1174,6 +1175,7 @@ |
195 | "flags": ["UP"], |
196 | "inet": ["127.0.0.1/32"], |
197 | "inet6": ["::1"], |
198 | + "index": 1, |
199 | }, |
200 | } |
201 | self.assertInterfacesResult(ip_addr, {}, {}, MatchesDict({})) |
202 | @@ -1185,6 +1187,7 @@ |
203 | "mac": factory.make_mac_address(), |
204 | "flags": ["UP"], |
205 | "inet": ["192.168.122.2/24"], |
206 | + "index": 2, |
207 | }, |
208 | } |
209 | self.assertInterfacesResult(ip_addr, {}, {}, MatchesDict({})) |
210 | @@ -1194,6 +1197,7 @@ |
211 | "vnet": { |
212 | "type": "ipip", |
213 | "flags": ["UP"], |
214 | + "index": 2, |
215 | }, |
216 | } |
217 | self.assertInterfacesResult(ip_addr, {}, {}, MatchesDict({})) |
218 | @@ -1202,6 +1206,7 @@ |
219 | ip_addr = { |
220 | "eth0": { |
221 | "type": "ethernet.physical", |
222 | + "index": 2, |
223 | "mac": factory.make_mac_address(), |
224 | "flags": ["UP"], |
225 | "inet": ["192.168.122.2/24"], |
226 | @@ -1210,6 +1215,7 @@ |
227 | expected_result = MatchesDict({ |
228 | "eth0": MatchesDict({ |
229 | "type": Equals("physical"), |
230 | + "index": Equals(ip_addr["eth0"]["index"]), |
231 | "mac_address": Equals(ip_addr["eth0"]["mac"]), |
232 | "enabled": Is(True), |
233 | "parents": Equals([]), |
234 | @@ -1226,6 +1232,7 @@ |
235 | ip_addr = { |
236 | "eth0": { |
237 | "type": "ethernet.physical", |
238 | + "index": random.randint(2, 99), |
239 | "mac": factory.make_mac_address(), |
240 | "flags": ["UP"], |
241 | "inet": ["192.168.122.2/24"], |
242 | @@ -1239,6 +1246,7 @@ |
243 | expected_result = MatchesDict({ |
244 | "eth0": MatchesDict({ |
245 | "type": Equals("physical"), |
246 | + "index": Equals(ip_addr["eth0"]["index"]), |
247 | "mac_address": Equals(ip_addr["eth0"]["mac"]), |
248 | "enabled": Is(True), |
249 | "parents": Equals([]), |
250 | @@ -1256,6 +1264,7 @@ |
251 | ip_addr = { |
252 | "eth0": { |
253 | "type": "ethernet", |
254 | + "index": random.randint(2, 99), |
255 | "mac": factory.make_mac_address(), |
256 | "flags": ["UP"], |
257 | "inet": ["192.168.122.2/24"], |
258 | @@ -1264,6 +1273,7 @@ |
259 | expected_result = MatchesDict({ |
260 | "eth0": MatchesDict({ |
261 | "type": Equals("physical"), |
262 | + "index": Equals(ip_addr["eth0"]["index"]), |
263 | "mac_address": Equals(ip_addr["eth0"]["mac"]), |
264 | "enabled": Is(True), |
265 | "parents": Equals([]), |
266 | @@ -1281,6 +1291,7 @@ |
267 | ip_addr = { |
268 | "eth0": { |
269 | "type": "ethernet.physical", |
270 | + "index": random.randint(2, 99), |
271 | "mac": factory.make_mac_address(), |
272 | "flags": ["UP"], |
273 | "inet": ["192.168.122.2/24", "192.168.122.200/32"], |
274 | @@ -1292,6 +1303,7 @@ |
275 | expected_result = MatchesDict({ |
276 | "eth0": MatchesDict({ |
277 | "type": Equals("physical"), |
278 | + "index": Equals(ip_addr["eth0"]["index"]), |
279 | "mac_address": Equals(ip_addr["eth0"]["mac"]), |
280 | "enabled": Is(True), |
281 | "parents": Equals([]), |
282 | @@ -1315,6 +1327,7 @@ |
283 | ip_addr = { |
284 | "eth0": { |
285 | "type": "ethernet.physical", |
286 | + "index": random.randint(2, 99), |
287 | "mac": factory.make_mac_address(), |
288 | "flags": ["UP"], |
289 | "inet": [ |
290 | @@ -1331,6 +1344,7 @@ |
291 | expected_result = MatchesDict({ |
292 | "eth0": MatchesDict({ |
293 | "type": Equals("physical"), |
294 | + "index": Equals(ip_addr["eth0"]["index"]), |
295 | "mac_address": Equals(ip_addr["eth0"]["mac"]), |
296 | "enabled": Is(True), |
297 | "parents": Equals([]), |
298 | @@ -1365,21 +1379,25 @@ |
299 | ip_addr = { |
300 | "eth0": { |
301 | "type": "ethernet.physical", |
302 | + "index": 2, |
303 | "mac": factory.make_mac_address(), |
304 | "flags": [], |
305 | }, |
306 | "eth1": { |
307 | "type": "ethernet.physical", |
308 | + "index": 3, |
309 | "mac": factory.make_mac_address(), |
310 | "flags": ["UP"], |
311 | }, |
312 | "eth2": { |
313 | "type": "ethernet.physical", |
314 | + "index": 4, |
315 | "mac": factory.make_mac_address(), |
316 | "flags": ["UP"], |
317 | }, |
318 | "bond0": { |
319 | "type": "ethernet.bond", |
320 | + "index": 5, |
321 | "mac": factory.make_mac_address(), |
322 | "flags": ["UP"], |
323 | "bonded_interfaces": ["eth1", "eth2"], |
324 | @@ -1388,6 +1406,7 @@ |
325 | }, |
326 | "bond0.10": { |
327 | "type": "ethernet.vlan", |
328 | + "index": 6, |
329 | "flags": ["UP"], |
330 | "vid": 10, |
331 | "inet": ["192.168.123.2/24", "192.168.123.3/32"], |
332 | @@ -1395,6 +1414,7 @@ |
333 | }, |
334 | "vlan20": { |
335 | "type": "ethernet.vlan", |
336 | + "index": 7, |
337 | "mac": factory.make_mac_address(), |
338 | "flags": ["UP"], |
339 | "vid": 20, |
340 | @@ -1402,11 +1422,13 @@ |
341 | }, |
342 | "wlan0": { |
343 | "type": "ethernet.wireless", |
344 | + "index": 8, |
345 | "mac": factory.make_mac_address(), |
346 | "flags": ["UP"], |
347 | }, |
348 | "br0": { |
349 | "type": "ethernet.bridge", |
350 | + "index": 9, |
351 | "bridged_interfaces": ["eth0"], |
352 | "mac": factory.make_mac_address(), |
353 | "flags": ["UP"], |
354 | @@ -1424,6 +1446,7 @@ |
355 | expected_result = MatchesDict({ |
356 | "eth0": MatchesDict({ |
357 | "type": Equals("physical"), |
358 | + "index": Equals(2), |
359 | "mac_address": Equals(ip_addr["eth0"]["mac"]), |
360 | "enabled": Is(False), |
361 | "parents": Equals([]), |
362 | @@ -1432,6 +1455,7 @@ |
363 | }), |
364 | "eth1": MatchesDict({ |
365 | "type": Equals("physical"), |
366 | + "index": Equals(3), |
367 | "mac_address": Equals(ip_addr["eth1"]["mac"]), |
368 | "enabled": Is(True), |
369 | "parents": Equals([]), |
370 | @@ -1440,6 +1464,7 @@ |
371 | }), |
372 | "eth2": MatchesDict({ |
373 | "type": Equals("physical"), |
374 | + "index": Equals(4), |
375 | "mac_address": Equals(ip_addr["eth2"]["mac"]), |
376 | "enabled": Is(True), |
377 | "parents": Equals([]), |
378 | @@ -1448,6 +1473,7 @@ |
379 | }), |
380 | "bond0": MatchesDict({ |
381 | "type": Equals("bond"), |
382 | + "index": Equals(5), |
383 | "mac_address": Equals(ip_addr["bond0"]["mac"]), |
384 | "enabled": Is(True), |
385 | "parents": Equals(["eth1", "eth2"]), |
386 | @@ -1471,6 +1497,7 @@ |
387 | }), |
388 | "bond0.10": MatchesDict({ |
389 | "type": Equals("vlan"), |
390 | + "index": Equals(6), |
391 | "enabled": Is(True), |
392 | "parents": Equals(["bond0"]), |
393 | "vid": Equals(10), |
394 | @@ -1486,24 +1513,27 @@ |
395 | ), |
396 | "source": Equals("ipaddr"), |
397 | }), |
398 | + "vlan20": MatchesDict({ |
399 | + "type": Equals("vlan"), |
400 | + "index": Equals(7), |
401 | + "enabled": Is(True), |
402 | + "parents": Equals(["eth0"]), |
403 | + "links": Equals([]), |
404 | + "source": Equals("ipaddr"), |
405 | + "vid": Equals(20), |
406 | + }), |
407 | "wlan0": MatchesDict({ |
408 | "type": Equals("physical"), |
409 | + "index": Equals(8), |
410 | "mac_address": Equals(ip_addr["wlan0"]["mac"]), |
411 | "enabled": Is(True), |
412 | "parents": Equals([]), |
413 | "links": Equals([]), |
414 | "source": Equals("ipaddr"), |
415 | }), |
416 | - "vlan20": MatchesDict({ |
417 | - "type": Equals("vlan"), |
418 | - "enabled": Is(True), |
419 | - "parents": Equals(["eth0"]), |
420 | - "links": Equals([]), |
421 | - "source": Equals("ipaddr"), |
422 | - "vid": Equals(20), |
423 | - }), |
424 | "br0": MatchesDict({ |
425 | "type": Equals("bridge"), |
426 | + "index": Equals(9), |
427 | "mac_address": Equals(ip_addr["br0"]["mac"]), |
428 | "enabled": Is(True), |
429 | "parents": Equals(["eth0"]), |
Transitioned to Git.
lp:maas has now moved from Bzr to Git.
Please propose your branches with Launchpad using Git.
git clone https:/ /git.launchpad. net/maas