Fix refresh time and add refresh schedule configuration

This commit is contained in:
Simon Stürz 2018-02-15 14:21:04 +01:00 committed by Michael Zanetti
parent 9e75362850
commit 9abc0982cb
7 changed files with 193 additions and 78 deletions

View File

@ -30,19 +30,26 @@ DevicePluginSnapd::DevicePluginSnapd()
}
DevicePluginSnapd::~DevicePluginSnapd()
{
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
hardwareManager()->pluginTimerManager()->unregisterTimer(m_updateTimer);
}
void DevicePluginSnapd::init()
{
// Check advanced mode
// Initialize plugin configurations
m_advancedMode = configValue(SnapdAdvancedModeParamTypeId).toBool();
m_refreshTime = configValue(SnapdRefreshScheduleParamTypeId).toInt();
connect(this, &DevicePluginSnapd::configValueChanged, this, &DevicePluginSnapd::onPluginConfigurationChanged);
// Setup timers
// Refresh timer for snapd checks
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(2);
connect(m_refreshTimer, &PluginTimer::timeout, this, &DevicePluginSnapd::onRefreshTimer);
// Check all 5 min if there is an update available
m_updateTimer = hardwareManager()->pluginTimerManager()->registerTimer(300);
connect(m_refreshTimer, &PluginTimer::timeout, this, &DevicePluginSnapd::onUpdateTimer);
// Check all 4 hours if there is an update available
m_updateTimer = hardwareManager()->pluginTimerManager()->registerTimer(14400);
connect(m_updateTimer, &PluginTimer::timeout, this, &DevicePluginSnapd::onUpdateTimer);
}
void DevicePluginSnapd::startMonitoringAutoDevices()
@ -69,9 +76,9 @@ void DevicePluginSnapd::startMonitoringAutoDevices()
void DevicePluginSnapd::postSetupDevice(Device *device)
{
if (m_snapdControl && m_snapdControl->device() == device)
if (m_snapdControl && m_snapdControl->device() == device) {
m_snapdControl->update();
}
}
void DevicePluginSnapd::deviceRemoved(Device *device)
@ -107,6 +114,7 @@ DeviceManager::DeviceSetupStatus DevicePluginSnapd::setupDevice(Device *device)
}
m_snapdControl = new SnapdControl(device, this);
m_snapdControl->setPreferedRefreshTime(configValue(SnapdRefreshScheduleParamTypeId).toInt());
connect(m_snapdControl, &SnapdControl::snapListUpdated, this, &DevicePluginSnapd::onSnapListUpdated);
} else if (device->deviceClassId() == snapDeviceClassId) {
@ -171,6 +179,9 @@ DeviceManager::DeviceError DevicePluginSnapd::executeAction(Device *device, cons
void DevicePluginSnapd::onPluginConfigurationChanged(const ParamTypeId &paramTypeId, const QVariant &value)
{
qCDebug(dcSnapd()) << "Plugin configuration changed";
// Check advanced mode
if (paramTypeId == SnapdAdvancedModeParamTypeId) {
qCDebug(dcSnapd()) << "Advanced mode" << (value.toBool() ? "enabled." : "disabled.");
m_advancedMode = value.toBool();
@ -189,6 +200,16 @@ void DevicePluginSnapd::onPluginConfigurationChanged(const ParamTypeId &paramTyp
m_snapdControl->update();
}
}
// Check refresh schedule
if (paramTypeId == SnapdRefreshScheduleParamTypeId) {
if (!m_snapdControl)
return;
m_refreshTime = value.toInt();
qCDebug(dcSnapd()) << "Refresh schedule start time" << QTime(m_refreshTime, 0, 0).toString("hh:mm");
m_snapdControl->setPreferedRefreshTime(m_refreshTime);
}
}
void DevicePluginSnapd::onRefreshTimer()

View File

@ -42,6 +42,8 @@ class DevicePluginSnapd: public DevicePlugin {
public:
explicit DevicePluginSnapd();
~DevicePluginSnapd();
void init() override;
void startMonitoringAutoDevices() override;
void postSetupDevice(Device *device) override;
@ -56,6 +58,7 @@ private:
PluginTimer *m_updateTimer = nullptr;
bool m_advancedMode = false;
int m_refreshTime = 2;
// Snap list for faster access (snap id, device)
QHash<QString, Device *> m_snapDevices;

View File

@ -9,6 +9,16 @@
"displayName": "Advanced mode",
"type": "bool",
"defaultValue": false
},
{
"id": "d2e697d1-9a68-4666-bf40-8d70fa694eec",
"name": "refreshSchedule",
"displayName": "Automatic daily refresh schedule",
"type": "int",
"unit": "Hours",
"minValue": 0,
"maxValue": 23,
"defaultValue": 2
}
],
"vendors": [

View File

@ -39,6 +39,11 @@ SnapdConnection::SnapdConnection(QObject *parent) :
connect(this, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(onError(QLocalSocket::LocalSocketError)));
}
SnapdConnection::~SnapdConnection()
{
close();
}
SnapdReply *SnapdConnection::get(const QString &path, QObject *parent)
{
SnapdReply *reply = new SnapdReply(parent);
@ -70,6 +75,22 @@ SnapdReply *SnapdConnection::post(const QString &path, const QByteArray &payload
return reply;
}
SnapdReply *SnapdConnection::put(const QString &path, const QByteArray &payload, QObject *parent)
{
SnapdReply *reply = new SnapdReply(parent);
reply->setRequestPath(path);
reply->setRequestMethod("PUT");
QByteArray header = createRequestHeader("PUT", path, payload);
reply->setRequestRawMessage(header.append(payload));
// Enqueue the new reply
m_replyQueue.enqueue(reply);
sendNextRequest();
// Note: the caller owns the object now
return reply;
}
bool SnapdConnection::isConnected() const
{
return m_connected;
@ -95,7 +116,7 @@ void SnapdConnection::setConnected(const bool &connected)
while (!m_replyQueue.isEmpty()) {
QPointer<SnapdReply> reply = m_replyQueue.dequeue();
if (!reply.isNull()) {
reply->setFinished(false);
reply->deleteLater();
}
}
} else {
@ -258,8 +279,10 @@ void SnapdConnection::sendNextRequest()
if (m_debug)
qCDebug(dcSnapd()) << "-->" << reply->requestMethod() << reply->requestPath();
// If write failes, the reply is finished invalid and the owner has to delete it
if (write(reply->requestRawMessage()) < 0) {
// Send current reply request. If write failes, the reply is finished invalid and the owner has to delete it
qint64 bytesWritten = write(reply->requestRawMessage());
if (bytesWritten < 0) {
qCWarning(dcSnapd()) << "Could not write request data" << reply->requestMethod() << reply->requestMethod();
m_currentReply->setFinished(false);
m_currentReply = nullptr;
sendNextRequest();

View File

@ -34,9 +34,11 @@ class SnapdConnection : public QLocalSocket
Q_OBJECT
public:
explicit SnapdConnection(QObject *parent = nullptr);
~SnapdConnection();
SnapdReply *get(const QString &path, QObject *parent);
SnapdReply *post(const QString &path, const QByteArray &payload, QObject *parent);
SnapdReply *put(const QString &path, const QByteArray &payload, QObject *parent);
bool isConnected() const;

View File

@ -33,6 +33,12 @@ SnapdControl::SnapdControl(Device *device, QObject *parent) :
m_device(device),
m_snapdSocketPath("/run/snapd.socket")
{
// If a change is one of following kind, the plugin will recognize it as update running
m_updateChangeKinds.append("install-snap");
m_updateChangeKinds.append("remove-snap");
m_updateChangeKinds.append("refresh-snap");
m_updateChangeKinds.append("revert-snap");
m_snapConnection = new SnapdConnection(this);
connect(m_snapConnection, &SnapdConnection::connectedChanged, this, &SnapdControl::onConnectedChanged);
}
@ -73,6 +79,11 @@ bool SnapdControl::enabled() const
return m_enabled;
}
bool SnapdControl::timerBasedSchedule() const
{
return m_timerBasedSchedule;
}
void SnapdControl::loadSystemInfo()
{
if (!m_snapConnection)
@ -109,7 +120,7 @@ void SnapdControl::loadRunningChanges()
connect(reply, &SnapdReply::finished, this, &SnapdControl::onLoadRunningChangesFinished);
}
void SnapdControl::loadChange(const int &change)
void SnapdControl::configureRefreshSchedule()
{
if (!m_snapConnection)
return;
@ -117,34 +128,15 @@ void SnapdControl::loadChange(const int &change)
if (!m_snapConnection->isConnected())
return;
SnapdReply *reply = m_snapConnection->get(QString("/v2/changes/%1").arg(QString::number(change)), this);
connect(reply, &SnapdReply::finished, this, &SnapdControl::onLoadChangeFinished);
}
QVariantMap configuration; QVariantMap configMap;
configMap.insert("timer", m_preferedRefreshSchedule);
configMap.insert("schedule", m_preferedRefreshSchedule);
configuration.insert("refresh", configMap);
void SnapdControl::processChange(const QVariantMap &changeMap)
{
int changeId = changeMap.value("id").toInt();
bool changeReady = changeMap.value("ready").toBool();
QString changeKind = changeMap.value("kind").toString();
QString changeStatus = changeMap.value("status").toString();
QString changeSummary = changeMap.value("summary").toString();
qCDebug(dcSnapd()) << "Configure refresh schedule from" << m_currentRefreshSchedule << "-->" << m_preferedRefreshSchedule;
qCDebug(dcSnapd()) << changeStatus << changeKind << changeSummary;
// Add this change if not already finishished or added
if (!m_watchingChanges.contains(changeId) && !changeReady)
m_watchingChanges.append(changeId);
// If change is on Doing, update the status
if (changeStatus == "Doing") {
device()->setStateValue(snapdControlStatusStateTypeId, changeSummary);
}
// If this change is on ready, we can remove it from our watch list
if (changeReady) {
qCDebug(dcSnapd()).noquote() << changeKind << (changeReady ? "finished." : "running.") << changeSummary;
m_watchingChanges.removeAll(changeId);
}
SnapdReply *reply = m_snapConnection->put(QString("/v2/snaps/core/conf"), QJsonDocument::fromVariant(configuration).toJson(QJsonDocument::Compact), this);
connect(reply, &SnapdReply::finished, this, &SnapdControl::onConfigureRefreshScheduleFinished);
}
bool SnapdControl::validAsyncResponse(const QVariantMap &responseMap)
@ -185,7 +177,23 @@ void SnapdControl::onLoadSystemInfoFinished()
device()->setStateValue(snapdControlLastUpdateTimeStateTypeId, lastRefreshTime.toTime_t());
device()->setStateValue(snapdControlNextUpdateTimeStateTypeId, nextRefreshTime.toTime_t());
// Check if we are working on refresh timer or refresh schedule
if (result.value("refresh").toMap().contains("schedule")) {
// Schedule based core snap
m_timerBasedSchedule = false;
m_currentRefreshSchedule = result.value("refresh").toMap().value("schedule").toString();
} else if (result.value("refresh").toMap().contains("timer")) {
// Timer based core snap: snapd >= 2.31
m_timerBasedSchedule = true;
m_currentRefreshSchedule = result.value("refresh").toMap().value("timer").toString();
}
reply->deleteLater();
// Check if the refresh schedule should be updated
if (m_currentRefreshSchedule != m_preferedRefreshSchedule) {
configureRefreshSchedule();
}
}
void SnapdControl::onLoadSnapListFinished()
@ -210,42 +218,63 @@ void SnapdControl::onLoadRunningChangesFinished()
return;
}
foreach (const QVariant &changeVariant, reply->dataMap().value("result").toList()) {
processChange(changeVariant.toMap());
}
// Check if there are still changes around
if (reply->dataMap().value("result").toList().isEmpty()) {
// If there are no running changes, we can forget old ones
m_watchingChanges.clear();
// Load changes list
QVariantList changes = reply->dataMap().value("result").toList();
reply->deleteLater();
// If there are no running changes, update is not running
if (changes.isEmpty()) {
// Update not running any more
device()->setStateValue(snapdControlUpdateRunningStateTypeId, false);
device()->setStateValue(snapdControlStatusStateTypeId, "-");
} else {
// Update running
device()->setStateValue(snapdControlUpdateRunningStateTypeId, true);
return;
}
reply->deleteLater();
bool updateRunning = false;
QString updateStatus = "-";
// Verifiy if a change is running and which one is currently doing something
foreach (const QVariant &changeVariant, changes) {
QVariantMap changeMap = changeVariant.toMap();
int changeId = changeMap.value("id").toInt();
bool changeReady = changeMap.value("ready").toBool();
QString changeKind = changeMap.value("kind").toString();
QString changeStatus = changeMap.value("status").toString();
QString changeSummary = changeMap.value("summary").toString();
// If there is a change kind "doing" or "Do"
if ( (changeStatus == "Doing" || changeStatus == "Do") && m_updateChangeKinds.contains(changeKind)) {
// Set the status of the current running change
updateRunning = true;
if (changeStatus == "Doing") {
updateStatus = changeSummary;
qCDebug(dcSnapd()).noquote() << "Current change:" << changeId << (changeReady ? "ready" : "not ready") << changeStatus << changeKind << changeSummary;
}
}
}
device()->setStateValue(snapdControlUpdateRunningStateTypeId, updateRunning);
device()->setStateValue(snapdControlStatusStateTypeId, updateStatus);
}
void SnapdControl::onLoadChangeFinished()
void SnapdControl::onConfigureRefreshScheduleFinished()
{
SnapdReply *reply = static_cast<SnapdReply *>(sender());
if (!reply->isValid()) {
qCDebug(dcSnapd()) << "Load change request finished with error" << reply->requestPath();
qCDebug(dcSnapd()) << "Set refresh schedule request finished with error" << reply->requestPath();
reply->deleteLater();
return;
}
processChange(reply->dataMap().value("result").toMap());
if (m_watchingChanges.isEmpty()) {
device()->setStateValue(snapdControlUpdateRunningStateTypeId, false);
device()->setStateValue(snapdControlStatusStateTypeId, "-");
if (!validAsyncResponse(reply->dataMap())) {
qCWarning(dcSnapd()) << "Async refresh configuration request finished with error" << reply->dataMap().value("status").toString() << reply->dataMap().value("status-code").toInt();
reply->deleteLater();
return;
}
qCDebug(dcSnapd()) << "Configure refresh schedule finished successfully";
reply->deleteLater();
}
@ -258,11 +287,10 @@ void SnapdControl::onSnapRefreshFinished()
return;
}
//qCDebug(dcSnapd()) << qUtf8Printable(QJsonDocument::fromVariant(reply->dataMap()).toJson(QJsonDocument::Indented));
if (!validAsyncResponse(reply->dataMap())) {
qCWarning(dcSnapd()) << "Async change request finished with error" << reply->dataMap().value("status").toString() << reply->dataMap().value("").toString();
qCWarning(dcSnapd()) << "Async refresh request finished with error" << reply->dataMap().value("status").toString() << reply->dataMap().value("status-code").toInt();
} else {
loadChange(reply->dataMap().value("change").toInt());
loadRunningChanges();
}
reply->deleteLater();
@ -277,12 +305,12 @@ void SnapdControl::onSnapRevertFinished()
return;
}
//qCDebug(dcSnapd()) << qUtf8Printable(QJsonDocument::fromVariant(reply->dataMap()).toJson(QJsonDocument::Indented));
if (!validAsyncResponse(reply->dataMap())) {
qCWarning(dcSnapd()) << "Async change request finished with error" << reply->dataMap().value("status").toString() << reply->dataMap().value("").toString();
qCWarning(dcSnapd()) << "Async change request finished with error" << reply->dataMap().value("status").toString() << reply->dataMap().value("status-code").toInt();;
} else {
loadChange(reply->dataMap().value("change").toInt());
loadRunningChanges();
}
reply->deleteLater();
}
@ -290,13 +318,26 @@ void SnapdControl::onCheckForUpdatesFinished()
{
SnapdReply *reply = static_cast<SnapdReply *>(sender());
if (!reply->isValid()) {
qCDebug(dcSnapd()) << "Snap check for updates request finished with error" << reply->requestPath();
qCDebug(dcSnapd()) << "Check for snap updates request finished with error" << reply->requestPath();
reply->deleteLater();
return;
}
//qCDebug(dcSnapd()) << qUtf8Printable(QJsonDocument::fromVariant(reply->dataMap()).toJson(QJsonDocument::Indented));
device()->setStateValue(snapdControlUpdateAvailableStateTypeId, !reply->dataMap().value("result").toList().isEmpty());
qCDebug(dcSnapd()) << "Check for available snap updates finished.";
if (reply->dataMap().value("result").toList().isEmpty()) {
qCDebug(dcSnapd()) << "There are no snap updates available.";
device()->setStateValue(snapdControlUpdateAvailableStateTypeId, false);
} else {
// Print available snap updates
qCDebug(dcSnapd()) << "Following snaps can be updated:";
foreach (const QVariant &resultVariant, reply->dataMap().value("result").toList()) {
QVariantMap resultMap = resultVariant.toMap();
qCDebug(dcSnapd()) << " -->" << resultMap.value("name").toString() << resultMap.value("version").toString();
}
device()->setStateValue(snapdControlUpdateAvailableStateTypeId, true);
}
reply->deleteLater();
}
@ -310,10 +351,11 @@ void SnapdControl::onChangeSnapChannelFinished()
}
if (!validAsyncResponse(reply->dataMap())) {
qCWarning(dcSnapd()) << "Async change request finished with error" << reply->dataMap().value("status").toString() << reply->dataMap().value("").toString();
qCWarning(dcSnapd()) << "Async change request finished with error" << reply->dataMap().value("status").toString() << reply->dataMap().value("status-code").toInt();
} else {
loadChange(reply->dataMap().value("change").toInt());
loadRunningChanges();
}
reply->deleteLater();
}
@ -354,17 +396,13 @@ void SnapdControl::update()
return;
// Update information
if (!m_watchingChanges.isEmpty()) {
// We are watching currently changes
foreach (const int &change, m_watchingChanges) {
loadChange(change);
}
if (device()->stateValue(snapdControlUpdateRunningStateTypeId).toBool()) {
// Note: if an update is running, just load the changes to save system resources
loadRunningChanges();
} else {
// Normal refresh
// Normal update
loadSystemInfo();
loadSnapList();
checkForUpdates();
loadRunningChanges();
}
}
@ -410,10 +448,20 @@ void SnapdControl::checkForUpdates()
if (!m_snapConnection->isConnected())
return;
qCDebug(dcSnapd()) << "Checking for available snap updates";
SnapdReply *reply = m_snapConnection->get("/v2/find?select=refresh", this);
connect(reply, &SnapdReply::finished, this, &SnapdControl::onCheckForUpdatesFinished);
}
void SnapdControl::setPreferedRefreshTime(int startTime)
{
// Schedule the refresh between startTime and startTime + 59 minutes
QTime start(startTime, 0, 0);
QTime end = start.addSecs(3540);
m_preferedRefreshSchedule = QString("%1-%2").arg(start.toString("h:mm")).arg(end.toString("h:mm"));
qCDebug(dcSnapd()) << "Set prefered refresh schedule to " << m_preferedRefreshSchedule;
}
void SnapdControl::snapRevert(const QString &snapName)
{
if (!m_snapConnection)

View File

@ -40,6 +40,7 @@ public:
bool available() const;
bool connected() const;
bool enabled() const;
bool timerBasedSchedule() const;
private:
Device *m_device = nullptr;
@ -48,15 +49,19 @@ private:
QString m_snapdSocketPath;
bool m_enabled = true;
QList<int> m_watchingChanges;
QStringList m_updateChangeKinds;
bool m_timerBasedSchedule = false;
QString m_currentRefreshSchedule;
QString m_preferedRefreshSchedule;
// Update calls
void loadSystemInfo();
void loadSnapList();
void loadRunningChanges();
void loadChange(const int &change);
void processChange(const QVariantMap &changeMap);
void configureRefreshSchedule();
bool validAsyncResponse(const QVariantMap &responseMap);
private slots:
@ -66,7 +71,7 @@ private slots:
void onLoadSystemInfoFinished();
void onLoadSnapListFinished();
void onLoadRunningChangesFinished();
void onLoadChangeFinished();
void onConfigureRefreshScheduleFinished();
void onSnapRefreshFinished();
void onSnapRevertFinished();
@ -84,6 +89,9 @@ public slots:
void update();
void snapRefresh();
void checkForUpdates();
void setPreferedRefreshTime(int startTime);
void snapRevert(const QString &snapName);
void changeSnapChannel(const QString &snapName, const QString &channel);
};