Exemplo n.º 1
0
    def __init__(self, master_vals, slave_vals, options, skip_conn_err=True):
        """Constructor

        master_vals[in]    master server connection dictionary
        slave_vals[in]     list of slave server connection dictionaries
        options[in]        options dictionary
        skip_conn_err[in]  if True, do not fail on connection failure
                           Default = True
        """
        from mysql.utilities.common.topology import Topology

        self.master_vals = master_vals
        self.options = options
        self.quiet = self.options.get("quiet", False)
        self.logging = self.options.get("logging", False)
        self.candidates = self.options.get("candidates", None)

        # Replace all local host IP addresses (i.e. 127.0.0.1) by localhost
        for candidate in self.candidates:
            if candidate['host'] == '127.0.0.1':
                candidate['host'] = 'localhost'
        for slave in slave_vals:
            if slave['host'] == '127.0.0.1':
                slave['host'] = 'localhost'

        self.rpl_user = self.options.get("rpl_user", None)
        self.topology = Topology(master_vals, slave_vals, self.options,
                                 skip_conn_err)
Exemplo n.º 2
0
    def _switch_master(self, master_vals, use_rpl_setup=True):
        """Switches replication to a new master.

        This method stops replication with the old master if exists and
        starts the replication with a new one.

        master_vals[in]      Master server connection dictionary.
        use_rpl_setup[in]    Used to control the first pass in the masters
                             round-robin scheduling.
        """
        if self.topology:
            # Stop slave
            master = self._get_master()
            if master.is_alive():
                master.disconnect()
            slave = self._get_slave()
            if not slave.is_alive() and not self._reconnect_server(slave):
                msg = "Failed to connect to the slave."
                self._report(msg, logging.CRITICAL, False)
                raise UtilRplError(msg)
            slave.stop()
            slave.disconnect()

        self._report(
            "# Switching to master '{0}:{1}'."
            "".format(master_vals["host"], master_vals["port"]), logging.INFO,
            True)

        try:
            # Setup replication on the new master
            self._setup_replication(master_vals, use_rpl_setup)

            # Create a Topology object
            self.topology = Topology(master_vals, [self.slave_vals],
                                     self.options)
        except UtilError as err:
            msg = "Error while switching master: {0}".format(err.errmsg)
            self._report(msg, logging.CRITICAL, False)
            raise UtilRplError(err.errmsg)

        # Only works for GTID_MODE=ON
        if not self.topology.gtid_enabled():
            msg = ("Topology must support global transaction ids and have "
                   "GTID_MODE=ON.")
            self._report(msg, logging.CRITICAL, False)
            raise UtilRplError(msg)

        # Check for mixing IP and hostnames
        if not self._check_host_references():
            print("# WARNING: {0}".format(HOST_IP_WARNING))
            self._report(HOST_IP_WARNING, logging.WARN, False)
Exemplo n.º 3
0
    def __init__(self, master_vals, slave_vals, options,
                 skip_conn_err=True):
        """Constructor

        master_vals[in]    master server connection dictionary
        slave_vals[in]     list of slave server connection dictionaries
        options[in]        options dictionary
        skip_conn_err[in]  if True, do not fail on connection failure
                           Default = True
        """
        # A sys.stdout copy, that can be used later to turn on/off stdout
        self.stdout_copy = sys.stdout
        self.stdout_devnull = open(os.devnull, "w")

        # Disable stdout when running --daemon with start, stop or restart
        daemon = options.get("daemon")
        if daemon:
            if daemon in ("start", "nodetach"):
                print("Starting failover daemon...")
            elif daemon == "stop":
                print("Stopping failover daemon...")
            else:
                print("Restarting failover daemon...")
            # Disable stdout if daemon not nodetach
            if daemon != "nodetach":
                sys.stdout = self.stdout_devnull

        self.master = None
        self.master_vals = master_vals
        self.options = options
        self.quiet = self.options.get("quiet", False)
        self.logging = self.options.get("logging", False)
        self.candidates = self.options.get("candidates", None)
        self.verbose = self.options.get("verbose", None)
        self.rpl_user = self.options.get("rpl_user", None)
        self.ssl_ca = options.get("ssl_ca", None)
        self.ssl_cert = options.get("ssl_cert", None)
        self.ssl_key = options.get("ssl_key", None)
        if self.ssl_ca or self.ssl_cert or self.ssl_key:
            self.ssl = True

        try:
            self.topology = Topology(master_vals, slave_vals, self.options,
                                     skip_conn_err)
        except Exception as err:
            if daemon and daemon != "nodetach":
                # Turn on sys.stdout
                sys.stdout = self.stdout_copy
            raise UtilRplError(str(err))
Exemplo n.º 4
0
def skip_slaves_trx(gtid_set, slaves_cnx_val, options):
    """Skip transactions on slaves.

    This method skips the given transactions (GTID set) on all the specified
    slaves. That is, an empty transaction is injected for each GTID in
    the given set for one of each slaves. In case a slave already has an
    executed transaction for a given GTID then that GTID is ignored for this
    slave.

    gtid_set[in]            String representing the set of GTIDs to skip.
    slaves_cnx_val[in]      List of the dictionaries with the connection
                            values for each target slave.
    options[in]             Dictionary of options (dry_run, verbosity).

    Throws an UtilError exception if an error occurs during the execution.
    """
    verbosity = options.get('verbosity')
    dryrun = options.get('dry_run')

    # Connect to slaves.
    rpl_topology = Topology(None, slaves_cnx_val, options)

    # Check required privileges.
    errors = rpl_topology.check_privileges(skip_master=True)
    if errors:
        err_details = ''
        for err in errors:
            err_msg = ERROR_USER_WITHOUT_PRIVILEGES.format(
                user=err[0], host=err[1], port=err[2],
                operation='inject empty transactions', req_privileges=err[3])
            err_details = '{0}{1}\n'.format(err_details, err_msg)
        err_details.strip()
        raise UtilRplError("Not enough privileges.\n{0}".format(err_details))

    # GTID must be enabled on all servers.
    srv_list = rpl_topology.get_servers_with_gtid_not_on()
    if srv_list:
        if verbosity:
            print("# Slaves with GTID not enabled:")
            for srv in srv_list:
                msg = "#  - GTID_MODE={0} on {1}:{2}".format(srv[2], srv[0],
                                                             srv[1])
                print(msg)
        raise UtilRplError(_GTID_ON_REQ.format(action='Transaction skip'))

    if dryrun:
        print("#")
        print("# WARNING: Executing utility in dry run mode (read only).")

    # Get GTID set that can be skipped, i.e., not in GTID_EXECUTED.
    gtids_by_slave = rpl_topology.slaves_gtid_subtract_executed(gtid_set)

    # Output GTID set that will be skipped.
    print("#")
    print("# GTID set to be skipped for each server:")
    has_gtid_to_skip = False
    for host, port, gtids_to_skip in gtids_by_slave:
        if not gtids_to_skip:
            gtids_to_skip = 'None'
        else:
            # Set flag to indicate that there is at least one GTID to skip.
            has_gtid_to_skip = True
        print("# - {0}@{1}: {2}".format(host, port, gtids_to_skip))

    # Create dictionary to directly access the slaves instances.
    slaves_dict = rpl_topology.get_slaves_dict()

    # Skip transactions for the given list of slaves.
    print("#")
    if has_gtid_to_skip:
        for host, port, gtids_to_skip in gtids_by_slave:
            if gtids_to_skip:
                # Decompose GTID set into a list of single transactions.
                gtid_items = gtid_set_itemize(gtids_to_skip)
                dryrun_mark = '(dry run) ' if dryrun else ''
                print("# {0}Injecting empty transactions for '{1}:{2}'"
                      "...".format(dryrun_mark, host, port))
                slave_key = '{0}@{1}'.format(host, port)
                slave_srv = slaves_dict[slave_key]['instance']
                for uuid, trx_list in gtid_items:
                    for trx_num in trx_list:
                        trx_to_skip = '{0}:{1}'.format(uuid, trx_num)
                        if verbosity:
                            print("# - {0}".format(trx_to_skip))
                        if not dryrun:
                            # Inject empty transaction.
                            slave_srv.inject_empty_trx(
                                trx_to_skip, gtid_next_automatic=False)
                if not dryrun:
                    slave_srv.set_gtid_next_automatic()
    else:
        print("# No transaction to skip.")
    print("#\n#...done.\n#")
Exemplo n.º 5
0
    def purge(self):
        """The Purge Method

        Determines the latest log file to purge among all the slaves, which
        becomes the target file to purge binary logs to, in case no other
        file is specified.
        """
        # Create a topology object to verify the connection between master and
        # slaves servers.
        self.topology = Topology(self.master_cnx_val,
                                 self.slaves_cnx_val,
                                 self.options,
                                 skip_conn_err=False)

        self.master = self.topology.master
        self.slaves = self.topology.slaves

        # Check required privileges
        check_privileges(self.master, BINLOG_OP_PURGE,
                         ["SUPER", "REPLICATION SLAVE"], BINLOG_OP_PURGE_DESC,
                         self.verbosity, self._report)

        # Get binlog info
        binlog_file_name, active_binlog_file, active_binlog_index = (
            get_binlog_info(self.master,
                            reporter=self._report,
                            server_name="master",
                            verbosity=self.verbosity))

        # Verify this Master has at least one slave.
        if not self.slaves:
            errormsg = (_CAN_NOT_VERIFY_SLAVES_STATUS.format(
                host=self.master.host, port=self.master.port))
            raise UtilError(errormsg)

        # verify the given slaves are connected to this Master.
        if self.slaves_cnx_val and self.slaves:
            for slave in self.slaves:
                slave['instance'].is_configured_for_master(self.master,
                                                           verify_state=False,
                                                           raise_error=True)

                # IO running verification for --slaves option
                if not slave['instance'].is_connected():
                    if self.verbosity:
                        self._report("# Slave '{0}:{1}' IO not running"
                                     "".format(slave['host'], slave['port']))
                    raise UtilError(
                        _CAN_NOT_VERIFY_SLAVE_STATUS.format(
                            host=slave['host'], port=slave['port']))

        target_binlog_index = self.get_target_binlog_index(binlog_file_name)

        index_last_in_use = determine_purgeable_binlogs(
            active_binlog_index,
            self.slaves,
            reporter=self._report,
            verbosity=self.verbosity)

        self._purge(index_last_in_use,
                    active_binlog_file,
                    binlog_file_name,
                    target_binlog_index,
                    server=self.master,
                    server_is_master=True)