diff --git a/app/plugins/CoreServer/src/CoreServerPlugin.php b/app/plugins/CoreServer/src/CoreServerPlugin.php index 310c7ccef..ef7816d3e 100644 --- a/app/plugins/CoreServer/src/CoreServerPlugin.php +++ b/app/plugins/CoreServer/src/CoreServerPlugin.php @@ -33,7 +33,7 @@ public function bootstrap(PluginApplicationInterface $app): void $oracleEnabled = \Cake\Core\Configure::read('registry.database.oracle.enable'); if($oracleEnabled === true) { - $app->addPlugin(\CakeDC\OracleDriver\Plugin::class, ['bootstrap' => true]); + $app->addPlugin(\Portal89\OracleDriver\Plugin::class, ['bootstrap' => true]); } } catch(\Error $e) { diff --git a/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php b/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php index a5ad9c46f..d84242fb2 100644 --- a/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php +++ b/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php @@ -44,6 +44,7 @@ class SqlServersTable extends Table { use \App\Lib\Traits\PrimaryLinkTrait; use \App\Lib\Traits\TableMetaTrait; use \App\Lib\Traits\ValidationTrait; + use \App\Lib\Traits\LabeledLogTrait; /** * Perform Cake Model initialization. @@ -162,9 +163,11 @@ public function connect(int $serverId, string $name): bool { // We don't test that the plugin is available here, an error should be thrown // when we try to connect. - $dbconfig['className'] = 'CakeDC\OracleDriver\Database\OracleConnection'; - $dbconfig['driver'] = 'CakeDC\OracleDriver\Database\Driver\OracleOCI'; // For OCI8 + $dbconfig['className'] = 'Portal89\OracleDriver\Database\OracleConnection'; + $dbconfig['driver'] = 'Portal89\OracleDriver\Database\Driver\OracleOCI'; // For OCI8 $dbconfig['quoteIdentifiers'] = true; + $dbconfig['init'] = []; + $dbconfig['flags'] = []; // Use 'CakeDC\\OracleDriver\\Database\\Driver\\OraclePDO' for PDO_OCI, but CakeDC // recommends OCI8 @@ -190,6 +193,44 @@ public function connect(int $serverId, string $name): bool { return true; } + /** + * Try to connect to the configured connection and run a minimal probe. + * + * @param string $name ConnectionManager name (eg: 'server42') + * @return bool True if the probe succeeds, false otherwise + */ + public function pingDb(string $name = 'my_temp_connection'): bool + { + try { + $conn = ConnectionManager::get($name); + // Force immediate connection attempt + $conn->connect(); + + // Use an engine-appropriate lightweight query + $sql = ($conn->getDriver() instanceof \Portal89\OracleDriver\Database\Driver\OracleOCI) + ? 'SELECT 1 FROM DUAL' + : 'SELECT 1'; + + $val = $conn->execute($sql)->fetchColumn(0); + $ok = ((int)$val === 1); + + if (!$ok) { + $this->llog( + 'debug', + sprintf("DB ping for connection '%s' returned unexpected value: %s", $name, var_export($val, true)) + ); + } + + return $ok; + } catch (\Throwable $e) { + $this->llog( + 'error', + sprintf("DB ping failed for connection '%s': %s", $name, $e->getMessage()) + ); + return false; + } + } + /** * Application Rule to determine if Oracle is enabled (if selected). * diff --git a/app/resources/locales/en_US/operation.po b/app/resources/locales/en_US/operation.po index bfd24d042..0b7da9324 100644 --- a/app/resources/locales/en_US/operation.po +++ b/app/resources/locales/en_US/operation.po @@ -153,6 +153,9 @@ msgstr "Configure {0}" msgid "configure.plugin" msgstr "Configure Plugin" +msgid "connection.test" +msgstr "Test Connection" + msgid "continue" msgstr "Continue" @@ -327,6 +330,15 @@ msgstr "Resend" msgid "resume" msgstr "Resume" +msgid "rs.test.ok" +msgstr "Connection OK" + +msgid "rs.test.error" +msgstr "Connection Error" + +msgid "rs.test.na" +msgstr "Connection N/A" + msgid "save" msgstr "Save" diff --git a/app/src/Controller/ServersController.php b/app/src/Controller/ServersController.php index 335d0df83..f91752086 100644 --- a/app/src/Controller/ServersController.php +++ b/app/src/Controller/ServersController.php @@ -30,6 +30,7 @@ namespace App\Controller; // XXX not doing anything with Log yet +use App\Lib\Util\StringUtilities; use Cake\Log\Log; class ServersController extends StandardPluggableController { @@ -38,4 +39,65 @@ class ServersController extends StandardPluggableController { 'Servers.description' => 'asc' ] ]; + + + public function testconnection(string $id) { + // We basically implement a redirect here to facilitate view rendering. + // (We only need to map into the plugin on actual link click, instead of + // potentially many times on an index view for links that may not be used.) + + /** var Cake\ORM\Table $table */ + $table = $this->getCurrentTable(); + + $serverId = $this->request->getParam('pass')[0]; + $serverObj = $table->findById($serverId) + ->firstOrFail(); + + $pluginTable = $this->getTableLocator()->get($serverObj->plugin); + $pluginObj = $pluginTable->find() + ->where([StringUtilities::tableToForeignKey($table) => $serverId]) + ->firstOrFail(); + + try { + if (method_exists($pluginTable, 'connect')) { + // Set the Connection Manager config + $connection = $pluginTable->connect((int)$serverId, 'server' . $serverId); + + if (method_exists($pluginTable, 'pingDb')) { + // This is database. We need to ping the database to be sure + $ok = $pluginTable->pingDb('server' . (int)$serverId); + if ($ok) { + $this->Flash->success(__d('operation', 'rs.test.ok')); + Log::debug("Successfully connected to database server " . $serverId); + } else { + $this->Flash->error(__d('operation', 'rs.test.error')); + Log::error("Failed to connect to database server " . $serverId . ": Database ping failed"); + } + } else { + if ($connection) { + $this->Flash->success(__d('operation', 'rs.test.ok')); + Log::debug("Successfully connected to server " . $serverId); + } else { + $this->Flash->error(__d('operation', 'rs.test.error')); + Log::error("Failed to connect to server " . $serverId . ": Connection failed"); + } + } + + } else { + $this->Flash->error(__d('operation', 'rs.test.na')); + } + } catch (\Exception $e) { + $this->Flash->error($e->getMessage()); + } + + $redirect = [ + 'controller' => 'Servers', + 'action' => 'index', + '?' => [ + 'co_id' => $serverObj->co_id + ] + ]; + + return $this->redirect($redirect); + } } \ No newline at end of file diff --git a/app/src/Model/Table/ServersTable.php b/app/src/Model/Table/ServersTable.php index 678b20bb1..bbcc26a19 100644 --- a/app/src/Model/Table/ServersTable.php +++ b/app/src/Model/Table/ServersTable.php @@ -79,6 +79,7 @@ public function initialize(array $config): void { $this->setDisplayField('description'); $this->setPrimaryLink('co_id'); + $this->setAllowLookupPrimaryLink(['testconnection']); $this->setRequiresCO(true); $this->setAutoViewVars([ @@ -96,6 +97,7 @@ public function initialize(array $config): void { // Actions that operate over an entity (ie: require an $id) 'entity' => [ 'configure' => ['platformAdmin', 'coAdmin'], + 'testconnection' => ['platformAdmin', 'coAdmin'], 'delete' => ['platformAdmin', 'coAdmin'], 'edit' => ['platformAdmin', 'coAdmin'], 'view' => ['platformAdmin', 'coAdmin'] diff --git a/app/templates/Servers/columns.inc b/app/templates/Servers/columns.inc index 19e4ae6fa..36462d44d 100644 --- a/app/templates/Servers/columns.inc +++ b/app/templates/Servers/columns.inc @@ -47,6 +47,11 @@ $rowActions = [ 'action' => 'configure', 'label' => __d('operation', 'configure.plugin'), 'icon' => 'electrical_services' + ], + [ + 'action' => 'testconnection', + 'label' => __d('operation', 'connection.test'), + 'icon' => 'fork_right' ] ];