diff --git a/application/classes/database.php b/application/classes/database.php index b4bda30..75a78f4 100644 --- a/application/classes/database.php +++ b/application/classes/database.php @@ -13,5 +13,6 @@ abstract class Database extends Kohana_Database { // Enable TSM SHOW commands const SHOW = 5; + const SET = 6; } ?> diff --git a/application/classes/database/tsm.php b/application/classes/database/tsm.php index 83136a5..2292121 100644 --- a/application/classes/database/tsm.php +++ b/application/classes/database/tsm.php @@ -10,7 +10,7 @@ * @copyright (c) 2010 phpTSMadmin Development Team * @license http://phptsmadmin.sf.net/license.html */ -class Database_TSM extends Database { +abstract class Database_TSM extends Database { // Database in use by each connection protected static $_current_databases = array(); @@ -24,25 +24,33 @@ class Database_TSM extends Database { protected $_identifier = ''; // Our TSM Message Format - private $msg_format = '[A-Z]{3}[0-9]{4}[I|E|W|S]'; + protected $msg_format = '[A-Z]{3}[0-9]{4}[I|E|W|S]'; // Our Message Codes stored in the last query - private $_query_msg_codes = array(); + protected $_query_msg_codes = array(); // Our return code error messages we can ignore - private $ignore_codes = array( + protected $ignore_codes = array( 'ANS8001I', // Return code %s. ); // Our return code error messages we can ignore - private $nodata_codes = array( + protected $nodata_codes = array( 'ANR2034E', // SELECT: No match found using this criteria. ); + protected $execute_stdout = ''; + protected $execute_stderr = ''; + protected $execute_rc = NULL; + // Our required abstract methods public function begin($mode = NULL) {} public function commit() {} public function rollback() {} public function set_charset($charset) {} + public function list_tables($like = NULL) {} + + // Required methods + abstract protected function execute($sql); /** * Return the caching defined in the current configuration. @@ -55,82 +63,6 @@ class Database_TSM extends Database { return ($this->_config['caching'] AND isset($this->_config['cache'][$table])) ? $this->_config['cache'][$table] : FALSE; } - public function connect() { - if ($this->_connection) - return; - - // Extract the connection parameters, adding required variabels - extract($this->_config['connection'] + array( - 'database' => '', - 'hostname' => '', - 'username' => '', - 'password' => '', - 'persistent' => FALSE, - )); - - // Get user login details from user session - these are set by login - if (! $username) - $username = Session::instance()->get_once(Kohana::config('auth.session_key')); - if (! $password) - $password = Session::instance()->get_once('password'); - - // Prevent this information from showing up in traces - unset($this->_config['connection']['username'], $this->_config['connection']['password']); - - if (! file_exists(Kohana::config('config.client'))) { - system_message(array('title'=>'Cant find DSMADMC', - 'body'=>sprintf('Unable to find the dsmadmc at %s',Kohana::config('client.config')), - 'type'=>'error')); - - return FALSE; - } - - if (! $username OR ! $password) - Request::current()->redirect('/login?need_login=1'); - - try { - if ($persistent) { - // Create a persistent connection - throw new Kohana_Exception('Cant do persistant connections'); - - } else { - // Create a connection and force it to be a new link - $this->_connection = sprintf('%s %s -id=%s -password=%s -displ=list -editor=no -dataonly=YES %s %s ', - Kohana::config('config.client'), - $database ? '-server='.$database : '', - $username, - $password, - Kohana::config('config.client_errorlogname') ? sprintf('-errorlogname=%s',Kohana::config('config.client_errorlogname')) : '', - $database ? sprintf('-se=%s',$database) : '' - ); - - $result = $this->query(Database::SELECT,'SELECT server_name,platform,version,release,level,sublevel FROM status'); - - if ($result) - return TSM::instance()->set($username,$password,$result); - else - Request::current()->redirect(Request::detect_uri()); - } - - } catch (ErrorException $e) { - // No connection exists - $this->_connection = NULL; - - throw new Database_Exception(':error', array( - ':error' => sprintf('%s error in %s (%s)',$e->getMessage(),$e->getFile(),$e->getLine()), - ), - $e->getCode()); - } - - // \xFF is a better delimiter, but the PHP driver uses underscore - $this->_connection_id = sha1($hostname.'_'.$username.'_'.$password); - - if ( ! empty($this->_config['charset'])) { - // Set the character set - $this->set_charset($this->_config['charset']); - } - } - private function clear() { $this->_query_msg_codes = array(); } @@ -144,35 +76,15 @@ class Database_TSM extends Database { // Benchmark this query for the current instance $benchmark = Profiler::start("Database ({$this->_instance})", $sql); - // We need to escape any back slashes, since the exec will transpose them - // @todo Is there a better way of doing this? - $sql = str_replace('\\','\\\\',$sql); - // Execute the query - $stderr = exec($this->_connection.'"'.$sql.'"',$stdout,$rc); + $result = $this->execute($sql); - // Work out our message codes - $result = array(); - foreach ($stdout as $line) - if (! preg_match('/^('.$this->msg_format.')\s+/',$line,$matches)) - array_push($result,$line); - elseif (! in_array($matches[1],$this->ignore_codes)) - array_push($this->_query_msg_codes,$matches[1]); - - // If we got a no data code - if (array_intersect($this->_query_msg_codes,$this->nodata_codes)) { - $result = array(); - $rc = 0; - } - - if ($stderr AND $rc) { + if ($this->execute_stderr AND $this->execute_rc) { + // This benchmark is worthless if (isset($benchmark)) - { - // This benchmark is worthless Profiler::delete($benchmark); - } - SystemMessage::TSM_Error($stdout,$sql); + SystemMessage::TSM_Error(sprintf('%s (%s)',$this->execute_stdout,$this->execute_stderr),$sql); Request::current()->redirect('welcome'); } @@ -189,91 +101,8 @@ class Database_TSM extends Database { throw new Kohana_Exception('Database INSERTS are not supported'); elseif ($type === Database::SHOW) return new Database_TSM_Show($result, $sql, $as_object, $params); - } - - public function list_tables($like = NULL) { - if (is_string($like)) - // Search for table names - $result = $this->query(Database::SELECT, 'SHOW TABLES LIKE '.$this->quote($like), FALSE); - else - // Find all table names - $result = $this->query(Database::SELECT, 'SHOW TABLES', FALSE); - - $tables = array(); - foreach ($result as $row) - $tables[] = reset($row); - - return $tables; - } - - public function list_columns($table, $like = NULL, $add_prefix = TRUE) { - // Quote the table name - $table = ($add_prefix === TRUE) ? $this->quote_table($table) : $table; - - if (is_string($like)) - // Search for column names - $result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table.' LIKE '.$this->quote($like), FALSE); - else - // Find all column names - $result = $this->query(Database::SELECT, sprintf('SELECT * FROM SYSCAT.COLUMNS WHERE TABNAME=\'%s\'',$table), FALSE); - - $count = 0; - $columns = array(); - foreach ($result as $row) { - list($type, $length) = $this->_parse_type($row['TYPENAME']); - - $column = $this->datatype($type); - - $column['column_name'] = $row['COLNAME']; - $column['data_type'] = $type; - $column['is_nullable'] = ($row['NULLS'] == 'TRUE'); - $column['ordinal_position'] = ++$count; - - switch (strtolower($column['data_type'])) { - case 'float': - if (isset($length)) - list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length); - break; - - case 'int': - if (isset($length)) - // MySQL attribute - $column['display'] = $length; - break; - - case 'string': - switch ($column['data_type']) { - case 'binary': - case 'varbinary': - $column['character_maximum_length'] = $length; - break; - - case 'char': - case 'varchar': - $column['character_maximum_length'] = $length; - case 'text': - case 'tinytext': - case 'mediumtext': - case 'longtext': - $column['collation_name'] = $row['Collation']; - break; - - case 'enum': - case 'set': - $column['collation_name'] = $row['Collation']; - $column['options'] = explode('\',\'', substr($length, 1, -1)); - break; - } - - break; - } - - // TSM attributes - $column['comment'] = $row['REMARKS']; - $columns[$row['COLNAME']] = $column; - } - - return $columns; + elseif ($type === Database::SET) + return new Database_TSM_Set($result, $sql, $as_object, $params); } public function escape($value) { @@ -283,5 +112,5 @@ class Database_TSM extends Database { // SQL standard is to use single-quotes for all values return "'$value'"; } - -} // End Database_TSM +} +?> diff --git a/application/classes/database/tsm/db2.php b/application/classes/database/tsm/db2.php new file mode 100644 index 0000000..dbc0439 --- /dev/null +++ b/application/classes/database/tsm/db2.php @@ -0,0 +1,162 @@ +_connection) + return; + + // Extract the connection parameters, adding required variabels + extract($this->_config['connection'] + array( + 'database' => '', + 'hostname' => '', + 'username' => '', + 'port' => '', + 'password' => '', + 'persistent' => FALSE, + )); + + // Get user login details from user session - these are set by login + if (! $username) + $username = Session::instance()->get_once(Kohana::config('auth.session_key')); + if (! $password) + $password = Session::instance()->get_once('password'); + + // Prevent this information from showing up in traces + unset($this->_config['connection']['username'], $this->_config['connection']['password']); + + if (! $username OR ! $password) + Request::current()->redirect('/login?need_login=1'); + + try { + if ($persistent) { + // Create a persistent connection + throw new Kohana_Exception('Cant do persistant connections'); + + } else { + // Create a connection and force it to be a new link + $constring = sprintf('DATABASE=%s;HOSTNAME=%s;PORT=%s;PROTOCOL=%s;UID=%s;PWD=%s', + $database,$hostname,$port,'TCPIP',$username,$password); + + $this->_connection = db2_connect($constring,NULL,NULL); + + if ($this->_connection) { + $result = DB::query(Database::SET,'SET SCHEMA='.$database)->execute(Kohana::config('config.client_type')); + $result = $this->query(Database::SELECT,'SELECT server_name,platform,version,release,level,sublevel FROM status'); + + return TSM::instance()->set($username,$password,$result); + + } else + Request::current()->redirect(Request::detect_uri()); + } + + } catch (ErrorException $e) { + // No connection exists + $this->_connection = NULL; + + throw new Database_Exception(':error', array( + ':error' => sprintf('%s error in %s (%s)',$e->getMessage(),$e->getFile(),$e->getLine()), + ), + $e->getCode()); + } + + // \xFF is a better delimiter, but the PHP driver uses underscore + $this->_connection_id = sha1($hostname.'_'.$username.'_'.$password); + + if ( ! empty($this->_config['charset'])) { + // Set the character set + $this->set_charset($this->_config['charset']); + } + } + + protected function execute($sql) { + $result = @db2_exec($this->_connection,$sql); + $this->execute_stderr = db2_stmt_errormsg(); + $this->execute_rc = db2_stmt_error(); + + return $result; + } + + public function list_columns($table, $like = NULL, $add_prefix = TRUE) { + // Make sure the database is connected + $this->_connection or $this->connect(); + + // Quote the table name + $table = ($add_prefix === TRUE) ? $this->quote_table($table) : $table; + + if (is_string($like)) + // Search for column names + throw new Kohana_Exception('Like queries not implemented'); + else + // Find all column names + $result = db2_columns($this->_connection,NULL,NULL,$table); + + $count = 0; + $columns = array(); + while ($row = db2_fetch_assoc($result)) { + list($type, $length) = $this->_parse_type($row['TYPE_NAME']); + + $column = $this->datatype($type); + + $column['column_name'] = $row['COLUMN_NAME']; + $column['data_type'] = $type; + $column['is_nullable'] = ($row['IS_NULLABLE'] == 'YES'); + $column['ordinal_position'] = ++$count; + + switch (strtolower($column['data_type'])) { + case 'float': + if (isset($length)) + list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length); + break; + + case 'int': + if (isset($length)) + // MySQL attribute + $column['display'] = $length; + break; + + case 'string': + switch ($column['data_type']) { + case 'binary': + case 'varbinary': + $column['character_maximum_length'] = $length; + break; + + case 'char': + case 'varchar': + $column['character_maximum_length'] = $length; + case 'text': + case 'tinytext': + case 'mediumtext': + case 'longtext': + $column['collation_name'] = $row['Collation']; + break; + + case 'enum': + case 'set': + $column['collation_name'] = $row['Collation']; + $column['options'] = explode('\',\'', substr($length, 1, -1)); + break; + } + + break; + } + + // TSM attributes + $column['comment'] = $row['REMARKS']; + $columns[$row['COLUMN_NAME']] = $column; + } + + return $columns; + } +} +?> diff --git a/application/classes/database/tsm/dsmadmc.php b/application/classes/database/tsm/dsmadmc.php new file mode 100644 index 0000000..31a15d5 --- /dev/null +++ b/application/classes/database/tsm/dsmadmc.php @@ -0,0 +1,186 @@ +_connection) + return; + + // Extract the connection parameters, adding required variabels + extract($this->_config['connection'] + array( + 'database' => '', + 'hostname' => '', + 'username' => '', + 'password' => '', + 'persistent' => FALSE, + )); + + // Get user login details from user session - these are set by login + if (! $username) + $username = Session::instance()->get_once(Kohana::config('auth.session_key')); + if (! $password) + $password = Session::instance()->get_once('password'); + + // Prevent this information from showing up in traces + unset($this->_config['connection']['username'], $this->_config['connection']['password']); + + if (! file_exists(Kohana::config('config.client'))) { + system_message(array('title'=>'Cant find DSMADMC', + 'body'=>sprintf('Unable to find the dsmadmc at %s',Kohana::config('client.config')), + 'type'=>'error')); + + return FALSE; + } + + if (! $username OR ! $password) + Request::current()->redirect('/login?need_login=1'); + + try { + if ($persistent) { + // Create a persistent connection + throw new Kohana_Exception('Cant do persistant connections'); + + } else { + // Create a connection and force it to be a new link + $this->_connection = sprintf('%s %s -id=%s -password=%s -displ=list -editor=no -dataonly=YES %s %s ', + Kohana::config('config.client'), + $database ? '-server='.$database : '', + $username, + $password, + Kohana::config('config.client_errorlogname') ? sprintf('-errorlogname=%s',Kohana::config('config.client_errorlogname')) : '', + $database ? sprintf('-se=%s',$database) : '' + ); + + $result = $this->query(Database::SELECT,'SELECT server_name,platform,version,release,level,sublevel FROM status'); + + if ($result) + return TSM::instance()->set($username,$password,$result); + else + Request::current()->redirect(Request::detect_uri()); + } + + } catch (ErrorException $e) { + // No connection exists + $this->_connection = NULL; + + throw new Database_Exception(':error', array( + ':error' => sprintf('%s error in %s (%s)',$e->getMessage(),$e->getFile(),$e->getLine()), + ), + $e->getCode()); + } + + // \xFF is a better delimiter, but the PHP driver uses underscore + $this->_connection_id = sha1($hostname.'_'.$username.'_'.$password); + + if ( ! empty($this->_config['charset'])) { + // Set the character set + $this->set_charset($this->_config['charset']); + } + } + + protected function execute($sql) { + $this->execute_stdout = ''; + + // We need to escape any back slashes, since the exec will transpose them + // @todo Is there a better way of doing this? + $sql = str_replace('\\','\\\\',$sql); + + $this->execute_stderr = exec($this->_connection.'"'.$sql.'"',$this->execute_stdout,$this->execute_rc); + + // Work out our message codes + $result = array(); + foreach ($this->execute_stdout as $line) + if (! preg_match('/^('.$this->msg_format.')\s+/',$line,$matches)) + array_push($result,$line); + elseif (! in_array($matches[1],$this->ignore_codes)) + array_push($this->_query_msg_codes,$matches[1]); + + // If we got a no data code + if (array_intersect($this->_query_msg_codes,$this->nodata_codes)) { + $result = array(); + $this->execute_rc = 0; + } + + return $result; + } + + public function list_columns($table, $like = NULL, $add_prefix = TRUE) { + // Quote the table name + $table = ($add_prefix === TRUE) ? $this->quote_table($table) : $table; + + if (is_string($like)) + // Search for column names + throw new Kohana_Exception('Like queries not implemented'); + else + // Find all column names + $result = $this->query(Database::SELECT, sprintf('SELECT * FROM SYSCAT.COLUMNS WHERE TABNAME=\'%s\'',$table), FALSE); + + $count = 0; + $columns = array(); + foreach ($result as $row) { + list($type, $length) = $this->_parse_type($row['TYPENAME']); + + $column = $this->datatype($type); + + $column['column_name'] = $row['COLNAME']; + $column['data_type'] = $type; + $column['is_nullable'] = ($row['NULLS'] == 'TRUE'); + $column['ordinal_position'] = ++$count; + + switch (strtolower($column['data_type'])) { + case 'float': + if (isset($length)) + list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length); + break; + + case 'int': + if (isset($length)) + // MySQL attribute + $column['display'] = $length; + break; + + case 'string': + switch ($column['data_type']) { + case 'binary': + case 'varbinary': + $column['character_maximum_length'] = $length; + break; + + case 'char': + case 'varchar': + $column['character_maximum_length'] = $length; + case 'text': + case 'tinytext': + case 'mediumtext': + case 'longtext': + $column['collation_name'] = $row['Collation']; + break; + + case 'enum': + case 'set': + $column['collation_name'] = $row['Collation']; + $column['options'] = explode('\',\'', substr($length, 1, -1)); + break; + } + + break; + } + + // TSM attributes + $column['comment'] = $row['REMARKS']; + $columns[$row['COLNAME']] = $column; + } + + return $columns; + } +} +?> diff --git a/application/classes/database/tsm/result.php b/application/classes/database/tsm/result.php index 2d06764..d1d3dbd 100644 --- a/application/classes/database/tsm/result.php +++ b/application/classes/database/tsm/result.php @@ -15,19 +15,32 @@ class Database_TSM_Result extends Database_Result { parent::__construct($result, $sql, $as_object, $params); $start = FALSE; - foreach ($result as $line) { - if (! trim($line)) { - if ($start) - $this->_internal_row++; + // Is this a DB2 result? + if (is_resource($result) AND function_exists('db2_fetch_object')) { + // If we are running a DB2 set command, we can return + if (preg_match('/^SET/',$sql)) + return; - continue; + while ($r = db2_fetch_object($result)) + $this->_rows[$this->_internal_row++] = (array)$r; + + // Is this an DSMADMC result? + } elseif (is_array($result)) + foreach ($result as $line) { + if (! trim($line)) { + if ($start) + $this->_internal_row++; + + continue; + } + + list($k,$v) = explode(':',$line,2); + + $this->_rows[$this->_internal_row][trim($k)] = trim($v); + $start = TRUE; } - - list($k,$v) = explode(':',$line,2); - - $this->_rows[$this->_internal_row][trim($k)] = trim($v); - $start = TRUE; - } + else + throw new Kohana_Exception('Unknown result :result',array(':result'=>$result)); $this->_total_rows = $this->_internal_row; $this->_internal_row = 0; diff --git a/application/classes/database/tsm/set.php b/application/classes/database/tsm/set.php new file mode 100644 index 0000000..55c6d1f --- /dev/null +++ b/application/classes/database/tsm/set.php @@ -0,0 +1,15 @@ + diff --git a/application/classes/model/auth/userdefault.php b/application/classes/model/auth/userdefault.php index e59b14c..0d67e6e 100644 --- a/application/classes/model/auth/userdefault.php +++ b/application/classes/model/auth/userdefault.php @@ -13,5 +13,12 @@ class Model_Auth_UserDefault extends Model_Auth_User { protected $_disable_wild_select = TRUE; protected $_disable_join_table_name = TRUE; protected $_disable_limit = TRUE; + + protected function _initialize() { + // Set out DB connection configuration. + $this->_db_group = Kohana::config('config.client_type'); + + parent::_initialize(); + } } ?> diff --git a/application/classes/model/domain.php b/application/classes/model/domain.php index f245204..f90945e 100644 --- a/application/classes/model/domain.php +++ b/application/classes/model/domain.php @@ -22,6 +22,16 @@ class Model_DOMAIN extends ORMTSM { 'SCHEDULE_CLIENT'=>array('foreign_key'=>'DOMAIN_NAME','far_key'=>'DOMAIN_NAME') ); + protected $_tsm = array( + 'db2'=>array( + '_has_many'=>array( + 'MGMTCLASS'=>array('foreign_key'=>'DOMAIN_NAME','far_key'=>'DOMAIN_NAME'), + 'NODE'=>array('foreign_key'=>'DOMAINNAME','far_key'=>'DOMAIN_NAME'), + 'SCHEDULE_CLIENT'=>array('foreign_key'=>'DOMAIN_NAME','far_key'=>'DOMAIN_NAME') + ), + ), + ); + // Pools used by a domain. private $pools = array(); diff --git a/application/classes/model/filespace.php b/application/classes/model/filespace.php index 9e83a1d..2139fd5 100644 --- a/application/classes/model/filespace.php +++ b/application/classes/model/filespace.php @@ -17,9 +17,29 @@ class Model_FILESPACE extends ORMTSM { 'FILESPACE_NAME'=>'ASC', ); + protected $_has_one = array( + 'NODE'=>array('foreign_key'=>'NODE_NAME','far_key'=>'NODE_NAME'), + ); + protected $_has_many = array( - 'VOLUMEUSAGE'=>array('foreign_key'=>array('NODE_NAME'=>'NODE_NAME','FILESPACE_NAME'=>'FILESPACE_NAME')), - 'OCCUPANCY'=>array('foreign_key'=>array('NODE_NAME'=>'NODE_NAME','FILESPACE_NAME'=>'FILESPACE_NAME')), + ); + + protected $_tsm = array( + 'db2'=>array( + '_primary_key'=>'FSNAME', + '_sorting'=>array( + 'NODEID'=>'ASC', + 'FSNAME'=>'ASC', + ), + '_has_one'=>array( + 'NODE'=>array('foreign_key'=>'NODEID','far_key'=>'NODEID'), + ), + 'translate'=>array( + 'FILESPACE_NAME'=>'FSNAME', + 'BACKUP_END'=>'BACKSTART', + 'PCT_UTIL'=>NULL, + ), + ), ); protected $_display_filters = array( @@ -29,16 +49,17 @@ class Model_FILESPACE extends ORMTSM { ); public function utilsation() { - return $this->CAPACITY * ($this->PCT_UTIL/100); + return $this->CAPACITY*($this->PCT_UTIL/100); } // $dtype must be Bkup or Arch public function storagepools($dtype) { $pool = array(); - foreach ($this->OCCUPANCY + foreach ($this->NODE->OCC ->select('STGPOOL_NAME') ->where('TYPE','=',$dtype) + ->and_where('FILESPACE_NAME','=',$this->FILESPACE_NAME) ->group_by('STGPOOL_NAME') ->order_by('STGPOOL_NAME') ->find_all() as $oo) @@ -49,11 +70,19 @@ class Model_FILESPACE extends ORMTSM { } public function pool_logical_util($pool,$btype) { - return $this->OCCUPANCY->where('STGPOOL_NAME','=',$pool)->where('TYPE','=',$btype)->find()->LOGICAL_MB; + return $this->NODE->OCC + ->where('STGPOOL_NAME','=',$pool) + ->and_where('TYPE','=',$btype) + ->and_where('FILESPACE_NAME','=',$this->FILESPACE_NAME) + ->find()->LOGICAL_MB; } public function pool_numvols($pool,$ctype) { - return $this->VOLUMEUSAGE->where('STGPOOL_NAME','=',$pool)->where('COPY_TYPE','=',$ctype)->find_all()->count(); + return $this->NODE->VOLUMEUSAGE + ->where('STGPOOL_NAME','=',$pool) + ->and_where('COPY_TYPE','=',$ctype) + ->and_where('FILESPACE_NAME','=',$this->FILESPACE_NAME) + ->find_all()->count(); } } ?> diff --git a/application/classes/model/node.php b/application/classes/model/node.php index 56f621c..59887ea 100644 --- a/application/classes/model/node.php +++ b/application/classes/model/node.php @@ -27,6 +27,61 @@ class Model_NODE extends ORMTSM { 'CLIENTOPT'=>array('foreign_key'=>'OPTIONSET_NAME','far_key'=>'OPTION_SET'), 'SUMMARY'=>array('foreign_key'=>'ENTITY','far_key'=>'NODE_NAME'), 'EVENT'=>array('foreign_key'=>'NODE_NAME','far_key'=>'NODE_NAME'), + 'OCC'=>array('foreign_key'=>'NODE_NAME','far_key'=>'NODE_NAME'), + ); + + protected $_tsm = array( + 'db2'=>array( + '_primary_key'=>'NODENAME', + '_sorting'=>array( + 'NODENAME'=>'ASC', + ), + '_has_one'=>array( + 'DOMAIN'=>array('foreign_key'=>'DOMAIN_NAME','far_key'=>'DOMAINNAME'), + ), + '_has_many'=>array( + 'FILESPACE'=>array('foreign_key'=>'NODEID','far_key'=>'NODEID'), + 'VOLUMEUSAGE'=>array('foreign_key'=>'NODE_NAME','far_key'=>'NODENAME'), + 'ASSOCIATION'=>array('foreign_key'=>'NODE_NAME','far_key'=>'NODENAME'), + 'MGMTCLASS'=>array('foreign_key'=>'DOMAIN_NAME','far_key'=>'DOMAIN_NAME'), + 'CLIENTOPT'=>array('foreign_key'=>'OPTIONSET_NAME','far_key'=>'OPTIONSET'), + 'SUMMARY'=>array('foreign_key'=>'ENTITY','far_key'=>'NODENAME'), + 'EVENT'=>array('foreign_key'=>'NODE_NAME','far_key'=>'NODENAME'), + 'OCC'=>array('foreign_key'=>'NODE_NAME','far_key'=>'NODENAME'), + ), + 'translate'=>array( + 'NODE_NAME'=>'NODENAME', + 'CLIENT_VERSION'=>'CLNTVERSION', + 'CLIENT_RELEASE'=>'CLNTRELEASE', + 'CLIENT_LEVEL'=>'CLNTLEVEL', + 'CLIENT_SUBLEVEL'=>'CLNTSUBLEVEL', + 'PLATFORM_NAME'=>'OSNAME', + 'CLIENT_OS_LEVEL'=>'CLNTOSLEVEL', + 'LASTACC_TIME'=>'LACDATE', + 'REG_TIME'=>'REGDATE', + 'PWSET_TIME'=>'PWSETDATE', + 'LASTSESS_SENT'=>'SESSSENT', + 'LASTSESS_RECVD'=>'SESSRECVD', + 'LASTSESS_DURATION'=>'SESSDURATION', + 'LASTSESS_IDLEWAIT'=>'SESSIDLEWAIT', + 'LASTSESS_COMMWAIT'=>'SESSCOMMWAIT', + 'LASTSESS_MEDIAWAIT'=>'SESSMEDIAWAIT', + 'TCP_ADDRESS'=>NULL, + 'EMAIL_ADDRESS'=>NULL, + 'REG_ADMIN'=>'REGISTRAR', + 'PASSEXP'=>NULL, + 'INVALID_PW_COUNT'=>'INVPWCOUNT', + 'LOCKED'=>'LOCKSTATE', + 'DOMAIN_NAME'=>'DOMAINNAME', + 'OPTION_SET'=>'OPTIONSET', + 'COLLOCGROUP_NAME'=>NULL, + 'COMPRESSION'=>'COMPRESS', + 'ARCHDELETE'=>'ARCHDEL', + 'BACKDELETE'=>'BACKDEL', + 'KEEP_MP'=>NULL, + 'MAX_MP_ALLOWED'=>'MAXNUMMP', + ), + ), ); protected $_display_filters = array( @@ -196,10 +251,9 @@ class Model_NODE extends ORMTSM { public function getStorageModeFiles($dtype,$ptype,$spo='') { $count = 0; - foreach ($this->FILESPACE->find_all() as $fo) - foreach ($fo->OCCUPANCY->where('TYPE','=',$dtype)->find_all() as $oa) - if ((! $spo OR $oa->STGPOOL_NAME == $spo) AND $oa->STGPOOL->POOLTYPE == $ptype) - $count += $oa->NUM_FILES; + foreach ($this->OCC->where('TYPE','=',$dtype)->find_all() as $oa) + if ((! $spo OR $oa->STGPOOL_NAME == $spo) AND $oa->STGPOOL->POOLTYPE == $ptype) + $count += $oa->NUM_FILES; return $count; } @@ -219,10 +273,9 @@ class Model_NODE extends ORMTSM { public function getStorageModeData($dtype,$ptype,$spo='') { $count = 0; - foreach ($this->FILESPACE->find_all() as $fo) - foreach ($fo->OCCUPANCY->where('TYPE','=',$dtype)->find_all() as $oa) - if ((! $spo OR $oa->STGPOOL_NAME == $spo) AND $oa->STGPOOL->POOLTYPE == $ptype) - $count += $oa->LOGICAL_MB; + foreach ($this->OCC->where('TYPE','=',$dtype)->find_all() as $oa) + if ((! $spo OR $oa->STGPOOL_NAME == $spo) AND $oa->STGPOOL->POOLTYPE == $ptype) + $count += $oa->LOGICAL_MB; return $count; } diff --git a/application/classes/model/occupancy.php b/application/classes/model/occ.php similarity index 83% rename from application/classes/model/occupancy.php rename to application/classes/model/occ.php index a69b027..1e636e8 100644 --- a/application/classes/model/occupancy.php +++ b/application/classes/model/occ.php @@ -8,8 +8,9 @@ * @author Deon George * @copyright (c) 2010 phpTSMadmin Development Team * @license http://phptsmadmin.sf.net/license.html + * @node This has been renamed to OCC from OCCUPANCY, as DB2/FILESPACES has an OCCUPANCY column */ -class Model_OCCUPANCY extends ORMTSM { +class Model_OCC extends ORMTSM { protected $_table_name = 'OCCUPANCY'; protected $_primary_key = 'NODE_NAME'; // We need a primary key to detect that the object is loaded. protected $_sorting = array( diff --git a/application/classes/ormtsm.php b/application/classes/ormtsm.php index 4927c22..f04b383 100644 --- a/application/classes/ormtsm.php +++ b/application/classes/ormtsm.php @@ -25,6 +25,39 @@ class ORMTSM extends ORM { protected $_formated = FALSE; protected $_formats = array(); + protected $_tsm = array(); + + /** + * + * Configuration to enable PLA to work with DSMADMC & DB2 connections + * This is required, because the schema is different between each + * connection? + */ + protected function _initialize() { + // Set out DB connection configuration. + $this->_db_group = Kohana::config('config.client_type'); + + // Adjustments for DSMADMC or DB2 connections + if (array_key_exists(Kohana::config('config.client_type'),$this->_tsm)) + foreach ($this->_tsm[Kohana::config('config.client_type')] as $k => $v) + if (preg_match('/^_/',$k)) + $this->{$k} = $v; + + if ($this->_db_group == 'db2') + $this->_disable_join_table_name = FALSE; + + parent::_initialize(); + } + + public function __get($column) { + // Get a substited column name - need for DB2/DSMADMC schema differences + if (isset($this->_tsm[Kohana::config('config.client_type')]['translate']) AND array_key_exists($column,$this->_tsm[Kohana::config('config.client_type')]['translate'])) { + return is_null($c=$this->_tsm[Kohana::config('config.client_type')]['translate'][$column]) ? NULL : parent::__get($c); +} + else + return parent::__get($column); + } + public function find() { // Check if we can preload our data and havent already done it if ($time = $this->isCacheable() AND is_null(Cache::instance()->get($cache_key = 'PRELOAD:'.$this->_table_name))) { diff --git a/application/classes/systemmessage.php b/application/classes/systemmessage.php index 5324687..e2b2125 100644 --- a/application/classes/systemmessage.php +++ b/application/classes/systemmessage.php @@ -14,7 +14,7 @@ class SystemMessage extends lnApp_SystemMessage { static public function TSM_Error($error,$sql) { SystemMessage::add(array( 'title'=>_('Error while talking to TSM'), - 'body'=>_('Running SQL').': '.$sql.'

'.implode('
',$error), + 'body'=>_('Running SQL').': '.$sql.'

'.(is_array($error) ? implode('
',$error) : $error), 'type'=>'error', )); } diff --git a/application/config/config.php b/application/config/config.php index 635d7d2..d9f4ae7 100644 --- a/application/config/config.php +++ b/application/config/config.php @@ -13,6 +13,7 @@ return array( 'cache_type' => 'file', 'client' => '/opt/tivoli/tsm/client/ba/bin/dsmadmc', + 'client_type' => 'db2', 'client_errorlogname' => '/tmp/pta-tsm-errorlog.log', 'date_format' => 'd-m-Y', 'tsmdatatypes' => array('Bkup'=>'BACKUP','Arch'=>'ARCHIVE'), diff --git a/application/config/database.php b/application/config/database.php index 9ccc8c0..fd4d24d 100644 --- a/application/config/database.php +++ b/application/config/database.php @@ -13,9 +13,9 @@ return array ( - 'default' => array + 'dsmadmc' => array ( - 'type' => 'tsm', + 'type' => 'tsm_dsmadmc', 'connection' => array( /** * The following options are available for MySQL: @@ -32,7 +32,71 @@ return array 'username' => FALSE, 'password' => FALSE, //'persistent' => FALSE, // Unused - 'database' => 'local', + ), + 'table_prefix' => '', + 'charset' => 'utf8', + 'caching' => TRUE, + 'cache' => array( + 'ASSOCIATIONS' => 1200, + 'AR_COPYGROUPS' => 1200, + 'BU_COPYGROUPS' => 1200, + 'CLIENT_SCHEDULES' => 1200, + 'CLIENTOPTS' => 1200, + 'DEVCLASSES' => 1200, + 'DOMAINS' => 1200, + 'DRIVES' => 1200, + 'EVENTS' => 1200, + 'FILESPACES' => 1200, + 'LIBRARIES' => 1200, + 'LIBVOLUMES' => 1200, + 'MEDIA' => 1200, + 'MGMTCLASSES' => 1200, + 'NODES' => 1200, + 'OCCUPANCY' => 1200, + 'PATHS' => 1200, + 'SCHEMA' => 604800, + 'STGPOOLS' => 1200, + 'SUMMARY' => 180, + 'VOLHISTORY' => 1200, + 'VOLUMES' => 1200, + 'VOLUMEUSAGE' => 1200, + ), + 'cachepreload' => array( + 'DEVCLASSES' => 1200, + 'DOMAINS' => 1200, + 'DRIVES' => 1200, + 'LIBVOLUMES' => 1200, + 'LIBRARIES' => 1200, + 'MEDIA' => 1200, + 'NODES' => 1200, + 'STGPOOLS' => 1200, + 'VOLHISTORY' => 1200, + 'VOLUMES' => 1200, + ), + 'profiling' => TRUE, + ), + + 'db2' => array + ( + 'type' => 'tsm_db2', + 'connection' => array( + /** + * The following options are available for DB2: + * + * string hostname server hostname, or socket + * string database database name + * string username database username + * string password database password + * boolean persistent use persistent connections? + * + * Ports and sockets may be appended to the hostname. + */ + 'hostname' => 'localhost', + 'port' => '60000', + 'username' => FALSE, + 'password' => FALSE, + 'persistent' => FALSE, + 'database' => 'TSMDB1', ), 'table_prefix' => '', 'charset' => 'utf8', diff --git a/application/config/userguide.php b/application/config/userguide.php index f5c3179..60b4839 100644 --- a/application/config/userguide.php +++ b/application/config/userguide.php @@ -20,16 +20,16 @@ return array( 'copyright' => '© 2008–2011 phpTSMadmin Development Team', ), 'cache' => array( - 'enabled' => FALSE, + 'enabled' => TRUE, ), 'database' => array( - 'enabled' => FALSE, + 'enabled' => TRUE, ), 'kohana' => array( - 'enabled' => FALSE, + 'enabled' => TRUE, ), 'orm' => array( - 'enabled' => FALSE, + 'enabled' => TRUE, ), 'userguide' => array( 'enabled' => FALSE, diff --git a/application/guide/pta/db2.md b/application/guide/pta/db2.md new file mode 100644 index 0000000..e52cfbd --- /dev/null +++ b/application/guide/pta/db2.md @@ -0,0 +1,10 @@ +-1. extract /.../COI/PackageSteps/DB2/FILES/ese/db2/linuxamd64/FILES/BASE_CLIENT_9.7.0.2_linuxamd64_x86_64.tar.gz +1. extract /.../COI/PackageSteps/DB2/FILES/ese/db2/linuxamd64/FILES/APPLICATION_DEVELOPMENT_TOOLS_9.7.0.2_linuxamd64_x86_64.tar.gz +2. link /opt/tivoli/tsm/db2/includes to extracted developer files +3. yum install gcc --enabler=updates +4. install php-pear (from webtatic) +5. install php-devel (from webtatic) +6. pecl install ibm_db2 (as root) +7. create php.d/db2.ini +8. run db2set DB2COMM=tcpip as tsminst1 +9. open TCP port to enable remote connections db2 UPDATE DBM CFG USING SVCENAME DB2_tsminst1 diff --git a/application/guide/pta/menu.md b/application/guide/pta/menu.md index 22f16f4..22a4392 100644 --- a/application/guide/pta/menu.md +++ b/application/guide/pta/menu.md @@ -1,2 +1,3 @@ ## [PTA]() - [Installation](install) +- [DB2](db2)