2015-08-29 01:16:16 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* Icinga 2 *
|
2016-01-12 08:29:59 +01:00
|
|
|
* Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/) *
|
2015-08-29 01:16:16 +02:00
|
|
|
* *
|
|
|
|
* 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 "icinga-studio/mainform.hpp"
|
|
|
|
#include "icinga-studio/aboutform.hpp"
|
|
|
|
#include <boost/algorithm/string/split.hpp>
|
|
|
|
#include <boost/algorithm/string/classification.hpp>
|
2015-09-30 09:40:29 +02:00
|
|
|
#include <wx/msgdlg.h>
|
2015-08-29 01:16:16 +02:00
|
|
|
|
|
|
|
using namespace icinga;
|
|
|
|
|
|
|
|
MainForm::MainForm(wxWindow *parent, const Url::Ptr& url)
|
|
|
|
: MainFormBase(parent)
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
SetIcon(wxICON(icinga));
|
|
|
|
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
|
2015-09-29 08:09:48 +02:00
|
|
|
String port = url->GetPort();
|
2015-08-29 01:16:16 +02:00
|
|
|
|
2015-09-29 08:09:48 +02:00
|
|
|
if (port.IsEmpty())
|
2015-08-29 01:16:16 +02:00
|
|
|
port = "5665";
|
|
|
|
|
2015-09-29 08:09:48 +02:00
|
|
|
m_ApiClient = new ApiClient(url->GetHost(), port, url->GetUsername(), url->GetPassword());
|
2015-09-30 09:40:29 +02:00
|
|
|
m_ApiClient->GetTypes(boost::bind(&MainForm::TypesCompletionHandler, this, _1, _2, true));
|
2015-08-29 01:16:16 +02:00
|
|
|
|
2015-09-29 08:09:48 +02:00
|
|
|
std::string title = url->Format() + " - Icinga Studio";
|
2015-08-29 01:16:16 +02:00
|
|
|
SetTitle(title);
|
|
|
|
|
|
|
|
m_ObjectsList->InsertColumn(0, "Name", 0, 300);
|
2015-10-01 05:57:15 +02:00
|
|
|
|
|
|
|
m_PropertyGrid->SetColumnCount(3);
|
2015-08-29 01:16:16 +02:00
|
|
|
}
|
|
|
|
|
2016-08-31 13:43:14 +02:00
|
|
|
void MainForm::TypesCompletionHandler(boost::exception_ptr eptr, const std::vector<ApiType::Ptr>& types, bool forward)
|
2015-08-29 01:16:16 +02:00
|
|
|
{
|
|
|
|
if (forward) {
|
2015-09-30 09:40:29 +02:00
|
|
|
CallAfter(boost::bind(&MainForm::TypesCompletionHandler, this, eptr, types, false));
|
2015-08-29 01:16:16 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_TypesTree->DeleteAllItems();
|
2015-09-30 09:40:29 +02:00
|
|
|
|
|
|
|
if (eptr) {
|
|
|
|
try {
|
2016-08-31 13:43:14 +02:00
|
|
|
boost::rethrow_exception(eptr);
|
2015-09-30 09:40:29 +02:00
|
|
|
} catch (const std::exception& ex) {
|
|
|
|
std::string message = "HTTP query failed: " + std::string(ex.what());
|
|
|
|
wxMessageBox(message, "Icinga Studio", wxOK | wxCENTRE | wxICON_ERROR, this);
|
|
|
|
Close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-29 01:16:16 +02:00
|
|
|
wxTreeItemId rootNode = m_TypesTree->AddRoot("root");
|
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const ApiType::Ptr& type : types) {
|
2015-11-03 08:27:36 +01:00
|
|
|
m_Types[type->Name] = type;
|
|
|
|
}
|
2015-08-29 01:16:16 +02:00
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const ApiType::Ptr& type : types) {
|
2015-11-03 08:27:36 +01:00
|
|
|
if (type->Abstract)
|
|
|
|
continue;
|
2015-08-29 01:16:16 +02:00
|
|
|
|
2015-11-03 08:27:36 +01:00
|
|
|
bool configObject = false;
|
|
|
|
ApiType::Ptr currentType = type;
|
2015-08-29 01:16:16 +02:00
|
|
|
|
2015-11-03 08:27:36 +01:00
|
|
|
for (;;) {
|
|
|
|
if (currentType->BaseName.IsEmpty())
|
|
|
|
break;
|
2015-08-29 01:16:16 +02:00
|
|
|
|
2015-11-03 08:27:36 +01:00
|
|
|
currentType = m_Types[currentType->BaseName];
|
2015-08-29 01:16:16 +02:00
|
|
|
|
2015-11-03 08:27:36 +01:00
|
|
|
if (!currentType)
|
|
|
|
break;
|
2015-08-29 01:16:16 +02:00
|
|
|
|
2015-11-03 08:27:36 +01:00
|
|
|
if (currentType->Name == "ConfigObject") {
|
|
|
|
configObject = true;
|
|
|
|
break;
|
2015-08-29 01:16:16 +02:00
|
|
|
}
|
2015-11-03 08:27:36 +01:00
|
|
|
}
|
2015-08-29 01:16:16 +02:00
|
|
|
|
2015-11-03 08:27:36 +01:00
|
|
|
if (configObject) {
|
|
|
|
std::string name = type->Name;
|
|
|
|
m_TypesTree->AppendItem(rootNode, name, 0);
|
2015-08-29 01:16:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainForm::OnTypeSelected(wxTreeEvent& event)
|
|
|
|
{
|
|
|
|
wxTreeItemId selectedId = m_TypesTree->GetSelection();
|
|
|
|
wxString typeName = m_TypesTree->GetItemText(selectedId);
|
|
|
|
ApiType::Ptr type = m_Types[typeName.ToStdString()];
|
|
|
|
|
|
|
|
std::vector<String> attrs;
|
2015-11-06 11:04:58 +01:00
|
|
|
attrs.push_back("__name");
|
2015-08-29 01:16:16 +02:00
|
|
|
|
2015-09-30 09:40:29 +02:00
|
|
|
m_ApiClient->GetObjects(type->PluralName, boost::bind(&MainForm::ObjectsCompletionHandler, this, _1, _2, true),
|
2015-08-29 01:16:16 +02:00
|
|
|
std::vector<String>(), attrs);
|
|
|
|
}
|
|
|
|
|
2015-11-06 11:04:58 +01:00
|
|
|
static bool ApiObjectLessComparer(const ApiObject::Ptr& o1, const ApiObject::Ptr& o2)
|
2015-11-03 09:44:12 +01:00
|
|
|
{
|
2015-11-06 11:04:58 +01:00
|
|
|
return o1->Name < o2->Name;
|
2015-11-03 09:44:12 +01:00
|
|
|
}
|
|
|
|
|
2016-08-31 13:43:14 +02:00
|
|
|
void MainForm::ObjectsCompletionHandler(boost::exception_ptr eptr, const std::vector<ApiObject::Ptr>& objects, bool forward)
|
2015-08-29 01:16:16 +02:00
|
|
|
{
|
|
|
|
if (forward) {
|
2015-09-30 09:40:29 +02:00
|
|
|
CallAfter(boost::bind(&MainForm::ObjectsCompletionHandler, this, eptr, objects, false));
|
2015-08-29 01:16:16 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-09-30 09:40:29 +02:00
|
|
|
m_ObjectsList->DeleteAllItems();
|
|
|
|
m_PropertyGrid->Clear();
|
|
|
|
|
|
|
|
if (eptr) {
|
|
|
|
try {
|
2016-08-31 13:43:14 +02:00
|
|
|
boost::rethrow_exception(eptr);
|
2015-09-30 09:40:29 +02:00
|
|
|
} catch (const std::exception& ex) {
|
|
|
|
std::string message = "HTTP query failed: " + std::string(ex.what());
|
|
|
|
wxMessageBox(message, "Icinga Studio", wxOK | wxCENTRE | wxICON_ERROR, this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-03 09:44:12 +01:00
|
|
|
std::vector<ApiObject::Ptr> sortedObjects = objects;
|
2015-11-06 11:04:58 +01:00
|
|
|
std::sort(sortedObjects.begin(), sortedObjects.end(), ApiObjectLessComparer);
|
2015-11-03 09:44:12 +01:00
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const ApiObject::Ptr& object : sortedObjects) {
|
2015-11-06 11:04:58 +01:00
|
|
|
std::string name = object->Name;
|
|
|
|
m_ObjectsList->InsertItem(0, name);
|
2015-08-29 01:16:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainForm::OnObjectSelected(wxListEvent& event)
|
|
|
|
{
|
|
|
|
wxTreeItemId selectedId = m_TypesTree->GetSelection();
|
|
|
|
wxString typeName = m_TypesTree->GetItemText(selectedId);
|
|
|
|
ApiType::Ptr type = m_Types[typeName.ToStdString()];
|
|
|
|
|
|
|
|
long itemIndex = -1;
|
|
|
|
std::string objectName;
|
|
|
|
|
|
|
|
while ((itemIndex = m_ObjectsList->GetNextItem(itemIndex,
|
|
|
|
wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != wxNOT_FOUND) {
|
|
|
|
objectName = m_ObjectsList->GetItemText(itemIndex);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (objectName.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::vector<String> names;
|
|
|
|
names.push_back(objectName);
|
|
|
|
|
2015-11-06 11:04:58 +01:00
|
|
|
m_ApiClient->GetObjects(type->PluralName, boost::bind(&MainForm::ObjectDetailsCompletionHandler, this, _1, _2, true),
|
|
|
|
names, std::vector<String>(), std::vector<String>(), true);
|
2015-08-29 01:16:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
wxPGProperty *MainForm::ValueToProperty(const String& name, const Value& value)
|
|
|
|
{
|
|
|
|
wxPGProperty *prop;
|
|
|
|
|
|
|
|
if (value.IsNumber()) {
|
2015-10-01 05:57:15 +02:00
|
|
|
prop = new wxFloatProperty(name.GetData(), wxPG_LABEL, value);
|
|
|
|
prop->SetAttribute(wxPG_ATTR_UNITS, "Number");
|
|
|
|
return prop;
|
2015-08-29 01:16:16 +02:00
|
|
|
} else if (value.IsBoolean()) {
|
2015-10-01 05:57:15 +02:00
|
|
|
prop = new wxBoolProperty(name.GetData(), wxPG_LABEL, value);
|
|
|
|
prop->SetAttribute(wxPG_ATTR_UNITS, "Boolean");
|
|
|
|
return prop;
|
2015-08-29 01:16:16 +02:00
|
|
|
} else if (value.IsObjectType<Array>()) {
|
|
|
|
wxArrayString val;
|
|
|
|
Array::Ptr arr = value;
|
2015-10-01 05:57:15 +02:00
|
|
|
|
2015-08-29 01:16:16 +02:00
|
|
|
{
|
2015-10-01 05:57:15 +02:00
|
|
|
ObjectLock olock(arr);
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const Value& aitem : arr) {
|
2015-10-01 05:57:15 +02:00
|
|
|
String val1 = aitem;
|
|
|
|
val.Add(val1.GetData());
|
|
|
|
}
|
2015-08-29 01:16:16 +02:00
|
|
|
}
|
|
|
|
|
2015-10-01 05:57:15 +02:00
|
|
|
prop = new wxArrayStringProperty(name.GetData(), wxPG_LABEL, val);
|
|
|
|
prop->SetAttribute(wxPG_ATTR_UNITS, "Array");
|
|
|
|
return prop;
|
2015-08-29 01:16:16 +02:00
|
|
|
} else if (value.IsObjectType<Dictionary>()) {
|
2015-10-01 05:57:15 +02:00
|
|
|
wxStringProperty *prop = new wxStringProperty(name.GetData(), wxPG_LABEL);
|
2015-08-29 01:16:16 +02:00
|
|
|
|
|
|
|
Dictionary::Ptr dict = value;
|
2015-10-01 05:57:15 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
ObjectLock olock(dict);
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const Dictionary::Pair& kv : dict) {
|
2015-10-01 05:57:15 +02:00
|
|
|
if (kv.first != "type")
|
|
|
|
prop->AppendChild(ValueToProperty(kv.first, kv.second));
|
|
|
|
}
|
2015-08-29 01:16:16 +02:00
|
|
|
}
|
|
|
|
|
2015-10-01 05:57:15 +02:00
|
|
|
String type = "Dictionary";
|
|
|
|
|
|
|
|
if (dict->Contains("type"))
|
|
|
|
type = dict->Get("type");
|
|
|
|
|
|
|
|
prop->SetAttribute(wxPG_ATTR_UNITS, type.GetData());
|
|
|
|
|
|
|
|
return prop;
|
|
|
|
} else if (value.IsEmpty() && !value.IsString()) {
|
|
|
|
prop = new wxStringProperty(name.GetData(), wxPG_LABEL, "");
|
|
|
|
prop->SetAttribute(wxPG_ATTR_UNITS, "Empty");
|
2015-08-29 01:16:16 +02:00
|
|
|
return prop;
|
|
|
|
} else {
|
|
|
|
String val = value;
|
2015-10-01 05:57:15 +02:00
|
|
|
prop = new wxStringProperty(name.GetData(), wxPG_LABEL, val.GetData());
|
|
|
|
prop->SetAttribute(wxPG_ATTR_UNITS, "String");
|
|
|
|
return prop;
|
2015-08-29 01:16:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-31 13:43:14 +02:00
|
|
|
void MainForm::ObjectDetailsCompletionHandler(boost::exception_ptr eptr, const std::vector<ApiObject::Ptr>& objects, bool forward)
|
2015-08-29 01:16:16 +02:00
|
|
|
{
|
|
|
|
if (forward) {
|
2015-09-30 09:40:29 +02:00
|
|
|
CallAfter(boost::bind(&MainForm::ObjectDetailsCompletionHandler, this, eptr, objects, false));
|
2015-08-29 01:16:16 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-09-30 09:40:29 +02:00
|
|
|
m_PropertyGrid->Clear();
|
|
|
|
|
|
|
|
if (eptr) {
|
|
|
|
try {
|
2016-08-31 13:43:14 +02:00
|
|
|
boost::rethrow_exception(eptr);
|
2015-09-30 09:40:29 +02:00
|
|
|
} catch (const std::exception& ex) {
|
|
|
|
std::string message = "HTTP query failed: " + std::string(ex.what());
|
|
|
|
wxMessageBox(message, "Icinga Studio", wxOK | wxCENTRE | wxICON_ERROR, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-29 01:16:16 +02:00
|
|
|
wxTreeItemId selectedId = m_TypesTree->GetSelection();
|
|
|
|
wxString typeName = m_TypesTree->GetItemText(selectedId);
|
|
|
|
ApiType::Ptr type = m_Types[typeName.ToStdString()];
|
|
|
|
|
|
|
|
String nameAttr = type->Name.ToLower() + ".__name";
|
|
|
|
|
|
|
|
if (objects.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ApiObject::Ptr object = objects[0];
|
|
|
|
|
2015-09-30 09:40:29 +02:00
|
|
|
std::map<String, wxStringProperty *> parents;
|
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const auto& kv : object->Attrs) {
|
2015-08-29 01:16:16 +02:00
|
|
|
std::vector<String> tokens;
|
|
|
|
boost::algorithm::split(tokens, kv.first, boost::is_any_of("."));
|
|
|
|
|
2015-09-30 09:40:29 +02:00
|
|
|
std::map<String, wxStringProperty *>::const_iterator it = parents.find(tokens[0]);
|
|
|
|
|
|
|
|
wxStringProperty *parent;
|
|
|
|
|
|
|
|
if (it == parents.end()) {
|
2015-10-01 05:57:15 +02:00
|
|
|
parent = new wxStringProperty(tokens[0].GetData(), wxPG_LABEL);
|
|
|
|
parent->SetAttribute(wxPG_ATTR_UNITS, "Object");
|
2015-09-30 09:40:29 +02:00
|
|
|
parents[tokens[0]] = parent;
|
|
|
|
} else
|
|
|
|
parent = it->second;
|
|
|
|
|
2015-08-29 01:16:16 +02:00
|
|
|
wxPGProperty *prop = ValueToProperty(tokens[1], kv.second);
|
2015-09-30 09:40:29 +02:00
|
|
|
parent->AppendChild(prop);
|
2015-09-30 14:13:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure the property node for the real object (as opposed to joined objects) is the first one */
|
|
|
|
String propName = type->Name.ToLower();
|
|
|
|
wxStringProperty *objProp = parents[propName];
|
|
|
|
|
|
|
|
if (objProp) {
|
|
|
|
m_PropertyGrid->Append(objProp);
|
|
|
|
m_PropertyGrid->SetPropertyReadOnly(objProp);
|
|
|
|
parents.erase(propName);
|
|
|
|
}
|
|
|
|
|
2016-08-25 06:19:44 +02:00
|
|
|
for (const auto& kv : parents) {
|
2015-09-30 14:13:18 +02:00
|
|
|
m_PropertyGrid->Append(kv.second);
|
|
|
|
m_PropertyGrid->SetPropertyReadOnly(kv.second);
|
2015-08-29 01:16:16 +02:00
|
|
|
}
|
2015-10-01 05:57:15 +02:00
|
|
|
|
|
|
|
m_PropertyGrid->FitColumns();
|
2015-08-29 01:16:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MainForm::OnQuitClicked(wxCommandEvent& event)
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MainForm::OnAboutClicked(wxCommandEvent& event)
|
|
|
|
{
|
|
|
|
AboutForm form(this);
|
|
|
|
form.ShowModal();
|
|
|
|
}
|