Update to ARC 4.1.0
[gp-arc-client-c.git] / ext / utils.cpp
1 // -*- indent-tabs-mode: nil -*-
2
3 #ifdef HAVE_CONFIG_H
4 #include <config.h>
5 #endif
6
7 #include <glibmm.h>
8
9 #include <arc/ArcConfig.h>
10 #include <arc/IString.h>
11 #include <arc/credential/Credential.h>
12 #include <arc/loader/FinderLoader.h>
13 #include <arc/loader/Plugin.h>
14
15 #include "utils.h"
16
17 std::list<std::string> getSelectedURLsFromUserConfigAndCommandLine(Arc::UserConfig usercfg, std::list<std::string> computingelements) {
18   std::list<Arc::Endpoint> endpoints = getServicesFromUserConfigAndCommandLine(usercfg, std::list<std::string>(), computingelements);
19   std::list<std::string> serviceURLs;
20   for (std::list<Arc::Endpoint>::const_iterator it = endpoints.begin(); it != endpoints.end(); it++) {
21     serviceURLs.push_back(it->URLString);
22   }
23   return serviceURLs;
24 }
25
26 std::list<std::string> getRejectDiscoveryURLsFromUserConfigAndCommandLine(Arc::UserConfig usercfg, std::list<std::string> rejectdiscovery) {
27   std::list<std::string> rejectDiscoveryURLs = usercfg.RejectDiscoveryURLs();  
28   rejectDiscoveryURLs.insert(rejectDiscoveryURLs.end(), rejectdiscovery.begin(), rejectdiscovery.end());
29   return rejectDiscoveryURLs;
30 }
31
32 std::list<std::string> getRejectManagementURLsFromUserConfigAndCommandLine(Arc::UserConfig usercfg, std::list<std::string> rejectmanagement) {
33   std::list<std::string> rejectManagementURLs = usercfg.RejectManagementURLs();  
34   rejectManagementURLs.insert(rejectManagementURLs.end(), rejectmanagement.begin(), rejectmanagement.end());
35   return rejectManagementURLs;
36 }
37
38
39 std::list<Arc::Endpoint> getServicesFromUserConfigAndCommandLine(Arc::UserConfig usercfg, std::list<std::string> registries, std::list<std::string> computingelements, std::string requestedSubmissionInterfaceName, std::string infointerface) {
40   std::list<Arc::Endpoint> services;
41   if (computingelements.empty() && registries.empty()) {
42     std::list<Arc::ConfigEndpoint> endpoints = usercfg.GetDefaultServices();
43     for (std::list<Arc::ConfigEndpoint>::const_iterator its = endpoints.begin(); its != endpoints.end(); its++) {
44       services.push_back(*its);
45     }
46   } else {
47     for (std::list<std::string>::const_iterator it = computingelements.begin(); it != computingelements.end(); it++) {
48       // check if the string is a group or alias
49       std::list<Arc::ConfigEndpoint> newServices = usercfg.GetServices(*it, Arc::ConfigEndpoint::COMPUTINGINFO);
50       if (newServices.empty()) {
51           // if it was not an alias or a group, then it should be the URL
52           Arc::Endpoint service(*it);
53           service.Capability.insert(Arc::Endpoint::GetStringForCapability(Arc::Endpoint::COMPUTINGINFO));
54           if (!infointerface.empty()) {
55             service.InterfaceName = infointerface;            
56           }
57           service.RequestedSubmissionInterfaceName = requestedSubmissionInterfaceName;
58           services.push_back(service);
59       } else {
60         // if it was a group (or an alias), add all the services
61         for (std::list<Arc::ConfigEndpoint>::iterator its = newServices.begin(); its != newServices.end(); its++) {
62           if (!requestedSubmissionInterfaceName.empty()) {
63             // if there was a submission interface requested, this overrides the one from the config
64             its->RequestedSubmissionInterfaceName = requestedSubmissionInterfaceName;    
65           }
66           services.push_back(*its);
67         }
68       }
69     }
70     for (std::list<std::string>::const_iterator it = registries.begin(); it != registries.end(); it++) {
71       // check if the string is a name of a group
72       std::list<Arc::ConfigEndpoint> newServices = usercfg.GetServices(*it, Arc::ConfigEndpoint::REGISTRY);
73       if (newServices.empty()) {
74           // if it was not an alias or a group, then it should be the URL
75           Arc::Endpoint service(*it);
76           service.Capability.insert(Arc::Endpoint::GetStringForCapability(Arc::Endpoint::REGISTRY));
77           services.push_back(service);
78       } else {
79         // if it was a group (or an alias), add all the services
80         services.insert(services.end(), newServices.begin(), newServices.end());
81       }
82     }
83   }
84   return services;
85 }
86
87
88 void showplugins(const std::string& program, const std::list<std::string>& types, Arc::Logger& logger, const std::string& chosenBroker) {
89
90   for (std::list<std::string>::const_iterator itType = types.begin();
91        itType != types.end(); ++itType) {
92     if (*itType == "HED:SubmitterPlugin") {
93       std::cout << Arc::IString("Types of execution services %s is able to submit jobs to:", program) << std::endl;
94     }
95     else if (*itType == "HED:ServiceEndpointRetrieverPlugin") {
96       std::cout << Arc::IString("Types of registry services which %s is able collect information from:", program) << std::endl;
97     }
98     else if (*itType == "HED:TargetInformationRetrieverPlugin") {
99       std::cout << Arc::IString("Types of local information services which %s is able collect information from:", program) << std::endl;
100     }
101     else if (*itType == "HED:JobListRetriever") {
102       std::cout << Arc::IString("Types of local information services which %s is able collect job information from:", program) << std::endl;
103     }
104     else if (*itType == "HED:JobControllerPlugin") {
105       std::cout << Arc::IString("Types of services %s is able to manage jobs at:", program) << std::endl;
106     }
107     else if (*itType == "HED:JobDescriptionParserPlugin") {
108       std::cout << Arc::IString("Job description languages supported by %s:", program) << std::endl;
109     }
110     else if (*itType == "HED:BrokerPlugin") {
111       std::cout << Arc::IString("Brokers available to %s:", program) << std::endl;
112     }
113
114     std::list<Arc::ModuleDesc> modules;
115     Arc::PluginsFactory pf(Arc::BaseConfig().MakeConfig(Arc::Config()).Parent());
116
117     bool isDefaultBrokerLocated = false;
118     pf.scan(Arc::FinderLoader::GetLibrariesList(), modules);
119     Arc::PluginsFactory::FilterByKind(*itType, modules);
120     for (std::list<Arc::ModuleDesc>::iterator itMod = modules.begin();
121          itMod != modules.end(); itMod++) {
122       for (std::list<Arc::PluginDesc>::iterator itPlug = itMod->plugins.begin();
123            itPlug != itMod->plugins.end(); itPlug++) {
124         std::cout << "  " << itPlug->name;
125         if (*itType == "HED:BrokerPlugin" && itPlug->name == chosenBroker) {
126           std::cout << " (default)";
127           isDefaultBrokerLocated = true;
128         }
129         std::cout << " - " << itPlug->description << std::endl;
130       }
131     }
132
133     if (*itType == "HED:BrokerPlugin" && !isDefaultBrokerLocated) {
134       logger.msg(Arc::WARNING, "Default broker (%s) is not available. When using %s a broker should be specified explicitly (-b option).", chosenBroker, program);
135     }
136   }
137 }
138
139 bool checkproxy(const Arc::UserConfig& uc)
140 {
141   if (!uc.ProxyPath().empty() ) {
142     Arc::Credential holder(uc.ProxyPath(), "", "", "");
143     if (holder.GetEndTime() < Arc::Time()){
144       std::cout << Arc::IString("Proxy expired. Job submission aborted. Please run 'arcproxy'!") << std::endl;
145       return false;
146     }
147   }
148   else {
149     std::cout << Arc::IString("Cannot find any proxy. This application currently cannot run without a proxy.\n"
150                               "  If you have the proxy file in a non-default location,\n"
151                               "  please make sure the path is specified in the client configuration file.\n"
152                               "  If you don't have a proxy yet, please run 'arcproxy'!") << std::endl;
153     return false;
154   }
155
156   return true;
157 }
158
159 void splitendpoints(std::list<std::string>& selected, std::list<std::string>& rejected)
160 {
161   // Removes slashes from end of endpoint strings, and put strings with leading '-' into rejected list.
162   for (std::list<std::string>::iterator it = selected.begin();
163        it != selected.end();) {
164     if ((*it)[it->length()-1] == '/') {
165       it->erase(it->length()-1);
166       continue;
167     }
168     if (it->empty()) {
169       it = selected.erase(it);
170       continue;
171     }
172
173     if ((*it)[0] == '-') {
174       rejected.push_back(it->substr(1));
175       it = selected.erase(it);
176     }
177     else {
178       ++it;
179     }
180   }
181 }
182
183 Arc::JobInformationStorage* createJobInformationStorage(const Arc::UserConfig& uc) {
184   Arc::JobInformationStorage* jis = NULL;
185   if (Glib::file_test(uc.JobListFile(), Glib::FILE_TEST_EXISTS)) {
186     for (int i = 0; Arc::JobInformationStorage::AVAILABLE_TYPES[i].name != NULL; ++i) {
187       jis = (Arc::JobInformationStorage::AVAILABLE_TYPES[i].instance)(uc.JobListFile());
188       if (jis && jis->IsValid()) {
189         return jis;
190       }
191       delete jis;
192     }
193     return NULL;
194   }
195   
196   for (int i = 0; Arc::JobInformationStorage::AVAILABLE_TYPES[i].name != NULL; ++i) {
197     if (uc.JobListType() == Arc::JobInformationStorage::AVAILABLE_TYPES[i].name) {
198       jis = (Arc::JobInformationStorage::AVAILABLE_TYPES[i].instance)(uc.JobListFile());
199       if (jis && jis->IsValid()) {
200         return jis;
201       }
202       delete jis;
203       return NULL;
204     }
205   }
206
207   if (Arc::JobInformationStorage::AVAILABLE_TYPES[0].instance != NULL) {
208     jis = (Arc::JobInformationStorage::AVAILABLE_TYPES[0].instance)(uc.JobListFile());
209     if (jis && jis->IsValid()) {
210       return jis;
211     }
212     delete jis;
213   }
214   
215   return NULL;
216 }
217
218
219 ClientOptions::ClientOptions(Client_t c,
220                              const std::string& arguments,
221                              const std::string& summary,
222                              const std::string& description) :
223     Arc::OptionParser(arguments, summary, description),
224     dryrun(false),
225     dumpdescription(false),
226     show_credentials(false),
227     show_plugins(false),
228     showversion(false),
229     all(false),
230     forcemigration(false),
231     keep(false),
232     forcesync(false),
233     truncate(false),
234     longlist(false),
235     printids(false),
236     same(false),
237     notsame(false),
238     show_stdout(true),
239     show_stderr(false),
240     show_joblog(false),
241     usejobname(false),
242     forcedownload(false),
243     list_configured_services(false),
244     direct_submission(false),
245     show_unavailable(false),
246     testjobid(-1),
247     runtime(5),
248     timeout(-1)
249 {
250   bool cIsJobMan = (c == CO_CAT || c == CO_CLEAN || c == CO_GET || c == CO_KILL || c == CO_RENEW || c == CO_RESUME || c == CO_STAT || c == CO_ACL);
251
252   AddOption('c', "cluster",
253             istring("select one or more computing elements: "
254                     "name can be an alias for a single CE, a group of CEs or a URL"),
255             istring("name"),
256             clusters);
257   
258   if (!cIsJobMan && c != CO_SYNC) {
259     AddOption('I', "infointerface",
260               istring("the computing element specified by URL at the command line "
261                       "should be queried using this information interface "
262                       "(possible options: org.nordugrid.ldapng, org.nordugrid.ldapglue2, "
263                       "org.nordugrid.wsrfglue2, org.ogf.glue.emies.resourceinfo)"),
264               istring("interfacename"),
265               infointerface);
266   }
267
268   if (c == CO_RESUB || c == CO_MIGRATE) {
269     AddOption('q', "qluster",
270               istring("selecting a computing element for the new jobs with a URL or an alias, "
271                       "or selecting a group of computing elements with the name of the group"),
272               istring("name"),
273               qlusters);
274   }
275
276   if (c == CO_MIGRATE) {
277     AddOption('f', "force",
278               istring("force migration, ignore kill failure"),
279               forcemigration);
280   }
281
282   if (c == CO_GET || c == CO_KILL || c == CO_MIGRATE || c == CO_RESUB) {
283     AddOption('k', "keep",
284               istring("keep the files on the server (do not clean)"),
285               keep);
286   }
287
288   if (c == CO_SYNC) {
289     AddOption('f', "force",
290               istring("do not ask for verification"),
291               forcesync);
292
293     AddOption('T', "truncate",
294               istring("truncate the joblist before synchronizing"),
295               truncate);
296   }
297
298   if (c == CO_INFO || c == CO_STAT) {
299     AddOption('l', "long",
300               istring("long format (more information)"),
301               longlist);
302   }
303
304   if (c == CO_INFO) {
305     AddOption('L', "list-configured-services",
306               istring("print a list of services configured in the client.conf"),
307               list_configured_services);
308   }
309
310   if (c == CO_CAT) {
311     AddOption('o', "stdout",
312               istring("show the stdout of the job (default)"),
313               show_stdout);
314
315     AddOption('e', "stderr",
316               istring("show the stderr of the job"),
317               show_stderr);
318
319     AddOption('l', "joblog",
320               istring("show the CE's error log of the job"),
321               show_joblog);
322   }
323
324   if (c == CO_GET) {
325     AddOption('D', "dir",
326               istring("download directory (the job directory will"
327                       " be created in this directory)"),
328               istring("dirname"),
329               downloaddir);
330
331     AddOption('J', "usejobname",
332               istring("use the jobname instead of the short ID as"
333                       " the job directory name"),
334               usejobname);
335
336     AddOption('f', "force",
337               istring("force download (overwrite existing job directory)"),
338               forcedownload);
339   }
340
341   if (c == CO_STAT) {
342     // Option 'long' takes precedence over this option (print-jobids).
343     AddOption('p', "print-jobids", istring("instead of the status only the IDs of "
344               "the selected jobs will be printed"), printids);
345
346     AddOption('S', "sort",
347               istring("sort jobs according to jobid, submissiontime or jobname"),
348               istring("order"), sort);
349     AddOption('R', "rsort",
350               istring("reverse sorting of jobs according to jobid, submissiontime or jobname"),
351               istring("order"), rsort);
352
353     AddOption('u', "show-unavailable",
354               istring("show jobs where status information is unavailable"),
355               show_unavailable);
356   }
357
358   if (c == CO_RESUB) {
359     AddOption('m', "same",
360               istring("resubmit to the same resource"),
361               same);
362
363     AddOption('M', "not-same",
364               istring("do not resubmit to the same resource"),
365               notsame);
366   }
367
368   if (c == CO_CLEAN) {
369     AddOption('f', "force",
370               istring("remove the job from the local list of jobs "
371                       "even if the job is not found in the infosys"),
372               forceclean);
373   }
374
375   if (!cIsJobMan) {
376     AddOption('g', "index",
377               istring("select one or more registries: "
378                       "name can be an alias for a single registry, a group of registries or a URL"),
379               istring("name"),
380               indexurls);
381   }
382
383   if (c == CO_TEST) {
384     AddOption('J', "job",
385               istring("submit test job given by the number"),
386               istring("int"),
387               testjobid);
388     AddOption('r', "runtime",
389               istring("test job runtime specified by the number"),
390               istring("int"),
391               runtime);
392   }
393
394   if (cIsJobMan || c == CO_RESUB) {
395     AddOption('s', "status",
396               istring("only select jobs whose status is statusstr"),
397               istring("statusstr"),
398               status);
399   }
400
401   if (cIsJobMan || c == CO_MIGRATE || c == CO_RESUB) {
402     AddOption('a', "all",
403               istring("all jobs"),
404               all);
405   }
406
407   if (c == CO_SUB) {
408     AddOption('e', "jobdescrstring",
409               istring("jobdescription string describing the job to "
410                       "be submitted"),
411               istring("string"),
412               jobdescriptionstrings);
413
414     AddOption('f', "jobdescrfile",
415               istring("jobdescription file describing the job to "
416                       "be submitted"),
417               istring("string"),
418               jobdescriptionfiles);
419   }
420
421   if (c == CO_MIGRATE || c == CO_RESUB || c == CO_SUB || c == CO_TEST) {
422     AddOption('b', "broker",
423               istring("select broker method (list available brokers with --listplugins flag)"),
424               istring("broker"), broker);
425
426     AddOption('o', "jobids-to-file",
427               istring("the IDs of the submitted jobs will be appended to this file"),
428               istring("filename"),
429               jobidoutfile);
430     
431     AddOption('S', "submissioninterface",
432               istring("only use this interface for submitting "
433                       "(e.g. org.nordugrid.gridftpjob, org.ogf.glue.emies.activitycreation, org.ogf.bes)"),
434               istring("InterfaceName"),
435               requestedSubmissionInterfaceName);
436
437   }
438   
439   if (c == CO_MIGRATE || c == CO_RESUB || c == CO_SUB || c == CO_TEST || c == CO_INFO) {
440     AddOption('R', "rejectdiscovery",
441               istring("skip the service with the given URL during service discovery"),
442               istring("URL"),
443               rejectdiscovery);
444   }
445   
446
447   if (cIsJobMan || c == CO_MIGRATE || c == CO_RESUB) {
448     AddOption('i', "jobids-from-file",
449               istring("a file containing a list of jobIDs"),
450               istring("filename"),
451               jobidinfiles);
452
453     AddOption('r', "rejectmanagement",
454               istring("skip jobs which are on a computing element with a given URL"),
455               istring("URL"),
456               rejectmanagement);    
457   }
458
459   if (c == CO_SUB || c == CO_TEST) {
460     AddOption('D', "dryrun", istring("submit jobs as dry run (no submission to batch system)"),
461               dryrun);
462
463     AddOption(0, "direct", istring("submit directly - no resource discovery or matchmaking"),
464               direct_submission);
465
466     AddOption('x', "dumpdescription",
467               istring("do not submit - dump job description "
468                       "in the language accepted by the target"),
469               dumpdescription);
470   }
471   
472   if (c == CO_INFO) {
473     AddOption('S', "submissioninterface",
474               istring("only get information about executon targets which support this job submission interface "
475                       "(e.g. org.nordugrid.gridftpjob, org.ogf.glue.emies.activitycreation, org.ogf.bes)"),
476               istring("InterfaceName"),
477               requestedSubmissionInterfaceName);
478   }
479
480   if (c == CO_TEST) {
481     AddOption('E', "certificate", istring("prints info about installed user- and CA-certificates"), show_credentials);
482   }
483
484   if (c != CO_INFO) {
485     AddOption('j', "joblist",
486               Arc::IString("the file storing information about active jobs (default %s)", Arc::UserConfig::JOBLISTFILE).str(),
487               istring("filename"),
488               joblist);
489   }
490
491   /* --- Standard options below --- */
492
493   AddOption('z', "conffile",
494             istring("configuration file (default ~/.arc/client.conf)"),
495             istring("filename"), conffile);
496
497   AddOption('t', "timeout", istring("timeout in seconds (default 20)"),
498             istring("seconds"), timeout);
499
500   AddOption('P', "listplugins",
501             istring("list the available plugins"),
502             show_plugins);
503
504   AddOption('d', "debug",
505             istring("FATAL, ERROR, WARNING, INFO, VERBOSE or DEBUG"),
506             istring("debuglevel"), debug);
507
508   AddOption('v', "version", istring("print version information"),
509             showversion);
510
511 }