diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/IRemoteJobManager.h b/Code/Mantid/Framework/API/inc/MantidAPI/IRemoteJobManager.h index eb6d2ef97bd9dc3a643f69368c4be2be348d5757..c4c671d891850ea8647b50312391321fc6524608 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/IRemoteJobManager.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/IRemoteJobManager.h @@ -99,6 +99,12 @@ public: /// Date-time the job finished. No particular format can be /// assumed Mantid::Kernel::DateAndTime completionTime; + /// Command line for this job (when running a command ideally this + /// would be provided by the underlying job scheduling + /// mechanism). As examples, Platform LSF provides this. For the + /// Mantid remote job submission API it probably doesn't add any + /// important information. + std::string cmdLine; }; /** @@ -116,6 +122,19 @@ public: virtual void authenticate(const std::string &username, const std::string &password) = 0; + /** + * Logout from the remote resource (close session). Depending on the + * underlying scheduler and authentication system this may have + * different implementations and effects. In some cases, like the + * Mantid Remote Job Submission API v1, this may not send any + * request to the remote. + * + * @param username Username on the remote resource. + * + * @throws std::runtime_error If the operation fails + */ + virtual void logout(const std::string &username) = 0; + /** * Submit a job (and implicitly request to start it) within a * transaction. diff --git a/Code/Mantid/Framework/API/test/RemoteJobManagerFactoryTest.h b/Code/Mantid/Framework/API/test/RemoteJobManagerFactoryTest.h index 90153f606e93124b98a2dcd09ad6d8dfec268a55..635ecac70d58e02a0ac79caecb06a318b980cc91 100644 --- a/Code/Mantid/Framework/API/test/RemoteJobManagerFactoryTest.h +++ b/Code/Mantid/Framework/API/test/RemoteJobManagerFactoryTest.h @@ -15,6 +15,8 @@ public: virtual void authenticate(const std::string & /*username*/, const std::string & /*password*/) {} + virtual void logout(const std::string & /*username*/) {} + virtual std::string submitRemoteJob(const std::string & /*transactionID*/, const std::string & /*runnable*/, const std::string & /*param*/, diff --git a/Code/Mantid/Framework/RemoteAlgorithms/CMakeLists.txt b/Code/Mantid/Framework/RemoteAlgorithms/CMakeLists.txt index 9b69bd26fb90e2bbd7e3c457e4ed2d2272e1d1d0..15642bbb5b84d047958319083251a625b0866ec6 100644 --- a/Code/Mantid/Framework/RemoteAlgorithms/CMakeLists.txt +++ b/Code/Mantid/Framework/RemoteAlgorithms/CMakeLists.txt @@ -5,6 +5,7 @@ set( SRC_FILES src/Authenticate2.cpp src/DownloadRemoteFile.cpp src/DownloadRemoteFile2.cpp + src/Logout2.cpp src/QueryAllRemoteJobs.cpp src/QueryAllRemoteJobs2.cpp src/QueryRemoteFile.cpp @@ -30,6 +31,7 @@ set( INC_FILES inc/MantidRemoteAlgorithms/Authenticate2.h inc/MantidRemoteAlgorithms/DownloadRemoteFile.h inc/MantidRemoteAlgorithms/DownloadRemoteFile2.h + inc/MantidRemoteAlgorithms/Logout2.h inc/MantidRemoteAlgorithms/QueryAllRemoteJobs.h inc/MantidRemoteAlgorithms/QueryAllRemoteJobs2.h inc/MantidRemoteAlgorithms/QueryRemoteFile.h @@ -55,6 +57,7 @@ set ( TEST_FILES Authenticate2Test.h DownloadRemoteFileTest.h DownloadRemoteFile2Test.h + Logout2Test.h QueryAllRemoteJobsTest.h QueryAllRemoteJobs2Test.h QueryRemoteFileTest.h diff --git a/Code/Mantid/Framework/RemoteAlgorithms/inc/MantidRemoteAlgorithms/Logout2.h b/Code/Mantid/Framework/RemoteAlgorithms/inc/MantidRemoteAlgorithms/Logout2.h new file mode 100644 index 0000000000000000000000000000000000000000..033f47da026443648a5bef9b8f94243d6ae01bf3 --- /dev/null +++ b/Code/Mantid/Framework/RemoteAlgorithms/inc/MantidRemoteAlgorithms/Logout2.h @@ -0,0 +1,60 @@ +#ifndef LOGOUT2_H_ +#define LOGOUT2_H_ + +#include "MantidAPI/Algorithm.h" + +namespace Mantid { +namespace RemoteAlgorithms { + +/** +Logout from the remote compute resource. + +Copyright © 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge +National Laboratory & European Spallation Source + +This file is part of Mantid. + +Mantid is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +Mantid is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. + +File change history is stored at: <https://github.com/mantidproject/mantid>. +Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class DLLExport Logout2 : public Mantid::API::Algorithm { +public: + /// Default constructor + Logout2() : Mantid::API::Algorithm() {} + /// Virtual destructor + virtual ~Logout2() {} + /// Algorithm's name + virtual const std::string name() const { return "Logout"; } + /// Summary of algorithms purpose + virtual const std::string summary() const { + return "Logout from a remote compute resource."; + } + + /// Algorithm's version + virtual int version() const { return (2); } + /// Algorithm's category for identification + virtual const std::string category() const { return "Remote"; } + +private: + void init(); + /// Execution code + void exec(); +}; + +} // end namespace RemoteAlgorithms +} // end namespace Mantid + +#endif /*LOGOUT2_H_*/ diff --git a/Code/Mantid/Framework/RemoteAlgorithms/src/Logout2.cpp b/Code/Mantid/Framework/RemoteAlgorithms/src/Logout2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b73758d8c1975e1ee2d0dc3bdca930fff9795646 --- /dev/null +++ b/Code/Mantid/Framework/RemoteAlgorithms/src/Logout2.cpp @@ -0,0 +1,50 @@ +#include "MantidAPI/RemoteJobManagerFactory.h" +#include "MantidKernel/FacilityInfo.h" +#include "MantidKernel/ListValidator.h" +#include "MantidKernel/MandatoryValidator.h" +#include "MantidRemoteAlgorithms/Logout2.h" + +namespace Mantid { +namespace RemoteAlgorithms { + +// Register the algorithm into the Algorithm Factory +DECLARE_ALGORITHM(Logout2) + +using namespace Mantid::Kernel; + +// A reference to the logger is provided by the base class, it is called g_log. + +void Logout2::init() { + // Unlike most algorithms, this wone doesn't deal with workspaces.... + + auto requireValue = boost::make_shared<MandatoryValidator<std::string>>(); + + // Compute Resources + std::vector<std::string> computes = Mantid::Kernel::ConfigService::Instance() + .getFacility() + .computeResources(); + declareProperty("ComputeResource", "", + boost::make_shared<StringListValidator>(computes), + "The remote computer to log out from", Direction::Input); + + declareProperty("UserName", "", requireValue, + "Name of the user to authenticate as", Direction::Input); + +} + +void Logout2::exec() { + + const std::string comp = getPropertyValue("ComputeResource"); + Mantid::API::IRemoteJobManager_sptr jobManager = + Mantid::API::RemoteJobManagerFactory::Instance().create(comp); + + const std::string user = getPropertyValue("UserName"); + jobManager->logout(user); + + g_log.information() << "Logged out from the compute resource " << comp + << ". You will need to authenticate before interacting " + "again with the resource. " << std::endl; +} + +} // end namespace RemoteAlgorithms +} // end namespace Mantid diff --git a/Code/Mantid/Framework/RemoteAlgorithms/src/QueryAllRemoteJobs2.cpp b/Code/Mantid/Framework/RemoteAlgorithms/src/QueryAllRemoteJobs2.cpp index 4ee0ffafd978e9369616ae56a8bef2d5b18efab2..b7792e177402ae9cd7612c5199531a07ce5ca1f6 100644 --- a/Code/Mantid/Framework/RemoteAlgorithms/src/QueryAllRemoteJobs2.cpp +++ b/Code/Mantid/Framework/RemoteAlgorithms/src/QueryAllRemoteJobs2.cpp @@ -32,8 +32,8 @@ void QueryAllRemoteJobs2::init() { // declare several array properties for different pieces of data. Values from // the same array index are for the same job. declareProperty( - new ArrayProperty<std::string>("JobId", nullValidator, Direction::Output), - "ID string for the job"); + new ArrayProperty<std::string>("JobID", nullValidator, Direction::Output), + "ID string for the job(s)"); declareProperty(new ArrayProperty<std::string>( "JobStatusString", nullValidator, Direction::Output), "Description of the job's current status (Queued, Running, " @@ -60,6 +60,10 @@ void QueryAllRemoteJobs2::init() { declareProperty(new ArrayProperty<std::string>( "CompletionDate", nullValidator, Direction::Output), "The date & time the job finished"); + + declareProperty(new ArrayProperty<std::string>( + "CommandLine", nullValidator, Direction::Output), + "The command line run by this job on the remote compute resource machine(s)"); } void QueryAllRemoteJobs2::exec() { @@ -78,6 +82,7 @@ void QueryAllRemoteJobs2::exec() { std::vector<std::string> submitDates; std::vector<std::string> startDates; std::vector<std::string> completionDates; + std::vector<std::string> cmdLine; for (size_t j = 0; j < infos.size(); ++j) { jobIds.push_back(infos[j].id); jobNames.push_back(infos[j].name); @@ -87,8 +92,9 @@ void QueryAllRemoteJobs2::exec() { submitDates.push_back(infos[j].submitDate.toISO8601String()); startDates.push_back(infos[j].startDate.toISO8601String()); completionDates.push_back(infos[j].completionTime.toISO8601String()); + cmdLine.push_back(infos[j].cmdLine); } - setProperty("JobId", jobIds); + setProperty("JobID", jobIds); setProperty("JobStatusString", jobStatusStrs); setProperty("JobName", jobNames); setProperty("ScriptName", runNames); @@ -96,6 +102,7 @@ void QueryAllRemoteJobs2::exec() { setProperty("SubmitDate", submitDates); setProperty("StartDate", startDates); setProperty("CompletionDate", completionDates); + setProperty("CommandLine", cmdLine); } } // end namespace RemoteAlgorithms diff --git a/Code/Mantid/Framework/RemoteAlgorithms/src/QueryRemoteJob2.cpp b/Code/Mantid/Framework/RemoteAlgorithms/src/QueryRemoteJob2.cpp index 3d6db94c551ed9f595ac5a957cf3c638297fba5c..d66c0e23ddddbd25cfb0474294736db839ac32a0 100644 --- a/Code/Mantid/Framework/RemoteAlgorithms/src/QueryRemoteJob2.cpp +++ b/Code/Mantid/Framework/RemoteAlgorithms/src/QueryRemoteJob2.cpp @@ -71,6 +71,14 @@ void QueryRemoteJob2::init() { "The date & time the job finished (availability is optional " "and implementation dependent)", Direction::Output); + + declareProperty("CommandLine", "", nullValidator, + "The command line run by this job on the remote compute " + "resource machine(s), which in some cases may provide " + "additional low level information on the environment used " + "(modules loaded, exact versions (paths) of scripts being " + "run, etc.)", + Direction::Output); } void QueryRemoteJob2::exec() { @@ -88,6 +96,7 @@ void QueryRemoteJob2::exec() { setProperty("SubmitDate", info.submitDate.toISO8601String()); setProperty("StartDate", info.startDate.toISO8601String()); setProperty("CompletionDate", info.completionTime.toISO8601String()); + setProperty("CommandLine", info.cmdLine); } } // end namespace RemoteAlgorithms diff --git a/Code/Mantid/Framework/RemoteAlgorithms/src/SubmitRemoteJob2.cpp b/Code/Mantid/Framework/RemoteAlgorithms/src/SubmitRemoteJob2.cpp index e07a810388c5b70675bd1ea49b214b4005a1b963..02f9f9081ba56494d6947a50be677080c7b616b6 100644 --- a/Code/Mantid/Framework/RemoteAlgorithms/src/SubmitRemoteJob2.cpp +++ b/Code/Mantid/Framework/RemoteAlgorithms/src/SubmitRemoteJob2.cpp @@ -75,8 +75,7 @@ void SubmitRemoteJob2::exec() { // Put the algorithm execution code here... const std::string comp = getPropertyValue("ComputeResource"); Mantid::API::IRemoteJobManager_sptr jm = - Mantid::API::RemoteJobManagerFactory::Instance().create( - comp); + Mantid::API::RemoteJobManagerFactory::Instance().create(comp); const std::string tid = getPropertyValue("TransactionID"); const std::string runnable = getPropertyValue("ScriptName"); @@ -84,11 +83,20 @@ void SubmitRemoteJob2::exec() { const std::string displayName = getPropertyValue("TaskName"); const int nodes = getProperty("NumNodes"); const int cores = getProperty("CoresPerNode"); - std::string jid = jm->submitRemoteJob(tid, runnable, params, displayName, nodes, cores); - - setPropertyValue("JobID", jid); - g_log.information() << "Job submitted. Job ID = " - << jid << " on (remote) compute resource " << comp << std::endl; + std::string jid = + jm->submitRemoteJob(tid, runnable, params, displayName, nodes, cores); + + try { + setProperty("JobID", jid); + } catch (std::exception &e) { + throw std::runtime_error("Could not set the output property JobID with the " + "ID value returned from the compute resource: '" + + jid + "'. This looks as if there has been " + "an error in the job submission. Error " + "description: " + e.what()); + } + g_log.information() << "Job submitted. Job ID = " << jid + << " on (remote) compute resource " << comp << std::endl; } } // end namespace RemoteAlgorithms diff --git a/Code/Mantid/Framework/RemoteAlgorithms/test/Logout2Test.h b/Code/Mantid/Framework/RemoteAlgorithms/test/Logout2Test.h new file mode 100644 index 0000000000000000000000000000000000000000..cd9b0b45e5f993ff53d7b05245bd048c8250d8b6 --- /dev/null +++ b/Code/Mantid/Framework/RemoteAlgorithms/test/Logout2Test.h @@ -0,0 +1,97 @@ +#ifndef MANTID_REMOTEALGORITHMS_LOGOUT2TEST_H_ +#define MANTID_REMOTEALGORITHMS_LOGOUT2TEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidAPI/AlgorithmManager.h" +#include "MantidKernel/ConfigService.h" +#include "MantidKernel/FacilityInfo.h" +#include "MantidRemoteAlgorithms/Logout2.h" + +using namespace Mantid::RemoteAlgorithms; + +class Logout2Test : public CxxTest::TestSuite { +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static Logout2Test *createSuite() { return new Logout2Test(); } + static void destroySuite(Logout2Test *suite) { delete suite; } + + void test_algorithm() { + testAlg = Mantid::API::AlgorithmManager::Instance().create("Logout", 2); + TS_ASSERT(testAlg); + TS_ASSERT_EQUALS(testAlg->name(), "Logout"); + TS_ASSERT_EQUALS(testAlg->version(), 2); + } + + void test_castAlgorithm() { + // can create + boost::shared_ptr<Logout2> a; + TS_ASSERT(a = boost::make_shared<Logout2>()); + // can cast to inherited interfaces and base classes + + TS_ASSERT(dynamic_cast<Mantid::RemoteAlgorithms::Logout2 *>(a.get())); + TS_ASSERT(dynamic_cast<Mantid::API::Algorithm *>(a.get())); + TS_ASSERT(dynamic_cast<Mantid::Kernel::PropertyManagerOwner *>(a.get())); + TS_ASSERT(dynamic_cast<Mantid::API::IAlgorithm *>(a.get())); + TS_ASSERT(dynamic_cast<Mantid::Kernel::IPropertyManager *>(a.get())); + } + + void test_init() { + if (!testAlg->isInitialized()) + TS_ASSERT_THROWS_NOTHING(testAlg->initialize()); + + TS_ASSERT(testAlg->isInitialized()); + + Logout2 lo; + TS_ASSERT_THROWS_NOTHING(lo.initialize()); + } + + void test_propertiesMissing() { + Logout2 lo; + TS_ASSERT_THROWS_NOTHING(lo.initialize()); + // username missing + TS_ASSERT_THROWS(lo.setPropertyValue("ComputeResource", "anything"), + std::invalid_argument); + + TS_ASSERT_THROWS(lo.execute(), std::runtime_error); + TS_ASSERT(!lo.isExecuted()); + } + + void test_wrongProperty() { + Logout2 lo; + TS_ASSERT_THROWS_NOTHING(lo.initialize()); + TS_ASSERT_THROWS(lo.setPropertyValue("usernam", "anything"), + std::runtime_error); + } + + void test_runOK() { + testFacilities.push_back(std::make_pair("SNS", "Fermi")); + testFacilities.push_back(std::make_pair("ISIS", "SCARF@STFC")); + + const Mantid::Kernel::FacilityInfo &prevFac = + Mantid::Kernel::ConfigService::Instance().getFacility(); + // test that job managers are created correctly for different facilities + for (size_t fi = 0; fi < testFacilities.size(); fi++) { + const std::string facName = testFacilities[fi].first; + const std::string compName = testFacilities[fi].second; + + Mantid::Kernel::ConfigService::Instance().setFacility(facName); + Logout2 lo; + TS_ASSERT_THROWS_NOTHING(lo.initialize()); + TS_ASSERT_THROWS_NOTHING( + lo.setPropertyValue("ComputeResource", compName)); + // Note: this would run the algorithm and could do a remote + // connection. uncomment only when/if we have a mock up for this + // TS_ASSERT_THROWS(lo.execute(), std::exception); + TS_ASSERT(!lo.isExecuted()); + } + Mantid::Kernel::ConfigService::Instance().setFacility(prevFac.name()); + } + +private: + Mantid::API::IAlgorithm_sptr testAlg; + std::vector<std::pair<std::string, std::string>> testFacilities; +}; + +#endif // MANTID_REMOTEALGORITHMS_LOGOUT2TEST_H_ diff --git a/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/LSFJobManager.h b/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/LSFJobManager.h index 9d95b93c703ffe1183d11c71e138d5e4f2751933..4f56c7406e8aad24b33b9da5cbe6d9f74f48b2d0 100644 --- a/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/LSFJobManager.h +++ b/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/LSFJobManager.h @@ -49,6 +49,9 @@ public: virtual void authenticate(const std::string &username, const std::string &password) = 0; + /// Most likely the logout method specific to SCARFLSF would be valid here + virtual void logout(const std::string &username) = 0; + virtual void abortRemoteJob(const std::string &jobID); virtual std::string @@ -110,7 +113,7 @@ protected: typedef std::pair<std::string, Token> UsernameToken; // store for username-token pairs - std::map<std::string, Token> m_tokenStash; + static std::map<std::string, Token> g_tokenStash; /// Minimal representation of a transaction: an ID and a list of job IDs struct Transaction { @@ -120,7 +123,7 @@ protected: }; /// Minimal store for transaction information - std::map<std::string, Transaction> m_transactions; + static std::map<std::string, Transaction> g_transactions; // HTTP specifics for SCARF (IBM LSF PAC) static std::string g_acceptType; diff --git a/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/MantidWebServiceAPIHelper.h b/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/MantidWebServiceAPIHelper.h index 9c97eedf87b1cfbb8c954200ae63ef246584d0ee..06d9636e3e9d6f006e4de602fa662d69af48f2b5 100644 --- a/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/MantidWebServiceAPIHelper.h +++ b/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/MantidWebServiceAPIHelper.h @@ -92,6 +92,9 @@ public: const std::string &getDisplayName() const { return m_displayName; } + // forget authentication token / cookie + void clearSessionCookies(); + private: // Wraps up some of the boilerplate code needed to execute HTTP GET and POST // requests @@ -120,7 +123,7 @@ private: // function // takes a NameValueCollection object, so we have to convert. (WTF Poco // devs?!?) - static std::vector<Poco::Net::HTTPCookie> m_cookies; + static std::vector<Poco::Net::HTTPCookie> g_cookies; Poco::Net::NameValueCollection getCookies() const; mutable Poco::Net::HTTPClientSession * diff --git a/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/MantidWebServiceAPIJobManager.h b/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/MantidWebServiceAPIJobManager.h index 164f8d245e67b636486f9e4868d80f324b474568..f248696ef7850fbd731338df31f9d772bfdb71fe 100644 --- a/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/MantidWebServiceAPIJobManager.h +++ b/Code/Mantid/Framework/RemoteJobManagers/inc/MantidRemoteJobManagers/MantidWebServiceAPIJobManager.h @@ -36,10 +36,12 @@ Code Documentation is available at: <http://doxygen.mantidproject.org> class DLLExport MantidWebServiceAPIJobManager : public Mantid::API::IRemoteJobManager { public: - virtual ~MantidWebServiceAPIJobManager() {}; + virtual ~MantidWebServiceAPIJobManager(){}; void authenticate(const std::string &username, const std::string &password); + void logout(const std::string &username); + std::string submitRemoteJob(const std::string &transactionID, const std::string &runnable, const std::string ¶m, const std::string &taskName = "", @@ -71,17 +73,17 @@ public: protected: /// Use the helper for these operations virtual std::istream &httpGet(const std::string &path, - const std::string &query_str = "", - const std::string &username = "", - const std::string &password = "") const { + const std::string &query_str = "", + const std::string &username = "", + const std::string &password = "") const { return m_helper.httpGet(path, query_str, username, password); } virtual std::istream & httpPost(const std::string &path, const MantidWebServiceAPIHelper::PostDataMap &postData, - const MantidWebServiceAPIHelper::PostDataMap &fileData = - MantidWebServiceAPIHelper::PostDataMap(), + const MantidWebServiceAPIHelper::PostDataMap & + fileData = MantidWebServiceAPIHelper::PostDataMap(), const std::string &username = "", const std::string &password = "") const { return m_helper.httpPost(path, postData, fileData, username, password); @@ -91,6 +93,8 @@ protected: return m_helper.lastStatus(); } + void clearSessionCookies() { m_helper.clearSessionCookies(); } + private: MantidWebServiceAPIHelper m_helper; }; diff --git a/Code/Mantid/Framework/RemoteJobManagers/src/LSFJobManager.cpp b/Code/Mantid/Framework/RemoteJobManagers/src/LSFJobManager.cpp index b1c13be5c299726ee4575eaf1fc0b5ade82aeacb..6656b57d4aff4ffb920c8e93652a225a62c7fe02 100644 --- a/Code/Mantid/Framework/RemoteJobManagers/src/LSFJobManager.cpp +++ b/Code/Mantid/Framework/RemoteJobManagers/src/LSFJobManager.cpp @@ -30,6 +30,11 @@ namespace { Mantid::Kernel::Logger g_log("LSFJobManager"); } +std::map<std::string, LSFJobManager::Token> LSFJobManager::g_tokenStash; +std::map<std::string, LSFJobManager::Transaction> LSFJobManager::g_transactions; + +std::string LSFJobManager::g_acceptType = "text/plain,application/xml,text/xml"; + std::string LSFJobManager::g_killPathBase = "webservice/pacclient/jobOperation/kill/"; @@ -48,12 +53,10 @@ std::string LSFJobManager::g_downloadOneBasePath = "webservice/pacclient/file/"; std::string LSFJobManager::g_downloadAllJobFilesBasePath = "webservice/pacclient/jobfiles/"; -std::string LSFJobManager::g_acceptType = "text/plain,application/xml,text/xml"; - using namespace Mantid::Kernel; void LSFJobManager::abortRemoteJob(const std::string &jobID) { - if (m_tokenStash.empty()) { + if (g_tokenStash.empty()) { throw std::runtime_error( "Abort job failed because you do not seem to have logged " "in."); @@ -64,7 +67,7 @@ void LSFJobManager::abortRemoteJob(const std::string &jobID) { } // only support for single-user - Token tok = m_tokenStash.begin()->second; + Token tok = g_tokenStash.begin()->second; const std::string token = tok.m_token_str; const Poco::URI fullURL = makeFullURI(tok.m_url, g_killPathBase, jobID); @@ -118,12 +121,12 @@ void LSFJobManager::abortRemoteJob(const std::string &jobID) { void LSFJobManager::downloadRemoteFile(const std::string &transactionID, const std::string &remoteFileName, const std::string &localFileName) { - auto it = m_transactions.find(transactionID); - if (m_transactions.end() == it) + auto it = g_transactions.find(transactionID); + if (g_transactions.end() == it) throw std::invalid_argument("Could not find a transaction with ID: " + transactionID); - if (m_tokenStash.empty()) { + if (g_tokenStash.empty()) { throw std::runtime_error( "File download failed. You do not seem to have logged in."); } @@ -136,7 +139,7 @@ void LSFJobManager::downloadRemoteFile(const std::string &transactionID, } // only support for single-user - Token tok = m_tokenStash.begin()->second; + Token tok = g_tokenStash.begin()->second; // assume that the last job is what we want const std::string jobId = jobIDs.back(); @@ -159,13 +162,13 @@ void LSFJobManager::downloadRemoteFile(const std::string &transactionID, */ std::vector<Mantid::API::IRemoteJobManager::RemoteJobInfo> LSFJobManager::queryAllRemoteJobs() const { - if (m_tokenStash.empty()) { + if (g_tokenStash.empty()) { throw std::runtime_error( "Job status query failed. You do not seem to have logged " "in."); } // only support for single-user - Token tok = m_tokenStash.begin()->second; + Token tok = g_tokenStash.begin()->second; // Job query status, needs these headers: // headers = {'Content-Type': 'application/xml', 'Cookie': token, @@ -225,16 +228,16 @@ LSFJobManager::queryAllRemoteJobs() const { */ std::vector<std::string> LSFJobManager::queryRemoteFile(const std::string &transactionID) const { - auto it = m_transactions.find(transactionID); - if (m_transactions.end() == it) + auto it = g_transactions.find(transactionID); + if (g_transactions.end() == it) throw std::invalid_argument("Could not find a transaction with ID: " + transactionID); - if (m_tokenStash.empty()) { + if (g_tokenStash.empty()) { throw std::runtime_error( "Remote file names query failed. You do not seem to have logged in."); } // only support for single-user - Token tok = m_tokenStash.begin()->second; + Token tok = g_tokenStash.begin()->second; std::vector<std::string> jobIDs = it->second.jobIDs; if (jobIDs.empty()) { @@ -311,7 +314,7 @@ LSFJobManager::queryRemoteFile(const std::string &transactionID) const { */ Mantid::API::IRemoteJobManager::RemoteJobInfo LSFJobManager::queryRemoteJob(const std::string &jobID) const { - if (m_tokenStash.empty()) { + if (g_tokenStash.empty()) { throw std::runtime_error( "Job status query failed. You do not seem to have logged " "in."); @@ -322,7 +325,7 @@ LSFJobManager::queryRemoteJob(const std::string &jobID) const { } // only support for single-user - Token tok = m_tokenStash.begin()->second; + Token tok = g_tokenStash.begin()->second; // Job query status, needs these headers: // headers = {'Content-Type': 'application/xml', 'Cookie': token, @@ -406,17 +409,17 @@ LSFJobManager::queryRemoteJob(const std::string &jobID) const { * @throw std::runtime_error if there is another issue (like not logged in) */ std::string LSFJobManager::startRemoteTransaction() { - if (m_tokenStash.empty()) { + if (g_tokenStash.empty()) { throw std::runtime_error( "Transaction start operation failed. You do not seem to have logged " "in."); } - size_t idx = m_transactions.size(); + size_t idx = g_transactions.size(); std::string tid = std::string("LSFTrans_") + boost::lexical_cast<std::string>(idx + 1); - auto ret = m_transactions.insert( + auto ret = g_transactions.insert( std::pair<std::string, Transaction>(tid, LSFJobManager::Transaction())); // not inserted @@ -442,15 +445,15 @@ std::string LSFJobManager::startRemoteTransaction() { * @throw std::runtime_error if there is another issue (like not logged in) */ void LSFJobManager::stopRemoteTransaction(const std::string &transactionID) { - if (m_tokenStash.empty()) { + if (g_tokenStash.empty()) { throw std::runtime_error( "Transaction stop operation failed. You do not seem to have logged " "in."); } - auto it = m_transactions.find(transactionID); + auto it = g_transactions.find(transactionID); - if (m_transactions.end() == it) + if (g_transactions.end() == it) throw std::invalid_argument("Could not find a transaction with ID: " + transactionID); @@ -460,7 +463,7 @@ void LSFJobManager::stopRemoteTransaction(const std::string &transactionID) { for (size_t i = 0; i < jobs.size(); i++) { abortRemoteJob(jobs[i]); } - m_transactions.erase(it); + g_transactions.erase(it); } /** @@ -491,7 +494,7 @@ std::string LSFJobManager::submitRemoteJob(const std::string &transactionID, const std::string &taskName, const int numNodes, const int coresPerNode) { - if (m_tokenStash.empty()) { + if (g_tokenStash.empty()) { throw std::runtime_error( "Job submission failed. You do not seem to have logged in."); } @@ -500,7 +503,7 @@ std::string LSFJobManager::submitRemoteJob(const std::string &transactionID, transactionID); } // only support for single-user - Token tok = m_tokenStash.begin()->second; + Token tok = g_tokenStash.begin()->second; // Job submit query, requires specific parameters for LSF submit // Example params passed to python submit utility: @@ -534,7 +537,7 @@ std::string LSFJobManager::submitRemoteJob(const std::string &transactionID, "Error while sending HTTP request to submit a job: " + std::string(ie.what())); } - std::string jobID = "not found"; + std::string jobID = ""; if (Mantid::Kernel::InternetHelper::HTTP_OK == code) { std::string resp = ss.str(); if (std::string::npos != resp.find("<errMsg>")) { @@ -542,16 +545,20 @@ std::string LSFJobManager::submitRemoteJob(const std::string &transactionID, << "Submitted job but got a a response that seems to contain " "an error message : " << extractPACErrMsg(ss.str()) << std::endl; } else { - g_log.notice() << "Submitted job successfully." << std::endl; - g_log.debug() << "Response from server: " << resp << std::endl; // get job id number - if (std::string::npos != resp.find("<id>")) { - jobID = resp.substr(resp.rfind("<id>") + 1); + const std::string idTag = "<id>"; + if (std::string::npos != resp.find(idTag)) { + jobID = resp.substr(resp.rfind(idTag) + idTag.length()); jobID = jobID.substr(0, jobID.find('<')); } else { - // default if badly formed string returned + // default if badly formed string returned / unable to parse ID from + // response jobID = "0"; } + g_log.notice() << "Submitted job successfully. It got ID: " << jobID + << std::endl; + g_log.debug() << "Response from server after submission: " << resp + << std::endl; } } else { throw std::runtime_error( @@ -565,10 +572,11 @@ std::string LSFJobManager::submitRemoteJob(const std::string &transactionID, int iid = boost::lexical_cast<int>(jobID); addJobInTransaction(jobID); g_log.debug() << "Submitted job, got ID: " << iid << std::endl; - } catch (std::runtime_error) { - g_log.warning() - << "The job has been succesfully submitted but the code returned does " - "not seem well formed." << std::endl; + } catch (std::exception &e) { + g_log.warning() << "The job has been submitted but the code returned does " + "not seem well formed: '" + + jobID + "'. Detailed error: " + + e.what() << std::endl; } return jobID; @@ -592,7 +600,7 @@ std::string LSFJobManager::submitRemoteJob(const std::string &transactionID, void LSFJobManager::uploadRemoteFile(const std::string &transactionID, const std::string &remoteFileName, const std::string &localFileName) { - if (m_tokenStash.empty()) { + if (g_tokenStash.empty()) { throw std::runtime_error( "File upload failed. You do not seem to have logged in."); } @@ -601,7 +609,7 @@ void LSFJobManager::uploadRemoteFile(const std::string &transactionID, transactionID); } // only support for single-user - Token tok = m_tokenStash.begin()->second; + Token tok = g_tokenStash.begin()->second; // File upload, needs these headers: // headers = {'Content-Type': 'multipart/mixed; boundary='+boundary, @@ -765,14 +773,19 @@ LSFJobManager::genOutputStatusInfo(const std::string &resp, info.back().status = "Unknown!"; } + // there is no safe simple way to extract the script/binary from command + // lines with potentially several 'load module' and other set-environment + // instructions. The whole command line is used below for 'Commandline' + info.back().runnableName = "Not available"; + + info.back().transactionID = "no ID"; + Poco::XML::Element *cmd = el->getChildElement("cmd"); if (cmd) { - info.back().runnableName = cmd->innerText(); + info.back().cmdLine = cmd->innerText(); } else { - info.back().runnableName = "Unknown!"; + info.back().cmdLine = "Not available"; } - - info.back().transactionID = "no ID"; } return info; @@ -1281,9 +1294,9 @@ LSFJobManager::makeHeaders(const std::string &contentType, * being stopped. */ bool LSFJobManager::findTransaction(const std::string &id) const { - auto it = m_transactions.find(id); + auto it = g_transactions.find(id); - return (it != m_transactions.end() && !it->second.stopped); + return (it != g_transactions.end() && !it->second.stopped); } /** @@ -1293,9 +1306,9 @@ bool LSFJobManager::findTransaction(const std::string &id) const { * */ void LSFJobManager::addJobInTransaction(const std::string &jobID) { - if (m_transactions.size() <= 0) + if (g_transactions.size() <= 0) return; - auto &jobs = m_transactions.rbegin()->second.jobIDs; + auto &jobs = g_transactions.rbegin()->second.jobIDs; auto it = std::find(jobs.begin(), jobs.end(), jobID); if (jobs.end() == it) jobs.push_back(jobID); diff --git a/Code/Mantid/Framework/RemoteJobManagers/src/MantidWebServiceAPIHelper.cpp b/Code/Mantid/Framework/RemoteJobManagers/src/MantidWebServiceAPIHelper.cpp index 76fc0b074470c62601011a0d468ab4b63efe302a..4e984538f4d94f6cb43219f35140766abaefba0b 100644 --- a/Code/Mantid/Framework/RemoteJobManagers/src/MantidWebServiceAPIHelper.cpp +++ b/Code/Mantid/Framework/RemoteJobManagers/src/MantidWebServiceAPIHelper.cpp @@ -17,7 +17,7 @@ namespace Mantid { namespace RemoteJobManagers { -std::vector<Poco::Net::HTTPCookie> MantidWebServiceAPIHelper::m_cookies; +std::vector<Poco::Net::HTTPCookie> MantidWebServiceAPIHelper::g_cookies; MantidWebServiceAPIHelper::MantidWebServiceAPIHelper() : m_session( @@ -64,7 +64,7 @@ std::istream &MantidWebServiceAPIHelper::httpGet( std::vector<Poco::Net::HTTPCookie> newCookies; m_response.getCookies(newCookies); if (newCookies.size() > 0) { - m_cookies = newCookies; + g_cookies = newCookies; } return respStream; @@ -153,12 +153,14 @@ std::istream &MantidWebServiceAPIHelper::httpPost( std::vector<Poco::Net::HTTPCookie> newCookies; m_response.getCookies(newCookies); if (newCookies.size() > 0) { - m_cookies = newCookies; + g_cookies = newCookies; } return respStream; } +void MantidWebServiceAPIHelper::clearSessionCookies() { g_cookies.clear(); } + // Wrappers for a lot of the boilerplate code needed to perform an HTTPS GET or // POST void MantidWebServiceAPIHelper::initGetRequest(Poco::Net::HTTPRequest &req, @@ -226,8 +228,8 @@ void MantidWebServiceAPIHelper::initHTTPRequest(Poco::Net::HTTPRequest &req, // Converts the vector of HTTPCookie objects into a NameValueCollection Poco::Net::NameValueCollection MantidWebServiceAPIHelper::getCookies() const { Poco::Net::NameValueCollection nvc; - std::vector<Poco::Net::HTTPCookie>::const_iterator it = m_cookies.begin(); - while (it != m_cookies.end()) { + std::vector<Poco::Net::HTTPCookie>::const_iterator it = g_cookies.begin(); + while (it != g_cookies.end()) { nvc.add((*it).getName(), (*it).getValue()); ++it; } diff --git a/Code/Mantid/Framework/RemoteJobManagers/src/MantidWebServiceAPIJobManager.cpp b/Code/Mantid/Framework/RemoteJobManagers/src/MantidWebServiceAPIJobManager.cpp index 4b604dabd601c8269b2e9605f4229606a34e8891..5697870ce5ed1d68392273edd513b66d5ecdfb40 100644 --- a/Code/Mantid/Framework/RemoteJobManagers/src/MantidWebServiceAPIJobManager.cpp +++ b/Code/Mantid/Framework/RemoteJobManagers/src/MantidWebServiceAPIJobManager.cpp @@ -28,8 +28,7 @@ using namespace Mantid::Kernel; * (remote) compute resource. */ void MantidWebServiceAPIJobManager::abortRemoteJob(const std::string &jobID) { - std::istream &respStream = - httpGet("/abort", std::string("JobID=") + jobID); + std::istream &respStream = httpGet("/abort", std::string("JobID=") + jobID); if (lastStatus() != Poco::Net::HTTPResponse::HTTP_OK) { JSONObject resp; initFromStream(resp, respStream); @@ -52,8 +51,7 @@ void MantidWebServiceAPIJobManager::authenticate(const std::string &username, const std::string &password) { MantidWebServiceAPIHelper helper; - std::istream &respStream = - httpGet("/authenticate", "", username, password); + std::istream &respStream = httpGet("/authenticate", "", username, password); if (lastStatus() != Poco::Net::HTTPResponse::HTTP_OK) { JSONObject resp; initFromStream(resp, respStream); @@ -63,6 +61,26 @@ void MantidWebServiceAPIJobManager::authenticate(const std::string &username, } } +/** + * Log out from the remote compute resource, which in the API v1. is + * not defined so it is just a no-op in the sense that it does not + * interact with the server. The current session cookie(s) is cleared + * though, so authentication would be required to start transactions + * again. + * + * Note that jobs that are currently running will not be affected by a logout. + * This method does not stop the remote transactions that might have been + * started, as that would imply more than simply logging out (removing files, + * stopping jobs, etc.) . + * + * @param username Username on the remote resource. + */ +void MantidWebServiceAPIJobManager::logout(const std::string &username) { + UNUSED_ARG(username); + + clearSessionCookies(); +} + /** * Download a file from a remote compute resource * @@ -82,8 +100,8 @@ void MantidWebServiceAPIJobManager::downloadRemoteFile( const std::string &localFileName) { std::istream &respStream = - httpGet("/download", std::string("TransID=") + transactionID + - "&File=" + remoteFileName); + httpGet("/download", std::string("TransID=") + transactionID + "&File=" + + remoteFileName); if (lastStatus() == Poco::Net::HTTPResponse::HTTP_OK) { @@ -146,6 +164,7 @@ MantidWebServiceAPIJobManager::queryAllRemoteJobs() const { std::vector<std::string> submitDates; std::vector<std::string> startDates; std::vector<std::string> completionDates; + std::vector<std::string> cmdLines; JSONObject::const_iterator it = resp.begin(); while (it != resp.end()) { @@ -188,6 +207,8 @@ MantidWebServiceAPIJobManager::queryAllRemoteJobs() const { startDates.push_back(""); completionDates.push_back(""); } + // see comment in queryRemoteJob + cmdLines.push_back("Not available"); ++it; } @@ -203,6 +224,7 @@ MantidWebServiceAPIJobManager::queryAllRemoteJobs() const { info.submitDate = DateAndTime(submitDates[i]); info.startDate = DateAndTime(startDates[i]); info.completionTime = DateAndTime(completionDates[i]); + info.cmdLine = cmdLines[i]; result.push_back(info); } @@ -258,8 +280,7 @@ std::vector<std::string> MantidWebServiceAPIJobManager::queryRemoteFile( */ Mantid::API::IRemoteJobManager::RemoteJobInfo MantidWebServiceAPIJobManager::queryRemoteJob(const std::string &jobID) const { - std::istream &respStream = - httpGet("/query", std::string("JobID=") + jobID); + std::istream &respStream = httpGet("/query", std::string("JobID=") + jobID); JSONObject resp; initFromStream(resp, respStream); @@ -293,9 +314,8 @@ MantidWebServiceAPIJobManager::queryRemoteJob(const std::string &jobID) const { info.transactionID = value; // The time stuff is actually an optional extension. We could check the - // info - // URL and see if the server implements it, but it's easier to just look in - // the output and see if the values are there... + // info URL and see if the server implements it, but it's easier to just look + // in the output and see if the values are there... if (status.find("SubmitDate") != status.end()) { status["SubmitDate"].getValue(value); info.submitDate = DateAndTime(value); @@ -307,6 +327,10 @@ MantidWebServiceAPIJobManager::queryRemoteJob(const std::string &jobID) const { info.completionTime = DateAndTime(value); } + // in principle not required for/provided by the Mantid remote job submission + // which always implicitly runs something like 'MantidPlot -xq ScriptName' + info.cmdLine = "Not available"; + return info; } @@ -348,8 +372,8 @@ std::string MantidWebServiceAPIJobManager::startRemoteTransaction() { void MantidWebServiceAPIJobManager::stopRemoteTransaction( const std::string &transactionID) { std::string transId = transactionID; - std::istream &respStream = httpGet( - "/transaction", std::string("Action=Stop&TransID=") + transId); + std::istream &respStream = + httpGet("/transaction", std::string("Action=Stop&TransID=") + transId); if (lastStatus() == Poco::Net::HTTPResponse::HTTP_OK) { g_log.information() << "Transaction ID " << transId << " stopped." diff --git a/Code/Mantid/Framework/RemoteJobManagers/src/SCARFLSFJobManager.cpp b/Code/Mantid/Framework/RemoteJobManagers/src/SCARFLSFJobManager.cpp index 7a7f41865bf4050de34e8c77ed0691a5e1aea5a8..7f85168998993f83c7b85ef686f3190ac24fb06f 100644 --- a/Code/Mantid/Framework/RemoteJobManagers/src/SCARFLSFJobManager.cpp +++ b/Code/Mantid/Framework/RemoteJobManagers/src/SCARFLSFJobManager.cpp @@ -43,8 +43,8 @@ std::string SCARFLSFJobManager::g_pingBaseURL = void SCARFLSFJobManager::authenticate(const std::string &username, const std::string &password) { // base LSFJobManager class only supports a single user presently - m_tokenStash.clear(); - m_transactions.clear(); + g_tokenStash.clear(); + g_transactions.clear(); // Do the URI %-encoding, but component by component std::string encodedUser = urlComponentEncode(username); @@ -83,7 +83,7 @@ void SCARFLSFJobManager::authenticate(const std::string &username, token_str = "platform_token=" + token_str; // insert in the token stash UsernameToken tok(username, Token(url, token_str)); - m_tokenStash.insert(tok); // the password is never stored + g_tokenStash.insert(tok); // the password is never stored g_log.notice() << "Got authentication token for user '" + username + "'. You are now logged in " << std::endl; } else { @@ -146,28 +146,28 @@ bool SCARFLSFJobManager::ping() { * successfully logged in). * * As the authentication method is specific to SCARF, this logout - * method has been placed here as specific to SCARF too. Probably it - * is general to other LSF systems without any/much changes. + * method has been placed here as specific to SCARF too. Most likely + * it is general to other LSF systems without any/much changes. * * @param username Username to use (should have authenticated * before). Leave it empty to log out the last (maybe only) user that * logged in with authenticate(). */ void SCARFLSFJobManager::logout(const std::string &username) { - if (0 == m_tokenStash.size()) { + if (0 == g_tokenStash.size()) { throw std::runtime_error("Logout failed. No one is currenlty logged in."); } std::map<std::string, Token>::iterator it; if (!username.empty()) { - it = m_tokenStash.find(username); - if (m_tokenStash.end() == it) { + it = g_tokenStash.find(username); + if (g_tokenStash.end() == it) { throw std::invalid_argument( "Logout failed. The username given is not logged in: " + username); } } // only support for single-user - Token tok = m_tokenStash.begin()->second; + Token tok = g_tokenStash.begin()->second; // logout query, needs headers = {'Content-Type': 'text/plain', 'Cookie': // token, @@ -196,11 +196,11 @@ void SCARFLSFJobManager::logout(const std::string &username) { // successfully logged out, forget the token if (username.empty()) { // delete first one - m_tokenStash.erase(m_tokenStash.begin()); + g_tokenStash.erase(g_tokenStash.begin()); } else { // delete requested one - if (m_tokenStash.end() != it) - m_tokenStash.erase(it); + if (g_tokenStash.end() != it) + g_tokenStash.erase(it); } } diff --git a/Code/Mantid/Framework/RemoteJobManagers/test/LSFJobManagerTest.h b/Code/Mantid/Framework/RemoteJobManagers/test/LSFJobManagerTest.h index 24406308861b3f6964c84bcfdcc4c623d4b9c85c..70fbb714179f7d1d35c70aec856bcecda4409f8f 100644 --- a/Code/Mantid/Framework/RemoteJobManagers/test/LSFJobManagerTest.h +++ b/Code/Mantid/Framework/RemoteJobManagers/test/LSFJobManagerTest.h @@ -13,6 +13,8 @@ class MockedLSFJobManager : public Mantid::RemoteJobManagers::LSFJobManager { /// needs to define this pure virtual method void authenticate(const std::string & /*username*/, const std::string & /*password*/) {} + + void logout(const std::string & /* username */) {} }; /// This just checks basic cast/interface properties of a virtual diff --git a/Code/Mantid/Framework/RemoteJobManagers/test/SCARFLSFJobManagerTest.h b/Code/Mantid/Framework/RemoteJobManagers/test/SCARFLSFJobManagerTest.h index cdd104ffe67c66f196247dd9d2b2579773dc4128..47b7f6c279977e88043a3c9545bd4112586713a7 100644 --- a/Code/Mantid/Framework/RemoteJobManagers/test/SCARFLSFJobManagerTest.h +++ b/Code/Mantid/Framework/RemoteJobManagers/test/SCARFLSFJobManagerTest.h @@ -334,6 +334,10 @@ public: "upload file when logged in, with a wrong transaction ID, should throw", jm.uploadRemoteFile("wrong_transID", "remote_fname", "local_fname"), std::invalid_argument); + + // logout for the next tests + TSM_ASSERT_THROWS_NOTHING("successful logout should not throw", + jm.logout(goodUsername)); } /// Login is required before running any other command with SCARF (except @@ -359,6 +363,10 @@ public: MockedGoodLoginResponse_SCARFLSFJM login; TSM_ASSERT_THROWS_NOTHING("successful authentication should not throw", login.authenticate(goodUsername, goodPassword)); + + // logout for the next tests + TSM_ASSERT_THROWS_NOTHING("successful logout should not throw", + login.logout(goodUsername)); } void test_startRemoteTransaction() { @@ -370,13 +378,17 @@ public: TSM_ASSERT_THROWS("start transaction should throw when not logged in", tid = jm->startRemoteTransaction(), std::runtime_error); + const std::string user = "user"; TSM_ASSERT_THROWS_NOTHING("successful authentication should not throw", - jm->authenticate("user", "pass")); + jm->authenticate(user, "pass")); TSM_ASSERT_THROWS_NOTHING( "start transaction should not throw when logged in", tid = jm->startRemoteTransaction()); TSM_ASSERT("a successful start transaction should not return an empty ID", tid != ""); + // logout for the next tests + TSM_ASSERT_THROWS_NOTHING("successful logout should not throw", + jm->logout(user)); } void test_stopRemoteTransaction() { @@ -384,8 +396,9 @@ public: TSM_ASSERT("dynamical allocation of job manager should not fail", jm = boost::make_shared<MockedGoodLoginResponse_SCARFLSFJM>()); + const std::string user = "user"; TSM_ASSERT_THROWS_NOTHING("successful authentication should not throw", - jm->authenticate("user", "pass")); + jm->authenticate(user, "pass")); std::string tid; TSM_ASSERT_THROWS_NOTHING( "start remote transaction should not throw when logged in", @@ -404,6 +417,9 @@ public: TSM_ASSERT_THROWS("stop transaction with an ID of a transaction already " "stopped should throw", jm->stopRemoteTransaction(tid), std::invalid_argument); + // logout for the next tests + TSM_ASSERT_THROWS_NOTHING("successful logout should not throw", + jm->logout(user)); } void test_submit() { @@ -430,8 +446,9 @@ public: void test_download() { MockedGoodLoginResponse_SCARFLSFJM jm; + const std::string user = "user"; TSM_ASSERT_THROWS_NOTHING("successful authentication should not throw", - jm.authenticate("user", "pass")); + jm.authenticate(user, "pass")); std::string tid; TSM_ASSERT_THROWS_NOTHING("successful start transaction should not throw", tid = jm.startRemoteTransaction()); @@ -453,6 +470,10 @@ public: TSM_ASSERT( "this fake job manager for testing should not create downloaded files", !Poco::File(localName).exists()); + + // logout for the next tests + TSM_ASSERT_THROWS_NOTHING("successful logout should not throw", + jm.logout(user)); } void test_queryStatus() { @@ -478,8 +499,9 @@ public: std::string id("id0001"); std::string name("name1"); MockedGoodJobStatus_SCARFLSFJM jm(id, name); + const std::string user = "user"; TSM_ASSERT_THROWS_NOTHING("successful authentication should not throw", - jm.authenticate("user", "password")); + jm.authenticate(user, "password")); TSM_ASSERT_THROWS_NOTHING("successful query all jobs should not throw", infos = jm.queryAllRemoteJobs()); std::string tid; @@ -499,12 +521,17 @@ public: "the name obtained when submitting the job", infos[0].name, name); } + + // logout for the next tests + TSM_ASSERT_THROWS_NOTHING("successful logout should not throw", + jm.logout(user)); } void test_queryRemoteFile() { MockedGoodLoginResponse_SCARFLSFJM jm; + const std::string user = "user"; TSM_ASSERT_THROWS_NOTHING("successful authentication should not throw", - jm.authenticate("user", "pass")); + jm.authenticate(user, "pass")); std::string tid; TSM_ASSERT_THROWS_NOTHING("successful start transaction should not throw", tid = jm.startRemoteTransaction()); @@ -521,6 +548,10 @@ public: TSM_ASSERT_THROWS_NOTHING("successful query remote file with correct " "transaction ID should not throw", jm.queryRemoteFile(tid)); + + // logout for the next tests + TSM_ASSERT_THROWS_NOTHING("successful logout should not throw", + jm.logout(user)); } void test_queryStatusByID() { @@ -569,12 +600,12 @@ public: MockedSCARFLSFJM jmFail; std::string tid("trans001"); TSM_ASSERT_THROWS("stop transaction without logging in should throw", - jmFail.stopRemoteTransaction(tid), std::runtime_error); + jmFail.stopRemoteTransaction(tid), std::invalid_argument); MockedErrorResponse_SCARFLSFJM err; TSM_ASSERT_THROWS( "stop transaction with error response from server should throw", - err.stopRemoteTransaction(tid), std::runtime_error); + err.stopRemoteTransaction(tid), std::invalid_argument); TSM_ASSERT_THROWS( "authenticate with error response from server should throw", err.authenticate("user", "pass"), std::runtime_error); diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/TomoReconstruction/TomoReconstruction.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/TomoReconstruction/TomoReconstruction.cpp index 77ef25b865853ae98edd1c20edebb8227a27df4a..3796eb8dc2065835d2c5a253382e09b8f6ae5a10 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/TomoReconstruction/TomoReconstruction.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/TomoReconstruction/TomoReconstruction.cpp @@ -96,8 +96,6 @@ void TomoReconstruction::cleanup() { } void TomoReconstruction::doSetupSectionParameters() { - // TODO: should split the tabs out into their own files? - // geometry, etc. niceties // on the left (just plugin names) 1/2, right: 2/3 QList<int> sizes; @@ -565,13 +563,10 @@ void TomoReconstruction::doLogin(const std::string &pw) { return; } - // TODO (trac #11538): once the remote algorithms are rearranged - // into the 'RemoteJobManager' design, this will use... - // auto alg = Algorithm::fromString("Authenticate"); - auto alg = Algorithm::fromString("SCARFTomoReconstruction"); + auto alg = Algorithm::fromString("Authenticate"); alg->initialize(); alg->setPropertyValue("UserName", user); - alg->setPropertyValue("Action", "LogIn"); + alg->setPropertyValue("ComputeResource", getComputeResource()); alg->setPropertyValue("Password", pw); try { alg->execute(); @@ -583,20 +578,19 @@ void TomoReconstruction::doLogin(const std::string &pw) { } void TomoReconstruction::doLogout() { - // TODO: once the remote algorithms are rearranged into the - // 'RemoteJobManager' design, this will use... - // auto alg = Algorithm::fromString("???"); - need an alg for this - auto alg = Algorithm::fromString("SCARFTomoReconstruction"); + const std::string comp = getComputeResource(); + auto alg = Algorithm::fromString("Logout"); alg->initialize(); - const std::string user = getUsername(); - alg->setPropertyValue("UserName", user); - alg->setPropertyValue("Action", "LogOut"); + alg->setProperty("ComputeResource", comp); + alg->setProperty("UserName", getUsername()); + try { alg->execute(); } catch (std::runtime_error &e) { throw std::runtime_error( "Error when trying to log out from the remote compute resource " + - getComputeResource() + " with username " + user + ": " + e.what()); + getComputeResource() + " with username " + getUsername() + ": " + + e.what()); } } @@ -607,20 +601,23 @@ void TomoReconstruction::doLogout() { * @return True if ping succeeded */ bool TomoReconstruction::doPing() { - // TODO: once the remote algorithms are rearranged into the - // 'RemoteJobManager' design, this will use... - // auto alg = Algorithm::fromString("???"); - auto alg = Algorithm::fromString("SCARFTomoReconstruction"); + // This actually does more than a simple ping. Ping and check that a + // transaction can be created succesfully + auto alg = Algorithm::fromString("StartRemoteTransaction"); alg->initialize(); - alg->setPropertyValue("UserName", getUsername()); - alg->setPropertyValue("Action", "Ping"); + alg->setProperty("ComputeResource", getComputeResource()); + std::string tid; try { alg->execute(); + tid = alg->getPropertyValue("TransactionID"); + g_log.information() << "Pinged succesfully. Checked that a transaction could " + "be created, with ID: " << tid << std::endl; } catch (std::runtime_error &e) { - throw std::runtime_error( - "Error when trying to ping the remote compute resource " + - getComputeResource() + ": " + e.what()); + throw std::runtime_error("Error. Failed to ping and start a transaction on " + "the remote resource." + + std::string(e.what())); } + return true; } @@ -635,26 +632,37 @@ void TomoReconstruction::doSubmitReconstructionJob() { g_log.warning() << "Could not prepare the requested reconstruction job " "submission. There was an error: " + std::string(e.what()); + return; } - // TODO: once the remote algorithms are rearranged into the - // 'RemoteJobManager' design, this will use: - // auto transAlg = Algorithm::fromString("StartRemoteTransaction"); - // auto submitAlg = Algorithm::fromString("SubmitRemoteJob"); - // submitAlg->setPropertyValue("ComputeResource", res); - auto alg = Algorithm::fromString("SCARFTomoReconstruction"); - alg->initialize(); - alg->setPropertyValue("Action", "SubmitJob"); - alg->setPropertyValue("UserName", getUsername()); + const std::string comp = getComputeResource(); - alg->setProperty("RunnablePath", run); - alg->setProperty("JobOptions", opt); + // with SCARF we use one (pseudo)-transaction for every submission + auto transAlg = Algorithm::fromString("StartRemoteTransaction"); + transAlg->initialize(); + transAlg->setProperty("ComputeResource", comp); + std::string tid; + try { + transAlg->execute(); + tid = transAlg->getPropertyValue("TransactionID"); + } catch (std::runtime_error &e) { + throw std::runtime_error("Error when trying to start a transaction right " + "before submitting a reconstruction job: " + + std::string(e.what())); + } + auto submitAlg = Algorithm::fromString("SubmitRemoteJob"); + submitAlg->initialize(); + submitAlg->setProperty("ComputeResource", comp); + submitAlg->setProperty("TaskName", "Mantid tomographic reconstruction job"); + submitAlg->setProperty("TransactionID", tid); + submitAlg->setProperty("ScriptName", run); + submitAlg->setProperty("ScriptParams", opt); try { - alg->execute(); + submitAlg->execute(); } catch (std::runtime_error &e) { throw std::runtime_error( - "Error when trying to cancel a reconstruction job: " + + "Error when trying to submit a reconstruction job: " + std::string(e.what())); } } @@ -669,7 +677,7 @@ void TomoReconstruction::doSubmitReconstructionJob() { */ void TomoReconstruction::makeRunnableWithOptions(std::string &run, std::string &opt) { - std::string comp = + const std::string comp = m_ui.comboBox_run_compute_resource->currentText().toStdString(); checkDataPathsSet(); @@ -722,21 +730,22 @@ void TomoReconstruction::makeRunnableWithOptions(std::string &run, } void TomoReconstruction::doCancelJob(const std::string &id) { - // TODO: once the remote algorithms are rearranged into the - // 'RemoteJobManager' design, this will use: - // auto alg = Algorithm::fromString("EndRemoteTransaction"); - auto alg = Algorithm::fromString("SCARFTomoReconstruction"); - alg->initialize(); - alg->setPropertyValue("UserName", getUsername()); - alg->setPropertyValue("Action", "CancelJob"); - alg->setPropertyValue("JobID", id); + const std::string comp = getComputeResource(); + + auto algJob = Algorithm::fromString("AbortRemoteJob"); + algJob->initialize(); + algJob->setPropertyValue("ComputeResource", comp); + algJob->setPropertyValue("JobID", id); try { - alg->execute(); + algJob->execute(); } catch (std::runtime_error &e) { throw std::runtime_error( "Error when trying to cancel a reconstruction job: " + std::string(e.what())); } + + // doesn't do StopRemoteTransaction. If there are multiple jobs per + // transaction there could be others that are still running. } void TomoReconstruction::toolSetupClicked() { @@ -921,12 +930,12 @@ void TomoReconstruction::getJobStatusInfo() { QMutexLocker lockit(&m_statusMutex); m_jobsStatus.clear(); m_jobsStatusCmds.clear(); - // TODO: udate when we update to remote algorithms v2 - // As SCARF doesn't provide all the info at the moment, and as we're - // using the SCARFTomoReconstruction algorithm, the - // IRemoteJobManager::RemoteJobInfo struct is for now used only partially - // (cmds out). So this loop feels both incomplete and an unecessary second - // step that could be avoided. + // TODO: udate when we update to remote algorithms v2 and more + // info might become available from SCARF. + // As SCARF doesn't provide all the info at the moment, the + // IRemoteJobManager::RemoteJobInfo struct is for now used only + // partially (cmds out). So this loop feels both incomplete and an + // unecessary second step that could be avoided. for (size_t i = 0; i < ids.size(); ++i) { IRemoteJobManager::RemoteJobInfo ji; ji.id = ids[i]; @@ -942,18 +951,9 @@ void TomoReconstruction::doQueryJobStatus(std::vector<std::string> &ids, std::vector<std::string> &names, std::vector<std::string> &status, std::vector<std::string> &cmds) { - // TODO: once the remote algorithms are rearranged into the - // 'RemoteJobManager' design, this will use... - // auto alg = Algorithm::fromString("QueryAllRemoteJobs"); - // and - // auto alg = Algorithm::fromString("QueryRemoteJob"); - - // output properties to get: RemoteJobsID, RemoteJobsNames, - // RemoteJobsStatus, RemoteJobsCommands - auto alg = Algorithm::fromString("SCARFTomoReconstruction"); + auto alg = Algorithm::fromString("QueryAllRemoteJobs"); alg->initialize(); - alg->setPropertyValue("UserName", getUsername()); - alg->setPropertyValue("Action", "JobStatus"); + alg->setPropertyValue("ComputeResource", getComputeResource()); try { alg->execute(); } catch (std::runtime_error &e) { @@ -961,10 +961,10 @@ void TomoReconstruction::doQueryJobStatus(std::vector<std::string> &ids, "Error when trying to query the status of jobs in " + getComputeResource() + ": " + e.what()); } - ids = alg->getProperty("RemoteJobsID"); - names = alg->getProperty("RemoteJobsNames"); - status = alg->getProperty("RemoteJobsStatus"); - cmds = alg->getProperty("RemoteJobsCommands"); + ids = alg->getProperty("JobId"); + names = alg->getProperty("JobName"); + status = alg->getProperty("JobStatusString"); + cmds = alg->getProperty("CommandLine"); } /** @@ -1289,7 +1289,8 @@ void TomoReconstruction::drawImage(const MatrixWorkspace_sptr &ws) { size_t width; try { width = boost::lexical_cast<size_t>(ws->run().getLogData("Axis1")->value()); - // TODO: add a settings option for this (like max mem allocation for images)? + // TODO: add a settings option for this (like max mem allocation for + // images)? if (width >= MAXDIM) width = MAXDIM; } catch (std::exception &e) {