add server group page (basic so far)
This commit is contained in:
parent
5945f18c75
commit
f40cda49d5
|
@ -23,6 +23,9 @@ set(PROJECT_SOURCES
|
|||
mainwindow.cpp
|
||||
mainwindow.h
|
||||
mainwindow.ui
|
||||
dedicatedservergroupwidget.h
|
||||
dedicatedservergroupwidget.cpp
|
||||
dedicatedservergroupwidget.ui
|
||||
dedicatedserverinfowidget.cpp
|
||||
dedicatedserverinfowidget.h
|
||||
dedicatedserverinfowidget.ui
|
||||
|
|
|
@ -1,6 +1,40 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="fr_FR">
|
||||
<context>
|
||||
<name>DedicatedServerGroupWidget</name>
|
||||
<message>
|
||||
<location filename="dedicatedservergroupwidget.ui" line="14"/>
|
||||
<source>Form</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="dedicatedservergroupwidget.ui" line="22"/>
|
||||
<source>Total monthly cost</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="dedicatedservergroupwidget.ui" line="35"/>
|
||||
<location filename="dedicatedservergroupwidget.ui" line="53"/>
|
||||
<source>...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="dedicatedservergroupwidget.cpp" line="27"/>
|
||||
<source>Group %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="dedicatedservergroupwidget.cpp" line="55"/>
|
||||
<source>(incomplete!) %1 %2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="dedicatedservergroupwidget.cpp" line="56"/>
|
||||
<source>For no good reason, OVH sometimes doesn't provide pricing information, sorry about that</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>DedicatedServerInfoWidget</name>
|
||||
<message>
|
||||
|
@ -286,115 +320,120 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="90"/>
|
||||
<location filename="mainwindow.cpp" line="94"/>
|
||||
<source>Expired token</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="90"/>
|
||||
<location filename="mainwindow.cpp" line="94"/>
|
||||
<source>It seems this token is expired. Please login again to continue.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="148"/>
|
||||
<location filename="mainwindow.cpp" line="153"/>
|
||||
<source>Validate credential</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="148"/>
|
||||
<location filename="mainwindow.cpp" line="153"/>
|
||||
<source>Have you validated the OVH credential request?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="190"/>
|
||||
<location filename="mainwindow.cpp" line="209"/>
|
||||
<source>Group %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="225"/>
|
||||
<source>Info %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="205"/>
|
||||
<location filename="mainwindow.cpp" line="240"/>
|
||||
<source>IPMI not activated</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="205"/>
|
||||
<location filename="mainwindow.cpp" line="240"/>
|
||||
<source>IPMI is not activated on this server</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="214"/>
|
||||
<location filename="mainwindow.cpp" line="249"/>
|
||||
<source>IPMI KVM not available</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="214"/>
|
||||
<location filename="mainwindow.cpp" line="249"/>
|
||||
<source>IPMI KVM is not available on this server</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="238"/>
|
||||
<location filename="mainwindow.cpp" line="273"/>
|
||||
<source>Waiting for task... %1</source>
|
||||
<oldsource>Waiting for task...</oldsource>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="258"/>
|
||||
<location filename="mainwindow.cpp" line="293"/>
|
||||
<source>KVM %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="267"/>
|
||||
<location filename="mainwindow.cpp" line="269"/>
|
||||
<location filename="mainwindow.cpp" line="302"/>
|
||||
<location filename="mainwindow.cpp" line="304"/>
|
||||
<source>JNLP KVM</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="267"/>
|
||||
<location filename="mainwindow.cpp" line="302"/>
|
||||
<source>This server only supports a Java WebStart KVM, it will thus not be embedded in this window.
|
||||
Java WebStart should be starting right now, wait until it finishes loading to close this dialog.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="269"/>
|
||||
<location filename="mainwindow.cpp" line="304"/>
|
||||
<source>Failed to launch javaws. Make sure it is properly installed.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="368"/>
|
||||
<location filename="mainwindow.cpp" line="403"/>
|
||||
<source>Target archive</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="368"/>
|
||||
<location filename="mainwindow.cpp" line="403"/>
|
||||
<source>Zip files (*.zip)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="372"/>
|
||||
<location filename="mainwindow.cpp" line="407"/>
|
||||
<source>OVH...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="372"/>
|
||||
<location filename="mainwindow.cpp" line="407"/>
|
||||
<source>OVH decided that getting a bill require a 'true' login. I'm going to show you a web browser for that.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="383"/>
|
||||
<location filename="mainwindow.cpp" line="418"/>
|
||||
<source>Failed to open archive</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="383"/>
|
||||
<location filename="mainwindow.cpp" line="418"/>
|
||||
<source>Failed to open archive...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="mainwindow.cpp" line="474"/>
|
||||
<location filename="mainwindow.cpp" line="510"/>
|
||||
<source>Logins cleaned up</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="mainwindow.cpp" line="474"/>
|
||||
<location filename="mainwindow.cpp" line="510"/>
|
||||
<source>%n login(s) purged</source>
|
||||
<translation type="unfinished">
|
||||
<numerusform></numerusform>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
#include "dedicatedservergroupwidget.h"
|
||||
#include "ui_dedicatedservergroupwidget.h"
|
||||
#include <QTimer>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include "ovhapi.h"
|
||||
|
||||
DedicatedServerGroupWidget::DedicatedServerGroupWidget(OvhApi *api, const QString &groupName, const QStringList &servers, QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::DedicatedServerGroupWidget),
|
||||
api(api),
|
||||
_groupName(groupName),
|
||||
_servers(servers)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
QTimer::singleShot(0, this, &DedicatedServerGroupWidget::fetchOvhData);
|
||||
}
|
||||
|
||||
DedicatedServerGroupWidget::~DedicatedServerGroupWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QCoro::Task<> DedicatedServerGroupWidget::fetchOvhData()
|
||||
{
|
||||
ui->groupLabel->setText(tr("Group %1").arg(_groupName));
|
||||
double totalMonthlyCost = 0;
|
||||
bool incompletePricing = false;
|
||||
QString currency;
|
||||
for (auto &&server: _servers) {
|
||||
qDebug() << server;
|
||||
|
||||
// Calculate monthly cost, using the /services API
|
||||
auto serviceInfo = (co_await api->get(QString("/dedicated/server/%1/serviceInfos").arg(server), 1h)).object();
|
||||
auto serviceId = serviceInfo["serviceId"].toInt();
|
||||
auto servicesData = (co_await api->get(QString("/services/%1").arg(serviceId), 6h)).object();
|
||||
auto billingData = servicesData["billing"].toObject();
|
||||
auto pricingData = billingData["pricing"].toObject();
|
||||
|
||||
double totalCost = pricingData["price"].toObject()["value"].toDouble();
|
||||
totalCost = totalCost / pricingData["interval"].toInt();
|
||||
|
||||
if (billingData["pricing"].isNull()) {
|
||||
incompletePricing = true;
|
||||
} else {
|
||||
qDebug() << pricingData["price"] << pricingData["interval"];
|
||||
if (currency.isEmpty())
|
||||
currency = pricingData["price"].toObject()["currency"].toString();
|
||||
// TODO: else ??
|
||||
totalMonthlyCost += totalCost;
|
||||
}
|
||||
}
|
||||
if (incompletePricing) {
|
||||
ui->monthlyCost->setText(tr("(incomplete!) %1 %2").arg(totalMonthlyCost).arg(currency));
|
||||
ui->monthlyCost->setToolTip(tr("For no good reason, OVH sometimes doesn't provide pricing information, sorry about that"));
|
||||
} else {
|
||||
ui->monthlyCost->setText(QString("%1 %2").arg(totalMonthlyCost).arg(currency));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef DEDICATEDSERVERGROUPWIDGET_H
|
||||
#define DEDICATEDSERVERGROUPWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <QCoroTask>
|
||||
|
||||
namespace Ui {
|
||||
class DedicatedServerGroupWidget;
|
||||
}
|
||||
|
||||
class OvhApi;
|
||||
|
||||
class DedicatedServerGroupWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DedicatedServerGroupWidget(OvhApi *api, const QString &groupName, const QStringList &servers, QWidget *parent = nullptr);
|
||||
~DedicatedServerGroupWidget();
|
||||
QString groupName() const { return _groupName; }
|
||||
private slots:
|
||||
QCoro::Task<> fetchOvhData();
|
||||
private:
|
||||
Ui::DedicatedServerGroupWidget *ui;
|
||||
OvhApi *api;
|
||||
QString _groupName;
|
||||
QStringList _servers;
|
||||
};
|
||||
|
||||
#endif // DEDICATEDSERVERGROUPWIDGET_H
|
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DedicatedServerGroupWidget</class>
|
||||
<widget class="QWidget" name="DedicatedServerGroupWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Total monthly cost</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="monthlyCost">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="groupLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -24,11 +24,15 @@
|
|||
#include <KArchive>
|
||||
#include <KZip>
|
||||
#include "servicesview.h"
|
||||
#include "dedicatedservergroupwidget.h"
|
||||
#include "dedicatedserverinfowidget.h"
|
||||
#include "ovhwebauthentication.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
const auto ServerGroup = Qt::UserRole + 1;
|
||||
const auto ServerName = Qt::UserRole + 2;
|
||||
|
||||
const auto ten_years = 87600h;
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
|
@ -107,7 +111,7 @@ QCoro::Task<> MainWindow::login(const QString &ck) {
|
|||
}
|
||||
}
|
||||
|
||||
QCoro::Task<> MainWindow::loadServerInfo(QString server) {
|
||||
QCoro::Task<> MainWindow::loadServerInfo(const QString &server) {
|
||||
qDebug() << "Loading for " << server;
|
||||
auto serverInfo = (co_await api->get(QString("/dedicated/server/") + server, 1h)).object();
|
||||
auto displayName = serverInfo["reverse"].toString();
|
||||
|
@ -120,6 +124,7 @@ QCoro::Task<> MainWindow::loadServerInfo(QString server) {
|
|||
if (!topLevelNodes.contains(serverDomain)) {
|
||||
topDomain = new QTreeWidgetItem(ui->serverList);
|
||||
topDomain->setText(0, serverDomain);
|
||||
topDomain->setData(0, ServerGroup, serverDomain);
|
||||
ui->serverList->addTopLevelItem(topDomain);
|
||||
topLevelNodes.insert(serverDomain, topDomain);
|
||||
} else {
|
||||
|
@ -127,7 +132,7 @@ QCoro::Task<> MainWindow::loadServerInfo(QString server) {
|
|||
}
|
||||
auto serverItem = new QTreeWidgetItem(topDomain);
|
||||
serverItem->setText(0, serverInfo["reverse"].toString());
|
||||
serverItem->setData(0, Qt::UserRole, server);
|
||||
serverItem->setData(0, ServerName, server);
|
||||
topDomain->addChild(serverItem);
|
||||
ui->serverList->sortItems(0, Qt::AscendingOrder);
|
||||
}
|
||||
|
@ -171,11 +176,41 @@ QCoro::Task<> MainWindow::on_actionLogin_triggered()
|
|||
}
|
||||
|
||||
|
||||
QCoro::Task<> MainWindow::on_serverList_itemClicked(QTreeWidgetItem *item, int column)
|
||||
QCoro::Task<> MainWindow::on_serverList_itemClicked(QTreeWidgetItem *item, [[maybe_unused]] int column)
|
||||
{
|
||||
auto serverName = item->data(0, Qt::UserRole).toString();
|
||||
if (serverName.isEmpty())
|
||||
auto serverName = item->data(0, ServerName).toString();
|
||||
if (serverName.isEmpty()) {
|
||||
// Maybe it's a server group
|
||||
auto groupName = item->data(0, ServerGroup).toString();
|
||||
if (groupName.isEmpty())
|
||||
co_return;
|
||||
|
||||
// Find existing tab
|
||||
for (int i = 0 ; i < ui->tabWidget->count() ; i++) {
|
||||
auto existingTab = ui->tabWidget->widget(i);
|
||||
auto existingGroupInfo = qobject_cast<DedicatedServerGroupWidget*>(existingTab);
|
||||
if (existingGroupInfo && existingGroupInfo->groupName() == groupName) {
|
||||
ui->tabWidget->setCurrentIndex(i);
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
// Build and fill a server group widget
|
||||
QStringList serverNames;
|
||||
for (int i = 0 ; i < item->childCount() ; i++) {
|
||||
auto node = item->child(i);
|
||||
serverName = node->data(0, ServerName).toString();
|
||||
if (!serverName.isEmpty())
|
||||
serverNames.append(serverName);
|
||||
}
|
||||
qDebug() << serverNames;
|
||||
|
||||
auto groupView = new DedicatedServerGroupWidget(api, groupName, serverNames, this);
|
||||
int tabId = ui->tabWidget->addTab(groupView, tr("Group %1").arg(groupName));
|
||||
ui->tabWidget->setCurrentIndex(tabId);
|
||||
|
||||
co_return;
|
||||
}
|
||||
auto serverReverse = item->text(0);
|
||||
|
||||
for (int i = 0 ; i < ui->tabWidget->count() ; i++) {
|
||||
|
@ -471,6 +506,7 @@ QCoro::Task<> MainWindow::on_actionPurgeLogins_triggered()
|
|||
validLoginActions.append(loginAction);
|
||||
}
|
||||
}
|
||||
// TODO : purge cache db
|
||||
QMessageBox::information(this, tr("Logins cleaned up"), tr("%n login(s) purged", nullptr, loginActions.size() - validLoginActions.size()));
|
||||
loginActions = validLoginActions;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ private slots:
|
|||
|
||||
void on_tabWidget_tabCloseRequested(int index);
|
||||
|
||||
QCoro::Task<> loadServerInfo(QString serverName);
|
||||
QCoro::Task<> loadServerInfo(const QString &serverName);
|
||||
|
||||
void loadKvm(const QString &name, const QString &kvmType, const QString &data);
|
||||
|
||||
|
|
|
@ -82,10 +82,10 @@ QCoro::Task<QByteArray> OvhApi::download(const QUrl &url)
|
|||
QCoro::Task<QJsonDocument> OvhApi::get(const QString &path, std::chrono::seconds cacheDuration, bool forceRefresh)
|
||||
{
|
||||
if (cacheDuration.count() > 0 && !forceRefresh) {
|
||||
qDebug() << "Gonna look at the cache";
|
||||
// qDebug() << "Gonna look at the cache";
|
||||
QSqlDatabase db = QSqlDatabase::database();
|
||||
if (db.isValid()) {
|
||||
qDebug() << "Cache check";
|
||||
// qDebug() << "Cache check";
|
||||
// Check from cache first
|
||||
auto cacheQuery = QSqlQuery{"SELECT data FROM cache WHERE ck=? AND path=? AND expiration>?", db};
|
||||
cacheQuery.bindValue(0, consumerKey);
|
||||
|
@ -110,7 +110,7 @@ QCoro::Task<QJsonDocument> OvhApi::get(const QString &path, std::chrono::seconds
|
|||
auto answer = QJsonDocument::fromJson(rawAnswer);
|
||||
|
||||
if (cacheDuration.count() > 0 || forceRefresh) {
|
||||
qDebug() << "Gonna store in the cache";
|
||||
// qDebug() << "Gonna store in the cache";
|
||||
QSqlDatabase db = QSqlDatabase::database();
|
||||
if (db.isValid()) {
|
||||
// Check from cache first
|
||||
|
|
Loading…
Reference in New Issue