Esempio n. 1
0
    def on_put(self, req, resp, address):
        """
        Handles the creation of a new 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
        """
        # TODO: Verify input
        try:
            host = self.store.get('/commissaire/hosts/{0}'.format(address))
            resp.status = falcon.HTTP_409
            return
        except etcd.EtcdKeyNotFound:
            data = req.stream.read().decode()
            host_creation = json.loads(data)
            ssh_priv_key = host_creation['ssh_priv_key']
            INVESTIGATE_QUEUE.put((host_creation, ssh_priv_key))
            host_creation['address'] = address
            host_creation['os'] = ''
            host_creation['status'] = 'investigating'
            host_creation['cpus'] = -1
            host_creation['memory'] = -1
            host_creation['space'] = -1
            host_creation['ssh_priv_key'] = ssh_priv_key
            host_creation['last_check'] = datetime.datetime.min.isoformat()
            host = Host(**host_creation)
            new_host = self.store.set('/commissaire/hosts/{0}'.format(address),
                                      host.to_json(secure=True))
            resp.status = falcon.HTTP_201
            req.context['model'] = Host(**json.loads(new_host.value))
Esempio n. 2
0
    def on_put(self, req, resp, address):
        """
        Handles the creation of a new 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
        """
        # TODO: Verify input
        try:
            host = self.store.get('/commissaire/hosts/{0}'.format(address))
            resp.status = falcon.HTTP_409
            return
        except etcd.EtcdKeyNotFound:
            data = req.stream.read().decode()
            host_creation = json.loads(data)
            ssh_priv_key = host_creation['ssh_priv_key']
            INVESTIGATE_QUEUE.put((host_creation, ssh_priv_key))
            host_creation['address'] = address
            host_creation['os'] = ''
            host_creation['status'] = 'investigating'
            host_creation['cpus'] = -1
            host_creation['memory'] = -1
            host_creation['space'] = -1
            host_creation['ssh_priv_key'] = ssh_priv_key
            host_creation['last_check'] = datetime.datetime.min.isoformat()
            host = Host(**host_creation)
            new_host = self.store.set(
                '/commissaire/hosts/{0}'.format(
                    address), host.to_json(secure=True))
            resp.status = falcon.HTTP_201
            req.context['model'] = Host(**json.loads(new_host.value))
Esempio n. 3
0
    def on_put(self, req, resp, address):
        """
        Handles the creation of a new 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
        """
        try:
            # Extract what we need from the input data.
            # Don't treat it as a skeletal host record.
            req_data = req.stream.read()
            req_body = json.loads(req_data.decode())
            ssh_priv_key = req_body['ssh_priv_key']
            # Cluster member is optional.
            cluster_name = req_body.get('cluster', None)
        except (KeyError, ValueError):
            self.logger.info(
                'Bad client PUT request for host {0}: {1}'.
                format(address, req_data))
            resp.status = falcon.HTTP_400
            return

        key = util.etcd_host_key(address)
        try:
            etcd_resp = self.store.get(key)
            self.logger.debug('Etcd Response: {0}'.format(etcd_resp))

            # Check if the request conflicts with the existing host.
            existing_host = Host(**json.loads(etcd_resp.value))
            if existing_host.ssh_priv_key != ssh_priv_key:
                resp.status = falcon.HTTP_409
                return
            if cluster_name:
                try:
                    assert util.etcd_cluster_has_host(
                        self.store, cluster_name, address)
                except (AssertionError, KeyError):
                    resp.status = falcon.HTTP_409
                    return

            # Request is compatible with the existing host, so
            # we're done.  (Not using HTTP_201 since we didn't
            # actually create anything.)
            resp.status = falcon.HTTP_200
            req.context['model'] = existing_host
            return
        except etcd.EtcdKeyNotFound:
            pass

        host_creation = {
            'address': address,
            'ssh_priv_key': ssh_priv_key,
            'os': '',
            'status': 'investigating',
            'cpus': -1,
            'memory': -1,
            'space': -1,
            'last_check': None
        }

        # Verify the cluster exists, if given.  Do it now
        # so we can fail before writing anything to etcd.
        if cluster_name:
            if not util.etcd_cluster_exists(self.store, cluster_name):
                resp.status = falcon.HTTP_409
                return

        host = Host(**host_creation)
        new_host = self.store.set(key, host.to_json(secure=True))
        INVESTIGATE_QUEUE.put((host_creation, ssh_priv_key))

        # Add host to the requested cluster.
        if cluster_name:
            util.etcd_cluster_add_host(self.store, cluster_name, address)

        resp.status = falcon.HTTP_201
        req.context['model'] = Host(**json.loads(new_host.value))
Esempio n. 4
0
def etcd_host_create(address, ssh_priv_key, remote_user, cluster_name=None):
    """
    Creates a new host record in etcd and optionally adds the host to
    the specified cluster.  Returns a (status, host) tuple where status
    is the Falcon HTTP status and host is a Host model instance, which
    may be None if an error occurred.

    This function is idempotent so long as the host parameters agree
    with an existing host record and cluster membership.

    :param address: Host address.
    :type address: str
    :param ssh_priv_key: Host's SSH key, base64-encoded.
    :type ssh_priv_key: str
    :param remote_user: The user to use with SSH.
    :type remote_user: str
    :param cluster_name: Name of the cluster to join, or None
    :type cluster_name: str or None
    :return: (status, host)
    :rtype: tuple
    """
    key = etcd_host_key(address)
    etcd_resp, error = cherrypy.engine.publish('store-get', key)[0]

    if not error:
        # Check if the request conflicts with the existing host.
        existing_host = Host(**json.loads(etcd_resp.value))
        if existing_host.ssh_priv_key != ssh_priv_key:
            return (falcon.HTTP_409, None)
        if cluster_name:
            try:
                assert etcd_cluster_has_host(cluster_name, address)
            except (AssertionError, KeyError):
                return (falcon.HTTP_409, None)

        # Request is compatible with the existing host, so
        # we're done.  (Not using HTTP_201 since we didn't
        # actually create anything.)
        return (falcon.HTTP_200, existing_host)

    host_creation = {
        'address': address,
        'ssh_priv_key': ssh_priv_key,
        'os': '',
        'status': 'investigating',
        'cpus': -1,
        'memory': -1,
        'space': -1,
        'last_check': None,
        'remote_user': remote_user,
    }

    # Verify the cluster exists, if given.  Do it now
    # so we can fail before writing anything to etcd.
    if cluster_name:
        if not etcd_cluster_exists(cluster_name):
            return (falcon.HTTP_409, None)

    host = Host(**host_creation)

    new_host, _ = cherrypy.engine.publish(
        'store-save', key, host.to_json(secure=True))[0]

    # Add host to the requested cluster.
    if cluster_name:
        etcd_cluster_add_host(cluster_name, address)

    INVESTIGATE_QUEUE.put((host_creation, ssh_priv_key, remote_user))

    return (falcon.HTTP_201, Host(**json.loads(new_host.value)))
Esempio n. 5
0
def etcd_host_create(address, ssh_priv_key, remote_user, cluster_name=None):
    """
    Creates a new host record in etcd and optionally adds the host to
    the specified cluster.  Returns a (status, host) tuple where status
    is the Falcon HTTP status and host is a Host model instance, which
    may be None if an error occurred.

    This function is idempotent so long as the host parameters agree
    with an existing host record and cluster membership.

    :param address: Host address.
    :type address: str
    :param ssh_priv_key: Host's SSH key, base64-encoded.
    :type ssh_priv_key: str
    :param remote_user: The user to use with SSH.
    :type remote_user: str
    :param cluster_name: Name of the cluster to join, or None
    :type cluster_name: str or None
    :return: (status, host)
    :rtype: tuple
    """
    store_manager = cherrypy.engine.publish('get-store-manager')[0]

    try:
        # Check if the request conflicts with the existing host.
        existing_host = store_manager.get(Host.new(address=address))
        if existing_host.ssh_priv_key != ssh_priv_key:
            return (falcon.HTTP_409, None)
        if cluster_name:
            try:
                assert etcd_cluster_has_host(cluster_name, address)
            except (AssertionError, KeyError):
                return (falcon.HTTP_409, None)

        # Request is compatible with the existing host, so
        # we're done.  (Not using HTTP_201 since we didn't
        # actually create anything.)
        return (falcon.HTTP_200, existing_host)
    except:
        pass

    host_creation = Host.new(
        address=address,
        ssh_priv_key=ssh_priv_key,
        status='investigating',
        remote_user=remote_user
    ).__dict__

    # Verify the cluster exists, if given.  Do it now
    # so we can fail before writing anything to etcd.
    if cluster_name:
        if not etcd_cluster_exists(cluster_name):
            return (falcon.HTTP_409, None)

    host = Host(**host_creation)

    new_host = store_manager.save(host)

    # Add host to the requested cluster.
    if cluster_name:
        etcd_cluster_add_host(cluster_name, address)

    manager_clone = store_manager.clone()
    job_request = (manager_clone, host_creation, ssh_priv_key, remote_user)
    INVESTIGATE_QUEUE.put(job_request)

    return (falcon.HTTP_201, new_host)
Esempio n. 6
0
    def on_put(self, req, resp, address):
        """
        Handles the creation of a new 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
        """
        # TODO: Verify input
        try:
            host = self.store.get("/commissaire/hosts/{0}".format(address))
            resp.status = falcon.HTTP_409
            return
        except etcd.EtcdKeyNotFound:
            pass

        data = req.stream.read().decode()
        host_creation = json.loads(data)
        ssh_priv_key = host_creation["ssh_priv_key"]
        host_creation["address"] = address
        host_creation["os"] = ""
        host_creation["status"] = "investigating"
        host_creation["cpus"] = -1
        host_creation["memory"] = -1
        host_creation["space"] = -1
        host_creation["last_check"] = None

        # Don't store the cluster name in etcd.
        cluster_name = host_creation.pop("cluster", None)

        # Verify the cluster exists, if given.  Do it now
        # so we can fail before writing anything to etcd.
        if cluster_name:
            # XXX: Based on ClusterSingleHostResource.on_put().
            #      Add a util module to share common operations.
            cluster_key = "/commissaire/clusters/{0}".format(cluster_name)
            try:
                etcd_resp = self.store.get(cluster_key)
                self.logger.info("Request for cluster {0}".format(cluster_name))
                self.logger.debug("{0}".format(etcd_resp))
            except etcd.EtcdKeyNotFound:
                self.logger.info("Request for non-existent cluster {0}.".format(cluster_name))
                resp.status = falcon.HTTP_409
                return
            cluster = Cluster(**json.loads(etcd_resp.value))
            hostset = set(cluster.hostset)
            hostset.add(address)  # Ensures no duplicates
            cluster.hostset = list(hostset)

        host = Host(**host_creation)
        new_host = self.store.set("/commissaire/hosts/{0}".format(address), host.to_json(secure=True))
        INVESTIGATE_QUEUE.put((host_creation, ssh_priv_key))

        # Add host to the requested cluster.
        if cluster_name:
            # 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.
            self.store.set(cluster_key, cluster.to_json(secure=True))

        resp.status = falcon.HTTP_201
        req.context["model"] = Host(**json.loads(new_host.value))
Esempio n. 7
0
def etcd_host_create(address, ssh_priv_key, cluster_name=None):
    """
    Creates a new host record in etcd and optionally adds the host to
    the specified cluster.  Returns a (status, host) tuple where status
    is the Falcon HTTP status and host is a Host model instance, which
    may be None if an error occurred.

    This function is idempotent so long as the host parameters agree
    with an existing host record and cluster membership.

    :param address: Host address.
    :type address: str
    :param ssh_priv_key: Host's SSH key, base64-encoded.
    :type ssh_priv_key: str
    :param cluster_name: Name of the cluster to join, or None
    :type cluster_name: str or None
    :return: (status, host)
    :rtype: tuple
    """
    key = etcd_host_key(address)
    etcd_resp, error = cherrypy.engine.publish('store-get', key)[0]

    if not error:
        # Check if the request conflicts with the existing host.
        existing_host = Host(**json.loads(etcd_resp.value))
        if existing_host.ssh_priv_key != ssh_priv_key:
            return (falcon.HTTP_409, None)
        if cluster_name:
            try:
                assert etcd_cluster_has_host(cluster_name, address)
            except (AssertionError, KeyError):
                return (falcon.HTTP_409, None)

        # Request is compatible with the existing host, so
        # we're done.  (Not using HTTP_201 since we didn't
        # actually create anything.)
        return (falcon.HTTP_200, existing_host)

    host_creation = {
        'address': address,
        'ssh_priv_key': ssh_priv_key,
        'os': '',
        'status': 'investigating',
        'cpus': -1,
        'memory': -1,
        'space': -1,
        'last_check': None
    }

    # Verify the cluster exists, if given.  Do it now
    # so we can fail before writing anything to etcd.
    if cluster_name:
        if not etcd_cluster_exists(cluster_name):
            return (falcon.HTTP_409, None)

    host = Host(**host_creation)

    new_host, _ = cherrypy.engine.publish(
        'store-save', key, host.to_json(secure=True))[0]

    # Add host to the requested cluster.
    if cluster_name:
        etcd_cluster_add_host(cluster_name, address)

    INVESTIGATE_QUEUE.put((host_creation, ssh_priv_key))

    return (falcon.HTTP_201, Host(**json.loads(new_host.value)))