Exemplo n.º 1
0
    def on_put(self, req, resp, name):
        """
        Handles the creation of a new Cluster.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being created.
        :type name: str
        """
        # PUT is idempotent, and since there's no body to this request,
        # there's nothing to conflict with.  The request should always
        # succeed, even if we didn't actually do anything.
        try:
            Cluster.retrieve(name)
            self.logger.info(
                'Creation of already exisiting cluster {0} requested.'.format(
                    name))
        except:
            pass

        cluster = Cluster(status='ok', hostset=[])
        cluster.save(name)
        self.logger.info(
            'Created cluster {0} per request.'.format(name))
        resp.status = falcon.HTTP_201
Exemplo n.º 2
0
def etcd_cluster_add_host(name, address):
    """
    Adds a host address to a cluster with the given name.
    If no such cluster exists, the function raises KeyError.

    Note the function is idempotent: if the host address is
    already in the cluster, no change occurs.

    :param name: Name of a cluster
    :type name: str
    :param address: Host address to add
    :type address: str
    """
    try:
        cluster = Cluster.retrieve(name)
    except:
        raise KeyError

    # FIXME: Need input validation.
    #        - 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.

    if address not in cluster.hostset:
        cluster.hostset.append(address)
        cluster.save(name)
Exemplo n.º 3
0
    def on_get(self, req, resp, name, address):
        """
        Handles GET requests for individual hosts in a Cluster.
        This is a membership test, returning 200 OK if the host
        address is part of the cluster, or else 404 Not Found.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being requested.
        :type name: str
        :param address: The address of the Host being requested.
        :type address: str
        """
        try:
            cluster = Cluster.retrieve(name)
        except:
            resp.status = falcon.HTTP_404
            return

        if address in cluster.hostset:
            resp.status = falcon.HTTP_200
        else:
            resp.status = falcon.HTTP_404
Exemplo n.º 4
0
    def on_get(self, req, resp, name):
        """
        Handles retrieval of an existing Cluster.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being requested.
        :type name: str
        """
        try:
            cluster = Cluster.retrieve(name)
            # key = util.etcd_cluster_key(name)
            # etcd_resp, error = cherrypy.engine.publish('store-get', key)[0]
        except:
            resp.status = falcon.HTTP_404
            return

        if not cluster:
            resp.status = falcon.HTTP_404
            return

        self._calculate_hosts(cluster)
        # Have to set resp.body explicitly to include Hosts.
        resp.body = cluster.to_json_with_hosts()
        resp.status = falcon.HTTP_200
Exemplo n.º 5
0
    def on_put(self, req, resp, name):
        """
        Handles PUT requests for Cluster hosts.
        This replaces the entire host list for a Cluster.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being requested.
        :type name: str
        """
        try:
            req_body = json.loads(req.stream.read().decode())
            old_hosts = set(req_body['old'])  # Ensures no duplicates
            new_hosts = set(req_body['new'])  # Ensures no duplicates
        except (KeyError, TypeError):
            self.logger.info(
                'Bad client PUT request for cluster {0}: {1}'.
                format(name, req_body))
            resp.status = falcon.HTTP_400
            return

        try:
            cluster = Cluster.retrieve(name)
        except:
            resp.status = falcon.HTTP_404
            return

        # old_hosts must match current hosts to accept new_hosts.
        # Note: Order doesn't matter, so etcd's atomic comparison
        #       of the raw values would be too strict.
        if old_hosts != set(cluster.hostset):
            self.logger.info(
                'Conflict setting hosts for cluster {0}'.format(name))
            resp.status = falcon.HTTP_409
            return

        # 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.hostset = list(new_hosts)
        cluster.save(name)
        resp.status = falcon.HTTP_200
Exemplo n.º 6
0
def etcd_cluster_has_host(name, address):
    """
    Checks if a host address belongs to a cluster with the given name.
    If no such cluster exists, the function raises KeyError.

    :param name: Name of a cluster
    :type name: str
    :param address: Host address
    :type address: str
    """
    try:
        cluster = Cluster.retrieve(name)
    except:
        raise KeyError

    return address in cluster.hostset
Exemplo n.º 7
0
    def on_get(self, req, resp, name):
        """
        Handles GET requests for Cluster hosts.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param name: The name of the Cluster being requested.
        :type name: str
        """
        try:
            cluster = Cluster.retrieve(name)
        except:
            resp.status = falcon.HTTP_404
            return

        resp.body = json.dumps(cluster.hostset)
        resp.status = falcon.HTTP_200
Exemplo n.º 8
0
    def on_delete(self, req, resp, address):
        """
        Handles the Deletion of a Host.

        :param req: Request instance that will be passed through.
        :type req: falcon.Request
        :param resp: Response instance that will be passed through.
        :type resp: falcon.Response
        :param address: The address of the Host being requested.
        :type address: str
        """
        resp.body = "{}"
        try:
            Host.delete(address)
            resp.status = falcon.HTTP_200
        except:
            resp.status = falcon.HTTP_404

        # Also remove the host from all clusters.
        # Note: We've done all we need to for the host deletion,
        #       so if an error occurs from here just log it and
        #       return.
        try:
            clusters = Clusters.retrieve()
        except:
            self.logger.warn("Etcd does not have any clusters")
            return
        try:
            for cluster_name in clusters.clusters:
                self.logger.debug("Checking cluster {0}".format(cluster_name))
                cluster = Cluster.retrieve(cluster_name)
                if address in cluster.hostset:
                    self.logger.info("Removing {0} from cluster {1}".format(address, cluster_name))
                    cluster.hostset.remove(address)
                    cluster.save(cluster_name)
                    self.logger.info("{0} has been removed from cluster {1}".format(address, cluster_name))
        except:
            self.logger.warn("Failed to remove {0} from cluster {1}".format(address, cluster_name))