def manual(): ''' Run a manual update ''' if is_running(): message('Updater is running, please wait.') return if xbmcgui.Dialog().yesno(__addonname__, "Would you like to do an online update?"): new_version, update_url, update_md5 = new_update() xbmc.log('BOXiK Manual Service: New update check - %s' % new_version) if not new_version: dp = xbmcgui.Dialog() dp.ok(__addonname__, "Your BOXiK version %s is up to date." % get_local_version()) else: start(new_version, update_url, update_md5) else: if xbmcgui.Dialog().yesno(__addonname__, \ "Do you have the update.zip on USB thumb?", \ "Selecting 'Yes' will backup, reboot and start the update."): try: backup = Backup(which_usb(), get_local_version()) backup.run() reboot() except: dp.ok(__addonname__, \ "Backup could not be completed. Please use a 16GB USB or larger.", " ", \ "Update manually from Settings > Update")
def createBackup(backups): print(backups) backup = Backup() backup.getUserInput() backups['backups'].append(json.loads(backup.returnJson())) with open("presets.json", "w") as jsonWriter: jsonWriter.write(json.dumps(backups, sort_keys = True, indent = 4))
def createBackup(backups): print(backups) backup = Backup() backup.getUserInput() backups['backups'].append(json.loads(backup.returnJson())) with open("presets.json", "w") as jsonWriter: jsonWriter.write(json.dumps(backups, sort_keys=True, indent=4))
def test_invoke(self, mock_send_errors, mock_error, mock_configure, mock_traceback): for thrown in [None, BaseException, Exception, IOError]: trace = 'trace-%s' % uid() def invoke(): mock_send_errors.assert_not_called() if thrown: raise thrown def format_exc(): mock_send_errors.assert_not_called() return trace with self.subTest(thrown=thrown): for m in (mock_send_errors, mock_error, mock_configure, mock_traceback): m.reset_mock() subj = Backup() method = Mock() method.side_effect = invoke mock_configure.return_value = method mock_traceback.format_exc.side_effect = format_exc subj.invoke() if thrown: mock_error.assert_called_once_with(subj, "%s", trace) else: mock_error.assert_not_called() mock_send_errors.assert_called_once_with(subj)
async def main(args): datastore = [] # Build blobs with args.file as f: while True: blob = f.read(args.blob_size) if not blob: break # This loop is used to not break word in half while not str.isspace(blob[-1]): ch = f.read(1) if not ch: break blob += ch logger.debug('Blob: %s\n\n', blob) datastore.append(blob) ############################################# # Create Coordinator coordinator = Coordinator(datastore) failCounter = 0 try: # Its a coordinator! loop = asyncio.get_event_loop() server = await loop.create_server(lambda: EchoProtocol(coordinator), "127.0.0.1", args.port) logger.info("Coordinator created!") await server.serve_forever() except: # Its a backup! port = args.port + 1 # Backup port, used to communicate with workers while True: try: backup_coord = Backup("127.0.0.1", args.port, datastore, "127.0.0.1", port) failCounter = 0 break except: failCounter += 1 port += 1 if failCounter >= 10: break pass logger.info("Backup created!") backup_coord.start_backup() # When the coordinator dies, backup becomes the new coordinator by launching a server coordinator = Coordinator(datastore, backup_coord.indexDatastore, backup_coord.maps) loop = asyncio.get_event_loop() server = await loop.create_server(lambda: EchoProtocol(coordinator), "127.0.0.1", args.port) logger.info("Coordinator created!") await server.serve_forever()
def manual(): ''' Run a manual update ''' if is_running(): message('Updater is running, please wait.') return if xbmcgui.Dialog().yesno(__addonname__, "Would you like to do an online update?"): new_version, update_url, update_md5 = new_update() xbmc.log('BOXiK Manual Service: New update check - %s' % new_version) if not new_version: dp = xbmcgui.Dialog() dp.ok(__addonname__, "Your BOXiK version %s is up to date." % get_local_version()) else: start(new_version, update_url, update_md5) else: if xbmcgui.Dialog().yesno(__addonname__, \ "Do you have the update.zip on USB thumb?", \ "Selecting 'Yes' will backup, reboot and start the update."): try: backup = Backup( which_usb() , get_local_version() ) backup.run() reboot() except: dp.ok(__addonname__, \ "Backup could not be completed. Please use a 16GB USB or larger.", " ", \ "Update manually from Settings > Update")
def test_read_md5_with_times(self, mock_read_dir_md5_with_time, mock_load_md5_with_times, mock_os): subj = Backup() directory = 'dir-%s' % uid() log_md5_with_time = uid() mock_load_md5_with_times.return_value = log_md5_with_time walk = [('root-%s' % uid(), 'dirs-%s' % uid(), ['file-%s' % uid() for _ in uid_range()]) for _ in uid_range()] mock_os.walk.return_value = walk dir_md5_with_time = [{ 'key-%s' % uid(): 'value-%s' % uid() for _ in range(0, 3 + uid(5)) } for _ in range(0, len(walk))] mock_read_dir_md5_with_time.side_effect = dir_md5_with_time expected = {} for i in dir_md5_with_time: expected.update(i) expected_calls = [ call(directory, root, set(files), log_md5_with_time) for root, dirs, files in walk ] self.assertEqual(subj.read_md5_with_times(directory), expected) mock_load_md5_with_times.assert_called_once_with(directory + '/.log') mock_os.walk.assert_called_once_with(directory) mock_read_dir_md5_with_time.assert_has_calls(expected_calls)
def test_configure(self, mock_command, mock_read_config, mock_log, mock_sys): for log_level in (None, logging.DEBUG, logging.INFO, logging.ERROR): for log_format in (None, str(uid())): with self.subTest(log_level=log_level, log_format=log_format): subj = Backup() mock_log.reset_mock() mock_sys.reset_mock() config = {} command = uid() mock_command.return_value = command mock_read_config.return_value = config if log_format is not None: config['log_format'] = log_format if log_level is not None: config['log_level'] = logging.getLevelName(log_level) mock_log.getLevelName.side_effect = logging.getLevelName self.assertEqual(subj.configure(), command) mock_log.basicConfig.assert_called_once_with( level=mock_log.INFO if log_level is None else log_level, stream=mock_sys.stdout, format="%(message)s" if log_format is None else log_format) mock_sys.setrecursionlimit.assert_called_once_with(100)
def initializeStorage(trezor, pwMap, settings): """ Initialize new encrypted password file, ask for master passphrase. Initialize RSA keypair for backup, encrypt private RSA key using backup passphrase and Trezor's cipher-key-value system. Makes sure a session is created on Trezor so that the passphrase will be cached until disconnect. @param trezor: Trezor client @param pwMap: PasswordMap where to put encrypted backupKeys @param settings: Settings object to store password database location """ dialog = InitializeDialog() if not dialog.exec_(): sys.exit(4) masterPassphrase = q2s(dialog.pw1()) trezor.prefillPassphrase(masterPassphrase) backup = Backup(trezor) backup.generate() pwMap.backupKey = backup settings.dbFilename = q2s(dialog.pwFile()) settings.store()
def test_no_params(): sys.argv = ['backup'] bck = Backup() with pytest.raises(SystemExit) as pytest_wrapped_e: bck.main() assert pytest_wrapped_e.type == SystemExit assert pytest_wrapped_e.value.code == 2
def test_command(self, mock_arg, mock_svn_separator, mock_time_separator, mock_socket): subj = Backup() for command, method, has_src_dirs, has_dst_dirs in ( ('full', subj.full, True, True), ('dump', subj.dump, True, True), ('clone', subj.clone, False, True), ('git', subj.git, True, False), ('checks', subj.checks, False, True), ('any', subj.help, False, False), (str(uid()), subj.help, False, False)): for empty in (False, True): with self.subTest(command=command, empty=empty): mock_time_separator.reset_mock() mock_svn_separator.reset_mock() subj.commands = {} src_dirs = ['src%s-%s' % (i, uid()) for i in uid_range()] dest_dirs = ['dst%s-%s' % (i, uid()) for i in uid_range()] num = uid() command_line = [command] if has_src_dirs: command_line.append(','.join(src_dirs)) if has_dst_dirs: command_line.append(','.join(dest_dirs)) command_line.append(str(num)) mock_arg.side_effect = command_line hostname = 'hostname%s' % uid() smtp_host = 'smtp_host%s' % uid() subj.config = {} if empty else { 'hostname': hostname, 'smtp_host': smtp_host } mock_socket.gethostname.return_value = hostname if empty else None commands = {} if not has_dst_dirs: dest_dirs = [] else: for dst in dest_dirs: commands[dst] = [] result = subj.command() self.assertEqual(result, method) self.assertEqual(subj.hostname, hostname) self.assertEqual(subj.smtp_host, None if empty else smtp_host) self.assertTrue( isinstance(subj.time_separator, TimeSeparator)) mock_time_separator.assert_called_once_with( num if command == 'full' or command == 'clone' else None) mock_svn_separator.assert_called_once_with() self.assertEqual(subj.separators[0], subj.time_separator) self.assertTrue( isinstance(subj.separators[1], SvnSeparator)) self.assertEqual(len(subj.separators), 2) self.assertEqual(subj.src_dirs, src_dirs if has_src_dirs else ['']) self.assertEqual(subj.dest_dirs, dest_dirs if has_dst_dirs else []) self.assertEqual(subj.commands, commands)
def test_recovery_for_each_dest(self): subj = Backup() key = 'key-%s' # TODO возвращать результат. Перенести конструирование recovery_key_search внутрь recovery_for_each_dest subj.recovery_for_each_dest(key) self.fail("TODO")
def test_multi_dir_backup(tmpdir): tmp_dir = os.path.join(str(tmpdir), '2') # Needed for Python 3.5 and older os.mkdir(tmp_dir) print('dir {}'.format(tmp_dir)) source_dir = os.path.join(tmp_dir, 'src') target_dir = os.path.join(tmp_dir, 'target') config_file = os.path.join(tmp_dir, 'cfg.json') os.mkdir(source_dir) os.mkdir(target_dir) src_dir_1 = os.path.join(source_dir, 'someplace') trg_dir_1 = 'some_backup' os.mkdir(src_dir_1) with open(config_file, 'w') as fh: json.dump( { 'pairs': [{ "src": src_dir_1, "trg": trg_dir_1, }], 'target': target_dir, }, fh) # pretend the git repository os.mkdir(os.path.join(target_dir, '.git')) with open(os.path.join(target_dir, '.git', 'HEAD'), 'w') as fh: fh.write('head') sys.argv = ['backup', '--config', config_file] with open(os.path.join(src_dir_1, 'a.txt'), 'w') as fh: fh.write('hello') os.mkdir(os.path.join(src_dir_1, 'notes')) os.mkdir(os.path.join(src_dir_1, 'notes', 'personal')) with open(os.path.join(src_dir_1, 'notes', 'personal', 'diary.txt'), 'w') as fh: fh.write('First entry') with open(os.path.join(src_dir_1, 'notes', 'personal', 'todo.txt'), 'w') as fh: fh.write('TODO list') os.mkdir(os.path.join(src_dir_1, 'clients')) with open(os.path.join(src_dir_1, 'clients', 'contacts.csv'), 'w') as fh: fh.write('Name,email,phone') bck = Backup() bck.main() assert set(os.listdir(target_dir)) == set(['.git', 'some_backup']) assert set(os.listdir(os.path.join(target_dir, 'some_backup'))) == set( ['a.txt', 'notes', 'clients']) assert set(os.listdir(os.path.join(target_dir, 'some_backup', 'notes'))) == set(['personal']) assert set( os.listdir(os.path.join(target_dir, 'some_backup', 'notes', 'personal'))) == set(['diary.txt', 'todo.txt']) assert set(os.listdir(os.path.join(target_dir, 'some_backup', 'clients'))) == set(['contacts.csv'])
def test_lazy_write_md5_to_md5dirs(self, mock_lazy_write_md5): subj = Backup() md5dirs = ['dir-%s' % uid() for _ in uid_range()] key = 'key-%s' % uid() md5files = 'md5files-%s' % uid() expected_calls = [call(subj, d, key, md5files) for d in md5dirs] subj.lazy_write_md5_to_md5dirs(md5dirs, key, md5files) mock_lazy_write_md5.assert_has_calls(expected_calls)
def do_backup(self, host, logger): host_backup = Backup(host, logger=logger) backup_lock.acquire() try: host_backup.setup() finally: backup_lock.release() return host_backup.do_backup()
def test_sorted_md5_with_time_by_time(self): subj = Backup() key1 = 'key1-%s' % uid() key2 = 'key2-%s' % uid() key3 = 'key3-%s' % uid() keys = [key3, key1, key2] md5_with_time = {k: (uid(), uid_datetime()) for k in keys} keys.reverse() self.assertEqual(subj.sorted_md5_with_time_by_time(md5_with_time), keys)
def test_write_md5_with_time(): subj = Backup() fd = Mock(name='fd') key = 'key-%s' % uid() checksum = 'sum-%s' % uid() sum_time = uid_datetime() subj.write_md5_with_time(fd, key, checksum, sum_time) fd.write.assert_called_once_with( bytes(checksum + ' ' + sum_time.isoformat() + ' ' + key + '\n', 'UTF-8'))
def test_read_config(self, mock_path): subj = Backup() path = str(uid()) mock_path.expanduser.return_value = path config = {'a': uid(), 'b': uid()} data = json.dumps(config) with patch('builtins.open', new_callable=mock.mock_open, read_data=data) as mock_file: self.assertEqual(subj.read_config(), config) mock_path.expanduser.assert_called_once_with( '~/.config/backup/backup.cfg') mock_file.assert_called_once_with(path, encoding='UTF-8')
def test_finds_correct_location_to_store_backup_file(self): tmp_path = '/tmp' actual = Backup.find_path_to_backup_file( '/Users/rob/test/googledrive/sports', '/Users/rob/test/googledrive/sports/notes.md', tmp_path) self.assertEquals(('/tmp/sports', '/tmp/sports/notes.md'), actual) actual = Backup.find_path_to_backup_file( '/Users/rob/test/googledrive/technical', '/Users/rob/test/googledrive/technical/java/notes.md', tmp_path) self.assertEquals( ('/tmp/technical/java', '/tmp/technical/java/notes.md'), actual)
def test_slow_check_dir(self, mock_datetime, mock_md5sum, mock_with_open, mock_write_md5_with_time, mock_sorted_md5_with_time_by_time): subj = Backup() path = 'path-%s' % uid() keys = ['key-%s' % uid() for _ in uid_range()] mock_sorted_md5_with_time_by_time.return_value = keys md5_with_time = {k: (uid(), uid()) for k in keys} times = [uid_datetime() for _ in keys] mock_datetime.now.side_effect = times corrupted_index = uid(len(keys)) mock_md5sum.side_effect = [ uid() if i == corrupted_index else md5_with_time[keys[i]][0] for i in range(0, len(keys)) ] lines = ['line-%s' % uid() for _ in keys] mock_write_md5_with_time.side_effect = lines index = 0 fd = ['fd-%s' % uid() for _ in keys] def md5_sum(i): return 'corrupted' if i == corrupted_index else md5_with_time[ keys[i]][0] def with_open(file, mode, handler): nonlocal index mock_write_md5_with_time.assert_has_calls([ call(fd[i], keys[i], md5_sum(i), times[i]) for i in range(0, index) ]) self.assertEqual(handler(fd[index]), lines[index]) index += 1 mock_write_md5_with_time.assert_has_calls([ call(fd[i], keys[i], md5_sum(i), times[i]) for i in range(0, index) ]) mock_with_open.side_effect = with_open subj.slow_check_dir(path, md5_with_time) mock_sorted_md5_with_time_by_time.assert_called_once_with( md5_with_time) mock_datetime.now.assert_has_calls([call(timezone.utc) for _ in keys]) mock_md5sum.assert_has_calls([ call(path + '/' + k, multiplier=subj.with_sleep_multiplier) for k in keys ]) mock_with_open.assert_has_calls( [call(path + '/.log', 'ab', mock.ANY) for _ in keys])
def actionBackup(): """ Backups the latest build. """ Backup.init() for target in Settings.targets: for platform in Settings.targetPlatforms: for cpu in Settings.targetCPUs: if System.checkIfCPUIsSupportedForPlatform(cpu, platform): for configuration in Settings.targetConfigurations: if not Summary.checkIfActionFailed( ACTION_BUILD, target, platform, cpu, configuration): Backup.run(target, platform, cpu, configuration)
def test_svn_revision(self, mock_system): subj = Backup() subj = SvnBackup(subj) for src in (str(uid()), "/%s" % uid(), "../%s" % uid()): with self.subTest(src=src): expected = uid() mock_system.reset_mock() mock_system.return_value = expected url = "file://" + os.path.abspath(src) self.assertEqual(subj.svn_revision(src), expected) mock_system.assert_called_once_with(("svn", "info", url), subj.read_revision) self.assertTrue(".." in "../%s" % uid()) self.assertFalse(".." in url)
def test_recovery_key_search(self, mock_recovery_entry, mock_recovery_dirs): subj = Backup() dst = 'dst-%s' % uid() key = 'key-%s' % uid() name = 'name-%s' % uid() md5dirs = ['md5dirs-%s' % uid() for _ in uid_range()] file_dict = {'file-%s' % uid(): uid() for _ in uid_range()} recovery = ['recovery-%s' % uid() for _ in uid_range()] indexes = [uid() for _ in uid_range()] lists = {i: ['list-%s' % uid() for _ in uid_range()] for i in indexes} subj.recovery_key_search(dst, key, name, md5dirs, file_dict, recovery, lists) self.fail("TODO")
def test_init(self, mock_time): now = uid_time() mock_time.time.return_value = now subj = Backup() self.assertEqual(time_to_iso(subj.checked), time_to_iso(now - 2 * 24 * 3600))
def load(self, fname): """ Load encrypted passwords from disk file, decrypt outer layer containing key names. Requires Trezor connected. @throws IOError: if reading file failed """ with file(fname) as f: header = f.read(len(Magic.headerStr)) if header != Magic.headerStr: raise IOError("Bad header in storage file") version = f.read(4) if len(version) != 4 or struct.unpack("!I", version)[0] != 1: raise IOError("Unknown version of storage file") wrappedKey = f.read(KEYSIZE) if len(wrappedKey) != KEYSIZE: raise IOError("Corrupted disk format - bad wrapped key length") self.outerKey = self.unwrapKey(wrappedKey) self.outerIv = f.read(BLOCKSIZE) if len(self.outerIv) != BLOCKSIZE: raise IOError("Corrupted disk format - bad IV length") lb = f.read(2) if len(lb) != 2: raise IOError("Corrupted disk format - bad backup key length") lb = struct.unpack("!H", lb)[0] self.backupKey = Backup(self.trezor) serializedBackup = f.read(lb) if len(serializedBackup) != lb: raise IOError("Corrupted disk format - not enough encrypted backup key bytes") self.backupKey.deserialize(serializedBackup) ls = f.read(4) if len(ls) != 4: raise IOError("Corrupted disk format - bad data length") l = struct.unpack("!I", ls)[0] encrypted = f.read(l) if len(encrypted) != l: raise IOError("Corrupted disk format - not enough data bytes") hmacDigest = f.read(MACSIZE) if len(hmacDigest) != MACSIZE: raise IOError("Corrupted disk format - HMAC not complete") #time-invariant HMAC comparison that also works with python 2.6 newHmacDigest = hmac.new(self.outerKey, encrypted, hashlib.sha256).digest() hmacCompare = 0 for (ch1, ch2) in zip(hmacDigest, newHmacDigest): hmacCompare |= int(ch1 != ch2) if hmacCompare != 0: raise IOError("Corrupted disk format - HMAC does not match or bad passphrase") serialized = self.decryptOuter(encrypted, self.outerIv) self.groups = cPickle.loads(serialized)
def test_write_md5_with_time_dict(self, mock_write_md5_with_time, mock_sorted_md5_with_time_by_time): subj = Backup() fd = 'fd-%s' % uid() keys = ['key-%s' % uid() for _ in uid_range()] mock_sorted_md5_with_time_by_time.return_value = keys md5_with_time = { k: ('sum-%s' % uid(), 'time-%s' % uid()) for k in keys } subj.write_md5_with_time_dict(fd, md5_with_time) mock_sorted_md5_with_time_by_time.assert_called_once_with( md5_with_time) mock_write_md5_with_time.assert_has_calls([ call(fd, k, md5_with_time[k][0], md5_with_time[k][1]) for k in keys ])
def backup_processor(self): if not self._backup_processor: self._backup_processor = TaskQueueProcessor( "Backups", Backup().type_name, "backups", self, self._max_workers, sleep_time=self.sleep_time) return self._backup_processor
def test_read_dir_md5_with_time(self, mock_update_dir_md5_with_time): for is_same_dir in (False, True): with self.subTest(is_same_dir=is_same_dir): mock_update_dir_md5_with_time.reset_mock() subj = Backup() backup_dir = 'backup-%s' % uid() prefix = '' if is_same_dir else 'prefix-%s' % uid() directory = backup_dir if is_same_dir else backup_dir + '/' + prefix if not is_same_dir: prefix += '/' targets = [ 'file-%s.any%s' % (uid(), uid()) for _ in uid_range() ] # целевые файлы md5_files = ['file-%s.md5' % uid() for _ in uid_range()] # файлы .md5 files = targets + md5_files + [ 'file-%s.any%s' % (uid(), uid()) for _ in uid_range() ] # + другие файлы dir_md5_with_time = {prefix + f: uid() for f in targets} # целевой результат log_md5_with_time = dir_md5_with_time.copy() log_md5_with_time.update({ f: uid() for f in [ # файлы в других директориях игнорируются 'dir-%s/file-%s' % (uid(), uid()) for _ in uid_range() ] }) log_md5_with_time.update({ prefix + 'file-%s.any%s' % (uid(), uid()): uid() for _ in uid_range() }) # файлы, которых уже нет, игнорируются expected_calls = [ call(dir_md5_with_time, len(backup_dir) + 1, directory, i, files) for i in md5_files ] self.assertEqual( subj.read_dir_md5_with_time(backup_dir, directory, files, log_md5_with_time), dir_md5_with_time) mock_update_dir_md5_with_time.assert_has_calls(expected_calls)
def readConfig(cmdargs): parser = OptionParser() parser.add_option( '--dry-run', action='store_true', dest='dry_run', help='show commands, do not execute except collection-status') parser.add_option('--cleanup', action='store_true', dest='cleanup', help='cleanup only, implies --dry-run') parser.add_option( '--remove-older', action='store', type='int', dest='remove_older', default=None, help= 'run remove_old only, with the given value. implies --dry-run (set the value in the config to customize for each run and do other operations)' ) parser.add_option('--config', action='store', type='string', dest='configFile', default='config.cfg.example', help='use this config file') parser.add_option( '--full', action='store_true', dest='full', help= 'force a full backup. will retry for each backup target if necessary until full backups are done' ) (options, args) = parser.parse_args(cmdargs) globals = {} locals = {} try: execfile(options.configFile, globals, locals) except: print 'exception raised while reading config file %s' % options.configFile raise if (not locals.has_key('DupiConfig')): raise 'DupiConfig dictionary was not defined' DupiConfig = locals['DupiConfig'] # setup default backup class if needed if (not DupiConfig.has_key('backup')): from backup import Backup DupiConfig['backup'] = Backup(DupiConfig) DupiConfig['backup'].commandLineOverrides(options) return DupiConfig
class TestBackup(unittest.TestCase): """ call to make unittest of the backup.Backup() class.""" def __init__( self, methodName = 'runTest' ): unittest.TestCase.__init__( self, methodName ) self.backup = Backup("VIM", config["VIM"], search=False, keep=True ) self.backup.compression = None def test_read_options(self): """read_options() should return a dictionary.""" options = read_options(config["TXT"]) self.assertEqual(type({}), type(options), "options has to ba a dictionary") self.assertEqual(type([]), type(options["dirs"]), "options[\"dirs\"] has to ba a list") self.assertEqual(type(""), type(options["archive_path"]), "options[\"archive_path\"] has to be a string") self.assertEqual(type(""), type(options["compression"]), "options[\"compression\"] has to be a string") self.assertEqual(type([]), type(options["input_files"]), "options[\"input_files\"] has to be a list") def test_target(self): """ Backup.target should be a string.""" self.assertEqual(type([]), type(self.backup._target)) def test_time(self): """ Backup.time should be a float number.""" self.assertEqual(type(float(0)), type(self.backup.time), "backup.time should be a float number") # The following test fails if test_make_backup() is run: # self.assertAlmostEqual(time.time(), self.backup.time, places=0 ) def test_log_file(self): """ Backup.log_file should be a string .""" backup = Backup( "VIM", config["VIM"], search=False, keep=True ) dir_name = os.path.dirname(backup.log_file) self.assertTrue(os.path.isdir(dir_name)) def test_make_backup(self): """ test for Backup.make_backup() """ self.backup.compression=None self.backup.target("/tmp") self.backup.find_files() self.backup.make_backup() self.assertTrue(os.path.isfile(self.backup.path)) self.assertNotEqual([],self.backup.file_list, "VIM backup should be non empty") self.backup.put() path = os.path.join(self.backup._target[2],os.path.basename(self.backup.path)) self.assertTrue(os.path.isfile(path), "Backup.make_backup() should make the backup file: %s" % path) with tarfile.open(path, 'r') as tarfile_o: has = len(tarfile_o.getnames()) should = len(tarfile_o.getnames()) self.assertEqual(has, should, "Backup should contain %d files, but contains %d files" % (has, should)) # The path is net removed unless both above test are passed. If a test # fails, an exception is raised which stopes the execution of the # module. os.remove(path)
def _test_full(self, mock_log, mock_time): now = uid_time() mock_time.time.return_value = now mock_log.debug = Mock() mock_log.info = Mock() mock_log.error = Mock() subj = Backup() self.assertEqual(time_to_iso(subj.checked), time_to_iso(now - 2 * 24 * 3600)) self.fail("TODO")
def test_backup_repr(monkeypatch): # Pretend path exists and is directory to allow initialisation monkeypatch.setattr(Path, 'is_dir', lambda _: True) monkeypatch.setattr(Path, 'exists', lambda _: True) # Get absolute path from current working directory to allow tests on # different platforms cwd = Path.cwd() given_path = cwd / 'example/string' expected_reprs = (f"Backup(path='{cwd}/example/string')", f"Backup(path='{cwd}\\example\\string')") # Act, assert actual_repr = str(Backup(given_path)) assert actual_repr in expected_reprs
def start(version, update_url, update_md5): ''' init update process ''' set_lock() if version and xbmcgui.Dialog().yesno(__addonname__, \ "New update ("+ version +") is available.", \ "Selecting 'Yes' will backup your data, download the update,", \ "reboot your device and start the update process."): download_location = which_usb() try: backup = Backup(download_location, get_local_version()) backup.run() except: dp = xbmcgui.Dialog() dp.ok(__addonname__, \ "Backup could not be completed.", "Please use a 16GB USB or larger.", \ "Update manually from Settings > Update") remove_lock() return if download_location: xbmc.log("BOXiK Auto Service: %s %s %s %s " % \ (remote_path(), download_location, update_url, update_md5)) if download.firmware(download_location, update_url, update_md5): reboot() else: dp = xbmcgui.Dialog() dp.ok(__addonname__, "Download failed", "", "Try again later.") else: dp = xbmcgui.Dialog() dp.ok(__addonname__, \ "Please insert a compatible USB into the BOXiK", " ", \ "Update manually from Settings > Update") remove_lock()
def test_recovery_for_each(self, mock_os): for isdir in (False, True): with self.subTest(isdir=isdir): mock_os.reset_mock() subj = Backup() dst = 'dst-%s' % uid() key = 'key-%s' recovery_key_search = Mock() mock_os.path.isdir.return_value = isdir mock_os.listdir.return_value = listdir = [ 'name-%s' % uid() for _ in uid_range() ] subj.recovery_for_each(dst, key, recovery_key_search) mock_os.path.isdir.assert_called_once_with(dst + key) if isdir: mock_os.listdir.assert_called_once_with(dst + key) recovery_key_search.assert_has_calls( [call(dst, name) for name in listdir]) else: mock_os.listdir.assert_not_called() recovery_key_search.assert_not_called()
def OnInit(self): if mf.err_msg: msg = mf.err_msg + u'\n\nもしエラーを自己解決できない場合は、お手数ですがこのエラーメッセージの内容を作者へお知らせください。\nCtrl + C キーを押すとテキストがコピーされます。\n個人情報の部分は、適当な文字に書き換えといてください。' wx.MessageBox(msg) sys.exit(1) self.conf = Config(mf.app_data_dir) if os.path.exists(mf.persist_path): self._processing_persist = True else: self._processing_persist = False self.wp = WavePlayer() self.snd = None self.bk = Backup() self.InitUI() self.binder.parent = self.frame self.binder.bindall(self) self.SetPersist() self.frame.Centre() self.frame.Show() self.RegisterControls() # spl_logの位置を記憶しておくため # register_controlsを呼び出したあとにログ画面を閉じる。 if not self.conf.show_log: self.OnLog(None) self.UpdateFlist() if self._processing_persist: # exe化したときにも正しく動くように遅らせて実行 wx.CallLater(1000, self.OffProcessingPersist) return True
def OnInit(self): self.conf = Config(mf.app_data_dir) if os.path.exists(mf.persist_path): self._processing_persist = True else: self._processing_persist = False self.wp = WavePlayer() self.snd = None self.bk = Backup() self.InitUI() self.binder.parent = self.frame self.binder.bindall(self) self.SetPersist() self.frame.Centre() self.frame.Show() self.RegisterControls() # spl_logの位置を記憶しておくため # register_controlsを呼び出したあとにログ画面を閉じる。 if not self.conf.show_log: self.OnLog(None) self.UpdateFlist() if self._processing_persist: # exe化したときにも正しく動くように遅らせて実行 wx.CallLater(1000, self.OffProcessingPersist) return True
def __init__(self, backup_path, backup_id, subprocess_factory=subprocess, db_connection_factory=sqlite3): Backup.__init__(self, backup_path, backup_id) self.subprocess_factory = subprocess_factory self.db_connection_factory = db_connection_factory
from cmd import Cmd from auothisation import Authorisation from gdrive import GDrive from backup import Backup if __name__ == '__main__': cli = Cmd() authorisation = Authorisation(cli) gdrive = GDrive(authorisation) backup = Backup(cli, gdrive) backup.backup()
def schedule_backup(self, **kwargs): try: backup = Backup() backup.created_date = date_now() backup.strategy = get_validate_arg(kwargs, "strategy", expected_type=BackupStrategy) backup.source = get_validate_arg(kwargs, "source", BackupSource) backup.target = get_validate_arg(kwargs, "target", BackupTarget) backup.priority = get_validate_arg(kwargs, "priority", expected_type=(int, long, float, complex), required=False) backup.plan_occurrence = \ get_validate_arg(kwargs, "plan_occurrence", expected_type=datetime, required=False) backup.plan = get_validate_arg(kwargs, "plan", expected_type=BackupPlan, required=False) backup.secondary_targets = get_validate_arg(kwargs, "secondary_targets", expected_type=list, required=False) backup.change_state(State.SCHEDULED) # set tags tags = get_validate_arg(kwargs, "tags", expected_type=dict, required=False) backup.tags = tags bc = get_mbs().backup_collection try: # resolve tags self._resolve_task_tags(backup) except Exception, ex: self._task_failed_to_schedule(backup, bc, ex) self.set_custom_backup_props(backup) backup_doc = backup.to_document() get_mbs().backup_collection.save_document(backup_doc) # set the backup id from the saved doc backup.id = backup_doc["_id"] self.info("Saved backup \n%s" % backup) if backup.state == State.FAILED: trigger_task_finished_event(backup, State.FAILED) return backup
def backup_rm(id, namespace): Backup.remove(id, namespace)
def backup_inspect(id, namespace): backup = Backup.fetch(id, namespace) result = backup.inspect() util.print_json_result(result)
def backup_list(namespace): backup_list = Backup.list(namespace) util.print_backup_ps_output(backup_list)
def do_backup(self, host, logger): host_backup = Backup(host, logger=logger) host_backup.setup() return host_backup.do_backup()
class InsPause(wx.App): binder = wx_utils.bind_manager() def OnInit(self): self.conf = Config(mf.app_data_dir) if os.path.exists(mf.persist_path): self._processing_persist = True else: self._processing_persist = False self.wp = WavePlayer() self.snd = None self.bk = Backup() self.InitUI() self.binder.parent = self.frame self.binder.bindall(self) self.SetPersist() self.frame.Centre() self.frame.Show() self.RegisterControls() # spl_logの位置を記憶しておくため # register_controlsを呼び出したあとにログ画面を閉じる。 if not self.conf.show_log: self.OnLog(None) self.UpdateFlist() if self._processing_persist: # exe化したときにも正しく動くように遅らせて実行 wx.CallLater(1000, self.OffProcessingPersist) return True def OffProcessingPersist(self): self._processing_persist = False #-------------------------------------------------------------------------- # persist (ウィンドウのサイズや最大化の保持)関連 def SetPersist(self): self.pm = PM.PersistenceManager.Get() self.pm.SetPersistenceFile(mf.persist_path) self.pm.RegisterAndRestore(self.frame) def RegisterControls(self): self.frame.Freeze() # ここに記憶させたいウィジェットを書く self.pm.RegisterAndRestore(self.spl_h) self.pm.RegisterAndRestore(self.spl_v) self.pm.RegisterAndRestore(self.spl_log) self.pm.RegisterAndRestore(self.flist) self.frame.Thaw() #-------------------------------------------------------------------------- # UI 初期化 def InitUI(self): self.res = xrc.XmlResource(mf.xrc_path) self.InitFrame() self.InitLog() self.InitView() self.InitSetting() self.InitFileList() self.InitMenu() self.InitToolbar() self.InitStatusbar() self.EnableUI() def InitFrame(self): self.frame = self.res.LoadFrame(None, 'MainFrame') self.frame.Title = APP_NAME self.wp.add_listener(self.frame) SetIcon(self.frame) self.spl_h = xrc.XRCCTRL(self.frame, 'HorizontalSplitter') self.spl_v = xrc.XRCCTRL(self.frame, 'VerticalSplitter') # ドラッグアンドドロップ dd = DirDrop(self) self.frame.SetDropTarget(dd) def InitLog(self): self.spl_log = xrc.XRCCTRL(self.frame, 'LogSplitter') self.win1 = self.spl_log.GetWindow1() self.win2 = self.spl_log.GetWindow2() logtext = xrc.XRCCTRL(self.frame, 'LogText') sys.stdout = sys.stderr = self.log = Mylog(logtext, mf.log_path) self.auto_show_log = xrc.XRCCTRL(self.frame, 'ChkAutoShowLog') self.auto_show_log.SetValue(self.conf.auto_show_log) def InitView(self): self.view = LabelsWindow(parent=self.frame) self.view.view_factor = self.conf.view_factor self.view.scale = self.conf.scale self.res.AttachUnknownControl('WaveView', self.view, self.frame) def InitSetting(self): # ---- ポーズ音声作成 self.SetDefaultSaveDir() # ---- ポーズ時間 self.NumCtrl('TextFactor', self.conf.factor, MIN_FACTOR, MAX_FACTOR) self.NumCtrl('TextAdd', self.conf.add_s, MIN_ADD_S, MAX_ADD_S) # ---- バックアップ(コンボボックス、復元ボタン) self.cmb_backup = xrc.XRCCTRL(self.frame, 'CmbBackup') for info in self.bk: self.AddBackupInfo(info) self.btn_restore = xrc.XRCCTRL(self.frame, 'BtnRestore') self.btn_delbackup = xrc.XRCCTRL(self.frame, 'BtnDelBackup') # ---- ラベル検索(無音認識時間、無音レベル、前余裕、後余裕) self.NumCtrl('TextSilDur', self.conf.sil_dur_s, MIN_SIL_DUR_S, MAX_SIL_DUR_S) xrc.XRCCTRL(self.frame, 'SilLvSlider').Value = self.conf.sil_lv self.NumCtrl('TextBeforeDur', self.conf.before_dur_s, 0.0, 9.99) self.NumCtrl('TextAfterDur', self.conf.after_dur_s, 0.0, 9.99) # ---- 色 self.SetViewBgColour(self.conf.bg) self.SetViewFgColour(self.conf.fg) self.view.SetHandleColour(self.conf.handle) # ---- 表示範囲 self.sld_scale = xrc.XRCCTRL(self.frame, 'SldScale') self.sld_scale.SetValue(self.conf.scale) # ---- ファイルリストの幅 self.sld_flist_width = xrc.XRCCTRL(self.frame, 'SldFlistWidth') self.sld_flist_width.SetValue(self.conf.flist_width) self.SetScroll('ScrMain') self.SetScroll('ScrTool') self.SetScroll('ScrView') def AddBackupInfo(self, info, tail=True): name = '(%02d) %s' % (info.num_labels, info.name) if tail: self.cmb_backup.Append(name, info) else: self.cmb_backup.Insert(name, 0, info) def InitFileList(self): self.flist = xrc.XRCCTRL(self.frame, 'FileList') self.flist.InsertColumn(0, u'音声') icon = wx.ArtProvider.GetIcon(wx.ART_LIST_VIEW, size=(16, 16)) bmp = wx.EmptyBitmap(16, 16) dc = wx.BufferedDC(None, bmp) dc.SetBackground(wx.WHITE_BRUSH) dc.Clear() del dc il = wx.ImageList(16, 16, False, 2) il.AddIcon(icon) il.Add(bmp) self.flist.AssignImageList(il, wx.IMAGE_LIST_SMALL) def InitMenu(self): self.menu = self.res.LoadMenuBar('MenuBar') self.frame.SetMenuBar(self.menu) self.menu.Check(xrc.XRCID('MenuLog'), self.conf.show_log) def InitToolbar(self): tools = [] self.tools = tools tools += [{'name': 'ToolHead', 'check': self.view.can_head}] tools += [{'name': 'ToolPlay', 'check': self.CanPlay}] tools += [{'name': 'ToolPlayPause', 'check': self.CanPlay}] tools += [{'name': 'ToolPause', 'check': self.CanPause}] tools += [{'name': 'ToolZoomIn', 'check': self.view.can_zoom_in}] tools += [{'name': 'ToolZoomOut', 'check': self.view.can_zoom_out}] tools += [{'name': 'ToolUndo', 'check': self.view.can_undo}] tools += [{'name': 'ToolRedo', 'check': self.view.can_redo}] tools += [{'name': 'ToolSaveLabels', 'check': self.view.can_save}] tools += [{'name': 'ToolSaveSound', 'check': lambda: True }] self.tool_bar = self.res.LoadToolBar(self.frame, 'ToolBar') if not self.conf.show_save2: self.tool_bar.RemoveTool(xrc.XRCID('ToolSaveSound')) self.tool_bar.Realize() def CanPlay(self): if self.snd and self.wp.can_play(): return True else: return False def CanPause(self): if self.snd and self.wp.can_pause(): return True else: return False def InitStatusbar(self): statusbar = self.frame.CreateStatusBar() statusbar.SetFieldsCount(2) label = ' 00:00.000 / 00:00.000 ' cw = wx.ClientDC(statusbar).GetTextExtent(label)[0] statusbar.SetStatusWidths([-1, cw]) self.statusbar = statusbar def SetFlistIcon(self): for i in range(self.flist.GetItemCount()): labels_path = mf.get_labels_path(self.snd_dir, i + 1) if os.path.exists(labels_path): image_no = IMG_HAS_LABELS else: image_no = IMG_NO_LABELS self.flist.SetItemImage(i, image_no, image_no) def SetDefaultSaveDir(self): path = mf.get_default_pause_dir(self.snd_dir) ctrl = xrc.XRCCTRL(self.frame, 'TextSaveDir') ctrl.SetValue(path) #-------------------------------------------------------------------------- # UI def SetTitleBar(self, snd_path): snd_file = os.path.basename(snd_path) self.frame.Title = snd_file + ' - ' + APP_NAME def NumCtrl(self, name, val, min_val, max_val): ''' 小数テキストコントロールの作成 #.## 形式で、最小値は引数min_val、最大値は引数max_valに制限される。 ''' ctrl = NumCtrl(self.frame) ctrl.SetAllowNegative(False) ctrl.SetIntegerWidth(1) ctrl.SetFractionWidth(2) ctrl.SetValue(val) ctrl.SetMin(min_val) ctrl.SetMax(max_val) ctrl.SetLimited(True) cw, ch = ctrl.GetTextExtent('X0.00X') ctrl.SetMinSize((cw, -1)) self.res.AttachUnknownControl(name, ctrl, self.frame) return ctrl def SetStatusPosText(self, pos_f): ''' ステータスバーに現在位置(秒)を設定 @param pos_f 現在位置(フレーム) ''' pos_s = self.view.f_to_s(pos_f) pos_str = sec_to_str(pos_s) s = ' %s / %s' % (pos_str, self.dur_str) self.statusbar.SetStatusText(s, STB_POS) def LogIsVisible(self): ''' ログ画面が表示されているか? ''' return self.spl_log.IsSplit() def SetScroll(self, name): ''' 設定ノートブックの中の領域をスクロールできるようにする ''' ctrl = xrc.XRCCTRL(self.frame, name) w, h = ctrl.Size su = 20 ctrl.SetScrollbars(su, su, w, su, h / su) def EnableUI(self): ''' ツールバーやコントロールの有効・無効を設定する ''' # ---- メニュー self.menu.Enable(xrc.XRCID('MenuSave'), self.view.can_save()) # ---- ツールバー for tool in self.tools: self.tool_bar.EnableTool(xrc.XRCID(tool['name']), tool['check']()) # ---- 設定コントロール notebook = xrc.XRCCTRL(self.frame, 'NoteBook') if self.wp.is_playing or self.snd is None: notebook.Enabled = False else: notebook.Enabled = True self.EnableBackup() def EnableBackup(self): ''' バックアップコンボボックス・バックアップボタン・復元ボタン・ 削除ボタンの有効/無効を設定 ''' if self.cmb_backup.Count == 0: self.cmb_backup.Enabled = False self.btn_restore.Enabled = False self.btn_delbackup.Enabled = False else: self.cmb_backup.Enabled = True if self.cmb_backup.Value != '': self.btn_restore.Enabled = True info = self.GetSelectedBkInfo() if info.is_sys: self.btn_delbackup.Enabled = False else: self.btn_delbackup.Enabled = True else: self.btn_restore.Enabled = False self.btn_delbackup.Enabled = False # ---- 色 def SetViewBgColour(self, colour): self.conf.bg = self.GetColour(colour) ctrl = xrc.XRCCTRL(self.frame, 'BtnBg') ctrl.SetBackgroundColour(colour) self.view.SetBackgroundColour(colour) def SetViewFgColour(self, colour): self.conf.fg = self.GetColour(colour) ctrl = xrc.XRCCTRL(self.frame, 'BtnFg') ctrl.SetBackgroundColour(colour) self.view.SetForegroundColour(colour) def GetColour(self, colour): if isinstance(colour, basestring): name = colour colour = wx.Colour() colour.SetFromName(name) return colour def ShowAboutDlg(self): msg = '''\ %s version %s Author: %s Web Site: %s\ ''' % (APP_NAME, __version__, __author__, web_site) dlg = wx.MessageDialog(self.frame, msg, 'About %s' % APP_NAME, wx.OK | wx.CENTRE) dlg.ShowModal() dlg.Destroy() #-------------------------------------------------------------------------- def ConfirmSave(self, cancel=False): ''' 保存するかをユーザに確認する @param cancel Trueならキャンセルボタンも表示する @return 保存の必要がないならNone それ以外はユーザの選択によりwx.YES, wx.NO, wx.CANCEL ''' if self.view.can_save(): flg = wx.YES_NO | wx.ICON_QUESTION if cancel: flg |= wx.CANCEL res = wx.MessageBox(u'変更を保存しますか?', u'確認', flg) if res == wx.YES: self.view.save() self.EnableUI() return wx.YES else: return res return None def UpdateFlist(self): ''' 音声ファイル一覧画面を更新する ''' self.ConfirmSave() self.flist.DeleteAllItems() snd_files = mf.get_snd_files(self.snd_dir) if not snd_files: self.conf.list_index = 0 self.snd = None self.view.SetVolume(None) self.view.SetLabels(Labels(), '') self.EnableUI() return snd_files.sort() for i, name in enumerate(snd_files): labels_path = mf.get_labels_path(self.snd_dir, i + 1) if os.path.exists(labels_path): image_no = IMG_HAS_LABELS else: image_no = IMG_NO_LABELS self.flist.InsertImageStringItem(i, name, image_no) list_index = self.conf.list_index list_index = max(0, min(list_index, self.flist.ItemCount - 1)) self.conf.list_index = list_index self.conf.list_index = -1 # OnSelectSndで波形画面を更新するため self.flist.Select(list_index) self.flist.EnsureVisible(list_index) self.flist.SetColumnWidth(0, self.conf.flist_width) def SetSound(self, snd_path, labels_path): ''' 波形画面に音声を設定する ''' if self.wp.is_playing: self.Pause() msg = u'音声ファイルを読み込み中' dlg = wx.ProgressDialog(u'読込', msg, parent=self.frame) try: self.snd = pausewave.open(snd_path, 'rb', dlg.Update) if self.snd.getnframes() == 0: raise Exception('num of frames == 0') except Exception as e: print str(e) msg = u'音声ファイルの読み込みに失敗しました。\n\n' \ u'音声ファイル=%s\nラベルファイル=%s' % (snd_path, labels_path) wx.MessageBox(msg) self.view.SetVolume(None) self.view.SetLabels(Labels(), labels_path) self.EnableUI() return finally: dlg.Destroy() vol = Volume(self.snd) self.view.SetVolume(vol) labels = self.LoadLabels(labels_path, self.snd) self.view.SetLabels(labels, labels_path) item = self.flist.GetFirstSelected() if item != -1: self.flist.SetItemImage(item, IMG_HAS_LABELS, IMG_HAS_LABELS) self.SetTitleBar(snd_path) nframes = self.snd.getnframes() rate = self.snd.getframerate() self.dur_str = sec_to_str(float(nframes) / rate) self.SetStatusPosText(self.view.pos_f) self.EnableUI() def LoadLabels(self, f, snd): ''' ラベル情報ファイルを読み込む。なければ作成する ''' if os.path.exists(f): labels = Labels(f) else: sil_lv = self.conf.sil_lv sil_dur = self.conf.sil_dur_s before_dur = self.conf.before_dur_s after_dur = self.conf.after_dur_s vol = Volume(snd, FIND_RATE) labels = find_sound(vol, sil_lv, sil_dur, before_dur, after_dur) labels.write(f) if not hasattr(labels, 'dist_s') or labels.dist_s == NO_DISTINCTION: labels.dist_s = find_dist_s(snd) labels.write(f) return labels def InsertPause(self): ''' ポーズファイルを作成する ''' if self.ConfirmSave() == wx.NO: return pause_dir = self.GetPauseDir() if not pause_dir: return if not self.ExistsLabels(): return snd_paths, labels_paths, pause_paths = self.GetFilesList(pause_dir) msg = u'ポーズ挿入中 %d / %d' max_n = len(snd_paths) style = wx.PD_AUTO_HIDE | wx.PD_APP_MODAL | wx.PD_CAN_ABORT | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME dlg = wx.ProgressDialog(u'進捗', msg % (1, max_n), maximum=max_n, parent=self.frame, style=style) err_num = 0 for i, (snd_path, labels_path, pause_path) in \ enumerate(zip(snd_paths, labels_paths, pause_paths)): keep_going, skip = dlg.Update(i, msg % (i + 1, max_n)) if not keep_going: break try: insp.insert_pause(snd_path, pause_path, labels_path, self.conf.factor, self.conf.add_s) except Exception: try: if not pause_path.endswith('.wav'): base = os.path.splitext(pause_path)[0] pause_path = base + '.wav' insp.insert_pause(snd_path, pause_path, labels_path, self.conf.factor, self.conf.add_s) print u'[Encoder Error] wav形式に自動的に変更しました:', pause_path continue except Exception as e: print str(type(e)), str(e) err_num += 1 dlg.Destroy() if err_num > 0: msg = u'ポーズ音声の作成中にエラーが発生しました' wx.MessageBox(msg, u'エラー', wx.OK | wx.ICON_ERROR) # ---- 結果を通知 if sys.platform == 'win32': try: import subprocess subprocess.Popen('explorer "%s"' % pause_dir.encode('cp932')) except: wx.MessageBox(u'出力場所 : %s' % pause_dir, u'完了', wx.OK) else: wx.MessageBox(u'出力場所 : %s' % pause_dir, u'完了', wx.OK) def GetPauseDir(self): ''' ポーズ音声出力先ディレクトリの取得 ''' pause_dir = xrc.XRCCTRL(self.frame, 'TextSaveDir').GetValue() if not os.path.exists(pause_dir): parent = os.path.dirname(pause_dir) if os.path.exists(parent): msg = u'フォルダを新しく作りますか?' flg = wx.YES_NO | wx.ICON_QUESTION res = wx.MessageBox(msg, u'確認', flg) if res == wx.YES: os.mkdir(pause_dir) else: return None else: msg = u'存在しないフォルダです:%s' % pause_dir wx.MessageBox(msg) return None if pause_dir == self.snd_dir: msg = u'入力音声ファイルと同じ場所には出力できません' wx.MessageBox(msg) return None return pause_dir def ExistsLabels(self): ''' ラベルファイルが存在するか? ''' is_all = xrc.XRCCTRL(self.frame, 'RadAllFiles').GetValue() if is_all: # すべてのファイル labels_paths = self.GetFullLabelsPath() if not labels_paths: return False return True def GetFilesList(self, pause_dir): ''' 入力音声ファイル・ラベルファイル・ポーズ付き音声ファイルの それぞれのリストを取得 ''' is_all = xrc.XRCCTRL(self.frame, 'RadAllFiles').GetValue() if is_all: # すべてのファイル rng = range(self.flist.ItemCount) else: # このファイルのみ list_index = self.conf.list_index rng = range(list_index, list_index+1) snd_paths = [] labels_paths = [] pause_paths = [] for i in rng: snd_path, labels_path, pause_path = self.GetFiles(i, pause_dir) snd_paths.append(snd_path) labels_paths.append(labels_path) pause_paths.append(pause_path) return snd_paths, labels_paths, pause_paths def GetFiles(self, i, pause_dir): snd_file = self.flist.GetItem(i, 0).GetText() snd_path = os.path.join(self.snd_dir, snd_file) labels_path = mf.get_labels_path(self.snd_dir, i + 1) pause_path = os.path.join(pause_dir, snd_file) return snd_path, labels_path, pause_path #-------------------------------------------------------------------------- # 再生/停止 def Play(self): self.snd.settable(None) self.snd.setpos(self.view.pos_f, True) self.pos_f_after_eow = self.snd.tell(True) self.view.playing = True self.wp.play(self.snd, True) self.EnableUI() self.tool_bar.EnableTool(xrc.XRCID('ToolHead'), True) def PausePlay(self): factor = self.conf.factor add = self.conf.add_s rate = self.snd.getframerate() nframes = self.snd.getnframes(True) labels = self.view.GetLabels() tbl = Conv_table(labels, rate, nframes, factor, add) self.snd.settable(tbl) self.snd.setpos(self.view.pos_f, True) self.pos_f_after_eow = self.snd.tell(True) self.view.playing = True self.wp.play(self.snd, True) self.EnableUI() self.tool_bar.EnableTool(xrc.XRCID('ToolHead'), True) def IfcutPlay(self, pos_s): tbl = self.CreateIfcutTable(pos_s) self.snd.settable(tbl) rate = self.snd.getframerate() pos_f = pos_s * rate start_f = max(0, int((pos_s - IFCUT_DUR_S) * rate)) self.snd.setpos(start_f, True) self.pos_f_after_eow = pos_f self.view.playing = True self.wp.play(self.snd, False) self.EnableUI() self.tool_bar.EnableTool(xrc.XRCID('ToolHead'), True) def CreateIfcutTable(self, pos_s): ''' 引数で指定された位置にポーズを入れるような変換テーブルを作成 ''' pause_lbl = Label(pos_s, pos_s, LBL_PAUSE) labels = Labels([str(pause_lbl)]) rate = self.snd.getframerate() end_f = int((pos_s + IFCUT_DUR_S) * rate) return Conv_table(labels, rate, end_f, 0, IFCUT_PAUSE_S) def Pause(self): self.wp.pause() self.view.playing = False self.EnableUI() #-------------------------------------------------------------------------- # ラベル情報のバックアップ # バックアップ def Backup(self, prefix=''): info = self.bk.backup(self.snd_dir, prefix) if info: self.AddBackupInfo(info, False) self.cmb_backup.SetSelection(0) self.EnableBackup() # 復元 def Restore(self): self.ConfirmSave() info = self.GetSelectedBkInfo() if not info: return # 自動バックアップ try: self.Backup('auto_') except: pass self.bk.restore(self.snd_dir, info) self.view.ReloadLabels() self.SetFlistIcon() # 削除 def DelBackup(self): info = self.GetSelectedBkInfo() if info and not info.is_sys: flg = wx.YES_NO | wx.ICON_QUESTION res = wx.MessageBox(u'本当に削除しますか?', u'確認', flg) if res != wx.YES: self.view.save() self.EnableUI() return if self.bk.delete(info): self.DelSelectedBkInfo() self.EnableBackup() # 自動ずれ調整 def AutoShift(self): if self.ConfirmSave() == wx.NO: return if not self.ExistsLabels(): return # 自動バックアップ try: self.Backup('auto_') except: pass snd_paths, labels_paths, pause_paths = self.GetFilesList('.') msg = u'自動ずれ調整中 %d / %d' max_n = len(snd_paths) style = wx.PD_AUTO_HIDE | wx.PD_APP_MODAL | wx.PD_CAN_ABORT | wx.PD_ELAPSED_TIME | wx.PD_REMAINING_TIME dlg = wx.ProgressDialog(u'進捗', msg % (1, max_n), maximum=max_n, parent=self.frame, style=style) err_num = 0 for i, (snd_path, labels_path, pause_path) in \ enumerate(zip(snd_paths, labels_paths, pause_paths)): keep_going, skip = dlg.Update(i, msg % (i + 1, max_n)) if not keep_going: break try: auto_shift(snd_path, labels_path) except Exception as e: print str(e) err_num += 1 dlg.Destroy() if err_num > 0: msg = u'エラーが発生しました' wx.MessageBox(msg, u'エラー', wx.OK | wx.ICON_ERROR) def GetFullLabelsPath(self): labels_paths = mf.get_labels_paths(self.snd_dir) num_labels = len(labels_paths) num_snds = self.flist.ItemCount if num_labels < num_snds: wx.MessageBox(u'まだポーズがついてない音声があります') return None # 音声ファイル数以上にあるラベルファイルは無視する return labels_paths[:num_snds] def GetSelectedBkInfo(self): selection = self.cmb_backup.Selection if selection == wx.NOT_FOUND: return None return self.cmb_backup.GetClientData(selection) def DelSelectedBkInfo(self): selection = self.cmb_backup.Selection if selection == wx.NOT_FOUND or self.cmb_backup.Value == '': return self.cmb_backup.Delete(selection) self.cmb_backup.SetSelection(0) #-------------------------------------------------------------------------- # イベント @binder(wx.EVT_SIZE, control='MainFrame') def OnSize(self, evt): if not self._processing_persist: w, h = evt.Size self.spl_h.SetSashPosition(h / 2) self.spl_v.SetSashPosition(w / 2) self.spl_log.SetSashPosition(w / 2) evt.Skip() # ---- ログ @binder(wx.EVT_BUTTON, control='BtnClearLog') def OnClearLog(self, evt): self.log.clear() @binder(wx.EVT_CHECKBOX, control='ChkAutoShowLog') def OnChangeAutoShowLog(self, evt): self.conf.auto_show_log = self.auto_show_log.IsChecked() @binder(EVT_LOG_CHANGE) def OnLogChange(self, evt): if not self.LogIsVisible() and self.conf.auto_show_log: self.OnLog(None) @binder(wx.EVT_MENU, id='MenuLog') @binder(wx.EVT_BUTTON, control='BtnCloseLog') def OnLog(self, evt): if self.LogIsVisible(): self.log_sash_pos = self.spl_log.GetSashPosition() self.spl_log.Unsplit() self.conf.show_log = False self.menu.Check(xrc.XRCID('MenuLog'), False) else: self.spl_log.SplitVertically(self.win1, self.win2, self.log_sash_pos) self.conf.show_log = True self.menu.Check(xrc.XRCID('MenuLog'), True) # ---- マニュアル @binder(wx.EVT_MENU, id='MenuManual') def OnManual(self, evt): webbrowser.open('http://vanya.jp.net/eng/inspause/manual.html') # ---- About @binder(wx.EVT_MENU, id='MenuAbout') def OnAbout(self, evt): self.ShowAboutDlg() # ---- # 音声ファイル一覧のクリック @binder(wx.EVT_LIST_ITEM_SELECTED, control='FileList') def OnSelectSnd(self, evt): # 同じファイルは読み直さない if self.conf.list_index == evt.m_itemIndex: return self.ConfirmSave() snd_path = os.path.join(self.snd_dir, evt.GetText()) labels_path = mf.get_labels_path(self.snd_dir, evt.m_itemIndex + 1) self.conf.list_index = evt.m_itemIndex self.SetSound(snd_path, labels_path) # 音声ディレクトリを開く @binder(wx.EVT_MENU, id='MenuOpen') def OnOpenDir(self, evt=None): snd_dir = self.snd_dir if not snd_dir: snd_dir = mf.get_music_dir() if not os.path.exists(snd_dir): snd_dir = os.getcwd() msg = u'音声フォルダの選択' style = wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST dlg = wx.DirDialog(self.frame, msg, snd_dir, style) if dlg.ShowModal() == wx.ID_OK: self.snd_dir = dlg.GetPath() self.CheckCanRead() dlg.Destroy() def CheckCanRead(self): ''' ffmpegがないのにwav以外を読み込もうとした場合に メッセージを表示する ''' snd_files = mf.get_snd_files(self.snd_dir) if not snd_files and not ffmpeg.has_ffmpeg and \ mf.exists(self.snd_dir, ffmpeg.EXTENSIONS): msg = u'wav形式以外に対応するにはffmpeg.exeを' \ u'inspause.exeと同じフォルダに置く必要があります' wx.MessageBox(msg, u'wav以外の読み書き') # ---- 設定項目 # FACTOR テキスト変更 @binder(wx.EVT_TEXT, control='TextFactor') def OnFactorChange(self, evt): self.conf.factor = float(evt.EventObject.Value) # ADD テキスト変更 @binder(wx.EVT_TEXT, control='TextAdd') def OnAddChange(self, evt): self.conf.add_s = float(evt.EventObject.Value) # -------- ポーズファイル作成 @binder(wx.EVT_BUTTON, control='BtnSaveDir') def OnSaveDir(self, evt): msg = u'ポーズ音声の保存先フォルダの選択' style = wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST | wx.DD_NEW_DIR_BUTTON default_dir = mf.get_default_pause_dir(self.snd_dir) dlg = wx.DirDialog(self.frame, msg, default_dir, style) if dlg.ShowModal() == wx.ID_OK: if dlg.GetPath() == self.snd_dir: msg = u'入力音声ファイルと同じ場所には出力できません' wx.MessageBox(msg) return ctrl = xrc.XRCCTRL(self.frame, 'TextSaveDir') ctrl.SetValue(dlg.GetPath()) dlg.Destroy() @binder(wx.EVT_BUTTON, control='BtnInsertPause') def OnInsertPauseAll(self, evt): self.conf.show_save2 = False self.InsertPause() # -------- ずれ調整 # ずれ調整スライダーのスクロール @binder(wx.EVT_SCROLL, control='ShiftSlider') def OnShiftSliderScroll(self, evt): lbl_shift = xrc.XRCCTRL(self.frame, 'LblShift') lbl_shift.Label = '%.3f%s' % (float(evt.Position) / 1000, u'秒') btn_shift = xrc.XRCCTRL(self.frame, 'BtnShift') if evt.Position == 0: btn_shift.Enabled = False else: btn_shift.Enabled = True @binder(wx.EVT_BUTTON, control='BtnShift') def OnShift(self, evt): shift_slider = xrc.XRCCTRL(self.frame, 'ShiftSlider') val_s = float(shift_slider.Value) / 1000 self.view.shift(val_s) shift_slider.Value = 0 evt = wx.ScrollEvent() evt.Position = shift_slider.Value self.OnShiftSliderScroll(evt) # -------- バックアップ # ポーズ情報コンボボックス @binder(wx.EVT_COMBOBOX, control='CmbBackup') def OnChangeBackupCmb(self, evt): self.EnableBackup() # バックアップの作成 @binder(wx.EVT_BUTTON, control='BtnBackup') def OnBackup(self, evt): self.Backup() # 復元 @binder(wx.EVT_BUTTON, control='BtnRestore') def OnRestore(self, evt): self.Restore() # 削除 @binder(wx.EVT_BUTTON, control='BtnDelBackup') def OnDelBackup(self, evt): self.DelBackup() # 自動ずれ調整 @binder(wx.EVT_BUTTON, control='BtnAutoShift') def OnAutoShift(self, evt): self.AutoShift() # -------- ポーズ再検索 # 無音認識時間 テキスト変更 @binder(wx.EVT_TEXT, control='TextSilDur') def OnSilDurChange(self, evt): self.conf.sil_dur_s = float(evt.EventObject.Value) # 無音レベルスライダーのスクロール @binder(wx.EVT_SCROLL, control='SilLvSlider') def OnSilLvSliderScroll(self, evt): pos = max(0, min(evt.Position, 100)) self.conf.sil_lv = pos # 前余裕 テキスト変更 @binder(wx.EVT_TEXT, control='TextBeforeDur') def OnBeforeDurChange(self, evt): self.conf.before_dur_s = float(evt.EventObject.Value) # 後余裕 テキスト変更 @binder(wx.EVT_TEXT, control='TextAfterDur') def OnAfterDurChange(self, evt): self.conf.after_dur_s = float(evt.EventObject.Value) @binder(wx.EVT_BUTTON, control='BtnResetSearch') def OnResetSearch(self, evt): xrc.XRCCTRL(self.frame, 'TextSilDur').Value = str(SIL_DUR_S) xrc.XRCCTRL(self.frame, 'SilLvSlider').Value = SIL_LV self.conf.sil_lv = SIL_LV xrc.XRCCTRL(self.frame, 'TextBeforeDur').Value = str(BEFORE_DUR_S) xrc.XRCCTRL(self.frame, 'TextAfterDur').Value = str(AFTER_DUR_S) @binder(wx.EVT_BUTTON, control='BtnSearch') def OnSearch(self, evt): sil_lv = self.conf.sil_lv sil_dur = self.conf.sil_dur_s before_dur = self.conf.before_dur_s after_dur = self.conf.after_dur_s self.view.find(sil_lv, sil_dur, before_dur, after_dur) # -------- 色 @binder(wx.EVT_BUTTON, control='BtnBg') def OnBgColour(self, evt): colour = wx.GetColourFromUser(self.frame, self.conf.bg) if colour.IsOk(): self.SetViewBgColour(colour) self.view.UpdateDrawing() @binder(wx.EVT_BUTTON, control='BtnFg') def OnFgColour(self, evt): colour = wx.GetColourFromUser(self.frame, self.conf.fg) if colour.IsOk(): self.SetViewFgColour(colour) self.view.UpdateDrawing() @binder(wx.EVT_BUTTON, control='BtnResetColour') def OnResetColour(self, evt): self.SetViewBgColour(BG_COLOUR) self.SetViewFgColour(FG_COLOUR) self.view.UpdateDrawing() # -------- 表示範囲 @binder(wx.EVT_SLIDER, control='SldScale') def OnScaleSlider(self, evt): self.conf.scale = self.sld_scale.GetValue() self.view.scale = self.conf.scale # -------- ファイルリストの幅 @binder(wx.EVT_SLIDER, control='SldFlistWidth') def OnFlistWidth(self, evt): self.conf.flist_width = self.sld_flist_width.GetValue() self.flist.SetColumnWidth(0, self.conf.flist_width) # ---- ツールバー # 先頭へ移動 @binder(wx.EVT_TOOL, id='ToolHead') def OnHead(self, evt): self.view.head() self.EnableUI() if self.view.playing: self.tool_bar.EnableTool(xrc.XRCID('ToolHead'), True) if self.wp.is_playing: self.snd.rewind() # 再生 @binder(wx.EVT_TOOL, id='ToolPlay') def OnPlay(self, evt): self.Play() # ポーズモード再生 @binder(wx.EVT_TOOL, id='ToolPlayPause') def OnPausePlay(self, evt): self.PausePlay() # 一時停止 @binder(wx.EVT_TOOL, id='ToolPause') def OnPause(self, evt): self.Pause() # 拡大 @binder(wx.EVT_TOOL, id='ToolZoomIn') def OnZoomIn(self, evt): self.view.zoom_in() self.EnableUI() # 縮小 @binder(wx.EVT_TOOL, id='ToolZoomOut') def OnZoomOut(self, evt): self.view.zoom_out() self.EnableUI() # もとに戻す @binder(wx.EVT_TOOL, id='ToolUndo') def OnUndo(self, evt): self.view.undo() self.EnableUI() # やり直し @binder(wx.EVT_TOOL, id='ToolRedo') def OnRedo(self, evt): self.view.redo() self.EnableUI() # ポーズ情報保存 @binder(wx.EVT_MENU, id='MenuSave') @binder(wx.EVT_TOOL, id='ToolSaveLabels') def OnSaveLabels(self, evt): self.view.save() self.EnableUI() # ポーズ音声出力 @binder(wx.EVT_TOOL, id='ToolSaveSound') def OnSaveSound(self, evt): res = wx.MessageBox(u'画面左下「メイン」タブの中の「ポーズ音声作成」ボタンを押してください。', u'メッセージ') # ---- メニュー # フレームを閉じる @binder(wx.EVT_MENU, id='MenuExit') @binder(wx.EVT_CLOSE) def OnClose(self, evt): if self.ConfirmSave(True) == wx.CANCEL: evt.Veto() return self._processing_persist = True self.conf.view_factor = self.view.view_factor self.conf.scale = self.view.scale self.conf.store() mf.store_map() if not self.spl_log.IsSplit(): self.frame.Hide() # スプリッタの位置を記憶させるために、一時的にスプリッタを表示 self.spl_log.SplitVertically(self.win1, self.win2, self.log_sash_pos) self.pm.SaveAndUnregister() self.wp.pause() self.frame.Destroy() # ---- 自作コントロールイベント @binder(EVT_REQ_PLAY) def OnReqPlay(self, evt): self.Play() self.view.UpdateDrawing(2) @binder(EVT_REQ_PAUSE_PLAY) def OnReqPausePlay(self, evt): self.PausePlay() self.view.UpdateDrawing(2) @binder(EVT_REQ_IFCUT_PLAY) def OnReqIfCutPlay(self, evt): self.IfcutPlay(evt.GetSec()) self.view.UpdateDrawing(2) @binder(EVT_REQ_PAUSE) def OnReqPause(self, evt): self.Pause() # WavePlayer からの現在位置変更イベント @binder(EVT_CUR_POS_CHANGE) def OnWPPosChange(self, evt): pos_f = self.snd.tell(True) self.view.pos_f = pos_f self.SetStatusPosText(pos_f) # LabelsWindow からの現在位置変更イベント @binder(EVT_VW_POS_CHANGE) def OnLWPosChange(self, evt): self.SetStatusPosText(evt.GetPos()) if self.wp.is_playing: self.snd.setpos(evt.GetPos(), True) self.EnableUI() @binder(EVT_OPEN_SND) def OnOpenSnd(self, evt): self.OnOpenDir(None) @binder(EVT_STATUS_MSG) def OnStatusMsg(self, evt): self.statusbar.SetStatusText(evt.GetMsg()) @binder(EVT_LABEL_CHANGE) def OnLabelChange(self, evt): self.EnableUI() @binder(EVT_SCALE_CHANGE) def OnScaleChange(self, evt): self.conf.scale = evt.GetPos() self.sld_scale.SetValue(self.conf.scale) @binder(EVT_EOW) def OnEOW(self, evt): self.view.playing = False self.view.pos_f = self.pos_f_after_eow self.EnableUI() #-------------------------------------------------------------------------- # プロパティ # ---- 音声ディレクトリ名 @property def snd_dir(self): return self.conf.snd_dir @snd_dir.setter def snd_dir(self, snd_dir): if snd_dir != self.snd_dir: self.conf.list_index = 0 self.conf.snd_dir = snd_dir self.snd = None self.view.SetVolume(None) self.frame.Title = APP_NAME self.UpdateFlist() self.SetDefaultSaveDir() self.EnableUI()
ret = op.mount_backup(options.drive, options.backup_dir) logger.info(ret) if 1: logger.info("* Creating Subvolume") ret = op.create_subvol(latest_dir) logger.info(ret) sys.exit(0) if options.subcommand == 'backup': from disks import Disks disks = Disks() di = disks.create_disk_info( options.drive ) from backup import Backup backup = Backup(di, options.source_dir) backup.do_backup() sys.exit(0) if options.subcommand == 'delete': from disks import Disks disks = Disks() di = disks.create_disk_info( options.drive ) from backup import Backup backup = Backup(di) backup.delete_backup(options.date) sys.exit(0)
def cron_backup(title): """ Make backup, send it to the user@server:directory and update the stamp file. The backup is made if STAMP[title] is greater than backup.get_stamp() """ global STAMPS backup = Backup(title, config[title], search=False, keep=True) stamp = backup.get_stamp() STAMP = STAMPS.get(title, 0) if options.verbose: print("state=%s" % backup.state) print("[%s] stamp=%f (%s)" % (title, stamp, time.strftime("%x %X %Z", time.localtime(stamp)))) STAMPS_dict = dict( map(lambda item: (item[0], time.strftime("%x %X %Z", time.localtime(item[1]))), STAMPS.iteritems())) print("[%s] STAMP=%f (%s)" % (title, STAMP, STAMPS_dict.get(title, 0))) print("STAMP > stamp: %s" % (STAMP > stamp)) if STAMP > stamp: if options.verbose: print("[%s] FIND_FILES" % title) backup.find_files() backup.time = STAMP if options.verbose: print("[%s] MAKE_BACKUP" % title) backup.make_backup() backup.log('fsize') if options.verbose: print("[%s] PUT" % title) backup.put() msg = "INFO: [%s] backuped to: \"%s\"" % (title, str(backup)) log(msg) if options.verbose: print(msg)
def main(args=None): """ Main: parse arguments and run. """ parser = ArgumentParser( description=DESCRIPTION, epilog=EPILOG, formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument( 'path', action='store', type=dir_argument, help='path to wordpress instance' ) parser.add_argument( '-v', '--verbose', action='store_const', dest='loglevel', const=logging.INFO, default=logging.WARN, help='enable log messages' ) parser.add_argument( '-d', '--debug', action='store_const', dest='loglevel', const=logging.DEBUG, default=logging.WARN, help='enable debug messages' ) parser.add_argument( '-q', '--quiet', action='store_true', help='do not print status messages' ) parser.add_argument( '--dry', action='store_true', help='perform dry run: do not store or delete any archives' ) parser.add_argument( '--database', action='store_true', help='backup wordpress database' ) parser.add_argument( '--filesystem', action='store_true', help='backup wordpress filesystem' ) parser.add_argument( '--thinning', action='store', metavar='STRATEGY', type=functools.partial(value_argument, callee=ThinningStrategy.fromArgument), help='thin out backups at targets (except local target) using the specified strategy' ) group_db = parser.add_argument_group( 'database backup options', '' ) group_db.add_argument( '--db', action='store', metavar='NAME', help='name for wordpress db' ) group_db.add_argument( '--dbhost', action='store', metavar='HOST', help='hostname for wordpress db' ) group_db.add_argument( '--dbport', action='store', metavar='PORT', type=int, help='portnumber for wordpress db' ) group_db.add_argument( '--dbuser', action='store', metavar='USER', help='username for wordpress db' ) group_db.add_argument( '--dbpass', action='store', metavar='PASS', help='password for wordpress db' ) group_db.add_argument( '--dbprefix', action='store', metavar='PREFIX', help='prefix for table names in wordpress db' ) group_local = parser.add_argument_group( 'local target', 'options for storing the backup archive on local filesystem' ) group_local.add_argument( '--attic', action='store', metavar='DIR', nargs='?', const='.', default=None, type=dir_argument, help='local directory to store backup archive' ) group_s3 = parser.add_argument_group( 's3 target', 'options for copying the backup archive to a s3 service' ) group_s3.add_argument( '--s3', action='store', metavar='HOST', help='host for s3 server' ) group_s3.add_argument( '--s3accesskey', action='store', metavar='KEY', help='access key for s3 server' ) group_s3.add_argument( '--s3secretkey', action='store', metavar='KEY', help='secret key for s3 server' ) group_s3.add_argument( '--s3bucket', action='store', metavar='BUCKET', help='bucket at s3 server' ) group_report = parser.add_argument_group( 'report options', '' ) group_report.add_argument( '--mail-from', action='store', metavar='MAIL', help='sender address for report mails' ) group_report.add_argument( '--mail-to-admin', action='store_true', help='send report to wordpress administrator' ) group_report.add_argument( '--mail-to', action='store', metavar='MAIL', help='recipient address for report mails' ) arguments = parser.parse_args() if args is None else parser.parse_args(args) # logging import coloredlogs coloredlogs.install( level=arguments.loglevel, format='%(asctime)s - %(filename)s:%(funcName)s - %(levelname)s - %(message)s', isatty=True ) # initialize source try: source = SourceFactory(arguments.path).create( dbname=arguments.db, dbhost=arguments.dbhost, dbport=arguments.dbport, dbuser=arguments.dbuser, dbpass=arguments.dbpass, dbprefix=arguments.dbprefix ) except SourceErrors as exception: logging.error("Site-Backup: {}".format(exception)) sys.exit(1) else: logging.info("Site-Backup: Source is %s" % (source)) # initialize targets targets = [] if arguments.s3: # transfer backup to s3 service s3target = S3( arguments.s3, arguments.s3accesskey, arguments.s3secretkey, arguments.s3bucket if arguments.s3bucket else source.slug ) targets.append(s3target) for target in targets: logging.info("Site-Backup: Target is %s" % (target)) # initialize options mailer = Mailer() if arguments.mail_from else None if mailer: if arguments.mail_to_admin: mailer.addRecipient(Recipient(source.email)) if arguments.mail_to: mailer.addRecipient(Recipient(arguments.mail_to)) mailer.setSender(Sender(arguments.mail_from)) # initialize and execute backup backup = Backup(source, mailer=mailer, quiet=arguments.quiet) backup.execute( targets=targets, database=arguments.database, filesystem=arguments.filesystem, thinning=arguments.thinning, attic=arguments.attic, dry=arguments.dry ) if backup.error: logging.error("Site-Backup: {}".format(backup.error)) sys.exit(1)
def __init__( self, methodName = 'runTest' ): unittest.TestCase.__init__( self, methodName ) self.backup = Backup("VIM", config["VIM"], search=False, keep=True ) self.backup.compression = None
def backup_create(name, service_name, mounted_dir, namespace): service = Service.fetch(service_name, namespace) backup = Backup(service=service, name=name, mounted_dir=mounted_dir) backup.create()
class PasswordMap(object): """Storage of groups of passwords in memory""" def __init__(self, trezor): assert trezor is not None self.groups = {} self.trezor = trezor self.outerKey = None # outer AES-CBC key self.outerIv = None # IV for data blob encrypted with outerKey self.backupKey = None def addGroup(self, groupName): """ Add group by name as utf-8 encoded string """ groupName = groupName if groupName in self.groups: raise KeyError("Password group already exists") self.groups[groupName] = PasswordGroup() def load(self, fname): """ Load encrypted passwords from disk file, decrypt outer layer containing key names. Requires Trezor connected. @throws IOError: if reading file failed """ with file(fname) as f: header = f.read(len(Magic.headerStr)) if header != Magic.headerStr: raise IOError("Bad header in storage file") version = f.read(4) if len(version) != 4 or struct.unpack("!I", version)[0] != 1: raise IOError("Unknown version of storage file") wrappedKey = f.read(KEYSIZE) if len(wrappedKey) != KEYSIZE: raise IOError("Corrupted disk format - bad wrapped key length") self.outerKey = self.unwrapKey(wrappedKey) self.outerIv = f.read(BLOCKSIZE) if len(self.outerIv) != BLOCKSIZE: raise IOError("Corrupted disk format - bad IV length") lb = f.read(2) if len(lb) != 2: raise IOError("Corrupted disk format - bad backup key length") lb = struct.unpack("!H", lb)[0] self.backupKey = Backup(self.trezor) serializedBackup = f.read(lb) if len(serializedBackup) != lb: raise IOError("Corrupted disk format - not enough encrypted backup key bytes") self.backupKey.deserialize(serializedBackup) ls = f.read(4) if len(ls) != 4: raise IOError("Corrupted disk format - bad data length") l = struct.unpack("!I", ls)[0] encrypted = f.read(l) if len(encrypted) != l: raise IOError("Corrupted disk format - not enough data bytes") hmacDigest = f.read(MACSIZE) if len(hmacDigest) != MACSIZE: raise IOError("Corrupted disk format - HMAC not complete") #time-invariant HMAC comparison that also works with python 2.6 newHmacDigest = hmac.new(self.outerKey, encrypted, hashlib.sha256).digest() hmacCompare = 0 for (ch1, ch2) in zip(hmacDigest, newHmacDigest): hmacCompare |= int(ch1 != ch2) if hmacCompare != 0: raise IOError("Corrupted disk format - HMAC does not match or bad passphrase") serialized = self.decryptOuter(encrypted, self.outerIv) self.groups = cPickle.loads(serialized) def save(self, fname): """ Write password database to disk, encrypt it. Requires Trezor connected. @throws IOError: if writing file failed """ assert len(self.outerKey) == KEYSIZE rnd = Random.new() self.outerIv = rnd.read(BLOCKSIZE) wrappedKey = self.wrapKey(self.outerKey) with file(fname, "wb") as f: version = 1 f.write(Magic.headerStr) f.write(struct.pack("!I", version)) f.write(wrappedKey) f.write(self.outerIv) serialized = cPickle.dumps(self.groups, cPickle.HIGHEST_PROTOCOL) encrypted = self.encryptOuter(serialized, self.outerIv) hmacDigest = hmac.new(self.outerKey, encrypted, hashlib.sha256).digest() serializedBackup = self.backupKey.serialize() lb = struct.pack("!H", len(serializedBackup)) f.write(lb) f.write(serializedBackup) l = struct.pack("!I", len(encrypted)) f.write(l) f.write(encrypted) f.write(hmacDigest) f.flush() f.close() def encryptOuter(self, plaintext, iv): """ Pad and encrypt with self.outerKey """ return self.encrypt(plaintext, iv, self.outerKey) def encrypt(self, plaintext, iv, key): """ Pad plaintext with PKCS#5 and encrypt it. """ cipher = AES.new(key, AES.MODE_CBC, iv) padded = Padding(BLOCKSIZE).pad(plaintext) return cipher.encrypt(padded) def decryptOuter(self, ciphertext, iv): """ Decrypt with self.outerKey and unpad """ return self.decrypt(ciphertext, iv, self.outerKey) def decrypt(self, ciphertext, iv, key): """ Decrypt ciphertext, unpad it and return """ cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(ciphertext) unpadded = Padding(BLOCKSIZE).unpad(plaintext) return unpadded def unwrapKey(self, wrappedOuterKey): """ Decrypt wrapped outer key using Trezor. """ ret = self.trezor.decrypt_keyvalue(Magic.unlockNode, Magic.unlockKey, wrappedOuterKey, ask_on_encrypt=False, ask_on_decrypt=True) return ret def wrapKey(self, keyToWrap): """ Encrypt/wrap a key. Its size must be multiple of 16. """ ret = self.trezor.encrypt_keyvalue(Magic.unlockNode, Magic.unlockKey, keyToWrap, ask_on_encrypt=False, ask_on_decrypt=True) return ret def encryptPassword(self, password, groupName): """ Encrypt a password. Does PKCS#5 padding before encryption. Store IV as first block. @param groupName key that will be shown to user on Trezor and used to encrypt the password. A string in utf-8 """ rnd = Random.new() rndBlock = rnd.read(BLOCKSIZE) padded = Padding(BLOCKSIZE).pad(password) ugroup = groupName.decode("utf-8") ret = rndBlock + self.trezor.encrypt_keyvalue(Magic.groupNode, ugroup, padded, ask_on_encrypt=False, ask_on_decrypt=True, iv=rndBlock) return ret def decryptPassword(self, encryptedPassword, groupName): """ Decrypt a password. First block is IV. After decryption strips PKCS#5 padding. @param groupName key that will be shown to user on Trezor and was used to encrypt the password. A string in utf-8. """ ugroup = groupName.decode("utf-8") iv, encryptedPassword = encryptedPassword[:BLOCKSIZE], encryptedPassword[BLOCKSIZE:] plain = self.trezor.decrypt_keyvalue(Magic.groupNode, ugroup, encryptedPassword, ask_on_encrypt=False, ask_on_decrypt=True, iv=iv) password = Padding(BLOCKSIZE).unpad(plain) return password