Exemple #1
0
def test_repo_add_success(app, qtbot, mocker, borg_json_output):
    LONG_PASSWORD = '******'
    # Add new repo window
    main = app.main_window
    add_repo_window = AddRepoWindow(main)
    qtbot.addWidget(add_repo_window)
    test_repo_url = f'vorta-test-repo.{uuid.uuid4()}.com:repo'  # Random repo URL to avoid macOS keychain

    qtbot.keyClicks(add_repo_window.repoURL, test_repo_url)
    qtbot.keyClicks(add_repo_window.passwordLineEdit, LONG_PASSWORD)

    stdout, stderr = borg_json_output('info')
    popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0)
    mocker.patch.object(vorta.borg.borg_thread, 'Popen', return_value=popen_result)

    qtbot.mouseClick(add_repo_window.saveButton, QtCore.Qt.LeftButton)

    with qtbot.waitSignal(add_repo_window.thread.result, timeout=3000) as blocker:
        pass

    main.repoTab.process_new_repo(blocker.args[0])

    qtbot.waitUntil(lambda: EventLogModel.select().count() == 2)
    assert EventLogModel.select().count() == 2
    assert RepoModel.get(id=2).url == test_repo_url

    from vorta.utils import keyring
    assert keyring.get_password("vorta-repo", RepoModel.get(id=2).url) == LONG_PASSWORD
Exemple #2
0
    def init_logs(self):
        self.logTableWidget.setAlternatingRowColors(True)
        header = self.logTableWidget.horizontalHeader()
        header.setVisible(True)
        [
            header.setSectionResizeMode(i, QHeaderView.ResizeToContents)
            for i in range(5)
        ]
        header.setSectionResizeMode(3, QHeaderView.Stretch)

        self.logTableWidget.setSelectionBehavior(QTableView.SelectRows)
        self.logTableWidget.setEditTriggers(QTableView.NoEditTriggers)

        event_logs = [
            s for s in EventLogModel.select().order_by(
                EventLogModel.start_time.desc())
        ]

        for row, log_line in enumerate(event_logs):
            self.logTableWidget.insertRow(row)
            formatted_time = log_line.start_time.strftime('%Y-%m-%d %H:%M')
            self.logTableWidget.setItem(row, 0,
                                        QTableWidgetItem(formatted_time))
            self.logTableWidget.setItem(row, 1,
                                        QTableWidgetItem(log_line.category))
            self.logTableWidget.setItem(row, 2,
                                        QTableWidgetItem(log_line.subcommand))
            self.logTableWidget.setItem(row, 3,
                                        QTableWidgetItem(log_line.repo_url))
            self.logTableWidget.setItem(
                row, 4, QTableWidgetItem(str(log_line.returncode)))
        self.logTableWidget.setRowCount(len(event_logs))
        self._draw_next_scheduled_backup()
Exemple #3
0
def test_repo_add_success(qapp, qtbot, mocker, borg_json_output):
    # Add new repo window
    main = qapp.main_window
    main.repoTab.repoSelector.setCurrentIndex(1)
    add_repo_window = main.repoTab._window
    test_repo_url = f'vorta-test-repo.{uuid.uuid4()}.com:repo'  # Random repo URL to avoid macOS keychain

    qtbot.keyClicks(add_repo_window.repoURL, test_repo_url)
    qtbot.keyClicks(add_repo_window.passwordLineEdit, LONG_PASSWORD)
    qtbot.keyClicks(add_repo_window.confirmLineEdit, LONG_PASSWORD)

    stdout, stderr = borg_json_output('info')
    popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0)
    mocker.patch.object(vorta.borg.borg_thread, 'Popen', return_value=popen_result)

    qtbot.mouseClick(add_repo_window.saveButton, QtCore.Qt.LeftButton)

    with qtbot.waitSignal(add_repo_window.thread.result, timeout=3000) as _:
        pass

    assert EventLogModel.select().count() == 2
    assert RepoModel.get(id=2).url == test_repo_url

    keyring = VortaKeyring.get_keyring()
    assert keyring.get_password("vorta-repo", RepoModel.get(id=2).url) == LONG_PASSWORD
    assert main.repoTab.repoSelector.currentText() == test_repo_url
Exemple #4
0
    def populate_logs(self):
        event_logs = [
            s for s in EventLogModel.select().order_by(
                EventLogModel.start_time.desc())
        ]

        sorting = self.logTableWidget.isSortingEnabled()
        self.logTableWidget.setSortingEnabled(
            False)  # disable sorting while modifying the table.
        self.logTableWidget.setRowCount(
            len(event_logs
                ))  # go ahead and set table length and then update the rows
        for row, log_line in enumerate(event_logs):
            formatted_time = log_line.start_time.strftime('%Y-%m-%d %H:%M')
            self.logTableWidget.setItem(row, LogTableColumn.Time,
                                        QTableWidgetItem(formatted_time))
            self.logTableWidget.setItem(row, LogTableColumn.Category,
                                        QTableWidgetItem(log_line.category))
            self.logTableWidget.setItem(row, LogTableColumn.Subcommand,
                                        QTableWidgetItem(log_line.subcommand))
            self.logTableWidget.setItem(row, LogTableColumn.Repository,
                                        QTableWidgetItem(log_line.repo_url))
            self.logTableWidget.setItem(
                row, LogTableColumn.ReturnCode,
                QTableWidgetItem(str(log_line.returncode)))
        self.logTableWidget.setSortingEnabled(
            sorting)  # restore sorting now that modifications are done
Exemple #5
0
def test_create(qapp, borg_json_output, mocker, qtbot):
    main = qapp.main_window
    stdout, stderr = borg_json_output('create')
    popen_result = mocker.MagicMock(stdout=stdout, stderr=stderr, returncode=0)
    mocker.patch.object(vorta.borg.borg_thread, 'Popen', return_value=popen_result)

    qtbot.mouseClick(main.createStartBtn, QtCore.Qt.LeftButton)
    qtbot.waitUntil(lambda: main.progressText.text().startswith('Backup finished.'), timeout=3000)
    qtbot.waitUntil(lambda: main.createStartBtn.isEnabled(), timeout=3000)
    assert EventLogModel.select().count() == 1
    assert ArchiveModel.select().count() == 3
    assert RepoModel.get(id=1).unique_size == 15520474
    assert main.createStartBtn.isEnabled()
    assert main.archiveTab.archiveTable.rowCount() == 3
    assert main.scheduleTab.logTableWidget.rowCount() == 1
Exemple #6
0
    def post_backup_tasks(self, profile_id):
        """
        Pruning and checking after successful backup.
        """
        profile = BackupProfileModel.get(id=profile_id)
        logger.info('Doing post-backup jobs for %s', profile.name)
        if profile.prune_on:
            msg = BorgPruneThread.prepare(profile)
            if msg['ok']:
                prune_thread = BorgPruneThread(msg['cmd'], msg)
                prune_thread.start()
                prune_thread.wait()

                # Refresh archives
                msg = BorgListRepoThread.prepare(profile)
                if msg['ok']:
                    list_thread = BorgListRepoThread(msg['cmd'], msg)
                    list_thread.start()
                    list_thread.wait()

        validation_cutoff = date.today() - timedelta(days=7 * profile.validation_weeks)
        recent_validations = EventLogModel.select().where(
            (
                EventLogModel.subcommand == 'check'
            ) & (
                EventLogModel.start_time > validation_cutoff
            ) & (
                EventLogModel.repo_url == profile.repo.url
            )
        ).count()
        if profile.validation_on and recent_validations == 0:
            msg = BorgCheckThread.prepare(profile)
            if msg['ok']:
                check_thread = BorgCheckThread(msg['cmd'], msg)
                check_thread.start()
                check_thread.wait()

        logger.info('Finished background task for profile %s', profile.name)
Exemple #7
0
    def run(self):
        self.started_event()
        mutex.lock()
        log_entry = EventLogModel(category='borg-run',
                                  subcommand=self.cmd[1],
                                  profile=self.params.get('profile_name', None)
                                  )
        log_entry.save()
        logger.info('Running command %s', ' '.join(self.cmd))

        p = Popen(self.cmd, stdout=PIPE, stderr=PIPE, bufsize=1, universal_newlines=True,
                  env=self.env, cwd=self.cwd, start_new_session=True)

        self.process = p

        # Prevent blocking of stdout/err. Via https://stackoverflow.com/a/7730201/3983708
        os.set_blocking(p.stdout.fileno(), False)
        os.set_blocking(p.stderr.fileno(), False)

        def read_async(fd):
            try:
                return fd.read()
            except (IOError, TypeError):
                return ''

        stdout = []
        while True:
            # Wait for new output
            select.select([p.stdout, p.stderr], [], [], 0.1)

            stdout.append(read_async(p.stdout))
            stderr = read_async(p.stderr)
            if stderr:
                for line in stderr.split('\n'):
                    try:
                        parsed = json.loads(line)
                        if parsed['type'] == 'log_message':
                            self.app.backup_log_event.emit(f'{parsed["levelname"]}: {parsed["message"]}')
                            level_int = getattr(logging, parsed["levelname"])
                            logger.log(level_int, parsed["message"])
                        elif parsed['type'] == 'file_status':
                            self.app.backup_log_event.emit(f'{parsed["path"]} ({parsed["status"]})')
                        elif parsed['type'] == 'archive_progress':
                            msg = (
                                f"{self.category_label['files']}: {parsed['nfiles']}, "
                                f"{self.category_label['original']}: {pretty_bytes(parsed['original_size'])}, "
                                f"{self.category_label['deduplicated']}: {pretty_bytes(parsed['deduplicated_size'])}, "
                                f"{self.category_label['compressed']}: {pretty_bytes(parsed['compressed_size'])}"
                            )
                            self.app.backup_progress_event.emit(msg)
                    except json.decoder.JSONDecodeError:
                        msg = line.strip()
                        if msg:  # Log only if there is something to log.
                            self.app.backup_log_event.emit(msg)
                            logger.warning(msg)

            if p.poll() is not None:
                time.sleep(0.1)
                stdout.append(read_async(p.stdout))
                break

        result = {
            'params': self.params,
            'returncode': self.process.returncode,
            'cmd': self.cmd,
        }
        stdout = ''.join(stdout)

        try:
            result['data'] = json.loads(stdout)
        except ValueError:
            result['data'] = stdout

        log_entry.returncode = p.returncode
        log_entry.repo_url = self.params.get('repo_url', None)
        log_entry.save()

        self.process_result(result)
        self.finished_event(result)
        mutex.unlock()