Example #1
0
File: web.py Project: namely/crab
    def index(self):
        """Displays the main crab dashboard."""

        try:
            jobs = self.store.get_jobs()
            # we dont want to expose envvars to everyone because blah bleh blih
            for job in jobs:
                job['command'] = whitelabel_command(job.get('command'))
            return self._write_template('joblist.html', {'jobs': jobs,
                          'environment':os.environ.get('METRICS_ENV')})

        except CrabError as err:
            raise HTTPError(message=str(err))
Example #2
0
File: web.py Project: namely/crab
    def job(self, id_, command=None, finishid=None,

            barerows=None, unfiltered=None, limit=None, enddate=None,

            submit_config=None, submit_relink=None,
            submit_confirm=None, submit_cancel=None,
            orphan=None, graceperiod=None, timeout=None,
            success_pattern=None, warning_pattern=None, fail_pattern=None,
            note=None, inhibit=None,
            crabid=None,

            submit_notify=None, **kwargs):
        """Displays information about a current job.

        Currently also supports showing the job output.
        If command='output' but the finishid is not provided, then
        it will find the most recent output for the given job."""

        try:
            id_ = int(id_)
        except ValueError:
            raise HTTPError(400, 'Job number not a number')

        info = self.store.get_job_info(id_)
        if info is None:
            raise HTTPError(404, 'Job not found')

        if command is None:
            if limit is None:
                limit = 100
            else:
                try:
                    limit = int(limit)
                except ValueError:
                    raise HTTPError(400, 'Limit is not a number')
                if limit < 1:
                    raise HTTPError(400, 'Limit should not be less than one')
                elif limit > 1000:
                    raise HTTPError(400, 'Limit greater than a thousand')

            if unfiltered is None:
                squash_start = True
            else:
                squash_start = False

            if enddate is not None:
                try:
                    enddate = self.store.parse_datetime(enddate)
                except ValueError:
                    raise HTTPError(400, 'Start date format is invalid')

            events = self.store.get_job_events(id_, limit, end=enddate)

            if events:
                lastdatetime = events[-1]['datetime']
            else:
                lastdatetime = None

            # Filter the events.
            filter = CrabEventFilter(self.store, info['timezone'])
            events = filter(events, squash_start=squash_start,
                            skip_trivial=squash_start)

            if barerows is not None:
                return self._write_template('jobevents.html',
                           {'id': id_, 'events': events,
                            'lastdatetime': lastdatetime,
                          'environment':os.environ.get('METRICS_ENV')})

            # Try to convert the times to the timezone shown on the page.
            info['installed'] = filter.in_timezone(info['installed'])
            info['deleted'] = filter.in_timezone(info['deleted'])

            # Fetch configuration.
            config = self.store.get_job_config(id_)

            # Fetch job notifications.
            if config is not None:
                notification = self.store.get_job_notifications(
                                   config['configid'])
            else:
                notification = None
            info['command'] = whitelabel_command(info.get('command'))
            return self._write_template('job.html',
                       {'id': id_, 'info': info, 'config': config,
                        'status': self.monitor.get_job_status(id_),
                        'notification': notification, 'events': events,
                        'lastdatetime': lastdatetime,
                          'environment':os.environ.get('METRICS_ENV')})

        elif command == 'clear':
            if submit_confirm:
                self.store.log_alarm(id_, CrabStatus.CLEARED)

                # Wait for the monitor to process new events so that
                # that the job is in an 'OK' state before we reload the page.
                with self.monitor.new_event:
                    self.monitor.new_event.wait(10)

                raise HTTPRedirect("/job/" + str(id_))

            elif submit_cancel:
                raise HTTPRedirect("/job/" + str(id_))

            else:
                return self._write_template('confirm.html',
                       {'id': id_, 'info': info,
                        'title': 'clear status',
                        'description': 'Reset the job status?',
                        'target': '/job/' + str(id_) + '/clear',
                          'environment':os.environ.get('METRICS_ENV')})

        elif command == 'uninhibit':
            if submit_confirm:
                self.store.disable_inhibit(id_)
                raise HTTPRedirect('/job/' + str(id_))
            elif submit_cancel:
                raise HTTPRedirect('/job/' + str(id_))
            else:
                return self._write_template('confirm.html',
                    {'id': id_, 'info': info, 'title': 'resume',
                     'description': 'Resume inhibited job?',
                     'target': '/job/' + str(id_) + '/uninhibit',
                     'environment':os.environ.get('METRICS_ENV')})

        elif command == 'delete':
            notdeleted = info['deleted'] is None
            if submit_confirm:
                if notdeleted:
                    self.store.delete_job(id_)
                else:
                    self.store.undelete_job(id_)

                raise HTTPRedirect('/job/' + str(id_))

            elif submit_cancel:
                raise HTTPRedirect('/job/' + str(id_))

            else:
                return self._write_template('confirm.html',
                       {'id': id_, 'info': info,
                       'title': 'delete' if notdeleted else 'undelete',
                       'description': ('Delete' if notdeleted else 'Undelete') +
                                       ' this job from the server?',
                       'target': '/job/' + str(id_) + '/delete',
                          'environment':os.environ.get('METRICS_ENV')})

        elif command == 'changeid':
            if submit_confirm:
                if crabid != '':
                    if self.store.get_jobs(info['host'], info['user'],
                            include_deleted=True, crabid=crabid):
                        raise HTTPError(400, 'Specified job ID already exists.')
                    else:
                        self.store.update_job(id_, crabid=crabid)
                        raise HTTPRedirect('/job/' + str(id_))
                else:
                    raise HTTPError(400, 'Specified job ID is blank.')

            elif submit_cancel:
                raise HTTPRedirect('/job/' + str(id_))

            else:
                return self._write_template('confirm.html',
                       {'id': id_, 'info': info,
                       'title': 'change identifier',
                       'description': 'Change Job ID for this job?  Please note '
                                      'that the ID which will be used to report '
                                      'events related to this job should be '
                                      'updated at the same time to ensure that '
                                      'the job continues to be correctly '
                                      'identified.  This should be done in the '
                                      'crontab if the CRABID variable is used, '
                                      'or in the cron job itself in the case '
                                      'of Crab-aware cron jobs.',
                       'target': '/job/' + str(id_) + '/changeid',
                       'data': {'crabid': crabid},
                       'environment':os.environ.get('METRICS_ENV')})

        elif command == 'output':
            finishid_next = None
            finishid_prev = None

            if finishid is None:
                # If finishid is not specified, select the most recent
                # for this job.
                finishes = self.store.get_job_finishes(id_, limit=2)

                if not finishes:
                    raise HTTPError(404, 'No job output found')

                finish = finishes[0]
                finishid = finish['finishid']

                if len(finishes) > 1:
                    finishid_prev = finishes[1]['finishid']

            else:
                try:
                    finishid = int(finishid)
                except ValueError:
                    raise HTTPError(400, 'Finish ID is not a number')

                finishes = self.store.get_job_finishes(id_, finishid=finishid)
                if not finishes:
                    raise HTTPError(404, 'Finish ID not found or wrong job')
                finish = finishes[0]

                finishes = self.store.get_job_finishes(id_, 1, before=finishid)
                if finishes:
                    finishid_prev = finishes[0]['finishid']

                finishes = self.store.get_job_finishes(id_, 1, after=finishid)
                if finishes:
                    finishid_next = finishes[0]['finishid']

            (stdout, stderr) = self.store.get_job_output(finishid,
                    info['host'], info['user'], id_, info['crabid'])

            filter = CrabEventFilter(self.store, info['timezone'])
            finish['datetime'] = filter.in_timezone(finish['datetime'])

            return self._write_template('joboutput.html',
                       {'id': id_, 'info': info, 'finish': finish,
                        'stdout': stdout, 'stderr': stderr,
                        'next': finishid_next, 'prev': finishid_prev,
                        'environment':os.environ.get('METRICS_ENV')})

        elif command == 'config':
            if submit_relink:
                try:
                    orphan = int(orphan)
                except ValueError:
                    raise HTTPError(400, 'Orphan number not a number')

                self.store.relink_job_config(orphan, id_)
                raise HTTPRedirect("/job/" + str(id_))

            elif submit_config:
                try:
                    if timeout == '':
                        timeout = None
                    elif timeout is not None:
                        timeout = int(timeout)
                    if graceperiod == '':
                        graceperiod = None
                    elif graceperiod is not None:
                        graceperiod = int(graceperiod)

                    if success_pattern == '':
                        success_pattern = None
                    if warning_pattern == '':
                        warning_pattern = None
                    if fail_pattern == '':
                        fail_pattern = None

                    if note is not None:
                        note = note.strip()
                        if note == '':
                            note = None

                    inhibit = inhibit is not None

                except ValueError:
                    raise HTTPError(400, 'Time not a number')

                self.store.write_job_config(id_, graceperiod, timeout,
                    success_pattern, warning_pattern, fail_pattern, note,
                    inhibit)
                raise HTTPRedirect("/job/" + str(id_))

            else:
                config = self.store.get_job_config(id_)

                if config is None:
                    orphan = self.store.get_orphan_configs()
                else:
                    orphan = None

                return self._write_template('jobconfig.html',
                           {'id': id_, 'info': info, 'config': config,
                            'orphan': orphan,
                          'environment':os.environ.get('METRICS_ENV')})

        elif command == 'notify':
            if submit_notify:
                # Ensure that this job has a configuration entry
                # so that we can link to it.
                config = self.store.get_job_config(id_)

                if config is not None:
                    configid = config['configid']
                else:
                    configid = self.store.write_job_config(id_)

                # Make a list of notifications for this job
                # so that we can delete those which are not
                # included in the POST parameters.
                existing = set()
                for notification in self.store.get_job_notifications(configid):
                    existing.add(notification['notifyid'])

                # Update existing notifications.
                for kwarg in kwargs:
                    match = re.search('method_(new_)?(\d+)', kwarg)
                    if not match:
                        continue

                    if match.group(1):
                        key = ''.join(match.groups())
                        notifyid = None
                    else:
                        key = match.group(2)
                        notifyid = int(key)
                        existing.discard(notifyid)

                    self.store.write_notification(
                            notifyid, configid,
                            None, None,
                            kwargs['method_' + key],
                            kwargs['address_' + key],
                            empty_to_none(kwargs['time_' + key]),
                            empty_to_none(kwargs['timezone_' + key]),
                            'include_ok_' + key not in kwargs,
                            'include_warning_' + key not in kwargs,
                            'include_error_' + key not in kwargs,
                            'include_output_' + key in kwargs)

                # Delete existing notifications which were not present.
                for notifyid in existing:
                    self.store.delete_notification(notifyid)

                raise HTTPRedirect('/job/' + str(id_))
            else:
                config = self.store.get_job_config(id_)

                if config is not None:
                    notifications = self.store.get_job_notifications(
                                        config['configid'])
                else:
                    notifications = None

                return self._write_template('editnotify.html',
                            {'match_mode': False,
                             'id': id_, 'info': info,
                             'notifications': notifications,
                             'environment':os.environ.get('METRICS_ENV')})

        else:
            raise HTTPError(404, 'Unknown job command')