index = $index; $this->type = 'tsm'; # Additional values that can go in our config.php $this->custom = new StdClass; $this->default = new StdClass; # TSM Server Stanza Name $this->default->server['stanza'] = array( 'desc'=>'TSM Server Stanza Name', 'default'=>null); $this->default->system['dsmadmc'] = array( 'desc'=>'Path to dsmadmc command.', 'default'=>'/opt/tivoli/tsm/client/ba/bin/dsmadmc'); $this->default->system['errorlog'] = array( 'desc'=>'Set errorlog filename', 'default'=>'/tmp/pta-tsm-errorlog.log'); $this->default->system['tapeage'] = array( 'desc'=>'Age of tapes to alert for rotation', 'default'=>180); # TSM Parameters $this->default->tsmparm['msgFormat'] = array( 'desc'=>'Format of TSM message codes.', 'default'=>'[A-Z]{3}[0-9]{4}[I|E|W|S]'); $this->default->tsmparm['queryStartMsg'] = array( 'desc'=>'TSM code depicting START of queries results.', 'default'=>'ANS8000I'); $this->default->tsmparm['queryEndMsg'] = array( 'desc'=>'TSM code depicting END of queries results.', 'default'=>'ANS8002I'); $this->default->tsmparm['loginFail'] = array( 'desc'=>'Known TSM "failed login" message codes.', 'default'=>array( 'ANS8023E', // 'ANS1017E', // TCP/IP connection rejected. 'ANS1217E', // Server name not found in System Options File 'ANS1355E', // Sessions disabled. 'ANS8034E' // Your administrator ID is not recognized by this server. )); $this->default->tsmparm['loginFailIgnore'] = array( 'desc'=>'Known TSM "ignore" message codes during login.', 'default'=>array( 'ANS0110E', // LogMsg: Unable to open error log file '%s' for output. 'ANS8002I' // Highest return code was %s. )); $this->default->tsmparm['ignoremsg'] = array( 'desc'=>'Known TSM "ignore" message codes.', 'default'=>array( 'ANS8001I', // Return code %s. 'ANS0102W' // Unable to open the message repository %s. The American English repository will be used instead. )); $this->default->tsmparm['nodatamsg'] = array( 'desc'=>'Known TSM "select returned no data" message codes.', 'default'=>array( 'ANR2034E' // SELECT: No match found using this criteria. )); } /** * Required ABSTRACT functions */ /** * Connect and Bind to the Database * * @param method This enables to have multiple different types of connects, ie: read only, readwrite, etc * @return resource|null Connection resource if successful, null if not. */ protected function connect($method,$debug=false) { $method = $this->getMethod($method); if (! $this->isLoggedIn($method)) system_message(array('title'=>'Not Logged In', 'body'=>sprintf('You are not logged into server %s',$this->getValue('server','name')), 'type'=>'warn'),'index.php'); if (! file_exists($this->getValue('system','dsmadmc'))) { system_message(array('title'=>'Cant find DSMADMC', 'body'=>sprintf('Unable to find the dsmadmc at %s',$this->getValue('system','dsmadmc')), 'type'=>'error')); return false; } if ($this->getValue('system','errorlog')) $errorlog = sprintf('-errorlogname=%s',$this->getValue('system','errorlog')); if (is_null($this->getValue('server','stanza')) || ! trim($this->getValue('server','stanza'))) $TSMcmd = sprintf('%s -id=%s -password=%s -displ=list %s', $this->getValue('system','dsmadmc'),$this->getLogin($method),$this->getPassword($method), isset($errorlog) ? $errorlog : ''); else $TSMcmd = sprintf('%s -se=%s -id=%s -password=%s -displ=list %s', $this->getValue('system','dsmadmc'), $this->getValue('server','stanza'),$this->getLogin($method),$this->getPassword($method), isset($errorlog) ? $errorlog : ''); return $TSMcmd; } /** * Login to the database with the application user/password * * @param method This enables to have multiple different types of connects, ie: read only, readwrite, etc * @return boolean true|false for successful login. */ public function login($user=null,$pass=null,$method=null) { # Set our user and password. $this->setLogin($user,$pass,$method); # Test that it works. $result = $this->query('SELECT platform,version,release,level,sublevel FROM status',$method); if (! $result) $this->logout($method); else return true; return false; } /** * Perform a query to the Database * * @param string SQL query to perform * @param string Index items according to this key * @return array|null Results of query. */ public function query($query,$method,$index=null,$debug=false) { $TSMcmd = sprintf('%s "%s"',$this->connect($method,$debug),$query); $TSMcmdOutput = exec($TSMcmd,$OutputLines,$return); if ($debug) debug_dump(array('cmd'=>$TSMcmd,'TSMcmdOutput'=>$TSMcmdOutput,'return'=>$return)); # Parse the ouput for key:value pairs. $outindex = 0; $isQueryResult = 0; $isKeyValue = 0; $haveHelp = false; $QueryResult = false; $tsmCommands = array(); foreach ($OutputLines as $OutputLine ) { $OutputLine = preg_replace('/\s+$/','',$OutputLine); if ($debug) debug_dump("($isQueryResult/$isKeyValue): WORKING line [$OutputLine]"); # See if we got a message number. if (preg_match("/^(".$this->getValue('tsmparm','msgFormat').")\s+/",$OutputLine,$matches)) { $msgnum = $matches[1]; if ($debug) debug_dump("($isQueryResult/$isKeyValue): Got SYSTEM Msg [$OutputLine]"); # Filter through the output and check if we got our start message. if (preg_match("/^".$this->getValue('tsmparm','queryStartMsg')."\s+/",$OutputLine)) { if ($debug) debug_dump("($isQueryResult/$isKeyValue): Got START Msg [$OutputLine]"); $isQueryResult = 1; continue; # Break out of loop when we get our end message. } elseif (preg_match("/^".$this->getValue('tsmparm','queryEndMsg')."\s+/",$OutputLine)) { if ($debug) debug_dump("($isQueryResult/$isKeyValue): Got END Msg [$OutputLine]"); break; } if (in_array($msgnum,$this->getValue('tsmparm','nodatamsg'))) continue; if (! in_array($msgnum,$this->getValue('tsmparm','ignoremsg'))) system_message(array('title'=>'TSM Message','body'=>$OutputLine)); } # If we the line is a queryResult line if ($isQueryResult) { # See if we got a key:value pair, otherwise go to next line. if (preg_match('/:/',$OutputLine) && ! (preg_match('/^help/',$query))) { $OutputLine = preg_replace('/\s*:\s+/',':',$OutputLine); list($key,$value) = explode(':',$OutputLine,2); $isKeyValue = 1; # Not key:value, so check if we need to increase the index. } else { # Is this a show slots command, so there is more info if (preg_match('/^show slots/',$query)) { if (preg_match('/^Slot /',$OutputLine)) { foreach ((preg_split('/,\s*/',$OutputLine,-1)) as $slotkey => $val) { if (preg_match('/^element number\s+/',$val)) { $key = preg_replace('/^element number\s+/','',$val); } elseif (preg_match('/^Slot\s+/',$val)) { $slots['slot'] = preg_replace('/^Slot\s+/','',$val); } elseif (preg_match('/^status\s+/',$val)) { $slots['status'] = preg_replace('/^status\s+/','',$val); } elseif (preg_match('/^barcode value /',"$1",$val); } elseif (preg_match('/^barcode\s+/',$val)) { $slots['barcode'] = preg_replace('/^barcode /','',$val); } } $QueryResult[$outindex]['SlotUsage'][$key] = $slots; } elseif (preg_match('/busy.$/',$OutputLine)) { system_message(array('title'=>'Library is Busy', 'body'=>sprintf('The library appears busy at the moment.
%s',$OutputLine), 'type'=>'info'), sprintf('index.php',get_request('cmd','REQUEST') ? sprintf('cmd=%s',get_request('cmd','REQUEST')) : '')); } # Is this a help command, so there is more info } elseif (preg_match('/^help$/',$query)) { if (preg_match('/^\s*([0-9]+) - Commands /',$OutputLine)) { $helpnum = preg_replace('/^\s*([0-9]+) - Commands .*/','$1',$OutputLine); $tsmCommands = array_unique(array_merge($tsmCommands,$this->query("help $helpnum",null))); } } elseif (preg_match('/^help\s+[0-9]+/',$query)) { if (! $OutputLine) continue; if (! $haveHelp && preg_match('/^\s*Command Name/',$OutputLine)) { $haveHelp = true; $helpCommands = array(); continue; } if ($haveHelp) { # Filter out the beginning spaces. $OutputLine = preg_replace('/^\s*/','',$OutputLine); # Filter out the "(See Note)" messages. $OutputLine = preg_replace('/\s\(.*\)/','',$OutputLine); # Join the two columns together $OutputLine = preg_replace('/\ \s+/','|',$OutputLine); # We should now just have commands and they should be in uppercase. if (preg_match('/[a-z0-9]/',$OutputLine)) continue; #debug_dump("Im finally here with [$OutputLine]."); foreach (explode('|',$OutputLine) as $helpCMD) array_push($helpCommands,$helpCMD); } } elseif (preg_match('/^help\s+[A-Z]+/',$query)) { # Sometimes QueryResult gets data because of colons - we need to capture that. if (isset($QueryResult) && is_array($QueryResult)) { foreach ($QueryResult as $lineDetail) foreach ($lineDetail as $key => $ldindex) $helpText[] = $key.$ldindex; $QueryResult = array(); } $helpText[] = $OutputLine; //debug_dump(array("Im finally here with [$OutputLine]."=>$QueryResult)); # If this is a blank line } elseif (!$OutputLine) { # Only increment index if the previous iteration was a key/value pair. # Reset isKeyValue so we dont increase index for 2 non key:value lines in a row. if ($isKeyValue) { if (!preg_match('/^show/',$query)) $outindex++; $isKeyValue = 0; } } // if continue; } // if $key = preg_replace('/^\s+/','',$key); $value = preg_replace('/^\s+/','',$value); $QueryResult[$outindex][$key] = $value; } // if } // foreach if (is_array($tsmCommands) && preg_match('/^help$/',$query)) { asort($tsmCommands); $QueryResult = $tsmCommands; } if (isset($helpCommands) && preg_match('/^help\s+[0-9]+/',$query)) $QueryResult = $helpCommands; if (isset($helpText) && preg_match('/^help\s+[A-Z]+/',$query)) $QueryResult = $helpText; if ($debug) debug_dump($QueryResult,0); if (! $isQueryResult && $debug) Error(sprintf('The query
%s
didnt return anything [%s]',preg_replace('/-password=(.*)\ -/','-password=x -',$TSMcmd),serialize($OutputLines))); if ($index && is_array($QueryResult)) { foreach ($QueryResult as $result => $val) { if (isset($val[$index])) $IndexQueryResult[$val[$index]] = $QueryResult[$result]; } return (isset($IndexQueryResult) ? $IndexQueryResult : false); } elseif (is_array($QueryResult)) { return $QueryResult; } return array(); } /** * Get the last error string */ protected function getErrorMessage() { return null; } /** * Get the last error number */ protected function getErrorNum() { return null; } /** * Return if anonymous bind is allowed in the configuration */ public function isAnonBindAllowed() { return false; } /** * Query the database for particular attributes values. */ public function queryAttr($table,$attrs,$key,$where=null,$orderby=null) { return array(); } /** * Register user to the application */ public function register($user,$pass,$other=array()) { return null; } /** * Query TSM and get Database information */ function QueryDBDetail() { $query = $this->query('select * from DB',null); $this->CACHE['db'] = $query[0]; $this->CACHE['db']['dbvols'] = $this->query('select * from DBVOLUMES',null); $this->CACHE['cache']['db'] = time(); $this->CACHE['cache']['log'] = time(); $query = $this->query('select * from LOG',null); $this->CACHE['log'] = $query[0]; $this->CACHE['log']['logvols'] = $this->query('select * from LOGVOLUMES',null); $_SESSION['tsm'][$this->index] = $this->CACHE; } /** * Return the DB information. * @param string attribute * @return string value for attribute */ function GetDBDetail($attribute) { if (! isset($this->CACHE['db'][$attribute])) $this->QueryDBDetail(); if (! isset($this->CACHE['db'][$attribute])) Error(sprintf(_('ERROR: A request to get DB attribute "%" didnt return a value'),$attribute)); return $this->CACHE['db'][$attribute]; } /** * Return the LOG information. * @param string attribute * @return string value for attribute */ function GetLogDetail($attribute) { if (! isset($this->CACHE['log'][$attribute])) $this->QueryDBDetail(); if (! isset($this->CACHE['log'][$attribute])) Error(sprintf(_('ERROR: A request to get LOG attribute "%" didnt return a value'),$attribute)); return $this->CACHE['log'][$attribute]; } /** * Return the VOLUMES information * @return array */ function GetVolumes() { if (isset($this->CACHE['volumes'])) return $this->CACHE['volumes']; $this->CACHE['volumes'] = $this->query('select * from VOLUMES',null,'VOLUME_NAME'); $this->CACHE['cache']['volumes'] = time(); $_SESSION['tsm'][$this->index] = $this->CACHE; return $this->CACHE['volumes']; } /** * Return the LIBVOLUMES information * @return array */ function GetLibVolumes() { if (isset($this->CACHE['libvols'])) return $this->CACHE['libvols']; $this->CACHE['libvols'] = $this->query('select * from LIBVOLUMES',null,'HOME_ELEMENT'); $this->CACHE['cache']['libvols'] = time(); $_SESSION['tsm'][$this->index] = $this->CACHE; return $this->CACHE['libvols']; } /** * Return the DB Backup Volumes * @return array */ function GetDBBackupInfo() { if (isset($this->CACHE['dbbackup'])) return $this->CACHE['dbbackup']; $this->CACHE['dbbackup']['vols'] = $this->query("select * from VOLHISTORY where TYPE in ('BACKUPFULL','BACKUPINCR','DBSNAPSHOT') order by BACKUP_SERIES,BACKUP_OPERATION,VOLUME_SEQ", null,'VOLUME_NAME'); # Now check that a backup series is valid - since it should be in order. $lastSeries = -1; $backupfirst = ''; $backuplast = ''; $backupcount = 0; if (is_array($this->CACHE['dbbackup']['vols'])) { foreach ($this->CACHE['dbbackup']['vols'] as $volume => $voldetails) { if ($lastSeries != $voldetails['BACKUP_SERIES']) { $lastSeries = $voldetails['BACKUP_SERIES']; $lastOperation = -1; $lastSeq = 0; if (! $backupfirst | $voldetails['DATE_TIME'] < $backupfirst) $backupfirst = $voldetails['DATE_TIME']; if (! $backuplast | $voldetails['DATE_TIME'] > $backuplast) $backuplast = $voldetails['DATE_TIME']; $backupcount++; } # No point trying this series if it is invalid. if (isset($status[$voldetails['BACKUP_SERIES']]['status']) && ($status[$voldetails['BACKUP_SERIES']]['status'] == 'InValid')) continue; $this->CACHE['dbbackup']['vols'][$volume]['STATUS'] = 'Valid'; if (($voldetails['BACKUP_OPERATION'] == $lastOperation) || ($voldetails['BACKUP_OPERATION']-1 == $lastOperation)) { $status[$voldetails['BACKUP_SERIES']]['status'] = 'Valid'; $lastOperation = $voldetails['BACKUP_OPERATION']; } else { $status[$voldetails['BACKUP_SERIES']]['status'] = 'InValid'; $status[$voldetails['BACKUP_SERIES']]['statusreason'] = sprintf(_('Missing SERIES volumes, expected [%s], found [%s].'), $lastOperation+1,$voldetails['BACKUP_OPERATION']); continue; } if (($voldetails['VOLUME_SEQ'] == $lastSeq) || $voldetails['VOLUME_SEQ']-1 == $lastSeq) { $status[$voldetails['BACKUP_SERIES']]['status'] = 'Valid'; $lastSeq = $voldetails['VOLUME_SEQ']; } else { $status[$voldetails['BACKUP_SERIES']]['status'] = 'InValid'; $status[$voldetails['BACKUP_SERIES']]['statusreason'] = sprintf(_('Missing SERIES volumes, expected [%s], found [%s].'),$lastSeq+1,$voldetails['VOLUME_SEQ']); } $lastSeq = $voldetails['VOLUME_SEQ']; } # Now we know which series are invalid, mark all the volumes. foreach ($this->CACHE['dbbackup']['vols'] as $volume => $voldetails) { $this->CACHE['dbbackup']['vols'][$volume]['STATUS'] = $status[$voldetails['BACKUP_SERIES']]['status']; if (isset($status[$voldetails['BACKUP_SERIES']]['statusreason'])) $this->CACHE['dbbackup']['vols'][$volume]['STATUSREASON'] = $status[$voldetails['BACKUP_SERIES']]['statusreason']; } } # Grab backup information $history = $this->query('select DATE_TIME,MSGNO,MESSAGE from ACTLOG where MSGNO in (4550,4551) order by DATE_TIME',null); if (! isset($history[0]['ANR2034E SELECT'])) foreach ($history as $index => $historydetail) { $this->CACHE['dbbackup']['history'][$index]['date'] = $historydetail['DATE_TIME']; switch ($historydetail['MSGNO']) { case 4550: if (preg_match('/([0-9]+) pages copied./',$historydetail['MESSAGE'],$matchall)) { $this->CACHE['dbbackup']['history'][$index]['size'] = $matchall[1]; $this->CACHE['dbbackup']['history'][$index]['type'] = 'FULL'; } break; case 4551: if (preg_match('/([0-9]+) pages copied./',$historydetail['MESSAGE'],$matchall)) { $this->CACHE['dbbackup']['history'][$index]['size'] = $matchall[1]; $this->CACHE['dbbackup']['history'][$index]['type'] = 'INCR'; } break; default: printf('Message %s not catered for [%s]...',$historydetail['MSGNO'],$historydetail['MESSAGE']); die(); } } # Get DBBackup Trigger information. $select = $this->query('select * from DBBACKUPTRIGGER',null); $result = array_shift($select); if (isset($result['ANR2034E SELECT'])) $this->CACHE['dbbackup']['trigger'] = false; else $this->CACHE['dbbackup']['trigger'] = $result; $this->CACHE['dbbackup']['first'] = $backupfirst; $this->CACHE['dbbackup']['last'] = $backuplast; $this->CACHE['dbbackup']['count'] = $backupcount; $this->CACHE['cache']['dbbackup'] = time(); $_SESSION['tsm'][$this->index] = $this->CACHE; return $this->CACHE['dbbackup']; } function GetDBBackupDetail($detail) { if (isset($this->CACHE['dbbackup'][$detail])) return $this->CACHE['dbbackup'][$detail]; $this->GetDBBackupInfo(); if (isset($this->CACHE['dbbackup'][$detail])) return $this->CACHE['dbbackup'][$detail]; else return null; } /** * Return the Status Information * @return array */ function GetStatus() { if (isset($this->CACHE['status'])) return $this->CACHE['status']; $select = $this->query('select * from STATUS',null); $this->CACHE['status'] = array_shift($select); $this->CACHE['cache']['status'] = time(); $_SESSION['tsm'][$this->index] = $this->CACHE; return $this->CACHE['status']; } function GetStatusDetail($detail) { if (isset($this->CACHE['status'][$detail])) return $this->CACHE['status'][$detail]; $this->GetStatus(); if (isset($this->CACHE['status'][$detail])) return $this->CACHE['status'][$detail]; else return null; } /** * Get TSM event information * @param string Start date for report * @param string End date for report * @param string order by key. * @return array Query result */ function GetEvents($reportStart='',$reportEnd='',$orderby='SCHEDULED_START') { if (isset($this->CACHE['events']) && $reportStart == $this->CACHE['events']['start'] && $reportEnd == $this->CACHE['events']['end'] && $orderby == $this->CACHE['events']['orderby']) return $this->CACHE['events']; $TSMQuery = 'select * from EVENTS where DOMAIN_NAME is not null'; if (($reportStart) && ($reportEnd)) $TSMQuery .= sprintf(" and (SCHEDULED_START between '%s' and '%s')",$reportStart,$reportEnd); else if ($reportStart) $TSMQuery .= sprintf(" and SCHEDULED_START \> '%s'",$reportStart); else if ($reportEnd) $TSMQuery .= sprintf(" and SCHEDULED_START <= '%s'",$reportEnd); $TSMQuery .= sprintf(' order by %s',$orderby); $result = $this->query($TSMQuery,null); if (isset($result[0]['ANR2034E SELECT'])) { $this->CACHE['events'] = false; } else { $this->CACHE['events']['detail'] = $result; $this->CACHE['events']['start'] = $reportStart; $this->CACHE['events']['end'] = $reportEnd; $this->CACHE['events']['orderby'] = $orderby; } $this->CACHE['cache']['events'] = time(); $_SESSION['tsm'][$this->index] = $this->CACHE; return $this->CACHE['events']; } /** * Get TSM Activity Log Client backup information. * @param string Start date for report * @param string End date for report * @return array Query result */ function GetActlogBackupSummary($reportStart='',$reportEnd='') { if (isset($this->CACHE['actlogbackup']) && $reportStart == $this->CACHE['actlogbackup']['start'] && $reportEnd == $this->CACHE['actlogbackup']['end']) return $this->CACHE['actlogbackup']; $tsmMSG['4952'] = 'Inspected'; $tsmMSG['4954'] = 'Backup'; $tsmMSG['4958'] = 'Update'; $tsmMSG['4957'] = 'Delete'; $tsmMSG['4959'] = 'Failed'; $tsmMSG['4960'] = 'Rebound'; $tsmMSG['4961'] = 'Transfer'; $tsmMSG['4964'] = 'ProcTime'; $tsmMSG['4967'] = 'AggRate'; $tsmMSG['4968'] = 'Compress'; $tsmMSG['4970'] = 'Expire'; $TSMQuery = sprintf('select MSGNO,NODENAME,SESSID,MESSAGE,SCHEDNAME,DOMAINNAME from ACTLOG where MSGNO in (%s)', implode(',',array_keys($tsmMSG))); if (($reportStart) && ($reportEnd)) $TSMQuery .= sprintf(" and (DATE_TIME between '%s' and '%s')",$reportStart,$reportEnd); else if ($reportStart) $TSMQuery .= sprintf(" and DATE_TIME \> '%s'",$reportStart); else if ($reportEnd) $TSMQuery .= sprintf(" and DATE_TIME <= '%s'",$reportEnd); $TSMQuery .= ' order by sessid'; foreach ($this->query($TSMQuery,null) as $tsmBackup => $tsmSession) { # TSM on AIX adds a "SESSION" at the end of each line - maybe others do as well. if (isset($tsmSession['MESSAGE'])) $tsmSession['MESSAGE'] = preg_replace('/\(SESSION:.*\)/','',$tsmSession['MESSAGE']); $tsmBackupSummary['detail'][$tsmSession['NODENAME']][$tsmSession['SESSID']][$tsmMSG[$tsmSession['MSGNO']]] = preg_replace('/^.*:\s*/U','',$tsmSession['MESSAGE']); } $tsmBackupSummary['start'] = $reportStart; $tsmBackupSummary['end'] = $reportEnd; $this->CACHE['actlogbackup'] = $tsmBackupSummary; $this->CACHE['cache']['actlogbackup'] = time(); $_SESSION['tsm'][$this->index] = $this->CACHE; return $this->CACHE['actlogbackup']; } function GetVolLocation($vol) { $volumes = $this->GetVolumes(); if ($this->GetLibVolumes()) foreach ($this->GetLibVolumes() as $slot => $details) { if ($details['VOLUME_NAME'] == $vol) return sprintf('**%s %s**',_('LIBRARY'),$details['LIBRARY_NAME']); } if (isset($volumes[$vol])) return $volumes[$vol]['LOCATION']; # If we get here, we dont know where the volume is. return null; } } ?>