Add monitor

more-debug
Simon Stürz 2018-08-21 22:09:59 +02:00
parent 2e15b840e9
commit faff3e9074
15 changed files with 577 additions and 10 deletions

11
debian/control vendored
View File

@ -5,6 +5,7 @@ Maintainer: Simon Stürz <simon.stürz@guh.io>
Build-depends: debhelper (>= 0.0.0),
dh-systemd,
libqt5websockets5-dev,
libncurses5-dev,
Standards-Version: 3.9.3
Package: nymea-remoteproxy
@ -13,10 +14,10 @@ Depends: ${shlibs:Depends},
${misc:Depends},
awscli,
libnymea-remoteproxy (= ${binary:Version}),
Suggests: nymea-remoteproxy-monitor (= ${binary:Version})
Description: The nymea remote proxy server
The nymea remote proxy server
Package: libnymea-remoteproxy
Architecture: any
Section: libs
@ -85,3 +86,11 @@ Depends: ${shlibs:Depends},
libnymea-remoteproxy (= ${binary:Version}),
Description: The nymea remote proxy server tests
The nymea remote proxy server tests
Package: nymea-remoteproxy-monitor
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
libncurses5,
Description: The nymea remote proxy monitor tool
The nymea remote proxy server tests

View File

@ -0,0 +1 @@
usr/bin/nymea-remoteproxy-monitor

View File

@ -53,9 +53,9 @@ bool ProxyConfiguration::loadConfiguration(const QString &fileName)
setMonitorSocketFileName(settings.value("monitorSocket", "/tmp/nymea-remoteproxy.monitor").toString());
setJsonRpcTimeout(settings.value("jsonRpcTimeout", 10000).toInt());
setAuthenticationTimeout(settings.value("authenticationTimeout", 5000).toInt());
setInactiveTimeout(settings.value("inactiveTimeout", 5000).toInt());
setAloneTimeout(settings.value("aloneTimeout", 5000).toInt());
setAuthenticationTimeout(settings.value("authenticationTimeout", 8000).toInt());
setInactiveTimeout(settings.value("inactiveTimeout", 8000).toInt());
setAloneTimeout(settings.value("aloneTimeout", 8000).toInt());
settings.endGroup();

View File

@ -23,6 +23,7 @@
#include "loggingcategories.h"
#include <QMetaObject>
#include <QVariantList>
#include <QJsonDocument>
namespace remoteproxy {
@ -64,6 +65,33 @@ QVariantMap ProxyServer::currentStatistics()
QVariantMap statisticsMap;
statisticsMap.insert("clientCount", m_proxyClients.count());
statisticsMap.insert("tunnelCount", m_tunnels.count());
// Create client list
QVariantList clientList;
foreach (ProxyClient *client, m_proxyClients) {
QVariantMap clientMap;
clientMap.insert("id", client->clientId().toString());
clientMap.insert("address", client->peerAddress().toString());
clientMap.insert("timestamp", client->creationTime());
clientMap.insert("authenticated", client->isAuthenticated());
clientMap.insert("tunnelConnected", client->isTunnelConnected());
clientMap.insert("name", client->name());
clientMap.insert("uuid", client->uuid());
clientList.append(clientMap);
}
statisticsMap.insert("clients", clientList);
// Create tunnel list
QVariantList tunnelList;
foreach (const TunnelConnection &tunnel, m_tunnels) {
QVariantMap tunnelMap;
tunnelMap.insert("clientOne", tunnel.clientOne()->clientId().toString());
tunnelMap.insert("clientTwo", tunnel.clientTwo()->clientId().toString());
tunnelMap.insert("timestamp", tunnel.creationTime());
tunnelList.append(tunnelMap);
}
statisticsMap.insert("tunnels", tunnelList);
return statisticsMap;
}

View File

@ -21,13 +21,15 @@
#include "tunnelconnection.h"
#include <QDateTime>
namespace remoteproxy {
TunnelConnection::TunnelConnection(ProxyClient *clientOne, ProxyClient *clientTwo):
m_clientOne(clientOne),
m_clientTwo(clientTwo)
{
m_creationTimeStamp = QDateTime::currentDateTime().toTime_t();
}
QString TunnelConnection::token() const
@ -38,6 +40,16 @@ QString TunnelConnection::token() const
return m_clientOne->token();
}
uint TunnelConnection::creationTime() const
{
return m_creationTimeStamp;
}
QString TunnelConnection::creationTimeString() const
{
return QDateTime::fromTime_t(creationTime()).toString("dd.MM.yyyy hh:mm:ss");
}
ProxyClient *TunnelConnection::clientOne() const
{
return m_clientOne;
@ -65,4 +77,12 @@ bool TunnelConnection::isValid() const
return true;
}
QDebug operator<<(QDebug debug, const TunnelConnection &tunnel)
{
debug.nospace() << "TunnelConnection(" << tunnel.creationTimeString() << ")" << endl;
debug.nospace() << " client:" << tunnel.clientOne() << endl;
debug.nospace() << " client:" << tunnel.clientTwo() << endl;
return debug;
}
}

View File

@ -33,17 +33,24 @@ public:
QString token() const;
uint creationTime() const;
QString creationTimeString() const;
ProxyClient *clientOne() const;
ProxyClient *clientTwo() const;
bool isValid() const;
private:
uint m_creationTimeStamp = 0;
ProxyClient *m_clientOne = nullptr;
ProxyClient *m_clientTwo = nullptr;
};
QDebug operator<< (QDebug debug, const TunnelConnection &tunnel);
}
#endif // TUNNELCONNECTION_H

View File

@ -25,6 +25,8 @@
#include <QCommandLineParser>
#include <QCommandLineOption>
#include "monitor.h"
int main(int argc, char *argv[])
{
@ -36,8 +38,7 @@ int main(int argc, char *argv[])
QCommandLineParser parser;
parser.addHelpOption();
parser.addVersionOption();
parser.setApplicationDescription(QString("\nThe nymea remote proxy server client application. This client allowes to test "
"a server application as client perspective.\n\n"
parser.setApplicationDescription(QString("\nThe nymea remote proxy monitor allowes to monitor the live server activity on the a local instance.\n\n"
"Server version: %1\n"
"API version: %2\n\n"
"Copyright %3 2018 Simon Stürz <simon.stuerz@guh.io>\n")
@ -46,10 +47,13 @@ int main(int argc, char *argv[])
.arg(QChar(0xA9)));
QCommandLineOption tokenOption(QStringList() << "s" << "socket", "The AWS token for authentication. Default /tmp/", "socket");
parser.addOption(tokenOption);
QCommandLineOption socketOption(QStringList() << "s" << "socket", "The socket descriptor for the nymea-remoteproxy monitor socket. Default is /tmp/nymea-remoteproxy-monitor.sock", "socket");
socketOption.setDefaultValue("/tmp/nymea-remoteproxy-monitor.sock");
parser.addOption(socketOption);
parser.process(application);
Monitor monitor(parser.value(socketOption));
return application.exec();
}

11
monitor/monitor.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "monitor.h"
Monitor::Monitor(const QString &serverName, QObject *parent) : QObject(parent)
{
m_terminal = new TerminalWindow(this);
m_monitorClient = new MonitorClient(serverName, this);
connect(m_monitorClient, &MonitorClient::dataReady, m_terminal, &TerminalWindow::refreshWindow);
m_monitorClient->connectMonitor();
}

42
monitor/monitor.h Normal file
View File

@ -0,0 +1,42 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea-remoteproxy. *
* *
* 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 3 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, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef MONITOR_H
#define MONITOR_H
#include <QObject>
#include "monitorclient.h"
#include "terminalwindow.h"
class Monitor : public QObject
{
Q_OBJECT
public:
explicit Monitor(const QString &serverName, QObject *parent = nullptr);
private:
TerminalWindow *m_terminal = nullptr;
MonitorClient *m_monitorClient = nullptr;
};
#endif // MONITOR_H

View File

@ -3,9 +3,17 @@ include(../nymea-remoteproxy.pri)
TARGET = nymea-remoteproxy-monitor
TEMPLATE = app
SOURCES += main.cpp
SOURCES += main.cpp \
monitorclient.cpp \
terminalwindow.cpp \
monitor.cpp
LIBS += -lncurses
target.path = /usr/bin
INSTALLS += target
HEADERS += \
monitorclient.h \
terminalwindow.h \
monitor.h

73
monitor/monitorclient.cpp Normal file
View File

@ -0,0 +1,73 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea-remoteproxy. *
* *
* 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 3 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, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "monitorclient.h"
#include <QJsonDocument>
MonitorClient::MonitorClient(const QString &serverName, QObject *parent) :
QObject(parent),
m_serverName(serverName)
{
m_socket = new QLocalSocket(this);
connect(m_socket, &QLocalSocket::connected, this, &MonitorClient::onConnected);
connect(m_socket, &QLocalSocket::disconnected, this, &MonitorClient::onDisconnected);
connect(m_socket, &QLocalSocket::readyRead, this, &MonitorClient::onReadyRead);
}
void MonitorClient::onConnected()
{
qDebug() << "Monitor connected to" << m_serverName;
}
void MonitorClient::onDisconnected()
{
qDebug() << "Monitor disconnected from" << m_serverName;
}
void MonitorClient::onReadyRead()
{
QByteArray data = m_socket->readAll();
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
if(error.error != QJsonParseError::NoError) {
qWarning() << "Failed to parse JSON data" << data << ":" << error.errorString();
return;
}
//qDebug() << qUtf8Printable(jsonDoc.toJson(QJsonDocument::Indented));
QVariantMap dataMap = jsonDoc.toVariant().toMap();
emit dataReady(dataMap);
}
void MonitorClient::connectMonitor()
{
m_socket->connectToServer(m_serverName, QLocalSocket::ReadOnly);
}
void MonitorClient::disconnectMonitor()
{
m_socket->close();
}

53
monitor/monitorclient.h Normal file
View File

@ -0,0 +1,53 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea-remoteproxy. *
* *
* 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 3 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, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef MONITORCLIENT_H
#define MONITORCLIENT_H
#include <QObject>
#include <QLocalSocket>
#include "terminalwindow.h"
class MonitorClient : public QObject
{
Q_OBJECT
public:
explicit MonitorClient(const QString &serverName, QObject *parent = nullptr);
private:
QString m_serverName;
QLocalSocket *m_socket = nullptr;
signals:
void dataReady(const QVariantMap &data);
private slots:
void onConnected();
void onDisconnected();
void onReadyRead();
public slots:
void connectMonitor();
void disconnectMonitor();
};
#endif // MONITORCLIENT_H

234
monitor/terminalwindow.cpp Normal file
View File

@ -0,0 +1,234 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea-remoteproxy. *
* *
* 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 3 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, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "terminalwindow.h"
#include <QDebug>
#include <QDateTime>
#include <QMetaObject>
TerminalWindow::TerminalWindow(QObject *parent) :
QObject(parent)
{
// Create main window
m_mainWindow = initscr();
start_color();
init_pair(1, COLOR_BLACK, COLOR_GREEN);
init_pair(2, COLOR_BLACK, COLOR_RED);
// Configure behaviour
cbreak();
noecho();
curs_set(FALSE);
//clear();
keypad(m_mainWindow, true);
nodelay(m_mainWindow, true);
getmaxyx(stdscr, m_terminalSizeY, m_terminalSizeX);
// Create header and content window
m_headerWindow = newwin(m_headerHeight, m_terminalSizeX, 0, 0);
m_contentWindow = newwin(m_terminalSizeY - m_headerHeight, m_terminalSizeX, m_headerHeight, 0);
// Draw borders
wclear(m_headerWindow);
wclear(m_contentWindow);
mvwprintw(m_headerWindow, 1, 2, convertString("Connecting..."));
box(m_headerWindow, 0 , 0);
box(m_contentWindow, 0 , 0);
// Refresh windows
wrefresh(m_headerWindow);
wrefresh(m_contentWindow);
refresh();
}
TerminalWindow::~TerminalWindow()
{
clear();
delwin(m_headerWindow);
delwin(m_contentWindow);
delwin(m_mainWindow);
endwin();
}
const char *TerminalWindow::convertString(const QString &string)
{
return reinterpret_cast<const char *>(string.toLatin1().data());
}
void TerminalWindow::resizeWindow()
{
int terminalSizeX;
int terminalSizeY;
getmaxyx(stdscr, terminalSizeY, terminalSizeX);
if (m_terminalSizeX != terminalSizeX || m_terminalSizeY != terminalSizeY) {
m_terminalSizeX = terminalSizeX;
m_terminalSizeY = terminalSizeY;
wresize(m_headerWindow, m_headerHeight, m_terminalSizeX);
wresize(m_contentWindow, m_terminalSizeY - m_headerHeight, m_terminalSizeX);
}
}
void TerminalWindow::paintHeader()
{
QString headerString = QString(" Server: %1 %2 | API version %3 | Clients: %4 | Tunnels %5 | %6 %7")
.arg(m_dataMap.value("serverName").toString())
.arg(m_dataMap.value("serverVersion").toString())
.arg(m_dataMap.value("apiVersion").toString())
.arg(m_dataMap.value("proxyStatistic").toMap().value("clientCount").toInt())
.arg(m_dataMap.value("proxyStatistic").toMap().value("tunnelCount").toInt())
.arg((m_view == ViewClients ? "Clients" : "Tunnels"))
.arg("", m_terminalSizeX, ' ');
wclear(m_headerWindow);
wattron(m_headerWindow, A_BOLD | COLOR_PAIR(1));
mvwprintw(m_headerWindow, 1, 2, convertString(headerString));
wattroff(m_headerWindow, A_BOLD | COLOR_PAIR(1));
box(m_headerWindow, 0 , 0);
wrefresh(m_headerWindow);
}
void TerminalWindow::paintContentClients()
{
int i = 1;
wclear(m_contentWindow);
foreach (const QVariant &clientVariant, m_clientHash.values()) {
QVariantMap clientMap = clientVariant.toMap();
uint timeStamp = clientMap.value("timestamp").toUInt();
QString clientConnectionTime = QDateTime::fromTime_t(timeStamp).toString("dd.MM.yyyy hh:mm:ss");
QString clientPrint = QString("%1 | %2 | %3 | %4 | %5 | %6 |")
.arg(clientConnectionTime)
.arg(clientMap.value("address").toString())
.arg(clientMap.value("uuid").toString())
.arg(clientMap.value("name").toString(), -30)
.arg((clientMap.value("authenticated").toBool() ? "auth" : "no auth"), -9)
.arg((clientMap.value("tunnelConnected").toBool() ? "<--->" : " | "));
mvwprintw(m_contentWindow, i, 2, convertString(clientPrint));
i++;
}
// Draw borders
box(m_contentWindow, 0 , 0);
wrefresh(m_contentWindow);
}
void TerminalWindow::paintContentTunnels()
{
int i = 1;
wclear(m_contentWindow);
foreach (const QVariant &tunnelVaiant, m_dataMap.value("proxyStatistic").toMap().value("tunnels").toList()) {
QVariantMap tunnelMap = tunnelVaiant.toMap();
QVariantMap clientOne = m_clientHash.value(tunnelMap.value("clientOne").toString());
QVariantMap clientTwo = m_clientHash.value(tunnelMap.value("clientTwo").toString());
// Tunnel time
uint timeStamp = tunnelMap.value("timestamp").toUInt();
QString tunnelConnectionTime = QDateTime::fromTime_t(timeStamp).toString("dd.MM.yyyy hh:mm:ss");
QString tunnelPrint = QString("%1 | %2 %3 %4 %5 %6")
.arg(tunnelConnectionTime)
.arg(clientOne.value("address").toString())
.arg(clientOne.value("name").toString())
.arg("<--->", 10)
.arg(clientTwo.value("address").toString())
.arg(clientTwo.value("name").toString())
;
mvwprintw(m_contentWindow, i, 2, convertString(tunnelPrint));
i++;
}
// Draw borders
box(m_contentWindow, 0 , 0);
wrefresh(m_contentWindow);
}
void TerminalWindow::eventLoop()
{
resizeWindow();
int keyInteger = getch();
switch (keyInteger) {
case KEY_LEFT:
m_view = ViewClients;
break;
case KEY_RIGHT:
m_view = ViewTunnels;
break;
case 27: // Esc
clear();
delwin(m_headerWindow);
delwin(m_contentWindow);
delwin(m_mainWindow);
endwin();
exit(0);
break;
default:
break;
}
// Refresh header window
paintHeader();
// Refresh content window
switch (m_view) {
case ViewClients:
paintContentClients();
break;
case ViewTunnels:
paintContentTunnels();
break;
}
refresh();
// Reinvoke the method for the next event loop run
QMetaObject::invokeMethod(this, "eventLoop", Qt::QueuedConnection);
}
void TerminalWindow::refreshWindow(const QVariantMap &dataMap)
{
m_dataMap = dataMap;
QVariantMap statisticMap = m_dataMap.value("proxyStatistic").toMap();
m_clientHash.clear();
foreach (const QVariant &clientVariant, statisticMap.value("clients").toList()) {
QVariantMap clientMap = clientVariant.toMap();
m_clientHash.insert(clientMap.value("id").toString(), clientMap);
}
eventLoop();
}

76
monitor/terminalwindow.h Normal file
View File

@ -0,0 +1,76 @@
#ifndef TERMINALWINDOW_H
#define TERMINALWINDOW_H
#include <QObject>
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2018 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea-remoteproxy. *
* *
* 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 3 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, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <QVariantMap>
#include <ncurses.h>
class TerminalWindow : public QObject
{
Q_OBJECT
public:
enum View {
ViewClients,
ViewTunnels
};
Q_ENUM(View)
explicit TerminalWindow(QObject *parent = nullptr);
~TerminalWindow();
private:
WINDOW *m_mainWindow = nullptr;
WINDOW *m_headerWindow = nullptr;
WINDOW *m_contentWindow = nullptr;
int m_headerHeight = 3;
int m_terminalSizeX = 0;
int m_terminalSizeY = 0;
View m_view = ViewClients;
QVariantMap m_dataMap;
QHash<QString, QVariantMap> m_clientHash;
const char *convertString(const QString &string);
// content paint methods
void resizeWindow();
void paintHeader();
void paintContentClients();
void paintContentTunnels();
signals:
private slots:
void eventLoop();
public slots:
void refreshWindow(const QVariantMap &dataMap);
};
#endif // TERMINALWINDOW_H

View File

@ -12,5 +12,6 @@ message("Building nymea-remoteproxy $${SERVER_VERSION}")
message("----------------------------------------------------------")
message("JSON-RPC API version $${API_VERSION_MAJOR}.$${API_VERSION_MINOR}")
message("Qt version:" $$[QT_VERSION])
coverage { message("Building with coverage report") }
ccache { message("Building with ccache support") }