def setUp(self): super(MySqlAppTest, self).setUp() self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout self.orig_time_sleep = dbaas.time.sleep util.init_db() self.FAKE_ID = randint(1, 10000) InstanceServiceStatus.create(instance_id=self.FAKE_ID, status=rd_instance.ServiceStatuses.NEW) self.appStatus = FakeAppStatus(self.FAKE_ID, rd_instance.ServiceStatuses.NEW) self.mySqlApp = MySqlApp(self.appStatus) dbaas.time.sleep = Mock()
def get_replication_snapshot(self, context, snapshot_info, replica_source_config=None): LOG.debug("Getting replication snapshot.") app = MySqlApp(MySqlAppStatus.get()) replication = REPLICATION_STRATEGY_CLASS(context) replication.enable_as_master(app, replica_source_config) snapshot_id, log_position = (replication.snapshot_for_replication( context, app, None, snapshot_info)) mount_point = CONF.get(MANAGER).mount_point volume_stats = dbaas.get_filesystem_volume_stats(mount_point) replication_snapshot = { 'dataset': { 'datastore_manager': MANAGER, 'dataset_size': volume_stats.get('used', 0.0), 'volume_size': volume_stats.get('total', 0.0), 'snapshot_id': snapshot_id }, 'replication_strategy': REPLICATION_STRATEGY, 'master': replication.get_master_ref(app, snapshot_info), 'log_position': log_position } return replication_snapshot
def _read_log_position(self): backup_var_file = ('%s/backup_variables.txt' % MySqlApp.get_data_dir()) if operating_system.exists(backup_var_file): try: LOG.info(_("Reading log position from %s") % backup_var_file) backup_vars = operating_system.read_file( backup_var_file, stream_codecs.PropertiesCodec(delimiter='='), as_root=True) binlog_position = backup_vars['binlog_position'] binlog_file, binlog_pos = binlog_position.split(':') return { 'log_file': binlog_file, 'log_position': int(binlog_pos) } except Exception as ex: LOG.exception(ex) raise self.UnableToDetermineBinlogPosition( {'binlog_file': backup_var_file}) else: LOG.info(_("Log position detail not available. " "Using default values.")) return {'log_file': '', 'log_position': 4}
def user_and_pass(self): return ('--user=%(user)s --password=%(password)s --host=localhost ' '--socket=%(socket_file)s' % { 'user': ADMIN_USER_NAME, 'password': MySqlApp.get_auth_password(), 'socket_file': '/var/run/mysqld/mysqld.sock' })
def cmd(self): cmd = ('sudo innobackupex' ' --stream=xbstream' ' %(extra_opts)s ' + MySqlApp.get_data_dir() + ' 2>/tmp/innobackupex.log' ) return cmd + self.zip_cmd + self.encrypt_cmd
def test_secure_keep_root(self): mock_conn = mock_sql_connection() when(mock_conn).execute(any()).thenReturn(None) when(utils).execute_with_timeout("sudo", any(str), "stop").thenReturn( None) # skip writing the file for now when(os.path).isfile(any()).thenReturn(False) when(utils).execute_with_timeout( "sudo", "chmod", any(), any()).thenReturn(None) mock_status = mock() when(mock_status).wait_for_real_status_to_change_to( any(), any(), any()).thenReturn(True) app = MySqlApp(mock_status) when(app)._write_mycnf(any(), any()).thenReturn(True) app.secure('foo') verify(mock_conn, never).execute(TextClauseMatcher('root'))
def cmd(self): cmd = ("sudo innobackupex" " --stream=xbstream" " --incremental" " --incremental-lsn=%(lsn)s" " %(extra_opts)s " + self.user_and_pass + MySqlApp.get_data_dir() + " 2>" + self.log_file_path) return cmd + self.zip_cmd + self.encrypt_cmd
def cmd(self): cmd = ('sudo innobackupex' ' --stream=xbstream' ' --incremental' ' --incremental-lsn=%(lsn)s' ' %(extra_opts)s ' + self.user_and_pass + ' ' + MySqlApp.get_data_dir() + ' 2>/tmp/innobackupex.log') return cmd + self.zip_cmd + self.encrypt_cmd
def cmd(self): cmd = ("sudo innobackupex" " --stream=xbstream" " %(extra_opts)s " + self.user_and_pass + MySqlApp.get_data_dir() + " 2>/tmp/innobackupex.log" ) return cmd + self.zip_cmd + self.encrypt_cmd
def cmd(self): cmd = ('sudo innobackupex' ' --stream=xbstream' ' --incremental' ' --incremental-lsn=%(lsn)s' ' %(extra_opts)s ' + self.user_and_pass + MySqlApp.get_data_dir() + ' 2>/tmp/innobackupex.log') return cmd + self.zip_cmd + self.encrypt_cmd
def cmd(self): user_and_pass = ( ' --password=%(password)s -u %(user)s ' '2>/tmp/mysqldump.log' % {'password': MySqlApp.get_auth_password(), 'user': ADMIN_USER_NAME}) cmd = ('mysqldump' ' --all-databases' ' %(extra_opts)s' ' --opt' + user_and_pass) return cmd + self.zip_cmd + self.encrypt_cmd
def attach_replication_slave(self, context, snapshot, slave_config): LOG.debug("Attaching replication snapshot.") app = MySqlApp(MySqlAppStatus.get()) try: self._validate_slave_for_replication(context, snapshot) replication = REPLICATION_STRATEGY_CLASS(context) replication.enable_as_slave(app, snapshot, slave_config) except Exception: LOG.exception("Error enabling replication.") app.status.set_status(rd_instance.ServiceStatuses.FAILED) raise
def cmd(self): user_and_pass = ( " --password='******' -u %(user)s " "2>/tmp/mysqldump.log" % {'password': MySqlApp.get_auth_password(), 'user': ADMIN_USER_NAME}) cmd = ("mysqldump" " --all-databases" " %(extra_opts)s" " --opt" + user_and_pass) return cmd + self.zip_cmd + self.encrypt_cmd
def cmd(self): user_and_pass = (' --password=%(password)s -u %(user)s ' '2>/tmp/mysqldump.log' % { 'password': MySqlApp.get_auth_password(), 'user': ADMIN_USER_NAME }) cmd = ('mysqldump' ' --all-databases' ' %(extra_opts)s' ' --opt' + user_and_pass) return cmd + self.zip_cmd + self.encrypt_cmd
def setUp(self): super(MySqlAppTest, self).setUp() self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout self.orig_time_sleep = time.sleep util.init_db() self.FAKE_ID = str(uuid4()) InstanceServiceStatus.create(instance_id=self.FAKE_ID, status=rd_instance.ServiceStatuses.NEW) self.appStatus = FakeAppStatus(self.FAKE_ID, rd_instance.ServiceStatuses.NEW) self.mySqlApp = MySqlApp(self.appStatus) mysql_service = { 'cmd_start': Mock(), 'cmd_stop': Mock(), 'cmd_enable': Mock(), 'cmd_disable': Mock(), 'bin': Mock() } dbaas.operating_system.service_discovery = Mock( return_value=mysql_service) time.sleep = Mock()
def _read_last_master_gtid(self): INFO_FILE = ('%s/xtrabackup_binlog_info' % MySqlApp.get_data_dir()) LOG.info(_("Setting read permissions on %s") % INFO_FILE) operating_system.chmod(INFO_FILE, FileMode.ADD_READ_ALL, as_root=True) LOG.info(_("Reading last master GTID from %s") % INFO_FILE) try: with open(INFO_FILE, 'rb') as f: row = f.read().split('\t') return row[2] except (IOError, IndexError) as ex: LOG.exception(ex) raise self.UnableToDetermineLastMasterGTID( {'binlog_file': INFO_FILE})
def _read_log_position(self): INFO_FILE = ('%s/xtrabackup_binlog_info' % MySqlApp.get_data_dir()) LOG.info(_("Setting read permissions on %s") % INFO_FILE) operating_system.chmod(INFO_FILE, FileMode.ADD_READ_ALL, as_root=True) LOG.info(_("Reading log position from %s") % INFO_FILE) try: with open(INFO_FILE, 'rb') as f: row = next(csv.reader(f, delimiter='\t', skipinitialspace=True)) return {'log_file': row[0], 'log_position': int(row[1])} except (IOError, IndexError) as ex: LOG.exception(ex) raise self.UnableToDetermineBinlogPosition( {'binlog_file': INFO_FILE})
def _read_log_position(self): INFO_FILE = ('%s/xtrabackup_binlog_info' % MySqlApp.get_data_dir()) LOG.info(_("Setting read permissions on %s") % INFO_FILE) operating_system.chmod(INFO_FILE, FileMode.ADD_READ_ALL, as_root=True) LOG.info(_("Reading log position from %s") % INFO_FILE) try: with open(INFO_FILE, 'rb') as f: row = csv.reader(f, delimiter='\t', skipinitialspace=True).next() return { 'log_file': row[0], 'log_position': int(row[1]) } except (IOError, IndexError) as ex: LOG.exception(ex) raise self.UnableToDetermineBinlogPosition( {'info_file': INFO_FILE})
def test_secure_with_mycnf_error(self): mock_conn = mock_sql_connection() when(mock_conn).execute(any()).thenReturn(None) when(utils).execute_with_timeout("sudo", any(str), "stop").thenReturn( None) # skip writing the file for now when(os.path).isfile(any()).thenReturn(False) mock_status = mock() when(mock_status).wait_for_real_status_to_change_to( any(), any(), any()).thenReturn(True) app = MySqlApp(mock_status) self.assertRaises(TypeError, app.secure, None) verify(mock_conn, atleast=2).execute(any()) inorder.verify(mock_status).wait_for_real_status_to_change_to( rd_instance.ServiceStatuses.SHUTDOWN, any(), any()) verifyNoMoreInteractions(mock_status)
def setUp(self): super(MySqlAppTest, self).setUp() self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout self.orig_time_sleep = dbaas.time.sleep util.init_db() self.FAKE_ID = randint(1, 10000) InstanceServiceStatus.create(instance_id=self.FAKE_ID, status=rd_instance.ServiceStatuses.NEW) self.appStatus = FakeAppStatus(self.FAKE_ID, rd_instance.ServiceStatuses.NEW) self.mySqlApp = MySqlApp(self.appStatus) mysql_service = {'cmd_start': Mock(), 'cmd_stop': Mock(), 'cmd_enable': Mock(), 'cmd_disable': Mock(), 'bin': Mock()} dbaas.operating_system.service_discovery = Mock(return_value= mysql_service) dbaas.time.sleep = Mock()
def update_overrides(self, context, overrides, remove=False): app = MySqlApp(MySqlAppStatus.get()) if remove: app.remove_overrides() app.update_overrides(overrides)
def reset_configuration(self, context, configuration): app = MySqlApp(MySqlAppStatus.get()) app.reset_configuration(configuration)
def apply_overrides(self, context, overrides): LOG.debug("Applying overrides (%s)." % overrides) app = MySqlApp(MySqlAppStatus.get()) app.apply_overrides(overrides)
def stop_db(self, context, do_not_start_on_reboot=False): app = MySqlApp(MySqlAppStatus.get()) app.stop_db(do_not_start_on_reboot=do_not_start_on_reboot)
def restart(self, context): app = MySqlApp(MySqlAppStatus.get()) app.restart()
def get_replica_context(self, context): LOG.debug("Getting replica context.") app = MySqlApp(MySqlAppStatus.get()) replication = REPLICATION_STRATEGY_CLASS(context) replica_info = replication.get_replica_context(app) return replica_info
def detach_replica(self, context, for_failover=False): LOG.debug("Detaching replica.") app = MySqlApp(MySqlAppStatus.get()) replication = REPLICATION_STRATEGY_CLASS(context) replica_info = replication.detach_slave(app, for_failover) return replica_info
def wait_for_txn(self, context, txn): LOG.debug("Calling wait_for_txn.") MySqlApp(MySqlAppStatus.get()).wait_for_txn(txn)
def get_last_txn(self, context): LOG.debug("Calling get_last_txn") return MySqlApp(MySqlAppStatus.get()).get_last_txn()
def get_txn_count(self, context): LOG.debug("Calling get_txn_count") return MySqlApp(MySqlAppStatus.get()).get_txn_count()
def enable_as_master(self, context, replica_source_config): LOG.debug("Calling enable_as_master.") app = MySqlApp(MySqlAppStatus.get()) replication = REPLICATION_STRATEGY_CLASS(context) replication.enable_as_master(app, replica_source_config)
def prepare(self, context, packages, databases, memory_mb, users, device_path=None, mount_point=None, backup_info=None, config_contents=None, root_password=None, overrides=None, cluster_config=None, snapshot=None): """Makes ready DBAAS on a Guest container.""" MySqlAppStatus.get().begin_install() # status end_mysql_install set with secure() app = MySqlApp(MySqlAppStatus.get()) app.install_if_needed(packages) if device_path: #stop and do not update database app.stop_db() device = volume.VolumeDevice(device_path) # unmount if device is already mounted device.unmount_device(device_path) device.format() if os.path.exists(mount_point): #rsync exiting data device.migrate_data(mount_point) #mount the volume device.mount(mount_point) LOG.debug("Mounted the volume.") app.start_mysql() if backup_info: self._perform_restore(backup_info, context, mount_point, app) LOG.debug("Securing MySQL now.") app.secure(config_contents, overrides) enable_root_on_restore = (backup_info and MySqlAdmin().is_root_enabled()) if root_password and not backup_info: app.secure_root(secure_remote_root=True) MySqlAdmin().enable_root(root_password) elif enable_root_on_restore: app.secure_root(secure_remote_root=False) MySqlAppStatus.get().report_root('root') else: app.secure_root(secure_remote_root=True) app.complete_install_or_restart() if databases: self.create_database(context, databases) if users: self.create_user(context, users) if snapshot: self.attach_replica(context, snapshot, snapshot['config']) LOG.info(_('Completed setup of MySQL database instance.'))
def make_read_only(self, context, read_only): LOG.debug("Executing make_read_only(%s)" % read_only) app = MySqlApp(MySqlAppStatus.get()) app.make_read_only(read_only)
def user_and_pass(self): return (' --user=%(user)s --password=%(password)s --host=127.0.0.1 ' % {'user': ADMIN_USER_NAME, 'password': MySqlApp.get_auth_password()})
def demote_replication_master(self, context): LOG.debug("Demoting replication master.") app = MySqlApp(MySqlAppStatus.get()) replication = REPLICATION_STRATEGY_CLASS(context) replication.demote_master(app)
class MySqlAppTest(testtools.TestCase): def setUp(self): super(MySqlAppTest, self).setUp() self.orig_utils_execute_with_timeout = dbaas.utils.execute_with_timeout self.orig_time_sleep = dbaas.time.sleep util.init_db() self.FAKE_ID = randint(1, 10000) InstanceServiceStatus.create(instance_id=self.FAKE_ID, status=rd_instance.ServiceStatuses.NEW) self.appStatus = FakeAppStatus(self.FAKE_ID, rd_instance.ServiceStatuses.NEW) self.mySqlApp = MySqlApp(self.appStatus) dbaas.time.sleep = Mock() def tearDown(self): super(MySqlAppTest, self).tearDown() dbaas.utils.execute_with_timeout = self.orig_utils_execute_with_timeout dbaas.time.sleep = self.orig_time_sleep InstanceServiceStatus.find_by(instance_id=self.FAKE_ID).delete() def assert_reported_status(self, expected_status): service_status = InstanceServiceStatus.find_by( instance_id=self.FAKE_ID) self.assertEqual(expected_status, service_status.status) def mysql_starts_successfully(self): def start(update_db=False): self.appStatus.set_next_status( rd_instance.ServiceStatuses.RUNNING) self.mySqlApp.start_mysql.side_effect = start def mysql_starts_unsuccessfully(self): def start(): raise RuntimeError("MySQL failed to start!") self.mySqlApp.start_mysql.side_effect = start def mysql_stops_successfully(self): def stop(): self.appStatus.set_next_status( rd_instance.ServiceStatuses.SHUTDOWN) self.mySqlApp.stop_db.side_effect = stop def mysql_stops_unsuccessfully(self): def stop(): raise RuntimeError("MySQL failed to stop!") self.mySqlApp.stop_db.side_effect = stop def test_stop_mysql(self): dbaas.utils.execute_with_timeout = Mock() self.appStatus.set_next_status( rd_instance.ServiceStatuses.SHUTDOWN) self.mySqlApp.stop_db() self.assert_reported_status(rd_instance.ServiceStatuses.NEW) def test_stop_mysql_with_db_update(self): dbaas.utils.execute_with_timeout = Mock() self.appStatus.set_next_status( rd_instance.ServiceStatuses.SHUTDOWN) self.mySqlApp.stop_db(True) self.assert_reported_status(rd_instance.ServiceStatuses.SHUTDOWN) def test_stop_mysql_error(self): dbaas.utils.execute_with_timeout = Mock() self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING) self.mySqlApp.state_change_wait_time = 1 self.assertRaises(RuntimeError, self.mySqlApp.stop_db) def test_restart_is_successful(self): self.mySqlApp.start_mysql = Mock() self.mySqlApp.stop_db = Mock() self.mysql_stops_successfully() self.mysql_starts_successfully() self.mySqlApp.restart() self.assertTrue(self.mySqlApp.stop_db.called) self.assertTrue(self.mySqlApp.start_mysql.called) self.assert_reported_status(rd_instance.ServiceStatuses.RUNNING) def test_restart_mysql_wont_start_up(self): self.mySqlApp.start_mysql = Mock() self.mySqlApp.stop_db = Mock() self.mysql_stops_unsuccessfully() self.mysql_starts_unsuccessfully() self.assertRaises(RuntimeError, self.mySqlApp.restart) self.assertTrue(self.mySqlApp.stop_db.called) self.assertFalse(self.mySqlApp.start_mysql.called) self.assert_reported_status(rd_instance.ServiceStatuses.NEW) def test_wipe_ib_logfiles_no_file(self): from trove.common.exception import ProcessExecutionError processexecerror = ProcessExecutionError('No such file or directory') dbaas.utils.execute_with_timeout = Mock(side_effect=processexecerror) self.mySqlApp.wipe_ib_logfiles() def test_wipe_ib_logfiles_error(self): from trove.common.exception import ProcessExecutionError mocked = Mock(side_effect=ProcessExecutionError('Error')) dbaas.utils.execute_with_timeout = mocked self.assertRaises(ProcessExecutionError, self.mySqlApp.wipe_ib_logfiles) def test_start_mysql(self): dbaas.utils.execute_with_timeout = Mock() self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING) self.mySqlApp.start_mysql() self.assert_reported_status(rd_instance.ServiceStatuses.NEW) def test_start_mysql_with_db_update(self): dbaas.utils.execute_with_timeout = Mock() self.appStatus.set_next_status(rd_instance.ServiceStatuses.RUNNING) self.mySqlApp.start_mysql(True) self.assert_reported_status(rd_instance.ServiceStatuses.RUNNING) def test_start_mysql_runs_forever(self): dbaas.utils.execute_with_timeout = Mock() self.mySqlApp.state_change_wait_time = 1 self.appStatus.set_next_status(rd_instance.ServiceStatuses.SHUTDOWN) self.assertRaises(RuntimeError, self.mySqlApp.start_mysql) self.assert_reported_status(rd_instance.ServiceStatuses.SHUTDOWN) def test_start_mysql_error(self): self.mySqlApp._enable_mysql_on_boot = Mock() from trove.common.exception import ProcessExecutionError mocked = Mock(side_effect=ProcessExecutionError('Error')) dbaas.utils.execute_with_timeout = mocked self.assertRaises(RuntimeError, self.mySqlApp.start_mysql) def test_start_db_with_conf_changes(self): self.mySqlApp.start_mysql = Mock() self.mySqlApp._write_mycnf = Mock() self.mysql_starts_successfully() self.appStatus.status = rd_instance.ServiceStatuses.SHUTDOWN self.mySqlApp.start_db_with_conf_changes(Mock()) self.assertTrue(self.mySqlApp._write_mycnf.called) self.assertTrue(self.mySqlApp.start_mysql.called) self.assertEqual(self.appStatus._get_actual_db_status(), rd_instance.ServiceStatuses.RUNNING) def test_start_db_with_conf_changes_mysql_is_running(self): self.mySqlApp.start_mysql = Mock() self.mySqlApp._write_mycnf = Mock() self.appStatus.status = rd_instance.ServiceStatuses.RUNNING self.assertRaises(RuntimeError, self.mySqlApp.start_db_with_conf_changes, Mock())
def prepare(self, context, packages, databases, memory_mb, users, device_path=None, mount_point=None, backup_info=None, config_contents=None, root_password=None, overrides=None, cluster_config=None, snapshot=None): """Makes ready DBAAS on a Guest container.""" MySqlAppStatus.get().begin_install() # status end_mysql_install set with secure() app = MySqlApp(MySqlAppStatus.get()) app.install_if_needed(packages) if device_path: # stop and do not update database app.stop_db() device = volume.VolumeDevice(device_path) # unmount if device is already mounted device.unmount_device(device_path) device.format() if os.path.exists(mount_point): # rsync existing data to a "data" sub-directory # on the new volume device.migrate_data(mount_point, target_subdir="data") # mount the volume device.mount(mount_point) operating_system.chown(mount_point, 'mysql', 'mysql', recursive=False, as_root=True) LOG.debug("Mounted the volume at %s." % mount_point) # We need to temporarily update the default my.cnf so that # mysql will start after the volume is mounted. Later on it # will be changed based on the config template and restart. app.update_overrides("[mysqld]\ndatadir=%s/data\n" % mount_point) app.start_mysql() if backup_info: self._perform_restore(backup_info, context, mount_point + "/data", app) LOG.debug("Securing MySQL now.") app.secure(config_contents, overrides) enable_root_on_restore = (backup_info and MySqlAdmin().is_root_enabled()) if root_password and not backup_info: app.secure_root(secure_remote_root=True) MySqlAdmin().enable_root(root_password) elif enable_root_on_restore: app.secure_root(secure_remote_root=False) MySqlAppStatus.get().report_root(context, 'root') else: app.secure_root(secure_remote_root=True) app.complete_install_or_restart() if databases: self.create_database(context, databases) if users: self.create_user(context, users) if snapshot: self.attach_replica(context, snapshot, snapshot['config']) LOG.info(_('Completed setup of MySQL database instance.'))
def prepare(self, context, packages, databases, memory_mb, users, device_path=None, mount_point=None, backup_info=None, config_contents=None, root_password=None, overrides=None): """Makes ready DBAAS on a Guest container.""" MySqlAppStatus.get().begin_install() # status end_mysql_install set with secure() app = MySqlApp(MySqlAppStatus.get()) app.install_if_needed(packages) if device_path: #stop and do not update database app.stop_db() device = volume.VolumeDevice(device_path) device.format() if os.path.exists(CONF.mount_point): #rsync exiting data device.migrate_data(CONF.mount_point) #mount the volume device.mount(mount_point) LOG.debug(_("Mounted the volume.")) app.start_mysql() if backup_info: self._perform_restore(backup_info, context, CONF.mount_point, app) LOG.info(_("Securing mysql now.")) app.secure(config_contents, overrides) enable_root_on_restore = (backup_info and MySqlAdmin().is_root_enabled()) if root_password and not backup_info: app.secure_root(secure_remote_root=True) MySqlAdmin().enable_root(root_password) elif enable_root_on_restore: app.secure_root(secure_remote_root=False) else: app.secure_root(secure_remote_root=True) app.complete_install_or_restart() if databases: self.create_database(context, databases) if users: self.create_user(context, users) LOG.info('"prepare" call has finished.')
def start_db_with_conf_changes(self, context, config_contents): app = MySqlApp(MySqlAppStatus.get()) app.start_db_with_conf_changes(config_contents)
def apply_overrides(self, context, overrides): app = MySqlApp(MySqlAppStatus.get()) app.apply_overrides(overrides)
def update_overrides(self, context, overrides, remove=False): LOG.debug("Updating overrides (%s)." % overrides) app = MySqlApp(MySqlAppStatus.get()) app.update_overrides(overrides, remove=remove)
def user_and_pass(self): return (" --user=%(user)s --password='******' " % {'user': ADMIN_USER_NAME, 'password': MySqlApp.get_auth_password()})
def get_latest_txn_id(self, context): LOG.debug("Calling get_latest_txn_id.") return MySqlApp(MySqlAppStatus.get()).get_latest_txn_id()