975 shared_ptr<structJobPar> job_par = thread_par->sptrJobParameter;
976 int thread_counter = thread_par->iThreadCounter;
979 int macro_counter = 0;
980 for (
size_t ind_stack = 0; ind_stack < job_par->vecJobStack.size(); ind_stack++) {
981 shared_ptr<structJobStackPar> cur_stack = job_par->vecJobStack[ind_stack];
984 if (cur_stack->iStackType == 1) {
989 shared_ptr<structMacroPar> cur_macro = cur_stack->sptrMacroParameter;
994 if (cur_macro->vecFiles.size() > 0)
995 cur_file = cur_macro->vecFiles[thread_counter - 1].get();
998 if (cur_file ==
nullptr) {
1000 stringstream ssROOTCommand;
1001 ssROOTCommand << job_par->strConfigPath <<
"root -b -q '" << cur_macro->nstrMacroPath.GetValue() <<
"(";
1002 if (cur_macro->strAddArgs !=
"")
1003 ssROOTCommand << cur_macro->strAddArgs;
1004 if (job_par->nstrLogs !=
nullptr)
1005 ssROOTCommand <<
")' > " << job_par->nstrLogs.GetValue() <<
"_" << to_string(thread_counter) <<
" 2>&1";
1007 ssROOTCommand <<
")'";
1011 cout <<
"DEBUG nica-scheduler$ Local ROOT command = " << ssROOTCommand.str() << endl;
1015 long int t = time(
nullptr);
1017 stringstream ssBashFile;
1018 ssBashFile <<
"temp_" << to_string(thread_counter) <<
"_" << to_string(t) <<
".sh";
1020 myfile.open(ssBashFile.str().c_str());
1021 myfile <<
"export ROOT_HIST=0 " << endl;
1022 myfile << ssROOTCommand.str();
1024 stringstream ssRunCommand;
1025 ssRunCommand <<
"bash " << ssBashFile.str();
1028 thread_par->mutJobMutex->lock();
1029 cout <<
"nica-scheduler$ Task " << macro_counter <<
" is running..." << endl << endl;
1030 thread_par->mutJobMutex->unlock();
1033 int system_return = system(ssRunCommand.str().c_str());
1034 if (system_return != 0)
1035 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero code: "
1036 << system_return << endl;
1040 stringstream ssDeleteCommand;
1041 ssDeleteCommand <<
"rm -rf " << ssBashFile.str();
1042 system_return = system(ssDeleteCommand.str().c_str());
1043 if (system_return != 0)
1044 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero code: "
1045 << system_return << endl;
1047 cout <<
"DEBUG nica-scheduler$ '" << ssBashFile.str() <<
"' bash file was used " << endl;
1053 string strRealInputFile = cur_file->
strFileIn;
1059 stringstream ssCopyCommand;
1063 int system_return = system(ssCopyCommand.str().c_str());
1064 if (system_return != 0)
1065 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero code: "
1066 << system_return << endl;
1075 int* cur_start_event = cur_macro->ptrStartEvent.get();
1080 int* cur_event_count = cur_macro->ptrEventCount.get();
1085 if ((cur_file->
iParallelMode > 1) && (cur_start_event ==
nullptr)) {
1088 cout <<
"nica-scheduler$ WARNING: parallel_mode is used, but start event is not set, so it is assigned "
1093 if ((cur_file->
iParallelMode > 1) && (cur_event_count ==
nullptr)) {
1096 cout <<
"nica-scheduler$ WARNING: parallel_mode is used, but event count is not set, so it is assigned "
1102 if ((cur_file->
iParallelMode > 1) && ((cur_start_event ==
nullptr) || (cur_event_count ==
nullptr))) {
1104 cout <<
"nica-scheduler$ WARNING: parallel_mode must be used with 'start_event' and 'event_count' "
1105 "parameters, parallel_mode is ignored!"
1109 int real_thread_count = 1;
1111 string strOutputUnion = nstrRealOutputFile.
GetValue();
1114 int start_event = -1, event_count = -1;
1115 if (cur_start_event !=
nullptr)
1116 start_event = *cur_start_event;
1117 if (cur_event_count !=
nullptr)
1118 event_count = *cur_event_count;
1122 string config_paths = job_par->strConfigPath;
1124 if (job_par->nstrLogs ==
nullptr)
1126 stringstream ssEventNumberCommand;
1127 ssEventNumberCommand << config_paths <<
"show_event_count auto \"" << strRealInputFile
1128 <<
"\" 2> /dev/null";
1130 cout <<
"DEBUG nica-scheduler$ trying to get event count from the input file: "
1131 << ssEventNumberCommand.str() << endl;
1133 array<char, 128> bufferCommand;
1134 string str_event_number =
"";
1135 FILE* stream = popen(ssEventNumberCommand.str().c_str(),
"r");
1136 while (fgets(bufferCommand.data(),
static_cast<int>(bufferCommand.size()), stream) !=
nullptr)
1137 str_event_number += bufferCommand.data();
1140 if (str_event_number ==
"") {
1142 "set and cannot be automatically defined. "
1143 "'%s' file will be skipped!",
1144 strRealInputFile.c_str())
1149 event_count = atoi(str_event_number.c_str());
1151 cout <<
"DEBUG nica-scheduler$ " << event_count <<
" event(s) found in the input file" << endl;
1155 vector<nullString> vecPartialOutputFiles;
1161 vector<thread> vecSubThreads;
1165 int event_per_thread = (event_count +
i) / cur_file->
iParallelMode;
1166 if (event_per_thread != 0) {
1167 nullString nstr_output_file = nstrRealOutputFile;
1169 if (nstr_output_file !=
nullptr)
1174 shared_ptr<structSubThreadPar> subthread_par = make_shared<structSubThreadPar>();
1175 subthread_par->sptrThreadParameter = thread_par;
1176 subthread_par->sptrMacroParameter = cur_macro;
1177 subthread_par->iStartEvent = start_event;
1178 subthread_par->iEventCount = event_per_thread;
1179 subthread_par->strFileIn = strRealInputFile;
1180 subthread_par->nstrFileOut = nstr_output_file;
1181 subthread_par->iSubthreadCounter =
i + 1;
1185 thread_par->semJobSemaphore->release();
1187 thread_par->semJobSemaphore->acquire();
1192 }
catch (
const system_error& e) {
1194 "code = %d and message = %s",
1195 e.code().value(), e.what()))
1201 if (nstr_output_file !=
nullptr) {
1202 strOutputUnion +=
" " + nstr_output_file.
GetValue();
1203 vecPartialOutputFiles.push_back(nstr_output_file);
1206 real_thread_count++;
1209 start_event = start_event + event_per_thread;
1214 cout <<
"nica-scheduler$ Waiting for " << real_thread_count <<
" subtask(s) to finish..." << endl;
1215 for (
auto& sub_thread : vecSubThreads) {
1218 }
catch (
const system_error& e) {
1220 (
TString::Format(
"ERROR nica-scheduler$ Sub-thread failed with code = %d and message = %s",
1221 e.code().value(), e.what()))
1226 cout <<
"nica-scheduler$ Subtasks " << macro_counter <<
":[] has been finished" << endl;
1227 thread_par->semJobSemaphore->acquire();
1232 if ((cur_file->
iMerge >= 0) && (strOutputUnion !=
"")) {
1236 stringstream ssMergeCommand;
1237 ssMergeCommand << job_par->strConfigPath <<
"root -b -q '" << UNIONc_path <<
"(\"" << strOutputUnion
1238 <<
"\", " << to_string(cur_file->
iMerge) <<
")'";
1241 if (job_par->nstrLogs !=
nullptr)
1242 ssMergeCommand <<
" >> " << job_par->nstrLogs.GetValue() <<
" 2>&1";
1246 long int t = time(
nullptr);
1247 stringstream ssBashFile;
1248 ssBashFile <<
"temp_union_" << to_string(t) <<
".sh";
1249 myfile.open(ssBashFile.str().c_str());
1250 myfile << ssMergeCommand.str();
1252 stringstream ssBashCommand;
1253 ssBashCommand <<
"bash " << ssBashFile.str().c_str();
1256 <<
"nica-scheduler$ Merging the result of subtasks " << macro_counter <<
":[]..." << endl
1258 int system_return = system(ssBashCommand.str().c_str());
1259 if (system_return != 0)
1260 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero code: "
1261 << system_return << endl;
1265 stringstream ssRemoveCommand;
1266 ssRemoveCommand <<
"rm -rf " << ssBashFile.str();
1267 system_return = system(ssRemoveCommand.str().c_str());
1268 if (system_return != 0)
1269 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero "
1271 << system_return << endl;
1273 cout <<
"DEBUG nica-scheduler$ '" << ssBashFile.str() <<
"' bash file was used " << endl;
1281 stringstream ssROOTCommand;
1282 ssROOTCommand << job_par->strConfigPath <<
"root -b -q '" << cur_macro->nstrMacroPath.GetValue() <<
"(";
1285 bool is_first =
true;
1286 if (strRealInputFile !=
"") {
1287 ssROOTCommand <<
"\"" << strRealInputFile <<
"\"";
1291 if (nstrRealOutputFile !=
nullptr) {
1293 ssROOTCommand <<
", ";
1296 ssROOTCommand <<
"\"" << nstrRealOutputFile.
GetValue() <<
"\"";
1299 if (cur_start_event !=
nullptr) {
1301 ssROOTCommand <<
", ";
1304 ssROOTCommand << to_string(*cur_start_event);
1307 if (cur_event_count !=
nullptr) {
1309 ssROOTCommand <<
", ";
1312 ssROOTCommand << to_string(*cur_event_count);
1315 if (cur_macro->strAddArgs !=
"") {
1317 ssROOTCommand <<
", ";
1320 ssROOTCommand << cur_macro->strAddArgs;
1324 if (job_par->nstrLogs !=
nullptr)
1325 ssROOTCommand <<
")' > " << job_par->nstrLogs.GetValue() <<
"_" << to_string(thread_counter)
1328 ssROOTCommand <<
")'";
1332 cout <<
"DEBUG nica-scheduler$ Local command = " << ssROOTCommand.str() << endl;
1336 long int t = time(
nullptr);
1338 stringstream ssBashFile;
1339 ssBashFile <<
"temp_" << to_string(thread_counter) <<
"_" <<
"_" << to_string(t) <<
".sh";
1341 myfile.open(ssBashFile.str().c_str());
1342 myfile <<
"export ROOT_HIST=0 " << endl;
1343 myfile << ssROOTCommand.str();
1345 stringstream ssRunCommand;
1346 ssRunCommand <<
"bash " << ssBashFile.str();
1349 if (nstrRealOutputFile !=
nullptr) {
1351 if (!outDir.empty()) {
1352 string mkdirCmd =
"mkdir -p \"" + outDir +
"\"";
1353 int ret = system(mkdirCmd.c_str());
1355 thread_par->mutJobMutex->lock();
1356 info_message(
"ERROR nica-scheduler$ Failed to create output directory for task "
1357 + to_string(thread_counter) +
": " + outDir,
1359 thread_par->mutJobMutex->unlock();
1363 string rmCmd =
"rm -f \"" + ssBashFile.str() +
"\"";
1364 system(rmCmd.c_str());
1367 thread_par->semJobSemaphore->release();
1374 thread_par->mutJobMutex->lock();
1375 cout <<
"nica-scheduler$ Task " << thread_counter <<
" is running..." << endl;
1376 if (strRealInputFile !=
"")
1377 cout <<
"input data - " << strRealInputFile << endl;
1378 if (nstrRealOutputFile !=
nullptr)
1379 cout <<
"output data - " << nstrRealOutputFile.
GetValue() << endl;
1381 thread_par->mutJobMutex->unlock();
1384 int system_return = system(ssRunCommand.str().c_str());
1385 if (system_return != 0)
1386 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero code: "
1387 << system_return << endl;
1391 stringstream ssRemoveCommand;
1392 ssRemoveCommand <<
"rm -rf " << ssBashFile.str();
1393 system_return = system(ssRemoveCommand.str().c_str());
1394 if (system_return != 0)
1395 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero code: "
1396 << system_return << endl;
1398 cout <<
"DEBUG nica-scheduler$ '" << ssBashFile.str() <<
"' bash file was used " << endl;
1402 bool isGetCopyError =
false;
1406 if (strFinalOutputDir ==
"")
1407 strFinalOutputDir =
".";
1409 stringstream ssCopyCommand;
1411 ssCopyCommand <<
"mkdir -p \"" << strFinalOutputDir <<
"\"; " << cur_file->
strGetCommand <<
" "
1413 int system_return = system(ssCopyCommand.str().c_str());
1414 if (system_return != 0) {
1415 isGetCopyError =
true;
1416 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero code: "
1417 << system_return << endl;
1422 for (
nullString& cur_partial_file : vecPartialOutputFiles) {
1423 stringstream ssCopyPartialsCommand;
1424 ssCopyPartialsCommand
1425 << cur_file->
strGetCommand <<
" " << cur_partial_file.GetValue() <<
" "
1427 system_return = system(ssCopyPartialsCommand.str().c_str());
1428 if (system_return != 0) {
1429 isGetCopyError =
true;
1430 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero "
1432 << system_return << endl;
1443 stringstream ssRemoveCommand;
1444 ssRemoveCommand <<
"rm -rf " << cur_file->
strPutPath;
1445 int system_return = system(ssRemoveCommand.str().c_str());
1446 if (system_return != 0)
1447 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero code: "
1448 << system_return << endl;
1451 if ((cur_file->
strGetPath !=
"") && isGetCopyError)
1452 cout <<
"nica-scheduler$ ERROR: copying of the results (GET) failed, "
1453 "temporary output files were preserved"
1455 if ((cur_file->
strGetPath !=
"") && !isGetCopyError) {
1456 stringstream ssRemoveCommand;
1457 ssRemoveCommand <<
"rm -rf " << cur_file->
strGetPath;
1458 int system_return = system(ssRemoveCommand.str().c_str());
1459 if (system_return != 0)
1460 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero code: "
1461 << system_return << endl;
1465 for (
nullString& cur_partial_file : vecPartialOutputFiles) {
1466 stringstream ssRemovePartialCommand;
1467 ssRemovePartialCommand <<
"rm -rf " << cur_partial_file.GetValue();
1468 system_return = system(ssRemovePartialCommand.str().c_str());
1469 if (system_return != 0)
1470 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero "
1472 << system_return << endl;
1492 stringstream ssRemoveCommand;
1493 for (
const string& cur_clean_path : cur_file->
vecCleanPath)
1494 ssRemoveCommand <<
"rm -rf " << cur_clean_path <<
"; ";
1495 int system_return = system(ssRemoveCommand.str().c_str());
1496 if (system_return != 0)
1497 cout <<
"nica-scheduler$ WARNING: System call (in ThreadLocalProcess) returned non-zero code: "
1498 << system_return << endl;
1502 cout <<
"nica-scheduler$ Task " << thread_counter <<
" has been finished" << endl;
1506 thread_par->semJobSemaphore->release();
1534 info_message(
"ERROR nica-scheduler$ XML file path (containing job description) must be set", 1);
1539 string xml_file_path =
"", exp_database =
"";
1540 int option_index = 0, c;
1541 while ((c = getopt_long(argc, argv,
"hf:d",
long_options, &option_index)) != -1) {
1548 printf(
"with arg %s", optarg);
1551 if (strcmp(
long_options[option_index].name,
"db") == 0)
1552 exp_database = optarg;
1553 if (strcmp(
long_options[option_index].name,
"database") == 0)
1554 exp_database = optarg;
1561 xml_file_path = optarg;
1572 if (optind < argc) {
1573 if (((argc - optind) == 1) && (xml_file_path ==
""))
1574 xml_file_path = argv[optind];
1576 info_message(
"ERROR nica-scheduler$ Argument list is incorrect.\nPlease, check information on command line "
1577 "arguments in the 'README.md' file",
1584 if (xml_file_path ==
"") {
1585 info_message(
"ERROR nica-scheduler$ XML file path (containing job description) must be set", 1);
1591 bool isDBSettingsHigh =
false;
1593 if (exp_database !=
"") {
1597 exp_database.c_str()))
1602 dbSettingsPre = iter_db_settings->second;
1603 isDBSettingsHigh =
true;
1608 xmlDocPtr doc = xmlReadFile(xml_file_path.c_str(),
nullptr, 0);
1609 if (doc ==
nullptr) {
1611 (
TString::Format(
"ERROR nica-scheduler$ Failed to open input XML file: '%s'", xml_file_path.c_str()))
1618 xmlNodePtr root = xmlDocGetRootElement(doc);
1621 multimap<string, string> mapJobName2ID;
1623 multimap<string, unique_ptr<vector<nullString>>> mapJobName2FileOut;
1629 srand(time(
nullptr));
1633 xmlNodePtr cur_node = root;
1636 if (cur_node->type == XML_ELEMENT_NODE) {
1640 if (strcmp((
char*)cur_node->name,
"jobs") == 0) {
1642 pc_temp = (
char*)xmlGetProp(cur_node, (
unsigned char*)
"batch");
1643 if (pc_temp !=
nullptr) {
1645 if (strcmp(pc_batch_system,
"sge") == 0)
1648 if (strcmp(pc_batch_system,
"torque") == 0)
1652 free(pc_batch_system);
1655 pc_temp = (
char*)xmlGetProp(cur_node, (
unsigned char*)
"database");
1656 if ((pc_temp !=
nullptr) && (!isDBSettingsHigh)) {
1660 "found for '%s'. The parameter will be ignored.",
1665 dbSettingsPre = iter_db_settings->second;
1669 cur_node = cur_node->children;
1673 if (strcmp((
char*)cur_node->name,
"job") == 0) {
1674 bool isError =
false;
1676 shared_ptr<structJobPar> job_par = make_shared<structJobPar>();
1679 pc_temp = (
char*)xmlGetProp(cur_node, (
unsigned char*)
"name");
1680 if (pc_temp !=
nullptr) {
1681 job_par->strJobName = pc_temp;
1687 pc_temp = (
char*)xmlGetProp(cur_node, (
unsigned char*)
"database");
1688 if ((pc_temp !=
nullptr) && (!isDBSettingsHigh)) {
1692 "found for '%s'. The parameter will be ignored.",
1697 dbSettings = iter_db_settings->second;
1700 if (dbSettings.
db_name ==
"") {
1706 job_par->dbSettings = dbSettings;
1709 pc_temp = (
char*)xmlGetProp(cur_node, (
unsigned char*)
"batch");
1710 if (pc_temp !=
nullptr) {
1712 if (strcmp(pc_batch_system,
"slurm") == 0)
1715 if (strcmp(pc_batch_system,
"torque") == 0)
1719 free(pc_batch_system);
1727 string strBatchFile = batch_com.
job_begin, strBatchInitInputFile =
"declare -a InputFileArray=(",
1728 strBatchInitOutputFile =
"declare -a OutputFileArray=(",
1729 strBatchInitStartEvent =
"declare -a StartEventArray=(",
1730 strBatchInitEventCount =
"declare -a EventCountArray=(",
1731 strBatchInitAddArgs =
"declare -a AddArgsArray=(", strBatchInitPut =
"declare -a PutArray=(",
1732 strBatchInitGet =
"declare -a GetArray=(", strBatchInitClean =
"declare -a CleanArray=(";
1735 pc_temp = (
char*)xmlGetProp(cur_node, (
unsigned char*)
"dependency");
1736 if (pc_temp !=
nullptr) {
1737 multimap<string, string>::iterator it = mapJobName2ID.find(pc_temp);
1738 if (it == mapJobName2ID.end())
1739 cout <<
"nica-scheduler$ WARNING: the following dependency for '" << job_par->strJobName
1740 <<
"' job was not found: " << pc_temp << endl;
1753 xmlNodePtr sub_node = cur_node->children;
1755 if (sub_node->type == XML_ELEMENT_NODE) {
1757 if (strcmp((
char*)sub_node->name,
"command") == 0) {
1758 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"line");
1759 if (pc_temp ==
nullptr) {
1761 info_message(
"ERROR nica-scheduler$ 'line' attribute was not set for the command!", 1);
1765 shared_ptr<structJobStackPar> stack_par = make_shared<structJobStackPar>((
string)pc_temp);
1766 job_par->vecJobStack.push_back(stack_par);
1769 sub_node = sub_node->next;
1773 if (strcmp((
char*)sub_node->name,
"macro") == 0) {
1774 shared_ptr<structMacroPar> macro_par = make_shared<structMacroPar>();
1777 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"name");
1778 macro_par->nstrMacroName.SetValue(pc_temp);
1781 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"path");
1782 macro_par->nstrMacroPath.SetValue(pc_temp);
1784 if (macro_par->nstrMacroPath ==
nullptr) {
1787 "ERROR nica-scheduler$ Macro path was not set (please, add 'path' attribute)!", 1);
1792 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"start_event");
1793 if (pc_temp !=
nullptr) {
1794 macro_par->ptrStartEvent = make_unique<int>(atoi(pc_temp));
1799 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"event_count");
1800 if (pc_temp !=
nullptr) {
1803 info_message(
"ERROR nica-scheduler$ Event count must be a number!", 1);
1807 macro_par->ptrEventCount = make_unique<int>(atoi(pc_temp));
1808 if (*macro_par->ptrEventCount < 0) {
1810 info_message(
"ERROR nica-scheduler$ Event count must be a positive number or 0 "
1811 "(for all events)!",
1820 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"add_args");
1821 if (pc_temp !=
nullptr) {
1822 macro_par->strAddArgs = pc_temp;
1829 if (
ParseMacroFiles(sub_node->children, job_par, macro_par, mapJobName2FileOut, batch_com)
1833 job_par->vecMacros.push_back(macro_par);
1834 job_par->vecJobStack.push_back(make_shared<structJobStackPar>(macro_par));
1836 sub_node = sub_node->next;
1841 if (strcmp((
char*)sub_node->name,
"run") == 0) {
1843 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"mode");
1844 if (pc_temp !=
nullptr) {
1845 if (strcmp(pc_temp,
"global") == 0)
1846 job_par->iRunMode = 1;
1847 else if (strcmp(pc_temp,
"container") == 0)
1848 job_par->iRunMode = 2;
1855 job_par->nstrProcCount.SetValue((
char*)xmlGetProp(sub_node, (
unsigned char*)
"count"));
1858 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"queuelimit");
1859 if (pc_temp !=
nullptr) {
1862 info_message(
"ERROR nica-scheduler$ Queue limit must be a positive number!", 1);
1866 job_par->iQueueLimit = atoi(pc_temp);
1871 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"arraylimit");
1872 if (pc_temp !=
nullptr) {
1875 info_message(
"ERROR nica-scheduler$ Array limit must be a positive number!", 1);
1879 job_par->iArrayLimit = atoi(pc_temp);
1885 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"memory1");
1886 if (pc_temp !=
nullptr) {
1891 cout <<
"nica-scheduler$ WARNING: 'memory1' field contains incorrect format, then "
1898 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"config");
1899 if (pc_temp !=
nullptr) {
1900 job_par->strConfigPath = pc_temp;
1904 job_par->nstrPriority.SetValue((
char*)xmlGetProp(sub_node, (
unsigned char*)
"priority"));
1906 job_par->nstrLogs.SetValue((
char*)xmlGetProp(sub_node, (
unsigned char*)
"logs"));
1908 job_par->nstrQueue.SetValue((
char*)xmlGetProp(sub_node, (
unsigned char*)
"queue"));
1910 job_par->nstrQOS.SetValue((
char*)xmlGetProp(sub_node, (
unsigned char*)
"qos"));
1912 job_par->nstrWorkDir.SetValue((
char*)xmlGetProp(sub_node, (
unsigned char*)
"work_dir"));
1914 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"container_os");
1915 if (pc_temp !=
nullptr) {
1916 job_par->strContainerType = pc_temp;
1921 job_par->nstrHosts.SetValue((
char*)xmlGetProp(sub_node, (
unsigned char*)
"hosts"));
1924 replace(job_par->nstrHosts.GetValue().begin(), job_par->nstrHosts.GetValue().end(),
',',
1928 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"core_dump");
1929 if (pc_temp !=
nullptr) {
1930 if (strcmp(pc_temp,
"true") == 0)
1931 job_par->isCoreDump =
true;
1935 sub_node = sub_node->next;
1940 sub_node = sub_node->next;
1947 if (job_par->vecJobStack.size() == 0) {
1948 info_message(
"ERROR nica-scheduler$ No macros or commands in the job!", 1);
1953 info_message(
"ERROR nica-scheduler$ This job will be skipped because of errors above!", 1);
1954 cur_node = cur_node->next;
1959 unique_ptr<vector<nullString>> vecJobFileOut = make_unique<vector<nullString>>();
1962 int globalParallelCount = 0;
1965 if (job_par->vecMacros.size() == 0)
1966 globalParallelCount = 1;
1968 bool isJobSkipped =
false;
1971 for (
size_t ind_macro = 0; ind_macro < job_par->vecMacros.size(); ind_macro++) {
1972 int iTotalParallelCount = 0;
1974 shared_ptr<structMacroPar> cur_macro = job_par->vecMacros[ind_macro];
1977 for (
const auto& iter : cur_macro->vecFiles) {
1984 if (job_par->iRunMode == 0)
1988 if (job_par->nstrQueue ==
nullptr)
2012 if (ind_macro == (job_par->vecMacros.size() - 1))
2016 if ((ind_macro > 0) && (globalParallelCount != iTotalParallelCount)) {
2017 info_message(
"ERROR nica-scheduler$ The file count in macros of the job is not the same. This "
2018 "job will be skipped!",
2020 isJobSkipped =
true;
2024 globalParallelCount = iTotalParallelCount;
2028 cur_node = cur_node->next;
2033 mapJobName2FileOut.insert(
2034 pair<
string, unique_ptr<vector<nullString>>>(job_par->strJobName, std::move(vecJobFileOut)));
2038 job_par->strConfigPath =
trim(job_par->strConfigPath);
2039 if (job_par->strConfigPath !=
"") {
2041 if (job_par->strConfigPath[job_par->strConfigPath.length() - 1] ==
';')
2042 job_par->strConfigPath = job_par->strConfigPath.substr(0, job_par->strConfigPath.length() - 1);
2044 job_par->strConfigPath =
". " + job_par->strConfigPath +
"; ";
2046 if (job_par->nstrLogs !=
nullptr)
2048 (
string)
" > " + job_par->nstrLogs.GetValue() + (
string)
" 2>&1;");
2054 FILE* stream =
nullptr;
2056 if (job_par->iRunMode > 0) {
2058 string strApptainerRunFile =
"#!/bin/bash\n\n";
2059 if (job_par->iRunMode == 2) {
2060 strApptainerRunFile +=
"echo \"Starting Apptainer Job\"\n";
2061 if (job_par->strConfigPath !=
"")
2062 strApptainerRunFile += job_par->strConfigPath +
"\n\n";
2063 strApptainerRunFile +=
"RUN_CMD=\"root -q -b \"\n";
2064 strApptainerRunFile +=
"RUN_CMD+=\"${@}\"\n\n";
2066 strApptainerRunFile +=
2067 "echo \"DEBUG nica-scheduler: ROOT command in container = $RUN_CMD\"\n";
2068 strApptainerRunFile +=
"eval $RUN_CMD\n";
2069 strApptainerRunFile +=
"exit 0\n";
2074 if (job_par->nstrProcCount !=
nullptr) {
2076 if ((job_par->nstrProcCount.GetValue()[0] ==
'0')
2077 && (job_par->nstrProcCount.GetValue().length() == 1))
2079 if (job_par->nstrQueue ==
nullptr)
2084 if (proc_count == 0) {
2085 info_message(
"ERROR nica-scheduler$ Batch processors were not defined. This job will "
2088 cur_node = cur_node->next;
2092 cout <<
"nica-scheduler$ The batch queue has " << proc_count
2093 <<
" processor cores being available for user jobs." << endl;
2095 proc_count = atoi(job_par->nstrProcCount.GetValue().c_str());
2097 proc_count = globalParallelCount;
2099 if (proc_count == 0) {
2101 "ERROR nica-scheduler$ Processor count can not be equal 0. This job will be skipped!", 1);
2102 cur_node = cur_node->next;
2109 if (job_par->isCoreDump)
2110 strBatchFile +=
"export ENABLE_CORE_DUMP=1\n\n";
2112 strBatchFile +=
"ulimit -c 0\n\n";
2115 if ((job_par->iRunMode != 2) && (job_par->strConfigPath !=
""))
2116 strBatchFile += job_par->strConfigPath +
"\n\n";
2120 if (job_par->nstrPriority !=
nullptr)
2121 iPriority = atoi(job_par->nstrPriority.GetValue().c_str());
2124 if ((globalParallelCount > 0) && (proc_count > globalParallelCount))
2125 proc_count = globalParallelCount;
2128 vector<int> vecMergeMode;
2129 vector<nullString> vecPartialOutputFiles;
2132 if (globalParallelCount > 0) {
2134 for (
const auto& cur_macro : job_par->vecMacros) {
2136 for (
const auto& cur_file : cur_macro->vecFiles) {
2138 string strRealInputFile = cur_file->strFileIn;
2140 nullString nstrRealOutputFile = cur_file->nstrFileOut;
2144 if (cur_file->strPutPath !=
"")
2145 strRealInputFile = cur_file->strPutPath;
2148 if (cur_file->strGetPath !=
"")
2149 nstrRealOutputFile.
SetValue(cur_file->strGetPath);
2152 int* cur_start_event = cur_macro->ptrStartEvent.get();
2153 if (cur_file->ptrStartEvent !=
nullptr)
2154 cur_start_event = cur_file->ptrStartEvent.get();
2157 int* cur_event_count = cur_macro->ptrEventCount.get();
2158 if (cur_file->ptrEventCount !=
nullptr)
2159 cur_event_count = cur_file->ptrEventCount.get();
2162 if ((cur_file->iParallelMode > 1) && (cur_start_event ==
nullptr)) {
2163 cur_file->ptrStartEvent = make_unique<int>(0);
2164 cur_start_event = cur_file->ptrStartEvent.get();
2165 cout <<
"nica-scheduler$ WARNING: parallel_mode is used, but start event is not "
2166 "set, so it is assigned to 0"
2170 if ((cur_file->iParallelMode > 1) && (cur_event_count ==
nullptr)) {
2171 cur_file->ptrEventCount = make_unique<int>(0);
2172 cur_event_count = cur_file->ptrEventCount.get();
2173 cout <<
"nica-scheduler$ WARNING: parallel_mode is used, but event count is not "
2174 "set, so it is assigned to 0"
2179 string strOutputUnion = nstrRealOutputFile.
GetValue();
2182 int start_event = -1, event_count = -1;
2183 if (cur_start_event !=
nullptr)
2184 start_event = *cur_start_event;
2185 if (cur_event_count !=
nullptr)
2186 event_count = *cur_event_count;
2190 if ((cur_file->iParallelMode > 1) && (event_count == 0)) {
2191 string config_paths = job_par->strConfigPath;
2193 if (job_par->nstrLogs ==
nullptr)
2195 stringstream ssEventNumberCommand;
2196 ssEventNumberCommand << config_paths <<
"show_event_count auto \""
2197 << strRealInputFile <<
"\" 2> /dev/null";
2199 cout <<
"DEBUG nica-scheduler$ trying to get event count from the input file: "
2200 << ssEventNumberCommand.str() << endl;
2202 array<char, 128> bufferCommand;
2203 string str_event_number =
"";
2204 stream = popen(ssEventNumberCommand.str().c_str(),
"r");
2205 while (fgets(bufferCommand.data(),
static_cast<int>(bufferCommand.size()), stream)
2207 str_event_number += bufferCommand.data();
2210 if (str_event_number ==
"") {
2212 "ERROR nica-scheduler$ parallel_mode is used, but event_count "
2213 "is not set and cannot be automatically defined. "
2214 "'%s' file will be skipped!",
2215 strRealInputFile.c_str())
2220 event_count = atoi(str_event_number.c_str());
2222 cout <<
"DEBUG nica-scheduler$ " << event_count
2223 <<
" event(s) found in the input file" << endl;
2226 int merge_proc_count = 0;
2228 for (
int j = 0; j < cur_file->iParallelMode; j++) {
2229 int event_per_proc = event_count;
2230 if (cur_file->iParallelMode > 1) {
2231 event_per_proc = (event_count + j) / cur_file->iParallelMode;
2233 if (event_per_proc == 0) {
2236 "ERROR nica-scheduler$ No events for the parallel part %d of "
2237 "'%s' file, the task indexing can be corrupted!",
2238 j + 1, strRealInputFile.c_str())
2241 start_event = start_event + event_per_proc;
2248 nullString nstr_output_file = nstrRealOutputFile;
2251 strBatchInitInputFile +=
"\"";
2252 strBatchInitInputFile += strRealInputFile;
2253 strBatchInitInputFile +=
"\" ";
2256 if (nstr_output_file !=
nullptr) {
2258 if (cur_file->iParallelMode > 1)
2261 strBatchInitOutputFile +=
"\"";
2262 strBatchInitOutputFile += nstr_output_file.
GetValue();
2263 strBatchInitOutputFile +=
"\" ";
2265 strBatchInitOutputFile +=
"\">\" ";
2268 if (cur_start_event !=
nullptr) {
2270 strBatchInitStartEvent +=
"\"";
2271 strBatchInitStartEvent += strInt;
2272 strBatchInitStartEvent +=
"\" ";
2274 strBatchInitStartEvent +=
"\">\" ";
2277 if (cur_event_count !=
nullptr) {
2279 strBatchInitEventCount +=
"\"";
2280 strBatchInitEventCount += strInt;
2281 strBatchInitEventCount +=
"\" ";
2283 strBatchInitEventCount +=
"\">\" ";
2286 if (cur_macro->strAddArgs !=
"") {
2287 strBatchInitAddArgs +=
"\"";
2288 string mask_string = cur_macro->strAddArgs;
2290 strBatchInitAddArgs += mask_string;
2291 strBatchInitAddArgs +=
"\" ";
2293 strBatchInitAddArgs +=
"\">\" ";
2295 bool is_clean =
false;
2298 if (cur_file->strPutPath !=
"") {
2300 strBatchInitPut +=
"\"";
2304 strBatchInitPut += cur_file->strPutCommand;
2305 strBatchInitPut +=
" ";
2306 strBatchInitPut += cur_file->strFileIn;
2307 strBatchInitPut +=
" ";
2308 strBatchInitPut += cur_file->strPutPath;
2310 if (cur_file->iParallelMode > 1) {
2311 strBatchInitPut +=
"; touch ";
2312 strBatchInitPut += cur_file->strPutPath + (string)
".lock\" ";
2314 if (is_clean ==
false)
2315 strBatchInitClean +=
"\"";
2317 strBatchInitClean +=
" ";
2318 strBatchInitClean += cur_file->strPutPath + (string)
".lock\" ";
2321 strBatchInitPut +=
"\" ";
2326 strBatchInitPut +=
"\"";
2327 strBatchInitPut +=
"wait ";
2328 strBatchInitPut += cur_file->strPutPath;
2329 strBatchInitPut +=
".lock\" ";
2332 strBatchInitPut +=
"\">\" ";
2335 if (cur_file->strGetPath !=
"") {
2338 if (strFileOutDir ==
"")
2339 strFileOutDir =
".";
2341 strBatchInitGet +=
"\"";
2343 strBatchInitGet +=
"mkdir -p " + strFileOutDir +
"; ";
2348 for (
const string& cur_extra_path : cur_file->vecGetExtraPath) {
2349 strBatchInitGet +=
"set +f; for extra_file in ";
2350 strBatchInitGet += cur_extra_path;
2351 strBatchInitGet +=
"; do if [ -e \\$extra_file ]; then ";
2352 strBatchInitGet += cur_file->strGetCommand;
2353 strBatchInitGet +=
" \\$extra_file ";
2355 strFileOutDir + string(
"/") +
"\\$(basename \\$extra_file)";
2356 strBatchInitGet +=
"; rm -rf \\$extra_file; fi; done; set -f; ";
2359 strBatchInitGet += cur_file->strGetCommand;
2360 strBatchInitGet +=
" ";
2361 if (cur_file->iParallelMode > 1) {
2362 strBatchInitGet += nstr_output_file.
GetValue();
2363 strBatchInitGet +=
" ";
2364 strBatchInitGet += strFileOutDir + string(
"/")
2367 strBatchInitGet += cur_file->strGetPath;
2368 strBatchInitGet +=
" ";
2369 strBatchInitGet += cur_file->nstrFileOut.GetValue();
2371 strBatchInitGet +=
"\" ";
2373 strBatchInitGet +=
"\">\" ";
2377 if (cur_file->strPutPath !=
"") {
2378 if (is_clean ==
false)
2379 strBatchInitClean +=
"\"";
2381 strBatchInitClean +=
" ";
2382 strBatchInitClean += cur_file->strPutPath;
2386 if (cur_file->strGetPath !=
"") {
2387 if (is_clean ==
false)
2388 strBatchInitClean +=
"\"";
2390 strBatchInitClean +=
" ";
2391 strBatchInitClean += cur_file->strGetPath;
2395 for (
const auto& str_clean_path : cur_file->vecCleanPath) {
2396 if (is_clean ==
false)
2397 strBatchInitClean +=
"\"";
2399 strBatchInitClean +=
" ";
2400 strBatchInitClean += str_clean_path;
2405 strBatchInitClean +=
"\" ";
2408 if (nstr_output_file !=
nullptr)
2409 strOutputUnion +=
" " + nstr_output_file.
GetValue();
2411 start_event = start_event + event_per_proc;
2414 if ((merge_proc_count > 1) && (cur_file->iMerge >= 0)) {
2418 vecPartialOutputFiles.push_back(strOutputUnion);
2419 vecMergeMode.push_back(cur_file->iMerge);
2426 if (job_par->iRunMode == 2)
2428 strBatchFile +=
"export CONTAINER_PATH=";
2431 strBatchFile += it->second;
2434 job_par->strContainerType.c_str()))
2439 strBatchFile +=
"\n\n";
2443 if (job_par->vecMacros.size() > 0) {
2444 strBatchFile += strBatchInitInputFile;
2445 strBatchFile +=
")\n\n";
2446 strBatchFile += strBatchInitOutputFile;
2447 strBatchFile +=
")\n\n";
2448 strBatchFile += strBatchInitStartEvent;
2449 strBatchFile +=
")\n\n";
2450 strBatchFile += strBatchInitEventCount;
2451 strBatchFile +=
")\n\n";
2452 strBatchFile += strBatchInitAddArgs;
2453 strBatchFile +=
")\n\n";
2454 strBatchFile += strBatchInitPut, strBatchFile +=
")\n\n";
2455 strBatchFile += strBatchInitGet, strBatchFile +=
")\n\n";
2456 strBatchFile += strBatchInitClean, strBatchFile +=
")\n\n";
2457 strBatchFile +=
": ${NICA_ARRAY_OFFSET:=0}\n\n";
2461 int start_line_number = 0;
2462 for (
size_t ind_stack = 0; ind_stack < job_par->vecJobStack.size(); ind_stack++) {
2468 strBatchFile +=
"echo \"DEBUG nica-scheduler: Run Command = ";
2470 strBatchFile +=
"\"\n";
2474 strBatchFile +=
"\n\n";
2481 strBatchFile +=
"let \"array_index = ";
2483 strBatchFile +=
" - 1 + ${NICA_ARRAY_OFFSET} + ";
2484 strBatchFile += to_string(start_line_number);
2485 strBatchFile +=
"\"\n\n";
2486 for (
const auto& iter : cur_macro->
vecFiles)
2487 start_line_number += iter->iParallelMode;
2491 strBatchFile +=
"element=${PutArray[${array_index}]}\n"
2492 "if [ \"${element}\" != \">\" ]\nthen\n"
2493 " # Check if PutElement contains \"wait <file_path>\"\n"
2494 " if [[ \"${element}\" == *\"wait \"* ]]; then\n"
2495 " # Extract file path after \"wait \"\n"
2496 " lock_file_path=\"${1#wait }\"\n"
2497 " # Wait until file appears\n"
2498 " while [ ! -f \"$lock_file_path\" ]; do\n";
2500 strBatchFile +=
" echo \"DEBUG nica-scheduler: wait while putting file\"\n";
2501 strBatchFile +=
" sleep 5\n"
2504 " set -f # avoid globbing (expansion of *)\n"
2505 " # arr=(${element// / })\n"
2506 " # comm=\"${arr[0]} ${arr[1]} ${arr[2]}\"\n"
2509 strBatchFile +=
" echo \"DEBUG nica-scheduler: PUT command = ${element}\"\n";
2510 strBatchFile +=
" eval ${element}\n"
2515 strBatchFile +=
"element=${InputFileArray[${array_index}]}\n";
2518 if (job_par->iRunMode == 1) {
2519 strBatchFile +=
"export ROOT_HIST=0\n";
2520 strBatchFile +=
"comm=\"root -b -q '";
2522 strBatchFile +=
"(\\\"${element}\\\"\"\n"
2523 "element=${OutputFileArray[${array_index}]}\n"
2524 "if [ \"${element}\" != \">\" ]\nthen\n"
2525 " mkdir -p \"$(dirname \"${element}\")\"\n"
2526 " comm=\"${comm},\\\"${element}\\\"\"\n";
2530 strBatchFile +=
"comm=\"'";
2533 strBatchFile +=
"(\\\\\\\"${element}\\\\\\\"\"\n"
2534 "element=${OutputFileArray[${array_index}]}\n"
2535 "if [ \"${element}\" != \">\" ]\nthen\n"
2536 " mkdir -p \"$(dirname \"${element}\")\"\n"
2537 " comm=\"${comm},\\\\\\\"${element}\\\\\\\"\"\n";
2541 strBatchFile +=
"fi\n"
2542 "element=${StartEventArray[${array_index}]}\n"
2543 "if [ \"${element}\" != \">\" ]\nthen\n"
2544 " comm=\"${comm},${element}\"\n"
2546 "element=${EventCountArray[${array_index}]}\n"
2547 "if [ \"${element}\" != \">\" ]\nthen\n"
2548 " comm=\"${comm},${element}\"\n"
2550 "element=${AddArgsArray[${array_index}]}\n"
2551 "if [ \"${element}\" != \">\" ]\nthen\n"
2552 " comm=\"${comm},${element}\"\n"
2554 "comm=\"$comm)'\"\n\n";
2557 strBatchFile +=
" echo \"DEBUG nica-scheduler: Run Command = $comm\"\n";
2559 if (job_par->iRunMode == 1)
2560 strBatchFile +=
"eval $comm\n\n";
2564 strBatchFile +=
" apptainer run -B \"/eos,/cvmfs\" --fakeroot \"$CONTAINER_PATH\" ";
2565 strBatchFile += exe_dir +
"apptainer_run.sh " +
"\"$@\" \"$comm\" \n\n";
2569 strBatchFile +=
"GET_FAILED=0\n"
2570 "element=${GetArray[${array_index}]}\n"
2571 "if [ \"${element}\" != \">\" ]\nthen\n"
2572 " set -f # avoid globbing (expansion of *)\n"
2573 "# arr=(${element// / })\n"
2574 "# comm=\"${arr[0]} ${arr[1]} ${arr[2]}\"\n"
2577 strBatchFile +=
" echo \"DEBUG nica-scheduler: GET command = ${element}\"\n";
2578 strBatchFile +=
" eval ${element}\n"
2579 " if [ $? -ne 0 ]; then\n"
2580 " echo \"ERROR nica-scheduler: copying of the results (GET) failed, "
2581 "temporary files will be preserved\"\n"
2588 strBatchFile +=
"element=${CleanArray[${array_index}]}\n"
2589 "if [ \"${element}\" != \">\" ] && [ \"${GET_FAILED}\" != \"1\" ]\nthen\n"
2590 " set -f # avoid globbing (expansion of *)\n"
2591 " arr=(${element// / })\n"
2592 " comm=\"rm -rf \"\n"
2593 " for i in \"${arr[@]}\"\n"
2595 " comm=\"${comm}${i} \"\n"
2598 strBatchFile +=
" echo \"DEBUG nica-scheduler: CLEAN command = $comm\"\n";
2599 strBatchFile +=
" eval $comm\n"
2603 if (job_par->iRunMode == 1)
2605 strBatchFile +=
"echo \" End date: `date`\"\nexit 0\n";
2608 if (job_par->iRunMode == 2)
2610 strBatchFile +=
"echo \" End date: `date`\"\nexit 0\n";
2612 string apptainer_run_file_path = exe_dir + (string)
"apptainer_run" + (
string)
".sh";
2613 FILE* pJobContFile = fopen(apptainer_run_file_path.c_str(),
"wt");
2614 if (pJobContFile ==
nullptr) {
2616 "This job will be skipped!",
2617 apptainer_run_file_path.c_str())
2620 cur_node = cur_node->next;
2624 fwrite(strApptainerRunFile.c_str(), strApptainerRunFile.length(),
sizeof(
char), pJobContFile);
2625 fclose(pJobContFile);
2627 chmod(apptainer_run_file_path.c_str(), S_IRWXU);
2635 stringstream ssMainScriptFile;
2636 ssMainScriptFile <<
"job_" << rand() << rand() <<
".sh";
2637 stringstream ssMainScriptPath;
2638 ssMainScriptPath << exe_dir << ssMainScriptFile.str();
2639 FILE* pJobFile = fopen(ssMainScriptPath.str().c_str(),
"wt");
2640 if (pJobFile ==
nullptr) {
2641 string error_path = ssMainScriptPath.str();
2643 ssMainScriptPath.str(
"");
2644 ssMainScriptPath.clear();
2645 ssMainScriptPath << ssMainScriptFile.str();
2647 pJobFile = fopen(ssMainScriptPath.str().c_str(),
"wt");
2648 if (pJobFile ==
nullptr) {
2649 error_path += (string)
" || " + ssMainScriptPath.str();
2651 ssMainScriptPath.str(
"");
2652 ssMainScriptPath.clear();
2654 pJobFile = fopen(ssMainScriptPath.str().c_str(),
"wt");
2655 if (pJobFile ==
nullptr) {
2656 error_path += (string)
" || " + ssMainScriptPath.str();
2658 "This job will be skipped!",
2662 cur_node = cur_node->next;
2667 fwrite(strBatchFile.c_str(), strBatchFile.length(),
sizeof(
char), pJobFile);
2674 int array_limit = job_par->iArrayLimit;
2675 if ((array_limit <= 0) || (array_limit > globalParallelCount))
2676 array_limit = globalParallelCount;
2678 vector<string> vecSubmittedJobID;
2679 bool is_submit_error =
false;
2682 for (
int array_offset = 0, array_part = 1; array_offset < globalParallelCount;
2683 array_offset += array_limit, array_part++)
2687 is_submit_error =
true;
2691 int current_array_count = globalParallelCount - array_offset;
2692 if (current_array_count > array_limit)
2693 current_array_count = array_limit;
2695 int current_proc_count = proc_count;
2696 if (current_proc_count > current_array_count)
2697 current_proc_count = current_array_count;
2699 string strBatchJobName = job_par->strJobName;
2700 if (array_limit < globalParallelCount)
2704 stringstream ssBatchCommand;
2707 job_par->strDdependencyName.c_str(), current_array_count,
2717 if (job_par->nstrQueue !=
nullptr)
2719 job_par->nstrQueue.GetValue().c_str())
2723 if (job_par->nstrQOS !=
nullptr)
2725 job_par->nstrQOS.GetValue().c_str())
2729 if (job_par->nstrHosts !=
nullptr) {
2731 if (job_par->nstrHosts.GetValue()[0] ==
'~')
2733 job_par->nstrHosts.GetValue().substr(1).c_str())
2738 job_par->nstrHosts.GetValue().c_str())
2743 if (job_par->nstrWorkDir !=
nullptr)
2745 job_par->nstrWorkDir.GetValue().c_str())
2749 if (job_par->strOperativeMemory1 !=
"")
2751 job_par->strOperativeMemory1.c_str())
2756 ssBatchCommand << batch_com.
set_job_variables <<
"\"ALL,NICA_ARRAY_OFFSET=" << array_offset
2759 ssBatchCommand << batch_com.
set_job_variables <<
"\"NICA_ARRAY_OFFSET=" << array_offset
2763 ssBatchCommand <<
" " << ssMainScriptPath.str();
2767 cout <<
"DEBUG nica-scheduler$ Batch command: " << ssBatchCommand.str() << endl;
2769 array<char, 128> bufferBatch;
2770 string localID =
"";
2771 stream = popen(ssBatchCommand.str().c_str(),
"r");
2772 while (fgets(bufferBatch.data(),
static_cast<int>(bufferBatch.size()), stream) !=
nullptr)
2773 localID += bufferBatch.data();
2778 cout <<
"DEBUG nica-scheduler$ Batch command output = " << localID << endl;
2781 if (localID ==
"") {
2782 info_message(
"ERROR nica-scheduler$ Job ID was not found: "
2783 "an error occurred or there is no batch system. This job will be skipped!",
2785 is_submit_error =
true;
2789 vecSubmittedJobID.push_back(localID);
2791 "Enter '%s' command to check status.",
2792 strBatchJobName.c_str(), localID.c_str(),
2797 if (is_submit_error || vecSubmittedJobID.empty()) {
2799 for (
const string& submitted_id : vecSubmittedJobID) {
2802 if (cancel_return != 0)
2804 "ID: %s was not cancelled, please cancel it manually!",
2805 submitted_id.c_str())
2810 "has been cancelled because of the errors above.",
2811 submitted_id.c_str())
2815 cur_node = cur_node->next;
2826 mapJobName2ID.insert(pair<string, string>(job_par->strJobName, ID));
2827 if (!vecPartialOutputFiles.empty()) {
2830 for (
size_t i = 0;
i < vecPartialOutputFiles.size();
i++) {
2838 string MERGEjob_path = exe_dir +
"merge.sh";
2839 stringstream ssMergeCommand;
2842 << vecPartialOutputFiles.at(
i).GetValue() <<
"\",MergeMode=\""
2843 << vecMergeMode.at(
i) <<
"\",ConfigFiles=\"" << job_par->strConfigPath
2844 <<
"\" " << MERGEjob_path;
2846 array<char, 128> bufferMerge;
2847 string localID =
"";
2848 stream = popen(ssMergeCommand.str().c_str(),
"r");
2849 while (fgets(bufferMerge.data(),
static_cast<int>(bufferMerge.size()), stream) !=
nullptr)
2850 localID += bufferMerge.data();
2854 if (localID ==
"") {
2855 info_message(
"ERROR nica-scheduler$ Union job ID was not found: an error occurred or "
2856 "there is no batch system.",
2861 cout <<
"nica-scheduler$ Task for merging parallel files has ID: " << localID << endl;
2869 if ((job_par->iQueueLimit > 0) || (job_par->iArrayLimit > 0))
2870 cout <<
"nica-scheduler$ WARNING: 'queuelimit' and 'arraylimit' are ignored in local mode"
2873 mapJobName2ID.insert(pair<string, string>(job_par->strJobName,
"dummy"));
2877 if (job_par->nstrProcCount !=
nullptr) {
2878 if ((job_par->nstrProcCount.GetValue()[0] ==
'0')
2879 && (job_par->nstrProcCount.GetValue().length()) == 1)
2883 if (proc_count == 0) {
2885 "ERROR nica-scheduler$ Core number was not defined. This job will be skipped!", 1);
2886 cur_node = cur_node->next;
2890 proc_count = atoi(job_par->nstrProcCount.GetValue().c_str());
2892 proc_count = globalParallelCount == 0 ? 1 : globalParallelCount;
2894 if (proc_count == 0) {
2896 "ERROR nica-scheduler$ Process count can not be equal 0. This job will be skipped!", 1);
2897 cur_node = cur_node->next;
2902 if (proc_count > globalParallelCount)
2903 proc_count = globalParallelCount;
2905 vector<thread> vecThreads;
2907 shared_ptr<Semaphore> job_semaphore = make_shared<Semaphore>(proc_count);
2908 shared_ptr<mutex> job_mutex = make_shared<mutex>();
2911 unsigned int thread_count = 1;
2912 if (job_par->vecMacros.size() > 0) {
2913 if (job_par->vecMacros[0]->vecFiles.size() > 1)
2914 thread_count = job_par->vecMacros[0]->vecFiles.size();
2918 for (
unsigned int i = 0;
i < thread_count;
i++) {
2919 shared_ptr<structThreadPar> thread_par = make_shared<structThreadPar>();
2920 thread_par->sptrJobParameter = job_par;
2921 thread_par->semJobSemaphore = job_semaphore;
2922 thread_par->mutJobMutex = job_mutex;
2923 thread_par->iThreadCounter =
i + 1;
2925 job_semaphore->acquire();
2930 }
catch (
const system_error& e) {
2932 "= %d and message = %s",
2933 e.code().value(), e.what()))
2942 for (
auto& cur_thread : vecThreads) {
2945 }
catch (
const system_error& e) {
2947 (
TString::Format(
"ERROR nica-scheduler$ Thread failed with code = %d and message = %s",
2948 e.code().value(), e.what()))
2954 cout <<
"nica-scheduler$ Local job has been finished" << endl;
2959 cur_node = cur_node->next;
2962 mapJobName2ID.clear();
2963 mapJobName2FileOut.clear();
2971 istringstream ss(input);
2976 bool isGen =
false, isEnergy =
false, isMinEnergy =
false, isMaxEnergy =
false, isBeam =
false,
2977 isTarget =
false, isDesc =
false, isPath =
false;
2978 string strGen, strBeam, strTarget, strDesc, strPath;
2979 double fEnergy, fMaxEnergy;
2981 while (getline(ss, token,
',')) {
2983 transform(token.begin(), token.end(), token.begin(), ::tolower);
2986 if ((token.length() > 4) && (token.substr(0, 4) ==
"gen=")) {
2988 strGen = token.substr(4);
2991 if ((token.length() > 7) && (token.substr(0, 7) ==
"energy=")) {
2992 token = token.substr(7);
2994 size_t indDash = token.find_first_of(
'-');
2995 if (indDash != string::npos) {
2996 stringstream stream;
2997 stream << token.substr(0, indDash);
2999 if (stream >> dVal) {
3004 if (token.length() > indDash) {
3005 stringstream stream2;
3006 stream2 << token.substr(indDash + 1);
3007 if (stream2 >> dVal) {
3016 stringstream stream;
3019 if (stream >> dVal) {
3027 if ((token.length() > 5) && (token.substr(0, 5) ==
"beam=")) {
3029 strBeam = token.substr(5);
3032 if ((token.length() > 5) && (token.substr(0, 5) ==
"desc=")) {
3034 strDesc = token.substr(5);
3037 if ((token.length() > 7) && (token.substr(0, 7) ==
"target=")) {
3039 strTarget = token.substr(7);
3042 if ((token.length() > 5) && (token.substr(0, 5) ==
"path=")) {
3044 strPath = token.substr(5);
3054 sql =
"select file_path "
3055 "from simulation_file";
3057 bool isWhere =
false;
3059 if (isGen ==
true) {
3061 sql +=
TString::Format(
" AND lower(generator_name) = '%s'", strGen.data());
3065 "where lower(generator_name) = '%s'",
3070 if (isEnergy ==
true) {
3091 if (isBeam ==
true) {
3093 sql +=
TString::Format(
" AND lower(beam_particle) = '%s'", strBeam.data());
3097 "where lower(beam_particle) = '%s'",
3102 if (isTarget ==
true) {
3104 sql +=
TString::Format(
" AND lower(target_particle) = '%s'", strTarget.data());
3108 "where lower(target_particle) = '%s'",
3112 if (isDesc ==
true) {
3114 sql +=
TString::Format(
" AND lower(file_desc) like '%%%s%%'", strDesc.data());
3118 "where lower(file_desc) like '%%%s%%'",
3122 if (isPath ==
true) {
3124 sql +=
TString::Format(
" AND lower(file_path) like '%%%s%%'", strPath.data());
3128 "where lower(file_path) like '%%%s%%'",
3132 sql +=
" order by generator_name,energy";
3136 bool isPeriod =
false, isMinPeriod =
false, isMaxPeriod =
false, isRun =
false, isMinRun =
false,
3137 isMaxRun =
false, isBeam =
false, isTarget =
false, isEnergy =
false, isMinEnergy =
false,
3138 isMaxEnergy =
false, isEvents =
false, isMinEvents =
false, isMaxEvents =
false, isTime =
false,
3139 isMinTime =
false, isMaxTime =
false, isField =
false, isMinField =
false, isMaxField =
false,
3140 isSize =
false, isMinSize =
false, isMaxSize =
false, isPath =
false;
3141 string strBeam, strTarget, strTime, strMaxTime, strPath;
3142 int iPeriod, iMaxPeriod, iRun, iMaxRun, iEvents, iMaxEvents, iField, iMaxField;
3143 double fEnergy, fMaxEnergy, fSize, fMaxSize;
3145 while (getline(ss, token,
',')) {
3147 transform(token.begin(), token.end(), token.begin(), ::tolower);
3150 if ((token.length() > 7) && (token.substr(0, 7) ==
"period=")) {
3151 token = token.substr(7);
3153 size_t indDash = token.find_first_of(
'-');
3154 if (indDash != string::npos) {
3155 stringstream stream;
3156 stream << token.substr(0, indDash);
3158 if (stream >> iVal) {
3163 if (token.length() > indDash) {
3164 stringstream stream2;
3165 stream2 << token.substr(indDash + 1);
3166 if (stream2 >> iVal) {
3175 stringstream stream;
3178 if (stream >> iVal) {
3186 if ((token.length() > 4) && (token.substr(0, 4) ==
"run=")) {
3187 token = token.substr(4);
3189 size_t indDash = token.find_first_of(
'-');
3190 if (indDash != string::npos) {
3191 stringstream stream;
3192 stream << token.substr(0, indDash);
3194 if (stream >> iVal) {
3199 if (token.length() > indDash) {
3200 stringstream stream2;
3201 stream2 << token.substr(indDash + 1);
3202 if (stream2 >> iVal) {
3211 stringstream stream;
3214 if (stream >> iVal) {
3222 if ((token.length() > 5) && (token.substr(0, 5) ==
"beam=")) {
3224 strBeam = token.substr(5);
3227 if ((token.length() > 7) && (token.substr(0, 7) ==
"target=")) {
3229 strTarget = token.substr(7);
3232 if ((token.length() > 7) && (token.substr(0, 7) ==
"energy=")) {
3233 token = token.substr(7);
3235 size_t indDash = token.find_first_of(
'-');
3236 if (indDash != string::npos) {
3237 stringstream stream;
3238 stream << token.substr(0, indDash);
3240 if (stream >> dVal) {
3245 if (token.length() > indDash) {
3246 stringstream stream2;
3247 stream2 << token.substr(indDash + 1);
3248 if (stream2 >> dVal) {
3257 stringstream stream;
3260 if (stream >> dVal) {
3268 if ((token.length() > 6) && (token.substr(0, 6) ==
"events=")) {
3269 token = token.substr(6);
3271 size_t indDash = token.find_first_of(
'-');
3272 if (indDash != string::npos) {
3273 stringstream stream;
3274 stream << token.substr(0, indDash);
3276 if (stream >> iVal) {
3281 if (token.length() > indDash) {
3282 stringstream stream2;
3283 stream2 << token.substr(indDash + 1);
3284 if (stream2 >> iVal) {
3293 stringstream stream;
3296 if (stream >> iVal) {
3304 if ((token.length() > 5) && (token.substr(0, 5) ==
"time=")) {
3305 token = token.substr(5);
3307 size_t indDash = token.find_first_of(
'-');
3308 if (indDash != string::npos) {
3312 strTime = token.substr(0, indDash);
3314 if (token.length() > indDash) {
3317 strMaxTime = token.substr(indDash + 1);
3328 if ((token.length() > 6) && (token.substr(0, 6) ==
"field=")) {
3329 token = token.substr(6);
3331 size_t indDash = token.find_first_of(
'-');
3332 if (indDash != string::npos) {
3333 stringstream stream;
3334 stream << token.substr(0, indDash);
3336 if (stream >> iVal) {
3341 if (token.length() > indDash) {
3342 stringstream stream2;
3343 stream2 << token.substr(indDash + 1);
3344 if (stream2 >> iVal) {
3353 stringstream stream;
3356 if (stream >> iVal) {
3364 if ((token.length() > 5) && (token.substr(0, 5) ==
"size=")) {
3365 token = token.substr(5);
3367 size_t indDash = token.find_first_of(
'-');
3368 if (indDash != string::npos) {
3369 stringstream stream;
3370 stream << token.substr(0, indDash);
3372 if (stream >> dVal) {
3377 if (token.length() > indDash) {
3378 stringstream stream2;
3379 stream2 << token.substr(indDash + 1);
3380 if (stream2 >> dVal) {
3389 stringstream stream;
3392 if (stream >> dVal) {
3400 if ((token.length() > 5) && (token.substr(0, 5) ==
"path=")) {
3402 strPath = token.substr(5);
3416 sql =
"select file_path "
3419 bool isWhere =
false;
3421 if (isPeriod ==
true) {
3442 if (isRun ==
true) {
3463 if (isBeam ==
true) {
3465 sql +=
TString::Format(
" AND lower(beam_particle) = '%s'", strBeam.data());
3469 "where lower(beam_particle) = '%s'",
3474 if (isTarget ==
true) {
3476 sql +=
TString::Format(
" AND lower(target_particle) = '%s'", strTarget.data());
3480 "where lower(target_particle) = '%s'",
3485 if (isEnergy ==
true) {
3506 if (isEvents ==
true) {
3527 if (isTime ==
true) {
3538 if (!strptime(strMaxTime.c_str(),
"%Y-%m-%d %H:%M:%S", &tm)) {
3540 cout <<
"Error: " << strMaxTime
3541 <<
" is not correct datetime. DateTime format should be yyyy-mm-dd 24hh:mm:ss." << endl;
3543 if (!strptime(strTime.c_str(),
"%Y-%m-%d %H:%M:%S", &tm)) {
3545 cout <<
"Error: " << strTime
3546 <<
" is not correct datetime. DateTime format should be yyyy-mm-dd 24hh:mm:ss." << endl;
3554 sql +=
TString::Format(
" AND start_datetime <= '%s'", strMaxTime.c_str());
3560 sql +=
TString::Format(
"start_datetime <= '%s' AND end_datetime >= '%s'", strTime.c_str(),
3566 if (isField ==
true) {
3587 if (isSize ==
true) {
3607 if (isPath ==
true) {
3609 sql +=
TString::Format(
" AND lower(file_path) like '%%%s%%'", strPath.data());
3613 "where lower(file_path) like '%%%s%%'",
3618 sql +=
" order by period_number,run_number";
3626 shared_ptr<structJobPar> job_par,
3627 shared_ptr<structMacroPar> macro_par,
3628 multimap<
string, unique_ptr<vector<nullString>>>& mapJobName2FileOut,
3631 char* pc_temp =
nullptr;
3634 if (strcmp((
char*)sub_node->name,
"file") == 0) {
3635 bool isStartEvent =
false, isEventCount =
false;
3636 int start_event, event_count;
3637 string sim_db =
"", exp_db =
"", job_input =
"", macro_input =
"", file_input =
"", json_input =
"",
3638 put_command =
"cp", put_path =
"", get_command =
"cp", get_path =
"", get_extras =
"",
3639 get_output =
"", clean_path =
"", parallel_mode =
"";
3643 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"input");
3644 if (pc_temp !=
nullptr) {
3645 nstr_file_in.
SetValue((
string)pc_temp);
3649 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"output");
3650 if (pc_temp !=
nullptr) {
3651 nstr_file_out.
SetValue((
string)pc_temp);
3656 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"start_event");
3657 if (pc_temp !=
nullptr) {
3658 start_event = atoi(pc_temp);
3660 isStartEvent =
true;
3664 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"event_count");
3665 if (pc_temp !=
nullptr) {
3667 info_message(
"ERROR nica-scheduler$ Event count must be a number!", 1);
3671 event_count = atoi(pc_temp);
3673 if (event_count < 0) {
3674 info_message(
"ERROR nica-scheduler$ Event count must be a positive number or 0 (for all events)!",
3678 isEventCount =
true;
3685 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"merge");
3687 if (pc_temp !=
nullptr) {
3689 if (strcmp(lower_merge,
"true") == 0)
3691 if (strcmp(lower_merge,
"chain") == 0)
3693 if (strcmp(lower_merge,
"preserve") == 0)
3700 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"parallel_mode");
3701 if (pc_temp !=
nullptr) {
3702 parallel_mode = pc_temp;
3707 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"sim_input");
3708 if (pc_temp !=
nullptr) {
3713 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"exp_input");
3714 if (pc_temp !=
nullptr) {
3719 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"job_input");
3720 if (pc_temp !=
nullptr) {
3721 job_input = pc_temp;
3725 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"macro_input");
3726 if (pc_temp !=
nullptr) {
3727 macro_input = pc_temp;
3731 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"file_input");
3732 if (pc_temp !=
nullptr) {
3733 file_input = pc_temp;
3737 pc_temp = (
char*)xmlGetProp(sub_node, (
unsigned char*)
"json_input");
3738 if (pc_temp !=
nullptr) {
3739 json_input = pc_temp;
3745 xmlNodePtr sub_file_node = sub_node->children;
3746 while (sub_file_node) {
3748 if (sub_file_node->type == XML_ELEMENT_NODE) {
3750 if (strcmp((
char*)sub_file_node->name,
"put") == 0) {
3752 pc_temp = (
char*)xmlGetProp(sub_file_node, (
unsigned char*)
"command");
3753 if (pc_temp !=
nullptr) {
3754 put_command = pc_temp;
3758 pc_temp = (
char*)xmlGetProp(sub_file_node, (
unsigned char*)
"directory");
3759 if (pc_temp ==
nullptr) {
3760 info_message(
"ERROR nica-scheduler$ There is no 'directory' property for the <put/> tag!",
3762 sub_file_node = sub_file_node->next;
3770 if (strcmp((
char*)sub_file_node->name,
"get") == 0) {
3772 pc_temp = (
char*)xmlGetProp(sub_file_node, (
unsigned char*)
"command");
3773 if (pc_temp !=
nullptr) {
3774 get_command = pc_temp;
3779 pc_temp = (
char*)xmlGetProp(sub_file_node, (
unsigned char*)
"directory");
3780 if (pc_temp ==
nullptr) {
3781 info_message(
"ERROR nica-scheduler$ There is no 'directory' property for the <get/> tag!",
3783 sub_file_node = sub_file_node->next;
3790 pc_temp = (
char*)xmlGetProp(sub_file_node, (
unsigned char*)
"extras");
3791 if (pc_temp !=
nullptr) {
3792 get_extras = pc_temp;
3798 if (strcmp((
char*)sub_file_node->name,
"clean") == 0) {
3799 pc_temp = (
char*)xmlGetProp(sub_file_node, (
unsigned char*)
"path");
3800 if (pc_temp !=
nullptr) {
3801 clean_path = pc_temp;
3807 sub_file_node = sub_file_node->next;
3810 vector<string> vecInputFiles;
3812 if (nstr_file_in !=
nullptr) {
3813 string str_file_in = nstr_file_in.
GetValue();
3814 vecInputFiles.push_back(str_file_in);
3817 if ((str_file_in.find(
'?') != std::string::npos) || (str_file_in.find(
'*') != std::string::npos)
3818 || (str_file_in.find(
'+') != std::string::npos))
3820 vecInputFiles =
glob(str_file_in.c_str());
3822 cout <<
"DEBUG nica-scheduler$ File count in the regular expression: " << vecInputFiles.size()
3828 if ((sim_db !=
"") || (exp_db !=
"")) {
3829 TString sql, strConnection =
"pgsql://" + (
TString)job_par->dbSettings.db_host +
"/"
3830 + (
TString)job_par->dbSettings.db_name;
3836 job_par->dbSettings.db_password.c_str());
3837 if (pSQLServer == 0x00) {
3838 info_message(
"ERROR nica-scheduler$ Connection to the database was not established!", 1);
3843 cout <<
"DEBUG nica-scheduler$ SQL query: " << sql << endl;
3847 info_message(
"ERROR nica-scheduler$ An error was occurred during the SQL query!", 1);
3850 int nrows = res->GetRowCount();
3852 info_message(
"nica-scheduler$ WARNING: there are no records for these parameters", 1);
3855 while ((row = res->Next()) !=
nullptr) {
3856 string cur_file_path((
char*)row->
GetField(0));
3857#ifdef MICC_NO_MANAGEMENT
3858 if ((simDB !=
"") && (batch_com.
run_command ==
"sbatch"))
3861 vecInputFiles.push_back(cur_file_path);
3871 if (job_input !=
"") {
3873 auto it = mapJobName2FileOut.find(job_input);
3874 if (it == mapJobName2FileOut.end())
3875 cout <<
"nica-scheduler$ WARNING: job (output list) was not found: " << job_input << endl;
3877 vector<nullString>* vec_string = it->second.get();
3878 for (vector<nullString>::iterator iter_output = vec_string->begin();
3879 iter_output != vec_string->end(); iter_output++)
3880 vecInputFiles.push_back((*iter_output).GetValue());
3885 if (macro_input !=
"") {
3887 bool is_found =
false;
3888 for (
const auto& iter : job_par->vecMacros) {
3891 for (
const auto& iter_output : prev_macro->
vecFiles) {
3892 if (iter_output.get()->nstrFileOut.isNull())
3894 vecInputFiles.push_back(iter_output.get()->nstrFileOut.GetValue());
3903 cout <<
"nica-scheduler$ WARNING: macro (output list) was not found: " << macro_input << endl;
3907 if (file_input !=
"") {
3910 ifstream listFile(str_file_input.c_str());
3913 while (getline(listFile, list_line))
3914 vecInputFiles.push_back(list_line);
3917 info_message(
"ERROR nica-scheduler$ Can not open text file with file input list!", 1);
3921 if (json_input !=
"") {
3927 bool isFileFound =
false;
3928 for (
unsigned int i = 0;
i < vecInputFiles.size();
i++) {
3929 unique_ptr<structFilePar> filePar = make_unique<structFilePar>();
3931 filePar->strFileIn =
trim(vecInputFiles[
i]);
3933 if (nstr_file_out !=
nullptr)
3938 filePar->ptrStartEvent = make_unique<int>(start_event);
3941 filePar->ptrEventCount = make_unique<int>(event_count);
3943 if (parallel_mode !=
"")
3944 filePar->strParallelMode = parallel_mode;
3946 filePar->iMerge = iMerge;
3948 if (put_path !=
"") {
3949 filePar->strPutCommand = put_command;
3950 filePar->strPutPath =
3952 filePar->strPutPath =
3956 if (get_path !=
"") {
3957 filePar->strGetCommand = get_command;
3959 filePar->strGetPath =
3962 string strGetDirectory = filePar->strGetPath;
3964 if (filePar->nstrFileOut ==
nullptr) {
3965 info_message(
"ERROR nica-scheduler$ <get> must employ output file path, but it is not set!", 1);
3969 filePar->strGetPath =
3972 if (get_extras !=
"") {
3975 for (
const string& cur_extra_path : vec_get_extra_path) {
3976 string strExtraPath =
form_file_name(cur_extra_path, filePar->strFileIn,
i + 1,
3980 strExtraPath = strGetDirectory + string(
"/") + strExtraPath;
3982 filePar->vecGetExtraPath.push_back(strExtraPath);
3987 if (clean_path !=
"") {
3989 for (
const string& cur_clean_path : vec_clean_path)
3990 filePar->vecCleanPath.push_back(
3994 macro_par->vecFiles.push_back(std::move(filePar));
4000 "ERROR nica-scheduler$ No input files were found (possible tags: "
4001 "<input>, <file_input>, <json_input>,<sim_input>, <exp_input>, <job_input>, <macro_input>!",
4007 sub_node = sub_node->next;