Skip to content

Commit

Permalink
Merge pull request #24 from TiTidom-RC/dev
Browse files Browse the repository at this point in the history
v0.4.5
  • Loading branch information
TiTidom-RC authored Sep 17, 2024
2 parents 5b01aeb + f29331f commit ce4a8ef
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 79 deletions.
124 changes: 75 additions & 49 deletions core/class/sshmanager.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,25 @@
}

class SSHConnectException extends \RuntimeException {
private $_log; // log of the SSH2 object

public function __construct($message, $log = '') {
}

class SSHException extends \RuntimeException {
private $log;
private $lastError;

public function __construct($message, $lastError = '', $log = '') {
parent::__construct($message);
$this->_log = $log;
$this->log = $log;
$this->lastError = $lastError;
}

public function getLastError() {
return $this->lastError;
}

public function getLog() {
return $this->_log;
return $this->log;
}
}

Expand Down Expand Up @@ -148,7 +158,7 @@ public static function checkConnection($hostId) {
* @param array|string $commands
* @return array|string $results
*/
public static function executeCmds($hostId, $commands) {
public static function executeCmds($hostId, $commands, $cmdName = '') {
/** @var sshmanager */
$sshmanager = eqLogic::byId($hostId);
if (!is_object($sshmanager)) {
Expand All @@ -163,8 +173,8 @@ public static function executeCmds($hostId, $commands) {
}
return $results;
} elseif (is_string($commands)) {
log::add(__CLASS__, 'debug', "[{$sshmanager->getName()}] Cmd :: " . $commands);
return $sshmanager->internalExecuteCmd($commands);
// log::add(__CLASS__, 'debug', "[" . $sshmanager->getName() . "] " . (!empty($cmdName) ? $cmdName : "Cmd") . " :: " . $commands);
return $sshmanager->internalExecuteCmd($commands, $cmdName);
} else {
throw new Exception('Invalid command type');
}
Expand Down Expand Up @@ -255,7 +265,6 @@ private function getAuthenticationData() {
log::add(__CLASS__, 'error', '[' . $this->getName() . '] ' . $ex->getMessage());
throw $ex;
}

break;
case self::AUTH_METHOD_AGENT:
//TODO: check if agent auth could be usefull? we only need to uncomment the following line and remove the exception
Expand All @@ -275,6 +284,10 @@ private function internalSendFile(string $localFile, string $remoteFile) {
$sftp = new SFTP($host, $port, $timeout);
if ($sftp->login($username, $keyOrpassword)) {
log::add(__CLASS__, 'debug', "[{$this->getName()}] Send file to {$host}");
// TODO Check if the file exists before sending it and add try catch block to handle exceptions :
// @throws \UnexpectedValueException — on receipt of unexpected packets
// @throws \BadFunctionCallException - if you're uploading via a callback and the callback function is invalid
// @throws FileNotFoundException - if you're uploading via a file and the file doesn't exist
return $sftp->put($remoteFile, $localFile, SFTP::SOURCE_LOCAL_FILE);
}
log::add(__CLASS__, 'debug', "[{$this->getName()}] login failed, could not put file {$remoteFile}");
Expand All @@ -288,6 +301,8 @@ private function internalGetFile(string $remoteFile, string $localFile) {
$sftp = new SFTP($host, $port, $timeout);
if ($sftp->login($username, $keyOrpassword)) {
log::add(__CLASS__, 'debug', "[{$this->getName()}] Get file from {$host}");
// TODO Check if the file exists before getting it and add try catch block to handle exceptions :
// @throws \UnexpectedValueException — on receipt of unexpected packets
return $sftp->get($remoteFile, $localFile);
}
log::add(__CLASS__, 'debug', "[{$this->getName()}] Login failed, could not get file {$remoteFile}");
Expand All @@ -306,34 +321,31 @@ private function getSSH2Client() {
[$host, $port, $timeout] = $this->getConnectionData();
[$username, $keyOrpassword] = $this->getAuthenticationData();
log::add(__CLASS__, 'debug', "[{$eqLogicName}] >>>> Creating SSH2 client (pid: {$pid}) for eqLogic {$eqLogicID} to {$host}");
$ssh2 = new SSH2($host, $port, $timeout);

try {
$ssh2 = new SSH2($host, $port, $timeout);
} catch (Exception $e) {
log::add(__CLASS__, 'error', "[{$eqLogicName}] >>>> SSH2Client Exception :: " . $e->getMessage());
throw $e;
}

try {
if (!$ssh2->login($username, $keyOrpassword)) {
throw new SSHConnectException("[{$eqLogicName}] >>>> Login failed for {$username}@{$host}:{$port}; please check username and password or ssh key.", $ssh2->getLog());
log::add(__CLASS__, 'error', "[{$eqLogicName}] >>>> Login failed for {$username}@{$host}:{$port}");
throw new RuntimeException("[{$eqLogicName}] >>>> Login failed for {$username}@{$host}:{$port}; please check username and password or ssh key.");
}

if (!$ssh2->isConnected()) {
throw new SSHConnectException("[{$eqLogicName}] >>>> Connection failed :: " . $ssh2->getLastError(), $ssh2->getLog());
}

if (!$ssh2->isAuthenticated()) {
throw new SSHConnectException("[{$eqLogicName}] >>>> Authentication failed :: " . $ssh2->getLastError(), $ssh2->getLog());
}
} catch (SSHConnectException $ex) {
} catch (RuntimeException $ex) {
log::add(__CLASS__, 'error', '[' . $eqLogicName . '] Login Exception :: ' . $ex->getMessage());
log::add(__CLASS__, 'debug', '[' . $eqLogicName . '] Login Exception log :: ' . $ex->getLog());
throw $ex;
} catch (\Throwable $th) {
log::add(__CLASS__, 'error', "[{$eqLogicName}] General SSH2Client Exception :: " . $th->getMessage());
throw $th;
}

log::add(__CLASS__, 'debug', "[{$eqLogicName}] >>>> Connected and authenticated");

sshmanager::$_ssh2_client[$eqLogicID] = $ssh2;

} else {
log::add(__CLASS__, 'debug', "[" . $eqLogicName . "] >>>> Existing SSH2 client (pid: {$pid}) for eqLogic {$eqLogicID}");
// log::add(__CLASS__, 'debug', "[" . $eqLogicName . "] >>>> Existing SSH2 client (pid: {$pid}) for eqLogic {$eqLogicID}");
}
return sshmanager::$_ssh2_client[$eqLogicID];
}
Expand All @@ -342,55 +354,69 @@ private function internalCheckConnection() {
try {
$ssh2 = $this->getSSH2Client();
return $ssh2->isConnected() && $ssh2->isAuthenticated();
} catch (SSHConnectException $ex) {
log::add(__CLASS__, 'error', "[{$this->getName()}] Exception :: {$ex->getMessage()}");
log::add(__CLASS__, 'debug', "[{$this->getName()}] Exception Log :: {$ex->getLog()}");
} catch (RuntimeException $ex) {
// log::add(__CLASS__, 'error', "[{$this->getName()}] CheckConnection Exception :: {$ex->getMessage()}");
return false;
} catch (\Throwable $th) {
log::add(__CLASS__, 'error', "[{$this->getName()}] General CheckConnection Exception :: " . $th->getMessage());
return false;
}
}

private function internalExecuteCmd(string $command) {
private function internalExecuteCmd(string $command, $cmdName = '') {
try {
$ssh2 = $this->getSSH2Client();
} catch (SSHConnectException $ex) {
log::add(__CLASS__, 'error', "[{$this->getName()}] Exception :: {$ex->getMessage()}");
log::add(__CLASS__, 'debug', "[{$this->getName()}] Exception Log :: {$ex->getLog()}");
throw $ex;
return false;
} catch (RuntimeException $ex) {
log::add(__CLASS__, 'error', "[{$this->getName()}] ExecCmd RunTimeEx :: {$ex->getMessage()}");
throw new SSHException("ExecCmd RunTimeEx :: {$ex->getMessage()}", $ssh2->getLastError(), $ssh2->getLog());
} catch (\Throwable $th) {
log::add(__CLASS__, 'error', "[{$this->getName()}] General ExecuteCmd Exception :: " . $th->getMessage());
log::add(__CLASS__, 'error', "[{$this->getName()}] ExecCmd General Exception :: " . $th->getMessage());
throw $th;
return false;
}

$result = '';

try {
$result = $ssh2->exec($command);

if (!$ssh2->isConnected()) {
log::add(__CLASS__, 'debug', '[' . $this->getName() . '] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' :: ' . str_replace("\r\n", "\\r\\n", $command));
log::add(__CLASS__, 'error', '[' . $this->getName() . '] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' :: Disconnected');
$result = '';
$ssh2->disconnect();
return $result;
}

if ($ssh2->isTimeout()) {
// log::add(__CLASS__, 'debug', '[' . $this->getName() . '] Cmd :: ' . str_replace("\r\n", "\\r\\n", $command));
log::add(__CLASS__, 'error', '[' . $this->getName() . '] Cmd Timeout :: ' . $ssh2->getLastError());
$ssh2->reset();
log::add(__CLASS__, 'debug', '[' . $this->getName() . '] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' :: ' . str_replace("\r\n", "\\r\\n", $command));
log::add(__CLASS__, 'error', '[' . $this->getName() . '] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' :: Timeout');
$result = '';
throw new SSHConnectException("[{$this->getName()}] Cmd Timeout :: " . $ssh2->getLastError(), $ssh2->getLog());
$ssh2->reset();
}

if (!empty($result)) {
$result = trim($result);
//TODO: '\n' should be escaped from $result before logging
// log::add(__CLASS__, 'debug', '[' . $this->getName() . '] Cmd :: ' . str_replace("\r\n", "\\r\\n", $command));
log::add(__CLASS__, 'debug', '[' . $this->getName() . '] Cmd Result :: ' . str_replace("\r\n", "\\r\\n", $result));
} else {
// log::add(__CLASS__, 'debug', '[' . $this->getName() . '] Cmd :: ' . str_replace("\r\n", "\\r\\n", $command));
log::add(__CLASS__, 'debug', '[' . $this->getName() . '] Cmd Result :: VIDE');
log::add(__CLASS__, 'debug', '[' . $this->getName() . '] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' :: ' . str_replace("\r\n", "\\r\\n", $command));
log::add(__CLASS__, 'debug', '[' . $this->getName() . '] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' Result :: ' . $result);
}
return $result;
} catch (Exception $e) {
// log::add(__CLASS__, 'debug', '[' . $this->getName() . '] Cmd :: ' . str_replace("\r\n", "\\r\\n", $command));
log::add(__CLASS__, 'error', '[' . $this->getName() . '] Cmd Exception :: ' . $e->getMessage());
// TODO est qu'il ne faut pas faire un SSH disconnect ici ?
return false;
} catch (RuntimeException $ex) {
log::add(__CLASS__, 'debug', '[' . $this->getName() . '] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' :: ' . str_replace("\r\n", "\\r\\n", $command));
log::add(__CLASS__, 'error', '[' . $this->getName() . '] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' RunTimeEx :: ' . $ex->getMessage());

log::add(__CLASS__, 'debug', '['. $this->getName() .'][SSH-EXEC] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' RuntimeEx LastError :: ' . $ssh2->getLastError());
log::add(__CLASS__, 'debug', '['. $this->getName() .'][SSH-EXEC] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' RuntimeEx Logs ::' . "\r\n" . $ssh2->getLog());

throw new SSHException($ex->getMessage(), $ssh2->getLastError(), $ssh2->getLog());

} catch (\Throwable $th) {
log::add(__CLASS__, 'debug', '[' . $this->getName() . '] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' :: ' . str_replace("\r\n", "\\r\\n", $command));
log::add(__CLASS__, 'error', '[' . $this->getName() . '] ' . (!empty($cmdName) ? $cmdName : 'Cmd') . ' Exception :: ' . $th->getMessage());

throw $th;
}

return $result;
}

public function preInsert() {
Expand Down
21 changes: 16 additions & 5 deletions data/commands/commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,41 @@
"name": "List Running Services",
"short_description": "List Running Services",
"description": "Retourne la liste des services dont l'état est 'running'",
"command": "sudo systemctl list-units --type=service --state=running --plain --no-legend | awk '{ print $1 }' | awk 'BEGIN {RS=\"\"}{gsub(/\\n/,\",\",$0); print $0}'"
"command": "sudo systemctl list-units --type=service --state=running --plain --no-legend | awk '{ print $1 }' | awk 'BEGIN {RS=\"\"}{gsub(/\\n/,\",\",$0); print $0}'",
"type": "info",
"subtype": "string"
},
"nb_sys_updates": {
"name": "Sys Updates Number",
"short_description": "Number of System Updates",
"description": "Retourne le nombre de mises à jour en attente sur le système distant",
"command": "sudo apt update -qq 2>/dev/null | cut -d'.' -f1"
"command": "sudo apt update -qq 2>/dev/null | cut -d'.' -f1",
"type": "info",
"subtype": "string"

},
"list_sys_updates": {
"name": "List System Updates",
"short_description": "List Packages to update",
"description": "Retourne la liste des paquets systèmes en attente de mise à jour sur le système distant",
"command": "sudo apt-get -qq update; sudo apt list --upgradable -qq 2>/dev/null | cut -d'/' -f1 | awk 'BEGIN {RS=\"\"}{gsub(/\\n/,\",\",$0); print $0}'"
"command": "sudo apt-get -qq update; sudo apt list --upgradable -qq 2>/dev/null | cut -d'/' -f1 | awk 'BEGIN {RS=\"\"}{gsub(/\\n/,\",\",$0); print $0}'",
"type": "info",
"subtype": "string"
},
"restart_service": {
"name": "Restart Service",
"short_description": "Restart Service",
"description": "Redémarre un service sur le système distant. remplacer #service_name# par le nom du service à redémarrer",
"command": "sudo systemctl restart #service_name#"
"command": "sudo systemctl restart #service_name#",
"type": "action",
"subtype": "other"
},
"update_sys_no_confirm": {
"name": "Update System",
"short_description": "Update System without confirmation",
"description": "Met à jour le système distant sans confirmation",
"command": "sudo apt-get -qq update; sudo apt-get -qq upgrade -y"
"command": "sudo apt-get -qq update; sudo apt-get -qq upgrade -y",
"type": "action",
"subtype": "other"
}
}
16 changes: 13 additions & 3 deletions desktop/js/mod.commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@ document.querySelector('.selectCmdTemplate[data-l1key="ssh-select"').addEventLis
console.log(commands[value]["description"]);
console.log(commands[value]["command"]); */

tr.querySelector('.cmdAttr[data-l1key="name"]').value = commands[value]["name"];
tr.querySelector('.cmdAttr[data-l1key="description"]').value = commands[value]["description"];
tr.querySelector('.cmdAttr[data-l1key="ssh-command"]').value = commands[value]["command"];
if (value == "") {
tr.querySelector('.cmdAttr[data-l1key="name"]').value = "";
tr.querySelector('.cmdAttr[data-l1key="description"]').value = "";
tr.querySelector('.cmdAttr[data-l1key="ssh-command"]').value = "";
tr.querySelector('.cmdAttr[data-l1key="type').value = "";
tr.querySelector('.cmdAttr[data-l1key="subtype').value = "";
} else {
tr.querySelector('.cmdAttr[data-l1key="name"]').value = commands[value]["name"];
tr.querySelector('.cmdAttr[data-l1key="description"]').value = commands[value]["description"];
tr.querySelector('.cmdAttr[data-l1key="ssh-command"]').value = commands[value]["command"];
tr.querySelector('.cmdAttr[data-l1key="type').value = commands[value]["type"];
tr.querySelector('.cmdAttr[data-l1key="subtype').value = commands[value]["subtype"];
}
});
Loading

0 comments on commit ce4a8ef

Please sign in to comment.