mirror of https://github.com/Icinga/icinga2.git
parent
a51bc4010a
commit
5d46f661ea
|
@ -27,7 +27,7 @@ endif()
|
|||
|
||||
add_executable(icinga-studio MACOSX_BUNDLE WIN32 icinga-studio.cpp
|
||||
forms.cpp aboutform.cpp connectform.cpp mainform.cpp
|
||||
icinga.icns apiclient.cpp ${WindowsSources})
|
||||
icinga.icns ${WindowsSources})
|
||||
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries(icinga-studio ${Boost_LIBRARIES} ${wxWidgets_LIBRARIES} base remote)
|
||||
|
|
|
@ -142,7 +142,6 @@ void MainForm::ObjectsCompletionHandler(boost::exception_ptr eptr, const std::ve
|
|||
m_PropertyGrid->Clear();
|
||||
|
||||
if (eptr) {
|
||||
|
||||
try {
|
||||
boost::rethrow_exception(eptr);
|
||||
} catch (const std::exception& ex) {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef MAINFORM_H
|
||||
#define MAINFORM_H
|
||||
|
||||
#include "icinga-studio/apiclient.hpp"
|
||||
#include "remote/apiclient.hpp"
|
||||
#include "remote/url.hpp"
|
||||
#include "base/exception.hpp"
|
||||
#include "icinga-studio/forms.h"
|
||||
|
|
|
@ -63,7 +63,7 @@ static Object::Ptr SerializeObject(const Object::Ptr& input, int attributeTypes)
|
|||
for (int i = 0; i < type->GetFieldCount(); i++) {
|
||||
Field field = type->GetFieldInfo(i);
|
||||
|
||||
if ((field.Attributes & attributeTypes) == 0)
|
||||
if (attributeTypes != 0 && (field.Attributes & attributeTypes) == 0)
|
||||
continue;
|
||||
|
||||
fields->Set(field.Name, Serialize(input->GetField(i), attributeTypes));
|
||||
|
|
|
@ -19,7 +19,11 @@
|
|||
|
||||
#include "cli/consolecommand.hpp"
|
||||
#include "config/configcompiler.hpp"
|
||||
#include "remote/apiclient.hpp"
|
||||
#include "remote/consolehandler.hpp"
|
||||
#include "remote/url.hpp"
|
||||
#include "base/configwriter.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/console.hpp"
|
||||
#include "base/application.hpp"
|
||||
|
@ -37,6 +41,8 @@ using namespace icinga;
|
|||
namespace po = boost::program_options;
|
||||
|
||||
static ScriptFrame *l_ScriptFrame;
|
||||
static ApiClient::Ptr l_ApiClient;
|
||||
static String l_Session;
|
||||
|
||||
REGISTER_CLICOMMAND("console", ConsoleCommand);
|
||||
|
||||
|
@ -60,89 +66,41 @@ void ConsoleCommand::InitParameters(boost::program_options::options_description&
|
|||
{
|
||||
visibleDesc.add_options()
|
||||
("connect,c", po::value<std::string>(), "connect to an Icinga 2 instance")
|
||||
("eval,e", po::value<std::string>(), "evaluate expression and terminate")
|
||||
("sandbox", "enable sandbox mode")
|
||||
;
|
||||
}
|
||||
|
||||
#ifdef HAVE_EDITLINE
|
||||
static void AddSuggestion(std::vector<String>& matches, const String& word, const String& suggestion)
|
||||
{
|
||||
if (suggestion.Find(word) != 0)
|
||||
return;
|
||||
|
||||
matches.push_back(suggestion);
|
||||
}
|
||||
|
||||
static char *ConsoleCompleteHelper(const char *word, int state)
|
||||
char *ConsoleCommand::ConsoleCompleteHelper(const char *word, int state)
|
||||
{
|
||||
static std::vector<String> matches;
|
||||
String aword = word;
|
||||
|
||||
if (state == 0) {
|
||||
matches.clear();
|
||||
if (!l_ApiClient)
|
||||
matches = ConsoleHandler::GetAutocompletionSuggestions(word, *l_ScriptFrame);
|
||||
else {
|
||||
boost::mutex mutex;
|
||||
boost::condition_variable cv;
|
||||
bool ready = false;
|
||||
Array::Ptr suggestions;
|
||||
|
||||
BOOST_FOREACH(const String& keyword, ConfigWriter::GetKeywords()) {
|
||||
AddSuggestion(matches, word, keyword);
|
||||
}
|
||||
l_ApiClient->AutocompleteScript(l_Session, word, l_ScriptFrame->Sandboxed,
|
||||
boost::bind(&ConsoleCommand::AutocompleteScriptCompletionHandler,
|
||||
boost::ref(mutex), boost::ref(cv), boost::ref(ready),
|
||||
_1, _2,
|
||||
boost::ref(suggestions)));
|
||||
|
||||
{
|
||||
ObjectLock olock(l_ScriptFrame->Locals);
|
||||
BOOST_FOREACH(const Dictionary::Pair& kv, l_ScriptFrame->Locals) {
|
||||
AddSuggestion(matches, word, kv.first);
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
while (!ready)
|
||||
cv.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ObjectLock olock(ScriptGlobal::GetGlobals());
|
||||
BOOST_FOREACH(const Dictionary::Pair& kv, ScriptGlobal::GetGlobals()) {
|
||||
AddSuggestion(matches, word, kv.first);
|
||||
}
|
||||
}
|
||||
matches.clear();
|
||||
|
||||
String::SizeType cperiod = aword.RFind(".");
|
||||
|
||||
if (cperiod != -1) {
|
||||
String pword = aword.SubStr(0, cperiod);
|
||||
|
||||
Value value;
|
||||
|
||||
try {
|
||||
Expression *expr = ConfigCompiler::CompileText("temp", pword);
|
||||
|
||||
if (expr)
|
||||
value = expr->Evaluate(*l_ScriptFrame);
|
||||
|
||||
if (value.IsObjectType<Dictionary>()) {
|
||||
Dictionary::Ptr dict = value;
|
||||
|
||||
ObjectLock olock(dict);
|
||||
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
|
||||
AddSuggestion(matches, word, pword + "." + kv.first);
|
||||
}
|
||||
}
|
||||
|
||||
Type::Ptr type = value.GetReflectionType();
|
||||
|
||||
for (int i = 0; i < type->GetFieldCount(); i++) {
|
||||
Field field = type->GetFieldInfo(i);
|
||||
|
||||
AddSuggestion(matches, word, pword + "." + field.Name);
|
||||
}
|
||||
|
||||
while (type) {
|
||||
Object::Ptr prototype = type->GetPrototype();
|
||||
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(prototype);
|
||||
|
||||
if (dict) {
|
||||
ObjectLock olock(dict);
|
||||
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
|
||||
AddSuggestion(matches, word, pword + "." + kv.first);
|
||||
}
|
||||
}
|
||||
|
||||
type = type->GetBaseType();
|
||||
}
|
||||
} catch (...) { /* Ignore the exception */ }
|
||||
ObjectLock olock(suggestions);
|
||||
std::copy(suggestions->Begin(), suggestions->End(), std::back_inserter(matches));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,30 +122,53 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::stri
|
|||
int next_line = 1;
|
||||
|
||||
#ifdef HAVE_EDITLINE
|
||||
rl_completion_entry_function = ConsoleCompleteHelper;
|
||||
rl_completion_entry_function = ConsoleCommand::ConsoleCompleteHelper;
|
||||
rl_completion_append_character = '\0';
|
||||
#endif /* HAVE_EDITLINE */
|
||||
|
||||
String addr, session;
|
||||
String addr;
|
||||
ScriptFrame scriptFrame;
|
||||
|
||||
l_ScriptFrame = &scriptFrame;
|
||||
l_Session = Utility::NewUniqueID();
|
||||
|
||||
if (vm.count("sandbox"))
|
||||
scriptFrame.Sandboxed = true;
|
||||
|
||||
if (!vm.count("eval"))
|
||||
std::cout << "Icinga 2 (version: " << Application::GetAppVersion() << ")\n";
|
||||
|
||||
const char *addrEnv = getenv("ICINGA2_API_URL");
|
||||
if (addrEnv)
|
||||
addr = addrEnv;
|
||||
|
||||
if (vm.count("connect")) {
|
||||
addr = vm["connect"].as<std::string>();
|
||||
session = Utility::NewUniqueID();
|
||||
}
|
||||
|
||||
if (vm.count("sandbox")) {
|
||||
if (vm.count("connect")) {
|
||||
Log(LogCritical, "ConsoleCommand", "Sandbox mode cannot be used together with --connect.");
|
||||
if (!addr.IsEmpty()) {
|
||||
Url::Ptr url;
|
||||
|
||||
try {
|
||||
url = new Url(addr);
|
||||
} catch (const std::exception& ex) {
|
||||
Log(LogCritical, "ConsoleCommand", ex.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
scriptFrame.Sandboxed = true;
|
||||
}
|
||||
const char *usernameEnv = getenv("ICINGA2_API_USERNAME");
|
||||
const char *passwordEnv = getenv("ICINGA2_API_PASSWORD");
|
||||
|
||||
std::cout << "Icinga (version: " << Application::GetAppVersion() << ")\n";
|
||||
if (usernameEnv)
|
||||
url->SetUsername(usernameEnv);
|
||||
if (passwordEnv)
|
||||
url->SetPassword(passwordEnv);
|
||||
|
||||
if (url->GetPort().IsEmpty())
|
||||
url->SetPort("5665");
|
||||
|
||||
l_ApiClient = new ApiClient(url->GetHost(), url->GetPort(), url->GetUsername(), url->GetPassword());
|
||||
}
|
||||
|
||||
while (std::cin.good()) {
|
||||
String fileName = "<" + Convert::ToString(next_line) + ">";
|
||||
|
@ -197,148 +178,191 @@ int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::stri
|
|||
std::string command;
|
||||
|
||||
incomplete:
|
||||
#ifdef HAVE_EDITLINE
|
||||
std::ostringstream promptbuf;
|
||||
std::ostream& os = promptbuf;
|
||||
#else /* HAVE_EDITLINE */
|
||||
std::ostream& os = std::cout;
|
||||
#endif /* HAVE_EDITLINE */
|
||||
|
||||
os << fileName;
|
||||
|
||||
if (!continuation)
|
||||
os << " => ";
|
||||
else
|
||||
os << " .. ";
|
||||
|
||||
#ifdef HAVE_EDITLINE
|
||||
String prompt = promptbuf.str();
|
||||
|
||||
char *cline;
|
||||
cline = readline(prompt.CStr());
|
||||
|
||||
if (!cline)
|
||||
break;
|
||||
|
||||
add_history(cline);
|
||||
|
||||
std::string line = cline;
|
||||
|
||||
free(cline);
|
||||
#else /* HAVE_EDITLINE */
|
||||
std::string line;
|
||||
std::getline(std::cin, line);
|
||||
|
||||
if (!vm.count("eval")) {
|
||||
#ifdef HAVE_EDITLINE
|
||||
std::ostringstream promptbuf;
|
||||
std::ostream& os = promptbuf;
|
||||
#else /* HAVE_EDITLINE */
|
||||
std::ostream& os = std::cout;
|
||||
#endif /* HAVE_EDITLINE */
|
||||
|
||||
os << fileName;
|
||||
|
||||
if (!continuation)
|
||||
os << " => ";
|
||||
else
|
||||
os << " .. ";
|
||||
|
||||
#ifdef HAVE_EDITLINE
|
||||
String prompt = promptbuf.str();
|
||||
|
||||
char *cline;
|
||||
cline = readline(prompt.CStr());
|
||||
|
||||
if (!cline)
|
||||
break;
|
||||
|
||||
add_history(cline);
|
||||
|
||||
line = cline;
|
||||
|
||||
free(cline);
|
||||
#else /* HAVE_EDITLINE */
|
||||
std::getline(std::cin, line);
|
||||
#endif /* HAVE_EDITLINE */
|
||||
} else
|
||||
line = vm["eval"].as<std::string>();
|
||||
|
||||
if (!command.empty())
|
||||
command += "\n";
|
||||
|
||||
command += line;
|
||||
|
||||
if (addr.IsEmpty()) {
|
||||
Expression *expr = NULL;
|
||||
Expression *expr = NULL;
|
||||
|
||||
try {
|
||||
lines[fileName] = command;
|
||||
try {
|
||||
lines[fileName] = command;
|
||||
|
||||
Value result;
|
||||
|
||||
if (!l_ApiClient) {
|
||||
expr = ConfigCompiler::CompileText(fileName, command);
|
||||
|
||||
if (expr) {
|
||||
Value result = expr->Evaluate(scriptFrame);
|
||||
std::cout << ConsoleColorTag(Console_ForegroundCyan);
|
||||
if (!result.IsObject() || result.IsObjectType<Array>() || result.IsObjectType<Dictionary>())
|
||||
std::cout << JsonEncode(result);
|
||||
else
|
||||
std::cout << result;
|
||||
std::cout << ConsoleColorTag(Console_Normal) << "\n";
|
||||
}
|
||||
} catch (const ScriptError& ex) {
|
||||
if (ex.IsIncompleteExpression()) {
|
||||
continuation = true;
|
||||
goto incomplete;
|
||||
}
|
||||
|
||||
DebugInfo di = ex.GetDebugInfo();
|
||||
|
||||
if (lines.find(di.Path) != lines.end()) {
|
||||
String text = lines[di.Path];
|
||||
|
||||
std::vector<String> ulines;
|
||||
boost::algorithm::split(ulines, text, boost::is_any_of("\n"));
|
||||
|
||||
for (int i = 1; i <= ulines.size(); i++) {
|
||||
int start, len;
|
||||
|
||||
if (i == di.FirstLine)
|
||||
start = di.FirstColumn;
|
||||
else
|
||||
start = 0;
|
||||
|
||||
if (i == di.LastLine)
|
||||
len = di.LastColumn - di.FirstColumn + 1;
|
||||
else
|
||||
len = ulines[i - 1].GetLength();
|
||||
|
||||
int offset;
|
||||
|
||||
if (di.Path != fileName) {
|
||||
std::cout << di.Path << ": " << ulines[i - 1] << "\n";
|
||||
offset = 2;
|
||||
} else
|
||||
offset = 4;
|
||||
|
||||
if (i >= di.FirstLine && i <= di.LastLine) {
|
||||
std::cout << String(di.Path.GetLength() + offset, ' ');
|
||||
std::cout << String(start, ' ') << String(len, '^') << "\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ShowCodeFragment(std::cout, di);
|
||||
}
|
||||
|
||||
std::cout << ex.what() << "\n";
|
||||
} catch (const std::exception& ex) {
|
||||
std::cout << "Error: " << DiagnosticInformation(ex) << "\n";
|
||||
}
|
||||
|
||||
delete expr;
|
||||
} else {
|
||||
Socket::Ptr socket;
|
||||
|
||||
#ifndef _WIN32
|
||||
if (addr.FindFirstOf("/") != String::NPos) {
|
||||
UnixSocket::Ptr usocket = new UnixSocket();
|
||||
usocket->Connect(addr);
|
||||
socket = usocket;
|
||||
result = Serialize(expr->Evaluate(scriptFrame), 0);
|
||||
} else {
|
||||
#endif /* _WIN32 */
|
||||
Log(LogCritical, "ConsoleCommand", "Sorry, TCP sockets aren't supported yet.");
|
||||
return 1;
|
||||
#ifndef _WIN32
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
boost::mutex mutex;
|
||||
boost::condition_variable cv;
|
||||
bool ready = false;
|
||||
boost::exception_ptr eptr;
|
||||
|
||||
String query = "SCRIPT " + session + "\n" + line + "\n\n";
|
||||
l_ApiClient->ExecuteScript(l_Session, command, scriptFrame.Sandboxed,
|
||||
boost::bind(&ConsoleCommand::ExecuteScriptCompletionHandler,
|
||||
boost::ref(mutex), boost::ref(cv), boost::ref(ready),
|
||||
_1, _2,
|
||||
boost::ref(result), boost::ref(eptr)));
|
||||
|
||||
NetworkStream::Ptr ns = new NetworkStream(socket);
|
||||
ns->Write(query.CStr(), query.GetLength());
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
while (!ready)
|
||||
cv.wait(lock);
|
||||
}
|
||||
|
||||
String result;
|
||||
char buf[1024];
|
||||
|
||||
while (!ns->IsEof()) {
|
||||
size_t rc = ns->Read(buf, sizeof(buf), true);
|
||||
result += String(buf, buf + rc);
|
||||
if (eptr)
|
||||
boost::rethrow_exception(eptr);
|
||||
}
|
||||
|
||||
if (result.GetLength() < 16) {
|
||||
Log(LogCritical, "ConsoleCommand", "Received invalid response from Livestatus.");
|
||||
continue;
|
||||
if (!vm.count("eval")) {
|
||||
std::cout << ConsoleColorTag(Console_ForegroundCyan);
|
||||
ConfigWriter::EmitValue(std::cout, 1, result);
|
||||
std::cout << ConsoleColorTag(Console_Normal) << "\n";
|
||||
} else {
|
||||
std::cout << JsonEncode(result) << "\n";
|
||||
break;
|
||||
}
|
||||
} catch (const ScriptError& ex) {
|
||||
if (ex.IsIncompleteExpression()) {
|
||||
continuation = true;
|
||||
goto incomplete;
|
||||
}
|
||||
|
||||
std::cout << result.SubStr(16) << "\n";
|
||||
DebugInfo di = ex.GetDebugInfo();
|
||||
|
||||
if (lines.find(di.Path) != lines.end()) {
|
||||
String text = lines[di.Path];
|
||||
|
||||
std::vector<String> ulines;
|
||||
boost::algorithm::split(ulines, text, boost::is_any_of("\n"));
|
||||
|
||||
for (int i = 1; i <= ulines.size(); i++) {
|
||||
int start, len;
|
||||
|
||||
if (i == di.FirstLine)
|
||||
start = di.FirstColumn;
|
||||
else
|
||||
start = 0;
|
||||
|
||||
if (i == di.LastLine)
|
||||
len = di.LastColumn - di.FirstColumn + 1;
|
||||
else
|
||||
len = ulines[i - 1].GetLength();
|
||||
|
||||
int offset;
|
||||
|
||||
if (di.Path != fileName) {
|
||||
std::cout << di.Path << ": " << ulines[i - 1] << "\n";
|
||||
offset = 2;
|
||||
} else
|
||||
offset = 4;
|
||||
|
||||
if (i >= di.FirstLine && i <= di.LastLine) {
|
||||
std::cout << String(di.Path.GetLength() + offset, ' ');
|
||||
std::cout << String(start, ' ') << String(len, '^') << "\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ShowCodeFragment(std::cout, di);
|
||||
}
|
||||
|
||||
std::cout << ex.what() << "\n";
|
||||
|
||||
if (vm.count("eval"))
|
||||
return EXIT_FAILURE;
|
||||
} catch (const std::exception& ex) {
|
||||
std::cout << "Error: " << DiagnosticInformation(ex) << "\n";
|
||||
|
||||
if (vm.count("eval"))
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
delete expr;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void ConsoleCommand::ExecuteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv,
|
||||
bool& ready, boost::exception_ptr eptr, const Value& result, Value& resultOut, boost::exception_ptr& eptrOut)
|
||||
{
|
||||
if (eptr) {
|
||||
try {
|
||||
boost::rethrow_exception(eptr);
|
||||
} catch (const ScriptError& ex) {
|
||||
eptrOut = boost::current_exception();
|
||||
} catch (const std::exception& ex) {
|
||||
Log(LogCritical, "ConsoleCommand")
|
||||
<< "HTTP query failed: " << ex.what();
|
||||
Application::Exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
resultOut = result;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
ready = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleCommand::AutocompleteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv,
|
||||
bool& ready, boost::exception_ptr eptr, const Array::Ptr& result, Array::Ptr& resultOut)
|
||||
{
|
||||
if (eptr) {
|
||||
try {
|
||||
boost::rethrow_exception(eptr);
|
||||
} catch (const std::exception& ex) {
|
||||
Log(LogCritical, "ConsoleCommand")
|
||||
<< "HTTP query failed: " << ex.what();
|
||||
Application::Exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
resultOut = result;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
ready = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define CONSOLECOMMAND_H
|
||||
|
||||
#include "cli/clicommand.hpp"
|
||||
#include "base/exception.hpp"
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
@ -41,6 +42,22 @@ public:
|
|||
virtual void InitParameters(boost::program_options::options_description& visibleDesc,
|
||||
boost::program_options::options_description& hiddenDesc) const override;
|
||||
virtual int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
|
||||
|
||||
private:
|
||||
mutable boost::mutex m_Mutex;
|
||||
mutable boost::condition_variable m_CV;
|
||||
mutable bool m_CommandReady;
|
||||
|
||||
static void ExecuteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv,
|
||||
bool& ready, boost::exception_ptr eptr, const Value& result, Value& resultOut,
|
||||
boost::exception_ptr& eptrOut);
|
||||
static void AutocompleteScriptCompletionHandler(boost::mutex& mutex, boost::condition_variable& cv,
|
||||
bool& ready, boost::exception_ptr eptr, const Array::Ptr& result, Array::Ptr& resultOut);
|
||||
|
||||
#ifdef HAVE_EDITLINE
|
||||
static char *ConsoleCompleteHelper(const char *word, int state);
|
||||
#endif /* HAVE_EDITLINE */
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include "livestatus/orfilter.hpp"
|
||||
#include "livestatus/andfilter.hpp"
|
||||
#include "icinga/externalcommandprocessor.hpp"
|
||||
#include "config/configcompiler.hpp"
|
||||
#include "base/debug.hpp"
|
||||
#include "base/convert.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
|
@ -52,36 +51,6 @@ using namespace icinga;
|
|||
|
||||
static int l_ExternalCommands = 0;
|
||||
static boost::mutex l_QueryMutex;
|
||||
static std::map<String, LivestatusScriptFrame> l_LivestatusScriptFrames;
|
||||
static Timer::Ptr l_FrameCleanupTimer;
|
||||
static boost::mutex l_LivestatusScriptMutex;
|
||||
|
||||
static void ScriptFrameCleanupHandler(void)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(l_LivestatusScriptMutex);
|
||||
|
||||
std::vector<String> cleanup_keys;
|
||||
|
||||
typedef std::pair<String, LivestatusScriptFrame> KVPair;
|
||||
|
||||
BOOST_FOREACH(const KVPair& kv, l_LivestatusScriptFrames) {
|
||||
if (kv.second.Seen < Utility::GetTime() - 1800)
|
||||
cleanup_keys.push_back(kv.first);
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const String& key, cleanup_keys)
|
||||
l_LivestatusScriptFrames.erase(key);
|
||||
}
|
||||
|
||||
static void InitScriptFrameCleanup(void)
|
||||
{
|
||||
l_FrameCleanupTimer = new Timer();
|
||||
l_FrameCleanupTimer->OnTimerExpired.connect(boost::bind(ScriptFrameCleanupHandler));
|
||||
l_FrameCleanupTimer->SetInterval(30);
|
||||
l_FrameCleanupTimer->Start();
|
||||
}
|
||||
|
||||
INITIALIZE_ONCE(InitScriptFrameCleanup);
|
||||
|
||||
LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String& compat_log_path)
|
||||
: m_KeepAlive(false), m_OutputFormat("csv"), m_ColumnHeaders(true), m_Limit(-1), m_ErrorCode(0),
|
||||
|
@ -123,16 +92,6 @@ LivestatusQuery::LivestatusQuery(const std::vector<String>& lines, const String&
|
|||
if (m_Verb == "COMMAND") {
|
||||
m_KeepAlive = true;
|
||||
m_Command = target;
|
||||
} else if (m_Verb == "SCRIPT") {
|
||||
m_Session = target;
|
||||
|
||||
for (unsigned int i = 1; i < lines.size(); i++) {
|
||||
if (m_Command != "")
|
||||
m_Command += "\n";
|
||||
m_Command += lines[i];
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (m_Verb == "GET") {
|
||||
m_Table = target;
|
||||
} else {
|
||||
|
@ -625,54 +584,6 @@ void LivestatusQuery::ExecuteCommandHelper(const Stream::Ptr& stream)
|
|||
SendResponse(stream, LivestatusErrorOK, "");
|
||||
}
|
||||
|
||||
void LivestatusQuery::ExecuteScriptHelper(const Stream::Ptr& stream)
|
||||
{
|
||||
Log(LogInformation, "LivestatusQuery")
|
||||
<< "Executing expression: " << m_Command;
|
||||
|
||||
m_ResponseHeader = "fixed16";
|
||||
|
||||
LivestatusScriptFrame& lsf = l_LivestatusScriptFrames[m_Session];
|
||||
lsf.Seen = Utility::GetTime();
|
||||
|
||||
if (!lsf.Locals)
|
||||
lsf.Locals = new Dictionary();
|
||||
|
||||
String fileName = "<" + Convert::ToString(lsf.NextLine) + ">";
|
||||
lsf.NextLine++;
|
||||
|
||||
lsf.Lines[fileName] = m_Command;
|
||||
|
||||
Expression *expr = NULL;
|
||||
Value result;
|
||||
try {
|
||||
expr = ConfigCompiler::CompileText(fileName, m_Command);
|
||||
ScriptFrame frame;
|
||||
frame.Locals = lsf.Locals;
|
||||
frame.Self = lsf.Locals;
|
||||
result = expr->Evaluate(frame);
|
||||
} catch (const ScriptError& ex) {
|
||||
delete expr;
|
||||
|
||||
DebugInfo di = ex.GetDebugInfo();
|
||||
|
||||
std::ostringstream msgbuf;
|
||||
|
||||
msgbuf << di.Path << ": " << lsf.Lines[di.Path] << "\n"
|
||||
<< String(di.Path.GetLength() + 2, ' ')
|
||||
<< String(di.FirstColumn, ' ') << String(di.LastColumn - di.FirstColumn + 1, '^') << "\n"
|
||||
<< ex.what() << "\n";
|
||||
|
||||
SendResponse(stream, LivestatusErrorQuery, msgbuf.str());
|
||||
return;
|
||||
} catch (...) {
|
||||
delete expr;
|
||||
throw;
|
||||
}
|
||||
delete expr;
|
||||
SendResponse(stream, LivestatusErrorOK, JsonEncode(Serialize(result, FAEphemeral | FAState | FAConfig), true));
|
||||
}
|
||||
|
||||
void LivestatusQuery::ExecuteErrorHelper(const Stream::Ptr& stream)
|
||||
{
|
||||
Log(LogDebug, "LivestatusQuery")
|
||||
|
@ -720,8 +631,6 @@ bool LivestatusQuery::Execute(const Stream::Ptr& stream)
|
|||
ExecuteGetHelper(stream);
|
||||
else if (m_Verb == "COMMAND")
|
||||
ExecuteCommandHelper(stream);
|
||||
else if (m_Verb == "SCRIPT")
|
||||
ExecuteScriptHelper(stream);
|
||||
else if (m_Verb == "ERROR")
|
||||
ExecuteErrorHelper(stream);
|
||||
else
|
||||
|
|
|
@ -40,18 +40,6 @@ enum LivestatusError
|
|||
LivestatusErrorQuery = 452
|
||||
};
|
||||
|
||||
struct LivestatusScriptFrame
|
||||
{
|
||||
double Seen;
|
||||
int NextLine;
|
||||
std::map<String, String> Lines;
|
||||
Dictionary::Ptr Locals;
|
||||
|
||||
LivestatusScriptFrame(void)
|
||||
: Seen(0), NextLine(1)
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup livestatus
|
||||
*/
|
||||
|
@ -106,7 +94,6 @@ private:
|
|||
|
||||
void ExecuteGetHelper(const Stream::Ptr& stream);
|
||||
void ExecuteCommandHelper(const Stream::Ptr& stream);
|
||||
void ExecuteScriptHelper(const Stream::Ptr& stream);
|
||||
void ExecuteErrorHelper(const Stream::Ptr& stream);
|
||||
|
||||
void SendResponse(const Stream::Ptr& stream, int code, const String& data);
|
||||
|
|
|
@ -21,10 +21,10 @@ mkclass_target(endpoint.ti endpoint.tcpp endpoint.thpp)
|
|||
mkclass_target(zone.ti zone.tcpp zone.thpp)
|
||||
|
||||
set(remote_SOURCES
|
||||
actionshandler.cpp apiaction.cpp
|
||||
actionshandler.cpp apiaction.cpp apiclient.cpp
|
||||
apifunction.cpp apilistener.cpp apilistener.thpp apilistener-configsync.cpp
|
||||
apilistener-filesync.cpp apiuser.cpp apiuser.thpp authority.cpp base64.cpp
|
||||
configfileshandler.cpp configpackageshandler.cpp configpackageutility.cpp configobjectutility.cpp
|
||||
consolehandler.cpp configfileshandler.cpp configpackageshandler.cpp configpackageutility.cpp configobjectutility.cpp
|
||||
configstageshandler.cpp createobjecthandler.cpp deleteobjecthandler.cpp
|
||||
endpoint.cpp endpoint.thpp eventshandler.cpp eventqueue.cpp filterutility.cpp
|
||||
httpchunkedencoding.cpp httpclientconnection.cpp httpserverconnection.cpp httphandler.cpp httprequest.cpp httpresponse.cpp
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
******************************************************************************/
|
||||
|
||||
#include "icinga-studio/apiclient.hpp"
|
||||
#include "remote/apiclient.hpp"
|
||||
#include "remote/base64.hpp"
|
||||
#include "base/json.hpp"
|
||||
#include "base/logger.hpp"
|
||||
|
@ -116,7 +116,6 @@ void ApiClient::GetObjects(const String& pluralType, const ObjectsCompletionCall
|
|||
path.push_back("objects");
|
||||
path.push_back(pluralType);
|
||||
url->SetPath(path);
|
||||
String qp;
|
||||
|
||||
std::map<String, std::vector<String> > params;
|
||||
|
||||
|
@ -206,3 +205,159 @@ void ApiClient::ObjectsHttpCompletionCallback(HttpRequest& request,
|
|||
callback(boost::current_exception(), std::vector<ApiObject::Ptr>());
|
||||
}
|
||||
}
|
||||
|
||||
void ApiClient::ExecuteScript(const String& session, const String& command, bool sandboxed,
|
||||
const ExecuteScriptCompletionCallback& callback) const
|
||||
{
|
||||
Url::Ptr url = new Url();
|
||||
url->SetScheme("https");
|
||||
url->SetHost(m_Connection->GetHost());
|
||||
url->SetPort(m_Connection->GetPort());
|
||||
|
||||
std::vector<String> path;
|
||||
path.push_back("v1");
|
||||
path.push_back("console");
|
||||
path.push_back("execute-script");
|
||||
url->SetPath(path);
|
||||
|
||||
std::map<String, std::vector<String> > params;
|
||||
params["session"].push_back(session);
|
||||
params["command"].push_back(command);
|
||||
params["sandboxed"].push_back(sandboxed ? "1" : "0");
|
||||
url->SetQuery(params);
|
||||
|
||||
try {
|
||||
boost::shared_ptr<HttpRequest> req = m_Connection->NewRequest();
|
||||
req->RequestMethod = "POST";
|
||||
req->RequestUrl = url;
|
||||
req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password));
|
||||
m_Connection->SubmitRequest(req, boost::bind(ExecuteScriptHttpCompletionCallback, _1, _2, callback));
|
||||
} catch (const std::exception& ex) {
|
||||
callback(boost::current_exception(), Empty);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiClient::ExecuteScriptHttpCompletionCallback(HttpRequest& request,
|
||||
HttpResponse& response, const ExecuteScriptCompletionCallback& callback)
|
||||
{
|
||||
Dictionary::Ptr result;
|
||||
|
||||
String body;
|
||||
char buffer[1024];
|
||||
size_t count;
|
||||
|
||||
while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0)
|
||||
body += String(buffer, buffer + count);
|
||||
|
||||
try {
|
||||
if (response.StatusCode < 200 || response.StatusCode > 299) {
|
||||
std::string message = "HTTP request failed; Code: " + Convert::ToString(response.StatusCode) + "; Body: " + body;
|
||||
|
||||
BOOST_THROW_EXCEPTION(ScriptError(message));
|
||||
}
|
||||
|
||||
result = JsonDecode(body);
|
||||
|
||||
Array::Ptr results = result->Get("results");
|
||||
Value result;
|
||||
bool incompleteExpression = false;
|
||||
String errorMessage = "Unexpected result from API.";
|
||||
|
||||
if (results && results->GetLength() > 0) {
|
||||
Dictionary::Ptr resultInfo = results->Get(0);
|
||||
errorMessage = resultInfo->Get("status");
|
||||
|
||||
if (resultInfo->Get("code") >= 200 && resultInfo->Get("code") <= 299) {
|
||||
result = resultInfo->Get("result");
|
||||
} else {
|
||||
DebugInfo di;
|
||||
Dictionary::Ptr debugInfo = resultInfo->Get("debug_info");
|
||||
if (debugInfo) {
|
||||
di.Path = debugInfo->Get("path");
|
||||
di.FirstLine = debugInfo->Get("first_line");
|
||||
di.FirstColumn = debugInfo->Get("first_column");
|
||||
di.LastLine = debugInfo->Get("last_line");
|
||||
di.LastColumn = debugInfo->Get("last_column");
|
||||
}
|
||||
bool incompleteExpression = resultInfo->Get("incomplete_expression");
|
||||
BOOST_THROW_EXCEPTION(ScriptError(errorMessage, di, incompleteExpression));
|
||||
}
|
||||
}
|
||||
|
||||
callback(boost::exception_ptr(), result);
|
||||
} catch (const std::exception& ex) {
|
||||
callback(boost::current_exception(), Empty);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiClient::AutocompleteScript(const String& session, const String& command, bool sandboxed,
|
||||
const AutocompleteScriptCompletionCallback& callback) const
|
||||
{
|
||||
Url::Ptr url = new Url();
|
||||
url->SetScheme("https");
|
||||
url->SetHost(m_Connection->GetHost());
|
||||
url->SetPort(m_Connection->GetPort());
|
||||
|
||||
std::vector<String> path;
|
||||
path.push_back("v1");
|
||||
path.push_back("console");
|
||||
path.push_back("auto-complete-script");
|
||||
url->SetPath(path);
|
||||
|
||||
std::map<String, std::vector<String> > params;
|
||||
params["session"].push_back(session);
|
||||
params["command"].push_back(command);
|
||||
params["sandboxed"].push_back(sandboxed ? "1" : "0");
|
||||
url->SetQuery(params);
|
||||
|
||||
try {
|
||||
boost::shared_ptr<HttpRequest> req = m_Connection->NewRequest();
|
||||
req->RequestMethod = "POST";
|
||||
req->RequestUrl = url;
|
||||
req->AddHeader("Authorization", "Basic " + Base64::Encode(m_User + ":" + m_Password));
|
||||
m_Connection->SubmitRequest(req, boost::bind(AutocompleteScriptHttpCompletionCallback, _1, _2, callback));
|
||||
} catch (const std::exception& ex) {
|
||||
callback(boost::current_exception(), Array::Ptr());
|
||||
}
|
||||
}
|
||||
|
||||
void ApiClient::AutocompleteScriptHttpCompletionCallback(HttpRequest& request,
|
||||
HttpResponse& response, const AutocompleteScriptCompletionCallback& callback)
|
||||
{
|
||||
Dictionary::Ptr result;
|
||||
|
||||
String body;
|
||||
char buffer[1024];
|
||||
size_t count;
|
||||
|
||||
while ((count = response.ReadBody(buffer, sizeof(buffer))) > 0)
|
||||
body += String(buffer, buffer + count);
|
||||
|
||||
try {
|
||||
if (response.StatusCode < 200 || response.StatusCode > 299) {
|
||||
std::string message = "HTTP request failed; Code: " + Convert::ToString(response.StatusCode) + "; Body: " + body;
|
||||
|
||||
BOOST_THROW_EXCEPTION(ScriptError(message));
|
||||
}
|
||||
|
||||
result = JsonDecode(body);
|
||||
|
||||
Array::Ptr results = result->Get("results");
|
||||
Array::Ptr suggestions;
|
||||
String errorMessage = "Unexpected result from API.";
|
||||
|
||||
if (results && results->GetLength() > 0) {
|
||||
Dictionary::Ptr resultInfo = results->Get(0);
|
||||
errorMessage = resultInfo->Get("status");
|
||||
|
||||
if (resultInfo->Get("code") >= 200 && resultInfo->Get("code") <= 299)
|
||||
suggestions = resultInfo->Get("suggestions");
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(ScriptError(errorMessage));
|
||||
}
|
||||
|
||||
callback(boost::exception_ptr(), suggestions);
|
||||
} catch (const std::exception& ex) {
|
||||
callback(boost::current_exception(), Array::Ptr());
|
||||
}
|
||||
}
|
|
@ -96,6 +96,14 @@ public:
|
|||
const std::vector<String>& names = std::vector<String>(),
|
||||
const std::vector<String>& attrs = std::vector<String>()) const;
|
||||
|
||||
typedef boost::function<void(boost::exception_ptr, const Value&)> ExecuteScriptCompletionCallback;
|
||||
void ExecuteScript(const String& session, const String& command, bool sandboxed,
|
||||
const ExecuteScriptCompletionCallback& callback) const;
|
||||
|
||||
typedef boost::function<void(boost::exception_ptr, const Array::Ptr&)> AutocompleteScriptCompletionCallback;
|
||||
void AutocompleteScript(const String& session, const String& command, bool sandboxed,
|
||||
const AutocompleteScriptCompletionCallback& callback) const;
|
||||
|
||||
private:
|
||||
HttpClientConnection::Ptr m_Connection;
|
||||
String m_User;
|
||||
|
@ -105,6 +113,10 @@ private:
|
|||
HttpResponse& response, const TypesCompletionCallback& callback);
|
||||
static void ObjectsHttpCompletionCallback(HttpRequest& request,
|
||||
HttpResponse& response, const ObjectsCompletionCallback& callback);
|
||||
static void ExecuteScriptHttpCompletionCallback(HttpRequest& request,
|
||||
HttpResponse& response, const ExecuteScriptCompletionCallback& callback);
|
||||
static void AutocompleteScriptHttpCompletionCallback(HttpRequest& request,
|
||||
HttpResponse& response, const AutocompleteScriptCompletionCallback& callback);
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
/******************************************************************************
|
||||
* Icinga 2 *
|
||||
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
|
||||
* *
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
******************************************************************************/
|
||||
|
||||
#include "remote/consolehandler.hpp"
|
||||
#include "remote/httputility.hpp"
|
||||
#include "remote/filterutility.hpp"
|
||||
#include "config/configcompiler.hpp"
|
||||
#include "base/configtype.hpp"
|
||||
#include "base/configwriter.hpp"
|
||||
#include "base/scriptglobal.hpp"
|
||||
#include "base/logger.hpp"
|
||||
#include "base/serializer.hpp"
|
||||
#include "base/timer.hpp"
|
||||
#include "base/initialize.hpp"
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <set>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
REGISTER_URLHANDLER("/v1/console", ConsoleHandler);
|
||||
|
||||
static int l_ExternalCommands = 0;
|
||||
static boost::mutex l_QueryMutex;
|
||||
static std::map<String, ApiScriptFrame> l_ApiScriptFrames;
|
||||
static Timer::Ptr l_FrameCleanupTimer;
|
||||
static boost::mutex l_ApiScriptMutex;
|
||||
|
||||
static void ScriptFrameCleanupHandler(void)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(l_ApiScriptMutex);
|
||||
|
||||
std::vector<String> cleanup_keys;
|
||||
|
||||
typedef std::pair<String, ApiScriptFrame> KVPair;
|
||||
|
||||
BOOST_FOREACH(const KVPair& kv, l_ApiScriptFrames) {
|
||||
if (kv.second.Seen < Utility::GetTime() - 1800)
|
||||
cleanup_keys.push_back(kv.first);
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const String& key, cleanup_keys)
|
||||
l_ApiScriptFrames.erase(key);
|
||||
}
|
||||
|
||||
static void InitScriptFrameCleanup(void)
|
||||
{
|
||||
l_FrameCleanupTimer = new Timer();
|
||||
l_FrameCleanupTimer->OnTimerExpired.connect(boost::bind(ScriptFrameCleanupHandler));
|
||||
l_FrameCleanupTimer->SetInterval(30);
|
||||
l_FrameCleanupTimer->Start();
|
||||
}
|
||||
|
||||
INITIALIZE_ONCE(InitScriptFrameCleanup);
|
||||
|
||||
bool ConsoleHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response)
|
||||
{
|
||||
if (request.RequestUrl->GetPath().size() > 3)
|
||||
return false;
|
||||
|
||||
if (request.RequestMethod != "POST")
|
||||
return false;
|
||||
|
||||
QueryDescription qd;
|
||||
Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request);
|
||||
|
||||
String methodName = request.RequestUrl->GetPath()[2];
|
||||
|
||||
String permission = "console/" + methodName;
|
||||
FilterUtility::CheckPermission(user, permission);
|
||||
|
||||
String session = HttpUtility::GetLastParameter(params, "session");
|
||||
|
||||
if (session.IsEmpty())
|
||||
session = Utility::NewUniqueID();
|
||||
|
||||
String command = HttpUtility::GetLastParameter(params, "command");
|
||||
|
||||
bool sandboxed = HttpUtility::GetLastParameter(params, "sandboxed");
|
||||
|
||||
if (methodName == "execute-script") {
|
||||
return ExecuteScriptHelper(request, response, command, session, sandboxed);
|
||||
} else if (methodName == "auto-complete-script") {
|
||||
return AutocompleteScriptHelper(request, response, command, session, sandboxed);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConsoleHandler::ExecuteScriptHelper(HttpRequest& request, HttpResponse& response,
|
||||
const String& command, const String& session, bool sandboxed)
|
||||
{
|
||||
Log(LogInformation, "Console")
|
||||
<< "Executing expression: " << command;
|
||||
|
||||
ApiScriptFrame& lsf = l_ApiScriptFrames[session];
|
||||
lsf.Seen = Utility::GetTime();
|
||||
|
||||
if (!lsf.Locals)
|
||||
lsf.Locals = new Dictionary();
|
||||
|
||||
String fileName = "<" + Convert::ToString(lsf.NextLine) + ">";
|
||||
lsf.NextLine++;
|
||||
|
||||
lsf.Lines[fileName] = command;
|
||||
|
||||
Array::Ptr results = new Array();
|
||||
Dictionary::Ptr resultInfo = new Dictionary();
|
||||
Expression *expr = NULL;
|
||||
Value exprResult;
|
||||
|
||||
try {
|
||||
expr = ConfigCompiler::CompileText(fileName, command);
|
||||
|
||||
ScriptFrame frame;
|
||||
frame.Locals = lsf.Locals;
|
||||
frame.Self = lsf.Locals;
|
||||
frame.Sandboxed = sandboxed;
|
||||
|
||||
exprResult = expr->Evaluate(frame);
|
||||
|
||||
resultInfo->Set("code", 200);
|
||||
resultInfo->Set("status", "Executed successfully.");
|
||||
resultInfo->Set("result", Serialize(exprResult, 0));
|
||||
} catch (const ScriptError& ex) {
|
||||
DebugInfo di = ex.GetDebugInfo();
|
||||
|
||||
std::ostringstream msgbuf;
|
||||
|
||||
msgbuf << di.Path << ": " << lsf.Lines[di.Path] << "\n"
|
||||
<< String(di.Path.GetLength() + 2, ' ')
|
||||
<< String(di.FirstColumn, ' ') << String(di.LastColumn - di.FirstColumn + 1, '^') << "\n"
|
||||
<< ex.what() << "\n";
|
||||
|
||||
resultInfo->Set("code", 500);
|
||||
resultInfo->Set("status", String(msgbuf.str()));
|
||||
resultInfo->Set("incomplete_expression", ex.IsIncompleteExpression());
|
||||
|
||||
Dictionary::Ptr debugInfo = new Dictionary();
|
||||
debugInfo->Set("path", di.Path);
|
||||
debugInfo->Set("first_line", di.FirstLine);
|
||||
debugInfo->Set("first_column", di.FirstColumn);
|
||||
debugInfo->Set("last_line", di.LastLine);
|
||||
debugInfo->Set("last_column", di.LastColumn);
|
||||
resultInfo->Set("debug_info", debugInfo);
|
||||
} catch (...) {
|
||||
delete expr;
|
||||
throw;
|
||||
}
|
||||
delete expr;
|
||||
|
||||
results->Add(resultInfo);
|
||||
|
||||
Dictionary::Ptr result = new Dictionary();
|
||||
result->Set("results", results);
|
||||
|
||||
response.SetStatus(200, "OK");
|
||||
HttpUtility::SendJsonBody(response, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConsoleHandler::AutocompleteScriptHelper(HttpRequest& request, HttpResponse& response,
|
||||
const String& command, const String& session, bool sandboxed)
|
||||
{
|
||||
Log(LogInformation, "Console")
|
||||
<< "Auto-completing expression: " << command;
|
||||
|
||||
ApiScriptFrame& lsf = l_ApiScriptFrames[session];
|
||||
lsf.Seen = Utility::GetTime();
|
||||
|
||||
if (!lsf.Locals)
|
||||
lsf.Locals = new Dictionary();
|
||||
|
||||
Array::Ptr results = new Array();
|
||||
Dictionary::Ptr resultInfo = new Dictionary();
|
||||
|
||||
ScriptFrame frame;
|
||||
frame.Locals = lsf.Locals;
|
||||
frame.Self = lsf.Locals;
|
||||
frame.Sandboxed = sandboxed;
|
||||
|
||||
resultInfo->Set("code", 200);
|
||||
resultInfo->Set("status", "Auto-completed successfully.");
|
||||
resultInfo->Set("suggestions", Array::FromVector(GetAutocompletionSuggestions(command, frame)));
|
||||
|
||||
results->Add(resultInfo);
|
||||
|
||||
Dictionary::Ptr result = new Dictionary();
|
||||
result->Set("results", results);
|
||||
|
||||
response.SetStatus(200, "OK");
|
||||
HttpUtility::SendJsonBody(response, result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void AddSuggestion(std::vector<String>& matches, const String& word, const String& suggestion)
|
||||
{
|
||||
if (suggestion.Find(word) != 0)
|
||||
return;
|
||||
|
||||
matches.push_back(suggestion);
|
||||
}
|
||||
|
||||
std::vector<String> ConsoleHandler::GetAutocompletionSuggestions(const String& word, ScriptFrame& frame)
|
||||
{
|
||||
std::vector<String> matches;
|
||||
|
||||
BOOST_FOREACH(const String& keyword, ConfigWriter::GetKeywords()) {
|
||||
AddSuggestion(matches, word, keyword);
|
||||
}
|
||||
|
||||
{
|
||||
ObjectLock olock(frame.Locals);
|
||||
BOOST_FOREACH(const Dictionary::Pair& kv, frame.Locals) {
|
||||
AddSuggestion(matches, word, kv.first);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ObjectLock olock(ScriptGlobal::GetGlobals());
|
||||
BOOST_FOREACH(const Dictionary::Pair& kv, ScriptGlobal::GetGlobals()) {
|
||||
AddSuggestion(matches, word, kv.first);
|
||||
}
|
||||
}
|
||||
|
||||
String::SizeType cperiod = word.RFind(".");
|
||||
|
||||
if (cperiod != -1) {
|
||||
String pword = word.SubStr(0, cperiod);
|
||||
|
||||
Value value;
|
||||
|
||||
try {
|
||||
Expression *expr = ConfigCompiler::CompileText("temp", pword);
|
||||
|
||||
if (expr)
|
||||
value = expr->Evaluate(frame);
|
||||
|
||||
if (value.IsObjectType<Dictionary>()) {
|
||||
Dictionary::Ptr dict = value;
|
||||
|
||||
ObjectLock olock(dict);
|
||||
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
|
||||
AddSuggestion(matches, word, pword + "." + kv.first);
|
||||
}
|
||||
}
|
||||
|
||||
Type::Ptr type = value.GetReflectionType();
|
||||
|
||||
for (int i = 0; i < type->GetFieldCount(); i++) {
|
||||
Field field = type->GetFieldInfo(i);
|
||||
|
||||
AddSuggestion(matches, word, pword + "." + field.Name);
|
||||
}
|
||||
|
||||
while (type) {
|
||||
Object::Ptr prototype = type->GetPrototype();
|
||||
Dictionary::Ptr dict = dynamic_pointer_cast<Dictionary>(prototype);
|
||||
|
||||
if (dict) {
|
||||
ObjectLock olock(dict);
|
||||
BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
|
||||
AddSuggestion(matches, word, pword + "." + kv.first);
|
||||
}
|
||||
}
|
||||
|
||||
type = type->GetBaseType();
|
||||
}
|
||||
} catch (...) { /* Ignore the exception */ }
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/******************************************************************************
|
||||
* Icinga 2 *
|
||||
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
|
||||
* *
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef CONSOLEHANDLER_H
|
||||
#define CONSOLEHANDLER_H
|
||||
|
||||
#include "remote/httphandler.hpp"
|
||||
#include "base/scriptframe.hpp"
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
struct I2_REMOTE_API ApiScriptFrame
|
||||
{
|
||||
double Seen;
|
||||
int NextLine;
|
||||
std::map<String, String> Lines;
|
||||
Dictionary::Ptr Locals;
|
||||
|
||||
ApiScriptFrame(void)
|
||||
: Seen(0), NextLine(1)
|
||||
{ }
|
||||
};
|
||||
|
||||
class I2_REMOTE_API ConsoleHandler : public HttpHandler
|
||||
{
|
||||
public:
|
||||
DECLARE_PTR_TYPEDEFS(ConsoleHandler);
|
||||
|
||||
virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override;
|
||||
|
||||
static std::vector<String> GetAutocompletionSuggestions(const String& word, ScriptFrame& frame);
|
||||
|
||||
private:
|
||||
static bool ExecuteScriptHelper(HttpRequest& request, HttpResponse& response,
|
||||
const String& command, const String& session, bool sandboxed);
|
||||
static bool AutocompleteScriptHelper(HttpRequest& request, HttpResponse& response,
|
||||
const String& command, const String& session, bool sandboxed);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CONSOLEHANDLER_H */
|
|
@ -192,7 +192,7 @@ bool HttpResponse::Parse(StreamReadContext& src, bool may_wait)
|
|||
if (srs != StatusNewItem)
|
||||
return false;
|
||||
|
||||
Log(LogInformation, "HttpResponse")
|
||||
Log(LogNotice, "HttpResponse")
|
||||
<< "Read " << size << " bytes";
|
||||
|
||||
m_Body->Write(data, size);
|
||||
|
|
|
@ -177,12 +177,14 @@ void Url::SetScheme(const String& scheme)
|
|||
m_Scheme = scheme;
|
||||
}
|
||||
|
||||
void Url::SetAuthority(const String& username, const String& password, const String& host, const String& port)
|
||||
void Url::SetUsername(const String& username)
|
||||
{
|
||||
m_Username = username;
|
||||
}
|
||||
|
||||
void Url::SetPassword(const String& password)
|
||||
{
|
||||
m_Password = password;
|
||||
m_Host = host;
|
||||
m_Port = port;
|
||||
}
|
||||
|
||||
void Url::SetHost(const String& host)
|
||||
|
|
|
@ -59,12 +59,13 @@ public:
|
|||
String GetFragment(void) const;
|
||||
|
||||
void SetScheme(const String& scheme);
|
||||
void SetAuthority(const String& username, const String& password,
|
||||
const String& host, const String& port);
|
||||
void SetUsername(const String& username);
|
||||
void SetPassword(const String& password);
|
||||
void SetHost(const String& host);
|
||||
void SetPort(const String& port);
|
||||
void SetPath(const std::vector<String>& path);
|
||||
void SetQuery(const std::map<String, std::vector<String> >& query);
|
||||
|
||||
void AddQueryElement(const String& name, const String& query);
|
||||
void SetQueryElements(const String& name, const std::vector<String>& query);
|
||||
void SetFragment(const String& fragment);
|
||||
|
|
|
@ -47,7 +47,10 @@ BOOST_AUTO_TEST_CASE(get_and_set)
|
|||
{
|
||||
Url::Ptr url = new Url();
|
||||
url->SetScheme("ftp");
|
||||
url->SetAuthority("Horst", "Seehofer", "koenigreich.bayern", "1918");
|
||||
url->SetUsername("Horst");
|
||||
url->SetPassword("Seehofer");
|
||||
url->SetHost("koenigreich.bayern");
|
||||
url->SetPort("1918");
|
||||
std::vector<String> p = boost::assign::list_of("path")("to")("münchen");
|
||||
url->SetPath(p);
|
||||
BOOST_CHECK(url->Format(true) == "ftp://Horst:Seehofer@koenigreich.bayern:1918/path/to/m%C3%BCnchen");
|
||||
|
|
Loading…
Reference in New Issue