def test_read_returns_limited_fields(self): factory.make_Pod(capabilities=[ Capabilities.FIXED_LOCAL_STORAGE, Capabilities.ISCSI_STORAGE ]) response = self.client.get(reverse('pods_handler')) parsed_result = json_load_bytes(response.content) self.assertItemsEqual([ 'id', 'name', 'tags', 'type', 'resource_uri', 'capabilities', 'architectures', 'total', 'used', 'zone', 'available', 'cpu_over_commit_ratio', 'memory_over_commit_ratio' ], list(parsed_result[0])) self.assertItemsEqual([ 'cores', 'memory', 'local_storage', 'local_disks', 'iscsi_storage', ], list(parsed_result[0]['total'])) self.assertItemsEqual([ 'cores', 'memory', 'local_storage', 'local_disks', 'iscsi_storage', ], list(parsed_result[0]['used'])) self.assertItemsEqual([ 'cores', 'memory', 'local_storage', 'local_disks', 'iscsi_storage', ], list(parsed_result[0]['available']))
def test_no_cert(self): name = factory.make_name() factory.make_Pod( name=name, pod_type="lxd", parameters={ "power_address": "1.2.3.4", "password": "******", }, ) response = self.client.get(reverse("vmhost-certificate", args=[name])) self.assertEqual(response.status_code, http.client.NOT_FOUND)
def test_pod_DELETE_delete_without_force(self): self.become_admin() vlan = factory.make_VLAN() subnet = factory.make_Subnet(vlan=vlan) region = factory.make_Node_with_Interface_on_Subnet( node_type=NODE_TYPE.REGION_CONTROLLER, subnet=subnet, vlan=vlan) ip = factory.make_StaticIPAddress( interface=region.interface_set.first()) factory.make_Pod(ip_address=ip) mock_async_delete = self.patch(Pod, "async_delete") response = self.client.delete(self.get_region_uri(region)) self.assertEqual( http.client.BAD_REQUEST, response.status_code, explain_unexpected_response(http.client.BAD_REQUEST, response)) self.assertThat(mock_async_delete, MockNotCalled())
def test_DELETE_force_not_required_for_pod_region_rack(self): self.become_admin() vlan = factory.make_VLAN() factory.make_Subnet(vlan=vlan) rack = factory.make_RegionRackController(vlan=vlan) ip = factory.make_StaticIPAddress(interface=rack.interface_set.first()) factory.make_Pod(ip_address=ip) mock_async_delete = self.patch(Pod, "async_delete") response = self.client.delete(self.get_region_uri(rack), QUERY_STRING=urlencode({'force': 'true'}, doseq=True)) self.assertEqual( http.client.NO_CONTENT, response.status_code, explain_unexpected_response(http.client.NO_CONTENT, response)) self.assertThat(mock_async_delete, MockNotCalled())
def test_get_resources_hugepages_round(self): node = factory.make_Node() numa_node0 = node.default_numanode numa_node0.cores = [0, 1] numa_node0.memory = 4096 numa_node0.save() numa_node1 = factory.make_NUMANode( node=node, cores=[2, 3], memory=8192 ) factory.make_NUMANodeHugepages( numa_node=numa_node0, page_size=2048 * MB, total=4096 * MB ) factory.make_NUMANodeHugepages( numa_node=numa_node1, page_size=4096 * MB, total=8192 * MB ) pod = factory.make_Pod(pod_type="lxd") pod.hints.nodes.add(node) factory.make_VirtualMachine( memory=2048, pinned_cores=[0, 2], hugepages_backed=True, bmc=pod, machine=factory.make_Node(system_id="vm0"), ) resources = get_vm_host_resources(pod) self.assertEqual( [asdict(r) for r in resources], [ { "cores": {"allocated": [0], "free": [1]}, "memory": { "general": {"allocated": 0, "free": 0}, "hugepages": [ { "allocated": 2048 * MB, "free": 2048 * MB, "page_size": 2048 * MB, } ], }, "node_id": 0, "vms": [{"pinned_cores": [0], "system_id": "vm0"}], }, { "cores": {"allocated": [2], "free": [3]}, "memory": { "general": {"allocated": 0, "free": 0}, "hugepages": [ { "allocated": 4096 * MB, "free": 4096 * MB, "page_size": 4096 * MB, } ], }, "node_id": 1, "vms": [{"pinned_cores": [2], "system_id": "vm0"}], }, ], )
def test_updates_existing_pod_minimal(self): self.fake_pod_discovery() zone = factory.make_Zone() pool = factory.make_ResourcePool() cpu_over_commit = random.randint(0, 10) memory_over_commit = random.randint(0, 10) power_parameters = { 'power_address': 'qemu+ssh://1.2.3.4/system', 'power_pass': '******', } orig_pod = factory.make_Pod( pod_type='virsh', zone=zone, pool=pool, cpu_over_commit_ratio=cpu_over_commit, memory_over_commit_ratio=memory_over_commit, parameters=power_parameters) new_name = factory.make_name("pod") form = PodForm( data={'name': new_name}, request=self.request, instance=orig_pod) self.assertTrue(form.is_valid(), form._errors) pod = form.save() self.assertEqual(new_name, pod.name) self.assertEqual(zone, pod.zone) self.assertEqual(pool, pod.pool) self.assertEqual(cpu_over_commit, pod.cpu_over_commit_ratio) self.assertEqual(memory_over_commit, pod.memory_over_commit_ratio) self.assertEqual(memory_over_commit, pod.memory_over_commit_ratio) self.assertEqual(power_parameters, pod.power_parameters)
def test_PUT_update_deletes_pod_console_logging_tag(self): self.become_admin() factory.make_Tag(name='pod-console-logging') pod_info = self.make_pod_info() pod = factory.make_Pod(pod_type=pod_info['type'], tags=['pod-console-logging']) self.fake_pod_discovery() response = self.client.put(get_pod_uri(pod), { 'console_logging': 'False', }) self.assertEqual(http.client.OK, response.status_code, response.content) self.assertRaises(Tag.DoesNotExist, Tag.objects.get, name='pod-console-logging') response = self.client.put(get_pod_uri(pod), { 'console_logging': 'False', }) self.assertEqual(http.client.OK, response.status_code, response.content) response = self.client.put(get_pod_uri(pod), { 'name': factory.make_name('name'), }) self.assertEqual(http.client.OK, response.status_code, response.content) pod.refresh_from_db() self.assertNotIn('pod-console-logging', pod.tags) self.assertRaises(Tag.DoesNotExist, Tag.objects.get, name='pod-console-logging')
def test_PUT_update_wont_delete_pod_console_logging_tag_if_in_use(self): self.become_admin() factory.make_Tag(name='pod-console-logging') pod1_info = self.make_pod_info() factory.make_Pod(pod_type=pod1_info['type'], tags=['pod-console-logging']) pod2_info = self.make_pod_info() pod2 = factory.make_Pod(pod_type=pod2_info['type'], tags=['pod-console-logging']) self.fake_pod_discovery() response = self.client.put(get_pod_uri(pod2), { 'console_logging': 'False', }) self.assertEqual(http.client.OK, response.status_code, response.content) self.assertIsNotNone(Tag.objects.get(name='pod-console-logging'))
def test_PUT_updates(self): self.become_admin() pod = factory.make_Pod(pod_type='virsh') new_name = factory.make_name('pod') new_tags = [ factory.make_name('tag'), factory.make_name('tag'), 'pod-console-logging', ] new_pool = factory.make_ResourcePool() new_zone = factory.make_Zone() new_power_parameters = { 'power_address': 'qemu+ssh://1.2.3.4/system', 'power_pass': factory.make_name('pass'), } discovered_pod, _, _ = self.fake_pod_discovery() response = self.client.put( get_pod_uri(pod), { 'name': new_name, 'tags': ','.join(new_tags), 'power_address': new_power_parameters['power_address'], 'power_pass': new_power_parameters['power_pass'], 'zone': new_zone.name, 'pool': new_pool.name, 'console_logging': 'True', }) self.assertEqual(http.client.OK, response.status_code, response.content) pod.refresh_from_db() self.assertIsNotNone(Tag.objects.get(name="pod-console-logging")) self.assertEqual(new_name, pod.name) self.assertEqual(new_pool, pod.pool) self.assertItemsEqual(new_tags, pod.tags) self.assertEqual(new_power_parameters, pod.power_parameters) self.assertEqual(new_zone, pod.zone)
def test_pod_DELETE_delete_without_force(self): self.become_admin() vlan = factory.make_VLAN() factory.make_Subnet(vlan=vlan) rack = factory.make_RackController(vlan=vlan) ip = factory.make_StaticIPAddress(interface=rack.interface_set.first()) factory.make_Pod(ip_address=ip) vlan.dhcp_on = True vlan.primary_rack = rack vlan.save() mock_async_delete = self.patch(Pod, "async_delete") response = self.client.delete(self.get_rack_uri(rack)) self.assertEqual( http.client.BAD_REQUEST, response.status_code, explain_unexpected_response(http.client.BAD_REQUEST, response)) self.assertThat(mock_async_delete, MockNotCalled())
def test_get_resources_interfaces_not_sriov(self): node = factory.make_Node() iface = factory.make_Interface( INTERFACE_TYPE.PHYSICAL, name="eth0", numa_node=node.default_numanode, sriov_max_vf=8, ) project = factory.make_string() pod = factory.make_Pod( pod_type="lxd", parameters={"project": project}, host=node, ) VirtualMachineInterface.objects.create( vm=factory.make_VirtualMachine(bmc=pod, project=project), host_interface=iface, attachment_type=InterfaceAttachType.BRIDGE, ) resources = get_vm_host_resources(pod) self.assertEqual( resources.interfaces, [ VMHostNetworkInterface( id=iface.id, name="eth0", numa_index=0, virtual_functions=VMHostResource( allocated_tracked=0, allocated_other=0, free=8, ), ), ], )
def test_get_resources_interfaces_no_vm_link(self): node = factory.make_Node() iface = factory.make_Interface( INTERFACE_TYPE.PHYSICAL, name="eth0", numa_node=node.default_numanode, sriov_max_vf=8, ) pod = factory.make_Pod( pod_type="lxd", host=node, ) resources = get_vm_host_resources(pod) self.assertEqual( resources.interfaces, [ VMHostNetworkInterface( id=iface.id, name="eth0", numa_index=0, virtual_functions=VMHostResource( allocated_tracked=0, allocated_other=0, free=8, ), ), ], )
def test_get_resources_no_detailed(self): pod = factory.make_Pod(pod_type="lxd", host=factory.make_Node()) factory.make_VirtualMachine(bmc=pod) resources = get_vm_host_resources(pod, detailed=False) # NUMA info and VMs list are not included when not in detailed mode self.assertEqual(resources.numa, []) self.assertEqual(resources.vms, [])
def test_deletes_hints_when_chassis_converted_to_bmc(self): pod = factory.make_Pod() pod = pod.as_bmc() pod.bmc_type = BMC_TYPE.BMC pod.save() self.assertRaises(PodHints.DoesNotExist, lambda: reload_object(pod).hints)
def test_discover_and_sync_existing_pod(self): discovered_pod, discovered_racks, failed_racks = ( self.fake_pod_discovery()) pod_info = self.make_pod_info() orig_pod = factory.make_Pod(pod_type=pod_info['type']) request = MagicMock() request.user = factory.make_User() form = PodForm(data=pod_info, request=request, instance=orig_pod) pod = form.discover_and_sync_pod() self.assertThat( pod, MatchesStructure( id=Equals(orig_pod.id), bmc_type=Equals(BMC_TYPE.POD), architectures=Equals(['amd64/generic']), name=Equals(orig_pod.name), cores=Equals(discovered_pod.cores), memory=Equals(discovered_pod.memory), cpu_speed=Equals(discovered_pod.cpu_speed), power_type=Equals(pod_info['type']), power_parameters=Equals({}), ip_address=Is(None), )) routable_racks = [ relation.rack_controller for relation in pod.routable_rack_relationships.all() if relation.routable ] not_routable_racks = [ relation.rack_controller for relation in pod.routable_rack_relationships.all() if not relation.routable ] self.assertItemsEqual(routable_racks, discovered_racks) self.assertItemsEqual(not_routable_racks, failed_racks)
def make_pod( self, cpu=0, mem=0, cpu_over_commit=1, mem_over_commit=1, pod_type="virsh", ): # Make one pod zone = factory.make_Zone() pool = factory.make_ResourcePool() ip = factory.make_ipv4_address() power_parameters = { "power_address": "qemu+ssh://%s/system" % ip, "power_pass": "******", } return factory.make_Pod( pod_type=pod_type, zone=zone, pool=pool, cores=cpu, memory=mem, cpu_over_commit_ratio=cpu_over_commit, memory_over_commit_ratio=mem_over_commit, parameters=power_parameters, )
def test_certificate_pem(self): cert = generate_certificate("maas") name = factory.make_name() factory.make_Pod( name=name, pod_type="lxd", parameters={ "power_address": "1.2.3.4", "certificate": cert.certificate_pem(), "key": cert.private_key_pem(), }, ) response = self.client.get(reverse("vmhost-certificate", args=[name])) self.assertEqual(response.status_code, http.client.OK) self.assertEqual(response["Content-Type"], "text/plain") self.assertEqual(response.content.decode(), cert.certificate_pem())
def test_parameters_requires_admin(self): pod = factory.make_Pod() response = self.client.get(get_pod_uri(pod), { 'op': 'parameters', }) self.assertEqual( http.client.FORBIDDEN, response.status_code, response.content)
def test_store_result_allows_pod_to_overwrite(self): pod = factory.make_Pod() node = factory.make_Node() script_set = factory.make_ScriptSet(node=node) script_result = factory.make_ScriptResult(script_set=script_set, status=SCRIPT_STATUS.PASSED) pod.hints.nodes.add(node) exit_status = random.randint(0, 255) output = factory.make_bytes() stdout = factory.make_bytes() stderr = factory.make_bytes() result = factory.make_bytes() script_result.store_result( random.randint(0, 255), factory.make_bytes(), factory.make_bytes(), factory.make_bytes(), factory.make_bytes(), ) script_result.store_result(exit_status, output, stdout, stderr, result) self.assertEqual(exit_status, script_result.exit_status) self.assertEqual(output, script_result.output) self.assertEqual(stdout, script_result.stdout) self.assertEqual(stderr, script_result.stderr) self.assertEqual(result, script_result.result)
def test_GET_reads_pod(self): pod = factory.make_Pod() response = self.client.get(get_pod_uri(pod)) self.assertEqual( http.client.OK, response.status_code, response.content) parsed_pod = json_load_bytes(response.content) self.assertEqual(pod.id, parsed_pod["id"])
def test__calls_and_returns_correctly(self): pod = factory.make_Pod() client = Mock() client.return_value = succeed({ "machine": sentinel.machine, "hints": sentinel.hints }) machine, hints = wait_for_reactor(compose_machine)( client, pod.power_type, pod.power_parameters, sentinel.request, pod.id, pod.name, ) self.assertThat( client, MockCalledOnceWith( ComposeMachine, type=pod.power_type, context=pod.power_parameters, request=sentinel.request, pod_id=pod.id, name=pod.name, ), ) self.assertEqual(sentinel.machine, machine) self.assertEqual(sentinel.hints, hints)
def test__calls_and_returns_correctly(self): hints = DiscoveredPodHints( cores=random.randint(1, 8), cpu_speed=random.randint(1000, 2000), memory=random.randint(1024, 8192), local_storage=0, ) pod = factory.make_Pod() client = Mock() client.return_value = succeed({"hints": hints}) result = wait_for_reactor(decompose_machine)(client, pod.power_type, pod.power_parameters, pod.id, pod.name) self.assertThat( client, MockCalledOnceWith( DecomposeMachine, type=pod.power_type, context=pod.power_parameters, pod_id=pod.id, name=pod.name, ), ) self.assertEqual(hints, result)
def test__raises_same_exception(self): pod = factory.make_Pod() node = factory.make_Node() token = NodeKey.objects.get_token_for_node(node) metadata_url = factory.make_url() client = Mock() exception_type = factory.make_exception_type() exception_msg = factory.make_name("error") client.return_value = fail(exception_type(exception_msg)) error = self.assertRaises( exception_type, 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(exception_msg, str(error))
def test__raises_PodProblem_for_PodActionFail(self): pod = factory.make_Pod() node = factory.make_Node() token = NodeKey.objects.get_token_for_node(node) metadata_url = factory.make_url() error_msg = factory.make_name("error") client = Mock() client.return_value = fail(PodActionFail(error_msg)) 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: {error_msg}", str(error), )
def test__raises_PodProblem_for_NotImplementedError(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(NotImplementedError()) 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}` driver does not implement the " "'send_pod_commissioning_results' method.", str(error), )
def make_pod_with_hints(self, **kwargs): architectures = [ "amd64/generic", "i386/generic", "arm64/generic", "armhf/generic", ] pod = factory.make_Pod( architectures=architectures, capabilities=[ Capabilities.FIXED_LOCAL_STORAGE, Capabilities.ISCSI_STORAGE, Capabilities.COMPOSABLE, Capabilities.STORAGE_POOLS, ], **kwargs, ) pod.hints.cores = random.randint(8, 16) pod.hints.memory = random.randint(4096, 8192) pod.hints.cpu_speed = random.randint(2000, 3000) pod.hints.save() for _ in range(3): pool = factory.make_PodStoragePool(pod) pod.default_storage_pool = pool pod.save() return pod
def test_remove_tag_requires_admin(self): pod = factory.make_Pod() response = self.client.post( get_pod_uri(pod), {'op': 'remove_tag', 'tag': factory.make_name('tag')}) self.assertEqual( http.client.FORBIDDEN, response.status_code, response.content)
def test_DELETE_delete_with_force(self): self.become_admin() vlan = factory.make_VLAN() subnet = factory.make_Subnet(vlan=vlan) region = factory.make_Node_with_Interface_on_Subnet( node_type=NODE_TYPE.REGION_CONTROLLER, subnet=subnet, vlan=vlan) ip = factory.make_StaticIPAddress( interface=region.interface_set.first()) factory.make_Pod(ip_address=ip) mock_async_delete = self.patch(Pod, "async_delete") response = self.client.delete(self.get_region_uri(region), QUERY_STRING=urlencode({'force': 'true'}, doseq=True)) self.assertEqual( http.client.NO_CONTENT, response.status_code, explain_unexpected_response(http.client.NO_CONTENT, response)) self.assertThat(mock_async_delete, MockCallsMatch(call()))
def test_get_resources_no_host(self): pod = factory.make_Pod(pod_type="lxd", host=None) factory.make_VirtualMachine( memory=1024, pinned_cores=[0, 2], bmc=pod, ) self.assertEqual(get_vm_host_resources(pod), [])
def test_get_resources_unaligned(self): node = factory.make_Node() numa_node0 = node.default_numanode numa_node0.cores = [0, 1] numa_node0.memory = 4096 numa_node0.save() factory.make_NUMANode(node=node, cores=[2, 3], memory=2048) pod = factory.make_Pod(pod_type="lxd") pod.hints.nodes.add(node) factory.make_VirtualMachine( memory=2048, pinned_cores=[0, 2], hugepages_backed=False, bmc=pod, machine=factory.make_Node(system_id="vm0"), ) resources = get_vm_host_resources(pod) self.assertEqual( [asdict(r) for r in resources], [ { "cores": { "allocated": [0], "free": [1] }, "memory": { "general": { "allocated": 1024 * MB, "free": 3072 * MB }, "hugepages": [], }, "node_id": 0, "vms": [{ "pinned_cores": [0], "system_id": "vm0" }], }, { "cores": { "allocated": [2], "free": [3] }, "memory": { "general": { "allocated": 1024 * MB, "free": 1024 * MB }, "hugepages": [], }, "node_id": 1, "vms": [{ "pinned_cores": [2], "system_id": "vm0" }], }, ], )