def sync_data_to_slaves(self, context): """Trigger an archive log switch and flush transactions down to the slaves. """ LOG.debug("sync_data_to_slaves - switching log file") with ora_service.LocalOracleClient(self.ORA_CONF.db_name) as client: client.execute("ALTER SYSTEM SWITCH LOGFILE")
def _complete_new_slave_setup(self, master_host, dbs): ora_conf = ora_service.OracleConfig() sys_password = ora_conf.sys_password with ora_service.OracleConnection( self.ORA_CONF.db_name, mode=(cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)) as conn: conn.startup() db_list = [db['db_unique_name'] for db in dbs] fal_server_list = ",".join("'%s'" % db for db in db_list) log_archive_dest = [] dest_index = 1 for db in db_list: if db != self.ORA_CONF.db_unique_name: dest_index += 1 log_archive_dest.append("SET LOG_ARCHIVE_DEST_%(dest_index)s=" "'SERVICE=%(db)s ASYNC VALID_FOR=" "(ONLINE_LOGFILES,PRIMARY_ROLE) " "DB_UNIQUE_NAME=%(db)s'" % { 'dest_index': dest_index, 'db': db }) # The RMAN DUPLICATE command requires connecting to target with the # 'sys' user. If we use any other user, such as 'os_admin', even with # the sysdba and sysoper roles assigned, it will still fail with: # ORA-01017: invalid username/password; logon denied cmd = ("""\"\ rman target %(admin_user)s/%(admin_pswd)s@%(host)s/%(db_name)s \ auxiliary %(admin_user)s/%(admin_pswd)s@%(db_unique_name)s <<EOF run { DUPLICATE TARGET DATABASE FOR STANDBY FROM ACTIVE DATABASE DORECOVER SPFILE SET db_unique_name='%(db_unique_name)s' COMMENT 'Is standby' %(log_archive_dest)s SET FAL_SERVER=%(fal_server_list)s COMMENT 'Is primary' NOFILENAMECHECK; } EXIT; EOF\" """) duplicate_cmd = (cmd % { 'admin_user': '******', 'admin_pswd': sys_password, 'host': master_host, 'db_name': self.ORA_CONF.db_name, 'db_unique_name': self.ORA_CONF.db_unique_name, 'fal_server_list': fal_server_list, 'log_archive_dest': "\n".join(log_archive_dest) }) utils.execute_with_timeout("su - oracle -c " + duplicate_cmd, run_as_root=True, root_helper='sudo', timeout=CONF.restore_usage_timeout, shell=True, log_output_on_error=True) with ora_service.LocalOracleClient(self.ORA_CONF.db_name) as client: client.execute("ALTER SYSTEM SET REDO_TRANSPORT_USER = %s " "SCOPE = BOTH" % ora_service.ADMIN_USER_NAME) client.execute("ALTER DATABASE OPEN READ ONLY") client.execute("ALTER DATABASE RECOVER MANAGED STANDBY DATABASE " "USING CURRENT LOGFILE DISCONNECT FROM SESSION")
def _log_apply_is_running(self): with ora_service.LocalOracleClient( self._get_config().db_name) as client: client.execute("select count(*) from v$managed_standby " "where process like 'MRP%'") row = client.fetchone() return int(row[0]) > 0
def get_master_ref(self, service, snapshot_info): """Capture information from a master node""" pfile = '/tmp/init%s_stby.ora' % self._get_config().db_name pwfile = ('%(ora_home)s/dbs/orapw%(db_name)s' % { 'ora_home': CONF.get(MANAGER).oracle_home, 'db_name': self._get_config().db_name }) ctlfile = '/tmp/%s_stby.ctl' % self._get_config().db_name oratabfile = '/etc/oratab' oracnffile = CONF.get(MANAGER).conf_file datafile = '/tmp/oradata.tar.gz' def _cleanup_tmp_files(): operating_system.remove(ctlfile, force=True, as_root=True) operating_system.remove(pfile, force=True, as_root=True) operating_system.remove(datafile, force=True, as_root=True) _cleanup_tmp_files() with ora_service.LocalOracleClient(self._get_config().db_name, service=True) as client: client.execute("ALTER DATABASE CREATE STANDBY CONTROLFILE AS " "'%s'" % ctlfile) ora_service.OracleAdmin().create_parameter_file(target=pfile, client=client) q = sql_query.Query() q.columns = ["value"] q.tables = ["v$parameter"] q.where = ["name = 'fal_server'"] client.execute(str(q)) row = client.fetchone() db_list = [] if row is not None and row[0] is not None: db_list = str(row[0]).split(",") db_list.insert(0, self._get_config().db_name) # Create a tar file containing files needed for slave creation utils.execute_with_timeout('tar', '-Pczvf', datafile, ctlfile, pwfile, pfile, oratabfile, oracnffile, run_as_root=True, root_helper='sudo') oradata_encoded = operating_system.read_file( datafile, codec=stream_codecs.Base64Codec(), as_root=True, decode=False) _cleanup_tmp_files() master_ref = { 'host': netutils.get_my_ipv4(), 'db_name': self._get_config().db_name, 'db_list': db_list, 'oradata': oradata_encoded, } return master_ref
def detach_slave(self, service, for_failover=False): """Detach this slave by disabling the log apply process""" if not for_failover: LOG.debug('detach_slave - Disabling the log apply process.') with ora_service.LocalOracleClient( self.ORA_CONF.db_name) as client: client.execute("ALTER DATABASE RECOVER MANAGED STANDBY " "DATABASE CANCEL")
def enable_as_master(self, service, master_config): # Turn this slave node into master when failing over # (eject-replica-source) if self._log_apply_is_running(): with ora_service.LocalOracleClient( self._get_config().db_name) as client: client.execute("ALTER DATABASE RECOVER MANAGED STANDBY " "DATABASE FINISH") client.execute("ALTER DATABASE ACTIVATE STANDBY DATABASE") client.execute("ALTER DATABASE OPEN") client.execute("ALTER SYSTEM SWITCH LOGFILE")
def complete_slave_setup(self, context, master_detail, slave_detail): """Finalize slave setup and start the slave Oracle processes.""" dbs = [master_detail] dbs.extend(slave_detail) is_new_repl_node = self._is_new_replication_node() self._create_tns_file(dbs) if is_new_repl_node: self._complete_new_slave_setup(master_detail['host'], dbs) else: with ora_service.LocalOracleClient(self.ORA_CONF.db_name, service=True) as ora_client: self._update_dynamic_params(ora_client, dbs)
def estimate_backup_size(self): """Estimate the backup size. The estimation is 1/3 the total size of datafiles, which is a conservative figure derived from the Oracle RMAN backupset compression ratio.""" with oracle_service.LocalOracleClient(self.db_name, service=True) as client: q = sql_query.Query() q.columns = ["sum(bytes)"] q.tables = ["dba_data_files"] client.execute(str(q)) result = client.fetchall() return result[0][0] / 3
def complete_master_setup(self, context, slave_detail): """Finalize master setup and start the master Oracle processes.""" dbs = [self.get_replication_detail(None)] dbs.extend(slave_detail) with ora_service.LocalOracleClient(self.ORA_CONF.db_name, service=True) as ora_client: if self._is_new_replication_node(): self._create_lsnr_file() self._create_standby_log_files(ora_client) self._create_static_params(ora_client) self._create_tns_file(dbs) self._update_dynamic_params(ora_client, dbs) ora_client.execute("ALTER SYSTEM SWITCH LOGFILE")
def detach_slave(self, service, for_failover=False): """Detach this slave by disabling the log apply process, setting it to read/write. """ if not for_failover: with ora_service.LocalOracleClient( self._get_config().db_name) as client: client.execute("ALTER DATABASE RECOVER MANAGED STANDBY " "DATABASE CANCEL") client.execute("ALTER DATABASE ACTIVATE STANDBY DATABASE") client.execute("ALTER DATABASE OPEN") for index in range(2, 31): client.execute("ALTER SYSTEM SET LOG_ARCHIVE_DEST_%s=''" % index) client.execute("ALTER SYSTEM SET LOG_ARCHIVE_CONFIG=''") client.execute("ALTER SYSTEM SET FAL_SERVER=''")
def enable_as_master(self, service, master_config, for_failover=False): """Turn a running slave node into a master node""" if for_failover: # Turn this slave node into master when failing over # (eject-replica-source) with ora_service.LocalOracleClient( self.ORA_CONF.db_name) as client: client.execute("ALTER DATABASE RECOVER MANAGED STANDBY " "DATABASE FINISH") client.execute("ALTER DATABASE ACTIVATE STANDBY DATABASE") client.execute("ALTER DATABASE OPEN") client.execute("ALTER SYSTEM SWITCH LOGFILE") else: # Turn this slave node into master when switching over # (promote-to-replica-source) if self._log_apply_is_running(): # Switchover from slave to master only if the current # instance is already a slave with ora_service.OracleConnection( self.ORA_CONF.db_name) as conn: cursor = conn.cursor() cursor.execute("ALTER DATABASE COMMIT TO SWITCHOVER TO " "PRIMARY WITH SESSION SHUTDOWN") conn.shutdown(mode=cx_Oracle.DBSHUTDOWN_IMMEDIATE) cursor.execute("alter database dismount") conn.shutdown(mode=cx_Oracle.DBSHUTDOWN_FINAL) # The DB has been shut down at this point, need to establish a # new connection in PRELIM_AUTH mode in order to start it up. with ora_service.OracleConnection( self.ORA_CONF.db_name, mode=(cx_Oracle.SYSDBA | cx_Oracle.PRELIM_AUTH)) as conn: conn.startup() # DB is now up but not open, re-connect to the DB in SYSDBA # mode to open it. with ora_service.OracleConnection( self.ORA_CONF.db_name) as conn: cursor = conn.cursor() cursor.execute("alter database mount") cursor.execute("alter database open") cursor.execute("ALTER SYSTEM SWITCH LOGFILE")
def enable_as_slave(self, service, snapshot, slave_config): """Turn this node into slave by enabling the log apply process.""" with ora_service.LocalOracleClient(self.ORA_CONF.db_name) as client: client.execute("select count(*) from v$managed_standby " "where process like 'MRP%'") row = client.fetchone() if int(row[0]) == 0: # Only attempt to enable log apply if it is not already # running LOG.debug('Slave processes does not exist in ' 'v$managed_standy, switching on LOG APPLY') client.execute("ALTER DATABASE RECOVER MANAGED STANDBY " "DATABASE USING CURRENT LOGFILE DISCONNECT " "FROM SESSION") utils.execute_with_timeout("sudo", "su", "-", "oracle", "-c", "lsnrctl reload", timeout=CONF.usage_timeout)