Qt开发之路——基于RedfishAPI的服务器管理小应用

在这里插入图片描述

前言

本文介绍了近期完成的一个小项目——基于Redfish的服务器管理小应用。一方面记录一下自己的开发经历;另一方面本项目适用于准备学习样式表,信号槽,treeWidget,https请求,SQlite数据库的人。代码在最下方。

注:公司内部网络才可以访问redfish接口,项目的网络请求无法发送成功,所以关于https请求部分仅供参考,也可以参看Qt开发之路——Json解析过程中遇到的readAll()清除内存缓冲区问题~

关于ipmi

IMPI(智能平台管理接口)是一种嵌入式功能,同时也是工业标准,由英特尔与戴尔、惠普和NEC合作开发,可实现对服务器的远程控制。但是这种规范也有它的局限性,层出不穷的安全问题使得它自从2015年更新2.0后没有再翻新,与此同时Redfish兴起。

关于Redfish

Redfish是由分布式管理任务组(DMTF)发布的开放式行业标准规范,旨在对平台硬件进行现代化和安全的管理,是一种管理标准,在超媒体RESTful接口中使用数据模型表示。它是一个超媒体API,所以它能够通过一个一致的接口来表示各种实现。它有管理数据中心资源、处理事件、长期任务和发现的机制。初识Redfish

关于RedfishAPI

RedfishAPI代表了一种新的编程风格,它能够以一致的方式管理从超级规模到刀片服务器再到独立服务器的系统。

对于背景可以移步https://blog.csdn.net/asmartkiller/article/details/106558952作更细致的了解。

效果

在这里插入图片描述

开发环境

应用:Qt Creator 4.11.1(Community)
开发环境:MinGW_32_bit
数据库:SQlite

功能介绍

添加服务器:
在这里插入图片描述
初始化:删除所有服务器
帮助:简单显示服务器小助手的功能
右键刷新告警及开关机状态信息(再次请求系统信息和告警信息,即两个get请求):
在这里插入图片描述
右键删除服务器

技术

1、Qt样式表,信号槽
2、通过treewidget组件显示数据。
3、通过解析https请求到的JSON数据获取相对应的特征信息。
4、将数据存储在Qt自带的SQlite数据库中,方便读取和删除。

Qt样式表

因为程序比较小,且界面不多,所以样式表直接在设计器里实现的。
在这里插入图片描述
比如存储按钮的点击状态

QPushButton#save{
border-style:outset;
font: 75 16pt "宋体";
font-weight:bold; 
color:rgb(255, 170, 0);
background-color:rgba(225, 225, 225, 0);
}

QPushButton#save:hover{ 
background-color:rgba(225, 225, 225,200);
}
 
QPushButton#save:pressed{
background-color:rgba(225, 225, 225, 200);
}

信号槽

为了便于统一管理,将信号和槽写在main函数中。主要用于界面之间的切换和值传递。

 /* + function
    ui from widget to addid for adding servers
    */
    QObject::connect(w,SIGNAL(showAddId()),i,SLOT(receive_widget_addid()));
    /* save function
    ui from addid to widget for showing servers data
    */
    QObject::connect(i,SIGNAL(showLabel(QString,QString,QString,QString,QString,QString)),w,SLOT(receive_save(QString,QString,QString,QString,QString,QString)));
    /* cancel function
    ui from addid to widget for showing widget
    */
    QObject::connect(i,SIGNAL(showWidget()),w,SLOT(receive_addid_cancel()));
    /* show loading ui
    ui from addid to loading for showing waiting process
    */
    QObject::connect(i,SIGNAL(showLoading()),l,SLOT(receive_addid_loading()));
    /* close loading ui
    ui from addid to loading for showing widget ui
    */
    QObject::connect(i,SIGNAL(closeLoading()),l,SLOT(receive_addid_closeloading()));
    /* refresh realdata
    ui from widget to addid for refreshing realdata——powerstate and serveritynum
    */
    QObject::connect(w,SIGNAL(refresh_realtimedata(QString)),i,SLOT(receive_widget_refresh(QString)));
    /* refresh realdata
    ui from addid to widget for sending realdata——powerstate and serveritynum
    */
    QObject::connect(i,SIGNAL(send_realTimeData(QString,QString)),w,SLOT(receive_realTimeData(QString,QString)));

treeWidget的使用

添加一行

      QStringList strs;
      strs<<QString("******")<<QString("*********")<<QString(powerstate)<<QString(severity)<<QString("***************");

      //set the header to appropriate size
      QHeaderView *head=ui->treeWidget->header();
      head->setSectionResizeMode(QHeaderView::ResizeToContents);

      //add one line for treewidget,content is strs
      QTreeWidgetItem *strsroot = new QTreeWidgetItem(ui->treeWidget,strs);

      //set currentitem
      ui->treeWidget->setCurrentItem(strsroot);

删除一行

QTreeWidgetItem *item = ui->treeWidget->currentItem();
delete(item);

删除treewidget的所有数据

//delete the treewidget for ui
            ui->treeWidget->clear();

https请求及解析

https请求:QNetworkRequest、QNetworkAccessManager、QNetworkReply、QEventLoop

JSON解析:QJsonDocument、QJsonParseError、QJsonObject、QJsonArray、QJsonValue

主要涉及到对三种形式JSON数据的解析。涉及到3条url,这里记为url1,url2,url3。首先获取token,然后根据token和url2发送get请求先后获取产品名称,开关机状态,健康状态以及uuid。通过url3和token获取服务器告警信息。
url1及用户名密码发送post请求获取token:

QString AddId::postToken(QString id)
{
 	// new request object
    QNetworkRequest request;

    // ready for sending https request
    QSslConfiguration config;
    QSslConfiguration conf = request.sslConfiguration();
    conf.setPeerVerifyMode(QSslSocket::VerifyNone);
    conf.setProtocol(QSsl::TlsV1SslV3);
    request.setSslConfiguration(conf);
    request.setUrl(QUrl("https://www.baidu.com"));
    request.setUrl(QUrl(id));


    request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));

    //set raw header of request
    request.setRawHeader("Content-Type", "application/json");

    //check supported agreement
    qDebug()<< manager->supportedSchemes();//("ftp", "file", "qrc", "http", "https", "data")

    //obtain form data——username and password
    QString submitMsg = QString(R"(
                                {
                                  "UserName": "%1",
                                  "Password": "%2"
                                })").arg(ui->lineEdit_2->text()).arg(ui->lineEdit_3->text());

    QNetworkReply *reply=manager->post(request,submitMsg.toUtf8());
    //open a local event loop, then wait reply but do not stop thread(significant)

    QEventLoop eventLoop;
    connect(manager, &QNetworkAccessManager::finished, &eventLoop, &QEventLoop::quit);
    eventLoop.exec();

    /* for  \"X-Auth-Token\":
    analysis json data
    */
    QString token = analysisSessionsJson(reply);
	return token;

得到如下形式的JSON数据:

{
    "@odata.context": "/redfish/v1/$metadata#Session.Session",
    "@odata.id": "/redfish/v1/****/*********",
    "@odata.type": "#Session.*****.Session",
    "Id": "********",
    "Name": "User Session",
    "Description": "Manager User Session",
    "UserName": "****",
    "SessionType": "Redfish",
    "Oem": {
        "BMC": {
            "LoginTime": "2021-01-04T11:30:51+08:00\n",
            "ClientAddress": "****",
            "ServerAddress": "id",
            "SessionId": "*****",
            "EnabledHttps": true,
            "Role": "Administrator",
            "Location": "/redfish/v1/*****/*************",
            "X-Auth-Token": "lhe6Gqi4Lz2CA7rx15vc0IRNh22iz4Vb",
            "UserId": 2
        }
    }
}

解析获取认证X-Token

/*analysis SessionsJson for token
url1
*/
QString AddId::analysisSessionsJson(QNetworkReply *reply){
    //judge if format of the json is right
    if (reply->error() == QNetworkReply::NoError)
    {
        QByteArray bytes = reply->readAll(); //read all bytes
        QJsonParseError jsonError_login;

        //switch to json document
        QJsonDocument document = QJsonDocument::fromJson(bytes, &jsonError_login);

        //analysis Json  error
        if (document.isObject())
        {
            QJsonObject obj = document.object();
            if (obj.contains("Oem"))
            {
                QJsonObject object_value = obj.value("Oem").toObject();
                if (object_value.contains("BMC")){
                    QJsonObject object1_value = object_value.value("BMC").toObject();
                    if(object1_value.contains("X-Auth-Token"))
                    {
                        QString token_val = object1_value.value("X-Auth-Token").toString();
                        return token_val;
                    }
                }
            }
        }
    }
}

url2及token发送get请求获取系统信息:

QStringList AddId::getCriticalMsg(QString url,QString token)
{
    QNetworkRequest request;

    QSslConfiguration config;
    QSslConfiguration conf = request.sslConfiguration();
    conf.setPeerVerifyMode(QSslSocket::VerifyNone);
    conf.setProtocol(QSsl::TlsV1SslV3);
    request.setSslConfiguration(conf);
    request.setUrl(QUrl("https://www.baidu.com"));

    //set url and xAuthToken
    request.setUrl(QUrl(url));

    request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
    request.setRawHeader(QByteArray("X-Auth-Token"),QByteArray(token.toUtf8()));

    QNetworkReply *systeminfo = manager->get(request);

    //if no below three codes,will first run the latter code after QNetworkReply *systeminfo = manager->get(request);then to run reply
    QEventLoop eventLoop;
    connect(manager, &QNetworkAccessManager::finished, &eventLoop, &QEventLoop::quit);
    eventLoop.exec();

    QStringList criticalstr = analysisSystemJson(systeminfo);

    return criticalstr;
}

得到如下形式的JSON数据:

{
    "@odata.context": "/redfish/v1/$metadata#ComputerSystem.ComputerSystem",
    "@odata.id": "/redfish/v1/******",
    "@odata.type": "#ComputerSystem.ComputerSystem",
    "Id": "1",
    "Name": "Computer System",
    "Actions": {
        "ResetType@Redfish.AllowableValues": [
            "On",
            "ForceOff",
            "ForceRestart",
            "GracefulShutdown",
            "ForcePowerCycle",
            "Nmi"
        ],
        "target": "/redfish/v1/*******"
    },
    "AssetTag": "******",
    "Manufacturer": "********",
    "Model": "********",
    "SerialNumber": "********",
    "PartNumber": "********",
    "HostingRole": "ApplicationServer",
    "SystemType": "Physical",
    "UUID": "4********",
    "HostName": "********",
    "PowerState": "On",
    "PowerRestorePolicyTypes": "LastState",
    "IndicatorLED": "Off",
    "BIOSVersion": "********",
    "Status": {
        "State": "Enabled",
        "Health": "OK"
    }
}

解析得到Manufacturer,PowerState,UUID,Health

/*analysis processing
 *url2
for product name,state of on or off,warnings num,GUID*/
QStringList AddId::analysisSystemJson(QNetworkReply *sys){
    QStringList criticalstr;
    if (sys->error() == QNetworkReply::NoError)
    {
        QByteArray bytes = sys->readAll();
        QJsonParseError jsonError_system;

        QJsonDocument document = QJsonDocument::fromJson(bytes, &jsonError_system);

        if (document.isObject())
        {
            QJsonObject obj = document.object();
            if (obj.contains("Manufacturer")) {
                QString manufacturer = obj.value("Manufacturer").toString();
                criticalstr.append(manufacturer);
                qDebug() << manufacturer;
            }
            if (obj.contains("PowerState")) {
                QString powerstate = obj.value("PowerState").toString();
                criticalstr.append(powerstate);
                qDebug() << powerstate;
            }
            if (obj.contains("UUID")) {
                QString uuid = obj.value("UUID").toString();
                criticalstr.append(uuid);
                qDebug() << uuid;
            }
            if (obj.contains("Status"))
            {
                QJsonObject object_value = obj.value("Status").toObject();
                if (object_value.contains("Health")){
                    QString health = object_value.value("Health").toString();
                    criticalstr.append(health);
                    qDebug() << health;
                }
            }
        }
    }
}

通过url3和token发送get请求获取告警率:

QString AddId::getSeverityMsg(QString url,QString token)
{
    QNetworkRequest request;
    QSslConfiguration config;
    QSslConfiguration conf = request.sslConfiguration();
    conf.setPeerVerifyMode(QSslSocket::VerifyNone);
    conf.setProtocol(QSsl::TlsV1SslV3);
    request.setSslConfiguration(conf);
    request.setUrl(QUrl("https://www.baidu.com"));
    //set url and xAuthToken
    request.setUrl(QUrl(url));
    request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
    request.setRawHeader(QByteArray("X-Auth-Token"),QByteArray(token.toUtf8()));

    QNetworkReply *logserverinfo = manager->get(request);

    //if no below three codes,will first run the latter code after QNetworkReply *systeminfo = manager->get(request);then to run reply
    QEventLoop eventLoop;
    connect(manager, &QNetworkAccessManager::finished, &eventLoop, &QEventLoop::quit);
    eventLoop.exec();

    QString healthrate = analysisLogServerJson(logserverinfo);

    return healthrate;
}

得到如下形式的JSON数据:

{
    "@odata.context": "/redfish/v1/******",
    "@odata.id": "redfish/v1/******",
    "@odata.type": "#LogEntryCollection.LogEntryCollection",
    "Description": "Collection of entries for this log service",
    "Name": "Log Service Entries Collection",
    "Members@odata.count": 24,
    "Members": [
        {
            "@odata.id": "/redfish/v1/******/24",
            "Id": "24",
            "Name": "Log Entry 24",
            "EntryType": "******",
            "SensorNumber": 186,
            "Created": "2021-01-04T07:14:12",
            "EventTimestamp": "2021-01-04T07:14:12",
            "Severity": "Ok",
            "EntryCode": "Assert",
            "SensorType": "******/Interconnect",
            "Message": "******/Interconnect is connected"
        },
        {
            "@odata.id": "/redfish/v1/******",
            "Id": "23",
            "Name": "Log Entry 23",
            "EntryType": "SEL",
            "SensorNumber": 184,
            "Created": "2021-01-04T07:14:12",
            "EventTimestamp": "2021-01-04T07:14:12",
            "Severity": "Ok",
            "EntryCode": "Assert",
            "SensorType": "******/Interconnect",
            "Message": "******/Interconnect is connected"
        },
        ...........
    ]
}

解析获得Severity = ok的数量和 不等于ok(严重告警)的数量。

/*get request
 *https://id/redfish/v1/Systems
for product name,state of on or off,warnings num,GUID*/
QString AddId::analysisLogServerJson(QNetworkReply *logserver){
    int serveritynum = 0;
    int healthnum = 0;
    qint32 logcount = 0;
    if (logserver->error() == QNetworkReply::NoError)
    {
        QByteArray bytes = logserver->readAll();
        QJsonParseError jsonError_logserver;

        QJsonDocument document = QJsonDocument::fromJson(bytes, &jsonError_logserver);

        if (document.isObject())
        {
            QJsonObject obj = document.object();
            if (obj.contains("Members@odata.count"))
            {
                //note that value for "Members@odata.count" is not a string
                logcount = obj.value("Members@odata.count").toInt();
            }
            if (obj.contains("Members"))
            {
                QJsonValue members = obj.value("Members");//the value of members is a array
                if(members.isArray())
                {
                    for(int i = 0;i< logcount;i++)
                    {
                        QJsonObject members_obj = members.toArray().at(i).toObject();
                        if(members_obj.contains("Severity"))
                        {
                            QString status_value = members_obj.value("Severity").toString();
                            if(!(status_value == "Ok"))
                            {
                                serveritynum +=1;
                            }
                            else{
                                healthnum +=1;
                            }
                        }
                    }

                }
            }
        }
    }

QSlite数据库的使用

QSqlDatabase、QSqlQuery、QSqlError、QSqlTableModel

Qt开发之路——SQlite的使用(简单粗暴)

github:https://github.com/hqy7777/SMS-Based-On-Redfish-Qt

参考

Qt开发之路——Json解析过程中遇到的readAll()清除内存缓冲区问题
Qt开发之路——SQlite的使用(简单粗暴)

欢迎讨论交流~

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂java杰尼龟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值