Fix scheduler issues.

This commit is contained in:
Manu 2018-10-31 20:42:11 +08:00
parent c896388ae8
commit 44cbd0e127
10 changed files with 112 additions and 82 deletions

View File

@ -14,11 +14,10 @@ class VortaApp(QApplication):
self.thread = None
self.setQuitOnLastWindowClosed(False)
self.scheduler = VortaScheduler(self)
self.profile = BackupProfileModel.get(id=1)
# Prepare tray and connect events.
self.tray = TrayMenu(self)
self.tray.start_backup.connect(self.on_create_backup)
self.tray.start_backup.connect(self.create_backup)
self.tray.open_main_window.connect(self.on_open_main_window)
# Prepare main window
@ -27,16 +26,53 @@ class VortaApp(QApplication):
if not getattr(sys, 'frozen', False):
self.main_window.show()
def on_create_backup(self):
if self.thread and self.app.isRunning():
self.app.process.kill()
self.app.terminate()
@property
def profile(self):
return BackupProfileModel.get(id=1)
def cancel_backup(self):
if self.thread and self.thread.isRunning():
self.thread.process.kill()
self.thread.terminate()
def create_backup(self):
if self.thread and self.thread.isRunning():
print('backup already in progress')
else:
msg = BorgThread.prepare_runner()
if msg['ok']:
self.thread = BorgThread(msg['cmd'], msg['params'])
self.thread.updated.connect(self.create_backup_log)
self.thread.result.connect(self.create_backup_result)
self.thread.start()
# TODO: error dialog
def on_open_main_window(self):
self.main_window.show()
def create_backup_log(self, text):
print(text)
def create_backup_result(self, result):
self.createStartBtn.setEnabled(True)
self.createStartBtn.repaint()
self.set_status(progress_max=100)
if result['returncode'] == 0:
new_snapshot, created = SnapshotModel.get_or_create(
snapshot_id=result['data']['archive']['id'],
defaults={
'name': result['data']['archive']['name'],
'time': parser.parse(result['data']['archive']['start']),
'repo': self.profile.repo
}
)
new_snapshot.save()
if 'cache' in result['data'] and created:
stats = result['data']['cache']['stats']
repo = self.profile.repo
repo.total_size = stats['total_size']
repo.unique_csize = stats['unique_csize']
repo.unique_size = stats['unique_size']
repo.total_unique_chunks = stats['total_unique_chunks']
repo.save()
self.snapshotTab.populate()

View File

@ -43,13 +43,13 @@
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
<number>15</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">

View File

@ -32,7 +32,10 @@
</sizepolicy>
</property>
<property name="text">
<string>Remote repository. The place to store your backups.</string>
<string>Configure your remote backup repository. First you will need a SSH key to login without a password. If you already have one, just keep it at the default. Next you can add a new or existing repository.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
@ -53,14 +56,14 @@
<property name="rightMargin">
<number>25</number>
</property>
<item row="0" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Repository:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<item row="2" column="1">
<widget class="QComboBox" name="repoSelector">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -75,7 +78,7 @@
</item>
</widget>
</item>
<item row="1" column="1">
<item row="3" column="1">
<widget class="QLabel" name="label_5">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Need hosting for your Borg repository? &lt;a href=&quot;https://www.borgbackup.org/support/commercial.html&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;View providers&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -85,14 +88,24 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_14">
<item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>SSH Key:</string>
<string>Compression:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="6" column="1">
<widget class="QComboBox" name="repoCompression"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>SSH Login Key:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="sshComboBox">
<item>
<property name="text">
@ -106,20 +119,10 @@
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Compression:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="repoCompression"/>
</item>
<item row="3" column="1">
<item row="1" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>To access repository securely.</string>
<string>To access repository securely. Keep at default if you already have one. Or create new key.</string>
</property>
</widget>
</item>
@ -139,6 +142,9 @@
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>20</number>
</property>
<property name="rightMargin">
<number>25</number>
</property>

View File

@ -252,7 +252,7 @@ font-weight: bold;
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;(&lt;a href=&quot;https://borgbackup.readthedocs.io/en/stable/usage.html#borg-prune&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;More&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;(&lt;a href=&quot;https://borgbackup.readthedocs.io/en/stable/usage.html#borg-prune&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;How?&lt;/span&gt;&lt;/a&gt;)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>

View File

@ -20,6 +20,7 @@ class BorgThread(QtCore.QThread):
def __init__(self, cmd, params):
super().__init__(QApplication.instance())
# Find packaged borg binary. Prefer globally installed.
if not shutil.which('borg'):
meipass_borg = os.path.join(sys._MEIPASS, 'bin', 'borg')
@ -29,9 +30,8 @@ class BorgThread(QtCore.QThread):
env = os.environ.copy()
env['BORG_HOSTNAME_IS_UNIQUE'] = '1'
if params.get('password') and params['password']:
if params.get('password') and params['password'] is not None:
env['BORG_PASSPHRASE'] = params['password']
params['password'] = '***'
env['BORG_RSH'] = 'ssh -oStrictHostKeyChecking=no'
if params.get('ssh_key') and params['ssh_key']:
@ -43,7 +43,7 @@ class BorgThread(QtCore.QThread):
self.process = None
def run(self):
log_entry = EventLogModel(category='borg-run', subcommand=self.cmd[1], params=self.params)
log_entry = EventLogModel(category='borg-run', subcommand=self.cmd[1])
log_entry.save()
self.process = Popen(self.cmd, stdout=PIPE, stderr=PIPE, bufsize=1, universal_newlines=True, env=self.env)
for line in iter(self.process.stderr.readline, ''):
@ -103,7 +103,7 @@ class BorgThread(QtCore.QThread):
wifi_is_disallowed = WifiSettingModel.select().where(
WifiSettingModel.ssid == current_wifi
& WifiSettingModel.allowed == 0
# & WifiSettingModel.profile == profile
& WifiSettingModel.profile == profile
)
if wifi_is_disallowed.count() > 0:
ret['message'] = 'Current Wifi is not allowed.'
@ -122,11 +122,10 @@ class BorgThread(QtCore.QThread):
exclude_dirs.append(expanded_directory)
if exclude_dirs:
pattern_file = tempfile.NamedTemporaryFile('w')
pattern_file = tempfile.NamedTemporaryFile('w', delete=False)
pattern_file.write('\n'.join(exclude_dirs))
pattern_file.flush()
cmd.extend(['--exclude-from', pattern_file.name])
params['pattern_file'] = pattern_file
if profile.exclude_if_present is not None:
for f in profile.exclude_if_present.split('\n'):
@ -139,7 +138,7 @@ class BorgThread(QtCore.QThread):
for f in SourceDirModel.select():
cmd.append(f.dir)
ret['message'] = 'Ready to start backup..'
ret['message'] = 'Starting backup..'
ret['ok'] = True
ret['cmd'] = cmd
ret['params'] = params

View File

@ -6,11 +6,14 @@ class VortaScheduler(QtScheduler):
def __init__(self, parent):
super().__init__()
self.app = parent
self.start()
self.reload()
def reload(self):
self.remove_all_jobs()
profile = self.app.profile
if profile.schedule_mode == 'off':
self.next_job = 'Manual Backups'
return None
elif profile.schedule_mode == 'interval':
trigger = cron.CronTrigger(hour=f'*/{profile.schedule_interval_hours}',
@ -19,12 +22,6 @@ class VortaScheduler(QtScheduler):
trigger = cron.CronTrigger(hour=profile.schedule_fixed_hour,
minute=profile.schedule_fixed_minute)
self.add_job(self.app.on_create_backup, trigger, id='create-backup', misfire_grace_time=180)
self.start()
def next_job(self):
if self.get_jobs():
job = self.scheduler.get_job('create-backup')
return f"Next run: {job.next_run_time.strftime('%Y-%m-%d %H:%M')}"
else:
return 'Manual Backups'
self.add_job(self.app.create_backup, trigger, id='create-backup', misfire_grace_time=180)
job = self.get_job('create-backup')
self.next_job = f"Next run: {job.next_run_time.strftime('%Y-%m-%d %H:%M')}"

View File

@ -17,7 +17,7 @@ class TrayMenu(QSystemTrayIcon):
self.app = parent
menu = QMenu()
self.status = menu.addAction(self.app.scheduler.next_job())
self.status = menu.addAction(self.app.scheduler.next_job)
self.status.setEnabled(False)
self.create_action = menu.addAction("Backup Now")
@ -53,5 +53,5 @@ class TrayMenu(QSystemTrayIcon):
self.status.setText('Backup in Progress')
self.create_action.setText('Cancel Backup')
else:
self.status.setText(self.app.scheduler.next_job())
self.status.setText(self.app.scheduler.next_job)
self.create_action.setText('Backup Now')

View File

@ -83,27 +83,27 @@ class MainWindow(MainWindowBase, MainWindowUI):
except:
pass
def create_get_result(self, result):
self.createStartBtn.setEnabled(True)
self.createStartBtn.repaint()
self.set_status(progress_max=100)
if result['returncode'] == 0:
new_snapshot, created = SnapshotModel.get_or_create(
snapshot_id=result['data']['archive']['id'],
defaults={
'name': result['data']['archive']['name'],
'time': parser.parse(result['data']['archive']['start']),
'repo': self.profile.repo
}
)
new_snapshot.save()
if 'cache' in result['data'] and created:
stats = result['data']['cache']['stats']
repo = self.profile.repo
repo.total_size = stats['total_size']
repo.unique_csize = stats['unique_csize']
repo.unique_size = stats['unique_size']
repo.total_unique_chunks = stats['total_unique_chunks']
repo.save()
self.snapshotTab.populate()
# def create_get_result(self, result):
# self.createStartBtn.setEnabled(True)
# self.createStartBtn.repaint()
# self.set_status(progress_max=100)
# if result['returncode'] == 0:
# new_snapshot, created = SnapshotModel.get_or_create(
# snapshot_id=result['data']['archive']['id'],
# defaults={
# 'name': result['data']['archive']['name'],
# 'time': parser.parse(result['data']['archive']['start']),
# 'repo': self.profile.repo
# }
# )
# new_snapshot.save()
# if 'cache' in result['data'] and created:
# stats = result['data']['cache']['stats']
# repo = self.profile.repo
# repo.total_size = stats['total_size']
# repo.unique_csize = stats['unique_csize']
# repo.unique_size = stats['unique_size']
# repo.total_unique_chunks = stats['total_unique_chunks']
# repo.save()
# self.snapshotTab.populate()
#

View File

@ -41,7 +41,7 @@ class ScheduleTab(ScheduleBase, ScheduleUI):
self.scheduleApplyButton.clicked.connect(self.on_scheduler_apply)
self.nextBackupDateTimeLabel.setText(self.app.scheduler.next_job())
self.nextBackupDateTimeLabel.setText(self.app.scheduler.next_job)
self.init_wifi()
self.init_logs()
@ -103,5 +103,5 @@ class ScheduleTab(ScheduleBase, ScheduleUI):
self.profile.schedule_fixed_hour, self.profile.schedule_fixed_minute = qtime.hour(), qtime.minute()
self.profile.save()
self.app.scheduler.reload()
self.nextBackupDateTimeLabel.setText(self.app.scheduler.next_job())
self.nextBackupDateTimeLabel.setText(self.app.scheduler.next_job)
self.nextBackupDateTimeLabel.repaint()

View File

@ -3,14 +3,12 @@ import pytest
import io
import peewee
from PyQt5 import QtCore
from PyQt5.QtWidgets import QMenu, QApplication, QSystemTrayIcon
import vorta.borg_runner
import vorta.models
from vorta.application import VortaApp
from vorta.views.repo_add import AddRepoWindow
from vorta.models import EventLogModel, RepoModel
from vorta.tray_menu import TrayMenu
@pytest.fixture()
@ -27,12 +25,6 @@ def main(app, qtbot):
qtbot.addWidget(main)
return main
# def test_tray(app, qtbot):
# # app.tray.activated.emit(QSystemTrayIcon.Context)
# menu = app.tray.contextMenu()
# qtbot.addWidget(menu)
# menu.popup(QtCore.QPoint())
def test_repo_tab(main, qtbot):
qtbot.mouseClick(main.createStartBtn, QtCore.Qt.LeftButton)