def decompose_machine(pod_type, context, pod_id, name): """Decompose a machine. The machine to delete is contained in the `context` just like power actions.""" pod_driver = PodDriverRegistry.get_item(pod_type) if pod_driver is None: raise UnknownPodType(pod_type) d = pod_driver.decompose(pod_id, context) if not isinstance(d, Deferred): raise PodActionFail( "bad pod driver '%s'; 'decompose' did not return Deferred." % pod_type) def convert(result): """Convert the result to send over RPC.""" if result is None or not isinstance(result, DiscoveredPodHints): raise PodActionFail( "bad pod driver '%s'; 'decompose' returned invalid result." % pod_type) else: return {"hints": result} def catch_all(failure): """Convert all failures into `PodActionFail` unless already a `PodActionFail` or `NotImplementedError`.""" # Log locally to help debugging. log.err(failure, "Failed to decompose machine.") if failure.check(NotImplementedError, PodActionFail): return failure else: raise PodActionFail(get_error_message(failure.value)) d.addCallback(convert) d.addErrback(catch_all) return d
def test__raises_PodProblem_for_UnknownPodType(self): pod = factory.make_Pod() node = factory.make_Node() token = NodeKey.objects.get_token_for_node(node) metadata_url = factory.make_url() client = Mock() client.return_value = fail(UnknownPodType(pod.power_type)) error = self.assertRaises( PodProblem, wait_for_reactor(send_pod_commissioning_results), client, pod.id, pod.name, pod.power_type, node.system_id, pod.power_parameters, token.consumer.key, token.key, token.secret, metadata_url, ) self.assertEqual( f"Unable to send commissioning results for {pod.name}({pod.id}) " f"because `{pod.power_type}` is an unknown Pod type.", str(error), )
def test_returns_discovered_projects_and_errors(self): pod_type = factory.make_name("pod") projects = [ DiscoveredPodProject(name="p1", description="Project 1"), DiscoveredPodProject(name="p2", description="Project 2"), ] clients = [] client = Mock() error_rack_id = factory.make_name("system_id") client.ident = error_rack_id exception = UnknownPodType(pod_type) client.return_value = fail(exception) clients.append(client) valid_rack_id = factory.make_name("system_id") client = Mock() client.ident = valid_rack_id client.return_value = succeed({"projects": projects}) clients.append(client) self.patch(pods_module, "getAllClients").return_value = clients discovered = yield discover_pod_projects(pod_type, {}) self.assertEqual(({ valid_rack_id: projects }, { error_rack_id: exception }), discovered)
def test_returns_discovered_pod_and_errors(self): pod_type = factory.make_name("pod") pod = DiscoveredPod( architectures=["amd64/generic"], cores=random.randint(1, 8), cpu_speed=random.randint(1000, 3000), memory=random.randint(1024, 4096), local_storage=random.randint(500, 1000), hints=DiscoveredPodHints( cores=random.randint(1, 8), cpu_speed=random.randint(1000, 3000), memory=random.randint(1024, 4096), local_storage=random.randint(500, 1000), ), ) clients = [] client = Mock() error_rack_id = factory.make_name("system_id") client.ident = error_rack_id exception = UnknownPodType(pod_type) client.return_value = fail(exception) clients.append(client) valid_rack_id = factory.make_name("system_id") client = Mock() client.ident = valid_rack_id client.return_value = succeed({"pod": pod}) clients.append(client) self.patch(pods_module, "getAllClients").return_value = clients discovered = yield discover_pod(pod_type, {}) self.assertEqual( ({valid_rack_id: pod}, {error_rack_id: exception}), discovered )
def test_raises_UnknownPodType_over_unknown(self): exc_type = factory.make_exception_type() self.assertRaises( UnknownPodType, get_best_discovered_result, ({}, { factory.make_name("system_id"): exc_type(), factory.make_name("system_id"): UnknownPodType("unknown"), }))
def compose_machine(pod_type, context, request, pod_id, name): """Compose a machine that at least matches equal to or greater than `request`. The region controller handles parsing the outputed `DiscoveredMachine` and updating the database as required. """ pod_driver = PodDriverRegistry.get_item(pod_type) if pod_driver is None: raise UnknownPodType(pod_type) d = pod_driver.compose(pod_id, context, request) if not isinstance(d, Deferred): raise PodActionFail( "bad pod driver '%s'; 'compose' did not return Deferred." % pod_type ) def convert(result): """Convert the result to send over RPC.""" if result is None: # None is allowed when a machine could not be composed with the # driver. This means it could not match the request. Returning None # allows the region to try another pod if available to compose # that machine. raise PodInvalidResources() else: if ( isinstance(result, tuple) and len(result) == 2 and isinstance(result[0], DiscoveredMachine) and isinstance(result[1], DiscoveredPodHints) ): return {"machine": result[0], "hints": result[1]} else: raise PodActionFail( "bad pod driver '%s'; 'compose' returned " "invalid result." % pod_type ) def catch_all(failure): """Convert all failures into `PodActionFail` unless already a `PodActionFail`, `PodInvalidResources` or `NotImplementedError`.""" if failure.check(PodInvalidResources): # Driver returned its own invalid resource exception instead of # None. Just pass this onto the region. return failure # Log locally to help debugging. log.err(failure, "%s: Failed to compose machine: %s" % (name, request)) if failure.check(NotImplementedError, PodActionFail): return failure else: raise PodActionFail(get_error_message(failure.value)) d.addCallback(convert) d.addErrback(catch_all) return d
def test_raises_NotImplemended_over_UnknownPodType(self): exc_type = factory.make_exception_type() self.assertRaises( NotImplementedError, get_best_discovered_result, ({}, { factory.make_name("system_id"): exc_type(), factory.make_name("system_id"): UnknownPodType("unknown"), factory.make_name("system_id"): NotImplementedError(), }))
def test__raises_PodProblem_for_UnknownPodType(self): pod = factory.make_Pod() client = Mock() client.return_value = fail(UnknownPodType(pod.power_type)) error = self.assertRaises(PodProblem, wait_for_reactor(decompose_machine), client, pod.power_type, pod.power_parameters, pod.id, pod.name) self.assertEqual( "Unable to decompose machine because '%s' is an " "unknown pod type." % pod.power_type, str(error))
def discover_pod(pod_type, context, pod_id=None, name=None): """Discover all the pod information and return the result to the region controller. The region controller handles parsing the output and updating the database as required. """ pod_driver = PodDriverRegistry.get_item(pod_type) if pod_driver is None: raise UnknownPodType(pod_type) try: d = ensureDeferred(pod_driver.discover(pod_id, context)) except ValueError: raise PodActionFail( "bad pod driver '%s'; 'discover' did not return Deferred." % pod_type ) def convert(result): """Convert the result to send over RPC.""" if result is None: raise PodActionFail("unable to discover pod information.") elif not isinstance(result, DiscoveredPod): raise PodActionFail( "bad pod driver '%s'; 'discover' returned invalid result." % pod_type ) else: return {"pod": result} def catch_all(failure): """Convert all failures into `PodActionFail` unless already a `PodActionFail` or `NotImplementedError`.""" # Log locally to help debugging. log.err(failure, "Failed to discover pod.") if failure.check(NotImplementedError, PodActionFail): return failure else: raise PodActionFail(get_error_message(failure.value)) d.addCallback(convert) d.addErrback(catch_all) return d
def send_pod_commissioning_results( pod_type, context, pod_id, name, system_id, consumer_key, token_key, token_secret, metadata_url, ): """Send commissioning results for the Pod to the region.""" pod_driver = PodDriverRegistry.get_item(pod_type) if pod_driver is None: raise UnknownPodType(pod_type) d = pod_driver.get_commissioning_data(pod_id, context) if not isinstance(d, Deferred): raise PodActionFail( f"bad pod driver '{pod_type}'; 'get_commissioning_data' did not return Deferred." ) def send_items(commissioning_results): for filename, content in commissioning_results.items(): if isinstance(content, dict) or isinstance(content, list): content = json.dumps(content, indent=4) if not isinstance(content, bytes): content = content.encode() try: signal( url=metadata_url.geturl(), creds={ "consumer_key": consumer_key, "token_key": token_key, "token_secret": token_secret, "consumer_secret": "", }, status="WORKING", files={ # UI shows combined output by default. filename: content, # STDOUT output is whats actually proceed. f"{filename}.out": content, # Clear out STDERR and results. f"{filename}.err": b"", f"{filename}.yaml": b"", }, exit_status=0, error=f"Finished {filename}: 0", ) except SignalException as e: raise PodActionFail( f"Unable to send Pod commissioning information for {name}({system_id}): {e.error}" ) def catch_all(failure): """Convert all failures into `PodActionFail` unless already a `PodActionFail` or `NotImplementedError`.""" # Log locally to help debugging. log.err(failure, "Failed to send_pod_commissioning_results.") if failure.check(NotImplementedError, PodActionFail): return failure else: raise PodActionFail(get_error_message(failure.value)) d.addCallback( lambda commissioning_results: deferToThread( send_items, commissioning_results ) ) d.addCallback(lambda _: {}) d.addErrback(catch_all) return d