Example #1
0
 def abort(self, user: Optional[str] = None) -> Tuple[bool, str]:
     '''Attempts to abort this task.
     Returns a pair of a boolean and a message string.
     The boolean is True if this task was cancelled or marked for abortion
     and False if this task cannot be aborted.
     '''
     whoAborted = ' by user%s at %s' % (
         '' if user is None else f' "{user}"', formatTime(getTime())
         )
     if self.isRunning():
         if 'abort' in self._properties:
             return False, 'was already being aborted'
         else:
             self._properties['abort'] = 'true'
             self._properties['summary'] = 'aborted' + whoAborted
             self._notify()
             return True, 'will be aborted shortly'
     elif self.isWaiting():
         self.cancelled('cancelled' + whoAborted)
         return True, 'has been aborted'
     elif self.isWaitingForInspection():
         self.__setState(
             ResultCode.ERROR, 'done',
             'postponed inspection cancelled' + whoAborted
             )
         return True, 'had its postponed inspection cancelled'
     else:
         return False, 'was not waiting or running'
Example #2
0
 def presentFeed(self, proc: Processor, ccURL: str) -> XMLContent:
     projectName = proc.project.name
     yield atom.title[f'{projectName} SoftFab - Recent Jobs']
     yield atom.subtitle[
         f'The most recent jobs running in the {projectName} SoftFab']
     yield atom.id[f'{rootURL}jobs']
     yield atom.updated[self.presentTime(getTime())]
     yield atom.generator(uri=HOMEPAGE, version=VERSION)['SoftFab']
     # TODO: Akregator for KDE4 won't show the icon, no matter what I try.
     #       Might be related to Konqueror often losing the icon.
     yield atom.icon[ccURL + 'styles/SoftFabIcon.png']
     #yield atom.link(
     #rel = 'shortcut icon',
     #href = ccURL + 'styles/SoftFabIcon.png',
     #type = 'image/png',
     #)
     # Link to the Control Center.
     yield atom.link(
         rel='alternate',
         href=ccURL + 'Home',
         type='text/html',
     )
     # Link to the feed itself.
     yield atom.link(
         rel='self',
         href=ccURL + pageURL(self.name, proc.args),
         type='application/atom+xml',
     )
     for job, jobTable in zip(proc.jobs, proc.tables):
         yield atom.entry[self.presentJob(proc, ccURL, job, jobTable)]
Example #3
0
    def __init__(self,
                 properties: Mapping[str, str],
                 resTypeDB: ResTypeDB,
                 taskRunDB: TaskRunDB,
                 token: Optional[Token]
                 ):
        # COMPAT 2.16: Rename 'paused' to 'suspended'.
        if 'paused' in properties:
            properties = dict(properties, suspended=properties['paused'])
            del properties['paused']

        if token is not None:
            assert token.role is TokenRole.RESOURCE, token
            assert token.getParam('resourceId') == properties['id'], token
        self.__token = token

        super().__init__(properties, resTypeDB)
        self.__taskRunDB = taskRunDB
        self._properties.setdefault('description', '')
        self._properties.setdefault('status', ConnectionStatus.NEW)
        self.__data: Optional[TaskRunnerData] = None
        self.__hasBeenInSync = False
        self.__executionObserver = ExecutionObserver(
            self, self.__shouldBeExecuting
            )
        self.__lastSyncTime = getTime()
        self.__markLostCall = None
        if self._properties['status'] is ConnectionStatus.CONNECTED:
            self.__startLostCallback()
Example #4
0
    def newJob(self,
               configId: Optional[str],
               target: Optional[str],
               owner: Optional[str],
               comment: str,
               jobParams: Mapping[str, str],
               runners: Iterable[str]
               ) -> Job:
        # TODO: Is validation needed?
        properties: Dict[str, XMLAttributeValue] = dict(
            jobId = createUniqueId(),
            timestamp = getTime(),
            )
        if configId is not None:
            properties['configId'] = configId
        if target is not None:
            properties['target'] = target
        if owner is not None:
            properties['owner'] = owner

        job = Job(properties, self)
        job.comment = comment
        # pylint: disable=protected-access
        job._params.update(jobParams)
        job._setRunners(runners)
        return job
Example #5
0
 def render(self, request: IRequest) -> bytes:
     # File expires a long time from now.
     # RFC-2616 section 14.21: "HTTP/1.1 servers SHOULD NOT send Expires
     # dates more than one year in the future."
     request.setHeader('expires',
                       datetimeToString(getTime() + 365 * secondsPerDay))
     return super().render(request)
Example #6
0
 def getDuration(self) -> Optional[int]:
     startTime = self.startTime
     if startTime is None:
         return None
     stopTime = self.stopTime
     if stopTime is None:
         return getTime() - startTime
     return stopTime - startTime
Example #7
0
 def setSuspend(self, suspended: bool, user: str) -> None:
     """Sets the (new) suspend state on request of `user`.
     """
     if self._properties['suspended'] != suspended:
         self._properties['suspended'] = suspended
         self._properties['changedtime'] = getTime()
         self._properties['changeduser'] = user
         self._notify()
Example #8
0
 def getConnectionStatus(self) -> ConnectionStatus:
     savedStatus = cast(ConnectionStatus, self._properties['status'])
     if savedStatus is not ConnectionStatus.CONNECTED:
         return savedStatus
     sinceLastSync = getTime() - self.__lastSyncTime
     if sinceLastSync > self.getLostTimeout():
         return ConnectionStatus.LOST
     elif sinceLastSync > self.getWarnTimeout():
         return ConnectionStatus.WARNING
     elif self.__hasBeenInSync:
         return ConnectionStatus.CONNECTED
     else:
         return ConnectionStatus.UNKNOWN
Example #9
0
    def assign(self, taskRunner: TaskRunner) -> Optional['TaskRun']:
        assert self.isWaiting()

        resources = self.__reserveResources(None)
        if resources is None:
            return None

        self.__reserved = {ref: res.getId() for ref, res in resources.items()}
        self._properties['state'] = 'running'
        self._properties['starttime'] = getTime()
        self._properties['runner'] = taskRunner.getId()
        #self._properties['runId'] += 1
        self._notify()
        return self
Example #10
0
 def trigger(self, scheduledMinute: int = 0) -> None:
     currentSecs = getTime()
     currentMinute = currentSecs // 60
     # If the call comes just before the minute boundary, trust the
     # scheduled minute instead of the current minute.
     minute = max(currentMinute, scheduledMinute)
     try:
         self.__triggerSchedules(minute * 60)
     finally:
         # Register callback at the next minute boundary.
         self.__reactor.callLater(
             (minute + 1) * 60 - currentSecs,
             self.trigger,
             minute + 1
             )
Example #11
0
    def __init__(self, jobDB: JobDB) -> None:
        super().__init__()
        # Determine minimum and maximum job time.
        createTimes = [ job.getCreateTime() for job in jobDB ]
        if createTimes:
            self.__minTime = min(createTimes)
            self.__maxTime = max(createTimes)
        else:
            now = getTime()
            self.__minTime = now
            self.__maxTime = now
        self.__minYear = localtime(self.__minTime)[0]
        self.__maxYear = localtime(self.__maxTime)[0]

        # Register for updates.
        jobDB.addObserver(self)
Example #12
0
 def getLeadTime(self) -> int:
     '''Gets the number of seconds elapsed between the creation of this job
     and its stop time. If the job is not stopped yet, the number of seconds
     elapsed until now is returned.
     '''
     leadTime = self.__leadTime
     if leadTime is not None:
         return leadTime
     queueTime = self.getCreateTime()
     if queueTime is None:
         return None
     stopTime = self.stopTime
     if stopTime is None:
         return getTime() - queueTime
     else:
         leadTime = stopTime - queueTime
         self.__leadTime = leadTime
         return leadTime
Example #13
0
 def sync(self, jobDB: JobDB, data: TaskRunnerData) -> bool:
     '''Synchronise database with data reported by the Task Runner.
     The sync time will be remembered, but not stored in the database.
     Returns True iff the run that the Task Runner reports should be aborted,
     for example because the user requested it or because it is not running
     the run the Control Center thinks it should be running.
     '''
     if data != self.__data or \
             self._properties['status'] is not ConnectionStatus.CONNECTED:
         self.__data = data
         self._properties['status'] = ConnectionStatus.CONNECTED
         self._notify()
     self.__lastSyncTime = getTime()
     self.__hasBeenInSync = True
     self.__cancelLostCallback()
     self.__startLostCallback()
     return self.__enforceSync(
         self.__executionObserver, lambda: data.getExecutionRunId(jobDB)
         )
Example #14
0
    def __addToQueue(self, schedule: 'Scheduled') -> None:
        # Have the jobs from the last run finished?
        jobDB = self.jobDB
        unfinishedJobIds = set()
        for jobId in schedule.getLastJobs():
            job = jobDB.get(jobId)
            if job is not None and not job.isExecutionFinished():
                unfinishedJobIds.add(jobId)
        if unfinishedJobIds:
            self.__runningJobs[schedule.getId()] = unfinishedJobIds
        else:
            schedule._jobsFinished() # pylint: disable=protected-access

        if not schedule.isBlocked():
            self.__heap.add(schedule)
            # If the new schedule should start right away, trigger it.
            # Doing this call via the reactor makes sure that no schedules
            # are instantiated on upgrade.
            self.__reactor.callLater(0, self.__triggerSchedules, getTime())
Example #15
0
 def presentHeader(self, **kwargs: object) -> XMLContent:
     proc = cast(ProcT, kwargs['proc'])
     ccURL = cast(str, kwargs['ccURL'])
     userName = proc.user.name
     return xhtml.div(class_='titlebar')[
         xhtml.div(class_='title')[self.__title(proc)],
         xhtml.div(class_='info')[xhtml.a(
             href=ccURL +
             self.loginURL(**kwargs))['log in'] if userName is None else
                                  (createUserDetailsLink(userName).present(
                                      **kwargs), ' \u2013 ',
                                   xhtml.a(href=ccURL +
                                           logoutURL(proc.req))['log out']),
                                  xhtml.br,
                                  formatTime(getTime())],
         xhtml.div(
             class_='logo'
         )[xhtml.a(href=ccURL +
                   'About', title=f'SoftFab {VERSION}')[_logoIcon.present(
                       **kwargs)]]]
Example #16
0
    def create(cls,
               role: TokenRole,
               params: Mapping[str, str],
               expireSecs: Optional[int] = None) -> 'Token':
        """Creates a new token.

        Before the new token can be used, a password must be set.

        @param expireSecs: Seconds before the token expires,
            or L{None} for tokens that never expire.
        """

        now = getTime()
        token = cls(
            dict(
                id=createUniqueId(),
                createtime=now,
                expiretime=None if expireSecs is None else now + expireSecs,
                role=role,
            ))
        token.__params = dict(params)  # pylint: disable=protected-access
        return token
Example #17
0
 def __getitem__(self, key: str) -> object:
     if key == 'lastSync':
         if self.__hasBeenInSync:
             return getTime() - self.__lastSyncTime
         else:
             return None
     elif key == 'version':
         # Note: This field is intended to sort Task Runners by version.
         #       Use the supportsFeature() methods for checking what a
         #       particular Task Runner can do.
         data = self.__data
         if data is None:
             return (0, 0, 0)
         else:
             return data.version
     elif key in ('host', 'runnerVersion'):
         data = self.__data
         if data is None:
             return '?'
         else:
             return data[key]
     else:
         return super().__getitem__(key)
Example #18
0
    def __setState(self,
                   result: Optional[ResultCode],
                   newState: str,
                   summary: Optional[str],
                   outputs: Optional[Mapping[str, str]] = None
                   ) -> None:
        products = self.getProduced()

        # Check if non-existing output.PRODUCT.locators are set.
        if outputs:
            filteredOutputs = dict(outputs)
            unknownProducts = [
                outputName
                for outputName in outputs.keys()
                if all(outputName != product.getName() for product in products)
                ]
            if unknownProducts:
                # Remove locators for non-existing products.
                for productName in unknownProducts:
                    del filteredOutputs[productName]
                # Overrule result and summary.
                result = ResultCode.WARNING
                summary = \
                    'Wrapper sets output locator for non-existing %s: %s' % (
                        pluralize('product', unknownProducts),
                        ', '.join(sorted(unknownProducts))
                        )
        else:
            filteredOutputs = {}

        if result is not None:
            self._properties['result'] = result
        if summary is not None:
            self._properties['summary'] = summary
        self._properties['state'] = newState
        self._properties['stoptime'] = getTime()
        if 'abort' in self._properties:
            del self._properties['abort']
        if 'alert' in self._properties:
            del self._properties['alert']

        # Update state of outputs.
        for product in products:
            # Store locator.
            locator = filteredOutputs.get(product.getName())
            if locator is not None:
                product.storeLocator(locator, self.getName())
            # Is the product done now?
            # pylint: disable=protected-access
            if product.isCombined():
                if self.getJob()._isProductFixed(product):
                    product.done()
            else:
                # Locator availability determines whether product is available
                # or not. This new behaviour was introduced in SoftFab 2.10.0,
                # along with local products.
                if locator is None:
                    if self.getJob()._isProductFixed(product):
                        self.getJob()._blockProduct(product)
                else:
                    product.done()

        # Trigger property cache for task objects in joblib.
        if self.hasResult():
            self.getTask().initCached(resultOnly = False)
Example #19
0
    def __adjustStartTime(self,
                          skipToNext: bool,
                          currentTime: Optional[int] = None
                          ) -> None:
        '''Calculate time of next scheduled job.
        If 'skipToNext' is True, the time is advanced one time period (for
        periodic schedules) or set to 0 (for non-periodic schedules);
        if 'skipToNext' is False, the current start time is rounded to a
        period boundary (for periodic schedules) or clipped to the current time
        (for non-periodic schedules).
        Passing currentTime is useful if the actual current time can be
        slightly before the minute boundary (see ScheduleManager.trigger()).
        '''
        if currentTime is None:
            currentTime = getTime()
        startTime = cast(Optional[int], self._properties.get('startTime'))
        repeat = self.repeat

        if startTime is None:
            # Start time can be None if a schedule is done or should start ASAP.
            assert repeat not in (ScheduleRepeat.DAILY, ScheduleRepeat.WEEKLY)

            if not skipToNext:
                # There is nothing to adjust.
                return
            # For continuous schedules started ASAP, it is essential to compute
            # the next start time because of the minimum delay feature.

        elif currentTime < startTime:
            # In theory forced skip and future start time would be possible,
            # but we have no use for it and therefore don't support it.
            assert not skipToNext

            if repeat is not ScheduleRepeat.WEEKLY:
                # Any start time in the future is fine as-is.
                return

        if repeat is ScheduleRepeat.ONCE:
            nextTime = None
        elif repeat is ScheduleRepeat.CONTINUOUSLY:
            assert startTime is None or currentTime >= startTime
            if skipToNext:
                nextTime = ( # round up to minute boundary
                    (currentTime + 59) // 60 + self.minDelay
                    ) * 60
            else:
                nextTime = None
        elif repeat is ScheduleRepeat.TRIGGERED:
            nextTime = None
        else:
            nextTimeList = list(time.localtime(startTime)[:9])
            nextTimeList[8] = -1 # local time: no time zone
            if startTime is None or currentTime >= startTime:
                # Set day to today.
                nextTimeList[:3] = time.localtime(currentTime)[:3]
                if skipToNext:
                    # Move ahead at least one day.
                    nextTimeList[2] += 1
            nextTime = _listToTimestamp(nextTimeList)
            dayFlags = self.dayFlags
            while nextTime < currentTime \
               or dayFlags[time.localtime(nextTime)[6]] != '1':
                nextTimeList[2] += 1
                nextTime = _listToTimestamp(nextTimeList)
        if nextTime is None:
            self._properties.pop('startTime', None)
        else:
            self._properties['startTime'] = nextTime
Example #20
0
        return Project(attributes)


class ProjectDB(Database[Project]):
    privilegeObject = 'p'
    description = 'project configuration'

    def __init__(self, baseDir: Path):
        super().__init__(baseDir, ProjectFactory())

    def _postLoad(self) -> None:
        super()._postLoad()

        # Create singleton record if it doesn't exist already.
        if len(self) == 0:
            record = Project({'name': defaultFactoryName})
            record.updateVersion()
            self.add(record)


class TimezoneUpdater(SingletonObserver):
    def updated(self, record: Project) -> None:
        _selectTimezone(record.timezone)


_bootTime = getTime()


def getBootTime() -> int:
    return _bootTime
Example #21
0
 def expired(self) -> bool:
     """Has this token expired?"""
     expireTime = self.expireTime
     return expireTime is not None and getTime() >= expireTime