// -*- indent-tabs-mode: nil -*- #ifndef __ARC_SUBMITTER_H__ #define __ARC_SUBMITTER_H__ #include #include #include #include #include #include #include #include namespace Arc { /** * \defgroup compute ARC Compute Library (libarccompute) * * libarccompute is a library for discovering, quering, matching and ranking, * submitting jobs to and managing jobs on Grid resources, as well as parsing * and assembling job descriptions. It features a uniform high-level interface * to a wide range of Service Registries, Information Systems and Computing * Services. With this interface, registries can be queried for service * endpoints, information systems can be queried for detailed resource and job * information, and jobs can be submitted to and managed in a Grid * environment. The library doesn't offer specific interfaces to different * services, instead it tries to provide a uniform interface to different kind * of services. * * An introduction on how to use the library to query services for information * is given in the description of the EntityRetriever class. How to use the * library for submitting jobs is described at the Submitter class reference * page. How to manage jobs with the library is described at the JobSupervisor * class reference page. * * The library uses ARC's dynamic plugin mechanism to load plugins for * specific services and features only when required at runtime. These plugins * for the libarccompute library are called ARC Compute Components (ACCs). * Each of the classes listed below will transparently load any required ACC * plugins at runtime when needed. If preferred ACC plugins can also be used * and loaded manually, which is described in more detail \ref accplugins * "here". * * Support for a custom service (info-system, registry or compute), a ranking * algorithm and/or a job description parsing/assembling algorithm is exactly * what is defined as a ACC, and it can easily be added to be accessible to * the libarccompute library. More details about creating such a plugin can be * found \ref accplugins "here". * * With the default NorduGrid ARC plugins installed the librarccompute library * supports the following services and specifications: * * Computing Services: * * EMI ES * * GridFTPJob interface (requires the nordugrid-arc-plugins-globus package) * * Registry and Index Services: * * EGIIS * * ARCHERY * * Local Information Schemes: * * %GLUE2 (through LDAP and EMI ES) * * NorduGrid schema (through LDAP) * * Matchmaking and Ranking Algorithms: * * Benchmark * * Fastest queue * * ACIX * * Random * * Python interface for custom broker * * %Job description languages: * * EMI ADL * * xRSL * * \page group__compute ARC Compute Library (libarccompute) */ /** * \ingroup compute * \headerfile Submitter.h arc/compute/Submitter.h */ class EndpointSubmissionStatus { public: /** The possible states: */ enum EndpointSubmissionStatusType { UNKNOWN, NOPLUGIN, SUCCESSFUL }; /** String representation of the states in the enum #EndpointSubmissionStatusType */ static std::string str(EndpointSubmissionStatusType status); /** A new EndpointSubmissionStatus is created with UNKNOWN status and with an empty description by default */ EndpointSubmissionStatus(EndpointSubmissionStatusType status = UNKNOWN, const std::string& description = "") : status(status), description(description) {}; /** This EndpointSubmissionStatus object equals to an enum #EndpointSubmissionStatusType if it contains the same state */ bool operator==(EndpointSubmissionStatusType s) const { return status == s; }; /** This EndpointSubmissionStatus object equals to another EndpointQueryingStatus object, if their state equals. The description doesn't matter. */ bool operator==(const EndpointSubmissionStatus& s) const { return status == s.status; }; /** Inequality. \see operator==(EndpointSubmissionStatus) */ bool operator!=(EndpointSubmissionStatusType s) const { return status != s; }; /** Inequality. \see operator==(const EndpointSubmissionStatus&) */ bool operator!=(const EndpointSubmissionStatus& s) const { return status != s.status; }; /** \return true if the status is not successful */ bool operator!() const { return status != SUCCESSFUL; }; /** \return true if the status is successful */ operator bool() const { return status == SUCCESSFUL; }; /** Setting the EndpointSubmissionStatus object's state \param[in] s the new enum #EndpointSubmissionStatusType status */ EndpointSubmissionStatus& operator=(EndpointSubmissionStatusType s) { status = s; return *this; }; /** Copying the EndpointSubmissionStatus object into this one. \param[in] s the EndpointSubmissionStatus object whose status and description will be copied into this object */ EndpointSubmissionStatus& operator=(const EndpointSubmissionStatus& s) { status = s.status; description = s.description; return *this; }; /** Return the enum #EndpointSubmissionStatusType contained within this EndpointSubmissionStatus object */ EndpointSubmissionStatusType getStatus() const { return status; }; /** Return the description string contained within this EndpointSubmissionStatus object */ const std::string& getDescription() const { return description; }; /** String representation of the EndpointSubmissionStatus object, which is currently simply the string representation of the enum #EndpointSubmissionStatusType */ std::string str() const { return str(status); }; private: EndpointSubmissionStatusType status; std::string description; }; class SubmissionStatus; /// Class for submitting jobs. /** * As the name indicates this class is used for submitting jobs. It has a * number of different submit methods which can be used directly for different * job submission purposes. The class it self can be considered as a frontend * to the SubmitterPlugin class, an abstract class which is extended by * specialised plugins providing the actual coupling to a particular type of * computing service. As a frontend, this class also takes care of loading * the specialised plugins and choosing the right plugin for a given computing * service, however that can also be done manually with the * SubmitterPluginLoader class. In order to use the Submitter class a * reference to a UserConfig object must be provided, it should exist * throughout the lifetime of the created Submitter object, and the UserConfig * object should contain configuration details such as the path to user * credentials. * * Generally there are two types of submit methods. One which doesn't * accept a reference to a Job or list of Job objects, and one which does. * This is because the Submitter class is able to pass submitted Job objects * to consumer objects. Registering a consumer object is done using the * \ref Submitter::addConsumer "addConsumer" method passing a reference to an * EntityConsumer object. An * example of such a consumer is the JobSupervisor class. Multiple consumers * can be registered for the same Submitter object. Every submit method will * then pass submitted Job objects to the registered consumer objects. A * registered consumer can be removed using the * \ref Submitter::removeConsumer "removeConsumer" method. * * For submitting a Grid job one should use one of the * \ref Submitter::BrokeredSubmit "BrokeredSubmit" methods. They accept a list * of job descriptions and a list of information system endpoints for which * computing services are discovered and matched to the job descriptions. * Jobs are then submitted to the matching services in the order ranked by the * \ref Broker "algorithm" specified in the UserConfig object. * * Another way of submitting a job is by using the * \ref Submitter::Submit "Submit" methods. These methods accepts submission * endpoints or ExecutionTarget objects. Using these methods will not do * any client side checks whether the computing service resources pointed to * by the submission endpoint (or ExecutionTarget) really matches the * specified job description(s). * * Common for both ways of submitting jobs is that they both return a * SubmissionStatus object indicating the outcome of the submission attemp(s). * If the returned status object indicates failures, further examination can * be carried out by using the * \ref Submitter::GetDescriptionsNotSubmitted "GetDescriptionsNotSubmitted", * \ref Submitter::GetEndpointQueryingStatuses "GetEndpointQueryingStatuses" * and/or \ref Submitter::GetEndpointSubmissionStatuses "GetEndpointSubmissionStatuses" * methods. Note that on each invocation of any of the submit methods the * state from a previous submission attemp will be cleared, thus the just * mentioned methods should be used just after an attempted submission fails. * * An example of submitting a Grid job using C++ is shown below: * \include basic_job_submission.cpp * This code can be compiled with * \code * g++ -o submit -I/usr/include/libxml2 `pkg-config --cflags glibmm-2.4` -l arccompute submit.cpp * \endcode * Same example using Python shown below: * \include basic_job_submission.py * * \since Added in 2.0.0 * \ingroup compute * \headerfile Submitter.h arc/compute/Submitter.h */ class Submitter { public: /// Create a Submitter object /** * Stores a reference to the passed UserConfig object which will be used * for obtaining among others location of user credentials. * \note The UserConfig object must exist throughout the life time of the * created Submitter object. */ Submitter(const UserConfig& uc) : uc(uc) {} ~Submitter() {} // === Using the consumer concept as in the EntityRetriever === /// Add a Job consumer /** * Add a consumer object which will be called every time a job is submitted. * * Example use of consumer concept: * \param[in] addConsumer_consumer consumer object receiving newly submitted jobs. * \see removeConsumer */ void addConsumer(EntityConsumer& addConsumer_consumer /* The name 'addConsumer_consumer' is important for Swig when matching methods */) { consumers.push_back(&addConsumer_consumer); } /// Remove a previous added consumer object. /** * \param[in] removeConsumer_consumer consumer object which should be removed. * \see addConsumer */ void removeConsumer(EntityConsumer& removeConsumer_consumer /* The name 'removeConsumer_consumer' is important for Swig when matching methods */); // === // === No brokering === /// Submit job to endpoint /** * Submit a job described by the passed JobDescription object to the * specified submission endpoint of a computing service. The method will * load the specialised \ref SubmitterPlugin "submitter plugin" which * corresponds to the specified \ref Endpoint::InterfaceName "interface name". * If no such plugin is found submission is unsuccessful. If however the * the interface name is unspecified (empty), then all available submitter * plugins will be tried. If submission is successful, the submitted job * will be added to the registered consumer object. If unsuccessful, more * details can be obtained from the returned SubmissionStatus object, or by * using the \ref GetDescriptionsNotSubmitted, * \ref GetEndpointQueryingStatuses and \ref GetEndpointSubmissionStatuses. * * \param[in] endpoint the endpoint to which job should be submitted. * \param[in] desc the JobDescription object describing the job to be * submitted. * \return A SubmissionStatus object is returned indicating the status of * submission. * \see addConsumer * \see GetDescriptionsNotSubmitted * \see GetEndpointQueryingStatuses * \see GetEndpointSubmissionStatuses * \since Added in 3.0.0 */ SubmissionStatus Submit(const Endpoint& endpoint, const JobDescription& desc) { return Submit(endpoint, std::list(1, desc)); } /// Submit job to endpoint /** * Identical to Submit(const Endpoint&, const JobDescription&), with the * addition that the Job object passed as reference will also be filled with * job information if submission was successful. * * \param[out] job a reference to a Job object which will be filled with * job details if submission was successful. * \see Submit(const Endpoint&, const JobDescription&) for detailed * description. * \since Added in 3.0.0 */ SubmissionStatus Submit(const Endpoint& endpoint, const JobDescription& desc, Job& job); /// Submit jobs to endpoint /** * Identical to Submit(const Endpoint&, const JobDescription&), except that * this method submits multiple jobs to same endpoint. Submitted jobs will * be added to the registered consumer. * * \see Submit(const Endpoint&, const JobDescription&) * \since Added in 3.0.0 */ SubmissionStatus Submit(const Endpoint& endpoint, const std::list& descs); /// Submit jobs to endpoint /** * Identical to Submit(const Endpoint&, const JobDescription&), with the * addition that the list of Job objects passed reference will filled with * the submitted jobs, and that multiple jobs are submitted to same * endpoint. * * \see Submit(const Endpoint&, const JobDescription&) * \since Added in 3.0.0 */ SubmissionStatus Submit(const Endpoint& endpoint, const std::list& descs, std::list& jobs); /// Submit jobs to any endpoints /** * Submit multiple jobs to a list of submission endpoints to computing * services. For each JobDescription object submission is tried against the * list of submission endpoints in order. If submission to a endpoint fails * the next in the list is tried - no ranking of endpoints will be done. * Also note that a job is only submitted once, and not to multiple * computing services. Submitted Job objects is passed to the registered * consumer objects. * * \return A SubmissionStatus object is returned which indicates the * outcome of the submission. * \see addConsumer * \see GetDescriptionsNotSubmitted * \see GetEndpointQueryingStatuses * \see GetEndpointSubmissionStatuses * \since Added in 3.0.0 */ SubmissionStatus Submit(const std::list& endpoint, const std::list& descs); /// Submit jobs to any endpoints /** * Identical to Submit(const Endpoint&, const std::list&, std::list&) * with the addition that submitted jobs are also added to the passed list * of Job objects. * * \see Submit(const Endpoint&, const std::list&, std::list&) * \since Added in 3.0.0 */ SubmissionStatus Submit(const std::list& endpoint, const std::list& descs, std::list& jobs); // ==== Submission to single configuration (adaption of job description) ==== // ===== Single job ===== /// Submit job to ExecutionTarget (computing service) /** * Submit a job to a computing service, represented by a ExecutionTarget * object. This is useful when resource discovery is carried out manually, * not using the \ref BrokeredSubmit methods, but using the * \ref ComputingServiceRetriever class. The * \ref SubmitterPlugin "submitter plugin" corresponding to the * \ref ComputingEndpointAttributes::InterfaceName "interface name" will be * loaded. If that plugin cannot be loaded, submission will be unsuccessful. * When loaded the ExecutionTarget and JobDescription object will be passed * to the \ref SubmitterPlugin::Submit(const std::list&, const ExecutionTarget&, EntityConsumer&, std::list&) "SubmitterPlugin::Submit" * method of the loaded plugin and the status of that method is returned. * * \param[in] et the ExecutionTarget to which job should be submitted. * \param[in] desc the JobDescription object describing the job to be * submitted. * \return A SubmissionStatus object is returned indicating the status of * submission. * \see addConsumer * \see GetDescriptionsNotSubmitted * \see GetEndpointQueryingStatuses * \see GetEndpointSubmissionStatuses */ SubmissionStatus Submit(const ExecutionTarget& et, const JobDescription& desc) { return Submit(et, std::list(1, desc)); } /// Submit job to ExecutionTarget (computing service) /** * Identical to Submit(const ExecutionTarget&, const JobDescription&), with * the addition that the Job object passed as reference will also be filled * with job information if submission was successful. * * \param[in] et the ExecutionTarget to which job should be submitted. * \param[in] desc the JobDescription object describing the job to be * submitted. * \return A SubmissionStatus object is returned indicating the status of * submission. * \see Submit(const ExecutionTarget&, const JobDescription&) */ SubmissionStatus Submit(const ExecutionTarget& et, const JobDescription& desc, Job& job); // ===== // ===== Multiple jobs ===== /// Submit jobs to ExecutionTarget (computing service) /** * Identical to Submit(const ExecutionTarget&, const JobDescription&), * except that this method submits multiple jobs to the same computing * service. Submitted jobs will be added to the registered consumer. * * \param[in] et the ExecutionTarget to which job should be submitted. * \param[in] desc the JobDescription object describing the job to be * submitted. * \return A SubmissionStatus object is returned indicating the status of * submission. * \see Submit(const ExecutionTarget&, const JobDescription&) */ SubmissionStatus Submit(const ExecutionTarget& et, const std::list& descs); /// Submit jobs to ExecutionTarget (computing service) /** * Identical to Submit(const ExecutionTarget&, const JobDescription&) * with the addition that this method submits multiple jobs to the same * computing service, and that submitted jobs are also added to the passed * list of Job objects. * * \param[in] et the ExecutionTarget to which job should be submitted. * \param[in] desc the JobDescription object describing the job to be * submitted. * \return A SubmissionStatus object is returned indicating the status of * submission. * \see Submit(const ExecutionTarget&, const JobDescription&) */ SubmissionStatus Submit(const ExecutionTarget& et, const std::list& descs, std::list& jobs); // ===== // ==== // === // === Brokering with service discovery (multiple endpoints) === // ==== Using provided JobDescription objects for brokering ==== /// Submit jobs to matching and ranked computing services /** * The passed job descriptions will be submitted to any of the matching * computing services in ranked order, which have been discovered using the * provided information endpoints. * * First all previously set statuses will be cleared by invoking the * \ref Submitter::ClearAll "ClearAll" method. Then resource discovery is * invoked using the ComputingServiceRetriever class to query the provided * information endpoints, then for each JobDescription object the discovered * computing services is matched against the job description, then * matching computing services is ranked according to the broker algorithm * (specified in the UserConfig object of the Submitter). Then if any * requested submission interfaces has been specified in the optional * parameter 'requestedSubmissionInterfaces', then computing services which * doesn't have a matching submission interface will be ignored. Lastly the * job description is tried submitted to the computing services in the * ranked order, and upon the first successful submission a corresponding * Job object is propagated to the registered consumers, and then the next * job description is processed. If a job description cannot be submitted a * pointer to it will be added to an internal list, which afterwards can be * obtained using the \ref GetDescriptionsNotSubmitted method. If any * problems was encountered during submission, more details can be obtained * from the returned SubmissionStatus object, or by using the * \ref GetDescriptionsNotSubmitted, \ref GetEndpointQueryingStatuses * and \ref GetEndpointSubmissionStatuses. * * \param[in] endpoints the information endpoints which will be used to * initiate resource discovery. * \param[in] descs the JobDescription objects describing the jobs to be * submitted. * \param[in] requestedSubmissionInterfaces an optional list of submission * interfaces to use for submission. * \return A SubmissionStatus object is returned indicating the status of * the submissions. * \see addConsumer * \see GetDescriptionsNotSubmitted * \see GetEndpointQueryingStatuses * \see GetEndpointSubmissionStatuses * \since Added in 3.0.0 */ SubmissionStatus BrokeredSubmit(const std::list& endpoints, const std::list& descs, const std::list& requestedSubmissionInterfaces = std::list()); /// Submit jobs to matching and ranked computing services /** * Identical to \ref BrokeredSubmit(const std::list& endpoints, const std::list& descs, const std::list& requestedSubmissionInterfaces) "BrokeredSubmit" * except that submitted jobs are added to the referenced list of Job * objects. * * \param[in] endpoints the information endpoints which will be used to * initiate resource discovery. * \param[in] descs the JobDescription objects describing the jobs to be * submitted. * \param[in] jobs reference to a list of Job obects for which to add * submitted jobs. * \param[in] requestedSubmissionInterfaces an optional list of submission * interfaces to use for submission. * \return A SubmissionStatus object is returned indicating the status of * the submissions. * \see addConsumer * \see GetDescriptionsNotSubmitted * \see GetEndpointQueryingStatuses * \see GetEndpointSubmissionStatuses * \since Added in 3.0.0 */ SubmissionStatus BrokeredSubmit(const std::list& endpoints, const std::list& descs, std::list& jobs, const std::list& requestedSubmissionInterfaces = std::list()); /// Submit jobs to matching and ranked computing services /** * Identical to \ref BrokeredSubmit(const std::list& endpoints, const std::list& descs, const std::list& requestedSubmissionInterfaces) "BrokeredSubmit" * except that the endpoints are not strings but Endpoint objects, which * can be used to provide detailed information about endpoints making * resource discovery more performant. * * \param[in] endpoints the information endpoints which will be used to * initiate resource discovery. * \param[in] descs the JobDescription objects describing the jobs to be * submitted. * \param[in] requestedSubmissionInterfaces an optional list of submission * interfaces to use for submission. * \return A SubmissionStatus object is returned indicating the status of * the submissions. * \see addConsumer * \see GetDescriptionsNotSubmitted * \see GetEndpointQueryingStatuses * \see GetEndpointSubmissionStatuses * \since Added in 3.0.0 */ SubmissionStatus BrokeredSubmit(const std::list& endpoints, const std::list& descs, const std::list& requestedSubmissionInterfaces = std::list()); /// Submit jobs to matching and ranked computing services /** * Identical to \ref BrokeredSubmit(const std::list& endpoints, const std::list& descs, const std::list& requestedSubmissionInterfaces) "BrokeredSubmit" * except that submitted jobs are added to the referenced list of Job * objects and that the endpoints are not strings but Endpoint objects, * which can be used to provide detailed information about endpoints making * resource discovery more performant. * * \param[in] endpoints the information endpoints which will be used to * initiate resource discovery. * \param[in] descs the JobDescription objects describing the jobs to be * submitted. * \param[in] jobs reference to a list of Job obects for which to add * submitted jobs. * \param[in] requestedSubmissionInterfaces an optional list of submission * interfaces to use for submission. * \return A SubmissionStatus object is returned indicating the status of * the submissions. * \see addConsumer * \see GetDescriptionsNotSubmitted * \see GetEndpointQueryingStatuses * \see GetEndpointSubmissionStatuses * \since Added in 3.0.0 */ SubmissionStatus BrokeredSubmit(const std::list& endpoints, const std::list& descs, std::list& jobs, const std::list& requestedSubmissionInterfaces = std::list()); // ==== // === // === Methods for handling errors === /// Get job descriptions not submitted /** * \return A reference to the list of the not submitted job descriptions is * returned. */ const std::list& GetDescriptionsNotSubmitted() const { return notsubmitted; } /// Clear list of not submitted job descriptions void ClearNotSubmittedDescriptions() { notsubmitted.clear(); } /// Get status map for queried endpoints /** * The returned map contains EndpointQueryingStatus objects of all the * information endpoints which were queried during resource discovery. * * \return A reference to the status map of queried endpoints. * \since Added in 3.0.0 */ const EndpointStatusMap& GetEndpointQueryingStatuses() const { return queryingStatusMap; } /// Clear endpoint status querying map. /** * \since Added in 3.0.0 */ void ClearEndpointQueryingStatuses() { queryingStatusMap.clear(); } /// Get submission status map /** * The returned map contains EndpointSubmissionStatus objects for all the * submission endpoints which were tried for job submission. * * \return A reference to the submission status map. * \since Added in 3.0.0 */ const std::map& GetEndpointSubmissionStatuses() const { return submissionStatusMap; } /// Clear submission status map /** * \since Added in 3.0.0 */ void ClearEndpointSubmissionStatuses() { submissionStatusMap.clear(); } /// Clear all status maps /** * Convenience method which calls \ref ClearEndpointQueryingStatuses and * \ref ClearEndpointSubmissionStatuses. * \since Added in 3.0.0 */ void ClearAllStatuses() { queryingStatusMap.clear(); submissionStatusMap.clear(); } /// Clear all /** * Convenience method which calls \ref ClearNotSubmittedDescriptions and * \ref ClearEndpointQueryingStatuses and * \ref ClearEndpointSubmissionStatuses. * * \since Added in 3.0.0 */ void ClearAll() { notsubmitted.clear(); queryingStatusMap.clear(); submissionStatusMap.clear(); } // === private: class ConsumerWrapper : public EntityConsumer { public: ConsumerWrapper(Submitter& s) : s(s) {} void addEntity(const Job& j) { for (std::list*>::iterator it = s.consumers.begin(); it != s.consumers.end(); ++it) { (*it)->addEntity(j); } } private: Submitter& s; }; SubmissionStatus SubmitNoClear(const Endpoint& endpoint, const std::list& descs); const UserConfig& uc; EndpointStatusMap queryingStatusMap; std::map submissionStatusMap; std::list notsubmitted; std::list*> consumers; static SubmitterPluginLoader& getLoader(); static Logger logger; }; } #endif // __ARC_SUBMITTER_H__