Пример #1
0
    def execute(self, proc_uuids=None):
        """Wait until a set of procedures uniquely identified by their uuids
        finish their execution.

        However, before starting waiting, the function checks if the procedures
        exist. If one of the procedures is not found, the following exception
        is raised :class:`~mysql.fabric.errors.ProcedureError`.

        :param proc_uuids: Iterable with procedures' UUIDs.
        """
        if proc_uuids is None:
            raise _errors.ProcedureError("Please, specify which procedure(s) you will be waiting for.")

        procedures = []
        for proc_uuid in proc_uuids:
            proc_uuid = _uuid.UUID(proc_uuid.strip())
            procedure = _executor.Executor().get_procedure(proc_uuid)
            if not procedure:
                raise _errors.ProcedureError("Procedure (%s) was not found." % (proc_uuid,))
            procedures.append(procedure)

        command_results = CommandResult(error=None)
        for procedure in procedures:
            command_result = ProcedureCommand.wait_for_procedures([procedure], True)
            if command_result.error is None:
                for result in command_result.results:
                    command_results.append_result(result)
            else:
                result = ResultSet(names=["uuid", "error"], types=[str, str])
                result.append_row([str(procedure.uuid), str(command_result.error)])
                command_results.append_result(result)

        return command_results
Пример #2
0
def _decode(packet):
    """Decode the data structure used with XML-RPC into a CommandResult.

    Since the result sets are merged into a single list of
    dictionaries, we separate them into result sets using the keys for
    the dictionaries.

    """

    _LOGGER.debug("Decode packet: %s", packet)
    version, fabric_uuid, ttl, error, rsets = packet
    if version != FORMAT_VERSION:
        raise TypeError("XML-RPC packet format version was %d, expected %d",
                        version, FORMAT_VERSION)
    result = CommandResult(error, uuid=fabric_uuid, ttl=ttl)
    if len(rsets) > 0:
        for rset in rsets:
            # If the result set contain at least one row, use that to
            # deduce the types for the columns. If not, assume it is
            # all strings.
            if len(rset['rows']) > 0:
                types = [type(val) for val in rset['rows'][0]]
            else:
                types = [str] * len(rset['info']['names'])

            # Create the result set and add the rows from the packet.
            rs = ResultSet(names=rset['info']['names'], types=types)
            for row in rset['rows']:
                rs.append_row(row)
            result.append_result(rs)
    return result
Пример #3
0
    def test_xmlrpc_execute(self):
        "Test XML-RPC encoding and decoding functions."
        cmd = _xmlrpc._CommandExecuteAndEncode(NewRemoteCommand())
        result1 = CommandResult(None)
        result1.append_result(self.rset)
        packet = cmd()
        self.assertEqual(
            packet,
            [
                _xmlrpc.FORMAT_VERSION,
                str(FABRIC_UUID),
                utils.TTL,
                '',  # No error
                [  # One result set with one row
                    {
                        'info': {
                            'names': ['foo']
                        },
                        'rows': [
                            ("executed", ),
                        ]
                    }
                ]
            ])

        result2 = _xmlrpc._decode(packet)
Пример #4
0
def _decode(packet):
    """Decode the data structure used with XML-RPC into a CommandResult.

    Since the result sets are merged into a single list of
    dictionaries, we separate them into result sets using the keys for
    the dictionaries.

    """

    _LOGGER.debug("Decode packet: %s", packet)
    version, fabric_uuid, ttl, error, rsets = packet
    if version != FORMAT_VERSION:
        raise TypeError("XML-RPC packet format version was %d, expected %d",
                        version, FORMAT_VERSION)
    result = CommandResult(error, uuid=fabric_uuid, ttl=ttl)
    if len(rsets) > 0:
        for rset in rsets:
            # If the result set contain at least one row, use that to
            # deduce the types for the columns. If not, assume it is
            # all strings.
            if len(rset['rows']) > 0:
                types = [ type(val) for val in rset['rows'][0] ]
            else:
                types = [ str ] * len(rset['info']['names'])

            # Create the result set and add the rows from the packet.
            rs = ResultSet(names=rset['info']['names'], types=types)
            for row in rset['rows']:
                rs.append_row(row)
            result.append_result(rs)
    return result
Пример #5
0
    def execute(self, provider_id=None):
        """Return information on existing provider(s).

        :param provider_id: None if one wants to list the existing providers
                            or provider's id if one wants information on a
                            provider.
        """
        rset = ResultSet(
            names=('provider_id', 'type', 'username', 'url', 'tenant',
                   'default_image', 'default_flavor', 'extra'),
            types=(str, str, str, str, str, str, str, str)
        )

        if provider_id is None:
            for prv in Provider.providers():
                rset.append_row((
                    prv.provider_id, prv.provider_type, prv.username, prv.url,
                    prv.tenant, prv.default_image, prv.default_flavor,
                    prv.extra
                ))
        else:
            prv = _retrieve_provider(provider_id)
            rset.append_row((
                prv.provider_id, prv.provider_type, prv.username, prv.url,
                prv.tenant, prv.default_image, prv.default_flavor, prv.extra
            ))

        return CommandResult(None, results=rset)
Пример #6
0
    def execute(self, event, locks, *args, **kwargs):
        """Trigger the execution of an event.

        :param event: Event's identification.
        :type event: String
        :param args: Event's non-keyworded arguments.
        :param kwargs: Event's keyworded arguments.

        :return: :class:`CommandResult` instance with UUID of the
                 procedures that were triggered.

        """

        lockable_objects = set()
        for lock in locks.split(","):
            lockable_objects.add(lock.strip())

        rset = ResultSet(names=['uuid'], types=[str])

        # Trigger the event and add the UUID of all procedures queued
        # to the result.
        for proc in _events.trigger(event, lockable_objects, *args, **kwargs):
            rset.append_row([str(proc.uuid)])

        return CommandResult(None, results=rset)
Пример #7
0
    def execute(self):
        """A remote command that returns None.
        """

        rset = ResultSet(names=['foo'], types=[int])
        rset.append_row([2L**32])
        return CommandResult(None, results=rset)
Пример #8
0
def search(provider_id, generic_filters, meta_filters, skip_store):
    """Return information on existing machine(s).
    """
    provider = _retrieve_provider(provider_id)

    if not skip_store:
        if generic_filters or meta_filters:
            raise ConfigurationError(
                "Filters are only supported when the 'skip_store' option "
                "is set.")
        machines = Machine.machines(provider.provider_id)
    else:
        generic_filters, meta_filters = \
           _preprocess_filters(generic_filters, meta_filters)
        manager = provider.get_provider_machine()

        if issubclass(manager, AbstractDatabaseManager):
            meta_filters = {}
            manager = _retrieve_database_manager(provider_id)
        elif issubclass(manager, AbstractMachineManager):
            manager = _retrieve_machine_manager(provider_id)

        machines = manager.search(generic_filters, meta_filters)

    rset = ResultSet(names=('uuid', 'provider_id', 'av_zone', 'addresses'),
                     types=(str, str, str, str))

    for machine in machines:
        row = (str(machine.uuid), machine.provider_id, machine.av_zone,
               machine.addresses)
        rset.append_row(row)
    return CommandResult(None, results=rset)
Пример #9
0
    def execute(self, group_id=None):
        """Return information on existing group(s).

        :param group_id: None if one wants to list the existing groups or
                         group's id if one wants information on a group.
        :return: List with {"group_id" : group_id, "failure_detector": ON/OFF,
                 "description" : description}.
        """

        if group_id is None:
            gids = _server.Group.groups()
        else:
            gids = [group_id]

        _LOGGER.debug("Group IDs: %s", gids)

        # Fetch all the groups before building the result set since an
        # exception can be thrown and there is little point in trying
        # to build a result set before all groups can be fetched.
        groups = [ _retrieve_group(gid) for gid in gids ]

        rset = ResultSet(
            names=('group_id', 'description', 'failure_detector', 'master_uuid'),
            types=(str, str, str, str)
        )

        for group in groups:
            rset.append_row([
                group.group_id,                           # group_id
                group.description,                        # description
                "ACTIVE" if group.status else "INACTIVE", # failure_detector
                group.master,                             # master_uuid
            ])

        return CommandResult(None, results=rset)
Пример #10
0
    def execute(self, module, level):
        """ Set logging level.

        :param module: Module that will have its logging level changed.
        :param level: The logging level that will be set.
        :return: Return True if the logging level is changed. Otherwise,
        False.
        """
        try:
            __import__(module)
            logger = logging.getLogger(module)
            logger.setLevel(_LOGGING_LEVELS[level.upper()])
        except (KeyError, ImportError) as error:
            _LOGGER.debug(error)
            return CommandResult(str(error))
        return CommandResult(None)
Пример #11
0
    def execute(self,
                provider_id,
                generic_filters=None,
                meta_filters=None,
                skip_store=False):
        """Return information on existing machine(s).

        :param provider_id: Provider's Id.
        :param generic_filters: Filter returned machines by some properites
                                but metadata properties.
        :param meta_filters: Filter returned machines by metadata properties.
        :param skip_store: Proceed anyway if there is no information on
                           the machine in the state store. Default is False.
        """
        rset = ResultSet(names=('uuid', 'provider_id', 'av_zone', 'addresses'),
                         types=(str, str, str, str))

        if not skip_store:
            if generic_filters or meta_filters:
                raise _errors.ConfigurationError(
                    "Filters are only supported when the 'skip_store' option "
                    "is set.")
            provider = _retrieve_provider(provider_id)
            for mc in Machine.machines(provider.provider_id):
                rset.append_row(
                    (str(mc.uuid), mc.provider_id, mc.av_zone, mc.addresses))
        else:
            generic_filters, meta_filters = \
                _preprocess_filters(generic_filters, meta_filters)
            manager = _retrieve_manager(provider_id)
            for mc in manager.search_machines(generic_filters, meta_filters):
                rset.append_row(
                    (str(mc.uuid), mc.provider_id, mc.av_zone, mc.addresses))

        return CommandResult(None, results=rset)
Пример #12
0
    def execute(self, ttl):
        """ Set TTL.

        :param ttl: New TTL value.
        :return: Return True if the logging level is changed. Otherwise,
        False.
        """
        try:
            old_ttl = _utils.TTL
            ttl = int(ttl)
            _utils.TTL = ttl
        except (_config.NoOptionError, _config.NoSectionError,
                ValueError) as error:
            _LOGGER.warning("TTL Change Failed (%s).", str(error))
            return CommandResult(str(error))

        _LOGGER.info("TTL has changed %d => %d", old_ttl, _utils.TTL)
        return CommandResult(None)
Пример #13
0
    def test_xmlrpc_encoding(self):
        "Test the XML-RPC encoder and decoder."

        results = [
            CommandResult(None),
        ]

        for result in results:
            self.assertEqual(str(result),
                             str(_xmlrpc._decode(_xmlrpc._encode(result))))
Пример #14
0
    def execute(self, protocol=None):
        """Return a list with all the available Fabric Servers.

        :return: List with existing Fabric Servers.
        :rtype: ["host:port", ...]
        """
        service = _services.ServiceManager()
        rset = ResultSet(names=('host', 'port'), types=(str, int))
        for _, address in service.address(protocol).items():
            rset.append_row(address.split(":"))
        return CommandResult(None, results=rset)
Пример #15
0
    def execute(self, address, timeout=None):
        """Return server's UUID.

        :param address: Server's address.
        :param timeout: Time in seconds after which an error is reported
                        if the UUID is not retrieved.
        :return: UUID.
        """
        rset = ResultSet(names=['uuid'], types=[str])
        rset.append_row([_lookup_uuid(address, timeout)])
        return CommandResult(None, results=rset)
Пример #16
0
    def execute(self):
        """The method returns all the shard mapping definitions.

        :return: A list of shard mapping definitions
                    An Empty List if no shard mapping definition is found.
        """
        rset = ResultSet(
            names=('mapping_id', 'type_name', 'global_group_id'),
            types=(int, str, str),
        )
        for row in ShardMapping.list_shard_mapping_defn():
            rset.append_row(row)
        return CommandResult(None, results=rset)
Пример #17
0
    def execute(self, group_id, server_id=None, status=None, mode=None):
        """Return information on existing server(s) in a group.

        :param group_id: Group's id.
        :param uuid: None if one wants to list the existing servers
                     in a group or server's id if one wants information
                     on a server in a group.
        :server_id type: Servers's UUID or HOST:PORT.
        :param status: Server's status one is searching for.
        :param mode: Server's mode one is searching for.
        :return: Information on servers.
        :rtype: List with [uuid, address, status, mode, weight]
        """
        # Determine the set of servers to iterate through.
        group = _retrieve_group(group_id)
        if server_id is None:
            servers = [server for server in group.servers()]
        else:
            servers = [_retrieve_server(server_id, group_id)]

        # Determine the set of status to check upon.
        if status is None:
            status = _server.MySQLServer.SERVER_STATUS
        else:
            status = [_retrieve_server_status(status)]

        # Determine the set of modes to check upon.
        if mode is None:
            mode = _server.MySQLServer.SERVER_MODE
        else:
            mode = [_retrieve_server_mode(mode)]

        # Create result set.
        rset = ResultSet(
            names=('server_uuid', 'address', 'status', 'mode', 'weight', 'connections'),
            types=(str, str, str, str, float, int),
        )
        for server in servers:
            if server.status in status and server.mode in mode:
                rset.append_row([
                    str(server.uuid),
                    server.address,
                    server.status,
                    server.mode,
                    server.weight,
                    server.connections
                ])

        return CommandResult(None, results=rset)
Пример #18
0
    def execute(self, connector_version=None, patterns=""):
        """Return information about all servers.

        :param connector_version: The connectors version of the data.
        :param patterns: group pattern.
        """
        rset = ResultSet(
            names=('server_uuid', 'group_id', 'host', 'port', 'mode', 'status', 'weight'),
            types=(str, str, str, int, int, int, float)
        )

        for row in _server.MySQLServer.dump_servers(connector_version, patterns):
            rset.append_row(row)

        return CommandResult(None, results=rset)
Пример #19
0
    def execute(self, connector_version=None, patterns=""):
        """Return information about all servers.

        :param connector_version: The connectors version of the data.
        :param patterns: group pattern.
        """
        rset = ResultSet(
            names=('group_id', 'primary', 'secondary', 'spare', 'faulty'),
            types=(str, int, int, int, int)
        )

        for row in _server.MySQLServer.dump_health(connector_version):
            rset.append_row(row)

        return CommandResult(None, results=rset)
Пример #20
0
    def execute(self):
        """Statistics on the Fabric node.

        It returns information on the Fabric node, specifically a list with
        the following fileds: node identification, how long it is running,
        when it was started.
        """
        fabric = FabricNode()
        node_id = fabric.uuid
        node_startup = fabric.startup
        node_uptime = _utils.get_time() - node_startup

        rset = ResultSet(names=('node_id', 'node_uptime', 'node_startup'),
                         types=(str, str, str))
        rset.append_row([node_id, node_uptime, node_startup])

        return CommandResult(None, results=rset)
Пример #21
0
    def execute(self, connector_version=None, patterns=""):
        """Return information about the index for all mappings matching
        any of the patterns provided.

        :param connector_version: The connectors version of the data.
        :param patterns: group pattern.
        """

        rset = ResultSet(
            names=('lower_bound', 'mapping_id', 'shard_id', 'group_id'),
            types=(str, int, int, str),
        )

        for row in Shards.dump_shard_indexes(connector_version, patterns):
            rset.append_row(row)

        return CommandResult(None, results=rset)
Пример #22
0
    def execute(self, connector_version=None, patterns=""):
        """Return information about all shard mappings matching any of the
        provided patterns.

        :param connector_version: The connectors version of the data.
        :param patterns: shard mapping pattern.
        """

        rset = ResultSet(
            names=('mapping_id', 'type_name', 'global_group_id'),
            types=(int, str, str),
        )

        for row in ShardMapping.dump_shard_maps(connector_version, patterns):
            rset.append_row(row)

        return CommandResult(None, results=rset)
Пример #23
0
    def execute(self, connector_version=None, patterns=""):
        """Return information about all tables belonging to mappings
        matching any of the provided patterns.

        :param connector_version: The connectors version of the data.
        :param patterns: shard mapping pattern.
        """

        rset = ResultSet(
            names=('schema_name', 'table_name', 'column_name', 'mapping_id'),
            types=(str, str, str, int),
        )

        for row in ShardMapping.dump_shard_tables(connector_version, patterns):
            rset.append_row(row)

        return CommandResult(None, results=rset)
Пример #24
0
    def execute(self, group_id=None):
        """Statistics on a Group.

        It returns how many promotions and demotions were executed within a
        group. Specifically, a list with the following fields are returned:
        group_id, number of promotions, number of demotions.

        :param group_id: Group one wants to retrieve information on.
        """

        rset = ResultSet(
            names=('group_id', 'call_count', 'call_abort'),
            types=(str, long, long),
        )

        for row in MySQLHandler.group_view(group_id):
            rset.append_row(row)

        return CommandResult(None, results=rset)
Пример #25
0
    def execute(self, procedure_name=None):
        """Statistics on the Fabric node.

        It returns information on procedures that match the %procedure_name%
        pattern. The information is returned is a list in which each member
        of the list is also a list with the following fields: procedure name,
        number of successful calls, number of unsuccessful calls.

        :param procedure_name: Procedure one wants to retrieve information on.
        """

        rset = ResultSet(
            names=('proc_name', 'call_count', 'call_abort'),
            types=(str, long, long),
        )

        for row in MySQLHandler.procedure_view(procedure_name):
            rset.append_row(row)
        return CommandResult(None, results=rset)
Пример #26
0
    def execute(self, event, locks=None, args=None, kwargs=None):
        """Trigger the execution of an event.

        :param event: Event's identification.
        :type event: String
        :param args: Event's non-keyworded arguments.
        :param kwargs: Event's keyworded arguments.

        :return: :class:`CommandResult` instance with UUID of the
                 procedures that were triggered.
        """
        # Prepare lockable objects.
        lockable_objects = None
        if locks:
            lockable_objects = set()
            for lock in locks:
                lockable_objects.add(lock.strip())

        # Prepare list arguments.
        param_args = []
        if args is not None:
            param_args = args

        # Prepare key word arguments.
        param_kwargs = {}
        if kwargs is not None:
            param_kwargs = kv_to_dict(kwargs)

        # Define the result set format.
        rset = ResultSet(names=['uuid'], types=[str])

        _LOGGER.debug("Triggering event (%s) with arguments: %s, %s.", event,
                      param_args, param_kwargs)

        # Trigger the event and add the UUID of all procedures queued
        # to the result.
        procedures = _events.trigger(event, lockable_objects, *param_args,
                                     **param_kwargs)
        for procedure in procedures:
            rset.append_row([str(procedure.uuid)])

        return CommandResult(None, results=rset)
Пример #27
0
    def execute(self, connector_version=None, patterns=""):
        """Return all the sharding information about the tables passed as
        patterns. If no patterns are provided, dump sharding information
        about all tables.

        :param connector_version: The connectors version of the data.
        :param patterns: shard table pattern.
        """

        rset = ResultSet(
            names=('schema_name', 'table_name', 'column_name', 'lower_bound',
                   'shard_id', 'type_name', 'group_id', 'global_group'),
            types=(str, str, str, str, int, str, str, str),
        )

        for row in ShardMapping.dump_sharding_info(connector_version,
                                                   patterns):
            rset.append_row(row)

        return CommandResult(None, results=rset)
Пример #28
0
    def test_remote_command(self):
        """Create a remote command and fire it.
        """
        # Configure a local command.
        from __main__ import xmlrpc_next_port
        params = {
            'protocol.xmlrpc': {
                'address': 'localhost:{0}'.format(xmlrpc_next_port),
                'user': '',
                'password': '',
            },
        }
        config = _config.Config(None, params)
        local_cmd = NewRemoteCommand()
        local_cmd.setup_client(_xmlrpc.MyClient(), None, config)

        # Dispatch request through local command to remote command.
        rs = ResultSet(names=["foo"], types=[str])
        rs.append_row(["executed"])
        self.assertEqual(str(local_cmd.dispatch()),
                         str(CommandResult(None, results=rs)))
        self.assertEqual(local_cmd.execution, None)
Пример #29
0
    def execute(self, proc_uuids):
        """Wait until a set of procedures uniquely identified by their uuids
        finish their execution.

        However, before starting waiting, the function checks if the procedures
        exist. If one of the procedures is not found, the following exception
        is raised :class:`~mysql.fabric.errors.ProcedureError`.

        :param proc_uuids: Iterable with procedures' uuids.
        """
        procs = []
        it_proc_uuids = proc_uuids.split(",")
        for proc_uuid in it_proc_uuids:
            proc_uuid = _uuid.UUID(proc_uuid.strip())
            procedure = _executor.Executor().get_procedure(proc_uuid)
            if not procedure:
                raise _errors.ProcedureError("Procedure (%s) was not found." %
                                             (proc_uuid, ))
            procs.append(procedure)

        for procedure in procs:
            procedure.wait()

        return CommandResult(None)
Пример #30
0
    def test_basic(self):
        result = CommandResult(None)
        self.assertEqual(result.error, None)
        self.assertEqual(len(result.results), 0)

        result.append_result(self.rset)
        self.assertEqual(len(result.results), 1)

        # Check that indexing works and return the result set added.
        self.assertEqual(result.results[0], self.rset)

        # Check that passing something that is not a result set will
        # raise an error.
        self.assertRaises(_errors.CommandResultError, result.append_result, [])

        result = CommandResult("Not working")
        self.assertEqual(result.error, "Not working")

        self.assertRaises(_errors.CommandResultError, result.append_result,
                          self.rset)
Пример #31
0
    def execute(self, proc_uuids=None):
        """Wait until a set of procedures uniquely identified by their uuids
        finish their execution.

        However, before starting waiting, the function checks if the procedures
        exist. If one of the procedures is not found, the following exception
        is raised :class:`~mysql.fabric.errors.ProcedureError`.

        :param proc_uuids: Iterable with procedures' UUIDs.
        """
        if proc_uuids is None:
            raise _errors.ProcedureError(
                "Please, specify which procedure(s) you will be waiting for.")

        procedures = []
        for proc_uuid in proc_uuids:
            proc_uuid = _uuid.UUID(proc_uuid.strip())
            procedure = _executor.Executor().get_procedure(proc_uuid)
            if not procedure:
                raise _errors.ProcedureError("Procedure (%s) was not found." %
                                             (proc_uuid, ))
            procedures.append(procedure)

        command_results = CommandResult(error=None)
        for procedure in procedures:
            command_result = ProcedureCommand.wait_for_procedures([
                procedure,
            ], True)
            if command_result.error is None:
                for result in command_result.results:
                    command_results.append_result(result)
            else:
                result = ResultSet(names=['uuid', 'error'], types=[str, str])
                result.append_row(
                    [str(procedure.uuid),
                     str(command_result.error)])
                command_results.append_result(result)

        return command_results
Пример #32
0
    def execute(self, group_id):
        """Check if any server within a group has failed.

        :param group_id: Group's id.
        """

        group = _server.Group.fetch(group_id)
        if not group:
            raise _errors.GroupError("Group (%s) does not exist." % (group_id, ))

        info = ResultSet(
            names=[
                'uuid', 'is_alive', 'status',
                'is_not_running', 'is_not_configured', 'io_not_running',
                'sql_not_running', 'io_error', 'sql_error', 'gtid_executed'
            ],
            types=[str, bool, str] + [bool] * 4 + [str, str, str]
        )
        issues = ResultSet(names=['issue'], types=[str])

        for server in group.servers():
            alive = False
            is_master = (group.master == server.uuid)
            status = server.status
            why_slave_issues = {}
            # These are used when server is not contactable.
            why_slave_issues = {
                'is_not_running': False,
                'is_not_configured': False,
                'io_not_running': False,
                'sql_not_running': False,
                'io_error': False,
                'sql_error': False,
            }
            try:
                # TODO: CHECK WHETHER WE SHOULD USE IS_ALIVE OR NOT.
                if server.is_alive:
                  server.connect()
                  alive = True
                  if not is_master:
                      slave_issues, why_slave_issues = \
                          _replication.check_slave_issues(server)
                      str_master_uuid = _replication.slave_has_master(server)
                      if (group.master is None or str(group.master) != \
                          str_master_uuid) and not slave_issues:
                          issues.append_row([
                              "Group has master (%s) but server is connected " \
                              "to master (%s)." % \
                              (group.master, str_master_uuid)
                          ])
                  gtid_executed= server.get_gtid_status()[0].GTID_EXECUTED
                else:
                  status = _server.MySQLServer.FAULTY
                  gtid_executed= "UNKNOWN"
                  
            except _errors.DatabaseError:
                status = _server.MySQLServer.FAULTY
                gtid_executed= "UNKNOWN"

            info.append_row([
                server.uuid,
                alive,
                status,
                why_slave_issues['is_not_running'],
                why_slave_issues['is_not_configured'],
                why_slave_issues['io_not_running'],
                why_slave_issues['sql_not_running'],
                why_slave_issues['io_error'],
                why_slave_issues['sql_error'],
                ' '.join(gtid_executed.splitlines()),
            ])

        return CommandResult(None, results=[info, issues])