class OracleConfParser(object):

    CODEC = stream_codecs.IniCodec(comment_markers='#')

    def __init__(self, config):
        self.config = config

    def parse(self):
        return self.CODEC.deserialize(self.config).items()
Beispiel #2
0
class MySQLConfParser(object):

    CODEC = stream_codecs.IniCodec(default_value='1',
                                   comment_markers=('#', ';', '!'))

    def __init__(self, config):
        self.config = config

    def parse(self):
        config_dict = self.CODEC.deserialize(self.config)
        mysqld_section_dict = config_dict['mysqld']
        return mysqld_section_dict.items()
Beispiel #3
0
 def __init__(self):
     self._admin_pwd = None
     self._sys_pwd = None
     self._db_name = None
     self._db_unique_name = None
     self.codec = stream_codecs.IniCodec()
     if not os.path.isfile(self._CONF_FILE):
         operating_system.create_directory(os.path.dirname(self._CONF_FILE),
                                           as_root=True)
         section = {self._CONF_ORA_SEC: {}}
         operating_system.write_file(self._CONF_FILE,
                                     section,
                                     codec=self.codec,
                                     as_root=True)
     else:
         config = operating_system.read_file(self._CONF_FILE,
                                             codec=self.codec,
                                             as_root=True)
         try:
             if self._CONF_SYS_KEY in config[self._CONF_ORA_SEC]:
                 self._sys_pwd = config[self._CONF_ORA_SEC][
                     self._CONF_SYS_KEY]
             if self._CONF_ADMIN_KEY in config[self._CONF_ORA_SEC]:
                 self._admin_pwd = config[self._CONF_ORA_SEC][
                     self._CONF_ADMIN_KEY]
             if self._CONF_ROOT_ENABLED in config[self._CONF_ORA_SEC]:
                 self._root_enabled = config[self._CONF_ORA_SEC][
                     self._CONF_ROOT_ENABLED]
             if self._CONF_DB_NAME in config[self._CONF_ORA_SEC]:
                 self._db_name = config[self._CONF_ORA_SEC][
                     self._CONF_DB_NAME]
             if self._CONF_DB_UNIQUE_NAME in config[self._CONF_ORA_SEC]:
                 self._db_unique_name = config[self._CONF_ORA_SEC][
                     self._CONF_DB_UNIQUE_NAME]
         except KeyError:
             # the ORACLE section does not exist, stop parsing
             pass
Beispiel #4
0
class BaseDbApp(object):
    CFG_CODEC = stream_codecs.IniCodec()

    def __init__(self, status, docker_client):
        self.status = status
        self.docker_client = docker_client

    @classmethod
    def get_client_auth_file(cls, file="os_admin.cnf"):
        # Save the password inside the mount point directory so we could
        # restore everyting when rebuilding the instance.
        conf_dir = guestagent_utils.get_conf_dir()
        return guestagent_utils.build_file_path(conf_dir, file)

    @classmethod
    def get_auth_password(cls, file="os_admin.cnf"):
        auth_config = operating_system.read_file(
            cls.get_client_auth_file(file), codec=cls.CFG_CODEC, as_root=True)
        return auth_config['client']['password']

    @classmethod
    def save_password(cls, user, password):
        content = {
            'client': {
                'user': user,
                'password': password,
                'host': "localhost"
            }
        }

        conf_dir = guestagent_utils.get_conf_dir()
        operating_system.write_file(
            f'{conf_dir}/{user}.cnf', content, codec=cls.CFG_CODEC,
            as_root=True)

    def remove_overrides(self):
        self.configuration_manager.remove_user_override()

    def reset_configuration(self, configuration):
        LOG.info("Resetting configuration.")
        self.configuration_manager.reset_configuration(configuration)

    def stop_db(self, update_db=False):
        LOG.info("Stopping database.")

        try:
            docker_util.stop_container(self.docker_client)
        except Exception:
            LOG.exception("Failed to stop database")
            raise exception.TroveError("Failed to stop database")

        if not self.status.wait_for_status(
            service_status.ServiceStatuses.SHUTDOWN,
            CONF.state_change_wait_time, update_db
        ):
            raise exception.TroveError("Failed to stop database")

    def start_db(self, *args, **kwargs):
        pass

    def start_db_with_conf_changes(self, config_contents, ds_version):
        """Start the database with given configuration.

        This method is called after resize.
        """
        LOG.info(f"Starting database service with new configuration and "
                 f"datastore version {ds_version}.")

        if self.status.is_running:
            LOG.info("Stopping database before applying changes.")
            self.stop_db()

        self.reset_configuration(config_contents)
        self.start_db(update_db=True, ds_version=ds_version)

    def get_backup_image(self):
        return cfg.get_configuration_property('backup_docker_image')

    def get_backup_strategy(self):
        return cfg.get_configuration_property('backup_strategy')

    def create_backup(self, context, backup_info, volumes_mapping={},
                      need_dbuser=True, extra_params=''):
        storage_driver = CONF.storage_strategy
        backup_driver = self.get_backup_strategy()
        incremental = ''
        backup_type = 'full'
        if backup_info.get('parent'):
            incremental = (
                f'--incremental '
                f'--parent-location={backup_info["parent"]["location"]} '
                f'--parent-checksum={backup_info["parent"]["checksum"]}')
            backup_type = 'incremental'

        name = 'db_backup'
        backup_id = backup_info["id"]
        image = self.get_backup_image()
        os_cred = (f"--os-token={context.auth_token} "
                   f"--os-auth-url={CONF.service_credentials.auth_url} "
                   f"--os-tenant-id={context.project_id}")

        db_userinfo = ''
        if need_dbuser:
            admin_pass = self.get_auth_password()
            # Use localhost to avoid host access verification.
            db_userinfo = (f"--db-host=localhost --db-user=os_admin "
                           f"--db-password={admin_pass}")

        swift_metadata = (
            f'datastore:{backup_info["datastore"]},'
            f'datastore_version:{backup_info["datastore_version"]}'
        )
        swift_container = (backup_info.get('swift_container') or
                           CONF.backup_swift_container)
        swift_params = (f'--swift-extra-metadata={swift_metadata} '
                        f'--swift-container={swift_container}')

        command = (
            f'/usr/bin/python3 main.py --backup --backup-id={backup_id} '
            f'--storage-driver={storage_driver} --driver={backup_driver} '
            f'{os_cred} '
            f'{db_userinfo} '
            f'{swift_params} '
            f'{incremental} '
            f'{extra_params}'
        )

        # Update backup status in db
        conductor = conductor_api.API(context)
        mount_point = cfg.get_configuration_property('mount_point')
        stats = guestagent_utils.get_filesystem_volume_stats(mount_point)
        backup_state = {
            'backup_id': backup_id,
            'size': stats.get('used', 0.0),
            'state': BackupState.BUILDING,
            'backup_type': backup_type
        }
        conductor.update_backup(CONF.guest_id,
                                sent=timeutils.utcnow_ts(microsecond=True),
                                **backup_state)
        LOG.debug(f"Updated state for backup {backup_id} to {backup_state}")

        # Start to run backup inside a separate docker container
        try:
            LOG.info(f'Starting to create backup {backup_id}, '
                     f'command: {command}')
            output, ret = docker_util.run_container(
                self.docker_client, image, name,
                volumes=volumes_mapping, command=command)
            result = output[-1]
            if not ret:
                msg = f'Failed to run backup container, error: {result}'
                LOG.error(msg)
                raise Exception(msg)

            backup_result = BACKUP_LOG_RE.match(result)
            if backup_result:
                backup_state.update({
                    'checksum': backup_result.group('checksum'),
                    'location': backup_result.group('location'),
                    'success': True,
                    'state': BackupState.COMPLETED,
                })
            else:
                msg = f'Cannot parse backup output: {result}'
                LOG.error(msg)
                backup_state.update({
                    'success': False,
                    'state': BackupState.FAILED,
                })

                # The exception message is visible to the user
                user_msg = msg
                ex_regex = re.compile(r'.+Exception: (.+)')
                for line in output[-5:-1]:
                    m = ex_regex.search(line)
                    if m:
                        user_msg = m.group(1)
                        break
                raise Exception(user_msg)
        except Exception as err:
            LOG.error("Failed to create backup %s", backup_id)
            backup_state.update({
                'success': False,
                'state': BackupState.FAILED,
            })
            raise exception.TroveError(
                "Failed to create backup %s, error: %s" %
                (backup_id, str(err))
            )
        finally:
            LOG.info("Completed backup %s.", backup_id)
            conductor.update_backup(
                CONF.guest_id,
                sent=timeutils.utcnow_ts(microsecond=True),
                **backup_state)
            LOG.debug("Updated state for %s to %s.", backup_id, backup_state)