def test_POST_calls_import_boot_images_on_all_clusters(self): self.become_admin() from maasserver.clusterrpc import boot_images self.patch(boot_images, "RackControllersImporter") name = factory.make_name("name") architecture = make_usable_architecture(self) params = { "name": name, "architecture": architecture, "content": (factory.make_file_upload(content=sample_binary_data)), } response = self.client.post(reverse("boot_resources_handler"), params) self.assertEqual(http.client.CREATED, response.status_code) self.assertThat(boot_images.RackControllersImporter.schedule, MockCalledOnceWith())
def test_mac_in_use_on_current_node_passes(self): node = factory.make_Node_with_Interface_on_Subnet( address="aa:bb:cc:dd:ee:ff") architecture = make_usable_architecture(self) form = MachineWithMACAddressesForm( data=self.make_params( mac_addresses=["aa:bb:cc:dd:ee:ff", "9a:bb:c3:33:e5:7f"], architecture=architecture, ), instance=node, ) self.assertTrue(form.is_valid(), dict(form.errors)) self.assertEqual( ["aa:bb:cc:dd:ee:ff", "9a:bb:c3:33:e5:7f"], form.cleaned_data["mac_addresses"], ) self.assertEqual(architecture, form.cleaned_data["architecture"])
def test_POST_admin_creates_machine_in_commissioning_state(self): # When an admin user enlists a machine, it goes into the # Commissioning state. response = self.client.post( reverse("machines_handler"), { "hostname": factory.make_string(), "architecture": make_usable_architecture(self), "power_type": "manual", "mac_addresses": ["aa:bb:cc:dd:ee:ff"], }, ) self.assertEqual(http.client.OK, response.status_code) system_id = json_load_bytes(response.content)["system_id"] self.assertEqual( NODE_STATUS.COMMISSIONING, Machine.objects.get(system_id=system_id).status, )
def test_create_with_form_uses_form_from_get_form_class(self): hostname = factory.make_name("hostname") arch = make_usable_architecture(self) handler = self.make_nodes_handler(fields=['hostname', 'architecture']) self.patch( handler, "get_form_class").return_value = AdminMachineWithMACAddressesForm json_obj = handler.create({ "hostname": hostname, "architecture": arch, "mac_addresses": [factory.make_mac_address()], }) self.expectThat({ "hostname": hostname, "architecture": arch, }, Equals(json_obj))
def test_rejects_invalid_osystem_distro_series(self): self.client_log_in() node = factory.make_Node(owner=self.logged_in_user) osystem = make_usable_osystem(self) release = factory.make_name('release') form = MachineForm(data={ 'hostname': factory.make_name('host'), 'architecture': make_usable_architecture(self), 'osystem': osystem['name'], 'distro_series': '%s/%s' % (osystem['name'], release), }, instance=node) self.assertFalse(form.is_valid()) self.assertItemsEqual(['distro_series'], form._errors.keys())
def test_POST_new_generates_hostname_if_ip_based_hostname(self): Domain.objects.get_or_create(name="domain") hostname = "192-168-5-19.domain" response = self.client.post( reverse("machines_handler"), { "hostname": hostname, "architecture": make_usable_architecture(self), "power_type": "manual", "mac_addresses": [factory.make_mac_address()], }, ) self.assertEqual(http.client.OK, response.status_code) parsed_result = json_load_bytes(response.content) system_id = parsed_result.get("system_id") machine = Machine.objects.get(system_id=system_id) self.assertNotEqual(hostname, machine.hostname)
def test_with_mac_in_use_on_uknown_interface_passes(self): factory.make_Interface( INTERFACE_TYPE.UNKNOWN, mac_address="aa:bb:cc:dd:ee:ff" ) architecture = make_usable_architecture(self) form = MachineWithMACAddressesForm( data=self.make_params( mac_addresses=["aa:bb:cc:dd:ee:ff", "9a:bb:c3:33:e5:7f"], architecture=architecture, ) ) self.assertTrue(form.is_valid(), dict(form.errors)) self.assertEqual( ["aa:bb:cc:dd:ee:ff", "9a:bb:c3:33:e5:7f"], form.cleaned_data["mac_addresses"], ) self.assertEqual(architecture, form.cleaned_data["architecture"])
def test_accepts_osystem_distro_series(self): user = factory.make_User() self.client.login(user=user) node = factory.make_Node(owner=user) osystem = make_usable_osystem(self) release = osystem['default_release'] form = MachineForm(data={ 'hostname': factory.make_name('host'), 'architecture': make_usable_architecture(self), 'osystem': osystem['name'], 'distro_series': '%s/%s' % (osystem['name'], release), }, instance=node) self.assertTrue(form.is_valid(), form._errors)
def test_set_distro_series_accepts_short_distro_series(self): user = factory.make_User() self.client.login(user=user) node = factory.make_Node(owner=user) release = factory.make_name("release") make_usable_osystem( self, releases=[release + "6", release + "0", release + "3"] ) form = MachineForm( data={ "hostname": factory.make_name("host"), "architecture": make_usable_architecture(self), }, instance=node, ) form.set_distro_series(release) form.save() self.assertEqual(release + "6", node.distro_series)
def test_rejects_mismatch_osystem_distro_series(self): user = factory.make_User() self.client.login(user=user) node = factory.make_Node(owner=user) osystem = make_usable_osystem(self) release = osystem["default_release"] invalid = factory.make_name("invalid_os") form = MachineForm( data={ "hostname": factory.make_name("host"), "architecture": make_usable_architecture(self), "osystem": osystem["name"], "distro_series": "%s/%s" % (invalid, release), }, instance=node, ) self.assertFalse(form.is_valid()) self.assertItemsEqual(["distro_series"], form._errors.keys())
def test_saves_power_parameters(self): self.prepare_rack_rpc() mac_addresses = [factory.make_mac_address() for _ in range(3)] architecture = make_usable_architecture(self) power_parameters = { "power_address": factory.make_ip_address(), # XXX: URLs break. "power_pass": factory.make_name("power_pass"), "power_id": factory.make_name("power_id"), } node = create_node(architecture, "virsh", power_parameters, mac_addresses) # Reload the object from the DB so that we're sure its power # parameters are being persisted. node = reload_object(node) self.assertEqual(power_parameters, node.power_parameters)
def test_POST_updates_power_parameters_rejects_unknown_param(self): hostname = factory.make_string() response = self.client.post( reverse('machines_handler'), { 'hostname': hostname, 'architecture': make_usable_architecture(self), 'power_type': 'manual', 'power_parameters_unknown_param': factory.make_string(), 'mac_addresses': [factory.make_mac_address()], }) self.assertEqual( ( http.client.BAD_REQUEST, {'power_parameters': ["Unknown parameter(s): unknown_param."]} ), (response.status_code, json_load_bytes(response.content))) self.assertFalse(Machine.objects.filter(hostname=hostname).exists())
def test_POST_create_creates_machine(self): hostname = factory.make_name("hostname") architecture = make_usable_architecture(self) response = self.client.post( reverse('machines_handler'), { 'hostname': hostname, 'architecture': architecture, 'power_type': 'manual', 'mac_addresses': ['aa:bb:cc:dd:ee:ff', '22:bb:cc:dd:ee:ff'], }) self.assertEqual(http.client.OK, response.status_code) node_metadata = NodeMetadata.objects.get(key='enlisting') self.assertEqual(node_metadata.value, 'True') [machine] = Machine.objects.filter(hostname=hostname) self.assertEqual(architecture, machine.architecture) self.assertEqual( machine.system_id, json_load_bytes(response.content)['system_id'])
def test_created_machine_gets_default_domain_appended(self): hostname_without_domain = factory.make_name('hostname') response = self.client.post( reverse('machines_handler'), { 'hostname': hostname_without_domain, 'architecture': make_usable_architecture(self), 'power_type': 'manual', 'mac_addresses': [factory.make_mac_address()], }) self.assertEqual( http.client.OK, response.status_code, response.content) parsed_result = json_load_bytes(response.content) expected_hostname = '%s.%s' % ( hostname_without_domain, Domain.objects.get_default_domain().name) self.assertEqual( expected_hostname, parsed_result.get('fqdn'))
def test_POST_create_associates_mac_addresses(self): # The API allows a Machine to be created and associated with MAC # Addresses. architecture = make_usable_architecture(self) self.client.post( reverse("machines_handler"), { "hostname": "diane", "architecture": architecture, "power_type": "manual", "mac_addresses": ["aa:bb:cc:dd:ee:ff", "22:bb:cc:dd:ee:ff"], }, ) diane = get_one(Machine.objects.filter(hostname="diane")) self.assertItemsEqual( ["aa:bb:cc:dd:ee:ff", "22:bb:cc:dd:ee:ff"], [interface.mac_address for interface in diane.interface_set.all()], )
def test_prevents_unsupported_osystem(self): reserved_name = "%s/%s" % ( factory.make_name("osystem"), factory.make_name("series"), ) upload_type, filetype = self.pick_filetype() size = random.randint(1024, 2048) content = factory.make_string(size).encode("utf-8") upload_name = factory.make_name("filename") uploaded_file = SimpleUploadedFile(content=content, name=upload_name) data = { "name": reserved_name, "title": factory.make_name("title"), "architecture": make_usable_architecture(self), "filetype": upload_type, } form = BootResourceForm(data=data, files={"content": uploaded_file}) self.assertFalse(form.is_valid())
def test_POST_create_creates_machine_with_arch_only(self): architecture = make_usable_architecture(self, subarch_name="generic") response = self.client.post( reverse('machines_handler'), { 'hostname': 'diane', 'architecture': architecture.split('/')[0], 'power_type': 'manual', 'mac_addresses': ['aa:bb:cc:dd:ee:ff', '22:bb:cc:dd:ee:ff'], }) self.assertEqual(http.client.OK, response.status_code) parsed_result = json_load_bytes(response.content) self.assertIn('application/json', response['Content-Type']) domain_name = Domain.objects.get_default_domain().name self.assertEqual('diane.%s' % domain_name, parsed_result['fqdn']) self.assertNotEqual(0, len(parsed_result.get('system_id'))) [diane] = Machine.objects.filter(hostname='diane') self.assertEqual(architecture, diane.architecture)
def test_create_with_form_creates_node(self): hostname = factory.make_name("hostname") arch = make_usable_architecture(self) handler = self.make_nodes_handler( fields=['hostname', 'architecture'], form=AdminMachineWithMACAddressesForm) handler.user = factory.make_admin() json_obj = handler.create({ "hostname": hostname, "architecture": arch, "mac_addresses": [factory.make_mac_address()], }) self.expectThat({ "hostname": hostname, "architecture": arch, }, Equals(json_obj))
def test_POST_power_on_returns_machine(self): self.patch(node_module.Machine, "_start") machine = factory.make_Node( owner=self.user, interface=True, power_type="manual", architecture=make_usable_architecture(self), ) osystem = make_usable_osystem(self) distro_series = osystem["default_release"] response = self.client.post( self.get_node_uri(machine), {"op": "power_on", "distro_series": distro_series}, ) self.assertEqual(http.client.OK, response.status_code) self.assertEqual( machine.system_id, json_load_bytes(response.content)["system_id"] )
def test_POST_create_returns_machine_with_matching_power_parameters(self): mock_create_machine = self.patch(machines_module, "create_machine") hostname = factory.make_name("hostname") architecture = make_usable_architecture(self) power_type = "ipmi" power_parameters = { "power_address": factory.make_ip_address(), "power_user": factory.make_name("power-user"), "power_pass": factory.make_name("power-pass"), "power_driver": "LAN_2_0", "mac_address": "", "power_boot_type": "auto", } machine = factory.make_Machine( hostname=hostname, status=NODE_STATUS.NEW, architecture="", power_type=power_type, power_parameters=power_parameters, ) # Simulate creating the MAAS IPMI user power_parameters["power_user"] = "******" power_parameters["power_pass"] = factory.make_name("power-pass") response = self.client.post( reverse("machines_handler"), { "hostname": "maas-enlistment", "architecture": architecture, "power_type": power_type, "mac_addresses": factory.make_mac_address(), "power_parameters": json.dumps(power_parameters), }, ) self.assertEqual(http.client.OK, response.status_code) machine = reload_object(machine) self.assertEqual(hostname, machine.hostname) self.assertEqual(architecture, machine.architecture) self.assertDictContainsSubset( machine.bmc.power_parameters, power_parameters ) self.assertThat(mock_create_machine, MockNotCalled()) self.assertEqual( machine.system_id, json_load_bytes(response.content)["system_id"] )
def test_POST_new_sets_power_parameters_skip_check(self): # The api allows to skip the validation step and set arbitrary # power parameters. param = factory.make_string() response = self.client.post( reverse('machines_handler'), { 'architecture': make_usable_architecture(self), 'power_type': 'manual', 'power_parameters_param': param, 'power_parameters_skip_check': 'true', 'mac_addresses': ['AA:BB:CC:DD:EE:FF'], }) self.assertEqual(http.client.OK, response.status_code, response.content) machine = Machine.objects.get( system_id=json_load_bytes(response.content)['system_id']) self.assertEqual({'param': param}, reload_object(machine).power_parameters)
def test_POST_creates_boot_resource_with_default_filetype(self): prevent_scheduling_of_image_imports(self) self.become_admin() name = factory.make_name('name') architecture = make_usable_architecture(self) params = { 'name': name, 'architecture': architecture, 'content': (factory.make_file_upload(content=sample_binary_data)), } response = self.client.post(reverse('boot_resources_handler'), params) self.assertEqual(http.client.CREATED, response.status_code) parsed_result = json_load_bytes(response.content) resource = BootResource.objects.get(id=parsed_result['id']) resource_set = resource.sets.first() rfile = resource_set.files.first() self.assertEqual(BOOT_RESOURCE_FILE_TYPE.ROOT_TGZ, rfile.filetype)
def test_created_machine_gets_default_domain_appended(self): hostname_without_domain = factory.make_name("hostname") response = self.client.post( reverse("machines_handler"), { "hostname": hostname_without_domain, "architecture": make_usable_architecture(self), "power_type": "manual", "mac_addresses": [factory.make_mac_address()], }, ) self.assertEqual(http.client.OK, response.status_code, response.content) parsed_result = json_load_bytes(response.content) expected_hostname = "%s.%s" % ( hostname_without_domain, Domain.objects.get_default_domain().name, ) self.assertEqual(expected_hostname, parsed_result.get("fqdn"))
def test_POST_create_creates_machine(self): hostname = factory.make_name("hostname") architecture = make_usable_architecture(self) response = self.client.post( reverse("machines_handler"), { "hostname": hostname, "architecture": architecture, "power_type": "manual", "mac_addresses": ["aa:bb:cc:dd:ee:ff", "22:bb:cc:dd:ee:ff"], }, ) self.assertEqual(http.client.OK, response.status_code) node_metadata = NodeMetadata.objects.get(key="enlisting") self.assertIsNone(node_metadata) [machine] = Machine.objects.filter(hostname=hostname) self.assertEqual(architecture, machine.architecture) self.assertEqual(machine.system_id, json_load_bytes(response.content)["system_id"])
def test_AdminMachineForm_changes_power_parameters_with_skip_check(self): node = factory.make_Node() hostname = factory.make_string() power_type = factory.pick_power_type() power_parameters_field = factory.make_string() arch = make_usable_architecture(self) form = AdminMachineForm(data={ 'hostname': hostname, 'architecture': arch, 'power_type': power_type, 'power_parameters_field': power_parameters_field, 'power_parameters_skip_check': 'true', }, instance=node) form.save() self.assertEqual((hostname, power_type, { 'field': power_parameters_field }), (node.hostname, node.power_type, node.power_parameters))
def test__creates_node_with_explicit_hostname(self): self.prepare_rack_rpc() mac_addresses = [factory.make_mac_address() for _ in range(3)] architecture = make_usable_architecture(self) hostname = factory.make_hostname() node = create_node(architecture, 'manual', {}, mac_addresses, hostname=hostname) self.assertEqual((architecture, 'manual', {}, hostname), (node.architecture, node.power_type, node.power_parameters, node.hostname)) self.expectThat(node.id, Not(Is(None))) self.assertItemsEqual( mac_addresses, [nic.mac_address for nic in node.interface_set.all()])
def test_POST_non_admin_creates_machine_in_declared_state(self): # Upon non-admin enlistment, a machine goes into the New # state. Deliberate approval is required before we start # reinstalling the system, wiping its disks etc. response = self.client.post( reverse("machines_handler"), { "hostname": factory.make_string(), "architecture": make_usable_architecture(self), "mac_addresses": ["aa:bb:cc:dd:ee:ff"], }, ) self.assertEqual( http.client.OK, response.status_code, response.content ) system_id = json_load_bytes(response.content)["system_id"] self.assertEqual( NODE_STATUS.NEW, Machine.objects.get(system_id=system_id).status )
def test_POST_creates_boot_resource_with_already_existing_largefile(self): self.become_admin() largefile = factory.make_LargeFile() name = factory.make_name('name') architecture = make_usable_architecture(self) params = { 'name': name, 'architecture': architecture, 'sha256': largefile.sha256, 'size': largefile.total_size, } response = self.client.post(reverse('boot_resources_handler'), params) self.assertEqual(http.client.CREATED, response.status_code) parsed_result = json_load_bytes(response.content) resource = BootResource.objects.get(id=parsed_result['id']) resource_set = resource.sets.first() rfile = resource_set.files.first() self.assertEqual(largefile, rfile.largefile)
def test_POST_create_creates_machine(self): architecture = make_usable_architecture(self) response = self.client.post( reverse("machines_handler"), { "hostname": "diane", "architecture": architecture, "power_type": "manual", "mac_addresses": ["aa:bb:cc:dd:ee:ff", "22:bb:cc:dd:ee:ff"], }, ) self.assertEqual(http.client.OK, response.status_code) parsed_result = json_load_bytes(response.content) self.assertIn("application/json", response["Content-Type"]) domain_name = Domain.objects.get_default_domain().name self.assertEqual("diane.%s" % domain_name, parsed_result["fqdn"]) self.assertNotEqual(0, len(parsed_result.get("system_id"))) [diane] = Machine.objects.filter(hostname="diane") self.assertEqual(architecture, diane.architecture)
def test__returns_global_kernel_params_for_enlisting_node(self): # An 'enlisting' node means it looks like a node with details but we # don't know about it yet. It should still receive the global # kernel options. rack_controller = factory.make_RackController() local_ip = factory.make_ip_address() remote_ip = factory.make_ip_address() value = factory.make_string() Config.objects.set_config("kernel_opts", value) architecture = make_usable_architecture(self) arch = architecture.split('/')[0] factory.make_default_ubuntu_release_bootable(arch) mac = factory.make_mac_address(delimiter='-') observed_config = get_config(rack_controller.system_id, local_ip, remote_ip, arch=arch, subarch='generic', mac=mac) self.assertEqual(value, observed_config['extra_opts'])