def build_log_file_name(self, log_name, owner, datastore_dir=None): """Build a log file name based on the log_name and make sure the directories exist and are accessible by owner. """ if datastore_dir is None: base_dir = self.GUEST_LOG_BASE_DIR if not operating_system.exists(base_dir, is_directory=True): operating_system.ensure_directory(base_dir, user=owner, group=owner, force=True, as_root=True) datastore_dir = guestagent_utils.build_file_path( base_dir, self.GUEST_LOG_DATASTORE_DIRNAME) if not operating_system.exists(datastore_dir, is_directory=True): operating_system.ensure_directory(datastore_dir, user=owner, group=owner, force=True, as_root=True) log_file_name = guestagent_utils.build_file_path( datastore_dir, '%s-%s.log' % (self.manager, log_name)) return self.validate_log_file(log_file_name, owner)
def rebuild(self, context, ds_version, config_contents=None, config_overrides=None): """Restore datastore service after instance rebuild.""" LOG.info("Starting to restore database service") self.status.begin_install() mount_point = CONF.get(CONF.datastore_manager).mount_point data_dir = mount_point + '/data' operating_system.ensure_directory(data_dir, user=CONF.database_service_uid, group=CONF.database_service_uid, as_root=True) # This makes sure the include dir is created. self.app.set_data_dir(data_dir) try: # Prepare mysql configuration LOG.debug('Preparing database configuration') self.app.configuration_manager.reset_configuration(config_contents) self.app.update_overrides(config_overrides) # Start database service. command = self.get_start_db_params(data_dir) self.app.start_db(ds_version=ds_version, command=command) except Exception as e: LOG.error(f"Failed to restore database service after rebuild, " f"error: {str(e)}") self.prepare_error = True raise finally: self.status.end_install(error_occurred=self.prepare_error)
def build_module_dir(cls, module_type, module_id): sub_dir = os.path.join(module_type, module_id) module_dir = guestagent_utils.build_file_path(cls.MODULE_BASE_DIR, sub_dir) if not operating_system.exists(module_dir, is_directory=True): operating_system.ensure_directory(module_dir, force=True) return module_dir
def _initialize_import_directory(self): """Lazy-initialize the directory for imported revision files. """ if not os.path.exists(self._revision_dir): operating_system.ensure_directory( self._revision_dir, user=self._owner, group=self._group, force=True, as_root=self._requires_root)
def start_db(self, update_db=False, ds_version=None, command=None, extra_volumes=None): """Start and wait for database service.""" docker_image = CONF.get(CONF.datastore_manager).docker_image image = (f'{docker_image}:latest' if not ds_version else f'{docker_image}:{ds_version}') command = command if command else '' try: root_pass = self.get_auth_password(file="root.cnf") except exception.UnprocessableEntity: root_pass = utils.generate_random_password() # Get uid and gid user = "******" % (CONF.database_service_uid, CONF.database_service_uid) # Create folders for mysql on localhost for folder in ['/etc/mysql', '/var/run/mysqld']: operating_system.ensure_directory( folder, user=CONF.database_service_uid, group=CONF.database_service_uid, force=True, as_root=True) volumes = { "/etc/mysql": {"bind": "/etc/mysql", "mode": "rw"}, "/var/run/mysqld": {"bind": "/var/run/mysqld", "mode": "rw"}, "/var/lib/mysql": {"bind": "/var/lib/mysql", "mode": "rw"}, } if extra_volumes: volumes.update(extra_volumes) try: LOG.info("Starting docker container, image: %s", image) docker_util.start_container( self.docker_client, image, volumes=volumes, network_mode="host", user=user, environment={ "MYSQL_ROOT_PASSWORD": root_pass, "MYSQL_INITDB_SKIP_TZINFO": 1, }, command=command ) # Save root password LOG.debug("Saving root credentials to local host.") self.save_password('root', root_pass) except Exception: LOG.exception("Failed to start mysql") raise exception.TroveError(_("Failed to start mysql")) if not self.status.wait_for_status( service_status.ServiceStatuses.HEALTHY, CONF.state_change_wait_time, update_db ): raise exception.TroveError(_("Failed to start mysql"))
def __init__(self, base_config_path, owner, group, codec, requires_root=False, override_strategy=None): """ :param base_config_path Path to the configuration file. :type base_config_path string :param owner Owner of the configuration files. :type owner string :param group Group of the configuration files. :type group string :param codec Codec for reading/writing of the particular configuration format. :type codec StreamCodec :param requires_root Whether the manager requires superuser privileges. :type requires_root boolean :param override_strategy Strategy used to manage configuration overrides (e.g. ImportOverrideStrategy). Defaults to OneFileOverrideStrategy if None. This strategy should be compatible with very much any datastore. It is recommended each datastore defines its strategy explicitly to avoid upgrade compatibility issues in case the default implementation changes in the future. :type override_strategy ConfigurationOverrideStrategy """ base_config_dir = os.path.dirname(base_config_path) operating_system.ensure_directory( base_config_dir, user=owner, group=group, force=True, as_root=True ) self._base_config_path = base_config_path self._owner = owner self._group = group self._codec = codec self._requires_root = requires_root self._value_cache = None if not override_strategy: # Use OneFile strategy by default. Store the revisions in a # sub-directory at the location of the configuration file. revision_dir = guestagent_utils.build_file_path( os.path.dirname(base_config_path), self.DEFAULT_STRATEGY_OVERRIDES_SUB_DIR) self._override_strategy = OneFileOverrideStrategy(revision_dir) else: self._override_strategy = override_strategy self._override_strategy.configure( base_config_path, owner, group, codec, requires_root)
def get_conf_dir(): """Get the config directory for the database related settings. For now, the files inside the config dir are mainly for instance rebuild. """ mount_point = CONF.get(CONF.datastore_manager).mount_point conf_dir = os.path.join(mount_point, 'conf.d') if not operating_system.exists(conf_dir, is_directory=True, as_root=True): operating_system.ensure_directory(conf_dir, as_root=True) return conf_dir
def do_prepare(self, context, packages, databases, memory_mb, users, device_path, mount_point, backup_info, config_contents, root_password, overrides, cluster_config, snapshot, ds_version=None): """This is called from prepare in the base class.""" data_dir = mount_point + '/data' self.app.stop_db() operating_system.ensure_directory(data_dir, user=CONF.database_service_uid, group=CONF.database_service_uid, as_root=True) # This makes sure the include dir is created. self.app.set_data_dir(data_dir) # Prepare mysql configuration LOG.info('Preparing database configuration') self.app.configuration_manager.reset_configuration(config_contents) self.app.update_overrides(overrides) # Restore data from backup and reset root password if backup_info: self.perform_restore(context, data_dir, backup_info) self.reset_password_for_restore(ds_version=ds_version, data_dir=data_dir) # Start database service. command = self.get_start_db_params(data_dir) self.app.start_db(ds_version=ds_version, command=command) self.app.secure() enable_remote_root = (backup_info and self.adm.is_root_enabled()) if enable_remote_root: self.status.report_root(context) else: self.app.secure_root() if snapshot: # This instance is a replication slave self.attach_replica(context, snapshot, snapshot['config'])
def do_prepare(self, context, packages, databases, memory_mb, users, device_path, mount_point, backup_info, config_contents, root_password, overrides, cluster_config, snapshot, ds_version=None): operating_system.ensure_directory(self.app.datadir, user=CONF.database_service_uid, group=CONF.database_service_uid, as_root=True) operating_system.ensure_directory(service.WAL_ARCHIVE_DIR, user=CONF.database_service_uid, group=CONF.database_service_uid, as_root=True) LOG.info('Preparing database config files') self.app.configuration_manager.save_configuration(config_contents) self.app.set_data_dir(self.app.datadir) self.app.update_overrides(overrides) # Prepare pg_hba.conf self.app.apply_access_rules() self.configuration_manager.apply_system_override( {'hba_file': service.HBA_CONFIG_FILE}) # Restore data from backup and reset root password if backup_info: self.perform_restore(context, self.app.datadir, backup_info) if not snapshot: signal_file = f"{self.app.datadir}/recovery.signal" operating_system.execute_shell_cmd( f"touch {signal_file}", [], shell=True, as_root=True) operating_system.chown(signal_file, CONF.database_service_uid, CONF.database_service_uid, force=True, as_root=True) if snapshot: # This instance is a replica self.attach_replica(context, snapshot, snapshot['config']) # config_file can only be set on the postgres command line command = f"postgres -c config_file={service.CONFIG_FILE}" self.app.start_db(ds_version=ds_version, command=command)
def mount(self): if not operating_system.exists( self.mount_point, is_directory=True, as_root=True): operating_system.ensure_directory(self.mount_point, as_root=True) LOG.debug("Mounting volume. Device path:{0}, mount_point:{1}, " "volume_type:{2}, mount options:{3}".format( self.device_path, self.mount_point, self.volume_fstype, self.mount_options)) try: utils.execute("mount", "-t", self.volume_fstype, "-o", self.mount_options, self.device_path, self.mount_point, run_as_root=True, root_helper="sudo") except exception.ProcessExecutionError: log_fmt = "Could not mount '%s'." exc_fmt = _("Could not mount '%s'.") log_and_raise(log_fmt, exc_fmt, self.mount_point)
def restart(self): LOG.info("Restarting mysql") # Ensure folders permission for database. for folder in ['/etc/mysql', '/var/run/mysqld']: operating_system.ensure_directory( folder, user=CONF.database_service_uid, group=CONF.database_service_uid, force=True, as_root=True) try: docker_util.restart_container(self.docker_client) except Exception: LOG.exception("Failed to restart mysql") raise exception.TroveError("Failed to restart mysql") if not self.status.wait_for_status( service_status.ServiceStatuses.HEALTHY, CONF.state_change_wait_time, update_db=True ): raise exception.TroveError("Failed to start mysql") LOG.info("Finished restarting mysql")
def start_db(self, update_db=False, ds_version=None, command=None, extra_volumes=None): """Start and wait for database service.""" docker_image = CONF.get(CONF.datastore_manager).docker_image image = (f'{docker_image}:latest' if not ds_version else f'{docker_image}:{ds_version}') command = command if command else '' try: postgres_pass = self.get_auth_password(file="postgres.cnf") except exception.UnprocessableEntity: postgres_pass = utils.generate_random_password() # Get uid and gid user = "******" % (CONF.database_service_uid, CONF.database_service_uid) # Create folders for postgres on localhost for folder in ['/etc/postgresql', '/var/run/postgresql']: operating_system.ensure_directory(folder, user=CONF.database_service_uid, group=CONF.database_service_uid, force=True, as_root=True) volumes = { "/etc/postgresql": { "bind": "/etc/postgresql", "mode": "rw" }, "/var/run/postgresql": { "bind": "/var/run/postgresql", "mode": "rw" }, "/var/lib/postgresql": { "bind": "/var/lib/postgresql", "mode": "rw" }, "/var/lib/postgresql/data": { "bind": "/var/lib/postgresql/data", "mode": "rw" }, } if extra_volumes: volumes.update(extra_volumes) # Expose ports ports = {} tcp_ports = cfg.get_configuration_property('tcp_ports') for port_range in tcp_ports: for port in port_range: ports[f'{port}/tcp'] = port try: docker_util.start_container(self.docker_client, image, volumes=volumes, network_mode="bridge", ports=ports, user=user, environment={ "POSTGRES_PASSWORD": postgres_pass, "PGDATA": self.datadir, }, command=command) # Save root password LOG.debug("Saving root credentials to local host.") self.save_password('postgres', postgres_pass) except Exception: LOG.exception("Failed to start database service") raise exception.TroveError("Failed to start database service") if not self.status.wait_for_status( service_status.ServiceStatuses.HEALTHY, CONF.state_change_wait_time, update_db): raise exception.TroveError("Failed to start database service")