/Library/New_Server/opt/local/pl.procesy5
// /opt/local/pl.procesy5/async_jobs - TODO: APP_PATH_ASYNC_JOB from .htaccess or config
// Config example:
// cat SE/config/.cnf-biuro.biall-net.pl.ini.php:
// APP_PATH_ASYNC_JOB="/opt/local/pl.procesy5/async_jobs"
// register new job:
// - $TODAY = date("Y-m-d");
// - $JOB_ID = generateJobID if not already started?
// - make folder APP_PATH_ASYNC_JOB / $TODAY / job-{$JOB_ID}
// - make base files:
// - log files: `out.log`, `error.log`
// - `output`: if process creates file. @param --out_file=out
// - `output_type`: output mime type or namespace
// - `progress`: if process implement progress. @param --progress_file=progress
Lib::loadClass('Request');
Lib::loadClass('Core_AsyncJobsFiles');
Lib::loadClass('Core_AsyncJobsDB');
Lib::loadClass('Core_AsyncJobsServer'); // pm2
/**
* @usage: Core_AsyncJobs::startAsyncJob($idJob)
* -> Core_AsyncJobsServer::startAsyncJob(execPath, name, [ 'out.log', 'error.log', 'cwd', 'args' ])
*/
class Core_AsyncJobs {
static function registerNewAntTask($props) {
$namespace = V::get('namespace', '', $props);
$primaryKey = V::get('primaryKey', '', $props);
$ant_path = V::get('ant_path', '', $props);
$ant_template = V::get('ant_template', '', $props);
if (empty($namespace)) throw new Exception("Missing namespace");
if (empty($primaryKey)) throw new Exception("Missing primaryKey");
if (empty($ant_path)) throw new Exception("Missing ant_path");
if (empty($ant_template)) throw new Exception("Missing ant_template");
// https://biuro.biall-net.pl/dev-pl/se-master/index.php?_route=UrlAction_Ant
// & _task=ant
// & path=default_db.in7_dziennik_koresp/test-async
// & template=test-loop
// & typeName=default_db:IN7_DZIENNIK_KORESP
// & primaryKey=66263
// & primaryKeyField=ID
Lib::loadClass('ACL');
$acl = ACL::getAclByNamespace($namespace);
$typeName = Api_WfsNs::typeName($namespace);
$pkField = $acl->getPrimaryKeyField();
// DB::getPDO()->tryHandleException
$idJob = V::tryHandleException($handler = [ 'Core_AsyncJobs', 'isInstalled' ], $callback = [ 'Core_AsyncJobsDB', 'insert' ], $args = [
[
'JOB_NAME' => "Ant|{$namespace}|{$ant_path}|{$ant_template}",
'LOCK_VALUE' => $primaryKey,
'USER' => User::getLogin(),
],
]);
{
$path = $ant_path; // default_db.in7_dziennik_koresp/test-async
$template = $ant_template; // test-loop
$webRootUrl = Request::getPathUri() . "schema/ant-url_action/{$path}"; // TODO: security - only for test
$outputFunctionUrl = Router::getRoute('UrlAction_Ant')->getLink('output') . "&path={$path}&file=";
$antFunctionUrl = Router::getRoute('UrlAction_Ant')->getLink('ant') . "&path={$path}&file={$file}&template={$template}&typeName={$typeName}&primaryKey={$primaryKey}&primaryKeyField={$pkField}"; // &confirmAntfile={$confirmAntfile}&confirmAntfileTarget={$confirmAntfileTarget} sdo confirmacji potrzebne - wzglednie przetwarzac z tresci output URL_TASK - ale potrzebne parametry analogiczne aby chodzily
// $testUrl = Request::getPathUri() . "wfs-data.php/default_db/?SERVICE=WFS&VERSION=1.0.0&TYPENAME={$typeName}&SRSNAME=EPSG:3003&featureID={$objectName}.{$primaryKey}";// &REQUEST=GetFeature
$uuid = date("Y-m-d-H_i_s") . substr(md5(time()), 0, 6); // TODO: uniq id for every request
Lib::loadClass('Crypt');
$passwordBase64Basic = base64_encode(User::getLogin() . ":" . Crypt::decrypt($_SESSION['ADM_PASS_HASH']));
// /* TODO: ??? */ if( strlen($confirmAntfile) > 0 ) $cmd .= " -DconfirmAntfile={$confirmAntfile}";
// /* TODO: ??? */ if( strlen($confirmAntfileTarget) > 0 ) $cmd .= " -DconfirmAntfileTarget={$confirmAntfileTarget}";
$php_session_id = session_id();
}
Core_AsyncJobsFiles::createNewJobFiles($idJob, $jobProps = [
'p5_ant_url_action_base_path' => APP_PATH_ROOT . "/schema/ant-url_action/{$path}",
'webRootUrl' => $webRootUrl,
'outputFunctionUrl' => $outputFunctionUrl,
'antFunctionUrl' => $antFunctionUrl,
'uuid' => $uuid,
'passwordBase64Basic' => $passwordBase64Basic,
'php_session_id' => $passwordBase64Basic,
'xpath' => $pkField,
'xpath_value' => $primaryKey,
'template' => $template,
'api_url' => Request::getPathUri()."wfs-data.php", //potrzebuje ten parametr do dzialania w AMS itp
'typeName' => $typeName,
]);
$jobStartScript = "";
{
$propFilePath = Core_AsyncJobsFiles::propertyFile($idJob, $today = date("Y-m-d"));
$cmd = "cd " . APP_PATH_SCHEMA . "/ant-url_action/{$ant_path}";
$cmd .= " && ";
$cmd .= implode(" ", [
'ant',
'-S',
'-propertyfile', $propFilePath,
($ant_template) ? $ant_template : '',
'2>&1',
]);
$pathCmd = [];
$pathCmd[] = '/opt/local/bin/ant';
$pathCmd[] = '/bin';
$pathCmd[] = '/usr/bin';
$pathCmd[] = '/usr/local/bin';
$pathCmd[] = '/opt/local/bin';
$pathCmd[] = '/sbin';
$pathCmd[] = '/usr/sbin';
$pathCmd[] = '/opt/local/sbin/skrypty';
$pathCmd[] = '/opt/local/var/macports/software';
$pathCmd[] = '/Applications/Server.app/Contents/ServerRoot/usr/bin';
$pathCmd[] = '/Applications/Server.app/Contents/ServerRoot/usr/sbin';
$jobStartScript = implode("\n", [
'export PATH=' . implode(':', $pathCmd),
'JAVA_HOME="/usr/bin/java"',
'MAVEN_OPTS="-Xms256m -Xmx512m"',
$cmd,
"",
]);
}
Core_AsyncJobsFiles::createTaskStartScript($idJob, $jobDate = date("Y-m-d"), $jobStartScript);
return $idJob;
}
static function startAsyncJob($idJob, $item = []) {
if (empty($item)) $item = Core_AsyncJobsDB::fetch($idJob);
DBG::nicePrint($item, '$item');
// Core_AsyncJobsFiles::basePath($idJob, $jobDate = $item['DATE']);
// Core_AsyncJobsFiles::propertyFile($idJob, $jobDate = $item['DATE']);
// Core_AsyncJobsFiles::startScript($idJob, $jobDate = $item['DATE']);
// Core_AsyncJobsFiles::outLog($idJob, $jobDate = $item['DATE']);
// Core_AsyncJobsFiles::errorLog($idJob, $jobDate = $item['DATE']);
$jobDate = $item['DATE'];
$execPath = Core_AsyncJobsFiles::startScript($idJob, $jobDate);
Core_AsyncJobsDB::updateStatus($idJob, $status = 'NORMAL', $item);
// Core_AsyncJobsServer::startAsyncJob(execPath, name, [ 'out.log', 'error.log', 'cwd', 'args' ])
$out = [];
$ret = Core_AsyncJobsServer::startAsyncJob(
$execPath,
$name = implode("|", [ $item['JOB_NAME'], $item['LOCK_VALUE'] ]),
[
'out.log' => Core_AsyncJobsFiles::outLog($idJob, $jobDate),
'error.log' => Core_AsyncJobsFiles::errorLog($idJob, $jobDate),
'cwd' => dirname(Core_AsyncJobsFiles::basePath($idJob, $jobDate)),
],
$out
);
if (0 === $ret) {
throw new AlertInfoException("Uruchomiono zadanie. " . implode(" ", $out));
} else {
throw new Exception("Nie uruchomiono zadania. " . implode(" ", $out));
}
}
static function getSimpleList() {
$fullList = self::getFullList();
return array_map(function ($jobInfo) {
return [
'name' => $jobInfo['name'],
'pid' => $jobInfo['pid'],
'pm_id' => $jobInfo['pm_id'],
'pm2_env.status' => $jobInfo['pm2_env']['status'],
'monit.memory' => $jobInfo['monit']['memory'],
'monit.cpu' => $jobInfo['monit']['cpu'],
// "pm_out_log_path": "/Library/WebServer/.pm2/logs/test-job-1-out.log",
// "pm_err_log_path": "/Library/WebServer/.pm2/logs/test-job-1-error.log",
// "pm_pid_path": "/Library/WebServer/.pm2/pids/test-job-0.pid",
'pm2_env.pm_out_log_path' => $jobInfo['pm2_env']['pm_out_log_path'],
'pm2_env.pm_err_log_path' => $jobInfo['pm2_env']['pm_err_log_path'],
'pm2_env.pm_pid_path' => $jobInfo['pm2_env']['pm_pid_path'],
];
}, $fullList);
}
static function getFullList() {
$jobListJson = V::shell_exec("pm2 jlist 2>&1");
if (empty($jobListJson)) throw new Exception("Reading async job list failed");
$parsedJobList = @json_decode($jobListJson, $assoc = true);
if (null == $parsedJobList && 0 !== json_last_error()) throw new Exception("Parsing async job list failed: " . json_last_error());
return $parsedJobList;
}
static function getJobLogs($jobNameOrID, $lastLines = 10) {
if (empty($jobNameOrID) && $jobNameOrID !== 0) throw new Exception("Missing job name or id in getJobLogs");
$lastLines = ((int)$lastLines > 0) ? (int)$lastLines : 10;
// $cmd = "pm2 logs {$jobNameOrID} --lines {$lastLines} --nostream | tail -n {$lastLines}";
$cmd = "pm2 logs {$jobNameOrID} --lines {$lastLines} --nostream";
return V::shell_exec($cmd);
// [TAILING] Tailing last 3 lines for [0] process (change the value with --lines option)
// /Library/WebServer/.pm2/logs/test-job-1-out.log last 3 lines:
// /Library/WebServer/
}
static function stopJob($jobNameOrID) {
if (empty($jobNameOrID) && $jobNameOrID !== 0) throw new Exception("Missing job name or id in stopJob");
$cmd = "pm2 stop {$jobNameOrID}";
return V::shell_exec($cmd);
}
static function startJob($jobNameOrID) {
if (empty($jobNameOrID) && $jobNameOrID !== 0) throw new Exception("Missing job name or id in startJob");
$cmd = "pm2 start {$jobNameOrID}";
return V::shell_exec($cmd);
}
static function deleteStopped() {
// TODO: script to remove stopped in loop with delay
// pm2 start app.js --restart-delay=3000
// $ pm2 list | grep '^│' | awk -F'│' '{ gsub(/ /, "", $2); gsub(/ /, "", $10); if ("stopped" == $10) { print $2" # STOPPED" } else { print $10 } }' | grep '# STOPPED' | xargs -n1 pm2 delete
$testCmd = implode(" | ", [
"pm2 list",
"grep '^│'",
"awk -F'│' '{ gsub(/ /, \"\", \$2); gsub(/ /, \"\", \$10); if (\"stopped\" == \$10) { print \$2\" # STOPPED\" } else { print \$10 } }'",
"grep '# STOPPED'",
"awk '{print \$1}'",
]);
V::exec($testCmd . " 2>&1", $out, $ret);
echo "cmd: {$cmd}
RETURN CODE: '{$ret}'
OUTPUT:\n" . implode("\n", $out) . "";
$cmd = implode(" | ", [
"pm2 list",
"grep '^│'",
"awk -F'│' '{ gsub(/ /, \"\", \$2); gsub(/ /, \"\", \$10); if (\"stopped\" == \$10) { print \$2\" # STOPPED\" } else { print \$10 } }'",
"grep '# STOPPED'",
"awk '{print \$1}'",
"xargs -n1 pm2 delete",
]);
}
static function isInstalled() {
if (!Core_AsyncJobsFiles::isInstalled()) throw new Exception("AsyncJobs files not installed");
if (!Core_AsyncJobsServer::isInstalled()) throw new Exception("AsyncJobs server not installed");
if (!Core_AsyncJobsDB::checkAsyncJobDatabase()) throw new Exception("AsyncJobs database schema not installed");
return true;
}
// pm2 logs -h
//
// Usage: logs [options] [id|name]
// stream logs file. Default stream all logs
// Options:
// --json json log output
// --format formated log output
// --raw raw output
// --err only shows error output
// --out only shows standard output
// --lines