Пример #1
0
def add_cluster_member(message, bus):
    """
    Adds a member to the cluster.

    :param message: jsonrpc message structure.
    :type message: dict
    :param bus: Bus instance.
    :type bus: commissaire_http.bus.Bus
    :returns: A jsonrpc structure.
    :rtype: dict
    """
    try:
        address = message['params']['host']
        name = message['params']['name']
    except KeyError as error:
        return create_jsonrpc_error(
            message, error, JSONRPC_ERRORS['BAD_REQUEST'])

    try:
        host = bus.storage.get_host(address)
        cluster = bus.storage.get_cluster(name)
    except _bus.StorageLookupError as error:
        return create_jsonrpc_error(
            message, error, JSONRPC_ERRORS['NOT_FOUND'])

    if host.address not in cluster.hostset:
        # FIXME: Need more input validation.
        #        - Does the host already belong to another cluster?

        # FIXME: Should guard against races here, since we're fetching
        #        the cluster record and writing it back with some parts
        #        unmodified.  Use either locking or a conditional write
        #        with the etcd 'modifiedIndex'.  Deferring for now.

        if host_suitable_for_cluster(host):
            cluster.hostset.append(host.address)
            bus.storage.save(cluster)

            # Register new host with the cluster's container manager
            # (if applicable), and update its status.
            update_new_cluster_member_status(bus, cluster, host)
        else:
            msg = (
                'Host {} (status: {}) not ready to join cluster '
                '"{}"'.format(host.address, host.status, cluster.name))
            LOGGER.error(msg)
            return create_jsonrpc_error(
                message, msg, JSONRPC_ERRORS['METHOD_NOT_ALLOWED'])

    # Return back the host in a list
    return create_jsonrpc_response(message['id'], [host.address])
Пример #2
0
def update_cluster_members(message, bus):
    """
    Updates the list of members in a cluster.

    :param message: jsonrpc message structure.
    :type message: dict
    :param bus: Bus instance.
    :type bus: commissaire_http.bus.Bus
    :returns: A jsonrpc structure.
    :rtype: dict
    """
    try:
        old_hosts = set(message['params']['old'])  # Ensures no duplicates
        new_hosts = set(message['params']['new'])  # Ensures no duplicates
        LOGGER.debug('old_hosts="{}", new_hosts="{}"'.format(
            old_hosts, new_hosts))
    except Exception as error:
        return return_error(message, error, JSONRPC_ERRORS['BAD_REQUEST'])

    try:
        cluster = bus.request(
            'storage.get',
            params=['Cluster', {
                'name': message['params']['name']
            }, True])
    except Exception as error:
        return return_error(message, error, JSONRPC_ERRORS['NOT_FOUND'])

    if old_hosts != set(cluster['result']['hostset']):
        msg = 'Conflict setting hosts for cluster {0}'.format(
            cluster['result']['name'])
        LOGGER.error(msg)
        return return_error(message, msg, JSONRPC_ERRORS['CONFLICT'])

    # FIXME: Need input validation.  For each new host,
    #        - Does the host exist at /commissaire/hosts/{IP}?
    #        - Does the host already belong to another cluster?

    # FIXME: Should guard against races here, since we're fetching
    #        the cluster record and writing it back with some parts
    #        unmodified.  Use either locking or a conditional write
    #        with the etcd 'modifiedIndex'.  Deferring for now.
    cluster['result']['hostset'] = list(new_hosts)
    cluster = models.Cluster.new(**cluster['result'])
    cluster._validate()
    response = bus.request('storage.save',
                           params=['Cluster', cluster.to_dict()])
    return create_response(message['id'], response['result'])
Пример #3
0
def update_cluster_members(message, bus):
    """
    Updates the list of members in a cluster.

    :param message: jsonrpc message structure.
    :type message: dict
    :param bus: Bus instance.
    :type bus: commissaire_http.bus.Bus
    :returns: A jsonrpc structure.
    :rtype: dict
    """
    try:
        old_hosts = set(message['params']['old'])  # Ensures no duplicates
        new_hosts = set(message['params']['new'])  # Ensures no duplicates
        LOGGER.debug('old_hosts="%s", new_hosts="%s"', old_hosts, new_hosts)
    except Exception as error:
        return create_jsonrpc_error(message, error,
                                    JSONRPC_ERRORS['BAD_REQUEST'])

    try:
        name = message['params']['name']
        cluster = bus.storage.get_cluster(name)
    except _bus.StorageLookupError as error:
        return create_jsonrpc_error(message, error,
                                    JSONRPC_ERRORS['NOT_FOUND'])

    if old_hosts != set(cluster.hostset):
        msg = 'Conflict setting hosts for cluster {}'.format(name)
        LOGGER.error(msg)
        return create_jsonrpc_error(message, msg, JSONRPC_ERRORS['CONFLICT'])

    # FIXME: Need more input validation.  For each new host,
    #        - Does the host already belong to another cluster?

    # Only verify *new* hosts are suitable to add to the cluster.
    # Rejecting existing cluster members would be surprising to users.
    actual_new_hosts = new_hosts.difference(old_hosts)
    LOGGER.debug('Checking status of new hosts (ignoring existing): %s',
                 ', '.join(actual_new_hosts))
    list_of_hosts = bus.storage.get_many(
        [models.Host.new(address=x) for x in actual_new_hosts])
    hosts_not_ready = [
        host.address for host in list_of_hosts
        if not host_suitable_for_cluster(host)
    ]
    if hosts_not_ready:
        msg = 'Hosts not ready to join cluster "{}": {}'.format(
            name, ','.join(hosts_not_ready))
        LOGGER.error(msg)
        return create_jsonrpc_error(message, msg,
                                    JSONRPC_ERRORS['METHOD_NOT_ALLOWED'])

    # FIXME: Should guard against races here, since we're fetching
    #        the cluster record and writing it back with some parts
    #        unmodified.  Use either locking or a conditional write
    #        with the etcd 'modifiedIndex'.  Deferring for now.
    cluster.hostset = list(new_hosts)
    saved_cluster = bus.storage.save(cluster)

    # Register newly added hosts with the cluster's container manager
    # (if applicable), and update their status.
    update_new_cluster_member_status(bus, cluster, *list_of_hosts)

    # XXX Using to_dict() instead of to_dict_safe() to include hostset.
    return create_jsonrpc_response(message['id'], saved_cluster.to_dict())