From 9a01d4e4469adc2d4b6b95145da8fa9ba778cae0 Mon Sep 17 00:00:00 2001 From: wllenyj Date: Wed, 18 May 2022 15:42:28 +0800 Subject: [PATCH] dragonball: add more unit test for virtio-blk device. Added more unit tests for virtio-blk device. Fixes: #4899 Signed-off-by: wllenyj --- .../src/device_manager/blk_dev_mgr.rs | 584 ++++++++++++++++++ 1 file changed, 584 insertions(+) diff --git a/src/dragonball/src/device_manager/blk_dev_mgr.rs b/src/dragonball/src/device_manager/blk_dev_mgr.rs index 0493a818a4..66890d7f9c 100644 --- a/src/dragonball/src/device_manager/blk_dev_mgr.rs +++ b/src/dragonball/src/device_manager/blk_dev_mgr.rs @@ -776,3 +776,587 @@ impl Default for BlockDeviceMgr { } } } + +#[cfg(test)] +mod tests { + use test_utils::skip_if_not_root; + use vmm_sys_util::tempfile::TempFile; + + use super::*; + use crate::test_utils::tests::create_vm_for_test; + + #[test] + fn test_block_device_type() { + let dev_type = BlockDeviceType::get_type("spool:/device1"); + assert_eq!(dev_type, BlockDeviceType::Spool); + let dev_type = BlockDeviceType::get_type("/device1"); + assert_eq!(dev_type, BlockDeviceType::RawBlock); + } + + #[test] + fn test_create_block_devices_configs() { + let mgr = BlockDeviceMgr::default(); + assert!(!mgr.has_root_block_device()); + assert!(!mgr.has_part_uuid_root()); + assert!(!mgr.is_read_only_root()); + assert_eq!(mgr.get_index_of_drive_id(""), None); + assert_eq!(mgr.info_list.len(), 0); + } + + #[test] + fn test_add_non_root_block_device() { + skip_if_not_root!(); + let dummy_file = TempFile::new().unwrap(); + let dummy_path = dummy_file.as_path().to_owned(); + let dummy_id = String::from("1"); + let dummy_block_device = BlockDeviceConfigInfo { + path_on_host: dummy_path.clone(), + device_type: BlockDeviceType::RawBlock, + is_root_device: false, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: dummy_id.clone(), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let mut vm = crate::vm::tests::create_vm_instance(); + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + assert!(BlockDeviceMgr::insert_device( + vm.device_manager_mut(), + ctx, + dummy_block_device.clone(), + ) + .is_ok()); + + assert_eq!(vm.device_manager().block_manager.info_list.len(), 1); + assert!(!vm.device_manager().block_manager.has_root_block_device()); + assert!(!vm.device_manager().block_manager.has_part_uuid_root()); + assert!(!vm.device_manager().block_manager.is_read_only_root()); + assert_eq!(vm.device_manager().block_manager.info_list.len(), 1); + assert_eq!( + vm.device_manager().block_manager.info_list[0] + .config + .device_type(), + BlockDeviceType::RawBlock + ); + assert_eq!( + vm.device_manager().block_manager.info_list[0] + .config + .queue_sizes(), + [128u16] + ); + + let dev_config = vm.device_manager().block_manager.iter().next().unwrap(); + assert_eq!(dev_config.config, dummy_block_device); + assert!(vm + .device_manager() + .block_manager + .get_index_of_drive_path(&dummy_path) + .is_some()); + assert!(vm + .device_manager() + .block_manager + .get_index_of_drive_id(&dummy_id) + .is_some()); + } + + #[test] + fn test_update_blk_device_ratelimiters() { + skip_if_not_root!(); + //Init vm for test. + let mut vm = create_vm_for_test(); + let device_op_ctx = DeviceOpContext::new( + Some(vm.epoll_manager().clone()), + vm.device_manager(), + Some(vm.vm_as().unwrap().clone()), + None, + false, + ); + + let dummy_file = TempFile::new().unwrap(); + let dummy_path = dummy_file.as_path().to_owned(); + + let dummy_block_device = BlockDeviceConfigInfo { + path_on_host: dummy_path, + device_type: BlockDeviceType::RawBlock, + is_root_device: true, + part_uuid: None, + is_read_only: true, + is_direct: false, + no_drop: false, + drive_id: String::from("1"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + BlockDeviceMgr::insert_device(vm.device_manager_mut(), device_op_ctx, dummy_block_device) + .unwrap(); + + let cfg = BlockDeviceConfigUpdateInfo { + drive_id: String::from("1"), + rate_limiter: None, + }; + + let mut device_op_ctx = DeviceOpContext::new( + Some(vm.epoll_manager().clone()), + vm.device_manager(), + Some(vm.vm_as().unwrap().clone()), + None, + false, + ); + + vm.device_manager_mut() + .block_manager + .attach_devices(&mut device_op_ctx) + .unwrap(); + assert_eq!(vm.device_manager().block_manager.info_list.len(), 1); + + //Patch while the epoll handler is invalid. + let expected_error = "could not send patch message to the block epoll handler".to_string(); + + assert_eq!( + BlockDeviceMgr::update_device_ratelimiters(vm.device_manager_mut(), cfg) + .unwrap_err() + .to_string(), + expected_error + ); + + //Invalid drive id + let cfg2 = BlockDeviceConfigUpdateInfo { + drive_id: String::from("2"), + rate_limiter: None, + }; + + let expected_error = format!("invalid block device id '{0}'", cfg2.drive_id); + + assert_eq!( + BlockDeviceMgr::update_device_ratelimiters(vm.device_manager_mut(), cfg2) + .unwrap_err() + .to_string(), + expected_error + ); + } + + #[test] + fn test_add_one_root_block_device() { + skip_if_not_root!(); + let dummy_file = TempFile::new().unwrap(); + let dummy_path = dummy_file.as_path().to_owned(); + let dummy_block_device = BlockDeviceConfigInfo { + path_on_host: dummy_path, + device_type: BlockDeviceType::RawBlock, + is_root_device: true, + part_uuid: None, + is_read_only: true, + is_direct: false, + no_drop: false, + drive_id: String::from("1"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let mut vm = crate::vm::tests::create_vm_instance(); + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + assert!(BlockDeviceMgr::insert_device( + vm.device_manager_mut(), + ctx, + dummy_block_device.clone(), + ) + .is_ok()); + + assert_eq!(vm.device_manager().block_manager.info_list.len(), 1); + assert!(vm.device_manager().block_manager.has_root_block); + assert!(!vm.device_manager().block_manager.has_part_uuid_root); + assert!(vm.device_manager().block_manager.read_only_root); + assert_eq!(vm.device_manager().block_manager.info_list.len(), 1); + + let dev_config = vm.device_manager().block_manager.iter().next().unwrap(); + assert_eq!(dev_config.config, dummy_block_device); + assert!(vm.device_manager().block_manager.is_read_only_root()); + } + + #[test] + fn test_add_two_root_block_devices_configs() { + skip_if_not_root!(); + let dummy_file_1 = TempFile::new().unwrap(); + let dummy_path_1 = dummy_file_1.as_path().to_owned(); + let root_block_device_1 = BlockDeviceConfigInfo { + path_on_host: dummy_path_1, + device_type: BlockDeviceType::RawBlock, + is_root_device: true, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("1"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let dummy_file_2 = TempFile::new().unwrap(); + let dummy_path_2 = dummy_file_2.as_path().to_owned(); + let root_block_device_2 = BlockDeviceConfigInfo { + path_on_host: dummy_path_2, + device_type: BlockDeviceType::RawBlock, + is_root_device: true, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("2"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let mut vm = crate::vm::tests::create_vm_instance(); + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, root_block_device_1).unwrap(); + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + assert!( + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, root_block_device_2) + .is_err() + ); + } + + #[test] + // Test BlockDevicesConfigs::add when you first add the root device and then the other devices. + fn test_add_root_block_device_first() { + skip_if_not_root!(); + let dummy_file_1 = TempFile::new().unwrap(); + let dummy_path_1 = dummy_file_1.as_path().to_owned(); + let root_block_device = BlockDeviceConfigInfo { + path_on_host: dummy_path_1, + device_type: BlockDeviceType::RawBlock, + is_root_device: true, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("1"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let dummy_file_2 = TempFile::new().unwrap(); + let dummy_path_2 = dummy_file_2.as_path().to_owned(); + let dummy_block_device_2 = BlockDeviceConfigInfo { + path_on_host: dummy_path_2, + device_type: BlockDeviceType::RawBlock, + is_root_device: false, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("2"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let dummy_file_3 = TempFile::new().unwrap(); + let dummy_path_3 = dummy_file_3.as_path().to_owned(); + let dummy_block_device_3 = BlockDeviceConfigInfo { + path_on_host: dummy_path_3, + device_type: BlockDeviceType::RawBlock, + is_root_device: false, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("3"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let mut vm = crate::vm::tests::create_vm_instance(); + vm.device_manager_mut() + .block_manager + .create(root_block_device.clone()) + .unwrap(); + vm.device_manager_mut() + .block_manager + .create(dummy_block_device_2.clone()) + .unwrap(); + vm.device_manager_mut() + .block_manager + .create(dummy_block_device_3.clone()) + .unwrap(); + + assert!(vm.device_manager().block_manager.has_root_block_device(),); + assert!(!vm.device_manager().block_manager.has_part_uuid_root()); + assert_eq!(vm.device_manager().block_manager.info_list.len(), 3); + + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, root_block_device).unwrap(); + + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, dummy_block_device_2).unwrap(); + + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, dummy_block_device_3).unwrap(); + } + + #[test] + // Test BlockDevicesConfigs::add when you add other devices first and then the root device. + fn test_root_block_device_add_last() { + skip_if_not_root!(); + let dummy_file_1 = TempFile::new().unwrap(); + let dummy_path_1 = dummy_file_1.as_path().to_owned(); + let root_block_device = BlockDeviceConfigInfo { + path_on_host: dummy_path_1, + device_type: BlockDeviceType::RawBlock, + is_root_device: true, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("1"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let dummy_file_2 = TempFile::new().unwrap(); + let dummy_path_2 = dummy_file_2.as_path().to_owned(); + let dummy_block_device_2 = BlockDeviceConfigInfo { + path_on_host: dummy_path_2, + device_type: BlockDeviceType::RawBlock, + is_root_device: false, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("2"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let dummy_file_3 = TempFile::new().unwrap(); + let dummy_path_3 = dummy_file_3.as_path().to_owned(); + let dummy_block_device_3 = BlockDeviceConfigInfo { + path_on_host: dummy_path_3, + device_type: BlockDeviceType::RawBlock, + is_root_device: false, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("3"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let mut vm = crate::vm::tests::create_vm_instance(); + + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, dummy_block_device_2.clone()) + .unwrap(); + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, dummy_block_device_3.clone()) + .unwrap(); + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, root_block_device.clone()) + .unwrap(); + + assert!(vm.device_manager().block_manager.has_root_block_device(),); + assert!(!vm.device_manager().block_manager.has_part_uuid_root()); + assert_eq!(vm.device_manager().block_manager.info_list.len(), 3); + + let mut block_dev_iter = vm.device_manager().block_manager.iter(); + // The root device should be first in the list no matter of the order in + // which the devices were added. + assert_eq!( + block_dev_iter.next().unwrap().config.drive_id, + root_block_device.drive_id + ); + assert_eq!( + block_dev_iter.next().unwrap().config.drive_id, + dummy_block_device_2.drive_id + ); + assert_eq!( + block_dev_iter.next().unwrap().config.drive_id, + dummy_block_device_3.drive_id + ); + } + + #[test] + fn test_block_device_update() { + skip_if_not_root!(); + let dummy_file_1 = TempFile::new().unwrap(); + let dummy_path_1 = dummy_file_1.as_path().to_owned(); + let root_block_device = BlockDeviceConfigInfo { + path_on_host: dummy_path_1.clone(), + device_type: BlockDeviceType::RawBlock, + is_root_device: true, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("1"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let dummy_file_2 = TempFile::new().unwrap(); + let dummy_path_2 = dummy_file_2.as_path().to_owned(); + let mut dummy_block_device_2 = BlockDeviceConfigInfo { + path_on_host: dummy_path_2.clone(), + device_type: BlockDeviceType::RawBlock, + is_root_device: false, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("2"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + + let mut vm = crate::vm::tests::create_vm_instance(); + + // Add 2 block devices. + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, root_block_device).unwrap(); + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, dummy_block_device_2.clone()) + .unwrap(); + + // Get index zero. + assert_eq!( + vm.device_manager() + .block_manager + .get_index_of_drive_id(&String::from("1")) + .unwrap(), + 0 + ); + + // Get None. + assert!(vm + .device_manager() + .block_manager + .get_index_of_drive_id(&String::from("foo")) + .is_none()); + + // Test several update cases using dummy_block_device_2. + // Validate `dummy_block_device_2` is already in the list + assert!(vm + .device_manager() + .block_manager + .get_index_of_drive_id(&dummy_block_device_2.drive_id) + .is_some()); + // Update OK. + dummy_block_device_2.is_read_only = true; + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, dummy_block_device_2.clone()) + .unwrap(); + + let index = vm + .device_manager() + .block_manager + .get_index_of_drive_id(&dummy_block_device_2.drive_id) + .unwrap(); + // Validate update was successful. + assert!( + vm.device_manager().block_manager.info_list[index] + .config + .is_read_only + ); + + // Update with invalid path. + let dummy_filename_3 = String::from("test_update_3"); + let dummy_path_3 = PathBuf::from(dummy_filename_3); + dummy_block_device_2.path_on_host = dummy_path_3; + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + assert!(BlockDeviceMgr::insert_device( + vm.device_manager_mut(), + ctx, + dummy_block_device_2.clone(), + ) + .is_err()); + + // Update with 2 root block devices. + dummy_block_device_2.path_on_host = dummy_path_2.clone(); + dummy_block_device_2.is_root_device = true; + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + assert!( + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, dummy_block_device_2,) + .is_err(), + ); + + // Switch roots and add a PARTUUID for the new one. + let root_block_device_old = BlockDeviceConfigInfo { + path_on_host: dummy_path_1, + device_type: BlockDeviceType::RawBlock, + is_root_device: false, + part_uuid: None, + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("1"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + let root_block_device_new = BlockDeviceConfigInfo { + path_on_host: dummy_path_2, + device_type: BlockDeviceType::RawBlock, + is_root_device: true, + part_uuid: Some("0eaa91a0-01".to_string()), + is_read_only: false, + is_direct: false, + no_drop: false, + drive_id: String::from("2"), + rate_limiter: None, + num_queues: BlockDeviceConfigInfo::default_num_queues(), + queue_size: 128, + use_shared_irq: None, + use_generic_irq: None, + }; + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, root_block_device_old).unwrap(); + let ctx = DeviceOpContext::create_boot_ctx(&vm, None); + BlockDeviceMgr::insert_device(vm.device_manager_mut(), ctx, root_block_device_new).unwrap(); + assert!(vm.device_manager().block_manager.has_part_uuid_root); + } +}