Ejemplo n.º 1
0
 def test__creates_listen_port_when_run_with_IReactorMulticast(self):
     # Note: Always use a random port for testing. (port=0)
     protocol = BeaconingSocketProtocol(reactor, port=0)
     self.assertThat(protocol.listen_port, Not(Is(None)))
     # This tests that the post gets closed properly; otherwise the test
     # suite will complain about things left in the reactor.
     yield protocol.stopProtocol()
Ejemplo n.º 2
0
 def test__skips_creating_listen_port_when_run_with_fake_reactor(self):
     # Note: Always use a random port for testing. (port=0)
     protocol = BeaconingSocketProtocol(Clock(), port=0)
     self.assertThat(protocol.listen_port, Is(None))
     # No listen port, so stopProtocol() shouldn't return a Deferred.
     result = protocol.stopProtocol()
     self.assertThat(result, Is(None))
Ejemplo n.º 3
0
 def test_send_multicast_beacons(self):
     interfaces = {
         "eth0": {
             "enabled": True,
             "links": []
         },
         "eth1": {
             "enabled": True,
             "links": []
         },
         "eth2": {
             "enabled": True,
             "links": []
         },
     }
     self.patch(socket, "if_nametoindex", lambda name: int(name[3:]))
     protocol = BeaconingSocketProtocol(
         Clock(),
         port=0,
         process_incoming=False,
         loopback=True,
         interface="::",
         debug=True,
     )
     self.patch(services, "create_beacon_payload")
     send_mcast_mock = self.patch(protocol, "send_multicast_beacon")
     protocol.send_multicast_beacons(interfaces)
     # beaconing is sent for each interface ID
     self.assertEqual(
         send_mcast_mock.mock_calls,
         [call(0, ANY), call(1, ANY),
          call(2, ANY)],
     )
Ejemplo n.º 4
0
 def test__hints_for_same_beacon_seen_on_multiple_interfaces(self):
     # Note: Always use a random port for testing. (port=0)
     protocol = BeaconingSocketProtocol(
         reactor,
         port=0,
         process_incoming=False,
         loopback=True,
         interface="::",
         debug=True,
     )
     # Don't try to send out any replies.
     self.patch(services, "create_beacon_payload")
     self.patch(protocol, "send_beacon")
     # Need to generate a real UUID with the current time, so it doesn't
     # get aged out.
     uuid = str(uuid1())
     # Make the protocol think we sent a beacon with this UUID already.
     fake_tx_beacon = FakeBeaconPayload(uuid, ifname="eth0")
     fake_rx_beacon_eth0 = {
         "source_ip": "127.0.0.1",
         "source_port": 5240,
         "destination_ip": "224.0.0.118",
         "interface": "eth0",
         "type": "solicitation",
         "payload": fake_tx_beacon.payload,
     }
     fake_rx_beacon_eth1 = {
         "source_ip": "127.0.0.1",
         "source_port": 5240,
         "destination_ip": "224.0.0.118",
         "interface": "eth1",
         "vid": 100,
         "type": "solicitation",
         "payload": fake_tx_beacon.payload,
     }
     protocol.beaconReceived(fake_rx_beacon_eth0)
     protocol.beaconReceived(fake_rx_beacon_eth1)
     hints = protocol.topology_hints[uuid]
     expected_hints = {
         TopologyHint(
             ifname="eth0",
             vid=None,
             hint="same_local_fabric_as",
             related_ifname="eth1",
             related_vid=100,
             related_mac=None,
         ),
         TopologyHint(
             ifname="eth1",
             vid=100,
             hint="same_local_fabric_as",
             related_ifname="eth0",
             related_vid=None,
             related_mac=None,
         ),
     }
     self.assertThat(hints, Equals(expected_hints))
     yield protocol.stopProtocol()
Ejemplo n.º 5
0
 def test__send_multicast_beacon_sets_ipv4_source(self):
     # Note: Always use a random port for testing. (port=0)
     protocol = BeaconingSocketProtocol(reactor,
                                        port=0,
                                        process_incoming=True,
                                        loopback=True,
                                        interface="::",
                                        debug=False)
     self.assertThat(protocol.listen_port, Not(Is(None)))
     listen_port = protocol.listen_port._realPortNumber
     self.write_secret()
     beacon = create_beacon_payload("advertisement", {})
     protocol.send_multicast_beacon("127.0.0.1", beacon, port=listen_port)
     # Verify that we received the packet.
     yield wait_for_rx_packets(protocol, 1)
     yield protocol.stopProtocol()
Ejemplo n.º 6
0
 def test__sends_and_receives_unicast_beacons(self):
     # Note: Always use a random port for testing. (port=0)
     logger = self.useFixture(TwistedLoggerFixture())
     protocol = BeaconingSocketProtocol(
         reactor,
         port=0,
         process_incoming=True,
         loopback=True,
         interface="::",
         debug=True,
     )
     self.assertThat(protocol.listen_port, Not(Is(None)))
     listen_port = protocol.listen_port._realPortNumber
     self.write_secret()
     beacon = create_beacon_payload("solicitation", {})
     rx_uuid = beacon.payload["uuid"]
     destination = random.choice(["::ffff:127.0.0.1", "::1"])
     protocol.send_beacon(beacon, (destination, listen_port))
     # Pretend we didn't send this packet. Otherwise we won't reply to it.
     # We have to do this now, before the reactor runs again.
     transmitted = protocol.tx_queue.pop(rx_uuid, None)
     # Since we've instructed the protocol to loop back packets for testing,
     # it should have sent a multicast solicitation, received it back, sent
     # an advertisement, then received it back. So we'll wait for two
     # packets to be sent.
     yield wait_for_rx_packets(protocol, 2)
     # Grab the beacon we know we transmitted and then received.
     received = protocol.rx_queue.pop(rx_uuid, None)
     self.assertThat(transmitted, Equals(beacon))
     self.assertThat(received[0].json["payload"]["uuid"], Equals(rx_uuid))
     # Grab the subsequent packets from the queues.
     transmitted = protocol.tx_queue.popitem()[1]
     received = protocol.rx_queue.popitem()[1]
     # We should have received a second packet to ack the first beacon.
     self.assertThat(received[0].json["payload"]["acks"], Equals(rx_uuid))
     # We should have transmitted an advertisement in response to the
     # solicitation.
     self.assertThat(transmitted.type, Equals("advertisement"))
     # This tests that the post gets closed properly; otherwise the test
     # suite will complain about things left in the reactor.
     yield protocol.stopProtocol()
     # In debug mode, the logger should have printed each packet.
     self.assertThat(
         logger.output,
         DocTestMatches("...Beacon received:...Own beacon received:..."),
     )
Ejemplo n.º 7
0
 def test__queues_multicast_beacon_soliciations_upon_request(self):
     # Note: Always use a random port for testing. (port=0)
     clock = Clock()
     protocol = BeaconingSocketProtocol(
         clock,
         port=0,
         process_incoming=False,
         loopback=True,
         interface="::",
         debug=True,
     )
     # Don't try to send out any replies.
     self.patch(services, "create_beacon_payload")
     send_mcast_mock = self.patch(protocol, "send_multicast_beacons")
     self.patch(protocol, "send_beacon")
     yield protocol.queueMulticastBeaconing(solicitation=True)
     clock.advance(0)
     self.assertThat(send_mcast_mock, MockCalledOnceWith({},
                                                         "solicitation"))
Ejemplo n.º 8
0
def do_beaconing(args, interfaces=None):
    """Sends out beacons based on the given arguments, and waits for replies.

    :param args: The command-line arguments.
    :param interfaces: The interfaces to send out beacons on.
        Must be the result of `get_all_interfaces_definition()`.
    """
    if args.source is None:
        source_ip = '::'
    else:
        source_ip = args.source
    protocol = BeaconingSocketProtocol(reactor,
                                       process_incoming=True,
                                       debug=True,
                                       interface=source_ip,
                                       port=args.port,
                                       interfaces=interfaces)
    if args.destination is None:
        destination_ip = "::ffff:" + BEACON_IPV4_MULTICAST
    elif ':' not in args.destination:
        destination_ip = "::ffff:" + args.destination
    else:
        destination_ip = args.destination
    if "224.0.0.118" in destination_ip:
        protocol.send_multicast_beacons(interfaces, verbose=args.verbose)
    else:
        log.msg("Sending unicast beacon to '%s'..." % destination_ip)
        beacon = create_beacon_payload("solicitation")
        protocol.send_beacon(beacon, (destination_ip, BEACON_PORT))
    reactor.callLater(args.timeout, lambda: reactor.stop())
    reactor.run()
    return protocol
Ejemplo n.º 9
0
 def test__hints_for_own_beacon_received_on_same_interface(self):
     # Note: Always use a random port for testing. (port=0)
     protocol = BeaconingSocketProtocol(
         reactor,
         port=0,
         process_incoming=False,
         loopback=True,
         interface="::",
         debug=True,
     )
     # Need to generate a real UUID with the current time, so it doesn't
     # get aged out.
     uuid = str(uuid1())
     # Make the protocol think we sent a beacon with this UUID already.
     fake_tx_beacon = FakeBeaconPayload(uuid, ifname="eth0")
     protocol.tx_queue[uuid] = fake_tx_beacon
     fake_rx_beacon = {
         "source_ip": "127.0.0.1",
         "source_port": 5240,
         "destination_ip": "224.0.0.118",
         "interface": "eth0",
         "type": "solicitation",
         "payload": fake_tx_beacon.payload,
     }
     protocol.beaconReceived(fake_rx_beacon)
     # Should only have created one hint.
     hint = protocol.topology_hints[uuid].pop()
     self.assertThat(hint.hint, Equals("rx_own_beacon_on_tx_interface"))
     yield protocol.stopProtocol()
Ejemplo n.º 10
0
 def test__multicasts_at_most_once_per_five_seconds(self):
     # Note: Always use a random port for testing. (port=0)
     clock = Clock()
     protocol = BeaconingSocketProtocol(
         clock,
         port=0,
         process_incoming=False,
         loopback=True,
         interface="::",
         debug=True,
     )
     # Don't try to send out any replies.
     self.patch(services, "create_beacon_payload")
     monotonic_mock = self.patch(services.time, "monotonic")
     send_mcast_mock = self.patch(protocol, "send_multicast_beacons")
     self.patch(protocol, "send_beacon")
     monotonic_mock.side_effect = [
         # Initial queue
         6,
         # Initial dequeue
         6,
         # Second queue (hasn't yet been 5 seconds)
         10,
         # Third queue
         11,
         # Second dequeue
         11,
     ]
     yield protocol.queueMulticastBeaconing()
     clock.advance(0)
     self.assertThat(send_mcast_mock, MockCalledOnceWith({},
                                                         "advertisement"))
     send_mcast_mock.reset_mock()
     yield protocol.queueMulticastBeaconing()
     yield protocol.queueMulticastBeaconing(solicitation=True)
     clock.advance(4.9)
     self.assertThat(send_mcast_mock, MockNotCalled())
     clock.advance(0.1)
     self.assertThat(send_mcast_mock, MockCalledOnceWith({},
                                                         "solicitation"))
Ejemplo n.º 11
0
 def test__send_multicast_beacon_sets_ipv6_source(self):
     # Due to issues beyond my control, this test doesn't do what I expected
     # it to do. But it's still useful for code coverage (to make sure no
     # blatant exceptions occur in the IPv6 path).
     # self.skipTest(
     #    "IPv6 loopback multicast isn't working, for whatever reason.")
     # Since we can't test IPv6 multicast on the loopback interface, another
     # method can be used to verify that it's working:
     # (1) sudo tcpdump -i <physical-interface> 'udp and port == 5240'
     # (2) bin/maas-rack send-beacons -p 5240
     # Verifying IPv6 (and IPv4) multicast group join behavior can be
     # validated by doing something like:
     # (1) bin/maas-rack send-beacons -t 600
     #     (the high timeout will cause it to wait for 10 minutes)
     # (2) ip maddr show | egrep 'ff02::15a|224.0.0.118|$'
     # The expected result from command (2) will be that 'egrep' will
     # highlight the MAAS multicast groups in red text. Any Ethernet
     # interface with an assigned IPv4 address should have joined the
     # 224.0.0.118 group. All Ethernet interfaces should have joined the
     # 'ff02::15a' group.
     # Note: Always use a random port for testing. (port=0)
     protocol = BeaconingSocketProtocol(
         reactor,
         port=0,
         process_incoming=True,
         loopback=True,
         interface="::",
         debug=False,
     )
     self.assertThat(protocol.listen_port, Not(Is(None)))
     listen_port = protocol.listen_port._realPortNumber
     self.write_secret()
     beacon = create_beacon_payload("advertisement", {})
     # The loopback interface ifindex should always be 1; this is saying
     # to send an IPv6 multicast on ifIndex == 1.
     protocol.send_multicast_beacon(1, beacon, port=listen_port)
     # Instead of skipping the test, just don't expect to receive anything.
     # yield wait_for_rx_packets(protocol, 1)
     yield protocol.stopProtocol()
Ejemplo n.º 12
0
 def test__getJSONTopologyHints_converts_hints_to_dictionary(self):
     # Note: Always use a random port for testing. (port=0)
     protocol = BeaconingSocketProtocol(
         reactor,
         port=0,
         process_incoming=False,
         loopback=True,
         interface="::",
         debug=True,
     )
     # Don't try to send out any replies.
     self.patch(services, "create_beacon_payload")
     self.patch(protocol, "send_beacon")
     # Need to generate a real UUID with the current time, so it doesn't
     # get aged out.
     uuid = str(uuid1())
     # Make the protocol think we sent a beacon with this UUID already.
     tx_mac = factory.make_mac_address()
     fake_tx_beacon = FakeBeaconPayload(uuid,
                                        ifname="eth1",
                                        mac=tx_mac,
                                        vid=100)
     fake_rx_beacon = {
         "source_ip": "127.0.0.1",
         "source_port": 5240,
         "destination_ip": "224.0.0.118",
         "interface": "eth0",
         "type": "solicitation",
         "payload": fake_tx_beacon.payload,
     }
     protocol.beaconReceived(fake_rx_beacon)
     all_hints = protocol.getJSONTopologyHints()
     expected_hints = [
         # Note: since vid=None on the received beacon, we expect that
         # the hint won't have a 'vid' field.
         dict(
             ifname="eth0",
             hint="on_remote_network",
             related_ifname="eth1",
             related_vid=100,
             related_mac=tx_mac,
         )
     ]
     self.assertThat(all_hints, Equals(expected_hints))
     yield protocol.stopProtocol()