コード例 #1
0
class Tasks(RPCRoot):
    # For XMLRPC methods in this class.
    exposed = True

    task_list_action_widget = TaskActionWidget()
    task_form = TaskSearchForm()
    task_widget = TasksWidget()

    _upload = widgets.FileField(name='task_rpm', label='Task RPM')
    form = HorizontalForm('task',
                          fields=[_upload],
                          action='save_data',
                          submit_text=_(u'Upload'))
    del _upload

    @expose(template='bkr.server.templates.form-post')
    @identity.require(identity.not_anonymous())
    def new(self, **kw):
        return_dict = dict(
            title='New Task',
            form=self.form,
            action='./save',
            options={},
            value=kw,
        )
        return return_dict

    @cherrypy.expose
    def filter(self, filter):
        """
        Returns a list of tasks filtered by the given criteria.

        The *filter* argument must be an XML-RPC structure (dict), with any of the following keys:

            'distro_name'
                Distro name. Include only tasks which are compatible 
                with this distro.
            'osmajor'
                OSVersion OSMajor, like RedHatEnterpriseLinux6.  Include only
                tasks which are compatible with this OSMajor.
            'names'
                Task name. Include only tasks that are named. Useful when
                combined with 'osmajor' or 'distro_name'.
            'packages'
                List of package names. Include only tasks which have a Run-For 
                entry matching any of these packages.
            'types'
                List of task types. Include only tasks which have one or more 
                of these types.
            'valid'
                bool 0 or 1. Include only tasks which are valid or not.
            'destructive'
                bool 0 or 1. Set to 0 for only non-destructive tasks. Set to 
                1 for only destructive tasks.

        The return value is an array of dicts, which are name and arches. 
        name is the name of the matching tasks.
        arches is an array of arches which this task does not apply for.
        Call :meth:`tasks.to_dict` to fetch metadata for a particular task.

        .. versionchanged:: 0.9
           Changed 'install_name' to 'distro_name' in the *filter* argument.
        """
        if filter.get('distro_name'):
            distro = Distro.by_name(filter['distro_name'])
            tasks = distro.tasks()
        elif 'osmajor' in filter and filter['osmajor']:
            try:
                osmajor = OSMajor.by_name(filter['osmajor'])
            except InvalidRequestError:
                raise BX(_('Invalid OSMajor: %s' % filter['osmajor']))
            tasks = osmajor.tasks()
        else:
            tasks = Task.query

        # Filter by valid task if requested
        if 'valid' in filter:
            tasks = tasks.filter(Task.valid == bool(filter['valid']))

        # Filter by destructive if requested
        if 'destructive' in filter:
            tasks = tasks.filter(
                Task.destructive == bool(filter['destructive']))

        # Filter by name if specified
        # /distribution/install, /distribution/reservesys
        if 'names' in filter and filter['names']:
            # if not a list, make it into a list.
            if isinstance(filter['names'], str):
                filter['names'] = [filter['names']]
            or_names = []
            for tname in filter['names']:
                or_names.append(Task.name == tname)
            tasks = tasks.filter(or_(*or_names))

        # Filter by packages if specified
        # apache, kernel, mysql, etc..
        if 'packages' in filter and filter['packages']:
            # if not a list, make it into a list.
            if isinstance(filter['packages'], str):
                filter['packages'] = [filter['packages']]
            tasks = tasks.filter(
                Task.runfor.any(
                    or_(*[
                        TaskPackage.package == package
                        for package in filter['packages']
                    ])))

        # Filter by type if specified
        # Tier1, Regression, KernelTier1, etc..
        if 'types' in filter and filter['types']:
            # if not a list, make it into a list.
            if isinstance(filter['types'], str):
                filter['types'] = [filter['types']]
            tasks = tasks.join('types')
            or_types = []
            for type in filter['types']:
                try:
                    tasktype = TaskType.by_name(type)
                except InvalidRequestError, err:
                    raise BX(_('Invalid Task Type: %s' % type))
                or_types.append(TaskType.id == tasktype.id)
            tasks = tasks.filter(or_(*or_types))

        # Return all task names
        return [
            dict(name=task.name,
                 arches=[str(arch.arch) for arch in task.excluded_arch])
            for task in tasks
        ]
コード例 #2
0
class Jobs(RPCRoot):
    # For XMLRPC methods in this class.
    exposed = True
    job_list_action_widget = JobActionWidget()
    job_page_action_widget = JobPageActionWidget()
    recipeset_widget = RecipeSetWidget()
    recipe_widget = RecipeWidget()
    priority_widget = PriorityWidget(
    )  #FIXME I have a feeling we don't need this as the RecipeSet widget declares an instance of it
    product_widget = ProductWidget()
    retention_tag_widget = RetentionTagWidget()
    job_type = {'RS': RecipeSet, 'J': Job}
    whiteboard_widget = JobWhiteboard()

    hidden_id = widgets.HiddenField(name='id')
    confirm = widgets.Label(name='confirm',
                            default="Are you sure you want to cancel?")
    message = widgets.TextArea(name='msg',
                               label=_(u'Reason?'),
                               help_text=_(u'Optional'))

    _upload = widgets.FileField(name='filexml', label='Job XML')
    form = HorizontalForm('jobs',
                          fields=[_upload],
                          action='save_data',
                          submit_text=_(u'Submit Data'))
    del _upload

    cancel_form = widgets.TableForm('cancel_job',
                                    fields=[hidden_id, message, confirm],
                                    action='really_cancel',
                                    submit_text=_(u'Yes'))

    job_form = JobForm()

    job_schema_doc = lxml.etree.parse(
        pkg_resources.resource_stream('bkr.common', 'schema/beaker-job.rng'))

    @classmethod
    def success_redirect(cls, id, url='/jobs/mine', *args, **kw):
        flash(_(u'Success! job id: %s' % id))
        redirect('%s' % url)

    @expose(template='bkr.server.templates.form-post')
    @identity.require(identity.not_anonymous())
    def new(self, **kw):
        return dict(
            title='New Job',
            form=self.form,
            action='./clone',
            options={},
            value=kw,
        )

    def _check_job_deletability(self, t_id, job):
        if not isinstance(job, Job):
            raise TypeError('%s is not of type %s' % (t_id, Job.__name__))
        if not job.can_delete(identity.current.user):
            raise BeakerException(
                _(u'You do not have permission to delete %s' % t_id))

    def _delete_job(self, t_id):
        job = TaskBase.get_by_t_id(t_id)
        self._check_job_deletability(t_id, job)
        Job.delete_jobs([job])
        return [t_id]

    @expose()
    @identity.require(identity.not_anonymous())
    @restrict_http_method('post')
    def delete_job_page(self, t_id):
        try:
            self._delete_job(t_id)
            flash(_(u'Succesfully deleted %s' % t_id))
        except (BeakerException, TypeError):
            flash(_(u'Unable to delete %s' % t_id))
            redirect('.')
        redirect('./mine')

    @expose()
    @identity.require(identity.not_anonymous())
    @restrict_http_method('post')
    def delete_job_row(self, t_id):
        try:
            self._delete_job(t_id)
            return [t_id]
        except (BeakerException, TypeError), e:
            log.debug(str(e))
            response.status = 400
            return ['Unable to delete %s' % t_id]
コード例 #3
0
class CSV(RPCRoot):
    # For XMLRPC methods in this class.
    exposed = False

    export_help_text = XML(
        u'<span>Refer to the <a href="http://beaker-project.org/docs/'
        'admin-guide/interface.html#export" target="_blank">'
        'documentation</a> to learn more about the exported data.</span>'
    ).expand()
    import_help_text = XML(
        u'<span>Refer to the <a href="http://beaker-project.org/docs/'
        'admin-guide/interface.html#import" target="_blank">'
        'documentation</a> for details about the supported CSV format.</span>'
    ).expand()

    upload     = widgets.FileField(name='csv_file', label='Import CSV', \
                                   help_text = import_help_text)
    download = RadioButtonList(name='csv_type',
                               label='CSV Type',
                               options=[
                                   ('system', 'Systems'),
                                   ('system_id', 'Systems (for modification)'),
                                   ('labinfo', 'System LabInfo'),
                                   ('power', 'System Power'),
                                   ('exclude', 'System Excluded Families'),
                                   ('install', 'System Install Options'),
                                   ('keyvalue', 'System Key/Values'),
                                   ('system_pool', 'System Pools'),
                                   ('user_group', 'User Groups')
                               ],
                               default='system',
                               help_text=export_help_text)

    importform = HorizontalForm(
        'import',
        fields=[upload],
        action='import data',
        submit_text=_(u'Import CSV'),
    )

    exportform = HorizontalForm(
        'export',
        fields=[download],
        action='export data',
        submit_text=_(u'Export CSV'),
    )

    @expose(template='bkr.server.templates.form')
    @identity.require(identity.not_anonymous())
    def index(self, **kw):
        return dict(
            form=self.exportform,
            title=_(u'CSV Export'),
            action='./action_export',
            options={},
            value=kw,
        )

    @expose(template='bkr.server.templates.form-post')
    @identity.require(identity.in_group('admin'))
    def csv_import(self, **kw):
        return dict(
            form=self.importform,
            title=_(u'CSV Import'),
            action='./action_import',
            options={},
            value=kw,
        )

    @expose()
    @identity.require(identity.not_anonymous())
    def action_export(self, csv_type, *args, **kw):
        file = NamedTemporaryFile()
        log = self.to_csv(file, csv_type)
        file.seek(0)

        return serve_file(file.name,
                          contentType="text/csv",
                          disposition="attachment",
                          name="%s.csv" % csv_type)

    def _import_row(self, data, log):
        if data['csv_type'] in system_types and ('fqdn' in data
                                                 or 'id' in data):
            if data.get('id', None):
                try:
                    system = System.query.filter(System.id == data['id']).one()
                except InvalidRequestError as e:
                    raise ValueError('Non-existent system id')
            else:
                try:
                    system = System.query.filter(
                        System.fqdn == data['fqdn']).one()
                except InvalidRequestError:
                    # Create new system with some defaults
                    # Assume the system is broken until proven otherwise.
                    # Also assumes its a machine.  we have to pick something
                    system = System(fqdn=data['fqdn'],
                                    owner=identity.current.user,
                                    type=SystemType.machine,
                                    status=SystemStatus.broken)
                    session.add(system)
                    # new systems are visible to everybody by default
                    system.custom_access_policy = SystemAccessPolicy()
                    system.custom_access_policy.add_rule(SystemPermission.view,
                                                         everybody=True)
            if not system.can_edit(identity.current.user):
                raise ValueError('You are not the owner of %s' % system.fqdn)
            # we change the FQDN only when a valid system id is supplied
            if not data.get('id', None):
                data.pop('fqdn')
            self.from_csv(system, data, log)
        elif data['csv_type'] == 'user_group' and 'user' in data:
            user = User.by_user_name(data['user'])
            if user is None:
                raise ValueError('%s is not a valid user' % data['user'])
            CSV_GroupUser.from_csv(user, data, log)
        else:
            raise ValueError('Invalid csv_type %s or missing required fields' %
                             data['csv_type'])

    @expose(template='bkr.server.templates.csv_import')
    @identity.require(identity.in_group('admin'))
    def action_import(self, csv_file, *args, **kw):
        """
        TurboGears method to import data from csv
        """

        log = []
        try:
            # ... process CSV file contents here ...
            missing = object()
            reader = UnicodeDictReader(csv_file.file,
                                       restkey=missing,
                                       restval=missing)
            is_empty = True

            for data in reader:

                is_empty = False

                if missing in data:
                    log.append('Too many fields on line %s (expecting %s)' %
                               (reader.line_num, len(reader.fieldnames)))
                    continue
                if any(value is missing for value in data.itervalues()):
                    missing_fields = [
                        field for field, value in data.iteritems()
                        if value is missing
                    ]
                    log.append('Missing fields on line %s: %s' %
                               (reader.line_num, ', '.join(missing_fields)))
                    continue
                if 'csv_type' not in data:
                    log.append('Missing csv_type on line %s' % reader.line_num)
                    continue
                try:
                    with session.begin_nested():
                        self._import_row(data, log)
                except Exception, e:
                    # log and continue processing more rows
                    log.append('Error importing line %s: %s' %
                               (reader.line_num, e))

            if is_empty:
                log.append('Empty CSV file supplied')

        except csv.Error, e:
            session.rollback()
            log.append('Error parsing CSV file: %s' % e)