Merge lp:~rvb/maas-test/bmc-params-1 into lp:maas-test

Proposed by Raphaël Badin
Status: Merged
Merged at revision: 49
Proposed branch: lp:~rvb/maas-test/bmc-params-1
Merge into: lp:maas-test
Diff against target: 116 lines (+79/-1)
2 files modified
maastest/tests/test_utils.py (+56/-0)
maastest/utils.py (+23/-1)
To merge this branch: bzr merge lp:~rvb/maas-test/bmc-params-1
Reviewer Review Type Date Requested Status
Julian Edwards (community) Disapprove
Graham Binns (community) Approve
Review via email: mp+196107@code.launchpad.net

Commit message

Add utility to parse nmap output.

Description of the change

This is the first branch in a series of branches aimed at adding a fully-automated, non-interactive testing mode where all the BMC details (MAC address, username and password) are given on the command line and then used to power up the node instead of asking the user to do that manually.

This branch adds a simple utility: a parser for the output of nmap. We will use nmap to scan the network and find the IP address of the BMC given its MAC address.

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) :
review: Approve
lp:~rvb/maas-test/bmc-params-1 updated
48. By Raphaël Badin

Merge trunk.

Revision history for this message
Julian Edwards (julian-edwards) wrote :

You need to rethink this, please don't use NMAP. It is largely disallowed on many networks.

There is a utility for working out IPs from MACs, it's called "arp". Better still perhaps use ipmiping.

review: Disapprove

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'maastest/tests/test_utils.py'
2--- maastest/tests/test_utils.py 2013-11-20 11:48:59 +0000
3+++ maastest/tests/test_utils.py 2013-11-21 14:36:23 +0000
4@@ -15,6 +15,7 @@
5 import os.path
6 import platform
7 from subprocess import PIPE
8+from textwrap import dedent
9 import time
10
11 import distro_info
12@@ -244,3 +245,58 @@
13 self.assertEqual(supported, utils.get_supported_series())
14 else:
15 self.assertEqual(supported, utils.get_supported_series())
16+
17+
18+class TestExtractMAPIPMapping(testtools.TestCase):
19+
20+ def test_extract_mac_ip_mapping_parses_output(self):
21+ NMAP_XML_OUTPUT = dedent("""
22+ <nmaprun scanner="nmap" args="nmap -sP -oX - 192.168.2.0/24">
23+ <host>
24+ <address addr="192.168.2.2" addrtype="ipv4"/>
25+ <address addr="00:9C:02:A2:82:74" addrtype="mac"/>
26+ </host>
27+ <host>
28+ <address addr="192.168.2.4" addrtype="ipv4"/>
29+ <address addr="00:9C:02:A0:4D:0A" addrtype="mac"/>
30+ </host>
31+ </nmaprun>
32+ """)
33+ expected_result = {
34+ '00:9C:02:A2:82:74': '192.168.2.2',
35+ '00:9C:02:A0:4D:0A': '192.168.2.4',
36+ }
37+ self.assertEqual(
38+ expected_result, utils.extract_mac_ip_mapping(NMAP_XML_OUTPUT))
39+
40+ def test_extract_mac_ip_mapping_returns_uppercase_mac(self):
41+ NMAP_XML_OUTPUT = dedent("""
42+ <nmaprun scanner="nmap" args="nmap -sP -oX - 192.168.2.0/24">
43+ <host>
44+ <address addr="192.168.2.2" addrtype="ipv4"/>
45+ <address addr="AA:bb:cc:dd:ee:FF" addrtype="mac"/>
46+ </host>
47+ </nmaprun>
48+ """)
49+ expected_result = {'AA:BB:CC:DD:EE:FF': '192.168.2.2'}
50+ self.assertEqual(
51+ expected_result, utils.extract_mac_ip_mapping(NMAP_XML_OUTPUT))
52+
53+ def test_extract_mac_ip_mapping_with_empty_doc(self):
54+ self.assertEqual(
55+ {}, utils.extract_mac_ip_mapping('<nmaprun></nmaprun>'))
56+
57+ def test_extract_mac_ip_mapping_with_missing_info(self):
58+ # If either the IP address of the MAC address is missing in a host
59+ # definition, the host entry is not considered.
60+ NMAP_XML_OUTPUT = dedent("""
61+ <nmaprun scanner="nmap" args="nmap -sP -oX - 192.168.2.0/24">
62+ <host>
63+ <address addr="192.168.2.2" addrtype="ipv4"/>
64+ </host>
65+ <host>
66+ <address addr="00:9C:02:A0:4D:0A" addrtype="mac"/>
67+ </host>
68+ </nmaprun>
69+ """)
70+ self.assertEqual({}, utils.extract_mac_ip_mapping(NMAP_XML_OUTPUT))
71
72=== modified file 'maastest/utils.py'
73--- maastest/utils.py 2013-11-20 11:50:18 +0000
74+++ maastest/utils.py 2013-11-21 14:36:23 +0000
75@@ -33,6 +33,7 @@
76 )
77
78 import distro_info
79+from lxml import etree
80 from testtools.content import Content
81 from testtools.content_type import ContentType
82
83@@ -64,7 +65,6 @@
84 api_root = '/api/1.0/'
85 return api_root + path
86
87-
88 BINARY = ContentType("application", "octet-stream")
89
90
91@@ -137,3 +137,25 @@
92 cut_off_index = (
93 supported.index('precise') if 'precise' in supported else 0)
94 return supported[cut_off_index:]
95+
96+
97+def extract_mac_ip_mapping(nmap_xml_scan):
98+ """Extract the IP->MAC address mapping from the result of a nmap scan.
99+
100+ The returned mapping uses uppercase MAC addresses.
101+
102+ :param nmap_xml_scan: The XML result of scanning a network using nmap.
103+ Scanning a network using nmap is typically done by running:
104+ `nmap -sP <network_definition> -oX -`.
105+ :type nmap_xml_scan: string
106+ """
107+ parser = etree.XMLParser(remove_blank_text=True)
108+ xml_doc = etree.fromstring(nmap_xml_scan, parser)
109+ hosts = xml_doc.xpath("/nmaprun/host")
110+ mapping = {}
111+ for host in hosts:
112+ ips = host.xpath("address[@addrtype='ipv4']/@addr")
113+ macs = host.xpath("address[@addrtype='mac']/@addr")
114+ if len(ips) == 1 and len(macs) == 1:
115+ mapping[macs[0].upper()] = ips[0]
116+ return mapping

Subscribers

People subscribed via source and target branches