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"]))
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, ), )
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)
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)
def send(_): client = getRegionClient() return client( SendEvent, system_id=system_id, type_name=event_type, description=description, )
def send(_): client = getRegionClient() return client( SendEventMACAddress, mac_address=mac_address, type_name=event_type, description=description, )
def send(_): client = getRegionClient() return client( SendEventIPAddress, ip_address=ip_address, type_name=event_type, description=description, )
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
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)
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())
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)
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
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))
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)
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)
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)
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)
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."))
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())
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'])
def get_archive_mirrors(): client = getRegionClient() return client(GetArchiveMirrors)