~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""The code import scheduler XML-RPC API."""

__metaclass__ = type
__all__ = [
    'CodeImportSchedulerAPI',
    ]

from zope.component import getUtility
from zope.interface import implements
from zope.security.proxy import removeSecurityProxy

from canonical.launchpad.webapp import (
    canonical_url,
    LaunchpadXMLRPCView,
    )
from lp.xmlrpc.faults import NoSuchCodeImportJob
from lp.xmlrpc.helpers import return_fault
from lp.code.enums import CodeImportResultStatus
from lp.code.interfaces.codeimportjob import (
    ICodeImportJobSet,
    ICodeImportJobWorkflow,
    )
from lp.code.interfaces.codeimportscheduler import ICodeImportScheduler
from lp.codehosting.codeimport.worker import CodeImportSourceDetails
from lp.services.librarian.interfaces import ILibraryFileAliasSet


class CodeImportSchedulerAPI(LaunchpadXMLRPCView):
    """See `ICodeImportScheduler`."""

    implements(ICodeImportScheduler)

    def getJobForMachine(self, hostname, worker_limit):
        """See `ICodeImportScheduler`."""
        job = getUtility(ICodeImportJobSet).getJobForMachine(
            hostname, worker_limit)
        if job is not None:
            return job.id
        else:
            return 0

    def _getJob(self, job_id):
        job_set = removeSecurityProxy(getUtility(ICodeImportJobSet))
        job = removeSecurityProxy(job_set.getById(job_id))
        if job is None:
            raise NoSuchCodeImportJob(job_id)
        return job

    # Because you can't use a decorated function as the implementation of a
    # method exported over XML-RPC, the implementations just thunk to an
    # implementation wrapped with @return_fault.

    def getImportDataForJobID(self, job_id):
        """See `ICodeImportScheduler`."""
        return self._getImportDataForJobID(job_id)

    def updateHeartbeat(self, job_id, log_tail):
        """See `ICodeImportScheduler`."""
        return self._updateHeartbeat(job_id, log_tail)

    def finishJobID(self, job_id, status_name, log_file_alias_url):
        """See `ICodeImportScheduler`."""
        return self._finishJobID(job_id, status_name, log_file_alias_url)

    @return_fault
    def _getImportDataForJobID(self, job_id):
        job = self._getJob(job_id)
        arguments = CodeImportSourceDetails.fromCodeImport(
            job.code_import).asArguments()
        branch = job.code_import.branch
        branch_url = canonical_url(branch)
        log_file_name = '%s.log' % branch.unique_name[1:].replace('/', '-')
        return (arguments, branch_url, log_file_name)

    @return_fault
    def _updateHeartbeat(self, job_id, log_tail):
        job = self._getJob(job_id)
        workflow = removeSecurityProxy(getUtility(ICodeImportJobWorkflow))
        workflow.updateHeartbeat(job, log_tail)
        return 0

    @return_fault
    def _finishJobID(self, job_id, status_name, log_file_alias_url):
        job = self._getJob(job_id)
        status = CodeImportResultStatus.items[status_name]
        workflow = removeSecurityProxy(getUtility(ICodeImportJobWorkflow))
        if log_file_alias_url:
            library_file_alias_set = getUtility(ILibraryFileAliasSet)
            # XXX This is so so so terrible:
            log_file_alias_id = int(log_file_alias_url.split('/')[-2])
            log_file_alias = library_file_alias_set[log_file_alias_id]
        else:
            log_file_alias = None
        workflow.finishJob(job, status, log_file_alias)