diff --git a/application/classes/controller/node.php b/application/classes/controller/node.php new file mode 100644 index 0000000..f428084 --- /dev/null +++ b/application/classes/controller/node.php @@ -0,0 +1,79 @@ +'node'); + + /** + * Default Index for Controller + */ + public function action_index() { + $no = ORM::factory('node'); + $output = ''; + + $output .= sprintf(_('This server has %s defined nodes.'),$no->count_all()); + $output .= '
'; + $output .= '
'; + + $select = array(); + $select[NULL] = ''; + + foreach ($no->find_all() as $node) + $select[$node->NODE_NAME] = $node->NODE_NAME; + + $output .= Form::open('/node/detail',array('id'=>'node_detail')); + $output .= sprintf('%s: %s',_('Choose a new to view'),Form::select('node_name',$select,NULL,array('id'=>'node_name'))); + $output .= Form::submit('form_submit',_('Go')); + $output .= Form::close(); + + Script::add(array( + 'type'=>'stdin', + 'data'=>' +$(document).ready(function () {$("#node_name").change(function () { + $("#node_detail").trigger("submit"); + });});' + )); + + Block::add(array( + 'title'=>_('TSM Nodes'), + 'body'=>$output, + )); + + $this->template->content = Block::factory(); + } + + public function action_detail($node_name=NULL) { + if (is_null($node_name) AND (empty($_POST['node_name']) OR ! $node_name = $_POST['node_name'])) + throw new Kohana_Exception('Missing NODE_NAME'); + + $no = ORM::factory('node',$node_name); + + $output = View::factory('nodes/detail') + ->set('node',$no); + + Block::add(array( + 'title'=>sprintf('%s %s',_('Detailed Node Information for'),$no->NODE_NAME), + 'body'=>$output, + )); + + $output = View::factory('nodes/detail_filesystem') + ->set('node',$no); + + Block::add(array( + 'title'=>_('Protected File System Information'), + 'body'=>$output, + )); + + $this->template->content = Block::factory(); + } +} +?> diff --git a/application/classes/database/tsm/result.php b/application/classes/database/tsm/result.php index e6909fb..60b6add 100644 --- a/application/classes/database/tsm/result.php +++ b/application/classes/database/tsm/result.php @@ -19,15 +19,19 @@ class Database_TSM_Result extends Database_Result { { parent::__construct($result, $sql, $as_object, $params); + $start = FALSE; foreach ($result as $line) { if (! trim($line)) { - $this->_internal_row++; + if ($start) + $this->_internal_row++; + continue; } list($k,$v) = explode(':',$line,2); $this->_rows[$this->_internal_row][trim($k)] = trim($v); + $start = TRUE; } $this->_total_rows = $this->_internal_row; diff --git a/application/classes/model/filespace.php b/application/classes/model/filespace.php new file mode 100644 index 0000000..dddf619 --- /dev/null +++ b/application/classes/model/filespace.php @@ -0,0 +1,56 @@ +array('foreign_key'=>array('NODE_NAME','FILESPACE_NAME'),'far_key'=>'FILESPACE_NAME'), + 'OCCUPANCY'=>array('foreign_key'=>array('NODE_NAME','FILESPACE_NAME'),'far_key'=>'FILESPACE_NAME'), + ); + + protected $_formats = array( + 'BACKUP_END'=>array('ORMTSM::date'=>array('d-M-Y')), + ); + + protected $_sorting = array( + 'NODE_NAME'=>'ASC', + ); + + public function utilsation() { + return $this->CAPACITY * ($this->PCT_UTIL/100); + } + + public function storagepools($dtype) { + $pool = array(); + + foreach ($this->VOLUMEUSAGE + ->select('STGPOOL_NAME') + ->where('COPY_TYPE','=',$dtype) + ->group_by('STGPOOL_NAME') + ->order_by('STGPOOL_NAME') + ->find_all() as $vo) { + + array_push($pool,$vo->STGPOOL_NAME); + } + + return $pool; + } + + public function pool_logical_util($pool) { + return $this->OCCUPANCY->where('STGPOOL_NAME','=',$pool)->find()->LOGICAL_MB; + } + + public function pool_numvols($pool) { + return $this->VOLUMEUSAGE->where('STGPOOL_NAME','=',$pool)->find_all()->count(); + } +} +?> diff --git a/application/classes/model/node.php b/application/classes/model/node.php index 0cc05c5..8a0c3d5 100644 --- a/application/classes/model/node.php +++ b/application/classes/model/node.php @@ -12,6 +12,9 @@ class Model_NODE extends ORMTSM { protected $_table_name = 'NODES'; protected $_primary_key = 'NODE_NAME'; + protected $_has_many = array( + 'FILESPACE'=>array('foreign_key'=>'NODE_NAME','far_key'=>'FILESPACE_NAME'), + ); protected $_formats = array( 'REG_TIME'=>array('ORMTSM::date'=>array('d-M-Y')), @@ -29,6 +32,9 @@ class Model_NODE extends ORMTSM { 'NODE_NAME'=>'ASC', ); + // Pools used by a node. + private $pools = array(); + public function tsmclientversion() { return sprintf('%s.%s.%s.%s',$this->CLIENT_VERSION,$this->CLIENT_RELEASE,$this->CLIENT_LEVEL,$this->CLIENT_SUBLEVEL); } @@ -42,33 +48,33 @@ class Model_NODE extends ORMTSM { } public function lasttransferpercent() { - return number_format(100-($this->LASTSESS_IDLEWAIT+$this->LASTSESS_COMMWAIT+$this->LASTSESS_MEDIAWAIT),2); + return 100-($this->LASTSESS_IDLEWAIT+$this->LASTSESS_COMMWAIT+$this->LASTSESS_MEDIAWAIT); } public function lasttransfertime() { - return number_format($this->LASTSESS_DURATION*($this->lasttransferpercent()/100),2); + return $this->LASTSESS_DURATION*($this->lasttransferpercent()/100); } public function lastsendperformance() { if ($this->lasttransfertime()) - return number_format($this->LASTSESS_SENT/$this->lasttransfertime()/1024/1024,2); + return $this->LASTSESS_SENT/$this->lasttransfertime()/1024/1024; else return _('N/A'); } public function lastreceiveperformance() { if ($this->lasttransfertime()) - return number_format($this->LASTSESS_RECVD/$this->lasttransfertime()/1024/1024,2); + return $this->LASTSESS_RECVD/$this->lasttransfertime()/1024/1024; else return _('N/A'); } public function lastsendaggperformance() { - return number_format($this->LASTSESS_SENT/$this->LASTSESS_DURATION/1024/1024,2); + return $this->LASTSESS_SENT/$this->LASTSESS_DURATION/1024/1024; } public function lastreceiveaggperformance() { - return number_format($this->LASTSESS_RECVD/$this->LASTSESS_DURATION/1024/1024,2); + return $this->LASTSESS_RECVD/$this->LASTSESS_DURATION/1024/1024; } // @todo This should return the system setting (cloptset), if the node setting is not configured. @@ -76,6 +82,36 @@ class Model_NODE extends ORMTSM { return $this->display('TXNGROUPMAX'); } + // Work out all the storage pools used by a node. + // $dtype is BACKUP or ARCHIVE + public function storagepools($dtype) { + return isset($this->pools[$dtype]) ? $this->pools[$dtype] : $this->_getpools($dtype); + } + + private function _getpools($dtype) { + $this->pools[$dtype] = array(); + + foreach ($this->FILESPACE->find_all() as $fso) { + foreach ($fso->storagepools($dtype) as $pool_name) { + $po = ORM::Factory('stgpool',$pool_name); + + if (! isset($this->pools[$dtype][$po->POOLTYPE]) OR ! in_array($pool_name,$this->pools[$dtype][$po->POOLTYPE])) + $this->pools[$dtype][$po->POOLTYPE][] = $pool_name; + } + } + + return $this->pools[$dtype]; + } + + // Return the storage pools used for a backup type + // $dtype is BACKUP or ARCHIVE + public function getStoragePools($dtype,$ptype) { + if (! isset($this->pools[$dtype])) + $this->_getpools($dtype); + + return isset($this->pools[$dtype][$ptype]) ? $this->pools[$dtype][$ptype] : array(); + } + /** * Get all the nodes by OS */ diff --git a/application/classes/model/occupancy.php b/application/classes/model/occupancy.php new file mode 100644 index 0000000..a86a3a3 --- /dev/null +++ b/application/classes/model/occupancy.php @@ -0,0 +1,27 @@ +'ASC', + 'FILESPACE_NAME'=>'ASC', + 'STGPOOL_NAME'=>'ASC', + ); +} +?> diff --git a/application/classes/model/stgpool.php b/application/classes/model/stgpool.php new file mode 100644 index 0000000..9707843 --- /dev/null +++ b/application/classes/model/stgpool.php @@ -0,0 +1,25 @@ +'ASC', + ); +} +?> diff --git a/application/classes/model/volumeusage.php b/application/classes/model/volumeusage.php new file mode 100644 index 0000000..f2784e3 --- /dev/null +++ b/application/classes/model/volumeusage.php @@ -0,0 +1,27 @@ +'ASC', + 'FILESPACE_NAME'=>'ASC', + 'VOLUME_NAME'=>'ASC', + ); +} +?> diff --git a/application/classes/orm.php b/application/classes/orm.php index c17c260..6c64393 100644 --- a/application/classes/orm.php +++ b/application/classes/orm.php @@ -23,7 +23,7 @@ class ORM extends Kohana_ORM { // If we have found our item return if ($this->_loaded) - return; + return $object; } } diff --git a/application/classes/ormtsm.php b/application/classes/ormtsm.php index e29bdc5..c6a7a1e 100644 --- a/application/classes/ormtsm.php +++ b/application/classes/ormtsm.php @@ -13,6 +13,8 @@ abstract class ORMTSM extends ORM { // Suppress ORMs inclusion of .* protected $_disable_wild_select = TRUE; + // Suppress ORMs inclusion of . to column joins + protected $_disable_join_table_name = TRUE; // Enable the formating of columns protected $_object_formated = array(); diff --git a/application/views/nodes/detail.php b/application/views/nodes/detail.php new file mode 100644 index 0000000..9391d7b --- /dev/null +++ b/application/views/nodes/detail.php @@ -0,0 +1,141 @@ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Node Information
 
Node Namedisplay('NODE_NAME'); ?> (display('TCP_ADDRESS'); ?>)
Operating Sytemdisplay('PLATFORM_NAME'),$node->display('CLIENT_OS_LEVEL')); ?>
TSM Client Versiontsmclientversion(); ?>
 
Date Registereddisplay('REG_TIME'); ?> (by display('REG_ADMIN'); ?>)
Date Last Password Changedisplay('PWSET_TIME'); ?>
 
Password Expirypassexp(); ?>
Invalid Password Countdisplay('INVALID_PW_COUNT'); ?> (LOCKED == 'NO' ? _('Not Locked') : _('Locked'); ?>)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Last Backup Performance Information
 
Last Accessdisplay('LASTACC_TIME'); ?>
Last SentLASTSESS_SENT/1024/1024,2); ?> MB
Last ReceiveLASTSESS_RECVD/1024/1024,2); ?> MB
Last DurationLASTSESS_DURATION/60,2); ?> min
Last Session Idle Wait Percentdisplay('LASTSESS_IDLEWAIT'); ?>% (LASTSESS_DURATION*($node->LASTSESS_IDLEWAIT/100),2); ?>s)
Last Session Comm Wait Percentdisplay('LASTSESS_COMMWAIT'); ?>% (LASTSESS_DURATION*($node->LASTSESS_COMMWAIT/100),2); ?>s)
Last Session Media Wait Percentdisplay('LASTSESS_MEDIAWAIT'); ?>% (LASTSESS_DURATION*($node->LASTSESS_MEDIAWAIT/100),2); ?>s)
Last Session Transfer Percentlasttransferpercent(),2); ?>% (lasttransfertime(),2); ?>s)
Last Session Send Performancelastsendperformance(),2); ?> MB/s (lastsendaggperformance(),2); ?> MB/s Aggregate)
Last Session Receive Performancelastreceiveperformance(),2); ?> MB/s (lastreceiveaggperformance(),2); ?> MB/s Aggregate)
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Backup Settings
 
Client Option Setdisplay('OPTION_SET'); ?>
Collocation GroupCOLLOCGROUP_NAME ? $node->display('COLLOCGROUP_NAME') : 'Not Set'; ?>
Client Compressiondisplay('COMPRESSION'); ?>
TXN Group Maxtxngroupmax(); ?>
Delete Archivesdisplay('ARCHDELETE'); ?>
Delete Backupsdisplay('BACKDELETE'); ?>
Keep Mount Pointsdisplay('KEEP_MP'); ?> (display('MAX_MP_ALLOWED'); ?>)
+
+ [PERFORMANCE PIE-GRAPH?] +
diff --git a/application/views/nodes/detail_filesystem.php b/application/views/nodes/detail_filesystem.php new file mode 100644 index 0000000..daf2d03 --- /dev/null +++ b/application/views/nodes/detail_filesystem.php @@ -0,0 +1,80 @@ + + + storagepools('BACKUP')) { ?> + + + + + + storagepools('ARCHIVE')) { ?> + + + + +
+ + + + + + + + + + + + getStoragePools('BACKUP',$type))) + foreach ($pools as $pool_name) { ?> + + + + FILESPACE->find_all() as $fso) { ?> + + + + + getStoragePools('BACKUP',$type))) + foreach ($pools as $pool_name) { ?> + + + + +
Backup Information
 
File SpaceLast DateUtilisation
display('FILESPACE_NAME'); ?>display('BACKUP_END'); ?>utilsation(),2); ?>pool_logical_util($pool_name),2); ?> (pool_numvols($pool_name); ?>)
+
+   +
+ + + + + + + + + + + + getStoragePools('ARCHIVE',$type))) + foreach ($pools as $pool_name) { ?> + + + + FILESPACE->find_all() as $fso) { ?> + + + + + getStoragePools('ARCHIVE',$type))) + foreach ($pools as $pool_name) { ?> + + + + +
Backup Information
 
File SpaceUtilisationLast Date
display('FILESPACE_NAME'); ?>utilsation(),2); ?>display('BACKUP_END'); ?>pool_logical_util($pool_name),2); ?> (pool_numvols($pool_name); ?>)
+
+   +