From b07ab9b22e49e0aaccce97e45f97cb6cea1ff821 Mon Sep 17 00:00:00 2001
From: Cristiano Urban <cristiano.urban@inaf.it>
Date: Fri, 6 Dec 2024 11:28:38 +0100
Subject: [PATCH] IBM Spectrum Protect commands integration, removed
 'getTaskList()' and 'getPoolList()' methods + cleanup.

Signed-off-by: Cristiano Urban <cristiano.urban@inaf.it>
---
 transfer_service/tape_client.py | 133 ++++++++++----------------------
 1 file changed, 39 insertions(+), 94 deletions(-)

diff --git a/transfer_service/tape_client.py b/transfer_service/tape_client.py
index 2c46e75..eeb70cb 100644
--- a/transfer_service/tape_client.py
+++ b/transfer_service/tape_client.py
@@ -16,13 +16,18 @@ from config import Config
 from exceptions import ScpInvalidFileException
 from exceptions import TapeClientException
 from redis_log_handler import RedisLogHandler
-from tape_pool import TapePool
-from tape_task import TapeTask
 
 
 class TapeClient(object):
-    # 'eeadm' command location on the tape library frontend
-    EEADM = "/opt/ibm/ltfsee/bin/eeadm"
+    # 'dsmdf' command location on the tape library frontend
+    DSMDF = "/usr/bin/dsmdf"
+
+    # 'dsmrecall' command location on the tape library frontend
+    DSMRECALL = "/usr/bin/dsmrecall"
+
+    # 'dsmmigrate' command location on the tape library frontend
+    DSMMIGRATE = "/usr/bin/dsmmigrate"
+
     # destination for the files containing the lists of files to recall or migrate
     VOSPACE_WD = "/tmp/vospace"
 
@@ -35,8 +40,7 @@ class TapeClient(object):
         self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
         self.keyFile = keyFile
         self.scp = None
-        self.taskList = []
-        self.poolList = []
+        self.HSMFilesystemList = []
 
     def connect(self):
         """Connects to the tape library frontend."""
@@ -51,81 +55,6 @@ class TapeClient(object):
             self.logger.exception("Unable to establish SSH connection with tape library frontend.")
             raise
 
-    def getPoolList(self):
-        """Returns a list of 'TapePool' objects."""
-        cmd = f"{self.EEADM} pool list --json"
-        try:
-            stdin, stdout, stderr = self.client.exec_command(cmd)
-        except Exception:
-            self.logger.exception(f"Unable to execute command: '{cmd}'")
-            raise
-        else:
-            exitCode = stdout.channel.recv_exit_status()
-            if not exitCode:
-                result = json.loads(stdout.readlines()[0].rstrip('\n'))
-                for el in result["payload"]:
-                    pool = TapePool()
-                    pool.id = el["id"]
-                    pool.name = el["name"]
-                    pool.mediaRestriction = el["media_restriction"]
-                    pool.capacity = el["capacity"]
-                    pool.usedSpace = el["used_space"]
-                    pool.freeSpace = el["free_space"]
-                    pool.reclaimableSpace = el["reclaimable_space"]
-                    pool.activeSpace = el["active_space"]
-                    pool.nonAppendableSpace = el["non_appendable_space"]
-                    pool.numOfTapes = el["num_of_tapes"]
-                    pool.formatClass = el["format_class"]
-                    pool.libraryName = el["library_name"]
-                    pool.libraryId = el["library_id"]
-                    pool.nodeGroupName = el["nodegroup_name"]
-                    pool.deviceType = el["device_type"]
-                    pool.worm = el["worm"]
-                    pool.fillPolicy = el["fill_policy"]
-                    pool.owner = el["owner"]
-                    pool.mountLimit = el["mount_limit"]
-                    pool.lowSpaceWarningEnable = el["low_space_warning_enable"]
-                    pool.lowSpaceWarningThreshold = el["low_space_warning_threshold"]
-                    pool.noSpaceWarningEnable = el["no_space_warning_enable"]
-                    pool.mode = el["mode"]
-                    self.poolList.append(pool)
-                return self.poolList.copy()
-            else:
-                raise TapeClientException(cmd, exitCode, stderr)
-
-    def getTaskList(self):
-        """Returns the whole task list."""
-        cmd = f"{self.EEADM} task list --json"
-        try:
-            stdin, stdout, stderr = self.client.exec_command(cmd)
-        except Exception:
-            self.logger.exception(f"Unable to execute command: '{cmd}'")
-            raise
-        else:
-            exitCode = stdout.channel.recv_exit_status()
-            if not exitCode:
-                result = json.loads(stdout.readlines()[0].rstrip('\n'))
-                for el in result["payload"]:
-                    task = TapeTask()
-                    task.inUseTapes = el["inuse_tapes"]
-                    task.inUsePools = el["inuse_pools"]
-                    task.inUseNodeGroups = el["inuse_node_groups"]
-                    task.inUseDrives = el["inuse_drives"]
-                    task.cmdParam = el["cmd_param"]
-                    task.result = el["result"]
-                    task.status = el["status"]
-                    task.completedTime = el["completed_time"]
-                    task.startedTime = el["started_time"]
-                    task.createdTime = el["created_time"]
-                    task.setInUseLibs = el["inuse_libs"]
-                    task.type = el["type"]
-                    task.taskId = el["task_id"]
-                    task.id = el["id"]
-                    self.taskList.append(task)
-                return self.taskList.copy()
-            else:
-                raise TapeClientException(cmd, exitCode, stderr)
-
     def copy(self, srcPath, destPath):
         """Copies files/dirs recursively by passing their absolute paths."""
         try:
@@ -145,14 +74,31 @@ class TapeClient(object):
         finally:
             self.scp.close()
 
-    def migrate(self, fileList, tapePool, jobId):
+    def getHSMFilesystemList(self):
+        """Returns a list containing all the available HSM filesystems."""
+        cmd = f"{self.DSMDF} -detail | grep \"HSM Filesystem\" | awk '{ print $3 }'"
+        try:
+            stdin, stdout, stderr = self.client.exec_command(cmd)
+        except Exception:
+            self.logger.exception(f"Unable to execute command: '{cmd}'")
+            raise
+        else:
+            exitCode = stdout.channel.recv_exit_status()
+            if not exitCode:
+                result = stdout.readlines()[0].rstrip('\n')
+                self.HSMFilesystemList = result.splitlines()
+                return self.HSMFilesystemList.copy()
+            else:
+                raise TapeClientException(cmd, exitCode, stderr)
+
+    def migrate(self, fileList, tapeHSMFilesystem, jobId):
         """
         Migrates to tape all files whose absolute path is
         contained in 'fileList'.
-        A tape pool and a VOSpace jobId are also required
-        as parameters.
+        A HSM filesystem and a VOSpace jobId are also
+        required as parameters.
         """
-        self.logger.info(f"Starting MIGRATE operation (tape pool = '{tapePool}')...")
+        self.logger.info(f"Starting MIGRATE operation (tape HSM filesystem = '{tapeHSMFilesystem}')...")
         migrateFileList = f"vos_migrate-{jobId}.lst"
         try:
             fp = open(migrateFileList, "a")
@@ -164,7 +110,7 @@ class TapeClient(object):
             fp.close()
             self.copy(f"./{migrateFileList}", f"{self.VOSPACE_WD}/{migrateFileList}")
             os.remove(f"./{migrateFileList}")
-            cmd = f"{self.EEADM} migrate {self.VOSPACE_WD}/{migrateFileList} -p {tapePool} > /dev/null 2>&1"
+            cmd = f"{self.DSMMIGRATE} -filelist={self.VOSPACE_WD}/{migrateFileList} > /dev/null 2>&1"
             try:
                 stdin, stdout, stderr = self.client.exec_command(cmd)
             except Exception:
@@ -172,7 +118,7 @@ class TapeClient(object):
                 raise
             else:
                 exitCode = stdout.channel.recv_exit_status()
-                if not exitCode:
+                if exitCode == 0 or exitCode == 4:
                     self.logger.info("MIGRATE operation COMPLETED.")
                 else:
                     self.logger.error("MIGRATE operation FAILED.")
@@ -197,7 +143,7 @@ class TapeClient(object):
             fp.close()
             self.copy(f"./{recallFileList}", f"{self.VOSPACE_WD}/{recallFileList}")
             os.remove(f"./{recallFileList}")
-            cmd = f"{self.EEADM} recall {self.VOSPACE_WD}/{recallFileList} > /dev/null 2>&1"
+            cmd = f"{self.DSMRECALL} -filelist={self.VOSPACE_WD}/{recallFileList} > /dev/null 2>&1"
             try:
                 stdin, stdout, stderr = self.client.exec_command(cmd)
             except Exception:
@@ -205,7 +151,7 @@ class TapeClient(object):
                 raise
             else:
                 exitCode = stdout.channel.recv_exit_status()
-                if not exitCode:
+                if exitCode == 0 or exitCode == 4:
                     self.logger.info("RECALL operation COMPLETED.")
                 else:
                     self.logger.error("RECALL operation FAILED.")
@@ -218,7 +164,8 @@ class TapeClient(object):
         the 'dirName' directory.
         """
         self.logger.info("Starting RECALL_CHECKSUM operation...")
-        cmd = f"find $(dirname {dirName}) -type f \( -iname \"*-md5sum.txt\" \) | {self.EEADM} recall > /dev/null 2>&1"
+        checksumFileList = "vos_recall_checksum_files-{jobId}.lst"
+        cmd = f"find $(dirname {dirName}) -type f \( -iname \"*-md5sum.txt\" \) > {self.VOSPACE_WD}/{checksumFileList} && {self.DSMRECALL} -filelist={self.VOSPACE_WD}/{checksumFileList} > /dev/null 2>&1"
         try:
             stdin, stdout, stderr = self.client.exec_command(cmd)
         except Exception:
@@ -226,7 +173,7 @@ class TapeClient(object):
             raise
         else:
             exitCode = stdout.channel.recv_exit_status()
-            if not exitCode:
+            if exitCode == 0 or exitCode == 4:
                 self.logger.info("RECALL_CHECKSUM operation COMPLETED.")
             else:
                 self.logger.error("RECALL_CHECKSUM operation FAILED.")
@@ -235,8 +182,7 @@ class TapeClient(object):
 
     def disconnect(self):
         """Performs a cleanup and closes the connection."""
-        self.taskList.clear()
-        self.poolList.clear()
+        self.HSMFilesystemList.clear()
         self.client.close()
 
     def getSize(self, fsMountPoint):
@@ -261,7 +207,6 @@ class TapeClient(object):
 #tc.connect()
 #tc.copy("/home/curban/store/mydir", "/home/mydir")
 #tc.copy("/home/curban/store/foo2.txt", "/home/mydir/foo2.txt")
-#tl = tc.getTaskList()
 #fsSize = tc.getSize("/ia2_tape_stb_01")
 #print(fsSize)
 #tc.disconnect()
-- 
GitLab