Newer
Older
// TMSSBridge.cc: Implementation of the TMSS Bridge, interface between MAC Scheduler and TMSS
//
// Copyright (C) 2020
// ASTRON (Netherlands Foundation for Research in Astronomy)
// P.O.Box 2, 7990 AA Dwingeloo, The Netherlands, softwaresupport@astron.nl
//
// This program 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 2 of the License, or
// (at your option) any later version.
//
// This program 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, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// $Id$
//#include <Common/LofarLogger.h>
#include <lofar_config.h>
#include <Common/LofarLogger.h>
#include "TMSSBridge.h"
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/algorithm/string.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
#include <curl/curl.h>
#include <jsoncpp/json/json.h>
using namespace std;
namespace LOFAR {
namespace MainCU {
//
// TMSSBridge Constructor
//

Jorrit Schaap
committed
TMSSBridge::TMSSBridge(const std::string &hostname, int port, const std::string &username, const std::string &password):
itsUser(username),
itsPassword(password),
itsHost(hostname),
itsPort(port)
{
}
//
// TMSSBridge Destructor
//
TMSSBridge::~TMSSBridge()
{
}
Json::Value TMSSBridge::getSubTask(int subtask_id)
{
string queryStr = "/api/subtask/" + to_string(subtask_id) + "/";
Json::Value result;
if(httpGETAsJson(queryStr, result))
return result;
return Json::Value("");
//
// get all subTaskIDS that should run within three minutes (ordered in time if multiple are found)
// for given cluster
//
Json::Value TMSSBridge::getSubTasksStartingInThreeMinutes()
time_t now = time(0);
ptime lower_limit = from_time_t(now);
ptime upper_limit = from_time_t(now+3*60);
//TODO: make exact query as in SAS/OTDB/sql/getTreeGroup_func.sql with OR'd states and exact timewindow
string queryStr = "/api/subtask/?state__value=scheduled&start_time__gt=" + to_iso_extended_string(lower_limit) + "&start_time__lt=" + to_iso_extended_string(upper_limit) + "&ordering=start_time";
Json::Value result;
if(httpGETAsJson(queryStr, result))
return result["results"];
return Json::Value("");
Json::Value TMSSBridge::getActiveSubTasks()
{
ptime now = from_time_t(time(0));
//TODO: make exact query as in SAS/OTDB/sql/getTreeGroup_func.sql with OR'd states and exact timewindow
string queryStr = "/api/subtask/?state__value=started&start_time__lt=" + to_iso_extended_string(now) + "&stop_time__gt=" + to_iso_extended_string(now) + "&ordering=start_time";
Json::Value result;
if(httpGETAsJson(queryStr, result))
return result["results"];
return Json::Value("");
}
Json::Value TMSSBridge::getFinishingSubTasks()
{
ptime justnow = from_time_t(time(0)-3*60);
//TODO: make exact query as in SAS/OTDB/sql/getTreeGroup_func.sql with OR'd states and exact timewindow
string queryStr = "/api/subtask/?state__value=finishing&stop_time__gt=" + to_iso_extended_string(justnow) + "&ordering=start_time";
Json::Value result;
if(httpGETAsJson(queryStr, result))
return result["results"];
return Json::Value("");
std::string TMSSBridge::getParsetAsText(int subtask_id)
{
string queryStr = "/api/subtask/" + to_string(subtask_id) + "/parset";
string result;
if(httpQuery(queryStr, result, "GET"))
return result;
return "";
Jörn Künsemöller
committed
}
bool TMSSBridge::setSubtaskState(int subtask_id, const string& state)
Jörn Künsemöller
committed
{
string queryStr = "/api/subtask/" + to_string(subtask_id) + "/";
string result;
if(httpQuery(queryStr, result, "PATCH", "{ \"state\": \"/api/subtask_state/" + state +"/\" }")) {
LOG_INFO_STR("Updated subtask id=" << subtask_id << " to status=" << state);
return true;
}
LOG_ERROR_STR("Could not update subtask id=" << subtask_id << " to status=" << state << " response=" << result);
return false;
}
std::size_t callback(const char* in,
std::size_t size,
std::size_t num,
std::string* out)
{
const std::size_t totalBytes(size * num);
out->append(in, totalBytes);
return totalBytes;
}
//
Jörn Künsemöller
committed
// Performs an HTTP query and return the response body
// Need to check response status code of http (200)
// Inspired by https://gist.github.com/connormanning/41efa6075515019e499c
// Example:
Jörn Künsemöller
committed
// httpQuery("/api/subtask/?start_time__lt=2020-03-04T12:03:00")
// results in a json string output
//
bool TMSSBridge::httpQuery(const string& target, string &result, const string& query_method, const string& data)
{
const std::string url(std::string("http://") + itsHost + std::string(":") + std::to_string(itsPort) + target);
CURL* curl = curl_easy_init();

Jorrit Schaap
committed
// setup authentication
curl_easy_setopt(curl, CURLOPT_USERNAME, itsUser.c_str());
curl_easy_setopt(curl, CURLOPT_PASSWORD, itsPassword.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
// Set remote URL.
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
Jörn Künsemöller
committed
// Set HTTP method
if (query_method == "GET")
{
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
}
else if (query_method == "POST")
{
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
}
else if (query_method == "PUT" || query_method == "PATCH" )
Jörn Künsemöller
committed
{
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, query_method.c_str());
curl_easy_setopt(curl, CURLOPT_USERAGENT, "TMSSBridge using libcurl");
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Expect:");
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
Jörn Künsemöller
committed
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1L);
}
Jörn Künsemöller
committed
// Don't bother trying IPv6, which would increase DNS resolution time.
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
// Don't wait forever, time out after 10 seconds.
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
// Follow HTTP redirects if necessary.
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
Jörn Künsemöller
committed
// Response information.
long httpCode(0);
std::unique_ptr<std::string> httpData(new std::string());
// Hook up data handling function.
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);
// Hook up data container (will be passed as the last parameter to the
// callback handling function). Can be any pointer type, since it will
// internally be passed as a void pointer.
curl_easy_setopt(curl, CURLOPT_WRITEDATA, httpData.get());
// Run our HTTP GET command, capture the HTTP response code, and clean up.
curl_easy_perform(curl);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
curl_easy_cleanup(curl);
LOG_INFO_STR("[" << query_method << "] code=" << httpCode << " " << url);
if (httpCode == 200)
result = string(*httpData.get());
return true;
LOG_ERROR_STR("Couldn't " + query_method + " from " + url + " exiting with http code " + to_string(httpCode));
result = "";
return false;
}
bool TMSSBridge::httpGETAsJson(const string& target, Json::Value &result)
{
result = Json::Value("");
std::string text_result;
if(this->httpQuery(target, text_result)) {
Json::Reader jsonReader;
if (jsonReader.parse(text_result, result))
{
if(result["count"] != 0) {
LOG_DEBUG_STR(string("JSON data for ") << target << std::endl << result.toStyledString());
}
return true;
}
LOG_ERROR_STR("Could not parse HTTP data as JSON. HTTP data was:\n" + text_result);
return false;
}
};//MainCU
};//LOFAR