Example #1
0
    def setUp(self):
        super(BaseFreezerTest, self).setUp()

        self.storage = Temp_Tree()
        self.source_trees = []
        self.backup_count = 0
        self.backup_name = 'backup_test'

        self.get_environ()
Example #2
0
    def setUp(self):
        super(TestFreezerCompressGzip, self).setUp()

        # create a source tree to backup with a few empty files
        # (files must be empty to avoid encoding errors with pure random data)
        self.source_tree = Temp_Tree()
        self.source_tree.add_random_data(size=0)

        self.storage_tree = Temp_Tree()
        self.dest_tree = Temp_Tree()

        self.environ = super(TestFreezerCompressGzip, self).get_environ()
Example #3
0
    def setUp(self):
        super(TestFreezerMetadataChecksum, self).setUp()

        # create a source tree to backup with a few empty files
        # (files must be empty to avoid encoding errors with pure random data)
        self.source_tree = Temp_Tree()
        self.source_tree.add_random_data(size=0)

        self.storage_tree = Temp_Tree()
        self.dest_tree = Temp_Tree()

        self.freezer_backup_name = 'freezer-test-backup-checksum-0'

        self.metadata_out = os.path.join(
            self.storage_tree.path,
            self.freezer_backup_name + '.json')

        self.environ = super(TestFreezerMetadataChecksum, self).get_environ()
Example #4
0
    def setUp(self):
        super(TestFreezerCompressGzip, self).setUp()

        # create a source tree to backup with a few empty files
        # (files must be empty to avoid encoding errors with pure random data)
        self.source_tree = Temp_Tree()
        self.source_tree.add_random_data(size=0)

        self.storage_tree = Temp_Tree()
        self.dest_tree = Temp_Tree()

        self.environ = super(TestFreezerCompressGzip, self).get_environ()
Example #5
0
    def setUp(self):
        super(TestFreezerMetadataChecksum, self).setUp()

        # create a source tree to backup with a few empty files
        # (files must be empty to avoid encoding errors with pure random data)
        self.source_tree = Temp_Tree()
        self.source_tree.add_random_data(size=0)

        self.storage_tree = Temp_Tree()
        self.dest_tree = Temp_Tree()

        self.freezer_backup_name = 'freezer-test-backup-checksum-0'

        self.metadata_out = os.path.join(self.storage_tree.path,
                                         self.freezer_backup_name + '.json')

        self.environ = super(TestFreezerMetadataChecksum, self).get_environ()
Example #6
0
class TestFreezerCompressGzip(base.BaseFreezerTest):
    def __init__(self, *args, **kwargs):
        super(TestFreezerCompressGzip, self).__init__(*args, **kwargs)

    # noinspection PyAttributeOutsideInit
    def setUp(self):
        super(TestFreezerCompressGzip, self).setUp()

        # create a source tree to backup with a few empty files
        # (files must be empty to avoid encoding errors with pure random data)
        self.source_tree = Temp_Tree()
        self.source_tree.add_random_data(size=0)

        self.storage_tree = Temp_Tree()
        self.dest_tree = Temp_Tree()

        self.environ = super(TestFreezerCompressGzip, self).get_environ()

    def tearDown(self):
        super(TestFreezerCompressGzip, self).tearDown()

        self.source_tree.cleanup()
        self.dest_tree.cleanup()
        self.storage_tree.cleanup()

    def _backup(self, name, method):
        # perform a normal backup, with gzip specified
        backup_args = [
            'freezer-agent', '--path-to-backup', self.source_tree.path,
            '--container', self.storage_tree.path, '--backup-name', name,
            '--storage', 'local', '--compress', method, '--metadata-out',
            os.path.join(self.storage_tree.path, 'metadata.json')
        ]

        self.run_subprocess(backup_args, 'Test gzip backup to local storage.')

    def _restore(self, name, method):
        restore_args = [
            'freezer-agent', '--action', 'restore', '--restore-abs-path',
            self.dest_tree.path, '--container', self.storage_tree.path,
            '--backup-name', name, '--storage', 'local', '--compress', method
        ]

        self.run_subprocess(restore_args, 'Test restore from local storage.')

    def _metadata(self):
        path = os.path.join(self.storage_tree.path, 'metadata.json')
        with open(path, 'r') as f:
            return json.load(f)

    def _file_get_mimetype(self, metadata):
        """Given some file metadata, find its mimetype using the file command

        :param metadata: the parsed json file metadata
        :return: the mimetype
        """
        """
        Data is stored like data/tar/localhost_False/1469786264/0_1469786264 so
        we need build the same directory structure.
        data: the directory that holds the backup data
        tar: the engine used to create backup
        localhost: the hostname of the machine where the backup was taken
        False: it should be backup name or False is backup is not provided
        1469786264: timestamp
        0_1469786264: level zero timestamp
        """
        data_file_path = 'data{0}{1}{0}{2}_{3}{0}{4}{0}{5}_{4}{0}data'.format(
            os.path.sep,
            "tar",  # currently we support only tar
            metadata['hostname'],
            metadata['backup_name'],
            metadata['time_stamp'],
            metadata['curr_backup_level'])
        data_file_path = os.path.join(self.storage_tree.path, data_file_path)
        self.assertEqual(True, os.path.exists(data_file_path))

        # run 'file' in brief mode to only output the values we want
        proc = subprocess.Popen(['file', '-b', '--mime-type', data_file_path],
                                stdout=subprocess.PIPE)
        out, err = proc.communicate()
        self.assertEqual(0, proc.returncode)

        return out.strip()

    @test.attr(type="gate")
    def test_freezer_backup_compress_gzip(self):
        backup_name = 'freezer-test-backup-gzip-0'

        self._backup(backup_name, 'gzip')
        self._restore(backup_name, 'gzip')

        # metadata should show the correct algorithm
        metadata = self._metadata()
        self.assertIn('compression', metadata)
        self.assertEqual('gzip', metadata['compression'])

        # file utility should detect the correct mimetype
        mimetype = self._file_get_mimetype(metadata)
        self.assertEqual('application/gzip', mimetype)

        # actual contents should be the same
        diff_args = [
            'diff', '-r', '-q', self.source_tree.path, self.dest_tree.path
        ]
        self.run_subprocess(
            diff_args, 'Verify restored copy is identical to '
            'original.')

    @test.attr(type="gate")
    def test_freezer_backup_compress_bzip2(self):
        backup_name = 'freezer-test-backup-bzip2-0'

        self._backup(backup_name, 'bzip2')
        self._restore(backup_name, 'bzip2')

        metadata = self._metadata()
        self.assertIn('compression', metadata)
        self.assertEqual('bzip2', metadata['compression'])

        mimetype = self._file_get_mimetype(metadata)
        self.assertEqual('application/x-bzip2', mimetype)

        diff_args = [
            'diff', '-r', '-q', self.source_tree.path, self.dest_tree.path
        ]
        self.run_subprocess(
            diff_args, 'Verify restored copy is identical to '
            'original.')

    @test.attr(type="gate")
    def test_freezer_backup_compress_xz(self):
        backup_name = 'freezer-test-backup-xz-0'

        self._backup(backup_name, 'xz')
        self._restore(backup_name, 'xz')

        metadata = self._metadata()
        self.assertIn('compression', metadata)
        self.assertEqual('xz', metadata['compression'])

        mimetype = self._file_get_mimetype(metadata)
        self.assertEqual('application/x-xz', mimetype)

        diff_args = [
            'diff', '-r', '-q', self.source_tree.path, self.dest_tree.path
        ]
        self.run_subprocess(
            diff_args, 'Verify restored copy is identical to '
            'original.')
Example #7
0
    def create_local_backup(self,
                            hostname=None,
                            compression=None,
                            consistency_check=None,
                            incremental=True,
                            always_level=None,
                            restart_always_level=None,
                            max_level=None):
        """Creates a new backup with the given parameters.

        The backup will immediately be created using a randomly-generated
        source tree on the local filesystem, and will be stored in a random
        temporary directory using the 'local' storage mode. All generated data
        files will be automatically removed during `tearDown()`, though
        implementations are responsible for cleaning up any additional copies
        or restores created via other methods.

        :param hostname: if set, set `--hostname` to the given value
        :param compression: if set, set `--compression` to the given value
        :param consistency_check: if True, set `--consistency_check`
        :param incremental: if False, set `--no-incremental`
        :param always_level: sets `--always-level` to the given value
        :param restart_always_level: sets `--restart-always-level`
        :param max_level: sets `--max-level` to the given value
        :return: the path to the stored backup metadata
        """
        metadata_path = os.path.join(
            self.storage.path, 'metadata-{}.json'.format(self.backup_count))
        self.backup_count += 1

        tree = Temp_Tree()
        tree.add_random_data()
        self.source_trees.append(tree)

        backup_args = [
            'freezer-agent',
            '--path-to-backup',
            tree.path,
            '--container',
            self.storage.path,
            '--backup-name',
            self.backup_name,
            '--storage',
            'local',
            '--metadata-out',
            metadata_path,
        ]

        if hostname:
            backup_args += ['--hostname', hostname]

        if compression:
            backup_args += ['--compression', compression]

        if consistency_check:
            backup_args += ['--consistency-check']

        if incremental:
            if always_level is not None:
                backup_args += ['--always-level', str(always_level)]

            if max_level is not None:
                backup_args += ['--max-level', str(max_level)]

            if restart_always_level:
                backup_args += [
                    '--restart-always-level',
                    str(restart_always_level)
                ]
        else:
            backup_args += ['--no-incremental', 'NO_INCREMENTAL']

        self.run_subprocess(backup_args, 'Test backup to local storage.')

        return metadata_path
Example #8
0
class BaseFreezerTest(test.BaseTestCase):
    credentials = ['primary']

    def __init__(self, *args, **kwargs):

        super(BaseFreezerTest, self).__init__(*args, **kwargs)

    # noinspection PyAttributeOutsideInit
    def setUp(self):
        super(BaseFreezerTest, self).setUp()

        self.storage = Temp_Tree()
        self.source_trees = []
        self.backup_count = 0
        self.backup_name = 'backup_test'

        self.get_environ()

    def tearDown(self):

        super(BaseFreezerTest, self).tearDown()

        for tree in self.source_trees:
            tree.cleanup()

        self.storage.cleanup()

    @classmethod
    def get_auth_url(cls):
        return cls.os_primary.auth_provider.auth_client.auth_url[:-len(
            '/auth/tokens')]

    @classmethod
    def setup_clients(cls):
        super(BaseFreezerTest, cls).setup_clients()
        cls.get_environ()

    @classmethod
    def get_environ(cls):
        os.environ['OS_PASSWORD'] = cls.os_primary.credentials.password
        os.environ['OS_USERNAME'] = cls.os_primary.credentials.username
        os.environ['OS_PROJECT_NAME'] = cls.os_primary.credentials.tenant_name
        os.environ['OS_TENANT_NAME'] = cls.os_primary.credentials.tenant_name
        os.environ['OS_PROJECT_DOMAIN_NAME'] = \
            cls.os_primary.credentials.project_domain_name
        os.environ['OS_USER_DOMAIN_NAME'] = \
            cls.os_primary.credentials.user_domain_name

        # Allow developers to set OS_AUTH_URL when developing so that
        # Keystone may be on a host other than localhost.
        if 'OS_AUTH_URL' not in os.environ:
            os.environ['OS_AUTH_URL'] = cls.get_auth_url()

        # Mac OS X uses gtar located in /usr/local/bin
        os.environ['PATH'] = '/usr/local/bin:' + os.environ['PATH']

        return os.environ

    def run_subprocess(self, sub_process_args, fail_message):

        proc = subprocess.Popen(sub_process_args,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                env=self.environ,
                                shell=False)

        out, err = proc.communicate()

        self.assertEqual(
            0, proc.returncode, fail_message + " Output: {0}. "
            "Error: {1}".format(out, err))

        self.assertEqual(
            '', err, fail_message + " Output: {0}. "
            "Error: {1}".format(out, err))

    def create_local_backup(self,
                            hostname=None,
                            compression=None,
                            consistency_check=None,
                            incremental=True,
                            always_level=None,
                            restart_always_level=None,
                            max_level=None):
        """Creates a new backup with the given parameters.

        The backup will immediately be created using a randomly-generated
        source tree on the local filesystem, and will be stored in a random
        temporary directory using the 'local' storage mode. All generated data
        files will be automatically removed during `tearDown()`, though
        implementations are responsible for cleaning up any additional copies
        or restores created via other methods.

        :param hostname: if set, set `--hostname` to the given value
        :param compression: if set, set `--compression` to the given value
        :param consistency_check: if True, set `--consistency_check`
        :param incremental: if False, set `--no-incremental`
        :param always_level: sets `--always-level` to the given value
        :param restart_always_level: sets `--restart-always-level`
        :param max_level: sets `--max-level` to the given value
        :return: the path to the stored backup metadata
        """
        metadata_path = os.path.join(
            self.storage.path, 'metadata-{}.json'.format(self.backup_count))
        self.backup_count += 1

        tree = Temp_Tree()
        tree.add_random_data()
        self.source_trees.append(tree)

        backup_args = [
            'freezer-agent',
            '--path-to-backup',
            tree.path,
            '--container',
            self.storage.path,
            '--backup-name',
            self.backup_name,
            '--storage',
            'local',
            '--metadata-out',
            metadata_path,
        ]

        if hostname:
            backup_args += ['--hostname', hostname]

        if compression:
            backup_args += ['--compression', compression]

        if consistency_check:
            backup_args += ['--consistency-check']

        if incremental:
            if always_level is not None:
                backup_args += ['--always-level', str(always_level)]

            if max_level is not None:
                backup_args += ['--max-level', str(max_level)]

            if restart_always_level:
                backup_args += [
                    '--restart-always-level',
                    str(restart_always_level)
                ]
        else:
            backup_args += ['--no-incremental', 'NO_INCREMENTAL']

        self.run_subprocess(backup_args, 'Test backup to local storage.')

        return metadata_path

    def create_mutated_backup(self, days_old=30, **kwargs):
        """Create a local backup with a mutated timestamp

        This creates a new backup using `create_local_backup()`, modifies it
        using `mutate_timestamp()`, and then returns the resulting (loaded)
        metadata dict.

        :param days_old: the age of the backup to create
        :param kwargs: arguments to pass to `create_local_backup()`
        :return: the loaded metadata
        """
        metadata_path = self.create_local_backup(**kwargs)

        metadata = load_metadata(metadata_path)
        mutate_timestamp(metadata, days_old)
        save_metadata(metadata, metadata_path)

        return metadata
Example #9
0
class TestFreezerMetadataChecksum(base.BaseFreezerTest):
    def __init__(self, *args, **kwargs):
        super(TestFreezerMetadataChecksum, self).__init__(*args, **kwargs)

    # noinspection PyAttributeOutsideInit
    def setUp(self):
        super(TestFreezerMetadataChecksum, self).setUp()

        # create a source tree to backup with a few empty files
        # (files must be empty to avoid encoding errors with pure random data)
        self.source_tree = Temp_Tree()
        self.source_tree.add_random_data(size=0)

        self.storage_tree = Temp_Tree()
        self.dest_tree = Temp_Tree()

        self.freezer_backup_name = 'freezer-test-backup-checksum-0'

        self.metadata_out = os.path.join(self.storage_tree.path,
                                         self.freezer_backup_name + '.json')

        self.environ = super(TestFreezerMetadataChecksum, self).get_environ()

    def tearDown(self):
        super(TestFreezerMetadataChecksum, self).tearDown()

        self.source_tree.cleanup()
        self.dest_tree.cleanup()
        self.storage_tree.cleanup()

    @test.attr(type="gate")
    def test_freezer_fs_backup_valid_checksum(self):
        # perform a normal backup, but enable consistency checks and save the
        # metadata to disk
        backup_args = [
            'freezer-agent', '--path-to-backup', self.source_tree.path,
            '--container', self.storage_tree.path, '--backup-name',
            self.freezer_backup_name, '--storage', 'local',
            '--consistency_check', '--metadata-out', self.metadata_out
        ]

        self.run_subprocess(backup_args, 'Test backup to local storage.')

        # load the stored metadata to retrieve the computed checksum
        with open(self.metadata_out) as f:
            metadata = json.load(f)
            self.assertIn('consistency_checksum', metadata,
                          'Checksum must exist in stored metadata.')

            checksum = metadata['consistency_checksum']
            restore_args = [
                'freezer-agent', '--action', 'restore', '--restore-abs-path',
                self.dest_tree.path, '--container', self.storage_tree.path,
                '--backup-name', self.freezer_backup_name, '--storage',
                'local', '--consistency_checksum', checksum
            ]

            self.run_subprocess(
                restore_args, 'Test restore from local storage with '
                'computed checksum.')

    @test.attr(type="gate")
    def test_freezer_fs_backup_bad_checksum(self):
        # as above, but we'll ignore the computed checksum
        backup_args = [
            'freezer-agent', '--path-to-backup', self.source_tree.path,
            '--container', self.storage_tree.path, '--backup-name',
            self.freezer_backup_name, '--storage', 'local',
            '--consistency_check', '--metadata-out', self.metadata_out
        ]

        self.run_subprocess(backup_args, 'Test backup to local storage.')

        # make a failing sha256 checksum (assuming no added path string)
        bad_checksum = '0' * 64

        # attempt to restore using the bad checksum
        restore_args = [
            'freezer-agent', '--action', 'restore', '--restore-abs-path',
            self.dest_tree.path, '--container', self.storage_tree.path,
            '--backup-name', self.freezer_backup_name, '--storage', 'local',
            '--consistency_checksum', bad_checksum
        ]

        process = subprocess.Popen(restore_args,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   env=self.environ,
                                   shell=False)
        out, err = process.communicate()

        # make sure the subprocess exist with an error due to checksum mismatch
        message = '{0} Output: {1} Error: {2}'.format(
            'Restore process should fail with checksum error.', out, err)
        self.assertEqual(1, process.returncode, message)
        self.assertEqual('', out, message)
        self.assertNotEqual('', err, message)
    def setUp(self):
        super(TestFreezerMetadataChecksum, self).setUp()

        self.environ = super(TestFreezerMetadataChecksum, self).get_environ()
        self.dest_tree = Temp_Tree()
        self.backup_name = 'backup_checksum_test'
class TestFreezerMetadataChecksum(base.BaseFreezerTest):

    def __init__(self, *args, **kwargs):
        super(TestFreezerMetadataChecksum, self).__init__(*args, **kwargs)

    # noinspection PyAttributeOutsideInit
    def setUp(self):
        super(TestFreezerMetadataChecksum, self).setUp()

        self.environ = super(TestFreezerMetadataChecksum, self).get_environ()
        self.dest_tree = Temp_Tree()
        self.backup_name = 'backup_checksum_test'

    def tearDown(self):
        super(TestFreezerMetadataChecksum, self).tearDown()

        self.dest_tree.cleanup()

    @test.attr(type="gate")
    def test_freezer_fs_backup_valid_checksum(self):
        # perform a normal backup, but enable consistency checks and save the
        # metadata to disk
        metadata_path = self.create_local_backup(consistency_check=True)

        metadata = base.load_metadata(metadata_path)

        # load the stored metadata to retrieve the computed checksum
        self.assertIn('consistency_checksum', metadata,
                      'Checksum must exist in stored metadata.')

        checksum = metadata['consistency_checksum']
        restore_args = ['freezer-agent',
                        '--action', 'restore',
                        '--restore-abs-path', self.dest_tree.path,
                        '--container', metadata['container'],
                        '--backup-name', self.backup_name,
                        '--storage', 'local',
                        '--consistency-checksum', checksum]

        self.run_subprocess(restore_args,
                            'Test restore from local storage with '
                            'computed checksum.')

    @test.attr(type="gate")
    def test_freezer_fs_backup_bad_checksum(self):
        # as above, but we'll ignore the computed checksum
        metadata_path = self.create_local_backup(consistency_check=True)
        metadata = base.load_metadata(metadata_path)

        # make a failing sha256 checksum (assuming no added path string)
        bad_checksum = '0' * 64

        # attempt to restore using the bad checksum
        restore_args = ['freezer-agent',
                        '--action', 'restore',
                        '--restore-abs-path', self.dest_tree.path,
                        '--container', metadata['container'],
                        '--backup-name', self.backup_name,
                        '--storage', 'local',
                        '--consistency-checksum', bad_checksum]

        process = subprocess.Popen(restore_args,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   env=self.environ, shell=False)
        out, err = process.communicate()

        # make sure the subprocess exist with an error due to checksum mismatch
        message = '{0} Output: {1} Error: {2}'.format(
            'Restore process should fail with checksum error.',
            out, err)
        self.assertEqual(1, process.returncode, message)
        self.assertEqual('', out, message)
        self.assertNotEqual('', err, message)
Example #12
0
class TestFreezerCompressGzip(base.BaseFreezerTest):
    def __init__(self, *args, **kwargs):
        super(TestFreezerCompressGzip, self).__init__(*args, **kwargs)

    # noinspection PyAttributeOutsideInit
    def setUp(self):
        super(TestFreezerCompressGzip, self).setUp()

        # create a source tree to backup with a few empty files
        # (files must be empty to avoid encoding errors with pure random data)
        self.source_tree = Temp_Tree()
        self.source_tree.add_random_data(size=0)

        self.storage_tree = Temp_Tree()
        self.dest_tree = Temp_Tree()

        self.environ = super(TestFreezerCompressGzip, self).get_environ()

    def tearDown(self):
        super(TestFreezerCompressGzip, self).tearDown()

        self.source_tree.cleanup()
        self.dest_tree.cleanup()
        self.storage_tree.cleanup()

    def _backup(self, name, method):
        # perform a normal backup, with gzip specified
        backup_args = ['freezer-agent',
                       '--path-to-backup',
                       self.source_tree.path,
                       '--container',
                       self.storage_tree.path,
                       '--backup-name',
                       name,
                       '--storage',
                       'local',
                       '--compress',
                       method,
                       '--metadata-out',
                       os.path.join(self.storage_tree.path, 'metadata.json')]

        self.run_subprocess(backup_args, 'Test gzip backup to local storage.')

    def _restore(self, name, method):
        restore_args = ['freezer-agent',
                        '--action',
                        'restore',
                        '--restore-abs-path',
                        self.dest_tree.path,
                        '--container',
                        self.storage_tree.path,
                        '--backup-name',
                        name,
                        '--storage',
                        'local',
                        '--compress',
                        method]

        self.run_subprocess(restore_args, 'Test restore from local storage.')

    def _metadata(self):
        path = os.path.join(self.storage_tree.path, 'metadata.json')
        with open(path, 'r') as f:
            return json.load(f)

    def _file_get_mimetype(self, metadata):
        """Given some file metadata, find its mimetype using the file command

        :param metadata: the parsed json file metadata
        :return: the mimetype
        """
        hostname = metadata['hostname']
        backup_name = metadata['backup_name']
        time_stamp = metadata['time_stamp']
        dir_name = '%s_%s' % (hostname, backup_name)
        file_name = '%s_%s_%s_0' % (hostname, backup_name, time_stamp)
        data_file_path = os.path.join(self.storage_tree.path,
                                      dir_name,
                                      str(time_stamp),
                                      file_name)
        self.assertEqual(True, os.path.exists(data_file_path))

        # run 'file' in brief mode to only output the values we want
        proc = subprocess.Popen(['file', '-b', '--mime-type', data_file_path],
                                stdout=subprocess.PIPE)
        out, err = proc.communicate()
        self.assertEqual(0, proc.returncode)

        return out.strip()

    @test.attr(type="gate")
    def test_freezer_backup_compress_gzip(self):
        backup_name = 'freezer-test-backup-gzip-0'

        self._backup(backup_name, 'gzip')
        self._restore(backup_name, 'gzip')

        # metadata should show the correct algorithm
        metadata = self._metadata()
        self.assertIn('compression', metadata)
        self.assertEqual('gzip', metadata['compression'])

        # file utility should detect the correct mimetype
        mimetype = self._file_get_mimetype(metadata)
        self.assertEqual('application/gzip', mimetype)

        # actual contents should be the same
        diff_args = ['diff', '-r', '-q',
                     self.source_tree.path,
                     self.dest_tree.path]
        self.run_subprocess(diff_args, 'Verify restored copy is identical to '
                                       'original.')

    @test.attr(type="gate")
    def test_freezer_backup_compress_bzip2(self):
        backup_name = 'freezer-test-backup-bzip2-0'

        self._backup(backup_name, 'bzip2')
        self._restore(backup_name, 'bzip2')

        metadata = self._metadata()
        self.assertIn('compression', metadata)
        self.assertEqual('bzip2', metadata['compression'])

        mimetype = self._file_get_mimetype(metadata)
        self.assertEqual('application/x-bzip2', mimetype)

        diff_args = ['diff', '-r', '-q',
                     self.source_tree.path,
                     self.dest_tree.path]
        self.run_subprocess(diff_args, 'Verify restored copy is identical to '
                                       'original.')

    @test.attr(type="gate")
    def test_freezer_backup_compress_xz(self):
        backup_name = 'freezer-test-backup-xz-0'

        self._backup(backup_name, 'xz')
        self._restore(backup_name, 'xz')

        metadata = self._metadata()
        self.assertIn('compression', metadata)
        self.assertEqual('xz', metadata['compression'])

        mimetype = self._file_get_mimetype(metadata)
        self.assertEqual('application/x-xz', mimetype)

        diff_args = ['diff', '-r', '-q',
                     self.source_tree.path,
                     self.dest_tree.path]
        self.run_subprocess(diff_args, 'Verify restored copy is identical to '
                                       'original.')
Example #13
0
class TestFreezerCompressGzip(base.BaseFreezerTest):
    def __init__(self, *args, **kwargs):
        super(TestFreezerCompressGzip, self).__init__(*args, **kwargs)

    # noinspection PyAttributeOutsideInit
    def setUp(self):
        super(TestFreezerCompressGzip, self).setUp()

        # create a source tree to backup with a few empty files
        # (files must be empty to avoid encoding errors with pure random data)
        self.source_tree = Temp_Tree()
        self.source_tree.add_random_data(size=0)

        self.storage_tree = Temp_Tree()
        self.dest_tree = Temp_Tree()

        self.environ = super(TestFreezerCompressGzip, self).get_environ()

    def tearDown(self):
        super(TestFreezerCompressGzip, self).tearDown()

        self.source_tree.cleanup()
        self.dest_tree.cleanup()
        self.storage_tree.cleanup()

    def _backup(self, name, method):
        # perform a normal backup, with gzip specified
        backup_args = [
            'freezer-agent', '--path-to-backup', self.source_tree.path,
            '--container', self.storage_tree.path, '--backup-name', name,
            '--storage', 'local', '--compress', method, '--metadata-out',
            os.path.join(self.storage_tree.path, 'metadata.json')
        ]

        self.run_subprocess(backup_args, 'Test gzip backup to local storage.')

    def _restore(self, name, method):
        restore_args = [
            'freezer-agent', '--action', 'restore', '--restore-abs-path',
            self.dest_tree.path, '--container', self.storage_tree.path,
            '--backup-name', name, '--storage', 'local', '--compress', method
        ]

        self.run_subprocess(restore_args, 'Test restore from local storage.')

    def _metadata(self):
        path = os.path.join(self.storage_tree.path, 'metadata.json')
        with open(path, 'r') as f:
            return json.load(f)

    def _file_get_mimetype(self, metadata):
        """Given some file metadata, find its mimetype using the file command

        :param metadata: the parsed json file metadata
        :return: the mimetype
        """
        hostname = metadata['hostname']
        backup_name = metadata['backup_name']
        time_stamp = metadata['time_stamp']
        dir_name = '%s_%s' % (hostname, backup_name)
        file_name = '%s_%s_%s_0' % (hostname, backup_name, time_stamp)
        data_file_path = os.path.join(self.storage_tree.path, dir_name,
                                      str(time_stamp), file_name)
        self.assertEqual(True, os.path.exists(data_file_path))

        # run 'file' in brief mode to only output the values we want
        proc = subprocess.Popen(['file', '-b', '--mime-type', data_file_path],
                                stdout=subprocess.PIPE)
        out, err = proc.communicate()
        self.assertEqual(0, proc.returncode)

        return out.strip()

    @test.attr(type="gate")
    def test_freezer_backup_compress_gzip(self):
        backup_name = 'freezer-test-backup-gzip-0'

        self._backup(backup_name, 'gzip')
        self._restore(backup_name, 'gzip')

        # metadata should show the correct algorithm
        metadata = self._metadata()
        self.assertIn('compression', metadata)
        self.assertEqual('gzip', metadata['compression'])

        # file utility should detect the correct mimetype
        mimetype = self._file_get_mimetype(metadata)
        self.assertEqual('application/gzip', mimetype)

        # actual contents should be the same
        diff_args = [
            'diff', '-r', '-q', self.source_tree.path, self.dest_tree.path
        ]
        self.run_subprocess(
            diff_args, 'Verify restored copy is identical to '
            'original.')

    @test.attr(type="gate")
    def test_freezer_backup_compress_bzip2(self):
        backup_name = 'freezer-test-backup-bzip2-0'

        self._backup(backup_name, 'bzip2')
        self._restore(backup_name, 'bzip2')

        metadata = self._metadata()
        self.assertIn('compression', metadata)
        self.assertEqual('bzip2', metadata['compression'])

        mimetype = self._file_get_mimetype(metadata)
        self.assertEqual('application/x-bzip2', mimetype)

        diff_args = [
            'diff', '-r', '-q', self.source_tree.path, self.dest_tree.path
        ]
        self.run_subprocess(
            diff_args, 'Verify restored copy is identical to '
            'original.')

    @test.attr(type="gate")
    def test_freezer_backup_compress_xz(self):
        backup_name = 'freezer-test-backup-xz-0'

        self._backup(backup_name, 'xz')
        self._restore(backup_name, 'xz')

        metadata = self._metadata()
        self.assertIn('compression', metadata)
        self.assertEqual('xz', metadata['compression'])

        mimetype = self._file_get_mimetype(metadata)
        self.assertEqual('application/x-xz', mimetype)

        diff_args = [
            'diff', '-r', '-q', self.source_tree.path, self.dest_tree.path
        ]
        self.run_subprocess(
            diff_args, 'Verify restored copy is identical to '
            'original.')
Example #14
0
class TestFreezerMetadataChecksum(base.BaseFreezerTest):

    def __init__(self, *args, **kwargs):
        super(TestFreezerMetadataChecksum, self).__init__(*args, **kwargs)

    # noinspection PyAttributeOutsideInit
    def setUp(self):
        super(TestFreezerMetadataChecksum, self).setUp()

        # create a source tree to backup with a few empty files
        # (files must be empty to avoid encoding errors with pure random data)
        self.source_tree = Temp_Tree()
        self.source_tree.add_random_data(size=0)

        self.storage_tree = Temp_Tree()
        self.dest_tree = Temp_Tree()

        self.freezer_backup_name = 'freezer-test-backup-checksum-0'

        self.metadata_out = os.path.join(
            self.storage_tree.path,
            self.freezer_backup_name + '.json')

        self.environ = super(TestFreezerMetadataChecksum, self).get_environ()

    def tearDown(self):
        super(TestFreezerMetadataChecksum, self).tearDown()

        self.source_tree.cleanup()
        self.dest_tree.cleanup()
        self.storage_tree.cleanup()

    @test.attr(type="gate")
    def test_freezer_fs_backup_valid_checksum(self):
        # perform a normal backup, but enable consistency checks and save the
        # metadata to disk
        backup_args = ['freezer-agent',
                       '--path-to-backup',
                       self.source_tree.path,
                       '--container',
                       self.storage_tree.path,
                       '--backup-name',
                       self.freezer_backup_name,
                       '--storage',
                       'local',
                       '--consistency_check',
                       '--metadata-out',
                       self.metadata_out]

        self.run_subprocess(backup_args, 'Test backup to local storage.')

        # load the stored metadata to retrieve the computed checksum
        with open(self.metadata_out) as f:
            metadata = json.load(f)
            self.assertIn('consistency_checksum', metadata,
                          'Checksum must exist in stored metadata.')

            checksum = metadata['consistency_checksum']
            restore_args = ['freezer-agent',
                            '--action',
                            'restore',
                            '--restore-abs-path',
                            self.dest_tree.path,
                            '--container',
                            self.storage_tree.path,
                            '--backup-name',
                            self.freezer_backup_name,
                            '--storage',
                            'local',
                            '--consistency_checksum',
                            checksum]

            self.run_subprocess(restore_args,
                                'Test restore from local storage with '
                                'computed checksum.')

    @test.attr(type="gate")
    def test_freezer_fs_backup_bad_checksum(self):
        # as above, but we'll ignore the computed checksum
        backup_args = ['freezer-agent',
                       '--path-to-backup',
                       self.source_tree.path,
                       '--container',
                       self.storage_tree.path,
                       '--backup-name',
                       self.freezer_backup_name,
                       '--storage',
                       'local',
                       '--consistency_check',
                       '--metadata-out',
                       self.metadata_out]

        self.run_subprocess(backup_args, 'Test backup to local storage.')

        # make a failing sha256 checksum (assuming no added path string)
        bad_checksum = '0' * 64

        # attempt to restore using the bad checksum
        restore_args = ['freezer-agent',
                        '--action',
                        'restore',
                        '--restore-abs-path',
                        self.dest_tree.path,
                        '--container',
                        self.storage_tree.path,
                        '--backup-name',
                        self.freezer_backup_name,
                        '--storage',
                        'local',
                        '--consistency_checksum',
                        bad_checksum]

        process = subprocess.Popen(restore_args,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   env=self.environ, shell=False)
        out, err = process.communicate()

        # make sure the subprocess exist with an error due to checksum mismatch
        message = '{0} Output: {1} Error: {2}'.format(
            'Restore process should fail with checksum error.',
            out, err)
        self.assertEqual(1, process.returncode, message)
        self.assertEqual('', out, message)
        self.assertNotEqual('', err, message)