Merge lp:~xavi-garcia-mena/keeper/command-line-client-plus-bugs into lp:keeper/devel

Proposed by Xavi Garcia
Status: Merged
Approved by: Xavi Garcia
Approved revision: 128
Merged at revision: 124
Proposed branch: lp:~xavi-garcia-mena/keeper/command-line-client-plus-bugs
Merge into: lp:keeper/devel
Diff against target: 1578 lines (+1048/-93)
27 files modified
data/CMakeLists.txt (+6/-0)
debian/keeper.install (+1/-0)
include/client/client.h (+8/-0)
src/cli/CMakeLists.txt (+4/-0)
src/cli/command-line-client-view.cpp (+128/-0)
src/cli/command-line-client-view.h (+57/-0)
src/cli/command-line-client.cpp (+247/-0)
src/cli/command-line-client.h (+53/-0)
src/cli/command-line.cpp (+188/-0)
src/cli/command-line.h (+52/-0)
src/cli/main.cpp (+29/-46)
src/client/CMakeLists.txt (+5/-0)
src/client/client.cpp (+148/-35)
src/helper/CMakeLists.txt (+15/-0)
src/helper/folder-restore.sh.in (+23/-0)
src/helper/restore-helper.cpp (+5/-0)
src/qdbus-stubs/CMakeLists.txt (+21/-4)
src/qdbus-stubs/dbus-types.h (+1/-2)
src/qdbus-stubs/org.freedesktop.DBus.Properties.xml (+27/-0)
src/service/keeper.cpp (+11/-0)
src/service/main.cpp (+2/-1)
src/service/task-manager.cpp (+8/-2)
src/service/task-manager.h (+1/-0)
src/storage-framework/storage_framework_client.cpp (+3/-3)
tests/unit/helper/CMakeLists.txt (+1/-0)
tests/unit/manifest/CMakeLists.txt (+1/-0)
tests/unit/metadata-providers/CMakeLists.txt (+3/-0)
To merge this branch: bzr merge lp:~xavi-garcia-mena/keeper/command-line-client-plus-bugs
Reviewer Review Type Date Requested Status
unity-api-1-bot continuous-integration Approve
Charles Kerr (community) Approve
Review via email: mp+312257@code.launchpad.net

Commit message

Changes to command line client and some bugs fixed.

Description of the change

Changes to command line client and some bugs fixed.

This branch changes the command line client to follow the design specs and to show the status of each active task.

The command line client has a separated class for the view, so changing HOW we show the results should be independent of the rest of the code.

It also fixed the following issues:
- Bug when getting downloader, some variables needed to be captured.
- Avoid setting current state for last task until manifest is stored.
- When task manager finishes all tasks we clear the backup choices cache. We do this to avoid repeating uuids in between backups. So if we start 2 backups the uuids now are different. They were equal previously and that created issues when obtaining the restore options, as they are identified by uuid.

What's missing:

* Command line library:
- We need to change the code style to follow the rest of the project's code.

- The library is oriented to the qml app, and only for backups, so some re-design will be necessary.

- We need to add the start restore functionality. Obtaining the restore choices is already done.

* Command line client:
- The output now is using special escape characters. We should investigate if there's something cleaner. (That would mean changes only in the view class)

- Add restore functionality

- Completely follow the specs. For example now the storage is unique and it only uses the option --storage to identify that we want to list the available options in the storage (restore options). To do this we need to change the storage framework client class in order to choose from different accounts and/or storages.

To post a comment you must log in.
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
123. By Xavi Garcia

conflict resolved

Revision history for this message
Xavi Garcia (xavi-garcia-mena) wrote :

It also fixes the dependency issues and undefined symbols that the client library had.

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

PASSED: Continuous integration, rev:123
https://jenkins.canonical.com/unity-api-1/job/lp-keeper-ci/147/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build/1225
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/1232
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1015
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1015/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=zesty/1015
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=zesty/1015/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1015
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1015/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=zesty/1015
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=zesty/1015/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/1015
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/1015/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=zesty/1015
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=zesty/1015/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-keeper-ci/147/rebuild

review: Approve (continuous-integration)
124. By Xavi Garcia

Added restore in command line client, added restore in backup registry with empty folder helper

125. By Xavi Garcia

Added folder-restore.sh.in

126. By Xavi Garcia

fixed debian packaging

127. By Xavi Garcia

Fix cppcheck test in zesty

Revision history for this message
Charles Kerr (charlesk) wrote :

Some optional suggestions inline, no blockers. LGTM.

review: Approve
128. By Xavi Garcia

Suggested changes in review

Revision history for this message
Xavi Garcia (xavi-garcia-mena) wrote :

Thanks for the review, Charles!

I've changed the code following your suggestions.

I only kept the "return true" statement in those two methods as the intention is to add more flags and options in the future...

Revision history for this message
unity-api-1-bot (unity-api-1-bot) wrote :

PASSED: Continuous integration, rev:128
https://jenkins.canonical.com/unity-api-1/job/lp-keeper-ci/154/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build/1275
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-0-fetch/1282
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1063
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=xenial+overlay/1063/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=zesty/1063
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=amd64,release=zesty/1063/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1063
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=xenial+overlay/1063/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=zesty/1063
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=armhf,release=zesty/1063/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/1063
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=xenial+overlay/1063/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=zesty/1063
        deb: https://jenkins.canonical.com/unity-api-1/job/build-2-binpkg/arch=i386,release=zesty/1063/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/unity-api-1/job/lp-keeper-ci/154/rebuild

review: Approve (continuous-integration)
Revision history for this message
Michi Henning (michihenning) wrote :

Asking out of curiosity:

"When task manager finishes all tasks we clear the backup choices cache. We do this to avoid repeating uuids in between backups."

That sounded strange to me. I mean, what sort of UUID generator comes up with same UUID twice?

Revision history for this message
Xavi Garcia (xavi-garcia-mena) wrote :

Hi Michi,

We clear the cache of uuids because if we run a backup twice we don't repeat the same UUID.
If the cache is not empty we don't recalculate the UUIDs, we need that in between calls to list-sections and backup as we need the UUIDs to be the same after asking for the list of available sections to backup and the moment when we run that backup. (the backup is triggered passing a list of uuids)

But, if we don't clean the uuids when the backup finishes and the user triggers a second backup, the uuids are the same, which is a problem when trying to restore as we'll have 2 backups with the same uuid.

makes sense?

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/CMakeLists.txt'
2--- data/CMakeLists.txt 2016-08-10 07:43:36 +0000
3+++ data/CMakeLists.txt 2016-12-16 09:34:32 +0000
4@@ -6,6 +6,12 @@
5 FOLDER_BACKUP_EXEC
6 ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}/folder-backup.sh
7 )
8+
9+set(
10+ FOLDER_RESTORE_EXEC
11+ ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}/folder-restore.sh
12+)
13+
14 configure_file(
15 ${HELPER_REGISTRY_FILENAME}.in
16 ${HELPER_REGISTRY_FILENAME}
17
18=== modified file 'debian/keeper.install'
19--- debian/keeper.install 2016-08-10 07:43:36 +0000
20+++ debian/keeper.install 2016-12-16 09:34:32 +0000
21@@ -1,4 +1,5 @@
22 /usr/lib/*/keeper/folder-backup.sh
23+/usr/lib/*/keeper/folder-restore.sh
24 /usr/lib/*/keeper/keeper-service
25 /usr/lib/*/keeper/keeper-tar-create
26 /usr/lib/*/ubuntu-app-launch/backup-helper/exec-tool
27
28=== modified file 'include/client/client.h'
29--- include/client/client.h 2016-09-06 18:28:45 +0000
30+++ include/client/client.h 2016-12-16 09:34:32 +0000
31@@ -55,10 +55,15 @@
32 Q_INVOKABLE void enableBackup(QString uuid, bool enabled);
33 Q_INVOKABLE void startBackup();
34
35+ Q_INVOKABLE void enableRestore(QString uuid, bool enabled);
36+ Q_INVOKABLE void startRestore();
37+
38 // C++
39 public:
40 QMap<QString, QVariantMap> getBackupChoices() const;
41+ QMap<QString, QVariantMap> getRestoreChoices() const;
42 void startBackup(QStringList const& uuids) const;
43+ void startRestore(QStringList const& uuids) const;
44
45 QMap<QString, QVariantMap> getState() const;
46
47@@ -68,6 +73,9 @@
48 void readyToBackupChanged();
49 void backupBusyChanged();
50
51+ void taskStatusChanged(QString const & displayName, QString const & status, double percentage);
52+ void finished();
53+
54 private Q_SLOTS:
55 void stateUpdated();
56
57
58=== modified file 'src/cli/CMakeLists.txt'
59--- src/cli/CMakeLists.txt 2016-09-06 01:31:59 +0000
60+++ src/cli/CMakeLists.txt 2016-12-16 09:34:32 +0000
61@@ -5,6 +5,9 @@
62
63 set(CLI_SOURCES
64 main.cpp
65+ command-line.cpp
66+ command-line-client.cpp
67+ command-line-client-view.cpp
68 )
69
70 add_executable(
71@@ -19,6 +22,7 @@
72 util
73 qdbus-stubs
74 ${SERVICE_DEVEL_SF_DEPS_LIBRARIES}
75+ ${KEEPER_CLIENT_LIB}
76 Qt5::Core
77 Qt5::DBus
78 )
79
80=== added file 'src/cli/command-line-client-view.cpp'
81--- src/cli/command-line-client-view.cpp 1970-01-01 00:00:00 +0000
82+++ src/cli/command-line-client-view.cpp 2016-12-16 09:34:32 +0000
83@@ -0,0 +1,128 @@
84+/*
85+ * Copyright (C) 2016 Canonical, Ltd.
86+ *
87+ * This program is free software: you can redistribute it and/or modify it
88+ * under the terms of the GNU General Public License version 3, as published
89+ * by the Free Software Foundation.
90+ *
91+ * This program is distributed in the hope that it will be useful, but
92+ * WITHOUT ANY WARRANTY; without even the implied warranties of
93+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
94+ * PURPOSE. See the GNU General Public License for more details.
95+ *
96+ * You should have received a copy of the GNU General Public License along
97+ * with this program. If not, see <http://www.gnu.org/licenses/>.
98+ *
99+ * Authors:
100+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
101+ */
102+#include "command-line-client-view.h"
103+
104+#include <client/client.h>
105+
106+#include <iostream>
107+#include <iomanip>
108+
109+CommandLineClientView::CommandLineClientView(QObject * parent)
110+ : QObject(parent)
111+{
112+ connect(&timer_status_, &QTimer::timeout, this, &CommandLineClientView::show_info);
113+
114+ // TODO see if we can do this in a better way
115+ // This line is for the global progress status and percentage
116+ std::cout << std::endl;
117+}
118+
119+void CommandLineClientView::progress_changed(double percentage)
120+{
121+ percentage_ = percentage * 100;
122+}
123+
124+void CommandLineClientView::status_changed(QString const & status)
125+{
126+ if (status_ != status)
127+ {
128+ status_ = status;
129+ }
130+}
131+
132+void CommandLineClientView::add_task(QString const & display_name, QString const & initial_status, double initial_percentage)
133+{
134+ tasks_strings_[display_name] = get_task_string(display_name, initial_status, initial_percentage);
135+ // TODO see if we can do this in a better way
136+ // We add a line per each backup task
137+ std::cout << std::endl;
138+}
139+
140+void CommandLineClientView::clear_all_tasks()
141+{
142+ tasks_strings_.clear();
143+}
144+
145+void CommandLineClientView::start_printing_tasks()
146+{
147+ timer_status_.start(300);
148+}
149+
150+void CommandLineClientView::clear_all()
151+{
152+ timer_status_.stop();
153+ std::cout << std::endl;
154+}
155+
156+void CommandLineClientView::print_sections(QStringList const & sections)
157+{
158+ for (auto const & section : sections)
159+ {
160+ std::cout << section.toStdString() << std::endl;
161+ }
162+}
163+
164+void CommandLineClientView::print_error_message(QString const & error_message)
165+{
166+ std::cerr << error_message.toStdString() << std::endl;
167+}
168+
169+void CommandLineClientView::show_info()
170+{
171+ // TODO Revisit this code to see if we can do this in a different way
172+ // Maybe using ncurses?
173+
174+ // Rewind to the beginning
175+ std::cout << '\r' << std::flush;
176+ // For every backup task we go up 1 line
177+ for (auto i = 0; i < tasks_strings_.size(); ++i)
178+ {
179+ std::cout << "\e[A";
180+ }
181+ // print the tasks
182+ for (auto iter = tasks_strings_.begin(); iter != tasks_strings_.end(); ++iter)
183+ {
184+ std::cout << (*iter).toStdString() << std::setfill(' ') << std::endl;
185+ }
186+ std::cout << '\r' << std::fixed << std::setw(30) << status_.toStdString() << std::setprecision(3)
187+ << std::setfill(' ') << " " << percentage_ << " % " << get_next_spin_char() << " " << std::flush;
188+}
189+
190+char CommandLineClientView::get_next_spin_char()
191+{
192+ char cursor[4]={'/','-','\\','|'};
193+ auto ret = cursor[spin_value_];
194+ spin_value_ = (spin_value_ + 1) % 4;
195+ return ret;
196+}
197+
198+QString CommandLineClientView::get_task_string(QString const & displayName, QString const & status, double percentage)
199+{
200+
201+ return QStringLiteral("%1 %2 % %3").arg(displayName, 15).arg((percentage * 100), 10, 'f', 2, ' ').arg(status, -15);
202+}
203+
204+void CommandLineClientView::on_task_state_changed(QString const & displayName, QString const & status, double percentage)
205+{
206+ auto iter = tasks_strings_.find(displayName);
207+ if (iter != tasks_strings_.end())
208+ {
209+ tasks_strings_[displayName] = get_task_string(displayName, status, percentage);
210+ }
211+}
212
213=== added file 'src/cli/command-line-client-view.h'
214--- src/cli/command-line-client-view.h 1970-01-01 00:00:00 +0000
215+++ src/cli/command-line-client-view.h 2016-12-16 09:34:32 +0000
216@@ -0,0 +1,57 @@
217+/*
218+ * Copyright (C) 2016 Canonical, Ltd.
219+ *
220+ * This program is free software: you can redistribute it and/or modify it
221+ * under the terms of the GNU General Public License version 3, as published
222+ * by the Free Software Foundation.
223+ *
224+ * This program is distributed in the hope that it will be useful, but
225+ * WITHOUT ANY WARRANTY; without even the implied warranties of
226+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
227+ * PURPOSE. See the GNU General Public License for more details.
228+ *
229+ * You should have received a copy of the GNU General Public License along
230+ * with this program. If not, see <http://www.gnu.org/licenses/>.
231+ *
232+ * Authors:
233+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
234+ */
235+#pragma once
236+
237+#include <QMap>
238+#include <QObject>
239+#include <QTimer>
240+
241+class CommandLineClientView : public QObject
242+{
243+ Q_OBJECT
244+public:
245+ explicit CommandLineClientView(QObject * parent = nullptr);
246+ ~CommandLineClientView() = default;
247+
248+ Q_DISABLE_COPY(CommandLineClientView)
249+
250+ void progress_changed(double percentage);
251+ void status_changed(QString const & status);
252+
253+ void add_task(QString const & display_name, QString const & initial_status, double initial_percentage);
254+ void clear_all_tasks();
255+ void start_printing_tasks();
256+ void clear_all();
257+ void print_sections(QStringList const & sections);
258+ void print_error_message(QString const & error_message);
259+
260+public Q_SLOTS:
261+ void show_info();
262+ void on_task_state_changed(QString const & displayName, QString const & status, double percentage);
263+
264+private:
265+ char get_next_spin_char();
266+ QString get_task_string(QString const & displayName, QString const & status, double percentage);
267+
268+ QString status_;
269+ QTimer timer_status_;
270+ double percentage_ = 0.0;
271+ int spin_value_ = 0;
272+ QMap<QString, QString> tasks_strings_;
273+};
274
275=== added file 'src/cli/command-line-client.cpp'
276--- src/cli/command-line-client.cpp 1970-01-01 00:00:00 +0000
277+++ src/cli/command-line-client.cpp 2016-12-16 09:34:32 +0000
278@@ -0,0 +1,247 @@
279+/*
280+ * Copyright (C) 2016 Canonical, Ltd.
281+ *
282+ * This program is free software: you can redistribute it and/or modify it
283+ * under the terms of the GNU General Public License version 3, as published
284+ * by the Free Software Foundation.
285+ *
286+ * This program is distributed in the hope that it will be useful, but
287+ * WITHOUT ANY WARRANTY; without even the implied warranties of
288+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
289+ * PURPOSE. See the GNU General Public License for more details.
290+ *
291+ * You should have received a copy of the GNU General Public License along
292+ * with this program. If not, see <http://www.gnu.org/licenses/>.
293+ *
294+ * Authors:
295+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
296+ */
297+#include "command-line-client.h"
298+#include "command-line-client-view.h"
299+
300+#include <client/client.h>
301+
302+#include <QCoreApplication>
303+#include <QDebug>
304+
305+#include <iostream>
306+#include <iomanip>
307+
308+CommandLineClient::CommandLineClient(QObject * parent)
309+ : QObject(parent)
310+ , keeper_client_(new KeeperClient(this))
311+ , view_(new CommandLineClientView(this))
312+{
313+ connect(keeper_client_.data(), &KeeperClient::statusChanged, this, &CommandLineClient::on_status_changed);
314+ connect(keeper_client_.data(), &KeeperClient::progressChanged, this, &CommandLineClient::on_progress_changed);
315+ connect(keeper_client_.data(), &KeeperClient::finished, this, &CommandLineClient::on_keeper_client_finished);
316+ connect(keeper_client_.data(), &KeeperClient::taskStatusChanged, view_.data(), &CommandLineClientView::on_task_state_changed);
317+}
318+
319+CommandLineClient::~CommandLineClient() = default;
320+
321+void CommandLineClient::run_list_sections(bool remote)
322+{
323+ QMap<QString, QVariantMap> choices_values;
324+ if(!remote)
325+ {
326+ choices_values = keeper_client_->getBackupChoices();
327+ list_backup_sections(choices_values);
328+ }
329+ else
330+ {
331+ choices_values = keeper_client_->getRestoreChoices();
332+ list_restore_sections(choices_values);
333+ }
334+}
335+
336+void CommandLineClient::run_backup(QStringList & sections)
337+{
338+ auto unhandled_sections = sections;
339+ auto choices_values = keeper_client_->getBackupChoices();
340+ QStringList uuids;
341+ for(auto iter = choices_values.begin(); iter != choices_values.end() && unhandled_sections.size(); ++iter)
342+ {
343+ const auto& values = iter.value();
344+ auto iter_values = values.find("type");
345+ if (iter_values != values.end())
346+ {
347+ if (iter_values.value() == "folder")
348+ {
349+ auto iter_display_name = values.find("display-name");
350+ if (iter_display_name != values.end())
351+ {
352+ auto index = unhandled_sections.indexOf((*iter_display_name).toString());
353+ if (index != -1)
354+ {
355+ // we have to backup this section
356+ uuids << iter.key();
357+ unhandled_sections.removeAt(index);
358+ view_->add_task((*iter_display_name).toString(), "waiting", 0.0);
359+ }
360+ }
361+ }
362+ }
363+ }
364+
365+ if (!unhandled_sections.isEmpty())
366+ {
367+ QString error_message("The following sections were not found: \n");
368+ for (auto const & section : unhandled_sections)
369+ {
370+ error_message += QStringLiteral("\t %1 \n").arg(section);
371+ }
372+ view_->print_error_message(error_message);
373+ exit(1);
374+ }
375+
376+ for (auto const & uuid: uuids)
377+ {
378+ keeper_client_->enableBackup(uuid, true);
379+ }
380+ keeper_client_->startBackup();
381+ view_->start_printing_tasks();
382+}
383+
384+void CommandLineClient::run_restore(QStringList & sections)
385+{
386+ auto unhandled_sections = sections;
387+ auto choices_values = keeper_client_->getRestoreChoices();
388+ QStringList uuids;
389+ for(auto iter = choices_values.begin(); iter != choices_values.end(); ++iter)
390+ {
391+ const auto& values = iter.value();
392+
393+ QVariant choice_value;
394+ auto has_type = find_choice_value(values, "type", choice_value);
395+ if (has_type && choice_value == "folder")
396+ {
397+ QVariant display_name;
398+ auto has_display_name = find_choice_value(values, "display-name", display_name);
399+ if (!has_display_name)
400+ continue;
401+
402+ QVariant dir_name;
403+ auto has_dir_name = find_choice_value(values, "dir-name", dir_name);
404+ if (!has_dir_name)
405+ continue;
406+
407+ auto section_name = QStringLiteral("%1:%2").arg(display_name.toString()).arg(dir_name.toString());
408+ auto index = unhandled_sections.indexOf(section_name);
409+ if (index != -1)
410+ {
411+ // we have to restore this section
412+ uuids << iter.key();
413+ unhandled_sections.removeAt(index);
414+ view_->add_task(display_name.toString(), "waiting", 0.0);
415+ }
416+ }
417+ }
418+ if (!unhandled_sections.isEmpty())
419+ {
420+ QString error_message("The following sections were not found: \n");
421+ for (auto const & section : unhandled_sections)
422+ {
423+ error_message += QStringLiteral("\t %1 \n").arg(section);
424+ }
425+ view_->print_error_message(error_message);
426+ exit(1);
427+ }
428+
429+ for (auto const & uuid: uuids)
430+ {
431+ keeper_client_->enableRestore(uuid, true);
432+ }
433+ keeper_client_->startRestore();
434+ view_->start_printing_tasks();
435+}
436+
437+void CommandLineClient::list_backup_sections(QMap<QString, QVariantMap> const & choices_values)
438+{
439+ QStringList sections;
440+ for(auto iter = choices_values.begin(); iter != choices_values.end(); ++iter)
441+ {
442+ const auto& values = iter.value();
443+
444+ QVariant choice_value;
445+ auto has_type = find_choice_value(values, "type", choice_value);
446+ if (has_type && choice_value == "folder")
447+ {
448+ auto has_display_name = find_choice_value(values, "display-name", choice_value);
449+ if (has_display_name)
450+ {
451+ sections << choice_value.toString();
452+ }
453+ }
454+ }
455+ view_->print_sections(sections);
456+}
457+
458+void CommandLineClient::list_restore_sections(QMap<QString, QVariantMap> const & choices_values)
459+{
460+ QMap<QString, QList<QVariantMap>> values_per_dir;
461+
462+ for(auto iter = choices_values.begin(); iter != choices_values.end(); ++iter)
463+ {
464+ const auto& values = iter.value();
465+
466+ QVariant choice_value;
467+ auto has_type = find_choice_value(values, "type", choice_value);
468+ if (has_type && choice_value == "folder")
469+ {
470+ auto has_dir_name = find_choice_value(values, "dir-name", choice_value);
471+ if (!has_dir_name)
472+ continue;
473+ values_per_dir[choice_value.toString()].push_back(values);
474+ }
475+ }
476+
477+ QStringList sections;
478+ for(auto iter = values_per_dir.begin(); iter != values_per_dir.end(); ++iter)
479+ {
480+ for(auto iter_items = (*iter).begin(); iter_items != (*iter).end(); ++iter_items)
481+ {
482+ const auto& values = (*iter_items);
483+
484+ QVariant choice_value;
485+ auto has_type = find_choice_value(values, "type", choice_value);
486+ if (has_type && choice_value == "folder")
487+ {
488+ auto has_display_name = find_choice_value(values, "display-name", choice_value);
489+ if (has_display_name)
490+ {
491+ sections << QStringLiteral("%1:%2").arg(choice_value.toString()).arg(iter.key());
492+ }
493+ }
494+ }
495+ sections << "";
496+ }
497+ view_->print_sections(sections);
498+}
499+
500+void CommandLineClient::on_progress_changed()
501+{
502+ view_->progress_changed(keeper_client_->progress());
503+}
504+
505+void CommandLineClient::on_status_changed()
506+{
507+ view_->status_changed(keeper_client_->status());
508+}
509+
510+void CommandLineClient::on_keeper_client_finished()
511+{
512+ QCoreApplication::processEvents();
513+ view_->show_info();
514+ view_->clear_all();
515+ QCoreApplication::exit(0);
516+}
517+
518+bool CommandLineClient::find_choice_value(QVariantMap const & choice, QString const & id, QVariant & value)
519+{
520+ auto iter = choice.find(id);
521+ if (iter == choice.end())
522+ return false;
523+ value = (*iter);
524+ return true;
525+}
526
527=== added file 'src/cli/command-line-client.h'
528--- src/cli/command-line-client.h 1970-01-01 00:00:00 +0000
529+++ src/cli/command-line-client.h 2016-12-16 09:34:32 +0000
530@@ -0,0 +1,53 @@
531+/*
532+ * Copyright (C) 2016 Canonical, Ltd.
533+ *
534+ * This program is free software: you can redistribute it and/or modify it
535+ * under the terms of the GNU General Public License version 3, as published
536+ * by the Free Software Foundation.
537+ *
538+ * This program is distributed in the hope that it will be useful, but
539+ * WITHOUT ANY WARRANTY; without even the implied warranties of
540+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
541+ * PURPOSE. See the GNU General Public License for more details.
542+ *
543+ * You should have received a copy of the GNU General Public License along
544+ * with this program. If not, see <http://www.gnu.org/licenses/>.
545+ *
546+ * Authors:
547+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
548+ */
549+#pragma once
550+
551+#include <QMap>
552+#include <QObject>
553+#include <QScopedPointer>
554+#include <QTimer>
555+
556+class KeeperClient;
557+class CommandLineClientView;
558+
559+class CommandLineClient : public QObject
560+{
561+ Q_OBJECT
562+public:
563+ explicit CommandLineClient(QObject * parent = nullptr);
564+ ~CommandLineClient();
565+
566+ Q_DISABLE_COPY(CommandLineClient)
567+
568+ void run_list_sections(bool remote);
569+ void run_backup(QStringList & sections);
570+ void run_restore(QStringList & sections);
571+
572+private Q_SLOTS:
573+ void on_progress_changed();
574+ void on_status_changed();
575+ void on_keeper_client_finished();
576+
577+private:
578+ static bool find_choice_value(QVariantMap const & choice, QString const & id, QVariant & value);
579+ void list_backup_sections(QMap<QString, QVariantMap> const & choices);
580+ void list_restore_sections(QMap<QString, QVariantMap> const & choices);
581+ QScopedPointer<KeeperClient> keeper_client_;
582+ QScopedPointer<CommandLineClientView> view_;
583+};
584
585=== added file 'src/cli/command-line.cpp'
586--- src/cli/command-line.cpp 1970-01-01 00:00:00 +0000
587+++ src/cli/command-line.cpp 2016-12-16 09:34:32 +0000
588@@ -0,0 +1,188 @@
589+/*
590+ * Copyright (C) 2016 Canonical, Ltd.
591+ *
592+ * This program is free software: you can redistribute it and/or modify it
593+ * under the terms of the GNU General Public License version 3, as published
594+ * by the Free Software Foundation.
595+ *
596+ * This program is distributed in the hope that it will be useful, but
597+ * WITHOUT ANY WARRANTY; without even the implied warranties of
598+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
599+ * PURPOSE. See the GNU General Public License for more details.
600+ *
601+ * You should have received a copy of the GNU General Public License along
602+ * with this program. If not, see <http://www.gnu.org/licenses/>.
603+ *
604+ * Authors:
605+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
606+ */
607+#include "command-line.h"
608+
609+#include <QCommandLineParser>
610+#include <QDebug>
611+
612+#include <iostream>
613+
614+namespace
615+{
616+ // arguments
617+ constexpr const char ARGUMENT_LIST_SECTIONS[] = "list-sections";
618+ constexpr const char ARGUMENT_BACKUP[] = "backup";
619+ constexpr const char ARGUMENT_RESTORE[] = "restore";
620+
621+ // argument descriptions
622+ constexpr const char ARGUMENT_LIST_SECTIONS_DESCRIPTION[] = "List the sections available to backup";
623+ constexpr const char ARGUMENT_BACKUP_DESCRIPTION[] = "Starts a backup";
624+ constexpr const char ARGUMENT_RESTORE_DESCRIPTION[] = "Starts a restore";
625+
626+ // options
627+ constexpr const char OPTION_STORAGE[] = "storage";
628+ constexpr const char OPTION_SECTIONS[] = "sections";
629+
630+ // option descriptions
631+ constexpr const char OPTION_STORAGE_DESCRIPTION[] = "Lists the available sections stored at the storage";
632+ constexpr const char OPTION_SECTIONS_DESCRIPTION[] = "Lists the sections to backup or restore";
633+}
634+
635+CommandLineParser::CommandLineParser()
636+ : parser_(new QCommandLineParser)
637+{
638+ parser_->setApplicationDescription("Keeper command line client");
639+ parser_->addHelpOption();
640+ parser_->addVersionOption();
641+ parser_->addPositionalArgument(ARGUMENT_LIST_SECTIONS, QCoreApplication::translate("main", ARGUMENT_LIST_SECTIONS_DESCRIPTION));
642+ parser_->addPositionalArgument(ARGUMENT_BACKUP, QCoreApplication::translate("main", ARGUMENT_BACKUP_DESCRIPTION));
643+ parser_->addPositionalArgument(ARGUMENT_RESTORE, QCoreApplication::translate("main", ARGUMENT_RESTORE_DESCRIPTION));
644+}
645+
646+bool CommandLineParser::parse(QStringList const & arguments, QCoreApplication const & app, CommandLineParser::CommandArgs & cmd_args)
647+{
648+ parser_->parse(arguments);
649+ const QStringList args = parser_->positionalArguments();
650+ if (!check_number_of_args(args))
651+ {
652+ return false;
653+ }
654+
655+ if (args.size() == 1)
656+ {
657+ // if something fails at the process call the process exists
658+ if (args.at(0) == ARGUMENT_LIST_SECTIONS)
659+ {
660+ return handle_list_sections(app, cmd_args);
661+ }
662+ else if (args.at(0) == ARGUMENT_BACKUP)
663+ {
664+ return handle_backup(app, cmd_args);
665+ }
666+ else if (args.at(0) == ARGUMENT_RESTORE)
667+ {
668+ return handle_restore(app, cmd_args);
669+ }
670+ else
671+ {
672+ std::cerr << "Bad argument." << std::endl;
673+ std::cerr << parser_->errorText().toStdString() << std::endl;
674+ exit(1);
675+ }
676+ }
677+ else
678+ {
679+ qDebug() << "More or none arguments";
680+ // maybe we have the version or help options
681+ parser_->process(app);
682+ }
683+
684+ return false;
685+}
686+
687+bool CommandLineParser::handle_list_sections(QCoreApplication const & app, CommandLineParser::CommandArgs & cmd_args)
688+{
689+ parser_->clearPositionalArguments();
690+ parser_->addPositionalArgument(ARGUMENT_LIST_SECTIONS, QCoreApplication::translate("main", ARGUMENT_LIST_SECTIONS_DESCRIPTION));
691+
692+ parser_->addOptions({
693+ {{"r", OPTION_STORAGE},
694+ QCoreApplication::translate("main", OPTION_STORAGE_DESCRIPTION)},
695+ });
696+ parser_->process(app);
697+
698+ // it didn't exit... we're good
699+ cmd_args.sections.clear();
700+ cmd_args.storage.clear();
701+ if (parser_->isSet(OPTION_STORAGE))
702+ {
703+ cmd_args.cmd = CommandLineParser::Command::LIST_REMOTE_SECTIONS;
704+ }
705+ else
706+ {
707+ cmd_args.cmd = CommandLineParser::Command::LIST_LOCAL_SECTIONS;
708+ }
709+
710+ return true;
711+}
712+
713+bool CommandLineParser::handle_backup(QCoreApplication const & app, CommandLineParser::CommandArgs & cmd_args)
714+{
715+ parser_->clearPositionalArguments();
716+ parser_->addPositionalArgument(ARGUMENT_BACKUP, QCoreApplication::translate("main", ARGUMENT_BACKUP_DESCRIPTION));
717+
718+ parser_->addOptions({
719+ {{"s", OPTION_SECTIONS},
720+ QCoreApplication::translate("main", OPTION_SECTIONS_DESCRIPTION),
721+ QCoreApplication::translate("main", OPTION_SECTIONS_DESCRIPTION)
722+ },
723+ });
724+ parser_->process(app);
725+
726+ // it didn't exit... we're good
727+ cmd_args.sections.clear();
728+ cmd_args.storage.clear();
729+ cmd_args.cmd = CommandLineParser::Command::BACKUP;
730+ if (!parser_->isSet(OPTION_SECTIONS))
731+ {
732+ std::cerr << "You need to specify some sections to run a backup." << std::endl;
733+ return false;
734+ }
735+ cmd_args.sections = parser_->value(OPTION_SECTIONS).split(',');
736+
737+ return true;
738+}
739+
740+bool CommandLineParser::handle_restore(QCoreApplication const & app, CommandLineParser::CommandArgs & cmd_args)
741+{
742+ parser_->clearPositionalArguments();
743+ parser_->addPositionalArgument(ARGUMENT_RESTORE, QCoreApplication::translate("main", ARGUMENT_RESTORE_DESCRIPTION));
744+
745+ parser_->addOptions({
746+ {{"s", OPTION_SECTIONS},
747+ QCoreApplication::translate("main", OPTION_SECTIONS_DESCRIPTION),
748+ QCoreApplication::translate("main", OPTION_SECTIONS_DESCRIPTION)
749+ },
750+ });
751+ parser_->process(app);
752+
753+ // it didn't exit... we're good
754+ cmd_args.sections = QStringList();
755+ cmd_args.storage = QString();
756+ cmd_args.cmd = CommandLineParser::Command::RESTORE;
757+ if (!parser_->isSet(OPTION_SECTIONS))
758+ {
759+ std::cerr << "You need to specify some sections to run a restore." << std::endl;
760+ return false;
761+ }
762+ cmd_args.sections = parser_->value(OPTION_SECTIONS).split(',');
763+
764+ return true;
765+}
766+
767+bool CommandLineParser::check_number_of_args(QStringList const & args)
768+{
769+ if (args.size() > 1)
770+ {
771+ std::cerr << "Please, pass only one argument." << std::endl;
772+ std::cerr << parser_->helpText().toStdString() << std::endl;
773+ return false;
774+ }
775+ return true;
776+}
777
778=== added file 'src/cli/command-line.h'
779--- src/cli/command-line.h 1970-01-01 00:00:00 +0000
780+++ src/cli/command-line.h 2016-12-16 09:34:32 +0000
781@@ -0,0 +1,52 @@
782+/*
783+ * Copyright (C) 2016 Canonical, Ltd.
784+ *
785+ * This program is free software: you can redistribute it and/or modify it
786+ * under the terms of the GNU General Public License version 3, as published
787+ * by the Free Software Foundation.
788+ *
789+ * This program is distributed in the hope that it will be useful, but
790+ * WITHOUT ANY WARRANTY; without even the implied warranties of
791+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
792+ * PURPOSE. See the GNU General Public License for more details.
793+ *
794+ * You should have received a copy of the GNU General Public License along
795+ * with this program. If not, see <http://www.gnu.org/licenses/>.
796+ *
797+ * Authors:
798+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
799+ */
800+#pragma once
801+
802+#include <QCoreApplication>
803+#include <QSharedPointer>
804+
805+class QCommandLineParser;
806+
807+class CommandLineParser
808+{
809+public:
810+ Q_ENUMS(Command)
811+ enum class Command {LIST_LOCAL_SECTIONS, LIST_REMOTE_SECTIONS, BACKUP, RESTORE};
812+ struct CommandArgs
813+ {
814+ Command cmd;
815+ QStringList sections;
816+ QString storage;
817+ };
818+
819+ CommandLineParser();
820+ ~CommandLineParser() = default;
821+ Q_DISABLE_COPY(CommandLineParser)
822+
823+ bool parse(QStringList const & arguments, QCoreApplication const & app, CommandArgs & cmd_args);
824+
825+private:
826+ bool handle_list_sections(QCoreApplication const & app, CommandArgs & cmd_args);
827+ bool handle_backup(QCoreApplication const & app, CommandArgs & cmd_args);
828+ bool handle_restore(QCoreApplication const & app, CommandArgs & cmd_args);
829+
830+ bool check_number_of_args(QStringList const & args);
831+
832+ QSharedPointer<QCommandLineParser> parser_;
833+};
834
835=== modified file 'src/cli/main.cpp'
836--- src/cli/main.cpp 2016-11-22 09:37:40 +0000
837+++ src/cli/main.cpp 2016-12-16 09:34:32 +0000
838@@ -17,6 +17,8 @@
839 * Charles Kerr <charles.kerr@canonical.com>
840 * Xavi Garcia <xavi.garcia.mena@canonical.com>
841 */
842+#include "command-line.h"
843+#include "command-line-client.h"
844
845 #include <dbus-types.h>
846 #include <util/logging.h>
847@@ -29,6 +31,8 @@
848 #include <libintl.h>
849 #include <cstdlib>
850 #include <ctime>
851+#include <iostream>
852+
853
854 int
855 main(int argc, char **argv)
856@@ -37,7 +41,6 @@
857
858 QCoreApplication app(argc, argv);
859 DBusTypes::registerMetaTypes();
860-// Variant::registerMetaTypes();
861 std::srand(unsigned(std::time(nullptr)));
862
863 // boilerplate locale
864@@ -46,51 +49,31 @@
865 bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
866 textdomain(GETTEXT_PACKAGE);
867
868- if (argc == 2 && QString("--print-address") == argv[1])
869- {
870- qDebug() << QDBusConnection::sessionBus().baseService();
871- }
872-
873- qDebug() << "Argc =" << argc;
874- if (argc == 2 && QString("--use-uuids") == argv[1])
875- {
876- QScopedPointer<DBusInterfaceKeeperUser> user_iface(new DBusInterfaceKeeperUser(
877- DBusTypes::KEEPER_SERVICE,
878- DBusTypes::KEEPER_USER_PATH,
879- QDBusConnection::sessionBus()
880- ) );
881- QDBusReply<QVariantDictMap> choices = user_iface->call("GetBackupChoices");
882- if (!choices.isValid())
883- {
884- qWarning() << "Error getting backup choices:" << choices.error().message();
885- }
886-
887- QStringList uuids;
888- auto choices_values = choices.value();
889- for(auto iter = choices_values.begin(); iter != choices_values.end(); ++iter)
890- {
891- const auto& values = iter.value();
892- auto iter_values = values.find("type");
893- if (iter_values != values.end())
894- {
895- if (iter_values.value().toString() == "folder")
896- {
897- qDebug() << "Adding uuid" << iter.key() << "with type:" << "folder";
898- uuids << iter.key();
899- }
900- }
901- }
902-
903- QDBusReply<void> backup_reply = user_iface->call("StartBackup", uuids);
904-
905- if (!backup_reply.isValid())
906- {
907- qWarning() << "Error starting backup:" << backup_reply.error().message();
908- }
909- }
910- else
911- {
912- qWarning() << "FIXME";
913+ QCoreApplication::setApplicationName("keeper");
914+ QCoreApplication::setApplicationVersion("1.0");
915+
916+ CommandLineParser parser;
917+ CommandLineClient client;
918+ CommandLineParser::CommandArgs cmd_args;
919+ if (parser.parse(QCoreApplication::arguments(), app, cmd_args))
920+ {
921+ switch(cmd_args.cmd)
922+ {
923+ case CommandLineParser::Command::LIST_LOCAL_SECTIONS:
924+ client.run_list_sections(false);
925+ exit(0);
926+ break;
927+ case CommandLineParser::Command::LIST_REMOTE_SECTIONS:
928+ client.run_list_sections(true);
929+ exit(0);
930+ break;
931+ case CommandLineParser::Command::BACKUP:
932+ client.run_backup(cmd_args.sections);
933+ break;
934+ case CommandLineParser::Command::RESTORE:
935+ client.run_restore(cmd_args.sections);
936+ break;
937+ };
938 }
939
940
941
942=== modified file 'src/client/CMakeLists.txt'
943--- src/client/CMakeLists.txt 2016-08-26 09:30:11 +0000
944+++ src/client/CMakeLists.txt 2016-12-16 09:34:32 +0000
945@@ -11,6 +11,11 @@
946 ${CLIENT_HEADERS}
947 )
948
949+set_target_properties(${KEEPER_CLIENT_LIB} PROPERTIES
950+ AUTOMOC TRUE
951+ LINK_FLAGS "-Wl,--no-undefined"
952+)
953+
954 target_link_libraries(
955 ${KEEPER_CLIENT_LIB}
956 qdbus-stubs
957
958=== modified file 'src/client/client.cpp'
959--- src/client/client.cpp 2016-08-26 08:48:05 +0000
960+++ src/client/client.cpp 2016-12-16 09:34:32 +0000
961@@ -22,34 +22,62 @@
962 #include <client/client.h>
963
964 #include <qdbus-stubs/keeper_user_interface.h>
965+#include <qdbus-stubs/DBusPropertiesInterface.h>
966
967 struct KeeperClientPrivate final
968 {
969 Q_DISABLE_COPY(KeeperClientPrivate)
970
971- KeeperClientPrivate(QObject* parent)
972+ enum class TasksMode { IDLE_MODE, BACKUP_MODE, RESTORE_MODE };
973+
974+ explicit KeeperClientPrivate(QObject* /* parent */)
975 : userIface(new DBusInterfaceKeeperUser(
976 DBusTypes::KEEPER_SERVICE,
977 DBusTypes::KEEPER_USER_PATH,
978 QDBusConnection::sessionBus()
979- )),
980- status(""),
981- progress(0),
982- readyToBackup(false),
983- backupBusy(false),
984- stateTimer(parent)
985+ ))
986+ , propertiesIface(new DBusPropertiesInterface(
987+ DBusTypes::KEEPER_SERVICE,
988+ DBusTypes::KEEPER_USER_PATH,
989+ QDBusConnection::sessionBus()
990+ ))
991 {
992 }
993
994 ~KeeperClientPrivate() = default;
995
996+ struct TaskStatus
997+ {
998+ QString status;
999+ double percentage;
1000+ };
1001+
1002+ static bool stateIsFinal(QString const & stateString)
1003+ {
1004+ return (stateString == "complete" || stateString == "cancelled" || stateString == "failed");
1005+ }
1006+
1007+ static bool checkAllTasksFinished(QMap<QString, QVariantMap> const & state)
1008+ {
1009+ bool ret = true;
1010+ for (auto iter = state.begin(); ret && (iter != state.end()); ++iter)
1011+ {
1012+ auto statusString = (*iter).value("action").toString();
1013+ ret = stateIsFinal(statusString);
1014+ }
1015+ return ret;
1016+ }
1017+
1018 QScopedPointer<DBusInterfaceKeeperUser> userIface;
1019+ QScopedPointer<DBusPropertiesInterface> propertiesIface;
1020 QString status;
1021 QMap<QString, QVariantMap> backups;
1022- double progress;
1023- bool readyToBackup;
1024- bool backupBusy;
1025- QTimer stateTimer;
1026+ QMap<QString, QVariantMap> restores;
1027+ double progress = 0;
1028+ bool readyToBackup = false;
1029+ bool backupBusy = false;
1030+ QMap<QString, TaskStatus> task_status;
1031+ TasksMode mode = TasksMode::IDLE_MODE;
1032 };
1033
1034 KeeperClient::KeeperClient(QObject* parent) :
1035@@ -66,7 +94,7 @@
1036 iter.value()["enabled"] = false;
1037 }
1038
1039- connect(&d->stateTimer, &QTimer::timeout, this, &KeeperClient::stateUpdated);
1040+ connect(d->propertiesIface.data(), &DBusPropertiesInterface::PropertiesChanged, this, &KeeperClient::stateUpdated);
1041 }
1042
1043 KeeperClient::~KeeperClient() = default;
1044@@ -120,17 +148,19 @@
1045 }
1046 }
1047
1048+ d->task_status[uuid] = KeeperClientPrivate::TaskStatus{"", 0.0};
1049+
1050 Q_EMIT readyToBackupChanged();
1051 }
1052
1053+void KeeperClient::enableRestore(QString uuid, bool enabled)
1054+{
1055+ // Until we re-design the client we treat restores as backups
1056+ enableBackup(uuid, enabled);
1057+}
1058+
1059 void KeeperClient::startBackup()
1060 {
1061- // TODO: Instead of polling for state, we should be listening to a stateChanged signal
1062- if (!d->stateTimer.isActive())
1063- {
1064- d->stateTimer.start(200);
1065- }
1066-
1067 // Determine which backups are enabled, and start only those
1068 QStringList backupList;
1069 for(auto iter = d->backups.begin(); iter != d->backups.end(); ++iter)
1070@@ -145,6 +175,7 @@
1071 {
1072 startBackup(backupList);
1073
1074+ d->mode = KeeperClientPrivate::TasksMode::BACKUP_MODE;
1075 d->status = "Preparing Backup...";
1076 Q_EMIT statusChanged();
1077 d->backupBusy = true;
1078@@ -152,6 +183,30 @@
1079 }
1080 }
1081
1082+void KeeperClient::startRestore()
1083+{
1084+ // Determine which restores are enabled, and start only those
1085+ QStringList restoreList;
1086+ for(auto iter = d->backups.begin(); iter != d->backups.end(); ++iter)
1087+ {
1088+ if (iter.value().value("enabled").toBool())
1089+ {
1090+ restoreList.append(iter.key());
1091+ }
1092+ }
1093+
1094+ if (!restoreList.empty())
1095+ {
1096+ startRestore(restoreList);
1097+
1098+ d->mode = KeeperClientPrivate::TasksMode::RESTORE_MODE;
1099+ d->status = "Preparing Restore...";
1100+ Q_EMIT statusChanged();
1101+ d->backupBusy = true;
1102+ Q_EMIT backupBusyChanged();
1103+ }
1104+}
1105+
1106 QString KeeperClient::getBackupName(QString uuid)
1107 {
1108 return d->backups.value(uuid).value("display-name").toString();
1109@@ -170,6 +225,20 @@
1110 return choices.value();
1111 }
1112
1113+QMap<QString, QVariantMap> KeeperClient::getRestoreChoices() const
1114+{
1115+ QDBusPendingReply<QMap<QString, QVariantMap>> choices = d->userIface->call("GetRestoreChoices");
1116+
1117+ choices.waitForFinished();
1118+ if (!choices.isValid())
1119+ {
1120+ qWarning() << "Error getting restore choices:" << choices.error().message();
1121+ return QMap<QString, QVariantMap>();
1122+ }
1123+
1124+ return choices.value();
1125+}
1126+
1127 void KeeperClient::startBackup(const QStringList& uuids) const
1128 {
1129 QDBusReply<void> backupReply = d->userIface->call("StartBackup", uuids);
1130@@ -180,6 +249,16 @@
1131 }
1132 }
1133
1134+void KeeperClient::startRestore(const QStringList& uuids) const
1135+{
1136+ QDBusReply<void> backupReply = d->userIface->call("StartRestore", uuids);
1137+
1138+ if (!backupReply.isValid())
1139+ {
1140+ qWarning() << "Error starting restore:" << backupReply.error().message();
1141+ }
1142+}
1143+
1144 QMap<QString, QVariantMap> KeeperClient::getState() const
1145 {
1146 return d->userIface->state();
1147@@ -191,9 +270,26 @@
1148
1149 if (!states.empty())
1150 {
1151- // Calculate current total progress
1152- // TODO: May be better to monitor each backup's progress separately instead of total
1153- // to avoid irregular jumps in progress between larger and smaller backups
1154+ for (auto const & uuid : d->task_status.keys())
1155+ {
1156+ auto iter_state = states.find(uuid);
1157+ if (iter_state == states.end())
1158+ {
1159+ qWarning() << "State for uuid: " << uuid << " was not found";
1160+ }
1161+ auto state = (*iter_state);
1162+ double progress = state.value("percent-done").toDouble();
1163+ auto status = state.value("action").toString();
1164+
1165+ auto current_state = d->task_status[uuid];
1166+ if (current_state.status != status || current_state.percentage < progress)
1167+ {
1168+ d->task_status[uuid].status = status;
1169+ d->task_status[uuid].percentage = progress;
1170+ Q_EMIT(taskStatusChanged(state.value("display-name").toString(), status, progress));
1171+ }
1172+ }
1173+
1174 double totalProgress = 0;
1175 for (auto const& state : states)
1176 {
1177@@ -203,28 +299,45 @@
1178 d->progress = totalProgress / states.count();
1179 Q_EMIT progressChanged();
1180
1181+ auto allTasksFinished = d->checkAllTasksFinished(states);
1182 // Update backup status
1183+ QString statusString;
1184+ if (d->mode == KeeperClientPrivate::TasksMode::BACKUP_MODE)
1185+ {
1186+ statusString = QStringLiteral("Backup");
1187+ }
1188+ else if (d->mode == KeeperClientPrivate::TasksMode::RESTORE_MODE)
1189+ {
1190+ statusString = QStringLiteral("Restore");
1191+ }
1192 if (d->progress > 0 && d->progress < 1)
1193 {
1194- d->status = "Backup In Progress...";
1195- Q_EMIT statusChanged();
1196-
1197- d->backupBusy = true;
1198- Q_EMIT backupBusyChanged();
1199- }
1200- else if (d->progress >= 1)
1201- {
1202- d->status = "Backup Complete";
1203+ d->status = statusString + QStringLiteral(" In Progress...");
1204+ Q_EMIT statusChanged();
1205+
1206+ d->backupBusy = true;
1207+ Q_EMIT backupBusyChanged();
1208+ }
1209+ else if (d->progress >= 1 && !allTasksFinished)
1210+ {
1211+ d->status = statusString + QStringLiteral(" Finishing...");
1212+ Q_EMIT statusChanged();
1213+
1214+ d->backupBusy = true;
1215+ Q_EMIT backupBusyChanged();
1216+ }
1217+ else if (allTasksFinished)
1218+ {
1219+ d->status = statusString + QStringLiteral(" Complete");
1220 Q_EMIT statusChanged();
1221
1222 d->backupBusy = false;
1223 Q_EMIT backupBusyChanged();
1224+ }
1225
1226- // Stop state timer
1227- if (d->stateTimer.isActive())
1228- {
1229- d->stateTimer.stop();
1230- }
1231+ if (d->checkAllTasksFinished(states))
1232+ {
1233+ Q_EMIT(finished());
1234 }
1235 }
1236 }
1237
1238=== modified file 'src/helper/CMakeLists.txt'
1239--- src/helper/CMakeLists.txt 2016-11-11 14:50:06 +0000
1240+++ src/helper/CMakeLists.txt 2016-12-16 09:34:32 +0000
1241@@ -18,6 +18,21 @@
1242 ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}
1243 )
1244
1245+set(
1246+ FOLDER_RESTORE
1247+ folder-restore.sh
1248+)
1249+configure_file(
1250+ ${CMAKE_CURRENT_SOURCE_DIR}/${FOLDER_RESTORE}.in
1251+ ${CMAKE_CURRENT_BINARY_DIR}/${FOLDER_RESTORE}
1252+)
1253+
1254+install(
1255+ PROGRAMS
1256+ ${CMAKE_CURRENT_BINARY_DIR}/${FOLDER_RESTORE}
1257+ DESTINATION
1258+ ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}
1259+)
1260
1261 #
1262 # the library
1263
1264=== added file 'src/helper/folder-restore.sh.in'
1265--- src/helper/folder-restore.sh.in 1970-01-01 00:00:00 +0000
1266+++ src/helper/folder-restore.sh.in 2016-12-16 09:34:32 +0000
1267@@ -0,0 +1,23 @@
1268+#!/bin/bash
1269+#
1270+# Copyright (C) 2016 Canonical, Ltd.
1271+#
1272+# This program is free software: you can redistribute it and/or modify it
1273+# under the terms of the GNU General Public License version 3, as published
1274+# by the Free Software Foundation.
1275+#
1276+# This program is distributed in the hope that it will be useful, but
1277+# WITHOUT ANY WARRANTY; without even the implied warranties of
1278+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1279+# PURPOSE. See the GNU General Public License for more details.
1280+#
1281+# You should have received a copy of the GNU General Public License along
1282+# with this program. If not, see <http://www.gnu.org/licenses/>.
1283+#
1284+# Authors:
1285+# Xavi Garcia <xavi.garcia.mena@canonical.com>
1286+# Charles Kerr <charles.kerr@canonical.com>
1287+#
1288+
1289+echo $PWD
1290+# TODO, please place here your restore helper code.
1291
1292=== modified file 'src/helper/restore-helper.cpp'
1293--- src/helper/restore-helper.cpp 2016-11-22 09:37:40 +0000
1294+++ src/helper/restore-helper.cpp 2016-12-16 09:34:32 +0000
1295@@ -100,6 +100,11 @@
1296 std::bind(&RestoreHelperPrivate::on_ready_read, this)
1297 );
1298
1299+ // TODO investigate why UAL takes so long to call the helper started callback
1300+ // At this point we are sure that the helper started, as it is the helper
1301+ // the ones that asks for a downloader socket.
1302+ q_ptr->Helper::on_helper_started();
1303+
1304 // maybe there's data already to be read
1305 process_more();
1306
1307
1308=== modified file 'src/qdbus-stubs/CMakeLists.txt'
1309--- src/qdbus-stubs/CMakeLists.txt 2016-08-10 05:41:26 +0000
1310+++ src/qdbus-stubs/CMakeLists.txt 2016-12-16 09:34:32 +0000
1311@@ -84,6 +84,23 @@
1312 KeeperUserAdaptor
1313 )
1314
1315+set(
1316+ properties_xml
1317+ "org.freedesktop.DBus.Properties.xml"
1318+)
1319+
1320+set_source_files_properties(
1321+ "${properties_xml}"
1322+ PROPERTIES
1323+ NO_NAMESPACE YES
1324+ CLASSNAME DBusPropertiesInterface
1325+)
1326+
1327+qt5_add_dbus_interface(
1328+ interface_files
1329+ ${properties_xml}
1330+ DBusPropertiesInterface
1331+)
1332 #
1333 #
1334
1335@@ -99,7 +116,7 @@
1336 ${adaptor_files}
1337 )
1338
1339-target_link_libraries(
1340- ${STUBS_LIB}
1341- backup-helper
1342-)
1343+#target_link_libraries(
1344+# ${STUBS_LIB}
1345+# backup-helper
1346+#)
1347
1348=== modified file 'src/qdbus-stubs/dbus-types.h'
1349--- src/qdbus-stubs/dbus-types.h 2016-07-14 15:31:11 +0000
1350+++ src/qdbus-stubs/dbus-types.h 2016-12-16 09:34:32 +0000
1351@@ -22,7 +22,6 @@
1352 #include <QtCore>
1353 #include <QString>
1354 #include <QVariantMap>
1355-#include <helper/helper.h>
1356
1357 typedef QMap<QString, QVariantMap> QVariantDictMap;
1358 Q_DECLARE_METATYPE(QVariantDictMap)
1359@@ -39,7 +38,7 @@
1360
1361 qDBusRegisterMetaType<QVariantDictMap>();
1362 qDBusRegisterMetaType<QStringMap>();
1363- Helper::registerMetaTypes();
1364+// Helper::registerMetaTypes();
1365 }
1366
1367 constexpr const char KEEPER_SERVICE[] = "com.canonical.keeper";
1368
1369=== added file 'src/qdbus-stubs/org.freedesktop.DBus.Properties.xml'
1370--- src/qdbus-stubs/org.freedesktop.DBus.Properties.xml 1970-01-01 00:00:00 +0000
1371+++ src/qdbus-stubs/org.freedesktop.DBus.Properties.xml 2016-12-16 09:34:32 +0000
1372@@ -0,0 +1,27 @@
1373+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
1374+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
1375+<node>
1376+ <interface name="org.freedesktop.DBus.Properties">
1377+ <method name="Get">
1378+ <arg type="s" name="interface_name" direction="in"/>
1379+ <arg type="s" name="property_name" direction="in"/>
1380+ <arg type="v" name="value" direction="out"/>
1381+ </method>
1382+ <method name="GetAll">
1383+ <arg type="s" name="interface_name" direction="in"/>
1384+ <arg type="a{sv}" name="properties" direction="out"/>
1385+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
1386+ </method>
1387+ <method name="Set">
1388+ <arg type="s" name="interface_name" direction="in"/>
1389+ <arg type="s" name="property_name" direction="in"/>
1390+ <arg type="v" name="value" direction="in"/>
1391+ </method>
1392+ <signal name="PropertiesChanged">
1393+ <arg type="s" name="interface_name"/>
1394+ <arg type="a{sv}" name="changed_properties"/>
1395+ <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
1396+ <arg type="as" name="invalidated_properties"/>
1397+ </signal>
1398+ </interface>
1399+</node>
1400
1401=== modified file 'src/service/keeper.cpp'
1402--- src/service/keeper.cpp 2016-11-22 09:37:40 +0000
1403+++ src/service/keeper.cpp 2016-12-16 09:34:32 +0000
1404@@ -75,6 +75,9 @@
1405 , restore_choices_(restore_choices)
1406 , task_manager_{helper_registry, storage_}
1407 {
1408+ QObject::connect(&task_manager_, &TaskManager::finished,
1409+ std::bind(&KeeperPrivate::on_task_manager_finished, this)
1410+ );
1411 }
1412
1413 enum class ChoicesType { BACKUP_CHOICES, RESTORES_CHOICES };
1414@@ -213,6 +216,7 @@
1415 QVariantDictMap get_restore_choices_var_dict_map(QDBusConnection bus,
1416 QDBusMessage const & msg)
1417 {
1418+ cached_restore_choices_.clear();
1419 connections_.connect_oneshot(
1420 this,
1421 &KeeperPrivate::restore_choices_ready,
1422@@ -306,6 +310,13 @@
1423
1424 private:
1425
1426+ void on_task_manager_finished()
1427+ {
1428+ // force a backup choices regeneration to avoid repeating uuids
1429+ // between backups
1430+ invalidate_choices_cache();
1431+ }
1432+
1433 void check_for_unhandled_tasks(QSet<QString> const & unhandled,
1434 QDBusConnection bus,
1435 QDBusMessage const & msg )
1436
1437=== modified file 'src/service/main.cpp'
1438--- src/service/main.cpp 2016-08-10 05:41:26 +0000
1439+++ src/service/main.cpp 2016-12-16 09:34:32 +0000
1440@@ -20,6 +20,7 @@
1441
1442 #include "dbus-types.h"
1443 #include "helper/data-dir-registry.h"
1444+#include "helper/helper.h"
1445 #include "service/backup-choices.h"
1446 #include "service/restore-choices.h"
1447 #include "service/keeper.h"
1448@@ -45,7 +46,7 @@
1449
1450 QCoreApplication app(argc, argv);
1451 DBusTypes::registerMetaTypes();
1452-// Variant::registerMetaTypes();
1453+ Helper::registerMetaTypes();
1454 std::srand(unsigned(std::time(nullptr)));
1455
1456 util::UnixSignalHandler handler([]{
1457
1458=== modified file 'src/service/task-manager.cpp'
1459--- src/service/task-manager.cpp 2016-11-21 11:09:07 +0000
1460+++ src/service/task-manager.cpp 2016-12-16 09:34:32 +0000
1461@@ -111,6 +111,7 @@
1462 // notify the initial state once for all tasks
1463 notify_state_changed();
1464 remaining_tasks_.clear();
1465+ Q_EMIT(q_ptr->finished());
1466 }
1467
1468 private:
1469@@ -172,17 +173,18 @@
1470 set_current_task_action(task_->to_string(Helper::State::FAILED));
1471 }
1472 active_manifest_.reset();
1473+
1474+ Q_EMIT(q_ptr->finished());
1475 }
1476
1477 void on_helper_state_changed(Helper::State state)
1478 {
1479 auto backup_task_ = qSharedPointerDynamicCast<KeeperTaskBackup>(task_);
1480 auto& td = task_data_[current_task_];
1481- update_task_state(td);
1482
1483 // for the last completed backup task we delay updating the
1484 // state until the manifest file is stored
1485- if (remaining_tasks_.size() || state != Helper::State::COMPLETE)
1486+ if (remaining_tasks_.size() || (state != Helper::State::COMPLETE && state != Helper::State::FAILED))
1487 update_task_state(td);
1488
1489 if (state == Helper::State::COMPLETE || state == Helper::State::FAILED)
1490@@ -213,6 +215,10 @@
1491 );
1492 active_manifest_->store();
1493 }
1494+ else
1495+ {
1496+ update_task_state(td);
1497+ }
1498 }
1499 }
1500 }
1501
1502=== modified file 'src/service/task-manager.h'
1503--- src/service/task-manager.h 2016-11-21 11:09:07 +0000
1504+++ src/service/task-manager.h 2016-12-16 09:34:32 +0000
1505@@ -65,6 +65,7 @@
1506 Q_SIGNALS:
1507 void socket_ready(int reply);
1508 void state_changed();
1509+ void finished();
1510
1511 private:
1512 QScopedPointer<TaskManagerPrivate> const d_ptr;
1513
1514=== modified file 'src/storage-framework/storage_framework_client.cpp'
1515--- src/storage-framework/storage_framework_client.cpp 2016-11-24 14:48:35 +0000
1516+++ src/storage-framework/storage_framework_client.cpp 2016-12-16 09:34:32 +0000
1517@@ -168,7 +168,7 @@
1518 connection_helper_.connect_future(
1519 get_keeper_folder(root, dir_name, false),
1520 std::function<void(sf::Folder::SPtr const&)>{
1521- [this, fi, file_name](sf::Folder::SPtr const& keeper_root){
1522+ [this, fi, file_name, root](sf::Folder::SPtr const& keeper_root){
1523 if (!keeper_root)
1524 {
1525 qWarning() << "Error accessing keeper root folder";
1526@@ -183,12 +183,12 @@
1527 connection_helper_.connect_future(
1528 get_storage_framework_file(keeper_root, file_name),
1529 std::function<void(sf::File::SPtr const&)>{
1530- [this, fi](sf::File::SPtr const& sf_file){
1531+ [this, fi, root, keeper_root](sf::File::SPtr const& sf_file){
1532 if (sf_file) {
1533 connection_helper_.connect_future(
1534 sf_file->create_downloader(),
1535 std::function<void(sf::Downloader::SPtr const&)>{
1536- [this, fi, sf_file](sf::Downloader::SPtr const& sf_downloader){
1537+ [this, fi, sf_file, keeper_root, root](sf::Downloader::SPtr const& sf_downloader){
1538 std::shared_ptr<Downloader> ret;
1539 if (sf_downloader)
1540 {
1541
1542=== modified file 'tests/unit/helper/CMakeLists.txt'
1543--- tests/unit/helper/CMakeLists.txt 2016-09-27 11:22:15 +0000
1544+++ tests/unit/helper/CMakeLists.txt 2016-12-16 09:34:32 +0000
1545@@ -28,6 +28,7 @@
1546 target_link_libraries(
1547 ${SPEED_TEST}
1548 ${UNIT_TEST_LIBRARIES}
1549+ storage-framework
1550 Qt5::Core
1551 Qt5::DBus
1552 Qt5::Test
1553
1554=== modified file 'tests/unit/manifest/CMakeLists.txt'
1555--- tests/unit/manifest/CMakeLists.txt 2016-10-06 14:53:44 +0000
1556+++ tests/unit/manifest/CMakeLists.txt 2016-12-16 09:34:32 +0000
1557@@ -27,6 +27,7 @@
1558 target_link_libraries(
1559 ${MANIFEST_TEST}
1560 ${UNIT_TEST_LIBRARIES}
1561+ backup-helper
1562 Qt5::Core
1563 Qt5::DBus
1564 Qt5::Test
1565
1566=== modified file 'tests/unit/metadata-providers/CMakeLists.txt'
1567--- tests/unit/metadata-providers/CMakeLists.txt 2016-07-31 17:20:13 +0000
1568+++ tests/unit/metadata-providers/CMakeLists.txt 2016-12-16 09:34:32 +0000
1569@@ -11,6 +11,9 @@
1570 target_link_libraries(
1571 ${TEST_NAME}
1572 ${UNIT_TEST_LIBRARIES}
1573+ storage-framework
1574+ backup-helper
1575+ ${SERVICE_DEVEL_SF_DEPS_LIBRARIES}
1576 Qt5::Core
1577 Qt5::DBus
1578 Qt5::Test

Subscribers

People subscribed via source and target branches

to all changes: