def test_status_erasure_failure_leaves_node_failed(self): node = factory.make_Node( interface=True, status=NODE_STATUS.DISK_ERASING ) payload = { "event_type": "finish", "result": "FAILURE", "origin": "curtin", "name": "cmd-erase", "description": "Erasing disk", "timestamp": datetime.utcnow(), } self.processMessage(node, payload) self.assertEqual( NODE_STATUS.FAILED_DISK_ERASING, reload_object(node).status ) # Check last node event. self.assertEqual( "Failed to erase disks.", Event.objects.filter(node=node).last().description, )
def test_status_commissioning_failure_leaves_node_failed(self): node = factory.make_Node( interface=True, status=NODE_STATUS.COMMISSIONING) payload = { 'event_type': 'finish', 'result': 'FAILURE', 'origin': 'curtin', 'name': 'commissioning', 'description': 'Commissioning', 'timestamp': datetime.utcnow(), } self.processMessage(node, payload) self.assertEqual( NODE_STATUS.FAILED_COMMISSIONING, reload_object(node).status) # Check last node event. self.assertItemsEqual( [ "'curtin' Commissioning", "Commissioning failed, cloud-init reported a failure (refer " "to the event log for more information)", ], [e.description for e in Event.objects.filter(node=node)])
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_sets_status_expires_when_flatlined_with_may_reboot_script(self): node, script_set = self.make_node() now = datetime.now() if self.status == NODE_STATUS.COMMISSIONING: script_type = SCRIPT_TYPE.COMMISSIONING else: script_type = SCRIPT_TYPE.TESTING script = factory.make_Script(script_type=script_type, may_reboot=True) factory.make_ScriptResult(script=script, script_set=script_set, status=SCRIPT_STATUS.RUNNING) script_set.last_ping = now - timedelta(11) script_set.save() mark_nodes_failed_after_missing_script_timeout() node = reload_object(node) self.assertEquals( now - (now - script_set.last_ping) + timedelta( minutes=NODE_FAILURE_MONITORED_STATUS_TIMEOUTS[self.status]), node.status_expires)
def test_format_formats_block_device_as_admin(self): self.become_admin() node = factory.make_Node(status=NODE_STATUS.READY) block_device = factory.make_VirtualBlockDevice(node=node) fstype = factory.pick_filesystem_type() fsuuid = "%s" % uuid.uuid4() uri = get_blockdevice_uri(block_device) response = self.client.post(uri, { "op": "format", "fstype": fstype, "uuid": fsuuid }) self.assertEqual(http.client.OK, response.status_code, response.content) parsed_device = json_load_bytes(response.content) self.assertEqual(parsed_device["filesystem"]["fstype"], fstype) self.assertEqual(parsed_device["filesystem"]["uuid"], fsuuid) self.assertIsNone(parsed_device["filesystem"]["mount_point"]) block_device = reload_object(block_device) self.assertIsNotNone(block_device.get_effective_filesystem())
def test_does_nothing_if_not_machine(self): node = factory.make_Node_with_Interface_on_Subnet( node_type=factory.pick_choice( NODE_TYPE_CHOICES, but_not=[NODE_TYPE.MACHINE] ), status=random.choice(self.reserved_statuses), power_state=POWER_STATE.ON, ) for interface in node.interface_set.all(): interface.claim_auto_ips() # Hack to get around node transition model Node.objects.filter(id=node.id).update(status=self.status) node = reload_object(node) node.power_state = POWER_STATE.OFF node.save() for ip in StaticIPAddress.objects.filter( interface__node=node, alloc_type=IPADDRESS_TYPE.AUTO ): self.assertIsNotNone(ip.ip)
def test_status_commissioning_failure_aborts_scripts(self): # Regression test for LP:1732948 user = factory.make_User() node = factory.make_Node(interface=True, status=NODE_STATUS.COMMISSIONING, owner=user, with_empty_script_sets=True) payload = { 'event_type': 'finish', 'result': 'FAILURE', 'origin': 'curtin', 'name': 'commissioning', 'description': 'Commissioning', 'timestamp': datetime.utcnow(), } self.assertEqual(user, node.owner) # Node has an owner self.processMessage(node, payload) self.assertEqual(NODE_STATUS.FAILED_COMMISSIONING, reload_object(node).status) for script_result in node.get_latest_script_results: self.assertEqual(SCRIPT_STATUS.ABORTED, script_result.status)
def update_interface(self, params): """Update the interface.""" # Only admin users can perform update. if not reload_object(self.user).is_superuser: raise HandlerPermissionError() node = self.get_object(params) interface = Interface.objects.get(node=node, id=params["interface_id"]) if node.status == NODE_STATUS.DEPLOYED: interface_form = DeployedInterfaceForm else: interface_form = InterfaceForm.get_interface_form(interface.type) form = interface_form(instance=interface, data=params) if form.is_valid(): interface = form.save() self._update_obj_tags(interface, params) else: raise ValidationError(form.errors) if 'mode' in params: self.link_subnet(params) return self.full_dehydrate(node)
def create_partition(self, params): """Create a partition.""" # Only admin users can perform delete. if not reload_object(self.user).is_superuser: raise HandlerPermissionError() node = self.get_object(params) disk_obj = BlockDevice.objects.get(id=params['block_id'], node=node) form = AddPartitionForm(disk_obj, { 'size': params['partition_size'], }) if not form.is_valid(): raise HandlerError(form.errors) else: partition = form.save() if 'fstype' in params: self.update_partition_filesystem(node, partition.id, params.get("fstype"), params.get("mount_point"), params.get("mount_options"))
def test_ensure_boot_source_definition_updates_default_source_snap(self): BootSource.objects.all().delete() self.assertTrue(ensure_boot_source_definition()) source = BootSource.objects.first() self.assertEqual( source.keyring_filename, "/usr/share/keyrings/ubuntu-cloudimage-keyring.gpg", ) self.useFixture( EnvironmentVariableFixture("SNAP", "/snap/maas/current") ) self.patch( bootsources, "DEFAULT_KEYRINGS_PATH", "/some/other/path/keyring.gpg", ) self.assertFalse(ensure_boot_source_definition()) source = reload_object(source) self.assertEqual( source.keyring_filename, "/some/other/path/keyring.gpg" )
def test_updates_subnet(self): new_name = factory.make_name("subnet") new_description = factory.make_name("description") subnet = factory.make_Subnet() new_vlan = factory.make_VLAN() new_network = factory.make_ip4_or_6_network() new_cidr = str(new_network.cidr) new_gateway_ip = factory.pick_ip_in_network(new_network) new_dns_servers = [] for _ in range(2): new_dns_servers.append( factory.pick_ip_in_network( new_network, but_not=[new_gateway_ip] + new_dns_servers ) ) form = SubnetForm( instance=subnet, data={ "name": new_name, "description": new_description, "vlan": new_vlan.id, "cidr": new_cidr, "gateway_ip": new_gateway_ip, "dns_servers": ",".join(new_dns_servers), }, ) self.assertTrue(form.is_valid(), dict(form.errors)) form.save() subnet = reload_object(subnet) self.assertThat( subnet, MatchesStructure.byEquality( name=new_name, description=new_description, vlan=new_vlan, cidr=new_cidr, gateway_ip=new_gateway_ip, dns_servers=new_dns_servers, ), )
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) node_metadata = NodeMetadata.objects.get(node=machine, key='enlisting') self.assertEqual(node_metadata.value, 'True') self.assertThat(mock_create_machine, MockNotCalled()) self.assertEqual( machine.system_id, json_load_bytes(response.content)['system_id'])
def test_update_custom_repository(self): """Updates a custom repository""" self.become_admin() # Creates a repository which is not 'default'. package_repository = factory.make_PackageRepository() new_values = { 'url': factory.make_url(scheme='http'), 'distributions': [factory.make_name("distribution%d" % i) for i in range(3)], 'components': [factory.make_name("comp%d" % i) for i in range(4)], 'arches': [ random.choice(PackageRepository.KNOWN_ARCHES), random.choice(PackageRepository.KNOWN_ARCHES), ] } response = self.client.put( self.get_package_repository_uri(package_repository), new_values) self.assertEqual(http.client.OK, response.status_code, response.content) package_repository = reload_object(package_repository) self.assertAttributes(package_repository, new_values)
def test_set_zone_works_if_rbac_pool_admin(self): rbac = self.useFixture(RBACEnabled()) user = factory.make_User() machine = factory.make_Machine() zone = factory.make_Zone() rbac.store.add_pool(machine.pool) rbac.store.allow(user.username, machine.pool, "admin-machines") rbac.store.allow(user.username, machine.pool, "view") form = BulkNodeSetZoneForm( user=user, data={ "zone": zone.name, "system_id": [machine.system_id] }, ) self.assertTrue(form.is_valid(), form._errors) done, not_actionable, not_permitted = form.save() self.assertEqual([1, 0, 0], [done, not_actionable, not_permitted]) machine = reload_object(machine) self.assertEqual(zone, machine.zone)
def test_delete_deletes_bcache(self): mock_create_audit_event = self.patch(bcache_module, "create_audit_event") self.become_admin() node = factory.make_Node(status=NODE_STATUS.READY) bcache = factory.make_FilesystemGroup( node=node, group_type=FILESYSTEM_GROUP_TYPE.BCACHE) uri = get_bcache_device_uri(bcache) response = self.client.delete(uri) self.assertEqual(http.client.NO_CONTENT, response.status_code, response.content) self.assertIsNone(reload_object(bcache)) self.assertThat( mock_create_audit_event, MockCalledOnceWith( EVENT_TYPES.NODE, ENDPOINT.API, ANY, node.system_id, "Deleted bcache.", ), )
def test_set_versions_info_change_type(self): controller = factory.make_RackController() deb_versions = DebVersionsInfo( current={ "version": "3.0.0", "origin": "http://archive.ubuntu.com/ focal/main", }, update={ "version": "3.0.1", "origin": "http://mymirror.example.com/ focal/main", }, ) snap_versions = SnapVersionsInfo( current={ "revision": "1234", "version": "3.1.0", }, channel={ "track": "3.0", "risk": "stable" }, update={ "revision": "5678", "version": "3.1.1", }, cohort="abc123", ) ControllerInfo.objects.set_versions_info(controller, deb_versions) ControllerInfo.objects.set_versions_info(controller, snap_versions) controller_info = reload_object(controller).controllerinfo # all fields are updated self.assertEqual(controller_info.install_type, CONTROLLER_INSTALL_TYPE.SNAP) self.assertEqual(controller_info.version, "3.1.0") self.assertEqual(controller_info.update_version, "3.1.1") self.assertEqual(controller_info.update_origin, "3.0/stable") self.assertEqual(controller_info.snap_revision, "1234") self.assertEqual(controller_info.snap_update_revision, "5678") self.assertEqual(controller_info.snap_cohort, "abc123")
def test_update_other_as_admin(self): admin_user = factory.make_admin() handler = UserHandler(admin_user, {}, None) user = factory.make_User() params = make_user_attribute_params(user) params.update({ "id": user.id, "last_name": factory.make_name("Newname"), "email": "new-{}@example.com".format(factory.make_string()), "is_superuser": True, "username": factory.make_name("newname"), }) handler.update(params) self.assertAttributes(reload_object(user), subset_dict(params, user_attributes))
def test__update_address__updates_single_address(self): user = factory.make_admin() handler = DomainHandler(user, {}, None) domain = factory.make_Domain() name = factory.make_hostname() resource = factory.make_DNSResource( domain=domain, name=name, ip_addresses=["127.0.0.1", "127.0.0.2"] ) handler.update_address_record( { "domain": domain.id, "dnsresource_id": resource.id, "previous_name": resource.name, "previous_rrdata": "127.0.0.1", "name": name, "ip_addresses": ["127.0.0.3"], } ) resource = reload_object(resource) self.assertThat( resource.get_addresses(), Equals(["127.0.0.2", "127.0.0.3"]) )
def test_configure_dhcp_gateway_inside_range_raises(self): user = factory.make_admin() handler = VLANHandler(user, {}, None) vlan = factory.make_VLAN() rack = factory.make_RackController() factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=rack, vlan=vlan) subnet = factory.make_Subnet(vlan=vlan, cidr="10.0.0.0/24", gateway_ip="") self.assertThat(subnet.get_dynamic_ranges().count(), Equals(0)) with ExpectedException(ValueError): handler.configure_dhcp({ "id": vlan.id, "controllers": [rack.system_id], "extra": { "subnet": subnet.id, "gateway": "10.0.0.1", "start": "10.0.0.1", "end": "10.0.0.99", }, }) vlan = reload_object(vlan)
def test_create_or_update_for_raid_updates_block_device(self): # This will create the filesystem group and a virtual block device. filesystem_group = factory.make_FilesystemGroup( group_type=FILESYSTEM_GROUP_TYPE.RAID_0) # Update the size of all block devices to change the size of the # filesystem group. # The hard-coded size is due to this random ValidationError: # {'size': ['Ensure this value is greater than or equal to 4194304.']} new_size = random.randint(4194304, 1000 * 1000 * 1000) for filesystem in filesystem_group.filesystems.all(): filesystem.block_device.size = new_size filesystem.block_device.save() # This also tests the post_save signal on `BlockDevice`. Because the # filesystem_group.save() does not need to be called here. The # post_save performs that operation. # The new size of the RAID-0 array should be the size of the smallest # filesystem (in this case, they are all the same) times the number of # filesystems in it. array_size = new_size * filesystem_group.filesystems.count() self.assertEqual(array_size, reload_object(filesystem_group.virtual_device).size)
def test_configure_dhcp_gateway_fe80_allowed(self): user = factory.make_admin() handler = VLANHandler(user, {}, None) vlan = factory.make_VLAN() rack = factory.make_RackController() factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=rack, vlan=vlan) subnet = factory.make_Subnet(vlan=vlan, cidr="2001:db8::/64", gateway_ip="") self.assertThat(subnet.get_dynamic_ranges().count(), Equals(0)) handler.configure_dhcp({ "id": vlan.id, "controllers": [rack.system_id], "extra": { "subnet": subnet.id, "gateway": "fe80::1", "start": "2001:db8:0:0:1::", "end": "2001:db8:0:0:1:ffff:ffff:ffff", }, }) subnet = reload_object(subnet) self.assertEqual(subnet.gateway_ip, "fe80::1")
def test_unmount_unmounts_filesystem_as_user(self): node = factory.make_Node(status=NODE_STATUS.ALLOCATED, owner=self.user) partition = self.make_partition(node) filesystem = factory.make_Filesystem( partition=partition, mount_point="/mnt", acquired=True ) uri = get_partition_uri(partition) response = self.client.post(uri, {"op": "unmount"}) content = response.content.decode(settings.DEFAULT_CHARSET) self.assertEqual(http.client.OK, response.status_code, content) self.assertThat( json.loads(content)["filesystem"], ContainsDict({"mount_point": Is(None), "mount_options": Is(None)}), ) self.assertThat( reload_object(filesystem), MatchesStructure( mount_point=Is(None), mount_options=Is(None), is_mounted=Is(False), ), )
def test__update_resource(self): user = factory.make_admin() handler = DomainHandler(user, {}, None) domain = factory.make_Domain() resource = factory.make_DNSResource(domain=domain) new_name = factory.make_hostname() new_ttl = randint(1, 3600) handler.update_dnsresource( { "domain": domain.id, "dnsresource_id": resource.id, "name": new_name, "address_ttl": new_ttl, "ip_addresses": ["127.0.0.1"], } ) resource = reload_object(resource) self.assertThat(resource.address_ttl, Equals(new_ttl)) self.assertThat(resource.name, Equals(new_name)) self.assertThat( list(resource.ip_addresses.all())[0].ip, Equals("127.0.0.1") )
def test_update_other_as_admin(self): admin_user = factory.make_admin() handler = UserHandler(admin_user, {}, None) user = factory.make_User() params = make_user_attribute_params(user) params.update({ 'id': user.id, 'last_name': factory.make_name('Newname'), 'email': 'new-{}@example.com'.format(factory.make_string()), 'is_superuser': True, 'username': factory.make_name('newname'), }) handler.update(params) self.assertAttributes(reload_object(user), subset_dict(params, user_attributes))
def test_store_result_logs_event_upon_hook_failure(self): script_set = factory.make_ScriptSet( result_type=RESULT_TYPE.COMMISSIONING) script_result = factory.make_ScriptResult(script_set=script_set, status=SCRIPT_STATUS.RUNNING) def _raise(): raise Exception() scriptresult_module.NODE_INFO_SCRIPTS[script_result.name] = { "hook": _raise } self.addCleanup(scriptresult_module.NODE_INFO_SCRIPTS.pop, script_result.name) script_result.store_result(0, stdout=b"") expected_event = Event.objects.first() self.assertThat( expected_event.description, DocTestMatches("...failed during post-processing."), ) self.assertEqual( reload_object(script_result).status, SCRIPT_STATUS.FAILED)
def test_configure_dhcp_ignores_undefined_subnet(self): user = factory.make_admin() handler = VLANHandler(user, {}, None) vlan = factory.make_VLAN() rack = factory.make_RackController() factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=rack, vlan=vlan) factory.make_ipv4_Subnet_with_IPRanges(vlan=vlan) handler.configure_dhcp( { "id": vlan.id, "controllers": [rack.system_id], "extra": { "subnet": None, "gateway": "", "start": "", "end": "", }, } ) vlan = reload_object(vlan) self.assertThat(vlan.dhcp_on, Equals(True)) self.assertThat(vlan.primary_rack, Equals(rack))
def test_update_virtual_block_device_as_admin(self): """Check update block device with a virtual one. PUT /api/2.0/nodes/{system_id}/blockdevice/{id} """ self.become_admin() node = factory.make_Node(status=NODE_STATUS.READY, owner=self.user) filesystem_group = factory.make_FilesystemGroup( node=node, group_type=factory.pick_enum(FILESYSTEM_GROUP_TYPE, but_not=FILESYSTEM_GROUP_TYPE.LVM_VG), ) block_device = filesystem_group.virtual_device uri = get_blockdevice_uri(block_device) name = factory.make_name("newname") response = self.client.put(uri, {"name": name}) block_device = reload_object(block_device) self.assertEqual(http.client.OK, response.status_code, response.content) parsed_device = json_load_bytes(response.content) self.assertEqual(block_device.id, parsed_device["id"]) self.assertEqual(name, parsed_device["name"])
def test_sets_status_expires_when_flatlined_with_may_reboot_script(self): node, script_set = self.make_node() current_time = now() if self.status == NODE_STATUS.COMMISSIONING: script_type = SCRIPT_TYPE.COMMISSIONING else: script_type = SCRIPT_TYPE.TESTING script = factory.make_Script(script_type=script_type, may_reboot=True) factory.make_ScriptResult(script=script, script_set=script_set, status=SCRIPT_STATUS.RUNNING) script_set.last_ping = current_time - timedelta(11) script_set.save() mark_nodes_failed_after_missing_script_timeout(current_time, 20) node = reload_object(node) self.assertEquals( current_time - (current_time - script_set.last_ping) + timedelta(minutes=get_node_timeout(self.status, 20)), node.status_expires, )
def test__marks_failed_if_save_raises(self): mock_pod_form = Mock() self.mock_PodForm.return_value = mock_pod_form mock_pod_form.errors = {} mock_pod_form.is_valid = Mock() mock_pod_form.is_valid.return_value = True mock_pod_form.save = Mock() mock_pod_form.save.side_effect = ValueError node = factory.make_Node_with_Interface_on_Subnet( status=NODE_STATUS.DEPLOYING, agent_name="maas-kvm-pod", install_kvm=True) factory.make_StaticIPAddress(interface=node.boot_interface) meta = NodeMetadata.objects.create(node=node, key="virsh_password", value="xyz123") _create_pod_for_deployment(node) meta = reload_object(meta) self.assertThat(meta, Is(None)) self.assertThat(node.status, Equals(NODE_STATUS.FAILED_DEPLOYMENT)) self.assertThat(node.error_description, DocTestMatches(POD_CREATION_ERROR))
def test_update(self): self.become_admin() authoritative = factory.pick_bool() domain = factory.make_Domain(authoritative=authoritative) new_name = factory.make_name("domain") new_ttl = random.randint(10, 1000) new_auth = not authoritative uri = get_domain_uri(domain) response = self.client.put( uri, {"name": new_name, "authoritative": new_auth, "ttl": new_ttl} ) self.assertEqual( http.client.OK, response.status_code, response.content ) ret = json.loads(response.content.decode(settings.DEFAULT_CHARSET)) domain = reload_object(domain) self.assertEqual(new_name, ret["name"]) self.assertEqual(new_name, domain.name) self.assertEqual(new_ttl, ret["ttl"]) self.assertEqual(new_ttl, domain.ttl) self.assertEqual(new_auth, ret["authoritative"]) self.assertEqual(new_auth, domain.authoritative)