Ejemplo n.º 1
0
    def test_processNotification_send_to_region(self):
        protocol, connecting = self.patch_rpc_UpdateLease()
        self.addCleanup((yield connecting))

        client = getRegionClient()
        rpc_service = MagicMock()
        rpc_service.getClientNow.return_value = defer.succeed(client)
        service = LeaseSocketService(rpc_service, reactor)

        # Notification to region.
        packet = {
            "action": "commit",
            "mac": factory.make_mac_address(),
            "ip_family": "ipv4",
            "ip": factory.make_ipv4_address(),
            "timestamp": int(time.time()),
            "lease_time": 30,
            "hostname": factory.make_name("host"),
        }
        yield service.processNotification(packet, clock=reactor)
        self.assertThat(
            protocol.UpdateLease,
            MockCalledOnceWith(protocol,
                               cluster_uuid=client.localIdent,
                               action=packet["action"],
                               mac=packet["mac"],
                               ip_family=packet["ip_family"],
                               ip=packet["ip"],
                               timestamp=packet["timestamp"],
                               lease_time=packet["lease_time"],
                               hostname=packet["hostname"]))
Ejemplo n.º 2
0
    def test_reports_lack_of_foreign_dhcp_servers_to_region(self):
        clock = Clock()
        interface_name = factory.make_name("eth")
        interfaces = {
            interface_name: {
                "enabled": True,
                "links": [{
                    "address": "10.0.0.1/24"
                }]
            }
        }

        protocol, connecting = self.patch_rpc_methods()
        self.addCleanup((yield connecting))

        deferToThread = self.patch(dhcp_probe_service, 'deferToThread')
        deferToThread.side_effect = [
            defer.succeed(interfaces),
        ]
        probe_interface = self.patch(dhcp_probe_service, 'probe_interface')
        probe_interface.return_value = []

        client = getRegionClient()
        rpc_service = Mock()
        rpc_service.getClientNow.return_value = defer.succeed(client)
        service = DHCPProbeService(rpc_service, clock)
        yield service.startService()
        yield service.stopService()

        self.assertThat(
            protocol.ReportForeignDHCPServer,
            MockCalledOnceWith(protocol,
                               system_id=client.localIdent,
                               interface_name=interface_name,
                               dhcp_ip=None))
    def test_query_nodes_calls_query_all_nodes(self):
        service = self.make_monitor_service()
        service.max_nodes_at_once = sentinel.max_nodes_at_once

        example_power_parameters = {
            "system_id": factory.make_UUID(),
            "hostname": factory.make_hostname(),
            "power_state": factory.make_name("power_state"),
            "power_type": factory.make_name("power_type"),
            "context": {},
        }

        rpc_fixture = self.useFixture(MockClusterToRegionRPCFixture())
        proto_region, io = rpc_fixture.makeEventLoop(
            region.ListNodePowerParameters)
        proto_region.ListNodePowerParameters.side_effect = [
            succeed({"nodes": [example_power_parameters]}),
            succeed({"nodes": []}),
        ]

        query_all_nodes = self.patch(npms, "query_all_nodes")

        d = service.query_nodes(getRegionClient())
        io.flush()

        self.assertEqual(None, extract_result(d))
        self.assertThat(
            query_all_nodes,
            MockCalledOnceWith(
                [example_power_parameters],
                max_concurrency=sentinel.max_nodes_at_once,
                clock=service.clock,
            ),
        )
Ejemplo n.º 4
0
def commission_node(system_id, user):
    """Commission a Node on the region.

    :param system_id: system_id of node to commission.
    :param user: user for the node.
    """
    # Avoid circular dependencies.
    from provisioningserver.rpc.region import CommissionNode

    for elapsed, remaining, wait in retries(15, 5, reactor):
        try:
            client = getRegionClient()
            break
        except NoConnectionsAvailable:
            yield pause(wait, reactor)
    else:
        maaslog.error("Can't commission node, no RPC connection to region.")
        return

    try:
        yield client(CommissionNode, system_id=system_id, user=user)
    except CommissionNodeFailed as e:
        # The node cannot be commissioned, give up.
        maaslog.error("Could not commission with system_id %s because %s.",
                      system_id, e.args[0])
    except UnhandledCommand:
        # The region hasn't been upgraded to support this method
        # yet, so give up.
        maaslog.error("Unable to commission node on region: Region does not "
                      "support the CommissionNode RPC method.")
    finally:
        returnValue(None)
Ejemplo n.º 5
0
def power_state_update(system_id, state):
    """Report to the region about a node's power state.

    :param system_id: The system ID for the node.
    :param state: Typically "on", "off", or "error".
    """
    client = getRegionClient()
    return client(UpdateNodePowerState, system_id=system_id, power_state=state)
Ejemplo n.º 6
0
 def send(_):
     client = getRegionClient()
     return client(
         SendEvent,
         system_id=system_id,
         type_name=event_type,
         description=description,
     )
Ejemplo n.º 7
0
 def send(_):
     client = getRegionClient()
     return client(
         SendEventMACAddress,
         mac_address=mac_address,
         type_name=event_type,
         description=description,
     )
Ejemplo n.º 8
0
 def send(_):
     client = getRegionClient()
     return client(
         SendEventIPAddress,
         ip_address=ip_address,
         type_name=event_type,
         description=description,
     )
Ejemplo n.º 9
0
 def getExpectedState(self):
     try:
         client = getRegionClient()
     except NoConnectionsAvailable:
         return SERVICE_STATE.ANY, None
     else:
         d = client(GetControllerType, system_id=client.localIdent)
         d.addCallback(self._getExpectedStateForControllerType)
         return d
Ejemplo n.º 10
0
def power_state_update(system_id, state):
    """Report to the region about a node's power state.

    :param system_id: The system ID for the node.
    :param state: Typically "on", "off", or "error".
    """
    client = getRegionClient()
    return client(
        UpdateNodePowerState,
        system_id=system_id,
        power_state=state)
Ejemplo n.º 11
0
def touch_last_image_sync_timestamp():
    """Inform the region that images have just been synchronised.

    :return: :class:`Deferred` that can fail with `NoConnectionsAvailable` or
        any exception arising from an `UpdateLastImageSync` RPC.
    """
    try:
        client = getRegionClient()
    except Exception:
        return fail()
    else:
        return client(UpdateLastImageSync, system_id=get_maas_id())
Ejemplo n.º 12
0
    def test_reports_services_to_region_when_changed(self):
        # Pretend we're in a production environment.
        self.patch(sms, "is_dev_environment").return_value = False

        protocol, connecting = self.patch_rpc_methods()
        self.addCleanup((yield connecting))

        class ExampleService(AlwaysOnService):
            name = service_name = snap_service_name = (
                factory.make_name("service"))

        service = ExampleService()
        # Inveigle this new service into the service monitor.
        self.addCleanup(service_monitor._services.pop, service.name)
        service_monitor._services[service.name] = service

        state = ServiceState(SERVICE_STATE.ON, "running")
        mock_ensureServices = self.patch(service_monitor, "ensureServices")
        mock_ensureServices.return_value = succeed({
            service.name: state
        })

        client = getRegionClient()
        rpc_service = Mock()
        rpc_service.getClientNow.return_value = succeed(client)
        monitor_service = sms.ServiceMonitorService(
            rpc_service, Clock())
        monitor_service._services = yield monitor_service._buildServices({
            service.name: state
        })

        # Force last reported state to dead. That way an update is performed.
        orig_cached_services = monitor_service._services
        for ser in orig_cached_services:
            ser['status'] = 'dead'

        yield monitor_service.startService()
        yield monitor_service.stopService()

        expected_services = list(monitor_service.ALWAYS_RUNNING_SERVICES)
        expected_services.append({
            "name": service.name,
            "status": "running",
            "status_info": "",
        })
        self.assertThat(
            protocol.UpdateServices,
            MockCalledOnceWith(
                protocol,
                system_id=client.localIdent,
                services=expected_services))
        self.assertIsNot(orig_cached_services, monitor_service._services)
Ejemplo n.º 13
0
    def try_query_nodes(self):
        """Attempt to query nodes' power states.

        Log errors on failure, but do not propagate them up; that will
        stop the timed loop from running.
        """
        try:
            client = getRegionClient()
        except NoConnectionsAvailable:
            log.debug(
                "Cannot monitor nodes' power status; region not available.")
        else:
            d = self.query_nodes(client)
            d.addErrback(self.query_nodes_failed, client.localIdent)
            return d
Ejemplo n.º 14
0
    def test_query_nodes_calls_the_region(self):
        service = self.make_monitor_service()

        rpc_fixture = self.useFixture(MockClusterToRegionRPCFixture())
        proto_region, io = rpc_fixture.makeEventLoop(
            region.ListNodePowerParameters)
        proto_region.ListNodePowerParameters.return_value = succeed(
            {"nodes": []})

        client = getRegionClient()
        d = service.query_nodes(client)
        io.flush()

        self.assertEqual(None, extract_result(d))
        self.assertThat(
            proto_region.ListNodePowerParameters,
            MockCalledOnceWith(ANY, uuid=client.localIdent))
Ejemplo n.º 15
0
    def start_services():
        rpc_service = ClusterClientService(reactor)
        rpc_service.setName("rpc")
        rpc_service.setServiceParent(services)

        yield services.startService()

        for elapsed, remaining, wait in retries(15, 1, reactor):
            try:
                yield getRegionClient()
            except NoConnectionsAvailable:
                yield pause(wait, reactor)
            else:
                break
        else:
            print("Can't connect to the region.", file=stderr)
            raise SystemExit(1)
Ejemplo n.º 16
0
def request_node_info_by_mac_address(mac_address):
    """Request node info for the given mac address.

    :param mac_address: The MAC Address of the node of the event.
    :type mac_address: unicode
    """
    if mac_address is None:
        return succeed(None)

    client = getRegionClient()
    d = client(RequestNodeInfoByMACAddress, mac_address=mac_address)

    def eb_request_node_info(failure):
        failure.trap(NoSuchNode)
        return None

    return d.addErrback(eb_request_node_info)
    def test_query_nodes_copes_with_NoSuchCluster(self):
        service = self.make_monitor_service()

        rpc_fixture = self.useFixture(MockClusterToRegionRPCFixture())
        proto_region, io = rpc_fixture.makeEventLoop(
            region.ListNodePowerParameters)
        client = getRegionClient()
        proto_region.ListNodePowerParameters.return_value = fail(
            exceptions.NoSuchCluster.from_uuid(client.localIdent))

        d = service.query_nodes(client)
        d.addErrback(service.query_nodes_failed, client.localIdent)
        with FakeLogger("maas") as maaslog:
            io.flush()

        self.assertEqual(None, extract_result(d))
        self.assertDocTestMatches("Rack controller '...' is not recognised.",
                                  maaslog.output)
Ejemplo n.º 18
0
def request_node_info_by_mac_address(mac_address):
    """Request node info for the given mac address.

    :param mac_address: The MAC Address of the node of the event.
    :type mac_address: unicode
    """
    if mac_address is None:
        maaslog.debug("Cannot determine node; MAC address is unknown.")
        return succeed(None)

    client = getRegionClient()
    d = client(RequestNodeInfoByMACAddress, mac_address=mac_address)

    def eb_request_node_info(failure):
        failure.trap(NoSuchNode)
        maaslog.debug("Node doesn't exist for MAC address: %s", mac_address)
        return None

    return d.addErrback(eb_request_node_info)
Ejemplo n.º 19
0
def power_change_failure(system_id, hostname, power_change, message):
    """Report a node that for which power control has failed."""
    assert power_change in ['on', 'off', 'cycle'
                            ], ("Unknown power change: %s" % power_change)
    maaslog.error("Error changing power state (%s) of node: %s (%s)",
                  power_change, hostname, system_id)
    client = getRegionClient()
    yield client(
        MarkNodeFailed,
        system_id=system_id,
        error_description=message,
    )
    if power_change == 'on':
        event_type = EVENT_TYPES.NODE_POWER_ON_FAILED
    elif power_change == 'off':
        event_type = EVENT_TYPES.NODE_POWER_OFF_FAILED
    elif power_change == 'cycle':
        event_type = EVENT_TYPES.NODE_POWER_CYCLE_FAILED
    yield send_node_event(event_type, system_id, hostname, message)
Ejemplo n.º 20
0
    def test_exits_gracefully_if_cant_report_foreign_dhcp_server(self):
        clock = Clock()
        interface_name = factory.make_name("eth")
        interfaces = {
            interface_name: {
                "enabled": True,
                "links": [{
                    "address": "10.0.0.1/24"
                }]
            }
        }

        maaslog = self.patch(dhcp_probe_service, 'maaslog')
        deferToThread = self.patch(dhcp_probe_service, 'deferToThread')
        deferToThread.side_effect = [
            defer.succeed(interfaces),
        ]
        probe_interface = self.patch(dhcp_probe_service, 'probe_interface')
        probe_interface.return_value = ['192.168.0.100']
        protocol, connecting = self.patch_rpc_methods()
        self.addCleanup((yield connecting))

        del protocol._commandDispatch[
            region.ReportForeignDHCPServer.commandName]

        rpc_service = Mock()
        rpc_service.getClientNow.return_value = defer.succeed(
            getRegionClient())
        service = DHCPProbeService(rpc_service, clock)
        yield service.startService()
        yield service.stopService()

        self.assertThat(
            maaslog.error,
            MockCalledOnceWith(
                "Unable to inform region of DHCP server: the region "
                "does not yet support the ReportForeignDHCPServer RPC "
                "method."))
Ejemplo n.º 21
0
    def test_doesnt_reports_services_to_region_when_the_same_status(self):
        # Pretend we're in a production environment.
        self.patch(sms, "is_dev_environment").return_value = False

        protocol, connecting = self.patch_rpc_methods()
        self.addCleanup((yield connecting))

        class ExampleService(AlwaysOnService):
            name = service_name = snap_service_name = (
                factory.make_name("service"))

        service = ExampleService()
        # Inveigle this new service into the service monitor.
        self.addCleanup(service_monitor._services.pop, service.name)
        service_monitor._services[service.name] = service

        state = ServiceState(SERVICE_STATE.ON, "running")
        mock_ensureServices = self.patch(service_monitor, "ensureServices")
        mock_ensureServices.return_value = succeed({
            service.name: state
        })

        client = getRegionClient()
        rpc_service = Mock()
        rpc_service.getClientNow.return_value = succeed(client)
        monitor_service = sms.ServiceMonitorService(
            rpc_service, Clock())
        monitor_service._services = yield monitor_service._buildServices({
            service.name: state
        })

        yield monitor_service.startService()
        yield monitor_service.stopService()

        self.assertThat(
            protocol.UpdateServices,
            MockNotCalled())
Ejemplo n.º 22
0
def create_node(macs,
                arch,
                power_type,
                power_parameters,
                domain=None,
                hostname=None):
    """Create a Node on the region and return its system_id.

    :param macs: A list of MAC addresses belonging to the node.
    :param arch: The node's architecture, in the form 'arch/subarch'.
    :param power_type: The node's power type as a string.
    :param power_parameters: The power parameters for the node, as a
        dict.
    :param domain: The domain the node should join.
    """
    if hostname is not None:
        hostname = coerce_to_valid_hostname(hostname)

    for elapsed, remaining, wait in retries(15, 5, reactor):
        try:
            client = getRegionClient()
            break
        except NoConnectionsAvailable:
            yield pause(wait, reactor)
    else:
        maaslog.error("Can't create node, no RPC connection to region.")
        return

    # De-dupe the MAC addresses we pass. We sort here to avoid test
    # failures.
    macs = sorted(set(macs))
    try:
        response = yield client(CreateNode,
                                architecture=arch,
                                power_type=power_type,
                                power_parameters=json.dumps(power_parameters),
                                mac_addresses=macs,
                                hostname=hostname,
                                domain=domain)
    except NodeAlreadyExists:
        # The node already exists on the region, so we log the error and
        # give up.
        maaslog.error(
            "A node with one of the mac addresses in %s already exists.", macs)
        returnValue(None)
    except UnhandledCommand:
        # The region hasn't been upgraded to support this method
        # yet, so give up.
        maaslog.error("Unable to create node on region: Region does not "
                      "support the CreateNode RPC method.")
        returnValue(None)
    except UnknownRemoteError as e:
        # This happens, for example, if a ValidationError occurs on the region.
        # (In particular, we see this if the hostname is a duplicate.)
        # We should probably create specific exceptions for these, so we can
        # act on them appropriately.
        maaslog.error(
            "Unknown error while creating node %s: %s (see regiond.log)", macs,
            e.description)
        returnValue(None)
    else:
        returnValue(response['system_id'])
Ejemplo n.º 23
0
def get_archive_mirrors():
    client = getRegionClient()
    return client(GetArchiveMirrors)