Esempio n. 1
0
    def delete(self, software_rq):
        """
        A ``DELETE`` to this endpoint will delete the requested software tag

        .. http:delete:: /api/v1/software/<str:softwarename> HTTP/1.1

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/software/Autodesk%20Maya HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO_CONTENT

        :statuscode 204: the software tag was deleted or didn't exist
        """
        if isinstance(software_rq, STRING_TYPES):
            software = Software.query.filter_by(software=software_rq).first()
        else:
            software = Software.query.filter_by(id=software_rq).first()

        if not software:
            return jsonify(None), NO_CONTENT

        db.session.delete(software)
        db.session.commit()
        logger.info("Deleted software %s", software.software)

        return jsonify(None), NO_CONTENT
Esempio n. 2
0
    def delete(self, agent_id):
        """
        Delete a single agent

        .. http:delete:: /api/v1/agents/(uuid:agent_id) HTTP/1.1

            **Request (agent exists)**

            .. sourcecode:: http

                DELETE /api/v1/agents/b25ee7eb-9586-439a-b131-f5d022e0d403 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO CONTENT
                Content-Type: application/json

        :statuscode 204: the agent was deleted or did not exist
        """
        agent = Agent.query.filter_by(id=agent_id).first()
        if agent is None:
            return jsonify(None), NO_CONTENT
        else:
            db.session.delete(agent)
            db.session.commit()
            assign_tasks.delay()
            return jsonify(None), NO_CONTENT
Esempio n. 3
0
    def delete(self, pathmap_id):
        """
        A ``DELETE`` to this endpoint will remove the specified pathmap

        .. http:delete:: /api/v1/pathmaps/<int:pathmap_id> HTTP/1.1

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/pathmaps/1 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO_CONTENT

        :statuscode 204: the path map was deleted or did not exist in the first
                         place
        """
        pathmap = PathMap.query.filter_by(id=pathmap_id).first()
        if not pathmap:
            return jsonify(None), NO_CONTENT

        db.session.delete(pathmap)
        db.session.commit()

        logger.info("deleted pathmap id %s", pathmap_id)

        return jsonify(None), NO_CONTENT
Esempio n. 4
0
    def delete(self, agent_id):
        """
        Delete a single agent

        .. http:delete:: /api/v1/agents/(uuid:agent_id) HTTP/1.1

            **Request (agent exists)**

            .. sourcecode:: http

                DELETE /api/v1/agents/b25ee7eb-9586-439a-b131-f5d022e0d403 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO CONTENT
                Content-Type: application/json

        :statuscode 204: the agent was deleted or did not exist
        """
        agent = Agent.query.filter_by(id=agent_id).first()
        if agent is None:
            return jsonify(None), NO_CONTENT
        else:
            db.session.delete(agent)
            db.session.commit()
            assign_tasks.delay()
            return jsonify(None), NO_CONTENT
Esempio n. 5
0
    def delete(self, jobtype_name, software):
        """
        A ``DELETE`` to this endpoint will delete the requested software
        requirement from the specified jobtype, creating a new version of the
        jobtype in the process

        .. http:delete:: /api/v1/jobtypes/[<str:name>|<int:id>]/software_requirements/<int:id> HTTP/1.1

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/jobtypes/TestJobType/software_requirements/1 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO CONTENT

        :statuscode 204:
            the software requirement was deleted or didn't exist
        """
        if isinstance(jobtype_name, STRING_TYPES):
            jobtype = JobType.query.filter_by(name=jobtype_name).first()
        else:
            jobtype = JobType.query.filter_by(id=jobtype_name).first()

        if not jobtype:
            return (jsonify(error="JobType %s not found" % jobtype_name), NOT_FOUND)

        jobtype_version = JobTypeVersion.query.filter_by(jobtype=jobtype).order_by("version desc").first()
        if not jobtype_version:
            return jsonify(error="JobType has no versions"), NOT_FOUND

        new_version = JobTypeVersion()
        for name in JobTypeVersion.types().columns:
            if name not in JobTypeVersion.types().primary_keys:
                setattr(new_version, name, getattr(jobtype_version, name))
        new_version.version += 1
        for old_req in jobtype_version.software_requirements:
            if old_req.software.software != software:
                new_req = JobTypeSoftwareRequirement()
                for name in JobTypeSoftwareRequirement.types().columns:
                    setattr(new_req, name, getattr(old_req, name))
                new_req.jobtype_version = new_version
                db.session.add(new_req)

        db.session.add(new_version)
        db.session.commit()
        logger.info(
            "Deleted software requirement %s for jobtype %s, creating " "new version %s",
            software,
            jobtype.id,
            new_version.version,
        )

        return jsonify(None), NO_CONTENT
Esempio n. 6
0
    def post(self):
        """
        A ``POST`` to this endpoint will create a new job queue.

        .. http:post:: /api/v1/jobqueues/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                POST /api/v1/jobqueues/ HTTP/1.1
                Accept: application/json

                {
                    "name": "Test Queue"
                }


            **Response**

            .. sourcecode:: http

                HTTP/1.1 201 CREATED
                Content-Type: application/json

                {
                    "weight": 10,
                    "jobs": [],
                    "minimum_agents": null,
                    "priority": 5,
                    "name": "Test Queue",
                    "maximum_agents": null,
                    "id": 1,
                    "parent": null,
                    "parent_jobqueue_id": null
                }

        :statuscode 201: a new job queue was created
        :statuscode 400: there was something wrong with the request (such as
                         invalid columns being included)
        :statuscode 409: a job queue with that name already exists
        """
        jobqueue = JobQueue.query.filter_by(name=g.json["name"]).first()
        if jobqueue:
            return (jsonify(error="Job queue %s already exists" %
                            g.json["name"]), CONFLICT)

        jobqueue = JobQueue(**g.json)
        db.session.add(jobqueue)
        db.session.flush()

        jobqueue.fullpath = jobqueue.path()
        db.session.add(jobqueue)
        db.session.commit()

        jobqueue_data = jobqueue.to_dict()
        logger.info("Created job queue %s: %r", jobqueue.name, jobqueue_data)

        return jsonify(jobqueue_data), CREATED
Esempio n. 7
0
    def post(self):
        """
        A ``POST`` to this endpoint will create a new job queue.

        .. http:post:: /api/v1/jobqueues/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                POST /api/v1/jobqueues/ HTTP/1.1
                Accept: application/json

                {
                    "name": "Test Queue"
                }


            **Response**

            .. sourcecode:: http

                HTTP/1.1 201 CREATED
                Content-Type: application/json

                {
                    "weight": 10,
                    "jobs": [],
                    "minimum_agents": null,
                    "priority": 5,
                    "name": "Test Queue",
                    "maximum_agents": null,
                    "id": 1,
                    "parent": null,
                    "parent_jobqueue_id": null
                }

        :statuscode 201: a new job queue was created
        :statuscode 400: there was something wrong with the request (such as
                         invalid columns being included)
        :statuscode 409: a job queue with that name already exists
        """
        jobqueue = JobQueue.query.filter_by(name=g.json["name"]).first()
        if jobqueue:
            return (jsonify(error="Job queue %s already exists" %
                            g.json["name"]), CONFLICT)

        jobqueue = JobQueue(**g.json)
        db.session.add(jobqueue)
        db.session.flush()

        jobqueue.fullpath = jobqueue.path()
        db.session.add(jobqueue)
        db.session.commit()

        jobqueue_data = jobqueue.to_dict()
        logger.info("Created job queue %s: %r", jobqueue.name, jobqueue_data)

        return jsonify(jobqueue_data), CREATED
    def delete(self, jobtype_name, software):
        """
        A ``DELETE`` to this endpoint will delete the requested software
        requirement from the specified jobtype, creating a new version of the
        jobtype in the process

        .. http:delete:: /api/v1/jobtypes/[<str:name>|<int:id>]/software_requirements/<int:id> HTTP/1.1

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/jobtypes/TestJobType/software_requirements/1 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO CONTENT

        :statuscode 204:
            the software requirement was deleted or didn't exist
        """
        if isinstance(jobtype_name, STRING_TYPES):
            jobtype = JobType.query.filter_by(name=jobtype_name).first()
        else:
            jobtype = JobType.query.filter_by(id=jobtype_name).first()

        if not jobtype:
            return (jsonify(error="JobType %s not found" % jobtype_name),
                    NOT_FOUND)

        jobtype_version = JobTypeVersion.query.filter_by(
            jobtype=jobtype).order_by("version desc").first()
        if not jobtype_version:
            return jsonify(error="JobType has no versions"), NOT_FOUND

        new_version = JobTypeVersion()
        for name in JobTypeVersion.types().columns:
            if name not in JobTypeVersion.types().primary_keys:
                setattr(new_version, name, getattr(jobtype_version, name))
        new_version.version += 1
        for old_req in jobtype_version.software_requirements:
            if old_req.software.software != software:
                new_req = JobTypeSoftwareRequirement()
                for name in JobTypeSoftwareRequirement.types().columns:
                    setattr(new_req, name, getattr(old_req, name))
                new_req.jobtype_version = new_version
                db.session.add(new_req)

        db.session.add(new_version)
        db.session.commit()
        logger.info(
            "Deleted software requirement %s for jobtype %s, creating "
            "new version %s", software, jobtype.id, new_version.version)

        return jsonify(None), NO_CONTENT
Esempio n. 9
0
    def get(self, software_rq, version_name):
        """
        A ``GET`` to this endpoint will return the specified version

        .. http:get:: /api/v1/software/<str:softwarename>/versions/<str:version> HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/software/Autodesk%20Maya/versions/2014 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "version": "2013",
                    "id": 1,
                    "rank": 100,
                    "discovery_function_name": null
                }

        :statuscode 200: no error
        :statuscode 404: the requested software tag or version was not found
        """
        if isinstance(software_rq, STRING_TYPES):
            software = Software.query.filter_by(software=software_rq).first()
        else:
            software = Software.query.filter_by(id=software_rq).first()

        if not software:
            return jsonify(error="Requested software not found"), NOT_FOUND

        if isinstance(version_name, STRING_TYPES):
            version = SoftwareVersion.query.filter(
                SoftwareVersion.software==software,
                SoftwareVersion.version==version_name).first()
        else:
            version = SoftwareVersion.query.filter_by(id=version_name).first()

        if not version:
            return jsonify(error="Requested version not found"), NOT_FOUND

        out = {
            "id": version.id,
            "version": version.version,
            "rank": version.rank,
            "discovery_function_name": version.discovery_function_name}
        return jsonify(out), OK
Esempio n. 10
0
    def get(self, job_id, task_id, attempt):
        """
        A ``GET`` to this endpoint will return a list of all known logs that are
        associated with this attempt at running this task

        .. http:get:: /api/v1/jobs/<job_id>/tasks/<task_id>/attempts/<attempt>/logs/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobs/4/tasks/1300/attempts/5/logs/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "agent_id": "3087ada4-290a-45b0-8c1a-21db4cd284fc",
                        "created_on": "2014-09-03T10:58:59.754880",
                        "identifier": "2014-09-03_10-58-59_4_4ee02475335911e4a935c86000cbf5fb.csv"
                    }
                ]

        :statuscode 200: no error
        :statuscode 404: the specified task was not found
        """
        task = Task.query.filter_by(id=task_id, job_id=job_id).first()
        if not task:
            return jsonify(task_id=task_id,
                           job_id=job_id,
                           error="Specified task not found"), NOT_FOUND

        association_objects = TaskTaskLogAssociation.query.filter(
            TaskTaskLogAssociation.task == task,
            TaskTaskLogAssociation.attempt == attempt)

        out = []
        for item in association_objects:
            log = item.log
            out.append({
                "identifier": log.identifier,
                "created_on": log.created_on,
                "agent_id": str(log.agent_id)
            })

        return jsonify(out), OK
Esempio n. 11
0
    def get(self, job_id, task_id, attempt, log_identifier):
        """
        A ``GET`` to this endpoint will return metadata about the specified
        logfile

        .. http:get:: /api/v1/jobs/<job_id>/tasks/<task_id>/attempts/<attempt>/logs/<log_identifier> HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobs/4/tasks/1300/attempts/5/logs/2014-09-03_10-58-59_4_4ee02475335911e4a935c86000cbf5fb.csv HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "id": 147,
                    "identifier": "2014-09-03_10-58-59_4_4ee02475335911e4a935c86000cbf5fb.csv",
                    "created_on": "2014-09-03T10:58:59.754880",
                    "agent_id": "836ce137-6ad4-443f-abb9-94c4465ff87c"
                }

        :statuscode 200: no error
        :statuscode 404: task or logfile not found
        """
        task = Task.query.filter_by(id=task_id, job_id=job_id).first()
        if not task:
            return jsonify(task_id=task_id,
                           job_id=job_id,
                           error="Specified task not found"), NOT_FOUND

        log = TaskLog.query.filter_by(identifier=log_identifier).first()
        if not log:
            return jsonify(task_id=task_id,
                           job_id=job_id,
                           error="Specified log not found"), NOT_FOUND

        association = TaskTaskLogAssociation.query.filter_by(
            task=task, log=log, attempt=attempt).first()
        if not association:
            return jsonify(task_id=task.id,
                           log=log.identifier,
                           error="Specified log not found in task"), NOT_FOUND

        return jsonify(log.to_dict(unpack_relationships=False))
Esempio n. 12
0
    def get(self, software_rq, version_name):
        """
        A ``GET`` to this endpoint will return just the python code for
        detecting whether this software version is installed on an agent.

        .. http:get:: /api/v1/software/[<str:software_name>|<int:software_id>]/versions/<str:version>/code HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/software/Blender/versions/2.72/code HTTP/1.1
                Accept: text/x-python

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: text/x-python

                def blender_2_72_installed()
                    return True

        :statuscode 200:
            no error

        :statuscode 404:
            software or version not found or this software version has no
            discovery code defined
        """
        if isinstance(software_rq, STRING_TYPES):
            software = Software.query.filter_by(software=software_rq).first()
        else:
            software = Software.query.filter_by(id=software_rq).first()

        if not software:
            return jsonify(error="Requested software not found"), NOT_FOUND

        version = SoftwareVersion.query.filter(
            SoftwareVersion.software==software,
            SoftwareVersion.version==version_name).first()

        if not version:
            return jsonify(error="Requested version not found"), NOT_FOUND

        if not version.discovery_code:
            return jsonify(error="Specified software version has no discovery "
                                 "code"), NOT_FOUND

        return version.discovery_code, OK, {"Content-Type": "text/x-python"}
Esempio n. 13
0
    def get(self, job_id, task_id, attempt, log_identifier):
        """
        A ``GET`` to this endpoint will return metadata about the specified
        logfile

        .. http:get:: /api/v1/jobs/<job_id>/tasks/<task_id>/attempts/<attempt>/logs/<log_identifier> HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobs/4/tasks/1300/attempts/5/logs/2014-09-03_10-58-59_4_4ee02475335911e4a935c86000cbf5fb.csv HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "id": 147,
                    "identifier": "2014-09-03_10-58-59_4_4ee02475335911e4a935c86000cbf5fb.csv",
                    "created_on": "2014-09-03T10:58:59.754880",
                    "agent_id": "836ce137-6ad4-443f-abb9-94c4465ff87c"
                }

        :statuscode 200: no error
        :statuscode 404: task or logfile not found
        """
        task = Task.query.filter_by(id=task_id, job_id=job_id).first()
        if not task:
            return jsonify(task_id=task_id, job_id=job_id,
                           error="Specified task not found"), NOT_FOUND

        log = TaskLog.query.filter_by(identifier=log_identifier).first()
        if not log:
            return jsonify(task_id=task_id, job_id=job_id,
                           error="Specified log not found"), NOT_FOUND

        association = TaskTaskLogAssociation.query.filter_by(
            task=task,
            log=log,
            attempt=attempt).first()
        if not association:
            return jsonify(task_id=task.id, log=log.identifier,
                           error="Specified log not found in task"), NOT_FOUND

        return jsonify(log.to_dict(unpack_relationships=False))
Esempio n. 14
0
    def get(self, software_rq):
        """
        A ``GET`` to this endpoint will return the requested software tag

        .. http:get:: /api/v1/software/<str:softwarename> HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/software/Autodesk%20Maya HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "software": "Autodesk Maya",
                    "id": 1,
                    "versions": [
                        {
                            "version": "2013",
                            "id": 1,
                            "rank": 100
                        },
                        {
                            "version": "2014",
                            "id": 2,
                            "rank": 200
                        }
                    ]
                }

        :statuscode 200: no error
        :statuscode 404: the requested software tag was not found
        """
        if isinstance(software_rq, STRING_TYPES):
            software = Software.query.filter_by(software=software_rq).first()
        else:
            software = Software.query.filter_by(id=software_rq).first()

        if not software:
            return jsonify(error="Requested software not found"), NOT_FOUND

        return jsonify(software.to_dict()), OK
Esempio n. 15
0
    def get(self, job_id, task_id, attempt):
        """
        A ``GET`` to this endpoint will return a list of all known logs that are
        associated with this attempt at running this task

        .. http:get:: /api/v1/jobs/<job_id>/tasks/<task_id>/attempts/<attempt>/logs/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobs/4/tasks/1300/attempts/5/logs/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "agent_id": "3087ada4-290a-45b0-8c1a-21db4cd284fc",
                        "created_on": "2014-09-03T10:58:59.754880",
                        "identifier": "2014-09-03_10-58-59_4_4ee02475335911e4a935c86000cbf5fb.csv"
                    }
                ]

        :statuscode 200: no error
        :statuscode 404: the specified task was not found
        """
        task = Task.query.filter_by(id=task_id, job_id=job_id).first()
        if not task:
            return jsonify(task_id=task_id, job_id=job_id,
                           error="Specified task not found"), NOT_FOUND

        association_objects = TaskTaskLogAssociation.query.filter(
            TaskTaskLogAssociation.task == task,
            TaskTaskLogAssociation.attempt == attempt)

        out = []
        for item in association_objects:
            log = item.log
            out.append({"identifier": log.identifier,
                        "created_on": log.created_on,
                        "agent_id": str(log.agent_id)})

        return jsonify(out), OK
Esempio n. 16
0
    def delete(self, agent_id, software_name, version_name):
        """
        A ``DELETE`` to this endpoint will remove the specified software version
        from the list of supported software in this agent

        .. http:delete:: /api/v1/agents/<str:agent_id>/software/<str:software>/versions/<str:version> HTTP/1.1

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/agents/bbf55143-f2b1-4c15-9d41-139bd8057931/software/Blender/versions/2.72 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 NO CONTENT

        :statuscode 204: the software version has been removed from the
                         supported versions on this agent or has not been on the
                         list in the first place
        :statuscode 404: agent not found
        """
        agent = Agent.query.filter_by(id=agent_id).first()
        if agent is None:
            return jsonify(error="Agent %r not found" % agent_id), NOT_FOUND

        software = Software.query.filter_by(software=software_name).first()
        if not software:
            return (jsonify(error="Software %s not found" % software_name),
                    NOT_FOUND)
        version = SoftwareVersion.query.filter_by(
            software=software, version=version_name).first()
        if not version:
            return (jsonify(error="Version %s not found" % version_name),
                    NOT_FOUND)

        if version not in agent.software_versions:
            return jsonify(), NO_CONTENT

        agent.software_versions.remove(version)

        db.session.add(agent)
        db.session.commit()

        return jsonify(), NO_CONTENT
Esempio n. 17
0
    def get(self, tagname=None):
        """
        A ``GET`` to this endpoint will list all agents associated with this
        tag.

        .. http:get:: /api/v1/tags/<str:tagname>/agents/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/tags/interesting/agents/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 201 CREATED
                Content-Type: application/json

                [
                    {
                        "hostname": "agent3",
                        "id": 1,
                        "href": "/api/v1/agents/1
                    }
                ]

        :statuscode 200: the list of agents associated with this tag is returned
        :statuscode 404: the tag specified does not exist
        """
        if isinstance(tagname, STRING_TYPES):
            tag = Tag.query.filter_by(tag=tagname).first()
        else:
            tag = Tag.query.filter_by(id=tagname).first()

        if tag is None:
            return jsonify(error="tag %s not found" % tagname), NOT_FOUND

        out = []
        for agent in tag.agents:
            out.append({
                "id": agent.id,
                "hostname": agent.hostname,
                "href": url_for(".single_agent_api", agent_id=agent.id)})

        return jsonify(out), OK
Esempio n. 18
0
    def delete(self, agent_id, software_name, version_name):
        """
        A ``DELETE`` to this endpoint will remove the specified software version
        from the list of supported software in this agent

        .. http:delete:: /api/v1/agents/<str:agent_id>/software/<str:software>/versions/<str:version> HTTP/1.1

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/agents/bbf55143-f2b1-4c15-9d41-139bd8057931/software/Blender/versions/2.72 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 NO CONTENT

        :statuscode 204: the software version has been removed from the
                         supported versions on this agent or has not been on the
                         list in the first place
        :statuscode 404: agent not found
        """
        agent = Agent.query.filter_by(id=agent_id).first()
        if agent is None:
            return jsonify(error="Agent %r not found" % agent_id), NOT_FOUND

        software = Software.query.filter_by(software=software_name).first()
        if not software:
            return (jsonify(error="Software %s not found" % software_name),
                    NOT_FOUND)
        version = SoftwareVersion.query.filter_by(
            software=software, version=version_name).first()
        if not version:
            return (jsonify(error="Version %s not found" % version_name),
                    NOT_FOUND)

        if version not in agent.software_versions:
            return jsonify(), NO_CONTENT

        agent.software_versions.remove(version)

        db.session.add(agent)
        db.session.commit()

        return jsonify(), NO_CONTENT
Esempio n. 19
0
def schema():
    """
    Returns the basic schema of :class:`.JobQueue`

    .. http:get:: /api/v1/jobqueues/schema HTTP/1.1

        **Request**

        .. sourcecode:: http

            GET /api/v1/jobqueues/schema HTTP/1.1
            Accept: application/json

        **Response**

        .. sourcecode:: http

            HTTP/1.1 200 OK
            Content-Type: application/json

            {
                "id": "INTEGER",
                "name": VARCHAR(255)",
                "minimum_agents": "INTEGER",
                "maximum_agents": "INTEGER",
                "priority": "INTEGER",
                "weight": "INTEGER",
                "parent_jobqueue_id": "INTEGER"
            }

    :statuscode 200: no error
    """
    return jsonify(JobQueue.to_schema()), OK
Esempio n. 20
0
def schema():
    """
    Returns the basic schema of :class:`.Tag`

    .. http:get:: /api/v1/tags/schema/ HTTP/1.1

        **Request**

        .. sourcecode:: http

            GET /api/v1/tags/schema/ HTTP/1.1
            Accept: application/json

        **Response**

        .. sourcecode:: http

            HTTP/1.1 200 OK
            Content-Type: application/json

            {
                "id": "INTEGER",
                "tag": "VARCHAR(64)"
            }

    :statuscode 200: no error
    """
    return jsonify(Tag.to_schema())
Esempio n. 21
0
    def delete(self, jobtype_name):
        """
        A ``DELETE`` to this endpoint will delete the requested jobtype

        .. http:delete:: /api/v1/jobtypes/[<str:name>|<int:id>] HTTP/1.1

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/jobtypes/TestJobType HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO CONTENT

        :statuscode 204:
            the jobtype was deleted or didn't exist
        """
        if isinstance(jobtype_name, STRING_TYPES):
            jobtype = JobType.query.filter(JobType.name == jobtype_name).first()
        else:
            jobtype = JobType.query.filter(JobType.id == jobtype_name).first()

        if jobtype:
            logger.debug("jobtype %s will be deleted", jobtype.name)
            db.session.delete(jobtype)
            db.session.commit()
            logger.info("jobtype %s has been deleted", jobtype.name)

        return jsonify(None), NO_CONTENT
Esempio n. 22
0
    def delete(self, jobtype_name):
        """
        A ``DELETE`` to this endpoint will delete the requested jobtype

        .. http:delete:: /api/v1/jobtypes/[<str:name>|<int:id>] HTTP/1.1

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/jobtypes/TestJobType HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO CONTENT

        :statuscode 204:
            the jobtype was deleted or didn't exist
        """
        if isinstance(jobtype_name, STRING_TYPES):
            jobtype = JobType.query.filter(
                JobType.name == jobtype_name).first()
        else:
            jobtype = JobType.query.filter(JobType.id == jobtype_name).first()

        if jobtype:
            logger.debug("jobtype %s will be deleted", jobtype.name)
            db.session.delete(jobtype)
            db.session.commit()
            logger.info("jobtype %s has been deleted", jobtype.name)

        return jsonify(None), NO_CONTENT
Esempio n. 23
0
def schema():
    """
    Returns the basic schema of :class:`.JobQueue`

    .. http:get:: /api/v1/jobqueues/schema HTTP/1.1

        **Request**

        .. sourcecode:: http

            GET /api/v1/jobqueues/schema HTTP/1.1
            Accept: application/json

        **Response**

        .. sourcecode:: http

            HTTP/1.1 200 OK
            Content-Type: application/json

            {
                "id": "INTEGER",
                "name": VARCHAR(255)",
                "minimum_agents": "INTEGER",
                "maximum_agents": "INTEGER",
                "priority": "INTEGER",
                "weight": "INTEGER",
                "parent_jobqueue_id": "INTEGER"
            }

    :statuscode 200: no error
    """
    return jsonify(JobQueue.to_schema()), OK
Esempio n. 24
0
def schema():
    """
    Returns the basic schema of :class:`.Agent`

    .. http:get:: /api/v1/pathmaps/schema HTTP/1.1

        **Request**

        .. sourcecode:: http

            GET /api/v1/pathmaps/schema HTTP/1.1
            Accept: application/json

        **Response**

        .. sourcecode:: http

            HTTP/1.1 200 OK
            Content-Type: application/json

            {
                "id": "INTEGER",
                "path_linux": "VARCHAR(512)",
                "path_windows": "VARCHAR(512)",
                "path_osx": "VARCHAR(512)",
                "tag": "VARCHAR(64)"
            }

    :statuscode 200: no error
    """
    out = PathMap.to_schema()
    del out["tag_id"]
    out["tag"] = "VARCHAR(%s)" % config.get("max_tag_length")
    return jsonify(out)
Esempio n. 25
0
    def delete(self, software_rq, version_name):
        """
        A ``DELETE`` to this endpoint will delete the requested software version

        .. http:delete:: /api/v1/software/<str:softwarename>/versions/<str:version> HTTP/1.1

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/software/Autodesk%20Maya/versions/2013 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO_CONTENT

        :statuscode 204: the software version was deleted or didn't exist
        :statuscode 404: the software specified does not exist
        """
        if isinstance(software_rq, STRING_TYPES):
            software = Software.query.filter_by(software=software_rq).first()
        else:
            software = Software.query.filter_by(id=software_rq).first()

        if not software:
            return jsonify(error="Requested software not found"), NOT_FOUND

        if isinstance(version_name, STRING_TYPES):
            version = SoftwareVersion.query.filter(
                SoftwareVersion.software==software,
                SoftwareVersion.version==version_name).first()
        else:
            version = SoftwareVersion.query.filter_by(id=version_name).first()

        if not version:
            return jsonify(None), NO_CONTENT

        db.session.delete(version)
        db.session.commit()

        logger.info("deleted software version %s for software %s",
                    version.id, software.software)

        return jsonify(None), NO_CONTENT
Esempio n. 26
0
    def get(self, software_rq):
        """
        A ``GET`` to this endpoint will list all known versions for this software

        .. http:get:: /api/v1/software/<str:softwarename>/versions/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/software/Autodesk%20Maya/versions/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "version": "2013",
                        "id": 1,
                        "rank": 100
                    },
                    {
                        "version": "2014",
                        "id": 2,
                        "rank": 200
                    }
                ]

        :statuscode 200: no error
        :statuscode 404: the requested software tag was not found
        """
        if isinstance(software_rq, STRING_TYPES):
            software = Software.query.filter_by(software=software_rq).first()
        else:
            software = Software.query.filter_by(id=software_rq).first()

        if not software:
            return jsonify(error="Requested software not found"), NOT_FOUND

        out = [{"version": x.version, "id": x.id, "rank": x.rank}
               for x in software.versions]
        return jsonify(out), OK
Esempio n. 27
0
    def get(self):
        """
        A ``GET`` to this endpoint will return a list of all registered path
        maps, with id.
        It can be made with a for_agent query parameter, in which case it will
        return only those path maps that apply to that agent.

        .. http:get:: /api/v1/pathmaps/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/pathmaps/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "id": 1,
                        "path_osx": "/mnt/nfs",
                        "path_windows": "\\\\domains\\cifs_server",
                        "path_linux": "/mnt/nfs"
                    },
                    {
                        "id": 7,
                        "path_osx": "/renderout",
                        "path_windows": "c:\\renderout",
                        "path_linux": "/renderout"
                        "tag": "usual",
                    }
                ]

        :statuscode 200: no error
        """
        query = PathMap.query

        for_agent = get_uuid_argument("for_agent")

        if for_agent:
            query = query.filter(or_(PathMap.tag == None, PathMap.tag.has(Tag.agents.any(Agent.id == for_agent))))

        logger.debug("Query: %s", str(query))

        output = []
        for map in query:
            map_dict = map.to_dict(unpack_relationships=False)
            if map.tag:
                map_dict["tag"] = map.tag.tag
            del map_dict["tag_id"]
            output.append(map_dict)

        return jsonify(output), OK
Esempio n. 28
0
    def get(self, tagname=None):
        """
        A ``GET`` to this endpoint will return the referenced tag, either by
        name or id, including a list of agents and jobs associated with it.

        .. http:get:: /api/v1/tags/<str:tagname> HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/tags/interesting HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "agents": [{
                        "hostname": "agent3",
                        "href": "/api/v1/agents/94522b7e-817b-4358-95da-670b31aad624",
                        "id": 1
                    }],
                    "id": 1,
                    "jobs": [],
                    "tag": "interesting"
                }

        :statuscode 200: no error
        :statuscode 404: tag not found
        """
        if isinstance(tagname, STRING_TYPES):
            tag = Tag.query.filter_by(tag=tagname).first()
        else:
            tag = Tag.query.filter_by(id=tagname).first()

        if tag is None:
            return jsonify(error="tag `%s` not found" % tagname), NOT_FOUND

        tag_dict = tag.to_dict(unpack_relationships=("agents", "jobs"))

        return jsonify(tag_dict), OK
Esempio n. 29
0
    def delete(self, queue_rq):
        """
        A ``DELETE`` to this endpoint will delete the specified job queue

        .. http:delete:: /api/v1/jobqueue/[<str:name>|<int:id>]

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/jobqueues/Test%20Queue HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO_CONTENT

        :statuscode 204: the job queue was deleted or didn't exist
        :statuscode 409: the job queue cannot be deleted because it still
                         contains jobs or child queues
        """
        if isinstance(queue_rq, STRING_TYPES):
            jobqueue = JobQueue.query.filter_by(name=queue_rq).first()
        else:
            jobqueue = JobQueue.query.filter_by(id=queue_rq).first()

        if not jobqueue:
            return jsonify(), NO_CONTENT

        num_sub_queues = JobQueue.query.filter_by(parent=jobqueue).count()
        if num_sub_queues > 0:
            return (jsonify(error="Cannot delete: job queue has child queues"),
                    CONFLICT)

        num_jobs = Job.query.filter_by(queue=jobqueue).count()
        if num_jobs > 0:
            return (jsonify(error="Cannot delete: job queue has jobs assigned"),
                    CONFLICT)

        db.session.delete(jobqueue)
        db.session.commit()
        logger.info("Deleted job queue %s", jobqueue.name)

        return jsonify(), NO_CONTENT
Esempio n. 30
0
    def delete(self, queue_rq):
        """
        A ``DELETE`` to this endpoint will delete the specified job queue

        .. http:delete:: /api/v1/jobqueue/[<str:name>|<int:id>]

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/jobqueues/Test%20Queue HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO_CONTENT

        :statuscode 204: the job queue was deleted or didn't exist
        :statuscode 409: the job queue cannot be deleted because it still
                         contains jobs or child queues
        """
        if isinstance(queue_rq, STRING_TYPES):
            jobqueue = JobQueue.query.filter_by(name=queue_rq).first()
        else:
            jobqueue = JobQueue.query.filter_by(id=queue_rq).first()

        if not jobqueue:
            return jsonify(), NO_CONTENT

        num_sub_queues = JobQueue.query.filter_by(parent=jobqueue).count()
        if num_sub_queues > 0:
            return (jsonify(error="Cannot delete: job queue has child queues"),
                    CONFLICT)

        num_jobs = Job.query.filter_by(queue=jobqueue).count()
        if num_jobs > 0:
            return (jsonify(
                error="Cannot delete: job queue has jobs assigned"), CONFLICT)

        db.session.delete(jobqueue)
        db.session.commit()
        logger.info("Deleted job queue %s", jobqueue.name)

        return jsonify(), NO_CONTENT
Esempio n. 31
0
    def get(self, queue_rq):
        """
        A ``GET`` to this endpoint will return the requested job queue

        .. http:get:: /api/v1/jobqueues/[<str:name>|<int:id>] HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobqueues/Test%20Queue HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "id": 1,
                    "parent": [],
                    "jobs": [],
                    "weight": 10,
                    "parent_jobqueue_id": null,
                    "priority": 5,
                    "minimum_agents": null,
                    "name": "Test Queue",
                    "maximum_agents": null
                }

        :statuscode 200: no error
        :statuscode 404: the requested job queue was not found
        """
        if isinstance(queue_rq, STRING_TYPES):
            jobqueue = JobQueue.query.filter_by(name=queue_rq).first()
        else:
            jobqueue = JobQueue.query.filter_by(id=queue_rq).first()

        if not jobqueue:
            return (jsonify(error="Requested job queue %r not found" %
                            queue_rq), NOT_FOUND)

        return jsonify(jobqueue.to_dict()), OK
Esempio n. 32
0
    def get(self, queue_rq):
        """
        A ``GET`` to this endpoint will return the requested job queue

        .. http:get:: /api/v1/jobqueues/[<str:name>|<int:id>] HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobqueues/Test%20Queue HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "id": 1,
                    "parent": [],
                    "jobs": [],
                    "weight": 10,
                    "parent_jobqueue_id": null,
                    "priority": 5,
                    "minimum_agents": null,
                    "name": "Test Queue",
                    "maximum_agents": null
                }

        :statuscode 200: no error
        :statuscode 404: the requested job queue was not found
        """
        if isinstance(queue_rq, STRING_TYPES):
            jobqueue = JobQueue.query.filter_by(name=queue_rq).first()
        else:
            jobqueue = JobQueue.query.filter_by(id=queue_rq).first()

        if not jobqueue:
            return (jsonify(error="Requested job queue %r not found" % queue_rq),
                    NOT_FOUND)

        return jsonify(jobqueue.to_dict()), OK
Esempio n. 33
0
    def get(self, jobtype_name, version):
        """
        A ``GET`` to this endpoint will return just the python code for this
        version of the specified jobtype.

        .. http:get:: /api/v1/jobtypes/[<str:name>|<int:id>]/versions/<int:version>/code HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobtypes/TestJobType/versions/1/code HTTP/1.1
                Accept: text/x-python

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: text/x-python

                from pyfarm.jobtypes.core.jobtype import JobType

                class TestJobType(JobType):
                    def get_command(self):
                        return "/usr/bin/touch"

                    def get_arguments(self):
                        return [os.path.join(
                            self.assignment_data["job"]["data"]["path"], "%04d" %
                            self.assignment_data["tasks"][0]["frame"])]

        :statuscode 200:
            no error

        :statuscode 404:
            jobtype or version not found
        """
        if isinstance(jobtype_name, STRING_TYPES):
            jt_tuple = db.session.query(JobType, JobTypeVersion).filter(
                JobType.id == JobTypeVersion.jobtype_id,
                JobType.name == jobtype_name,
                JobTypeVersion.version == version).first()
        else:
            jt_tuple = db.session.query(JobType, JobTypeVersion).filter(
                JobType.id == JobTypeVersion.jobtype_id,
                JobType.id == jobtype_name,
                JobTypeVersion.version == version).first()

        if not jt_tuple:
            return (jsonify(error="JobType %s, version %s not found" %
                            (jobtype_name, version)), NOT_FOUND)

        jobtype, jobtype_version = jt_tuple

        return Response(jobtype_version.code, OK, mimetype="text/x-python")
Esempio n. 34
0
    def get(self, version):
        """
        A ``GET`` to this endpoint will return the update package as a zip file
        the specified version

        .. http:get:: /api/v1/agents/updates/<string:version> HTTP/1.1

            **Request**

            .. sourcecode:: http

                PUT /api/v1/agents/updates/1.2.3 HTTP/1.1
                Accept: application/zip

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/zip

                <binary data>

        :statuscode 200: The update file was found and is returned
        :statuscode 301: The update can be found under a different URL
        :statuscode 400: there was something wrong with the request (such as an
                         invalid version number specified or the  mime type not
                         being application/zip)
        """
        if not VERSION_REGEX.match(version):
            return (jsonify(
                error="Version is not an acceptable version number"),
                    BAD_REQUEST)
        filename = "pyfarm-agent-%s.zip" % version

        if UPDATES_WEBDIR:
            return redirect(join(UPDATES_WEBDIR, filename))

        update_file = join(UPDATES_DIR, filename)
        if not isfile(update_file):
            return (jsonify(error="Specified update not found"), NOT_FOUND)

        return send_file(update_file)
Esempio n. 35
0
    def get(self, agent_id):
        """
        A ``GET`` to this endpoint will return a list of all software versions
        available on this agent.

        .. http:get:: /api/v1/agents/<str:agent_id>/software/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/agents/bbf55143-f2b1-4c15-9d41-139bd8057931/software/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "software": "Blender",
                        "version": "2.72"
                    }
                ]

        :statuscode 200: no error
        :statuscode 404: agent not found
        """
        agent = Agent.query.filter_by(id=agent_id).first()
        if agent is None:
            return jsonify(error="Agent %r not found" % agent_id), NOT_FOUND

        out = []
        for version in agent.software_versions:
            software_dict = {
                "software": version.software.software,
                "version": version.version
            }
            out.append(software_dict)
        return jsonify(out), OK
Esempio n. 36
0
    def get(self, version):
        """
        A ``GET`` to this endpoint will return the update package as a zip file
        the specified version

        .. http:get:: /api/v1/agents/updates/<string:version> HTTP/1.1

            **Request**

            .. sourcecode:: http

                PUT /api/v1/agents/updates/1.2.3 HTTP/1.1
                Accept: application/zip

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/zip

                <binary data>

        :statuscode 200: The update file was found and is returned
        :statuscode 301: The update can be found under a different URL
        :statuscode 400: there was something wrong with the request (such as an
                         invalid version number specified or the  mime type not
                         being application/zip)
        """
        if not VERSION_REGEX.match(version):
            return (jsonify(error="Version is not an acceptable version number"),
                    BAD_REQUEST)
        filename = "pyfarm-agent-%s.zip" % version

        if UPDATES_WEBDIR:
            return redirect(join(UPDATES_WEBDIR, filename))

        update_file = join(UPDATES_DIR, filename)
        if not isfile(update_file):
            return (jsonify(error="Specified update not found"), NOT_FOUND)

        return send_file(update_file)
    def get(self, group_id):
        """
        A ``GET`` to this endpoint will return the requested job group

        .. http:get:: /api/v1/jobgroups/<int:id> HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobgroups/2 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "id": 2,
                    "user": "******",
                    "main_jobtype": "Test JobType",
                    "jobs": [],
                    "title": "Test Group"
                }

        :statuscode 200: no error
        :statuscode 404: the requested job group was not found
        """
        jobgroup = JobGroup.query.filter_by(id=group_id).first()

        if not jobgroup:
            return (jsonify(error="Requested job group %s not found" % group_id),
                    NOT_FOUND)

        jobgroup_data = jobgroup.to_dict()
        jobgroup_data.pop("user_id", None)
        jobgroup_data.pop("main_jobtype_id", None)

        return jsonify(jobgroup_data), OK
Esempio n. 38
0
    def delete(self, tagname=None):
        """
        A ``DELETE`` to this endpoint will delete the tag under this URI,
        including all relations to tags or jobs.

        .. http:delete:: /api/v1/tags/<str:tagname> HTTP/1.1

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/tags/interesting HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 201 CREATED
                Content-Type: application/json

                {
                    "id": 1,
                    "tag": "interesting"
                }

        :statuscode 204: the tag was deleted or did not exist in the first place
        """
        if isinstance(tagname, STRING_TYPES):
            tag = Tag.query.filter_by(tag=tagname).first()
        else:
            tag = Tag.query.filter_by(id=tagname).first()

        if tag is None:
            return jsonify(None), NO_CONTENT

        db.session.delete(tag)
        db.session.commit()

        logger.info("deleted tag %s", tag.tag)

        return jsonify(None), NO_CONTENT
Esempio n. 39
0
    def put(self, version):
        """
        A ``PUT`` to this endpoint will upload a new version of pyfarm-agent to
        be used for agent auto-updates.  The update must be a zip file.

        .. http:put:: /api/v1/agents/updates/<string:version> HTTP/1.1

            **Request**

            .. sourcecode:: http

                PUT /api/v1/agents/updates/1.2.3 HTTP/1.1
                Content-Type: application/zip

                <binary data>

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

        :statuscode 201: The update was put in place
        :statuscode 400: there was something wrong with the request (such as an
                         invalid version number specified or the  mime type not
                         being application/zip)
        """
        if request.mimetype != "application/zip":
            return (jsonify(error="Data for agent updates must be "
                            "application/zip"), BAD_REQUEST)
        if not VERSION_REGEX.match(version):
            return (jsonify(
                error="Version is not an acceptable version number"),
                    BAD_REQUEST)

        path = join(UPDATES_DIR, "pyfarm-agent-%s.zip" % version)
        with open(path, "wb+") as zip_file:
            zip_file.write(request.data)

        return "", CREATED
Esempio n. 40
0
    def get(self, pathmap_id):
        """
        A ``GET`` to this endpoint will return a single path map specified by
        pathmap_id

        .. http:get:: /api/v1/pathmaps/<int:pathmap_id> HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/pathmaps/1 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "id": 1,
                    "path_osx": "/mnt/nfs",
                    "path_windows": "\\\\domains\\cifs_server",
                    "path_linux": "/mnt/nfs"
                }

        :statuscode 200: no error
        """
        pathmap = PathMap.query.filter_by(id=pathmap_id).first()

        if not pathmap:
            return jsonify(error="No pathmap with that id"), NOT_FOUND

        out = pathmap.to_dict(unpack_relationships=False)
        if pathmap.tag:
            out["tag"] = pathmap.tag.tag
        del out["tag_id"]

        return jsonify(out), OK
Esempio n. 41
0
    def get(self, agent_id):
        """
        A ``GET`` to this endpoint will return a list of all software versions
        available on this agent.

        .. http:get:: /api/v1/agents/<str:agent_id>/software/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/agents/bbf55143-f2b1-4c15-9d41-139bd8057931/software/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "software": "Blender",
                        "version": "2.72"
                    }
                ]

        :statuscode 200: no error
        :statuscode 404: agent not found
        """
        agent = Agent.query.filter_by(id=agent_id).first()
        if agent is None:
            return jsonify(error="Agent %r not found" % agent_id), NOT_FOUND

        out = []
        for version in agent.software_versions:
            software_dict = {"software": version.software.software,
                             "version": version.version}
            out.append(software_dict)
        return jsonify(out), OK
Esempio n. 42
0
    def get(self, group_id):
        """
        A ``GET`` to this endpoint will return the requested job group

        .. http:get:: /api/v1/jobgroups/<int:id> HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobgroups/2 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                {
                    "id": 2,
                    "user": "******",
                    "main_jobtype": "Test JobType",
                    "jobs": [],
                    "title": "Test Group"
                }

        :statuscode 200: no error
        :statuscode 404: the requested job group was not found
        """
        jobgroup = JobGroup.query.filter_by(id=group_id).first()

        if not jobgroup:
            return (jsonify(error="Requested job group %s not found" % group_id), NOT_FOUND)

        jobgroup_data = jobgroup.to_dict()
        jobgroup_data.pop("user_id", None)
        jobgroup_data.pop("main_jobtype_id", None)

        return jsonify(jobgroup_data), OK
Esempio n. 43
0
    def get(self, jobtype_name):
        """
        A ``GET`` to this endpoint will return a sorted list of of all known
        versions of the specified jobtype.

        .. http:get:: /api/v1/jobtypes/[<str:name>|<int:id>]/versions/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobtypes/TestJobType/versions/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [1, 2]

        :statuscode 200:
            no error

        :statuscode 404:
            jobtype not found
        """
        if isinstance(jobtype_name, STRING_TYPES):
            jobtype = JobType.query.filter(
                JobType.name == jobtype_name).first()
        else:
            jobtype = JobType.query.filter(JobType.id == jobtype_name).first()

        if not jobtype:
            return jsonify(error="jobtype not found"), NOT_FOUND

        out = [x.version for x in jobtype.versions]

        return jsonify(sorted(out)), OK
Esempio n. 44
0
def login_page():
    """display and process the login for or action"""
    if request.method == "POST" and request.content_type == "application/json":
        user = User.get(request.json["username"])

        if user and user.check_password(request.json["password"]):
            login_user(user, remember=True)
            return jsonify(None)

        return jsonify(None), UNAUTHORIZED

    form = LoginForm(request.form)
    if request.method == "POST" and form.validate():
        login_user(form.dbuser, remember=True)
        return redirect(request.args.get("next") or "/")

    if request.content_type == "application/json":
        abort(BAD_REQUEST)

    return render_template("pyfarm/login.html", form=form,
                           next=request.args.get("next") or "/")
Esempio n. 45
0
    def get(self, jobtype_name):
        """
        A ``GET`` to this endpoint will return a sorted list of of all known
        versions of the specified jobtype.

        .. http:get:: /api/v1/jobtypes/[<str:name>|<int:id>]/versions/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobtypes/TestJobType/versions/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [1, 2]

        :statuscode 200:
            no error

        :statuscode 404:
            jobtype not found
        """
        if isinstance(jobtype_name, STRING_TYPES):
            jobtype = JobType.query.filter(JobType.name == jobtype_name).first()
        else:
            jobtype = JobType.query.filter(JobType.id == jobtype_name).first()

        if not jobtype:
            return jsonify(error="jobtype not found"), NOT_FOUND

        out = [x.version for x in jobtype.versions]

        return jsonify(sorted(out)), OK
Esempio n. 46
0
    def put(self, version):
        """
        A ``PUT`` to this endpoint will upload a new version of pyfarm-agent to
        be used for agent auto-updates.  The update must be a zip file.

        .. http:put:: /api/v1/agents/updates/<string:version> HTTP/1.1

            **Request**

            .. sourcecode:: http

                PUT /api/v1/agents/updates/1.2.3 HTTP/1.1
                Content-Type: application/zip

                <binary data>

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

        :statuscode 201: The update was put in place
        :statuscode 400: there was something wrong with the request (such as an
                         invalid version number specified or the  mime type not
                         being application/zip)
        """
        if request.mimetype != "application/zip":
            return (jsonify(error="Data for agent updates must be "
                                  "application/zip"), BAD_REQUEST)
        if not VERSION_REGEX.match(version):
            return (jsonify(error="Version is not an acceptable version number"),
                    BAD_REQUEST)

        path = join(UPDATES_DIR, "pyfarm-agent-%s.zip" % version)
        with open(path, "wb+") as zip_file:
            zip_file.write(request.data)

        return "", CREATED
Esempio n. 47
0
def login_page():
    """display and process the login for or action"""
    if request.method == "POST" and request.content_type == "application/json":
        user = User.get(request.json["username"])

        if user and user.check_password(request.json["password"]):
            login_user(user, remember=True)
            return jsonify(None)

        return jsonify(None), UNAUTHORIZED

    form = LoginForm(request.form)
    if request.method == "POST" and form.validate():
        login_user(form.dbuser, remember=True)
        return redirect(request.args.get("next") or "/")

    if request.content_type == "application/json":
        abort(BAD_REQUEST)

    return render_template("pyfarm/login.html",
                           form=form,
                           next=request.args.get("next") or "/")
    def delete(self, group_id):
        """
        A ``DELETE`` to this endpoint will delete the specified job group

        .. http:delete:: /api/v1/jobgroup/<int:id>

            **Request**

            .. sourcecode:: http

                DELETE /api/v1/jobgroups/1 HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 204 NO_CONTENT

        :statuscode 204: the job group was deleted or didn't exist
        :statuscode 409: the job group cannot be deleted because it still
                         contains jobs
        """
        jobgroup = JobGroup.query.filter_by(id=group_id).first()

        if not jobgroup:
            return jsonify(), NO_CONTENT

        num_jobs = Job.query.filter_by(group=jobgroup).count()
        if num_jobs > 0:
            return (jsonify(error="Cannot delete: job group has jobs assigned"),
                    CONFLICT)

        db.session.delete(jobgroup)
        db.session.commit()
        logger.info("Deleted job group %s", jobgroup.title)

        return jsonify(), NO_CONTENT
Esempio n. 49
0
    def get(self):
        """
        A ``GET`` to this endpoint will return a list of known job queues.

        .. http:get:: /api/v1/jobqueues/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobqueues/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "priority": 5,
                        "weight": 10,
                        "parent_jobqueue_id": null,
                        "name": "Test Queue",
                        "minimum_agents": null,
                        "id": 1,
                        "maximum_agents": null
                    },
                    {
                        "priority": 5,
                        "weight": 10,
                        "parent_jobqueue_id": null,
                        "name": "Test Queue 2",
                        "minimum_agents": null,
                        "id": 2,
                        "maximum_agents": null
                    }
                ]

        :statuscode 200: no error
        """
        out = []
        for jobqueue in JobQueue.query:
            out.append(jobqueue.to_dict(unpack_relationships=False))

        return jsonify(out), OK
Esempio n. 50
0
    def get(self):
        """
        A ``GET`` to this endpoint will return a list of known tags, with id.
        Associated agents and jobs are included for every tag

        :rtype : object
        .. http:get:: /api/v1/tags/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/tags/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "agents": [
                            1
                        ],
                        "jobs": [],
                        "id": 1,
                        "tag": "interesting"
                    },
                    {
                        "agents": [],
                        "jobs": [],
                        "id": 2,
                        "tag": "boring"
                    }
                ]

        :statuscode 200: no error
        """
        out = []

        for tag in Tag.query.all():
            out.append(tag.to_dict(unpack_relationships=("agents", "jobs")))

        return jsonify(out), OK
    def get(self):
        """
        A ``GET`` to this endpoint will return a list of known job groups.

        .. http:get:: /api/v1/jobgroups/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobgroups/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "id": 2,
                        "user": "******",
                        "main_jobtype": "Test JobType",
                        "title": "Test Group"
                    }
                ]

        :statuscode 200: no error
        """
        out = []
        for jobgroup in JobGroup.query:
            jobgroup_data = jobgroup.to_dict(
                unpack_relationships=["user", "main_jobtype"])
            jobgroup_data.pop("user_id", None)
            jobgroup_data.pop("main_jobtype_id", None)
            out.append(jobgroup_data)

        return jsonify(out), OK
def schema():
    """
    Returns the basic schema of :class:`.JobGroup`

    .. http:get:: /api/v1/jobgroups/schema HTTP/1.1

        **Request**

        .. sourcecode:: http

            GET /api/v1/jobgroups/schema HTTP/1.1
            Accept: application/json

        **Response**

        .. sourcecode:: http

            HTTP/1.1 200 OK
            Content-Type: application/json

            {
                "main_jobtype": "VARCHAR(64)",
                "title": "VARCHAR(255)",
                "user": "******",
                "id": "INTEGER"
            }

    :statuscode 200: no error
    """
    schema_dict = JobGroup.to_schema()

    # In the database, we are storing the user by id, but over the wire, we are
    # using the username to identify the user instead.
    schema_dict["user"] = "******" % config.get("max_username_length")
    del schema_dict["user_id"]
    schema_dict["main_jobtype"] = \
        "VARCHAR(%s)" % config.get("job_type_max_name_length")
    del schema_dict["main_jobtype_id"]
    return jsonify(schema_dict), OK
Esempio n. 53
0
def schema():
    """
    Returns the basic schema of :class:`.JobType`

    .. http:get:: /api/v1/jobtypes/schema HTTP/1.1

        **Request**

        .. sourcecode:: http

            GET /api/v1/jobtypes/schema HTTP/1.1
            Accept: application/json

        **Response**

        .. sourcecode:: http

            HTTP/1.1 200 OK
            Content-Type: application/json

            {
                "batch_contiguous": "BOOLEAN",
                "classname": "VARCHAR(64)",
                "code": "TEXT",
                "description": "TEXT",
                "id": "INTEGER",
                "version": "INTEGER",
                "max_batch": "INTEGER",
                "no_automatic_start_time": "INTEGER",
                "name": "VARCHAR(64)"
            }

    :statuscode 200:
        no error
    """
    schema_dict = JobTypeVersion.to_schema()
    schema_dict.update(JobType.to_schema())
    return jsonify(schema_dict), OK
Esempio n. 54
0
def schema():
    """
    Returns the basic schema of :class:`.Agent`

    .. http:get:: /api/v1/agents/schema HTTP/1.1

        **Request**

        .. sourcecode:: http

            GET /api/v1/agents/schema HTTP/1.1
            Accept: application/json

        **Response**

        .. sourcecode:: http

            HTTP/1.1 200 OK
            Content-Type: application/json

            {
                "ram": "INTEGER",
                "free_ram": "INTEGER",
                "time_offset": "INTEGER",
                "use_address": "INTEGER",
                "hostname": "VARCHAR(255)",
                "cpus": "INTEGER",
                "port": "INTEGER",
                "state": "INTEGER",
                "ram_allocation": "FLOAT",
                "cpu_allocation": "FLOAT",
                "id": "UUIDType",
                "remote_ip": "IPv4Address"
            }

    :statuscode 200: no error
    """
    return jsonify(Agent.to_schema())
Esempio n. 55
0
    def get(self):
        """
        A ``GET`` to this endpoint will return a list of registered jobtypes.

        .. http:get:: /api/v1/jobtypes/ HTTP/1.1

            **Request**

            .. sourcecode:: http

                GET /api/v1/jobtypes/ HTTP/1.1
                Accept: application/json

            **Response**

            .. sourcecode:: http

                HTTP/1.1 200 OK
                Content-Type: application/json

                [
                    {
                        "id": 1,
                        "name": "TestJobType"
                    }
                ]

        :statuscode 200:
            no error
        """
        out = []
        q = db.session.query(JobType.id, JobType.name)

        for id, name in q:
            out.append({"id": id, "name": name})

        return jsonify(out), OK
Esempio n. 56
0
    def put(self, job_id, task_id, attempt, log_identifier):
        """
        A ``PUT`` to this endpoint will upload the request's body as the
        specified logfile

        .. http:put:: /api/v1/jobs/<job_id>/tasks/<task_id>/attempts/<attempt>/logs/<log_identifier>/logfile HTTP/1.1

            **Request**

            .. sourcecode:: http

                PUT /api/v1/jobs/4/tasks/1300/attempts/5/logs/2014-09-03_10-58-59_4_4ee02475335911e4a935c86000cbf5fb.csv/logfile HTTP/1.1

                <content of the logfile>

            **Response**

            .. sourcecode:: http

                HTTP/1.1 201 CREATED

        :statuscode 201: lofile was uploaded
        :statuscode 400: the specified logfile identifier is not acceptable
        :statuscode 404: task or logfile not found
        """
        task = Task.query.filter_by(id=task_id, job_id=job_id).first()
        if not task:
            return jsonify(task_id=task_id,
                           log=log_identifier,
                           error="Specified task not found"), NOT_FOUND

        log = TaskLog.query.filter_by(identifier=log_identifier).first()
        if not log:
            return jsonify(task_id=task_id,
                           log=log_identifier,
                           error="Specified log not found"), NOT_FOUND

        association = TaskTaskLogAssociation.query.filter_by(
            task=task, log=log, attempt=attempt).first()
        if not association:
            return jsonify(task_id=task_id,
                           log=log.identifier,
                           error="Specified log not found in task"), NOT_FOUND

        path = realpath(join(LOGFILES_DIR, log_identifier))
        if not realpath(path).startswith(LOGFILES_DIR):
            return jsonify(error="Identifier is not acceptable"), BAD_REQUEST

        logger.info("Writing task log file for task %s, attempt %s to path %s",
                    task_id, attempt, path)

        try:
            with open(path, "wb+") as log_file:
                log_file.write(request.data)
        except (IOError, OSError) as e:
            logger.error("Could not write task log file: %s (%s)", e.errno,
                         e.strerror)
            return (jsonify(error="Could not write file %s to disk: %s" %
                            (path, e)), INTERNAL_SERVER_ERROR)

        return "", CREATED