1
# Copyright 2015 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
"""Tests for `provisioningserver.pserv_services.neighbours`."""
6
from __future__ import (
17
from maastesting.matchers import MockCalledOnceWith
18
from maastesting.testcase import (
27
from provisioningserver.pserv_services import neighbours
28
from provisioningserver.pserv_services.neighbours import NeighboursService
29
from provisioningserver.rpc.testing import TwistedLoggerFixture
30
from provisioningserver.utils.network import NeighboursProtocol
31
from testtools.matchers import (
35
from twisted.internet.defer import inlineCallbacks
37
# The lines in the following output containing "lladdr" in the 4th column will
38
# become part of the parsed mappings. Other lines will be silently ignored.
39
example_ip_neigh_output = b"""\
40
fe80::9e97:26ff:fe94:f884 dev eth0 lladdr 9c:97:26:94:f8:84 router STALE
41
2001:8b0:1219:a75e:9e97::f884 dev eth0 lladdr 9c:97:26:94:f8:84 router STALE
42
172.16.1.254 dev eth1 lladdr 00:50:56:f9:33:8e STALE
43
192.168.1.254 dev eth0 lladdr 9c:97:26:94:f8:84 STALE
44
172.16.1.1 dev eth1 lladdr 00:50:56:c0:00:01 STALE
45
172.16.1.1 dev eth1 lladdr 00:50:56:c0:00:02 STALE
46
10.0.3.166 dev lxcbr0 lladdr 00:16:3e:da:8b:9e STALE
47
10.0.3.167 dev lxcbr0 lladdr 00:16:3e:da:8b:9e STALE
48
10.155.0.4 dev wlan0 FAILED
49
2.2.2.2 dev wlan0 INCOMPLETE
52
example_ip_neigh_mappings = (
53
NeighboursProtocol.collateNeighbours(
54
NeighboursProtocol.parseOutput(
55
example_ip_neigh_output.splitlines())))
58
class TestNeighboursService(MAASTestCase):
59
"""Tests for `NeighboursService`."""
62
super(TestNeighboursService, self).setUp()
63
self.reactor = self.patch(neighbours, "reactor")
65
def test__update_spawns_process(self):
66
NeighboursService().update()
68
self.reactor.spawnProcess,
69
MockCalledOnceWith(ANY, b"ip", (b"ip", b"neigh")))
71
def test__find_ip_addresses(self):
72
service = NeighboursService()
73
service.update().callback(example_ip_neigh_mappings)
74
# Multiple IP addresses or MAC addresses can be returned.
76
service.find_ip_addresses(EUI("00:50:56:c0:00:01")),
77
Equals({IPAddress("172.16.1.1")}))
79
service.find_ip_addresses(EUI("00:16:3e:da:8b:9e")),
80
Equals({IPAddress("10.0.3.166"), IPAddress("10.0.3.167")}))
81
# Some addresses will yield empty results.
83
service.find_ip_addresses(EUI("12:34:56:78:90:ab")), Equals(set()))
85
def test__find_ip_address(self):
86
service = NeighboursService()
87
service.update().callback(example_ip_neigh_mappings)
88
# A single IP address or MAC address will be returned.
90
service.find_ip_address(EUI("00:50:56:c0:00:01")),
91
Equals(IPAddress("172.16.1.1")))
93
service.find_ip_address(EUI("00:16:3e:da:8b:9e")),
94
Equals(IPAddress("10.0.3.166")))
95
# Some addresses will yield None.
97
service.find_ip_address(EUI("12:34:56:78:90:ab")), Is(None))
99
def test__find_mac_addresses(self):
100
service = NeighboursService()
101
service.update().callback(example_ip_neigh_mappings)
102
# Multiple IP addresses or MAC addresses can be returned.
104
service.find_mac_addresses(IPAddress("172.16.1.1")),
105
Equals({EUI("00:50:56:c0:00:01"), EUI("00:50:56:c0:00:02")}))
107
service.find_mac_addresses(IPAddress("10.0.3.166")),
108
Equals({EUI("00:16:3e:da:8b:9e")}))
109
# Some addresses will yield empty results.
111
service.find_mac_addresses(IPAddress("1.2.3.4")), Equals(set()))
113
def test__find_mac_address(self):
114
service = NeighboursService()
115
service.update().callback(example_ip_neigh_mappings)
116
# A single IP address or MAC address will be returned.
118
service.find_mac_address(IPAddress("172.16.1.1")),
119
Equals(EUI("00:50:56:c0:00:01")))
121
service.find_mac_address(IPAddress("10.0.3.166")),
122
Equals(EUI("00:16:3e:da:8b:9e")))
123
# Some addresses will yield None.
125
service.find_mac_address(IPAddress("1.2.3.4")), Is(None))
128
class TestNeighboursServiceLive(MAASTestCase):
129
"""Tests for `NeighboursService` with a reactor."""
131
run_tests_with = MAASTwistedRunTest.make_factory(timeout=5)
134
def test__update_catches_failures_spawning_the_process(self):
135
non_executable = self.make_file()
136
service = NeighboursService()
137
service.command = (non_executable.encode("ascii"), )
138
with TwistedLoggerFixture() as logger:
139
yield service.update()
140
self.assertDocTestMatches(
142
`ip neigh` wrote to stderr (an error may be reported separately):
144
OSError: [Errno 13] Permission denied
146
Updating neighbours failed.
147
Traceback (most recent call last):
148
Failure: twisted.internet.error.ProcessTerminated: ...
153
def test__update_catches_failures_coming_from_the_process(self):
154
service = NeighboursService()
155
service.command = (b"false", )
156
with TwistedLoggerFixture() as logger:
157
yield service.update()
158
self.assertDocTestMatches(
160
Updating neighbours failed.
161
Traceback (most recent call last):
162
Failure: twisted.internet.error.ProcessTerminated: ...