diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c09c90321453feadd9808e5b8de4ef227409e0d3..14f36c93e438ae78dd11fd8f1bfc1973516c3e64 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,7 @@ linux-amd64-debian-buster-dbg: image: demlabs/amd64/debian-buster:linuxbuilder before_script: /opt/buildtools/prepare_environment.sh amd64-linux script: - - ./prod_build/build.sh --target linux debug -DBUILD_WITH_PYTHON_ENV=ON + - ./prod_build/build.sh --target linux debug -DBUILD_WITH_PYTHON_ENV=ON -DBUILD_DIAGTOOL=ON - ./prod_build/pack.sh --target linux debug linux-amd64-debian-buster-rwd: @@ -56,7 +56,7 @@ linux-amd64-debian-buster-rwd: image: demlabs/amd64/debian-buster:linuxbuilder before_script: /opt/buildtools/prepare_environment.sh amd64-linux script: - - ./prod_build/build.sh --target linux rwd -DBUILD_WITH_PYTHON_ENV=ON + - ./prod_build/build.sh --target linux rwd -DBUILD_WITH_PYTHON_ENV=ON -DBUILD_DIAGTOOL=ON - ./prod_build/pack.sh --target linux rwd linux-amd64-debian-buster: @@ -64,7 +64,7 @@ linux-amd64-debian-buster: image: demlabs/amd64/debian-buster:linuxbuilder before_script: /opt/buildtools/prepare_environment.sh amd64-linux script: - - ./prod_build/build.sh --target linux release -DBUILD_WITH_PYTHON_ENV=ON + - ./prod_build/build.sh --target linux release -DBUILD_WITH_PYTHON_ENV=ON -DBUILD_DIAGTOOL=ON - ./prod_build/pack.sh --target linux release linux-armhf-debian-bullseye: diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a2213d0fb85db4c2a953f599de52dfdefbc1e7f..2a1d256bb62ae82564e3a513e1b62d682b018b2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,9 @@ SET( CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) SET( CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) SET( CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH}) +#enable for diagtool +#set(BUILD_DIAGTOOL OFF) + SET(CMAKE_INSTALL_PREFIX "/opt/${PROJECT_NAME}") SET(CPACK_INSTALL_PREFIX "/opt/${PROJECT_NAME}") SET(DESTDIR "/opt/${PROJECT_NAME}") @@ -223,6 +226,11 @@ if(UNIX AND NOT WIN32) set(NODE_CLI_LIBRARIES m cellframe-sdk) set(NODE_TOOL_LIBRARIES m cellframe-sdk) + + if(BUILD_DIAGTOOL) + message("[*] Diagtool build on") + add_subdirectory(diagtool) + endif() if (SUPPORT_PYTHON_PLUGINS) message("[+] Build with python plugins support") @@ -307,6 +315,7 @@ if(DARWIN) INSTALL(TARGETS ${NODE_CLI_TARGET} DESTINATION ${BINDIR} ) INSTALL(TARGETS ${NODE_TOOL_TARGET} DESTINATION ${BINDIR} ) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/os/macos/com.demlabs.cellframe-node.plist DESTINATION ${SHAREDIR} ) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/scripts/ DESTINATION ${SHAREDIR} FILES_MATCHING PATTERN "*" PATTERN "*" PERMISSIONS OWNER_EXECUTE;OWNER_READ;OWNER_WRITE;WORLD_READ;GROUP_READ ) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/scripts.darwin/ DESTINATION ${SHAREDIR} FILES_MATCHING PATTERN "*" PATTERN "*" PERMISSIONS OWNER_EXECUTE;OWNER_READ;OWNER_WRITE;WORLD_READ;GROUP_READ ) else() @@ -322,6 +331,10 @@ else() INSTALL(TARGETS ${NODE_CLI_TARGET} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin ) INSTALL(TARGETS ${NODE_TOOL_TARGET} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin ) + if (BUILD_DIAGTOOL) + INSTALL(TARGETS cellframe-diagtool DESTINATION ${CMAKE_INSTALL_PREFIX}/bin ) + endif() + if(NOT ANDROID) #install all python-specific files back to its original location if (SUPPORT_PYTHON_PLUGINS AND BUILD_WITH_PYTHON_ENV) diff --git a/diagtool/AbstractDiagnostic.cpp b/diagtool/AbstractDiagnostic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b175b29ed3ff856dafce1d9d7b8a48bb0e6fa8b1 --- /dev/null +++ b/diagtool/AbstractDiagnostic.cpp @@ -0,0 +1,150 @@ +#include "AbstractDiagnostic.h" + +AbstractDiagnostic::AbstractDiagnostic(QObject *parent) + :QObject(parent) +{ + s_timer_update = new QTimer(); + s_mac = get_mac(); +} + +AbstractDiagnostic::~AbstractDiagnostic() +{ + delete s_timer_update; +} + +void AbstractDiagnostic::set_timeout(int timeout){ + s_timer_update->stop(); + s_timeout = timeout; + s_timer_update->start(s_timeout); +} + +void AbstractDiagnostic::start_diagnostic() +{ + s_timer_update->start(s_timeout); +} + +void AbstractDiagnostic::stop_diagnostic() +{ + s_timer_update->stop(); +} + +QJsonValue AbstractDiagnostic::get_mac() +{ + QString MAC{"unknown"}; + foreach(QNetworkInterface netInterface, QNetworkInterface::allInterfaces()) + { + // Return only the first non-loopback MAC Address + if (!(netInterface.flags() & QNetworkInterface::IsLoopBack)) + { + qDebug()<<netInterface.hardwareAddress(); + if(!netInterface.hardwareAddress().isEmpty()) + { + MAC = netInterface.hardwareAddress(); + break; + } + } + } + + return MAC; +} + +QString AbstractDiagnostic::get_uptime_string(long sec) +{ + QTime time(0, 0); + time = time.addSecs(sec); + int fullHours = sec/3600; + + QString uptime = QString("%1:").arg(fullHours) + time.toString("mm:ss"); + + return uptime; +} + +quint64 AbstractDiagnostic::get_file_size (QString flag, QString path ) { + + if(flag == "log") + path += "/var/log"; + else + if (flag == "DB") + path += "/var/lib/global_db"; + else + if (flag == "chain") + path += "/var/lib/network"; + else + path += ""; + + QDir currentFolder( path ); + + quint64 totalsize = 0; + + currentFolder.setFilter( QDir::Dirs | QDir::Files | QDir::NoSymLinks ); + currentFolder.setSorting( QDir::Name ); + + QFileInfoList folderitems( currentFolder.entryInfoList() ); + + foreach ( QFileInfo i, folderitems ) { + QString iname( i.fileName() ); + if ( iname == "." || iname == ".." || iname.isEmpty() ) + continue; + if(flag == "log" && i.suffix() != "log" && !i.isDir()) + continue; + else + if(flag == "DB" && (i.suffix() != "dat" && !i.suffix().isEmpty()) && !i.isDir()) + continue; + else + if(flag == "chain" && i.suffix() != "dchaincell" && !i.isDir()) + continue; + + if ( i.isDir() ) + totalsize += get_file_size("", path+"/"+iname); + else + totalsize += i.size(); + } + + return totalsize; +} + +QString AbstractDiagnostic::get_memory_string(long num) +{ + QString result = QString::number(num); + return result; + + int gb = (num / 1024) / 1024; + int mb = (num-gb*1024*1024) /1024; + int kb = (num - (gb*1024*1024+mb*1024)); + if (gb > 0) + result = QString::number(gb) + QString(" Gb "); + else + result = QString(""); + if (mb > 0) + result += QString::number(mb) + QString(" Mb "); + if (kb > 0) + result += QString::number(kb) + QString(" Kb "); + + return result; +} + +QJsonObject AbstractDiagnostic::roles_processing() +{ + QJsonObject rolesObject; + + QDir currentFolder("/opt/cellframe-node/etc/network"); + + currentFolder.setFilter( QDir::Dirs | QDir::Files | QDir::NoSymLinks ); + currentFolder.setSorting( QDir::Name ); + + QFileInfoList folderitems( currentFolder.entryInfoList() ); + + foreach ( QFileInfo i, folderitems ) { + QString iname( i.fileName() ); + if ( iname == "." || iname == ".." || iname.isEmpty() ) + continue; + if(i.suffix() == "cfg" && !i.isDir()) + { + QSettings config(i.absoluteFilePath(), QSettings::IniFormat); + + rolesObject.insert(i.completeBaseName(), config.value("node-role", "unknown").toString()); + } + } + + return rolesObject; +} diff --git a/diagtool/AbstractDiagnostic.h b/diagtool/AbstractDiagnostic.h new file mode 100644 index 0000000000000000000000000000000000000000..b02714602357c97629fd4a3cf71588d3c5c8e407 --- /dev/null +++ b/diagtool/AbstractDiagnostic.h @@ -0,0 +1,52 @@ +#ifndef ABSTRACTDIAGNOSTIC_H +#define ABSTRACTDIAGNOSTIC_H + +#include <QObject> +#include "qtimer.h" +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonArray> +#include <QDebug> +#include <QUrl> +#include <QFileInfo> +#include <QCoreApplication> +#include <QProcess> +#include <QString> +#include <QRegularExpression> +#include <QSettings> + +#include <QDir> +#include <QNetworkInterface> + +class AbstractDiagnostic : public QObject +{ + Q_OBJECT +public: + explicit AbstractDiagnostic(QObject * parent = nullptr); + ~AbstractDiagnostic(); + +public: + void start_diagnostic(); + void stop_diagnostic(); + void set_timeout(int timeout); + quint64 get_file_size(QString flag, QString path); + QString get_uptime_string(long sec); + QString get_memory_string(long num); + QJsonValue get_mac(); + + QJsonDocument get_full_info(){return s_full_info;}; + + QJsonObject roles_processing(); + +public: + QTimer * s_timer_update; + int s_timeout{1000}; + QJsonDocument s_full_info; + QJsonValue s_mac; + +signals: + void data_updated(QJsonDocument); + +}; + +#endif // ABSTRACTDIAGNOSTIC_H diff --git a/diagtool/CMakeLists.txt b/diagtool/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..76aea61926b58360c6856d9b1ddf10d8220acb9e --- /dev/null +++ b/diagtool/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.10) + +project(cellframe-diagtool) + +find_package(Qt5 5.15 REQUIRED COMPONENTS + Core + Network +) + +set(CMAKE_AUTOMOC ON) + +add_executable(${PROJECT_NAME} + main.cpp + DiagnosticWorker.cpp + AbstractDiagnostic.cpp + LinuxDiagnostic.cpp +) + +target_link_libraries(${PROJECT_NAME} + Qt5::Core Qt5::Network +) + diff --git a/diagtool/DiagnosticWorker.cpp b/diagtool/DiagnosticWorker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba1f9a385a6d33d4bd70d54abf319d0baa3ee84e --- /dev/null +++ b/diagtool/DiagnosticWorker.cpp @@ -0,0 +1,88 @@ +#include "DiagnosticWorker.h" + +static QString group = "global.users.statistic"; + +DiagnosticWorker::DiagnosticWorker(QObject * parent) + : QObject{parent} +{ + s_uptime_timer = new QTimer(this); + connect(s_uptime_timer, &QTimer::timeout, + this, &DiagnosticWorker::slot_uptime, + Qt::QueuedConnection); + s_uptime_timer->start(1000); + + s_elapsed_timer = new QElapsedTimer(); + s_elapsed_timer->start(); + + m_diagnostic = new LinuxDiagnostic(); + + connect(m_diagnostic, &AbstractDiagnostic::data_updated, + this, &DiagnosticWorker::slot_diagnostic_data, + Qt::QueuedConnection); + + m_diagnostic->start_diagnostic(); + m_diagnostic->set_timeout(30000); +} +DiagnosticWorker::~DiagnosticWorker() +{ + delete s_uptime_timer; + delete s_elapsed_timer; + delete m_diagnostic; +} + +void DiagnosticWorker::slot_diagnostic_data(QJsonDocument data) +{ + //insert uptime dashboard and more into system info + QJsonObject obj = data.object(); + QJsonObject system = data["system"].toObject(); + QJsonObject proc = data["process"].toObject(); + + system.insert("uptime_dashboard", s_uptime); + system.insert("time_update_unix", QDateTime::currentSecsSinceEpoch()); + +// system.insert("time_update", QDateTime::currentDateTime().toString("dd.MM.yyyy hh:mm")); + obj.insert("system",system); + + if(proc["status"].toString() == "Offline") //if node offline - clear version + m_node_version = ""; + else + if(m_node_version.isEmpty()) + { + QProcess proc; + proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"), QStringList()<<"version"); + proc.waitForFinished(5000); + QString result = proc.readAll(); + if(result.contains("version")) + { + result = result.split("version")[1]; + m_node_version = result.split('\n', QString::SkipEmptyParts).first().trimmed(); + } + } + + proc.insert("version", m_node_version); + obj.insert("process",proc); + data.setObject(obj); + + write_data(data); +} + +void DiagnosticWorker::slot_uptime() +{ + s_uptime = m_diagnostic->get_uptime_string(s_elapsed_timer->elapsed()/1000); +} + +void DiagnosticWorker::write_data(QJsonDocument data) +{ + QString key = m_diagnostic->s_mac.toString(); + + QProcess proc; + QString program = "/opt/cellframe-node/bin/cellframe-node-cli"; + QStringList arguments; + arguments << "global_db" << "write" << "-group" << QString(group) + << "-key" << QString(key) << "-value" << QByteArray(data.toJson()); + proc.start(program, arguments); + proc.waitForFinished(5000); + QString res = proc.readAll(); + + qDebug()<<res; +} diff --git a/diagtool/DiagnosticWorker.h b/diagtool/DiagnosticWorker.h new file mode 100644 index 0000000000000000000000000000000000000000..01341f29c0a589d448adb3fffa35535f7cb563ec --- /dev/null +++ b/diagtool/DiagnosticWorker.h @@ -0,0 +1,41 @@ +#ifndef DIAGNOSTICWORKER_H +#define DIAGNOSTICWORKER_H + +#include <QObject> +#include <QTimer> +#include <QElapsedTimer> +#include <QSettings> + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "LinuxDiagnostic.h" + + +class DiagnosticWorker : public QObject +{ + Q_OBJECT +public: + explicit DiagnosticWorker(QObject *parent = nullptr); + ~DiagnosticWorker(); + +private: + QString m_node_version{""}; + QSettings m_settings; + AbstractDiagnostic* m_diagnostic; + + QTimer *s_uptime_timer; + QElapsedTimer *s_elapsed_timer; + QString s_uptime{"00:00:00"}; + +private slots: + void slot_diagnostic_data(QJsonDocument); + void slot_uptime(); + +private: + void write_data(QJsonDocument); + +}; + +#endif // DIAGNOSTICWORKER_H diff --git a/diagtool/LinuxDiagnostic.cpp b/diagtool/LinuxDiagnostic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..40528801d64be54f05ca85b1f5352c4e7e5a8cb3 --- /dev/null +++ b/diagtool/LinuxDiagnostic.cpp @@ -0,0 +1,509 @@ +#include "LinuxDiagnostic.h" +#include <sys/vfs.h> +#include <sys/stat.h> + +LinuxDiagnostic::LinuxDiagnostic(AbstractDiagnostic *parent) + : AbstractDiagnostic{parent} +{ + connect(s_timer_update, &QTimer::timeout, + this, &LinuxDiagnostic::info_update, + Qt::QueuedConnection); +} + + +void LinuxDiagnostic::info_update(){ + + // QSettings config("/opt/cellframe-node/etc/cellframe-node.cfg", + // QSettings::IniFormat); + + // bool isEnabled = config.value("Diagnostic/enabled",false).toBool(); + + if(true) //isEnabled + { + QJsonObject proc_info; + QJsonObject sys_info; + QJsonObject cli_info; + QJsonObject full_info; + + sys_info = get_sys_info(); + sys_info.insert("mac", s_mac); + sys_info.insert("disk", get_disk_info()); + + QFile file("/opt/cellframe-node/etc/diagdata.json"); + if(file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &err); + file.close(); + + if(err.error == QJsonParseError::NoError && !doc.isEmpty()) + sys_info.insert("data", doc.object()); + else + qWarning()<<err.errorString(); + } + + QJsonObject obj = sys_info["memory"].toObject(); + int mem = obj["total"].toInt(); + + proc_info = get_process_info(get_pid(), mem); + proc_info.insert("roles", roles_processing()); + + if(s_node_status) + { + cli_info = get_cli_info(); + full_info.insert("cli_data", cli_info); + } + + + full_info.insert("system", sys_info); + full_info.insert("process", proc_info); + + s_full_info.setObject(full_info); + + emit data_updated(s_full_info); + } +} + +QJsonObject LinuxDiagnostic::get_disk_info() +{ + QString path = "/opt/cellframe-node"; + + QString apath = QDir(path).absolutePath(); + + struct statfs stfs; + + if (::statfs(apath.toLocal8Bit(),&stfs) == -1) + { + QJsonObject diskObj; + diskObj.insert("total", -1); + diskObj.insert("free", -1); + diskObj.insert("available", -1); + diskObj.insert("used", -1); + + return diskObj; + } + + quint64 total = stfs.f_blocks * stfs.f_bsize; + quint64 free = stfs.f_bfree * stfs.f_bsize; + quint64 available = stfs.f_bavail * stfs.f_bsize; + quint64 used = total - free; + + QJsonObject diskObj; + diskObj.insert("total", QString::number(total)); + diskObj.insert("free", QString::number(free)); + diskObj.insert("available", QString::number(available)); + diskObj.insert("used", QString::number(used)); + + return diskObj; +} + + +long LinuxDiagnostic::get_pid() +{ + long pid; + QString path = QString("%1/var/run/cellframe-node.pid").arg("/opt/cellframe-node/"); + + QFile file(path); + QByteArray data; + if (!file.open(QIODevice::ReadOnly)) + return 0; + data = file.readAll(); + pid = data.toLong(); + + return pid; +} + +/// --------------------------------------------------------------- +/// Sys info +/// --------------------------------------------------------------- +QJsonObject LinuxDiagnostic::get_sys_info() +{ + QJsonObject obj_sys_data, obj_cpu, obj_memory; + + ifstream stat_stream; + string buff; + + //------- + + //get cpu info + size_t idle_time, total_time; + get_cpu_times(idle_time, total_time); + + const float idle_time_delta = idle_time - previous_idle_time; + const float total_time_delta = total_time - previous_total_time; + const float utilization = 100.0 * (1.0 - idle_time_delta / total_time_delta); + + previous_idle_time = idle_time; + previous_total_time = total_time; + + stat_stream.open("/proc/cpuinfo"); + for(int i = 0; i < 16;i++) stat_stream >> buff; + getline(stat_stream,buff); + + obj_cpu.insert("load", (int)utilization); + obj_cpu.insert("model", QString::fromStdString(buff)); + stat_stream.close(); + + //get uptime system + stat_stream.open("/proc/uptime"); + stat_stream >> buff; + QString uptime = get_uptime_string(atoi(buff.c_str())); + stat_stream.close(); + + //get memory data + stat_stream.open("/proc/meminfo"); + QString memory, memory_used, memory_free; + string total,free,available; + stat_stream >> buff >> total >> buff >> buff >> free >> buff >> buff >> available; + stat_stream.close(); + + int total_value = atoi(total.c_str()); + int available_value = atoi(available.c_str()); + + memory = get_memory_string(total_value); + memory_used = QString::number((total_value - available_value) *100 / total_value); + memory_free = get_memory_string(available_value); + +// obj_memory.insert("total", memory); + obj_memory.insert("total", total_value); +// obj_memory.insert("total_value", total_value); + obj_memory.insert("free", memory_free); + obj_memory.insert("load", memory_used); + + //------- + + obj_sys_data.insert("uptime", uptime); + obj_sys_data.insert("CPU", obj_cpu); + obj_sys_data.insert("memory", obj_memory); + + return obj_sys_data; +} + +QString LinuxDiagnostic::get_running(char* pid) +{ + char tbuf[32]; + char *cp; + int fd; + char c; + + sprintf(tbuf, "/proc/%s/stat", pid); + fd = open(tbuf, O_RDONLY, 0); + if (fd == -1) return QString("no open"); + + memset(tbuf, '\0', sizeof tbuf); // didn't feel like checking read() + read(fd, tbuf, sizeof tbuf - 1); + + cp = strrchr(tbuf, ')'); + if(!cp) return QString("no read"); + + c = cp[2]; + close(fd); + qDebug()<<c; + + if (c=='R') { + return "running"; + }else + if (c=='D') { + return "blocked"; + } + return QString("blocked"); +} + + +QString LinuxDiagnostic::get_proc_path(long pid) // work with root +{ + char exePath[PATH_MAX]; + char arg1[20]; + sprintf( arg1, "/proc/%ld/exe", pid ); + ssize_t len = ::readlink(arg1, exePath, sizeof(exePath)); + if (len == -1 || len == sizeof(exePath)) + len = 0; + exePath[len] = '\0'; + return QString::fromUtf8(exePath); +} + +std::vector<size_t> LinuxDiagnostic::get_cpu_times() { + std::ifstream proc_stat("/proc/stat"); + proc_stat.ignore(5, ' '); // Skip the 'cpu' prefix. + std::vector<size_t> times; + for (size_t time; proc_stat >> time; times.push_back(time)); + return times; +} + +bool LinuxDiagnostic::get_cpu_times(size_t &idle_time, size_t &total_time) { + const std::vector<size_t> cpu_times = get_cpu_times(); + if (cpu_times.size() < 4) + return false; + idle_time = cpu_times[3]; + total_time = std::accumulate(cpu_times.begin(), cpu_times.end(), 0); + return true; +} + + +/// --------------------------------------------------------------- +/// Process info +/// --------------------------------------------------------------- +QJsonObject LinuxDiagnostic::get_process_info(long proc_id, int totalRam) +{ + using std::ios_base; + using std::ifstream; + using std::string; + +// vm_usage = 0.0; +// resident_set = 0.0; + + // 'file' stat seems to give the most reliable results + char arg1[20]; + sprintf( arg1, "/proc/%ld/stat", proc_id ); + ifstream stat_stream(arg1,ios_base::in); + + // dummy vars for leading entries in stat that we don't care about + // + string pid, comm, state, ppid, pgrp, session, tty_nr; + string tpgid, flags, minflt, cminflt, majflt, cmajflt; + string utime, stime, cutime, cstime, priority, nice; + string O, itrealvalue; + + // the two fields we want + // + unsigned long vsize; + long rss; + double starttime; + + stat_stream >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr + >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt + >> utime >> stime >> cutime >> cstime >> priority >> nice + >> O >> itrealvalue >> starttime >> vsize >> rss; // don't care about the rest + + stat_stream.close(); + + long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages +// vm_usage = (vsize/1024.0); + long resident_set = rss * page_size_kb; + + int precentUseRss = (resident_set * 100) / totalRam; + + QProcess proc; + QString program = "ps"; + QStringList arguments; + arguments << "-p" << pid.c_str() << "-o" << "etimes"; + proc.start(program, arguments); + proc.waitForFinished(1000); + QString res = proc.readAll(); + + static QRegularExpression rx(R"([0-9]+)"); + + QJsonObject process_info; + QString status = "Offline"; + process_info.insert("status", "Offline"); + + QString node_dir = "/opt/cellframe-node/"; + + QString log_size, db_size, chain_size; + log_size = get_memory_string(get_file_size("log", node_dir) / 1024); + db_size = get_memory_string(get_file_size("DB", node_dir) / 1024); + chain_size = get_memory_string(get_file_size("chain", node_dir) / 1024); + + if(log_size.isEmpty()) log_size = "0"; + if(db_size.isEmpty()) db_size = "0"; + if(chain_size.isEmpty()) chain_size = "0"; + + process_info.insert("log_size", log_size); + process_info.insert("DB_size", db_size); + process_info.insert("chain_size", chain_size); + + if(QString::fromLocal8Bit(comm.c_str()).contains("cellframe-node")) + { + int uptime_sec = rx.match(res).captured(0).toInt(); + + QString uptime= get_uptime_string(uptime_sec); + + QString memory_use_value = get_memory_string(resident_set); + +// process_info.insert("PPID",QString::fromLocal8Bit(ppid.c_str())); +// process_info.insert("priory",QString::fromLocal8Bit(priority.c_str())); +// process_info.insert("start_time",QString::number(((starttime/100)/60))); + process_info.insert("memory_use",precentUseRss); + process_info.insert("memory_use_value",memory_use_value); + process_info.insert("uptime",uptime); + process_info.insert("name","cellframe-node"); + + status = "Online"; + } + + if(status == "Offline") + { + process_info.insert("memory_use","0"); + process_info.insert("memory_use_value","0 Kb"); + process_info.insert("uptime","00:00:00"); + } + + s_node_status = status == "Online" ? true : false; + + process_info.insert("status", status); + + return process_info; +} + +/// --------------------------------------------------------------- +/// Cli info +/// --------------------------------------------------------------- +QJsonObject LinuxDiagnostic::get_cli_info() +{ + QStringList networks = get_networks(); + + QJsonObject netObj; + + for(QString net : networks) + { + QJsonObject dataObj; + + dataObj.insert("net_info", get_net_info(net)); + dataObj.insert("mempool" , get_mempool_count(net)); +// dataObj.insert("ledger" , get_ledger_count(net)); + dataObj.insert("blocks" , get_blocks_count(net)); + dataObj.insert("events" , get_events_count(net)); + + netObj.insert(net, dataObj); + } + + return netObj; +} + +QStringList LinuxDiagnostic::get_networks() +{ + QProcess proc; + proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"), QStringList()<<"net"<<"list"); + proc.waitForFinished(5000); + QString result = proc.readAll(); + + QStringList listNetworks; + result.remove(' '); + result.remove("\r"); + result.remove("\n"); + result.remove("Networks:"); + if(!(result.isEmpty() || result.isNull() || result.contains('\'') || result.contains("error") || result.contains("Error") || result.contains("err"))) + { + listNetworks = result.split("\t", QString::SkipEmptyParts); + } + + return listNetworks; +} + +QJsonObject LinuxDiagnostic::get_net_info(QString net) +{ + QProcess proc; + proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"), + QStringList()<<"net"<<"-net"<<QString(net)<<"get"<<"status"); + proc.waitForFinished(5000); + QString result = proc.readAll(); + + // ---------- State & TargetState ---------------- + + QRegularExpression rx(R"***(^Network "(\S+)" has state (\S+) \(target state (\S*)\), .*cur node address ([A-F0-9]{4}::[A-F0-9]{4}::[A-F0-9]{4}::[A-F0-9]{4}))***"); + QRegularExpressionMatch match = rx.match(result); + if (!match.hasMatch()) { + return {}; + } + + QJsonObject resultObj({ + {"state" , match.captured(2)}, + {"target_state" , match.captured(3)}, + {"node_address" , match.captured(4)} + }); + + // ---------- Links count ---------------- + QRegularExpression rxLinks(R"(\), active links (\d+) from (\d+),)"); + match = rxLinks.match(result); + if (!match.hasMatch()) { + return resultObj; + } + + resultObj.insert("active_links_count", match.captured(1)); + resultObj.insert("links_count" , match.captured(2)); + + return resultObj; +} + +QJsonObject LinuxDiagnostic::get_mempool_count(QString net) +{ + QProcess proc; + proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"), + QStringList()<<"mempool_list"<<"-net"<<QString(net)); + proc.waitForFinished(5000); + QString result = proc.readAll(); + + QRegularExpression rx(R"(\.(.+): Total (.+) records)"); + + ///TODO: bug in requests. Always returns both chains +// QRegularExpressionMatch match = rx.match(result); +// if (!match.hasMatch()) { +// return {}; +// } + +// proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"), +// QStringList()<<"mempool_list"<<"-net"<<QString(net)<<"-chain"<<"zero"); +// proc.waitForFinished(5000); +// result = proc.readAll(); + + QJsonObject resultObj; + + QRegularExpressionMatchIterator matchItr = rx.globalMatch(result); + + while (matchItr.hasNext()) + { + QRegularExpressionMatch match = matchItr.next(); + resultObj.insert(match.captured(1), match.captured(2)); + } + + return resultObj; +} + +QJsonObject LinuxDiagnostic::get_ledger_count(QString net) +{ + //TODO: legder tx -all -net NOT WORKING + return {}; +} + +QJsonObject LinuxDiagnostic::get_blocks_count(QString net) +{ + QProcess proc; + proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"), + QStringList()<<"block"<<"list"<<"-net"<<QString(net) <<"-chain" << "main"); + proc.waitForFinished(5000); + QString result = proc.readAll(); + + QRegularExpression rx(R"(\.(.+): Have (.+) blocks)"); + QRegularExpressionMatch match = rx.match(result); + if (!match.hasMatch()) { + return {}; + } + + QJsonObject resultObj; + resultObj.insert(match.captured(1), match.captured(2)); + + return resultObj; + +} + +QJsonObject LinuxDiagnostic::get_events_count(QString net) +{ + QProcess proc; + proc.start(QString("/opt/cellframe-node/bin/cellframe-node-cli"), + QStringList()<<"dag"<<"event"<<"list"<<"-net"<<QString(net) <<"-chain" << "zerochain"); + proc.waitForFinished(5000); + QString result = proc.readAll(); + + QRegularExpression rx(R"(\.(.+) have total (.+) events)"); + QRegularExpressionMatch match = rx.match(result); + if (!match.hasMatch()) { + return {}; + } + + QJsonObject resultObj; + resultObj.insert(match.captured(1), match.captured(2)); + + return resultObj; +} + + diff --git a/diagtool/LinuxDiagnostic.h b/diagtool/LinuxDiagnostic.h new file mode 100644 index 0000000000000000000000000000000000000000..4258138a0ee21329901afb5330926648a153a6c7 --- /dev/null +++ b/diagtool/LinuxDiagnostic.h @@ -0,0 +1,63 @@ +#ifndef LINUXDIAGNOSTIC_H +#define LINUXDIAGNOSTIC_H + + +#include "AbstractDiagnostic.h" +#include <unistd.h> +#include <fcntl.h> +#include <vector> + +#include <fstream> +#include <numeric> +#include <vector> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <locale.h> +#include <signal.h> +#include <dirent.h> + +#include <sys/sysinfo.h> + +using namespace std; + + +class LinuxDiagnostic : public AbstractDiagnostic +{ + Q_OBJECT +public: + explicit LinuxDiagnostic(AbstractDiagnostic * parent = nullptr); + +private: + QJsonObject get_sys_info(); + bool get_cpu_times(size_t &idle_time, size_t &total_time); + std::vector<size_t> get_cpu_times(); + long get_pid(); + QString get_running(char* pid); + QString get_proc_path(long pid); + QJsonObject get_process_info(long pid, int totalRam); + QJsonObject get_disk_info(); + QJsonObject get_cli_info(); + + + //CLI + + QStringList get_networks(); + QJsonObject get_net_info(QString net); + QJsonObject get_mempool_count(QString net); + QJsonObject get_ledger_count(QString net); + QJsonObject get_blocks_count(QString net); + QJsonObject get_events_count(QString net); + +private slots: + void info_update(); + +private: + size_t previous_idle_time{0}, previous_total_time{0}; + bool s_node_status{false}; + +}; + +#endif // LINUXDIAGNOSTIC_H diff --git a/diagtool/NodeDiagnostic.pro b/diagtool/NodeDiagnostic.pro new file mode 100644 index 0000000000000000000000000000000000000000..828b78ba9bb68f21463c9b012dd5a58b5ea43644 --- /dev/null +++ b/diagtool/NodeDiagnostic.pro @@ -0,0 +1,25 @@ +QT -= gui +QT += core network + +CONFIG += c++17 console +CONFIG -= app_bundle + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + AbstractDiagnostic.cpp \ + DiagnosticWorker.cpp \ + LinuxDiagnostic.cpp \ + main.cpp + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +HEADERS += \ + AbstractDiagnostic.h \ + DiagnosticWorker.h \ + LinuxDiagnostic.h diff --git a/diagtool/main.cpp b/diagtool/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1c0a7638c198ee4a821d11dc7f0839014f322597 --- /dev/null +++ b/diagtool/main.cpp @@ -0,0 +1,11 @@ +#include <QCoreApplication> +#include "DiagnosticWorker.h" + +int main(int argc, char *argv[]) +{ + qSetMessagePattern("%{type} %{if-category}%{category}: %{endif}%{function}: %{message}"); + QCoreApplication a(argc, argv); + DiagnosticWorker * wrkr = new DiagnosticWorker(); + + return a.exec(); +} diff --git a/dist.linux/share/cellframe-diagtool.service b/dist.linux/share/cellframe-diagtool.service new file mode 100644 index 0000000000000000000000000000000000000000..f88e370fc1f98e97e834aca9a855a1c2fba292ea --- /dev/null +++ b/dist.linux/share/cellframe-diagtool.service @@ -0,0 +1,17 @@ +[Unit] +Description=Cellframe DiagTool +After=network.target + +[Service] +WorkingDirectory=/opt/cellframe-node/ +ExecStart=/opt/cellframe-node/bin/cellframe-diagtool & +ExecStop=/bin/kill -SIGTERM $MAINPID +Restart=always +User=root +Group=root +RestartSec=10 +LogNamespace=cellframe +CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_IPC_LOCK CAP_KILL CAP_LEASE CAP_MKNOD CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW CAP_SYS_NICE CAP_SYS_RAWIO CAP_SYSLOG CAP_WAKE_ALARM CAP_SYS_RESOURCE CAP_DAC_READ_SEARCH + +[Install] +WantedBy=multi-user.target diff --git a/os/debian/postinst b/os/debian/postinst index 222cd3e85ba2fb067217ff85e93ce071ae5c2255..244613ab2ad1e27f5433c7af77aa5a17f73f106d 100755 --- a/os/debian/postinst +++ b/os/debian/postinst @@ -244,11 +244,33 @@ else chmod 0664 $filename 2>/dev/null || true fi done + + #diagtool service + if [ -f "$DAP_PREFIX/bin/cellframe-diagtool" ]; then + if [ -e /etc/systemd/system/cellframe-diagtool.service ]; then + echo "[*] Restarting cellframe-diagtool service" + systemctl --system stop cellframe-diagtool >> /dev/null|| true + echo "[*] Stopped cellframe-diagtool" + systemctl daemon-reload || true + systemctl --system start cellframe-diagtool || true + echo "[*] Started cellframe-diagtool" + else + echo "[!] Installing cellframe-diagtool as systemd service" + ln -sf $DAP_PREFIX/share/cellframe-diagtool.service /etc/systemd/system/cellframe-diagtool.service || true + systemctl --system enable $DAP_PREFIX/share/cellframe-diagtool.service || true + fi + fi + chmod 0666 $DAP_CFG chmod 0666 $DAP_CFG_TPL chmod 0774 $DAP_PREFIX/bin/* || true chmod 0777 $DAP_PREFIX/bin/$DAP_APP_NAME-cli $DAP_PREFIX/bin/pip3* $DAP_PREFIX/bin/python3* || true + #set rwo permissions to configs + chmod 666 $(find ${DAP_PREFIX}/etc/ -type f) + #set rwx permissions to dirs + chmod 777 $(find ${DAP_PREFIX}/etc/ -type d) + service cellframe-node start || true systemctl restart cellframe-node || true diff --git a/version.mk b/version.mk index 9e74273e8118a983877f84ffd0626cb74cb4cdf5..b5583b90a9fe3440c9b768e36892af4b5684b28f 100644 --- a/version.mk +++ b/version.mk @@ -1,3 +1,3 @@ VERSION_MAJOR=5 VERSION_MINOR=2 -VERSION_PATCH=116 +VERSION_PATCH=117