Skip to content

Commit

Permalink
add option to renew job in existing container, closes #32
Browse files Browse the repository at this point in the history
  • Loading branch information
schorschii committed Jun 5, 2023
1 parent a55c1f0 commit 1de6097
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 103 deletions.
10 changes: 6 additions & 4 deletions frontend/ajax-handler/job-containers.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,19 @@
}

// ----- renew failed jobs in container if requested -----
if(!empty($_POST['create_renew_job_container'])
&& isset($_POST['job_container_id'])
if(isset($_POST['renew_job_container'])
&& isset($_POST['create_new_job_container'])
&& isset($_POST['job_container_name'])
&& isset($_POST['notes'])
&& isset($_POST['start_time'])
&& isset($_POST['end_time'])
&& isset($_POST['use_wol'])
&& isset($_POST['shutdown_waked_after_completion'])
&& isset($_POST['priority'])) {
die($cl->renewFailedStaticJobsInJobContainer(
$_POST['create_renew_job_container'], $_POST['notes'],
$_POST['job_container_id'], $_POST['job_id'] ?? [], $_POST['start_time'], $_POST['end_time'],
$_POST['renew_job_container'], $_POST['job_id'] ?? [], $_POST['create_new_job_container'],
$_POST['job_container_name'], $_POST['notes'],
$_POST['start_time'], $_POST['end_time'],
$_POST['use_wol'], $_POST['shutdown_waked_after_completion'],
0/*sequence mode*/, $_POST['priority']
));
Expand Down
7 changes: 4 additions & 3 deletions frontend/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -1881,13 +1881,14 @@ function showDialogRenewFailedStaticJobs(id, defaultName, jobIds) {
txtRenewJobContainerJobId.value = jobIds;
});
}
function renewFailedStaticJobs(id, jobId, name, notes, startTime, endTime, useWol, shutdownWakedAfterCompletion, priority) {
function renewFailedStaticJobs(id, jobId, createNewJobContainer, name, notes, startTime, endTime, useWol, shutdownWakedAfterCompletion, priority) {
var params = [];
params.push({'key':'create_renew_job_container', 'value':name});
params.push({'key':'job_container_id', 'value':id});
params.push({'key':'renew_job_container', 'value':id});
jobId.toString().split(',').forEach(function(entry) {
if(entry.trim() != '') params.push({'key':'job_id[]', 'value':entry});
});
params.push({'key':'create_new_job_container', 'value':createNewJobContainer ? 1 : 0});
params.push({'key':'job_container_name', 'value':name});
params.push({'key':'notes', 'value':notes});
params.push({'key':'start_time', 'value':startTime});
params.push({'key':'end_time', 'value':endTime});
Expand Down
17 changes: 11 additions & 6 deletions frontend/views/dialog-jobs-renew.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,20 @@
require_once('../session.php');
?>

<p style='max-width:450px'><?php echo LANG('renew_jobs_description'); ?></p>
<input type='hidden' id='txtRenewJobContainerId' value=''></input>
<input type='hidden' id='txtRenewJobContainerJobId' value=''></input>
<table class='fullwidth aligned'>
<tr>
<th><?php echo LANG('mode'); ?></th>
<td>
<label><input type='checkbox' id='chkCreateNewJobContainer' autofocus='true' onclick='if(this.checked) tbNewJobContainer.style.display="table-row-group"; else tbNewJobContainer.style.display="none";'><?php echo LANG('create_new_job_container'); ?></label>
<div style='max-width:400px' class='hint'><?php echo LANG('renew_jobs_description'); ?></div>
</td>
</tr>
<tbody id='tbNewJobContainer' style='display:none'>
<tr>
<th><?php echo LANG('name'); ?></th>
<td><input type='text' class='fullwidth' autocomplete='new-password' id='txtRenewJobContainerName' autofocus='true' value='<?php echo LANG('renew').' '.date('Y-m-d H:i:s'); ?>'></input></td>
<td><input type='text' class='fullwidth' autocomplete='new-password' id='txtRenewJobContainerName' value='<?php echo LANG('renew').' '.date('Y-m-d H:i:s'); ?>'></input></td>
</tr>
<tr>
<th><?php echo LANG('description'); ?></th>
Expand Down Expand Up @@ -48,17 +55,15 @@
</div>
</td>
</tr>
<tr>
<th></th>
<td></td>
</tr>
</tbody>
</table>

<div class='controls right'>
<button onclick='hideDialog();showLoader(false);showLoader2(false);'><img src='img/close.dyn.svg'>&nbsp;<?php echo LANG('close'); ?></button>
<button class='primary' onclick='renewFailedStaticJobs(
txtRenewJobContainerId.value,
txtRenewJobContainerJobId.value,
chkCreateNewJobContainer.checked,
txtRenewJobContainerName.value,
txtRenewJobContainerNotes.value,
txtRenewJobContainerStartDate.value+" "+txtRenewJobContainerStartTime.value,
Expand Down
191 changes: 104 additions & 87 deletions lib/CoreLogic.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -1188,112 +1188,129 @@ public function removeComputerAssignedPackage($id) {
$this->db->insertLogEntry(Models\Log::LEVEL_INFO, $this->su->username, $package->id, 'oco.package.remove_assignment', ['computer_id'=>$computer->id]);
return $result;
}
public function renewFailedStaticJobsInJobContainer($name, $description, $renewJobContainerId, $renewJobIds, $dateStart, $dateEnd, $useWol, $shutdownWakedAfterCompletion, $sequenceMode=0, $priority=0) {
public function renewFailedStaticJobsInJobContainer($renewJobContainerId, $renewJobIds, $createNewJobContainer, $name, $description, $dateStart, $dateEnd, $useWol, $shutdownWakedAfterCompletion, $sequenceMode=0, $priority=0) {
$container = $this->db->selectJobContainer($renewJobContainerId);
if(!$container) throw new NotFoundException();
$this->checkPermission($container, PermissionManager::METHOD_WRITE);
$this->checkPermission(new Models\JobContainer(), PermissionManager::METHOD_CREATE);

// check user input
if(empty(trim($name))) {
throw new InvalidRequestException(LANG('name_cannot_be_empty'));
}
if(empty($dateStart) || strtotime($dateStart) === false) {
throw new InvalidRequestException(LANG('please_fill_required_fields'));
}
if(!empty($dateEnd) // check end date if not empty
&& (strtotime($dateEnd) === false || strtotime($dateStart) >= strtotime($dateEnd))
) {
throw new InvalidRequestException(LANG('end_time_before_start_time'));
}
if($sequenceMode != Models\JobContainer::SEQUENCE_MODE_IGNORE_FAILED
&& $sequenceMode != Models\JobContainer::SEQUENCE_MODE_ABORT_AFTER_FAILED) {
throw new InvalidRequestException(LANG('invalid_input'));
}
if($priority < -100 || $priority > 100) {
throw new InvalidRequestException(LANG('invalid_input'));
}
$this->db->getDbHandle()->beginTransaction();
if(empty($createNewJobContainer)) {

// wol handling
$computer_ids = [];
foreach($this->db->selectAllStaticJobByJobContainer($container->id) as $job) {
if(!empty($renewJobIds) && !in_array($job->id, $renewJobIds)) continue;
if($job->isFailed()) {
$computer_ids[] = $job->computer_id;
}
}
$wolSent = -1;
if($useWol) {
if(strtotime($dateStart) <= time()) {
// instant WOL if start time is already in the past
$wolSent = 1;
$wolMacAdresses = [];
foreach($computer_ids as $cid) {
foreach($this->db->selectAllComputerNetworkByComputerId($cid) as $cn) {
$wolMacAdresses[] = $cn->mac;
}
foreach($this->db->selectAllStaticJobByJobContainer($container->id) as $job) {
if(!empty($renewJobIds) && !in_array($job->id, $renewJobIds)) continue;
if($job->isFailed()) {
$this->db->renewStaticJob($job->id);
}
$wolController = new WakeOnLan($this->db);
$wolController->wol($wolMacAdresses, false);
} else {
$wolSent = 0;
}
}

// create new jobs
$jobs = [];
if($jcid = $this->db->insertJobContainer(
$name, $this->su->id??null, $this->du->id??null,
$dateStart, empty($dateEnd) ? null : $dateEnd,
$description, $wolSent, $shutdownWakedAfterCompletion,
$sequenceMode, $priority, $container->agent_ip_ranges,
0/*self-service*/
)) {
} else {

$this->checkPermission(new Models\JobContainer(), PermissionManager::METHOD_CREATE);

// check user input
if(empty(trim($name))) {
throw new InvalidRequestException(LANG('name_cannot_be_empty'));
}
if(empty($dateStart) || strtotime($dateStart) === false) {
throw new InvalidRequestException(LANG('please_fill_required_fields'));
}
if(!empty($dateEnd) // check end date if not empty
&& (strtotime($dateEnd) === false || strtotime($dateStart) >= strtotime($dateEnd))
) {
throw new InvalidRequestException(LANG('end_time_before_start_time'));
}
if($sequenceMode != Models\JobContainer::SEQUENCE_MODE_IGNORE_FAILED
&& $sequenceMode != Models\JobContainer::SEQUENCE_MODE_ABORT_AFTER_FAILED) {
throw new InvalidRequestException(LANG('invalid_input'));
}
if($priority < -100 || $priority > 100) {
throw new InvalidRequestException(LANG('invalid_input'));
}

// wol handling
$computer_ids = [];
foreach($this->db->selectAllStaticJobByJobContainer($container->id) as $job) {
if(!empty($renewJobIds) && !in_array($job->id, $renewJobIds)) continue;
if($job->isFailed()) {

// use the current package procedure, return codes, post action
$package = $this->db->selectPackage($job->package_id);
$newJob = new Models\StaticJob();
$newJob->job_container_id = $jcid;
$newJob->computer_id = $job->computer_id;
$newJob->package_id = $job->package_id;
$newJob->procedure = empty($job->is_uninstall) ? $package->install_procedure : $package->uninstall_procedure;
$newJob->success_return_codes = empty($job->is_uninstall) ? $package->install_procedure_success_return_codes : $package->uninstall_procedure_success_return_codes;
$newJob->upgrade_behavior = $package->upgrade_behavior;
$newJob->is_uninstall = $job->is_uninstall;
$newJob->download = $package->getFilePath() ? 1 : 0;
$newJob->post_action = empty($job->is_uninstall) ? $package->install_procedure_post_action : $package->uninstall_procedure_post_action;
$newJob->post_action_timeout = $job->post_action_timeout;
$newJob->sequence = $job->sequence;

if($this->db->insertStaticJob($newJob->job_container_id,
$newJob->computer_id, $newJob->package_id,
$newJob->procedure, $newJob->success_return_codes,
$newJob->upgrade_behavior, $newJob->is_uninstall, $newJob->download,
$newJob->post_action, $newJob->post_action_timeout,
$newJob->sequence
)) {
$jobs[] = $newJob;
$this->db->deleteStaticJob($job->id);
$computer_ids[] = $job->computer_id;
}
}
$wolSent = -1;
if($useWol) {
if(strtotime($dateStart) <= time()) {
// instant WOL if start time is already in the past
$wolSent = 1;
$wolMacAdresses = [];
foreach($computer_ids as $cid) {
foreach($this->db->selectAllComputerNetworkByComputerId($cid) as $cn) {
$wolMacAdresses[] = $cn->mac;
}
}
$wolController = new WakeOnLan($this->db);
$wolController->wol($wolMacAdresses, false);
} else {
$wolSent = 0;
}
}

// check if there are any computer & packages
if(count($jobs) == 0) {
$this->db->deleteJobContainer($jcid);
throw new InvalidRequestException(LANG('no_jobs_created'));
}
// create new jobs
$jobs = [];
if($jcid = $this->db->insertJobContainer(
$name, $this->su->id??null, $this->du->id??null,
$dateStart, empty($dateEnd) ? null : $dateEnd,
$description, $wolSent, $shutdownWakedAfterCompletion,
$sequenceMode, $priority, $container->agent_ip_ranges,
0/*self-service*/
)) {
foreach($this->db->selectAllStaticJobByJobContainer($container->id) as $job) {
if(!empty($renewJobIds) && !in_array($job->id, $renewJobIds)) continue;
if($job->isFailed()) {

// use the current package procedure, return codes, post action
$package = $this->db->selectPackage($job->package_id);
$newJob = new Models\StaticJob();
$newJob->job_container_id = $jcid;
$newJob->computer_id = $job->computer_id;
$newJob->package_id = $job->package_id;
$newJob->procedure = empty($job->is_uninstall) ? $package->install_procedure : $package->uninstall_procedure;
$newJob->success_return_codes = empty($job->is_uninstall) ? $package->install_procedure_success_return_codes : $package->uninstall_procedure_success_return_codes;
$newJob->upgrade_behavior = $package->upgrade_behavior;
$newJob->is_uninstall = $job->is_uninstall;
$newJob->download = $package->getFilePath() ? 1 : 0;
$newJob->post_action = empty($job->is_uninstall) ? $package->install_procedure_post_action : $package->uninstall_procedure_post_action;
$newJob->post_action_timeout = $job->post_action_timeout;
$newJob->sequence = $job->sequence;

if($this->db->insertStaticJob($newJob->job_container_id,
$newJob->computer_id, $newJob->package_id,
$newJob->procedure, $newJob->success_return_codes,
$newJob->upgrade_behavior, $newJob->is_uninstall, $newJob->download,
$newJob->post_action, $newJob->post_action_timeout,
$newJob->sequence
)) {
$jobs[] = $newJob;
$this->db->deleteStaticJob($job->id);
}
}
}

// if instant WOL: check if computers are currently online (to know if we should shut them down after all jobs are done)
if(strtotime($dateStart) <= time() && $useWol && $shutdownWakedAfterCompletion) {
$this->db->setComputerOnlineStateForWolShutdown($jcid);
// check if there are any computer & packages
if(count($jobs) == 0) {
$this->db->deleteJobContainer($jcid);
throw new InvalidRequestException(LANG('no_jobs_created'));
}

// if instant WOL: check if computers are currently online (to know if we should shut them down after all jobs are done)
if(strtotime($dateStart) <= time() && $useWol && $shutdownWakedAfterCompletion) {
$this->db->setComputerOnlineStateForWolShutdown($jcid);
}

$this->db->insertLogEntry(Models\Log::LEVEL_INFO, $this->su->username??'SYSTEM', $jcid, 'oco.job_container.create', ['name'>=$name, 'jobs'=>$jobs]);
}

$this->db->insertLogEntry(Models\Log::LEVEL_INFO, $this->su->username??'SYSTEM', $jcid, 'oco.job_container.create', ['name'>=$name, 'jobs'=>$jobs]);
}
$this->db->getDbHandle()->commit();

}
public function editJobContainer($id, $name, $enabled, $start_time, $end_time, $notes, $sequence_mode, $priority, $agent_ip_ranges) {
$jc = $this->db->selectJobContainer($id);
Expand Down
6 changes: 6 additions & 0 deletions lib/DatabaseController.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -1747,6 +1747,12 @@ public function removeWolShutdownStaticJobInJobContainer($job_container_id, $job
':post_action' => $post_action,
])) return false;
}
public function renewStaticJob($id) {
$this->stmt = $this->dbh->prepare(
'UPDATE job_container_job SET state = 0, return_code = NULL, message = "", download_started = NULL, execution_started = NULL, execution_finished = NULL WHERE id = :id'
);
return $this->stmt->execute([':id' => $id]);
}
public function updateJobExecutionState($job) {
if($job instanceof Models\StaticJob) {
if($job->state === Models\Job::STATE_DOWNLOAD_STARTED) {
Expand Down
4 changes: 3 additions & 1 deletion lib/Language/de.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@
'progress' => 'Fortschritt',
'expired' => 'Abgelaufen',
'in_progress' => 'Wird verarbeitet...',
'renew_failed_jobs' => 'Fehlgeschlagene Jobs erneuern',
'renew_failed_jobs' => '(Ausgewählte) fehlgeschlagene Jobs erneuern',
'renew_failed' => 'Fehlgeschlagene erneuern',
'renew_jobs_description' => 'Es wird ein neuer Jobcontainer mit allen (ausgewählten) fehlgeschlagenen Jobs erstellt. Anschließend werden die fehlgeschlagen Jobs vom ursprünglichen Container gelöscht.',
'renew_failed_deployment_rule_jobs_now' => 'Möchten Sie die ausgewählten Jobs (bzw. alle fehlgeschlagenen Jobs im Falle keiner Auswahl) jetzt in dieser Bereitstellungsregel erneuern?',
Expand Down Expand Up @@ -526,6 +526,8 @@
'upgrade_behavior' => 'Upgrade-Verhalten',
'keep_other_versions' => 'Andere Versionen beibehalten',
'create_explicit_uninstall_jobs' => 'Explizite Deinstallationssjobs erstellen',
'create_new_job_container' => 'Neuen Jobcontainer erstellen',
'mode' => 'Modus',
'package_creation_notes' => '
<p>
Ein Paket besteht aus einem ZIP-Archiv, welches bei der Bereitstellung in ein temporäres Verzeichnis entpackt wird. Anschließend wird ein Kommando (die Prozedur) ausgeführt, um die Installation zu starten. Längere Kommandos sollten in ein selbst geschriebenes Skript (.bat bzw. .sh) ausgelagert werden.
Expand Down
4 changes: 3 additions & 1 deletion lib/Language/en.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@
'progress' => 'Progress',
'expired' => 'Expired',
'in_progress' => 'In Progress...',
'renew_failed_jobs' => 'Renew Failed Jobs',
'renew_failed_jobs' => 'Renew (Selected) Failed Jobs',
'renew_failed' => 'Renew Failed',
'renew_jobs_description' => 'A new job container with all (selected) failed jobs will be created and the failed jobs will be deleted from the original job container.',
'renew_failed_deployment_rule_jobs_now' => 'Do you want to renew the selected jobs (or all failed jobs if nothing is selected) in this deployment rule now?',
Expand Down Expand Up @@ -531,6 +531,8 @@
'upgrade_behavior' => 'Upgrade Behavior',
'keep_other_versions' => 'Keep other versions',
'create_explicit_uninstall_jobs' => 'Create explicit uninstall jobs',
'create_new_job_container' => 'Create new job container',
'mode' => 'Mode',
'package_creation_notes' => '
<p>
A package consists of a ZIP archive, which is unpacked into a temporary directory when it is made available. Then a command (the procedure) is executed to start the installation. Longer commands should be stored in a script (.bat or .sh) you have written yourself.
Expand Down
4 changes: 3 additions & 1 deletion lib/Language/fr.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@
'progress' => 'Progrès',
'expired' => 'Expiré',
'in_progress' => 'En cours...',
'renew_failed_jobs' => 'Relancer les tâches en échec',
'renew_failed_jobs' => 'Renouveler les tâches ayant échoué (sélectionnées)',
'renew_failed' => 'Relancer les échecs',
'renew_jobs_description' => 'Un nouveau container de tâche avec toutes les tâches en échec (sélectionnées) sera créé et ses tâches échues seront supprimées du container d\'origine.',
'renew_failed_deployment_rule_jobs_now' => 'Voulez-vous relancer les tâches sélectionnées (ou bien toutes les tâches si rien n\'est sélectionné) dans cette règle de déploiement?',
Expand Down Expand Up @@ -531,6 +531,8 @@
'upgrade_behavior' => 'Comportement de mise à jour',
'keep_other_versions' => 'Conserver les autres versions',
'create_explicit_uninstall_jobs' => 'Créer une tâche de désinstallation explicite',
'create_new_job_container' => 'Créer un nouveau conteneur de tâches',
'mode' => 'Mode',
'package_creation_notes' => '
<p>
Un paquet consiste en une archive ZIP, qui sera décompressé dans un fichier temporaire. Ensuite une commande (la procédure) est exécutée pour démarrer l\'installation. Les commandes longues doivent être stockées dans un script (.bat or .sh) que vous créez vous-même.
Expand Down

0 comments on commit 1de6097

Please sign in to comment.