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()
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()
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
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)