+#[api(
+ input: {
+ properties: {
+ drive: {
+ schema: DRIVE_NAME_SCHEMA,
+ },
+ },
+ },
+ returns: {
+ description: "A List of medium auxiliary memory attributes.",
+ type: Array,
+ items: {
+ type: MamAttribute,
+ },
+ },
+ access: {
+ permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_AUDIT, false),
+ },
+)]
+/// Read Cartridge Memory (Medium auxiliary memory attributes)
+pub async fn cartridge_memory(drive: String) -> Result<Vec<MamAttribute>, Error> {
+ run_drive_blocking_task(
+ drive.clone(),
+ "reading cartridge memory".to_string(),
+ move |config| {
+ let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
+ let mut handle = drive_config.open()?;
+
+ handle.cartridge_memory()
+ }
+ )
+ .await
+}
+
+#[api(
+ input: {
+ properties: {
+ drive: {
+ schema: DRIVE_NAME_SCHEMA,
+ },
+ },
+ },
+ returns: {
+ type: Lp17VolumeStatistics,
+ },
+ access: {
+ permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_AUDIT, false),
+ },
+)]
+/// Read Volume Statistics (SCSI log page 17h)
+pub async fn volume_statistics(drive: String) -> Result<Lp17VolumeStatistics, Error> {
+ run_drive_blocking_task(
+ drive.clone(),
+ "reading volume statistics".to_string(),
+ move |config| {
+ let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
+ let mut handle = drive_config.open()?;
+
+ handle.volume_statistics()
+ }
+ )
+ .await
+}
+
+#[api(
+ input: {
+ properties: {
+ drive: {
+ schema: DRIVE_NAME_SCHEMA,
+ },
+ },
+ },
+ returns: {
+ type: LinuxDriveAndMediaStatus,
+ },
+ access: {
+ permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_AUDIT, false),
+ },
+)]
+/// Get drive/media status
+pub async fn status(drive: String) -> Result<LinuxDriveAndMediaStatus, Error> {
+ run_drive_blocking_task(
+ drive.clone(),
+ "reading drive status".to_string(),
+ move |config| {
+ let drive_config: LinuxTapeDrive = config.lookup("linux", &drive)?;
+
+ // Note: use open_linux_tape_device, because this also works if no medium loaded
+ let file = open_linux_tape_device(&drive_config.path)?;
+
+ let mut handle = LinuxTapeHandle::new(file);
+
+ handle.get_drive_and_media_status()
+ }
+ )
+ .await
+}
+
+#[api(
+ input: {
+ properties: {
+ drive: {
+ schema: DRIVE_NAME_SCHEMA,
+ },
+ force: {
+ description: "Force overriding existing index.",
+ type: bool,
+ optional: true,
+ },
+ verbose: {
+ description: "Verbose mode - log all found chunks.",
+ type: bool,
+ optional: true,
+ },
+ },
+ },
+ returns: {
+ schema: UPID_SCHEMA,
+ },
+ access: {
+ permission: &Permission::Privilege(&["tape", "device", "{drive}"], PRIV_TAPE_READ, false),
+ },
+)]
+/// Scan media and record content
+pub fn catalog_media(
+ drive: String,
+ force: Option<bool>,
+ verbose: Option<bool>,
+ rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Value, Error> {
+ let verbose = verbose.unwrap_or(false);
+ let force = force.unwrap_or(false);
+
+ let upid_str = run_drive_worker(
+ rpcenv,
+ drive.clone(),
+ "catalog-media",
+ Some(drive.clone()),
+ move |worker, config| {
+ let mut drive = open_drive(&config, &drive)?;
+
+ drive.rewind()?;
+
+ let media_id = match drive.read_label()? {
+ (Some(media_id), key_config) => {
+ worker.log(format!(
+ "found media label: {}",
+ serde_json::to_string_pretty(&serde_json::to_value(&media_id)?)?
+ ));
+ if key_config.is_some() {
+ worker.log(format!(
+ "encryption key config: {}",
+ serde_json::to_string_pretty(&serde_json::to_value(&key_config)?)?
+ ));
+ }
+ media_id
+ },
+ (None, _) => bail!("media is empty (no media label found)"),
+ };
+
+ let status_path = Path::new(TAPE_STATUS_DIR);
+
+ let mut inventory = Inventory::load(status_path)?;
+ inventory.store(media_id.clone(), false)?;
+
+ let pool = match media_id.media_set_label {
+ None => {
+ worker.log("media is empty");
+ MediaCatalog::destroy(status_path, &media_id.label.uuid)?;
+ return Ok(());
+ }
+ Some(ref set) => {
+ if set.uuid.as_ref() == [0u8;16] { // media is empty
+ worker.log("media is empty");
+ MediaCatalog::destroy(status_path, &media_id.label.uuid)?;
+ return Ok(());
+ }
+ let encrypt_fingerprint = set.encryption_key_fingerprint.clone()
+ .map(|fp| (fp, set.uuid.clone()));
+
+ drive.set_encryption(encrypt_fingerprint)?;
+
+ set.pool.clone()
+ }
+ };
+
+ let _lock = MediaPool::lock(status_path, &pool)?;
+
+ if MediaCatalog::exists(status_path, &media_id.label.uuid) && !force {
+ bail!("media catalog exists (please use --force to overwrite)");
+ }
+
+ restore_media(&worker, &mut drive, &media_id, None, verbose)?;
+
+ Ok(())
+ },
+ )?;
+
+ Ok(upid_str.into())
+}
+
+#[api(
+ input: {
+ properties: {
+ changer: {
+ schema: CHANGER_NAME_SCHEMA,
+ optional: true,
+ },
+ },
+ },
+ returns: {
+ description: "The list of configured drives with model information.",
+ type: Array,
+ items: {
+ type: DriveListEntry,
+ },
+ },
+ access: {
+ description: "List configured tape drives filtered by Tape.Audit privileges",
+ permission: &Permission::Anybody,
+ },
+)]
+/// List drives
+pub fn list_drives(
+ changer: Option<String>,
+ _param: Value,
+ rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<DriveListEntry>, Error> {
+ let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+ let user_info = CachedUserInfo::new()?;
+
+ let (config, _) = config::drive::config()?;
+
+ let linux_drives = linux_tape_device_list();
+
+ let drive_list: Vec<LinuxTapeDrive> = config.convert_to_typed_array("linux")?;
+
+ let mut list = Vec::new();
+
+ for drive in drive_list {
+ if changer.is_some() && drive.changer != changer {
+ continue;
+ }
+
+ let privs = user_info.lookup_privs(&auth_id, &["tape", "drive", &drive.name]);
+ if (privs & PRIV_TAPE_AUDIT) == 0 {
+ continue;
+ }
+
+ let info = lookup_device_identification(&linux_drives, &drive.path);
+ let state = get_tape_device_state(&config, &drive.name)?;
+ let entry = DriveListEntry { config: drive, info, state };
+ list.push(entry);
+ }
+
+ Ok(list)
+}
+