Exemplo n.º 1
0
class PrivateTitleFields(widgets.WidgetsList):
    import operator
    preferred_distributor = widgets.TextField()
    title_id = widgets.HiddenField()
    all_sections = [(l.id, l.sectionName) for l in list(Section.select())]
    all_sections.sort(key=operator.itemgetter(1))
    sections = widgets.CheckBoxList(options=all_sections, field_class='inline')
Exemplo n.º 2
0
class RatingFields(widgets.WidgetsList):
    rater = widgets.TextField(validators=validators.NotEmpty)
    score = widgets.RadioButtonList(options=[(1, "1"), (2, "2"), (3, "3"),
                                             (4, "4"), (5, "5")],
                                    default=5,
                                    attrs={'class': 'ratingsform'})
    comments = widgets.TextArea()
    title = widgets.HiddenField()
Exemplo n.º 3
0
class JobForm(widgets.Form):

    template = 'bkr.server.templates.job_form'
    name = 'job'
    submit_text = _(u'Queue')
    fields = [widgets.TextArea(name='textxml')]
    hidden_fields = [
        widgets.HiddenField(name='confirmed',
                            validator=validators.StringBool())
    ]
    params = ['xsd_errors']
    xsd_errors = None

    def update_params(self, d):
        super(JobForm, self).update_params(d)
        if 'xsd_errors' in d['options']:
            d['xsd_errors'] = d['options']['xsd_errors']
            d['submit_text'] = _(u'Queue despite validation errors')
Exemplo n.º 4
0
class GroupForm(HorizontalForm):
    fields = [
        widgets.HiddenField(name='group_id'),
        widgets.TextField(name='group_name', label=_(u'Group Name')),
        widgets.TextField(name='display_name', label=_(u'Display Name')),
        widgets.PasswordField(name='root_password',
                              label=_(u'Root Password'),
                              validator=StrongPassword()),
        widgets.CheckBox(name='ldap',
                         label=_(u'LDAP'),
                         help_text=_(u'Populate group membership from LDAP?')),
    ]
    name = 'Group'
    action = 'save_data'
    submit_text = _(u'Save')
    validator = GroupFormSchema()

    def update_params(self, d):
        if not identity.current.user.is_admin() or \
                not config.get('identity.ldap.enabled', False):
            d['disabled_fields'] = ['ldap']
        super(GroupForm, self).update_params(d)
Exemplo n.º 5
0
class Users(AdminPage):
    # For XMLRPC methods in this class.
    exposed = True

    user_id = widgets.HiddenField(name='user_id')
    user_name = widgets.TextField(name='user_name', label=_(u'Login'))
    display_name = widgets.TextField(name='display_name',
                                     label=_(u'Display Name'))
    email_address = widgets.TextField(name='email_address',
                                      label=_(u'Email Address'))
    password = widgets.PasswordField(name='password', label=_(u'Password'))
    disabled = widgets.CheckBox(name='disabled', label=_(u'Disabled'))
    user_form = HorizontalForm(
        'User',
        fields=[
            user_id, user_name, display_name, email_address, password, disabled
        ],
        action='save_data',
        submit_text=_(u'Save'),
    )

    def __init__(self, *args, **kw):
        kw['search_url'] = url("/users/by_name?anywhere=1&ldap=0")
        kw['search_name'] = 'user'
        super(Users, self).__init__(*args, **kw)

        self.search_col = User.user_name
        self.search_mapper = User

    @identity.require(identity.in_group("admin"))
    @expose(template='bkr.server.templates.form')
    def new(self, **kw):
        return dict(
            form=self.user_form,
            action='./save',
            options={},
            value=kw,
        )

    @identity.require(identity.in_group("admin"))
    @expose(template='bkr.server.templates.user_edit_form')
    def edit(self, id=None, **kw):
        if id:
            user = User.by_id(id)
            title = _(u'User %s') % user.user_name
            value = user
        else:
            user = None
            title = _(u'New user')
            value = kw
        return_vals = dict(form=self.user_form,
                           action='./save',
                           title=title,
                           options={},
                           value=value)
        if id:
            return_vals['groupsgrid'] = self.show_groups()
        else:
            return_vals['groupsgrid'] = None
        return return_vals

    @identity.require(identity.in_group("admin"))
    @expose()
    @validate(user_form, validators=UserFormSchema())
    @error_handler(edit)
    def save(self, **kw):
        if kw.get('user_id'):
            user = User.by_id(kw['user_id'])
        else:
            user = User()
            session.add(user)
        user.display_name = kw['display_name']
        user.user_name = kw['user_name']
        user.email_address = kw['email_address']
        if kw.get('disabled') != user.disabled:
            user.disabled = kw.get('disabled')
            if user.disabled:
                self._disable(user, method="WEBUI")
        if kw['password'] != user.password:
            user.password = kw['password']

        flash(_(u"%s saved" % user.display_name))
        redirect(".")

    def make_remove_link(self, user):
        if user.removed is not None:
            return XML('<a class="btn" href="unremove?id=%s">'
                       '<i class="fa fa-plus"/> Re-Add</a>' % user.user_id)
        else:
            return XML('<a class="btn" href="remove?id=%s">'
                       '<i class="fa fa-times"/> Remove</a>' % user.user_id)

    @expose(template="bkr.server.templates.admin_grid")
    @paginate('list', default_order='user_name', limit=20)
    def index(self, *args, **kw):
        users = session.query(User)
        list_by_letters = set(
            [elem.user_name[0].capitalize() for elem in users])
        result = self.process_search(**kw)
        if result:
            users = result

        users_grid = myPaginateDataGrid(fields=[
            ('Login', lambda x: make_edit_link(x.user_name, x.user_id)),
            ('Display Name', lambda x: x.display_name),
            ('Disabled', lambda x: x.disabled),
            ('', lambda x: self.make_remove_link(x)),
        ],
                                        add_action='./new')
        return dict(title="Users",
                    grid=users_grid,
                    alpha_nav_bar=AlphaNavBar(list_by_letters, 'user'),
                    search_widget=self.search_widget_form,
                    list=users)

    @identity.require(identity.in_group("admin"))
    @expose()
    def remove(self, id, **kw):
        try:
            user = User.by_id(id)
        except InvalidRequestError:
            flash(_(u'Invalid user id %s' % id))
            raise redirect('.')
        try:
            self._remove(user=user, method='WEBUI')
        except BX, e:
            flash(
                _(u'Failed to remove User %s, due to %s' %
                  (user.user_name, e)))
            raise redirect('.')
        else:
Exemplo n.º 6
0
class OSVersions(AdminPage):
    # For XMLRPC methods in this class.
    exposed = False

    id      = widgets.HiddenField(name="id")
    alias   = widgets.TextField(name="alias",
                                validator=validators.UnicodeString(if_empty=None))
    arches  = CheckBoxList(name="arches", label="Arches",
                                      options=lambda: [(arch.id, arch.arch) for arch in Arch.query],
                                      validator=validators.Int())

    osmajor_form = HorizontalForm(
        fields      = [id, alias],
        submit_text = _(u"Edit OSMajor"),
    )

    osversion_form = HorizontalForm(
        fields      = [id, arches],
        action      = "edit osversion",
        submit_text = _(u"Edit OSVersion"),
    )
 
    def __init__(self,*args,**kw):
        kw['search_name'] = 'osversion' 
        kw['search_url'] = url("/osversions/by_name?anywhere=1")
        super(OSVersions,self).__init__(*args,**kw) 

        self.search_col = OSMajor.osmajor
        self.join = [OSVersion.osmajor]
        self.search_mapper = OSVersion
        self.add = False
     
    @identity.require(identity.in_group("admin"))
    @expose(template="bkr.server.templates.form")
    def edit(self, id=None, *args, **kw):
        try:
            osversion = OSVersion.by_id(id)
        except InvalidRequestError:
            flash(_(u"Invalid OSVersion ID %s" % id))
            redirect(".")
        return dict(title   = unicode(osversion),
                    value   = dict(id     = osversion.id,
                                   arches = [arch.id for arch in osversion.arches]),
                    form    = self.osversion_form,
                    action  = "./save",
                    options = None)

    @identity.require(identity.in_group("admin"))
    @expose(template="bkr.server.templates.osmajor")
    def edit_osmajor(self, id=None, *args, **kw):
        try:
            osmajor = OSMajor.by_id(id)
        except InvalidRequestError:
            flash(_(u"Invalid OSMajor ID %s" % id))
            redirect(".")
        return dict(title   = "OSMajor",
                    value   = osmajor,
                    form    = self.osmajor_form,
                    action  = "./save_osmajor",
                    options = None)

    @identity.require(identity.in_group("admin"))
    @expose()
    @validate(form=osmajor_form)
    def save_osmajor(self, id=None, alias=None, *args, **kw):
        try:
            osmajor = OSMajor.by_id(id)
        except InvalidRequestError:
            flash(_(u"Invalid OSMajor ID %s" % id))
            redirect(".")
        if osmajor.alias != alias:
            if alias:
                try:
                    existing = OSMajor.by_name_alias(alias)
                except NoResultFound:
                    pass
                else:
                    flash(_(u'Cannot save alias %s, it is already used by %s')
                            % (alias, existing))
                    redirect('.')
            osmajor.alias = alias
            flash(_(u"Changes saved for %s" % osmajor))
        else:
            flash(_(u"No changes for %s" % osmajor))
        redirect(".")

    @identity.require(identity.in_group('admin'))
    @expose()
    def save_osmajor_installopts(self, osmajor_id=None, installopts=None):
        try:
            osmajor = OSMajor.by_id(osmajor_id)
        except InvalidRequestError:
            flash(_(u"Invalid OSMajor ID %s" % id))
            redirect(".")
        for arch, options in installopts.iteritems():
            # arch=None means applied to all arches
            io = OSMajorInstallOptions.lazy_create(osmajor_id=osmajor.id,
                    arch_id=Arch.by_name(arch).id if arch else None)
            io.ks_meta = options['ks_meta']
            io.kernel_options = options['kernel_options']
            io.kernel_options_post = options['kernel_options_post']
        flash(_(u'Install options saved for %s') % osmajor)
        redirect('.')

    @identity.require(identity.in_group("admin"))
    @expose()
    @validate(form=osversion_form)
    def save(self, id=None, arches=None, *args, **kw):
        try:
            osversion = OSVersion.by_id(id)
        except InvalidRequestError:
            flash(_(u"Invalid OSVersion ID %s" % id))
            redirect(".")
        arch_objects = [Arch.by_id(arch) for arch in arches]
        if osversion.arches != arch_objects:
            osversion.arches = arch_objects
            flash(_(u"Changes Saved for %s" % osversion))
        else:
            flash(_(u"No Changes for %s" % osversion))
        redirect(".")

    
    @expose(format='json')
    def by_name(self, input,*args,**kw):
        input = input.lower()
        if 'anywhere' in kw:
            search = OSVersion.list_osmajor_by_name(input, find_anywhere=True)
        else:
            search = OSVersion.list_osmajor_by_name(input)

        osmajors =  ["%s" % (match.osmajor.osmajor) for match in search] 
        osmajors = list(set(osmajors))
        return dict(matches=osmajors)

    @expose(template="bkr.server.templates.admin_grid")
    @paginate('list',limit=50, default_order='osmajor.osmajor')
    def index(self,*args,**kw):
        osversions = self.process_search(*args,**kw) 
        list_by_letters = []
        for elem in osversions:
            osmajor_name = elem.osmajor.osmajor
            if osmajor_name:
                list_by_letters.append(osmajor_name[0].capitalize())
        alpha_nav_data = set(list_by_letters)
        template_data = self.osversions(osversions,*args, **kw)
        nav_bar = self._build_nav_bar(alpha_nav_data,self.search_name)
        template_data['alpha_nav_bar'] = nav_bar
        template_data['search_widget'] = self.search_widget_form
        return template_data
         

    def osversions(self, osversions=None, *args, **kw):
        q = session.query(self.search_mapper) # This line +3 dupes the start of process_search
        if osversions is None:
            for j in self.join:
                q = q.join(j)
            osversions = q
        osversions_grid = myPaginateDataGrid(fields=[
                                  myPaginateDataGrid.Column(name='osmajor.osmajor', getter=lambda x: make_link(url = './edit_osmajor?id=%s' % x.osmajor.id, text = x.osmajor), title='OS Major', options=dict(sortable=True)),
                                  myPaginateDataGrid.Column(name='osmajor.alias', getter=lambda x: x.osmajor.alias, title='Alias', options=dict(sortable=True)),
                                  myPaginateDataGrid.Column(name='osminor', getter=lambda x: make_link(url  = './edit?id=%s' % x.id, text = x.osminor), title='OS Minor', options=dict(sortable=True)),
                                  myPaginateDataGrid.Column(name='arches', getter=lambda x: " ".join([arch.arch for arch in x.arches]), title='Arches', options=dict(sortable=True)),
                              ])
 
        return dict(title="OS Versions", 
                    grid = osversions_grid, 
                    addable = False,              
                    list = osversions)

    default = index
Exemplo n.º 7
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]
Exemplo n.º 8
0
    def default(self, id):
        try:
            job = Job.by_id(id)
        except InvalidRequestError:
            flash(_(u"Invalid job id %s" % id))
            redirect(".")

        if job.counts_as_deleted():
            flash(_(u'Invalid %s, has been deleted' % job.t_id))
            redirect(".")

        recipe_set_history = [
            RecipeSetActivity.query.with_parent(elem, "activity")
            for elem in job.recipesets
        ]
        recipe_set_data = []
        for query in recipe_set_history:
            for d in query:
                recipe_set_data.append(d)

        recipe_set_data += job.activity
        recipe_set_data = sorted(recipe_set_data,
                                 key=lambda x: x.created,
                                 reverse=True)

        job_history_grid = BeakerDataGrid(
            name='job_history_datagrid',
            fields=[
                BeakerDataGrid.Column(name='user',
                                      getter=lambda x: x.user,
                                      title='User',
                                      options=dict(sortable=True)),
                BeakerDataGrid.Column(name='service',
                                      getter=lambda x: x.service,
                                      title='Via',
                                      options=dict(sortable=True)),
                BeakerDataGrid.Column(name='created',
                                      title='Created',
                                      getter=lambda x: x.created,
                                      options=dict(sortable=True)),
                BeakerDataGrid.Column(name='object_name',
                                      getter=lambda x: x.object_name(),
                                      title='Object',
                                      options=dict(sortable=True)),
                BeakerDataGrid.Column(name='field_name',
                                      getter=lambda x: x.field_name,
                                      title='Field Name',
                                      options=dict(sortable=True)),
                BeakerDataGrid.Column(name='action',
                                      getter=lambda x: x.action,
                                      title='Action',
                                      options=dict(sortable=True)),
                BeakerDataGrid.Column(name='old_value',
                                      getter=lambda x: x.old_value,
                                      title='Old value',
                                      options=dict(sortable=True)),
                BeakerDataGrid.Column(name='new_value',
                                      getter=lambda x: x.new_value,
                                      title='New value',
                                      options=dict(sortable=True)),
            ])

        return_dict = dict(
            title='Job',
            recipeset_widget=self.recipeset_widget,
            recipe_widget=self.recipe_widget,
            hidden_id=widgets.HiddenField(name='job_id', value=job.id),
            job_history=recipe_set_data,
            job_history_grid=job_history_grid,
            whiteboard_widget=self.whiteboard_widget,
            action_widget=self.job_page_action_widget,
            delete_action=url('delete_job_page'),
            job=job,
            product_widget=self.product_widget,
            retention_tag_widget=self.retention_tag_widget,
        )
        return return_dict
Exemplo n.º 9
0
class RecipeSets(RPCRoot):
    # For XMLRPC methods in this class.
    exposed = True

    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'))
    cancel_form = widgets.TableForm('cancel_recipeset',
                                    fields=[hidden_id, message, confirm],
                                    action='really_cancel',
                                    submit_text=_(u'Yes'))

    @identity.require(identity.not_anonymous())
    @expose(template="bkr.server.templates.form")
    def cancel(self, id):
        """
        Confirm cancel recipeset
        """
        try:
            recipeset = RecipeSet.by_id(id)
        except InvalidRequestError:
            flash(_(u"Invalid recipeset id %s" % id))
            redirect("/jobs/%s" % recipeset.job.id)
        if not recipeset.can_cancel(identity.current.user):
            flash(
                _(u"You don't have permission to cancel recipeset id %s" % id))
            redirect("/jobs/%s" % recipeset.job.id)
        return dict(
            title='Cancel RecipeSet %s' % id,
            form=self.cancel_form,
            action='./really_cancel',
            options={},
            value=dict(id=recipeset.id,
                       confirm='really cancel recipeset %s?' % id),
        )

    @identity.require(identity.not_anonymous())
    @expose()
    def really_cancel(self, id, msg=None):
        """
        Confirm cancel recipeset
        """
        try:
            recipeset = RecipeSet.by_id(id)
        except InvalidRequestError:
            flash(_(u"Invalid recipeset id %s" % id))
            redirect("/jobs/%s" % recipeset.job.id)
        if not recipeset.can_cancel(identity.current.user):
            flash(
                _(u"You don't have permission to cancel recipeset id %s" % id))
            redirect("/jobs/%s" % recipeset.job.id)
        recipeset.cancel(msg)
        recipeset.record_activity(user=identity.current.user,
                                  service=u'WEBUI',
                                  field=u'Status',
                                  action=u'Cancelled',
                                  old='',
                                  new='')
        flash(_(u"Successfully cancelled recipeset %s" % id))
        redirect("/jobs/%s" % recipeset.job.id)

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def stop(self, recipeset_id, stop_type, msg=None):
        """
        Set recipeset status to Completed
        """
        try:
            recipeset = RecipeSet.by_id(recipeset_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipeset ID: %s' % recipeset_id))
        if stop_type not in recipeset.stop_types:
            raise BX(
                _('Invalid stop_type: %s, must be one of %s' %
                  (stop_type, recipeset.stop_types)))
        kwargs = dict(msg=msg)
        return getattr(recipeset, stop_type)(**kwargs)
Exemplo n.º 10
0
class Configuration(AdminPage):
    exposed = False

    id = widgets.HiddenField(name='id')
    value_str = widgets.TextArea(name='value', label=_(u'Value'))
    value_int = widgets.TextField(name='value',
                                  label=_(u'Value'),
                                  validator=validators.Int())
    valid_from = widgets.TextField(
        name='valid_from',
        label=_(u'Effective from date'),
        help_text=
        u"Enter date and time (YYYY-MM-DD HH:MM) in the future or leave blank for setting to take immediate effect"
    )

    string_form = HorizontalForm(
        'configitem',
        fields=[id, value_str, valid_from],
        action='save_data',
        submit_text=_(u'Save'),
    )

    int_form = HorizontalForm(
        'configitem',
        fields=[id, value_int, valid_from],
        action='save_data',
        submit_text=_(u'Save'),
    )

    value_grid = BeakerDataGrid(fields=[
                    ('Value', lambda x: x.value),
                    ('Effective from', lambda x: x.valid_from, {'datetime': True}),
                    ('Set by', lambda x: x.user),
                    ('Date set', lambda x: x.modified, {'datetime': True}),
                    ('', lambda x: x.valid_from <= datetime.utcnow() and " " or \
                                   make_link(url = 'delete?item=%s&id=%s' % (x.config_item.id, x.id), text = 'Delete')),
                 ])

    def __init__(self, *args, **kw):
        kw['search_url'] = url("/configuration/by_name?anywhere=1"),
        kw['search_name'] = 'name'
        super(Configuration, self).__init__(*args, **kw)

        self.search_col = ConfigItem.name
        self.search_mapper = ConfigItem

    @identity.require(identity.in_group("admin"))
    @expose(template='bkr.server.templates.config_edit')
    def edit(self, **kw):
        if kw.get('id'):
            item = ConfigItem.by_id(kw['id'])
            form_values = dict(id=item.id,
                               numeric=item.numeric,
                               value=item.current_value())
        else:
            flash(_(u"Error: No item ID specified"))
            raise redirect(".")

        # Show all future values, and the previous five
        config_values = item.values().filter(item.value_class.valid_from > datetime.utcnow()).order_by(item.value_class.valid_from.desc()).all() \
                      + item.values().filter(item.value_class.valid_from <= datetime.utcnow()).order_by(item.value_class.valid_from.desc())[:5]

        if item.readonly:
            form = None
        elif item.numeric:
            form = self.int_form
        else:
            form = self.string_form

        return dict(
            title=item.name,
            subtitle=item.description,
            form=form,
            action='./save',
            options={},
            value=form_values,
            list=config_values,
            grid=self.value_grid,
            warn_msg=item.readonly and "This item is read-only",
        )

    @expose()
    @error_handler(edit)
    @identity.require(identity.in_group("admin"))
    def save(self, **kw):
        if 'id' in kw and kw['id']:
            item = ConfigItem.by_id(kw['id'])
        else:
            flash(_(u"Error: No item ID"))
            raise redirect(".")
        if kw['valid_from']:
            try:
                valid_from = datetime.strptime(kw['valid_from'],
                                               '%Y-%m-%d %H:%M')
            except ValueError:
                flash(
                    _(u"Invalid date and time specification, use: YYYY-MM-DD HH:MM"
                      ))
                raise redirect("/configuration/edit?id=%d" % item.id)
        else:
            valid_from = None

        try:
            item.set(kw['value'], valid_from, identity.current.user)
        except Exception, msg:
            flash(_(u"Failed to save setting: %s" % msg))
            raise redirect("/configuration/edit?id=%d" % item.id)

        flash(_(u"%s saved" % item.name))
        redirect(".")
Exemplo n.º 11
0
class Recipes(RPCRoot):
    # For XMLRPC methods in this class.
    exposed = True

    hidden_id = widgets.HiddenField(name='id')
    confirm = widgets.Label(
        name='confirm', default="Are you sure you want to release the system?")
    return_reservation_form = widgets.TableForm(
        'end_recipe_reservation',
        fields=[hidden_id, confirm],
        action='./really_return_reservation',
        submit_text=_(u'Yes'))

    tasks = RecipeTasks()

    recipe_widget = RecipeWidget()

    log_types = dict(
        R=LogRecipe,
        T=LogRecipeTask,
        E=LogRecipeTaskResult,
    )

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def by_log_server(self, server, limit=50):
        """
        Returns a list of recipe IDs which have logs stored on the given 
        server. By default, returns at most 50 at a time.

        Only returns recipes where the whole recipe set has completed. Also 
        excludes recently completed recipe sets, since the system may continue 
        uploading logs for a short while until beaker-provision powers it off.
        """
        finish_threshold = datetime.utcnow() - timedelta(minutes=2)
        recipes = Recipe.query.join(Recipe.recipeset)\
                .filter(RecipeSet.status.in_([s for s in TaskStatus if s.finished]))\
                .filter(not_(RecipeSet.recipes.any(Recipe.finish_time >= finish_threshold)))\
                .filter(Recipe.log_server == server)\
                .limit(limit)
        return [recipe_id for recipe_id, in recipes.values(Recipe.id)]

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def register_file(self, server, recipe_id, path, filename, basepath):
        """
        register file and return path to store
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        if recipe.is_finished():
            raise BX('Cannot register file for finished recipe %s' %
                     recipe.t_id)

        # Add the log to the DB if it hasn't been recorded yet.
        log_recipe = LogRecipe.lazy_create(
            recipe_id=recipe.id,
            path=path,
            filename=filename,
        )
        log_recipe.server = server
        log_recipe.basepath = basepath
        # Pull log_server out of server_url.
        recipe.log_server = urlparse.urlparse(server)[1]
        return '%s' % recipe.filepath

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def files(self, recipe_id):
        """
        Return an array of logs for the given recipe.

        :param recipe_id: id of recipe
        :type recipe_id: integer

        .. deprecated:: 0.9.4
           Use :meth:`taskactions.files` instead.
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        return [log for log in recipe.all_logs]

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def change_files(self, recipe_id, server, basepath):
        """
        Change the server and basepath where the log files lives, Usually
         used to move from lab controller cache to archive storage.
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        for mylog in recipe.all_logs:
            myserver = '%s/%s/' % (server, mylog['filepath'])
            mybasepath = '%s/%s/' % (basepath, mylog['filepath'])
            self.change_file(mylog['tid'], myserver, mybasepath)
        recipe.log_server = urlparse.urlparse(server)[1]
        return True

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def change_file(self, tid, server, basepath):
        """
        Change the server and basepath where the log file lives, Usually
         used to move from lab controller cache to archive storage.
        """
        log_type, log_id = tid.split(":")
        if log_type.upper() in self.log_types.keys():
            try:
                mylog = self.log_types[log_type.upper()].by_id(log_id)
            except InvalidRequestError:
                raise BX(_("Invalid %s" % tid))
        mylog.server = server
        mylog.basepath = basepath
        return True

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def extend(self, recipe_id, kill_time):
        """
        Extend recipe watchdog by kill_time seconds
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        return recipe.extend(kill_time)

    @cherrypy.expose
    def console_output(self, recipe_id, output_length=None, offset=None):
        """
        Get text console log output from OpenStack 
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        manager = dynamic_virt.VirtManager(recipe.recipeset.job.owner)
        return manager.get_console_output(recipe.resource.instance_id,
                                          output_length)

    @cherrypy.expose
    def watchdog(self, recipe_id):
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        return recipe.status_watchdog()

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def stop(self, recipe_id, stop_type, msg=None):
        """
        Set recipe status to Completed
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        if stop_type not in recipe.stop_types:
            raise BX(
                _('Invalid stop_type: %s, must be one of %s' %
                  (stop_type, recipe.stop_types)))
        kwargs = dict(msg=msg)
        return getattr(recipe, stop_type)(**kwargs)

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def install_start(self, recipe_id=None):
        """
        Report comencement of provisioning of a recipe's resource, extend
        first task's watchdog, and report 'Install Started' against it.
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_("Invalid Recipe ID %s" % recipe_id))

        first_task = recipe.first_task
        if not recipe.resource.install_started:
            recipe.resource.install_started = datetime.utcnow()
            # extend watchdog by 3 hours 60 * 60 * 3
            kill_time = 10800
            # XXX In future releases where 'Provisioning'
            # is a valid recipe state, we will no longer
            # need the following block.
            log.debug('Extending watchdog for %s', first_task.t_id)
            first_task.extend(kill_time)
            log.debug('Recording /start for %s', first_task.t_id)
            first_task.pass_(path=u'/start',
                             score=0,
                             summary=u'Install Started')
            return True
        else:
            log.debug('Already recorded /start for %s', first_task.t_id)
            return False

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def postinstall_done(self, recipe_id=None):
        """
        Report completion of postinstallation
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_(u'Invalid Recipe ID %s' % recipe_id))
        recipe.resource.postinstall_finished = datetime.utcnow()
        return True

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def install_done(self, recipe_id=None, fqdn=None):
        """
        Report completion of installation with current FQDN
        """
        if not recipe_id:
            raise BX(_("No recipe id provided!"))
        if not fqdn:
            raise BX(_("No fqdn provided!"))

        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_("Invalid Recipe ID %s" % recipe_id))

        recipe.resource.install_finished = datetime.utcnow()
        # We don't want to change an existing FQDN, just set it
        # if it hasn't been set already (see BZ#879146)
        configured = recipe.resource.fqdn
        if configured is None:
            recipe.resource.fqdn = configured = fqdn
        elif configured != fqdn:
            # We use eager formatting here to make this easier to test
            log.info("Configured FQDN (%s) != reported FQDN (%s) in R:%s" %
                     (configured, fqdn, recipe_id))
        return configured

    @identity.require(identity.not_anonymous())
    @expose()
    def really_return_reservation(self, id, msg=None):
        try:
            recipe = Recipe.by_id(id)
        except InvalidRequestError:
            raise BX(_("Invalid Recipe ID %s" % id))
        recipe.return_reservation()

        flash(_(u"Successfully released reserved system for %s" % recipe.t_id))
        redirect('/jobs/mine')

    @expose(template="bkr.server.templates.form")
    @identity.require(identity.not_anonymous())
    def return_reservation(self, recipe_id=None):
        """
        End recipe reservation
        """
        if not recipe_id:
            raise BX(_("No recipe id provided!"))

        return dict(
            title='Release reserved system for Recipe %s' % recipe_id,
            form=self.return_reservation_form,
            action='./really_return_reservation',
            options={},
            value=dict(id=recipe_id),
        )

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def postreboot(self, recipe_id=None):
        # Backwards compat only, delete this after 0.10:
        # the recipe_id arg used to be hostname
        try:
            int(recipe_id)
        except ValueError:
            system = System.by_fqdn(recipe_id, identity.current.user)
            system.action_power('reboot', service=u'XMLRPC', delay=30)
            return system.fqdn

        try:
            recipe = Recipe.by_id(int(recipe_id))
        except (InvalidRequestError, NoResultFound, ValueError):
            raise BX(_('Invalid recipe ID %s') % recipe_id)
        if isinstance(recipe.resource, SystemResource):
            recipe.resource.system.action_power('reboot',
                                                service=u'XMLRPC',
                                                delay=30)
        return True

    @cherrypy.expose
    def to_xml(self, recipe_id=None):
        """ 
            Pass in recipe id and you'll get that recipe's xml
        """
        if not recipe_id:
            raise BX(_("No recipe id provided!"))
        try:
            recipexml = Recipe.by_id(recipe_id).to_xml().toprettyxml()
        except InvalidRequestError:
            raise BX(_("Invalid Recipe ID %s" % recipe_id))
        return recipexml

    def _recipe_search(self, recipe, **kw):
        recipe_search = search_utility.Recipe.search(recipe)
        for search in kw['recipesearch']:
            col = search['table']
            try:
                recipe_search.append_results(search['value'], col,
                                             search['operation'], **kw)
            except KeyError, e:
                log.error(e)
                return recipe_search.return_results()

        return recipe_search.return_results()
Exemplo n.º 12
0
class RetentionTag(AdminPage):
    exposed = False

    tag = widgets.TextField(name='tag', label=_(u'Tag'))
    default = widgets.SingleSelectField(name='default', label=(u'Default'),
            options=[(0,'False'),(1,'True')])
    id = widgets.HiddenField(name='id') 
    expire_in_days = widgets.TextField(name='expire_in_days', label=_(u'Expire In Days'),
            help_text=_(u'Number of days after which jobs will expire'))
    needs_product = widgets.CheckBox('needs_product', label=u'Needs Product')

    tag_form = HorizontalForm(
        'Retention Tag',
        fields = [tag, default, expire_in_days, needs_product, id],
        action = 'save_data',
        submit_text = _(u'Save'),
    )

    def __init__(self,*args,**kw):
        kw['search_url'] =  url("/retentiontag/by_tag")
        kw['search_name'] = 'tag'
        kw['widget_action'] = './admin'
        super(RetentionTag,self).__init__(*args,**kw)

        self.search_col = Tag.tag
        self.search_mapper = Tag 

    @identity.require(identity.in_group("admin"))
    @expose(template='bkr.server.templates.form')
    def new(self, **kw):
        return dict(
            form = self.tag_form,
            action = './save',
            options = {},
            value = kw,
        )

    @identity.require(identity.in_group("admin"))
    @expose()
    @validate(form=tag_form, validators=TagFormSchema())
    @error_handler(new)
    def save(self, id=None, **kw):
        retention_tag = Tag(kw['tag'], kw['default'], kw['needs_product'])
        retention_tag.expire_in_days = kw['expire_in_days']
        session.add(retention_tag)
        flash(_(u"OK"))
        redirect("./admin")

    @expose(format='json')
    def by_tag(self, input, *args, **kw):
        input = input.lower()
        search = Tag.list_by_tag(input)
        tags = [match.tag for match in search]
        return dict(matches=tags)

    @expose(template="bkr.server.templates.admin_grid")
    @identity.require(identity.in_group('admin'))
    @paginate('list', default_order='tag', limit=20)
    def admin(self, *args, **kw):
        tags = self.process_search(*args, **kw)
        alpha_nav_data = set([elem.tag[0].capitalize() for elem in tags])
        nav_bar = self._build_nav_bar(alpha_nav_data,'tag')
        template_data = self.tags(tags, identity.current.user, *args, **kw)
        template_data['alpha_nav_bar'] = nav_bar
        template_data['addable'] = True
        return template_data

    @identity.require(identity.in_group('admin'))
    @expose()
    def delete(self, id):
        tag = Tag.by_id(id)
        if not tag.can_delete(): # Trying to be funny...
            flash(u'%s is not applicable for deletion' % tag.tag)
            redirect('/retentiontag/admin')
        session.delete(tag)
        flash(u'Successfully deleted %s' % tag.tag)
        redirect('/retentiontag/admin')

    @identity.require(identity.in_group("admin"))
    @expose(template='bkr.server.templates.form')
    def edit(self, id, **kw):
        tag = Tag.by_id(id) 
        return dict(
            form = self.tag_form,
            title=_(u'Retention tag %s' % tag.tag),
            action = './save_edit',
            options = {},
            value = tag,
            disabled_fields = ['tag']
        )

    @identity.require(identity.in_group("admin"))
    @expose()
    @validate(form=tag_form, validators=TagFormSchema())
    @error_handler(edit)
    def save_edit(self, id=None, **kw):
        retention_tag = Tag.by_id(id)
        retention_tag.tag = kw['tag']
        retention_tag.default = kw['default']
        retention_tag.expire_in_days = kw['expire_in_days']
        retention_tag.needs_product = kw['needs_product']
        flash(_(u"OK"))
        redirect("./admin")

    @expose(template="bkr.server.templates.grid")
    @paginate('list', default_order='tag', limit=20)
    def index(self, *args, **kw):
        return self.tags()

    def tags(self, tags=None, user=None, *args, **kw):
        if tags is None:
            tags = Tag.get_all()

        def show_delete(x):
            if x.can_delete():
                return XML('<a class="btn" href="./delete/%s">'
                        '<i class="fa fa-times"/> Delete</a>' % x.id)
            else:
                return None

        def show_tag(x):
            if x.is_default: #If we are the default, we can't change to not default
                return x.tag
            elif user and user.is_admin():
                return make_edit_link(x.tag,x.id)
            else:  #no perms to edit
                return x.tag

        my_fields = [myPaginateDataGrid.Column(name='tag', title='Tags', getter=lambda x: show_tag(x),options=dict(sortable=True)),
                     myPaginateDataGrid.Column(name='default', title='Default', getter=lambda x: x.default,options=dict(sortable=True)),
                     myPaginateDataGrid.Column(name='delete', title='Delete', getter=lambda x: show_delete(x))]
        tag_grid = myPaginateDataGrid(fields=my_fields, add_action='./new')
        return_dict = dict(title='Tags',
                           grid = tag_grid,
                           search_bar = None,
                           search_widget = self.search_widget_form,
                           list = tags)
        return return_dict
Exemplo n.º 13
0
class Groups(AdminPage):
    # For XMLRPC methods in this class.
    exposed = True
    group_id = widgets.HiddenField(name='group_id')
    auto_users = AutoCompleteField(name='user',
                                   search_controller=url("/users/by_name"),
                                   search_param="input",
                                   result_name="matches")
    auto_systems = AutoCompleteField(name='system',
                                     search_controller=url("/by_fqdn"),
                                     search_param="input",
                                     result_name="matches")

    search_groups = AutoCompleteField(
        name='group',
        search_controller=url("/groups/by_name?anywhere=1"),
        search_param="name",
        result_name="groups")

    search_permissions = AutoCompleteField(
        name='permissions',
        search_controller=url("/groups/get_permissions"),
        search_param="input",
        result_name="matches")

    group_form = GroupForm()

    permissions_form = InlineRemoteForm(
        'Permissions',
        fields=[search_permissions, group_id],
        submit_text=_(u'Add'),
        on_success='add_group_permission_success(http_request.responseText)',
        on_failure='add_group_permission_failure(http_request.responseText)',
        before='before_group_permission_submit()',
        after='after_group_permission_submit()',
    )

    group_user_form = InlineForm(
        'GroupUser',
        fields=[group_id, auto_users],
        action='save_data',
        submit_text=_(u'Add'),
    )

    group_system_form = InlineForm(
        'GroupSystem',
        fields=[group_id, auto_systems],
        action='save_data',
        submit_text=_(u'Add'),
    )

    delete_link = DeleteLinkWidgetForm()

    def __init__(self, *args, **kw):
        kw['search_url'] = url("/groups/by_name?anywhere=1")
        kw['search_name'] = 'group'
        kw['widget_action'] = ''
        super(Groups, self).__init__(*args, **kw)

        self.search_col = Group.group_name
        self.search_mapper = Group

    @expose(format='json')
    def by_name(self, input, *args, **kw):
        input = input.lower()
        if 'anywhere' in kw:
            search = Group.list_by_name(input, find_anywhere=True)
        else:
            search = Group.list_by_name(input)

        groups = [match.group_name for match in search]
        return dict(matches=groups)

    @expose(format='json')
    @identity.require(identity.not_anonymous())
    def remove_group_permission(self, group_id, permission_id):
        try:
            group = Group.by_id(group_id)
        except DatabaseLookupError:
            log.exception('Group id %s is not a valid Group to remove' %
                          group_id)
            return ['0']

        if not group.can_edit(identity.current.user):
            log.exception(
                'User %d does not have edit permissions for Group id %s' %
                (identity.current.user.user_id, group_id))
            response.status = 403
            return ['You are not an owner of group %s' % group]

        try:
            permission = Permission.by_id(permission_id)
        except NoResultFound:
            log.exception(
                'Permission id %s is not a valid Permission to remove' %
                permission_id)
            return ['0']
        group.permissions.remove(permission)
        return ['1']

    @expose(format='json')
    def get_permissions(self, input):
        results = Permission.by_name(input, anywhere=True)
        permission_names = [result.permission_name for result in results]
        return dict(matches=permission_names)

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

    def show_members(self, group):
        can_edit = False
        if identity.current.user:
            can_edit = group.can_modify_membership(identity.current.user)

        def show_ownership_status(member):
            is_owner = member.is_owner
            if can_edit:
                if is_owner:
                    return XML('<a class="btn change_ownership_remove" '
                               'href="revoke_owner?group_id=%s&amp;id=%s">'
                               '<i class="fa fa-times"/> Remove</a>' %
                               (group.group_id, member.user_id))
                else:
                    return XML('<a class="btn change_ownership_add" '
                               'href="grant_owner?group_id=%s&amp;id=%s">'
                               '<i class="fa fa-plus"/> Add</a>' %
                               (group.group_id, member.user_id))
            else:
                is_owner = 'Yes' if is_owner else 'No'
                return is_owner

        def remove_button(member):
            return XML(
                '<a class="btn" href="removeUser?group_id=%s&amp;id=%s">'
                '<i class="fa fa-times"/> Remove</a>' %
                (group.group_id, member.user_id))

        user_fields = [('User', lambda x: x.user.user_name)]

        user_fields.append(('Group Ownership', show_ownership_status))
        if can_edit:
            user_fields.append(('Group Membership', remove_button))

        return BeakerDataGrid(name='group_members_grid', fields=user_fields)

    @expose(template='bkr.server.templates.grid')
    @paginate('list', default_order='fqdn', limit=20, max_limit=None)
    def systems(self, group_id=None, *args, **kw):
        try:
            group = Group.by_id(group_id)
        except DatabaseLookupError:
            log.exception('Group id %s is not a valid group id' % group_id)
            flash(_(u'Need a valid group to search on'))
            redirect('../groups/mine')

        systems = System.all(identity.current.user). \
                  filter(System.groups.contains(group)). \
                  filter(System.status != SystemStatus.removed)
        title = 'Systems in Group %s' % group.group_name
        from bkr.server.controllers import Root
        return Root()._systems(systems, title, group_id=group_id, **kw)

    @expose(template='bkr.server.templates.group_form')
    def edit(self, group_id=None, group_name=None, **kw):
        # Not just for editing, also provides a read-only view
        if group_id is not None:
            try:
                group = Group.by_id(group_id)
            except DatabaseLookupError:
                log.exception('Group id %s is not a valid group id' % group_id)
                flash(_(u'Need a valid group to search on'))
                redirect('../groups/mine')
        elif group_name is not None:
            try:
                group = Group.by_name(group_name)
            except NoResultFound:
                log.exception('Group name %s is not a valid group name' %
                              group_name)
                flash(_(u'Need a valid group to search on'))
                redirect('../groups/mine')
        else:
            redirect('../groups/mine')

        usergrid = self.show_members(group)

        can_edit = False
        if identity.current.user:
            can_edit = group.can_edit(identity.current.user)

        systems_fields = [('System', lambda x: x.link)]
        if can_edit:
            system_remove_widget = DeleteLinkWidgetForm(
                action='removeSystem',
                hidden_fields=[
                    widgets.HiddenField(name='group_id'),
                    widgets.HiddenField(name='id')
                ],
                action_text=u'Remove')
            systems_fields.append((' ', lambda x: system_remove_widget.display(
                dict(group_id=group_id, id=x.id))))
        systemgrid = BeakerDataGrid(fields=systems_fields)

        permissions_fields = [('Permission', lambda x: x.permission_name)]
        if can_edit:
            permissions_fields.append((' ', lambda x: XML(
                '<a class="btn" href="#" id="remove_permission_%s">'
                '<i class="fa fa-times"/> Remove</a>' % x.permission_id)))
        group_permissions_grid = BeakerDataGrid(name='group_permission_grid',
                                                fields=permissions_fields)
        group_permissions = GroupPermissions()

        return dict(
            form=self.group_form,
            system_form=self.group_system_form,
            user_form=self.group_user_form,
            group_edit_js=LocalJSLink('bkr',
                                      '/static/javascript/group_users_v2.js'),
            action='./save',
            system_action='./save_system',
            user_action='./save_user',
            options={},
            value=group,
            group_pw=group.root_password,
            usergrid=usergrid,
            systemgrid=systemgrid,
            disabled_fields=[],
            group_permissions=group_permissions,
            group_form=self.permissions_form,
            group_permissions_grid=group_permissions_grid,
        )

    def _new_group(self, group_id, display_name, group_name, ldap,
                   root_password):
        user = identity.current.user
        if ldap and not user.is_admin():
            flash(_(u'Only admins can create LDAP groups'))
            redirect('.')
        try:
            Group.by_name(group_name)
        except NoResultFound:
            pass
        else:
            flash(_(u"Group %s already exists." % group_name))
            redirect(".")

        group = Group()
        session.add(group)
        group.record_activity(user=user,
                              service=u'WEBUI',
                              field=u'Group',
                              action=u'Created')
        group.display_name = display_name
        group.group_name = group_name
        group.ldap = ldap
        if group.ldap:
            group.refresh_ldap_members()
        group.root_password = root_password
        if not ldap:  # LDAP groups don't have owners
            group.user_group_assocs.append(UserGroup(user=user, is_owner=True))
            group.activity.append(
                GroupActivity(user,
                              service=u'WEBUI',
                              action=u'Added',
                              field_name=u'User',
                              old_value=None,
                              new_value=user.user_name))
            group.activity.append(
                GroupActivity(user,
                              service=u'WEBUI',
                              action=u'Added',
                              field_name=u'Owner',
                              old_value=None,
                              new_value=user.user_name))
        return group

    @expose()
    @validate(form=group_form)
    @error_handler(new)
    @identity.require(identity.not_anonymous())
    def save_new(self,
                 group_id=None,
                 display_name=None,
                 group_name=None,
                 ldap=False,
                 root_password=None,
                 **kwargs):
        # save_new() is needed because 'edit' is not a viable
        # error handler for new groups.
        self._new_group(group_id, display_name, group_name, ldap,
                        root_password)
        flash(_(u"OK"))
        redirect("mine")

    @expose()
    @validate(form=group_form)
    @error_handler(edit)
    @identity.require(identity.not_anonymous())
    def save(self,
             group_id=None,
             display_name=None,
             group_name=None,
             ldap=False,
             root_password=None,
             **kwargs):

        user = identity.current.user

        if ldap and not user.is_admin():
            flash(_(u'Only admins can create LDAP groups'))
            redirect('mine')

        try:
            group = Group.by_id(group_id)
        except DatabaseLookupError:
            flash(_(u"Group %s does not exist." % group_id))
            redirect('mine')

        try:
            Group.by_name(group_name)
        except NoResultFound:
            pass
        else:
            if group_name != group.group_name:
                flash(
                    _(u'Failed to update group %s: Group name already exists: %s'
                      % (group.group_name, group_name)))
                redirect('mine')

        if not group.can_edit(user):
            flash(_(u'You are not an owner of group %s' % group))
            redirect('../groups/mine')

        try:
            group.set_name(user, u'WEBUI', group_name)
            group.set_display_name(user, u'WEBUI', display_name)
            group.ldap = ldap
            group.set_root_password(user, u'WEBUI', root_password)
        except BeakerException, err:
            session.rollback()
            flash(_(u'Failed to update group %s: %s' %
                    (group.group_name, err)))
            redirect('.')

        flash(_(u"OK"))
        redirect("mine")
Exemplo n.º 14
0
    def edit(self, group_id=None, group_name=None, **kw):
        # Not just for editing, also provides a read-only view
        if group_id is not None:
            try:
                group = Group.by_id(group_id)
            except DatabaseLookupError:
                log.exception('Group id %s is not a valid group id' % group_id)
                flash(_(u'Need a valid group to search on'))
                redirect('../groups/mine')
        elif group_name is not None:
            try:
                group = Group.by_name(group_name)
            except NoResultFound:
                log.exception('Group name %s is not a valid group name' %
                              group_name)
                flash(_(u'Need a valid group to search on'))
                redirect('../groups/mine')
        else:
            redirect('../groups/mine')

        usergrid = self.show_members(group)

        can_edit = False
        if identity.current.user:
            can_edit = group.can_edit(identity.current.user)

        systems_fields = [('System', lambda x: x.link)]
        if can_edit:
            system_remove_widget = DeleteLinkWidgetForm(
                action='removeSystem',
                hidden_fields=[
                    widgets.HiddenField(name='group_id'),
                    widgets.HiddenField(name='id')
                ],
                action_text=u'Remove')
            systems_fields.append((' ', lambda x: system_remove_widget.display(
                dict(group_id=group_id, id=x.id))))
        systemgrid = BeakerDataGrid(fields=systems_fields)

        permissions_fields = [('Permission', lambda x: x.permission_name)]
        if can_edit:
            permissions_fields.append((' ', lambda x: XML(
                '<a class="btn" href="#" id="remove_permission_%s">'
                '<i class="fa fa-times"/> Remove</a>' % x.permission_id)))
        group_permissions_grid = BeakerDataGrid(name='group_permission_grid',
                                                fields=permissions_fields)
        group_permissions = GroupPermissions()

        return dict(
            form=self.group_form,
            system_form=self.group_system_form,
            user_form=self.group_user_form,
            group_edit_js=LocalJSLink('bkr',
                                      '/static/javascript/group_users_v2.js'),
            action='./save',
            system_action='./save_system',
            user_action='./save_user',
            options={},
            value=group,
            group_pw=group.root_password,
            usergrid=usergrid,
            systemgrid=systemgrid,
            disabled_fields=[],
            group_permissions=group_permissions,
            group_form=self.permissions_form,
            group_permissions_grid=group_permissions_grid,
        )
Exemplo n.º 15
0
class KeyTypes(AdminPage):
    # For XMLRPC methods in this class.
    exposed = False

    id         = widgets.HiddenField(name='id')
    key_name   = widgets.TextField(name='key_name', label=_(u'Name'))
    numeric    = widgets.CheckBox(name='numeric', label=_(u'Numeric'))

    form = HorizontalForm(
        'keytypes',
        fields = [id, key_name, numeric],
        action = 'save_data',
        submit_text = _(u'Submit Data'),
    )

    
    def __init__(self,*args,**kw):
        kw['search_url'] =  url("/keytypes/by_name?anywhere=1"),
        kw['search_name'] = 'key'
        super(KeyTypes,self).__init__(*args,**kw)

        self.search_col = Key.key_name
        self.search_mapper = Key
    
    @expose(template='bkr.server.templates.form')
    def new(self, **kw):
        return dict(
            title=_(u'New Key Type'),
            form = self.form,
            action = './save',
            options = {},
            value = kw,
        )

    @expose(template='bkr.server.templates.form')
    def edit(self,**kw):
        values = []
        if kw.get('id'):
            key = Key.by_id(kw['id'])
            values = dict(
                id         = key.id,
                key_name   = key.key_name,
                numeric    = key.numeric
            )
        
        return dict(
            form = self.form,
            action = './save',
            options = {},
            value = values,
        )
    
    @expose()
    @error_handler(edit)
    def save(self, **kw):
        if kw['id']:
            key = Key.by_id(kw['id'])
            key.key_name = kw['key_name']
        else:
            key = Key(key_name=kw['key_name'])
            session.add(key)
        if 'numeric' in kw:
            key.numeric = kw['numeric']
        flash( _(u"OK") )
        redirect(".")

    @expose(template="bkr.server.templates.admin_grid")
    @paginate('list')
    def index(self,*args,**kw):
        keytypes = session.query(Key) 
        list_by_letters = set([elem.key_name[0].capitalize() for elem in keytypes])
        results = self.process_search(**kw)
        if results:
            keytypes = results.order_by(Key.key_name)
        keytypes_grid = myPaginateDataGrid(fields=[
                                  ('Key', lambda x: make_edit_link(x.key_name, x.id)),
                                  ('Numeric', lambda x: x.numeric),
                                  (' ', lambda x: make_remove_link(x.id)),
                              ],
                              add_action='./new')
        return dict(title="Key Types", 
                    grid = keytypes_grid, 
                    search_widget = self.search_widget_form,
                    alpha_nav_bar = AlphaNavBar(list_by_letters,self.search_name),
                    list = keytypes)

    @expose()
    def remove(self, **kw):
        remove = Key.by_id(kw['id'])
        session.delete(remove)
        flash( _(u"%s Deleted") % remove.key_name )
        raise redirect(".")

    @expose(format='json')
    def by_name(self,input,*args,**kw):
        if 'anywhere' in kw:
            search = Key.list_by_name(input,find_anywhere=True)
        else:
            search = Key.list_by_name(input)

        keys = [elem.key_name for elem in search]
        return dict(matches=keys)
Exemplo n.º 16
0
class Recipes(RPCRoot):
    # For XMLRPC methods in this class.
    exposed = True

    hidden_id = widgets.HiddenField(name='id')
    confirm = widgets.Label(
        name='confirm', default="Are you sure you want to release the system?")
    return_reservation_form = widgets.TableForm(
        'end_recipe_reservation',
        fields=[hidden_id, confirm],
        action='./really_return_reservation',
        submit_text=_(u'Yes'))

    tasks = RecipeTasks()

    recipe_widget = RecipeWidget()

    log_types = dict(
        R=LogRecipe,
        T=LogRecipeTask,
        E=LogRecipeTaskResult,
    )

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def by_log_server(self, server, limit=50):
        """
        Returns a list of recipe IDs which have logs stored on the given 
        server. By default, returns at most 50 at a time.

        Only returns recipes where the whole recipe set has completed. Also 
        excludes recently completed recipe sets, since the system may continue 
        uploading logs for a short while until beaker-provision powers it off.
        """
        finish_threshold = datetime.utcnow() - timedelta(minutes=2)
        recipes = Recipe.query.join(Recipe.recipeset)\
                .join(RecipeSet.job)\
                .filter(not_(Job.is_deleted))\
                .filter(RecipeSet.status.in_([s for s in TaskStatus if s.finished]))\
                .filter(not_(RecipeSet.recipes.any(Recipe.finish_time >= finish_threshold)))\
                .filter(Recipe.log_server == server)\
                .limit(limit)
        return [recipe_id for recipe_id, in recipes.values(Recipe.id)]

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def register_file(self, server, recipe_id, path, filename, basepath):
        """
        register file and return path to store
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        if recipe.is_finished():
            raise BX('Cannot register file for finished recipe %s' %
                     recipe.t_id)

        # Add the log to the DB if it hasn't been recorded yet.
        log_recipe = LogRecipe.lazy_create(
            recipe_id=recipe.id,
            path=path,
            filename=filename,
        )
        log_recipe.server = server
        log_recipe.basepath = basepath
        # Pull log_server out of server_url.
        recipe.log_server = urlparse.urlparse(server)[1]
        return '%s' % recipe.filepath

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def files(self, recipe_id):
        """
        Return an array of logs for the given recipe.

        :param recipe_id: id of recipe
        :type recipe_id: integer

        .. deprecated:: 0.9.4
           Use :meth:`taskactions.files() <bkr.server.task_actions.taskactions.files>` instead.
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        # Build a list of logs excluding duplicate paths, to mitigate:
        # https://bugzilla.redhat.com/show_bug.cgi?id=963492
        logdicts = []
        seen_paths = set()
        for log in recipe.all_logs():
            logdict = log.dict
            # The path we care about here is the path which beaker-transfer
            # will move the file to.
            # Don't be tempted to use os.path.join() here since log['path']
            # is often '/' which does not give the result you would expect.
            path = os.path.normpath(
                '%s/%s/%s' %
                (logdict['filepath'], logdict['path'], logdict['filename']))
            if path in seen_paths:
                logger.warn('%s contains duplicate log %s', log.parent.t_id,
                            path)
            else:
                seen_paths.add(path)
                logdicts.append(logdict)
        return logdicts

    @cherrypy.expose
    @identity.require(identity.in_group('lab_controller'))
    def change_files(self, recipe_id, server, basepath):
        """
        Change the server and basepath where the log files lives, Usually
         used to move from lab controller cache to archive storage.
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        for mylog in recipe.all_logs():
            mylog.server = '%s/%s/' % (server, mylog.parent.filepath)
            mylog.basepath = '%s/%s/' % (basepath, mylog.parent.filepath)
        recipe.log_server = urlparse.urlparse(server)[1]
        return True

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def extend(self, recipe_id, kill_time):
        """
        Extend recipe watchdog by kill_time seconds
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        return recipe.extend(kill_time)

    @cherrypy.expose
    def console_output(self, recipe_id, output_length=None, offset=None):
        """
        Get text console log output from OpenStack 
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        manager = dynamic_virt.VirtManager(recipe.recipeset.job.owner)
        return manager.get_console_output(recipe.resource.instance_id,
                                          output_length)

    @cherrypy.expose
    def watchdog(self, recipe_id):
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        return recipe.status_watchdog()

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def stop(self, recipe_id, stop_type, msg=None):
        """
        Set recipe status to Completed
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_('Invalid recipe ID: %s' % recipe_id))
        if stop_type not in recipe.stop_types:
            raise BX(
                _('Invalid stop_type: %s, must be one of %s' %
                  (stop_type, recipe.stop_types)))
        kwargs = dict(msg=msg)
        return getattr(recipe, stop_type)(**kwargs)

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def install_start(self, recipe_id=None):
        """
        Records the start of a recipe's installation. The watchdog is extended 
        by 3 hours to allow the installation to complete.
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_("Invalid Recipe ID %s" % recipe_id))
        if not recipe.installation:
            raise BX(_('Recipe %s not provisioned yet') % recipe_id)

        installation = recipe.installation
        if not installation.install_started:
            installation.install_started = datetime.utcnow()
            # extend watchdog by 3 hours 60 * 60 * 3
            kill_time = 10800
            logger.debug('Extending watchdog for %s', recipe.t_id)
            recipe.extend(kill_time)
            return True
        else:
            logger.debug('Already recorded install_started for %s',
                         recipe.t_id)
            return False

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def postinstall_done(self, recipe_id=None):
        """
        Report completion of postinstallation
        """
        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_(u'Invalid Recipe ID %s' % recipe_id))
        if not recipe.installation:
            raise BX(_('Recipe %s not provisioned yet') % recipe_id)
        recipe.installation.postinstall_finished = datetime.utcnow()
        return True

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def install_done(self, recipe_id=None, fqdn=None):
        """
        Report completion of installation with current FQDN
        """
        if not recipe_id:
            raise BX(_("No recipe id provided!"))

        try:
            recipe = Recipe.by_id(recipe_id)
        except InvalidRequestError:
            raise BX(_("Invalid Recipe ID %s" % recipe_id))
        if not recipe.installation:
            raise BX(_('Recipe %s not provisioned yet') % recipe_id)

        recipe.installation.install_finished = datetime.utcnow()
        # We don't want to change an existing FQDN, just set it
        # if it hasn't been set already (see BZ#879146)
        configured = recipe.resource.fqdn
        if configured is None and fqdn:
            recipe.resource.fqdn = configured = fqdn
        elif configured != fqdn:
            # We use eager formatting here to make this easier to test
            logger.info("Configured FQDN (%s) != reported FQDN (%s) in R:%s" %
                        (configured, fqdn, recipe_id))
        return configured

    @identity.require(identity.not_anonymous())
    @expose()
    def really_return_reservation(self, id, msg=None):
        try:
            recipe = Recipe.by_id(id)
        except InvalidRequestError:
            raise BX(_("Invalid Recipe ID %s" % id))
        recipe.return_reservation()

        flash(_(u"Successfully released reserved system for %s" % recipe.t_id))
        redirect('/jobs/mine')

    @expose(template="bkr.server.templates.form")
    @identity.require(identity.not_anonymous())
    def return_reservation(self, recipe_id=None):
        """
        End recipe reservation
        """
        if not recipe_id:
            raise BX(_("No recipe id provided!"))

        return dict(
            title='Release reserved system for Recipe %s' % recipe_id,
            form=self.return_reservation_form,
            action='./really_return_reservation',
            options={},
            value=dict(id=recipe_id),
        )

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def postreboot(self, recipe_id=None):
        # Backwards compat only, delete this after 0.10:
        # the recipe_id arg used to be hostname
        try:
            int(recipe_id)
        except ValueError:
            system = System.by_fqdn(recipe_id, identity.current.user)
            system.action_power('reboot', service=u'XMLRPC', delay=30)
            return system.fqdn

        try:
            recipe = Recipe.by_id(int(recipe_id))
        except (InvalidRequestError, NoResultFound, ValueError):
            raise BX(_('Invalid recipe ID %s') % recipe_id)
        if isinstance(recipe.resource, SystemResource):
            recipe.resource.system.action_power('reboot',
                                                service=u'XMLRPC',
                                                delay=30)
        return True

    @cherrypy.expose
    def to_xml(self, recipe_id=None):
        """ 
            Pass in recipe id and you'll get that recipe's xml
        """
        if not recipe_id:
            raise BX(_("No recipe id provided!"))
        try:
            recipexml = etree.tostring(Recipe.by_id(recipe_id).to_xml(),
                                       pretty_print=True,
                                       encoding='utf8')
        except InvalidRequestError:
            raise BX(_("Invalid Recipe ID %s" % recipe_id))
        return recipexml

    def _recipe_search(self, recipe, **kw):
        recipe_search = search_utility.Recipe.search(recipe)
        for search in kw['recipesearch']:
            col = search['table']
            try:
                recipe_search.append_results(search['value'], col,
                                             search['operation'], **kw)
            except KeyError, e:
                logger.error(e)
                return recipe_search.return_results()

        return recipe_search.return_results()
Exemplo n.º 17
0
class PowerTypes(AdminPage):
    # For XMLRPC methods in this class.
    exposed = False

    id = widgets.HiddenField(name='id')
    name = widgets.TextField(name='name', label=_(u'Name'))

    form = HorizontalForm(
        'powertypes',
        fields=[id, name],
        action='save_data',
        submit_text=_(u'Save'),
    )

    def __init__(self, *args, **kw):
        kw['search_url'] = url("/powertypes/by_name?anywhere=1"),
        kw['search_name'] = 'power'
        super(PowerTypes, self).__init__(*args, **kw)

        self.search_col = PowerType.name
        self.search_mapper = PowerType

    @identity.require(identity.in_group("admin"))
    @expose(template='bkr.server.templates.form')
    def new(self, **kw):
        return dict(
            form=self.form,
            title=_(u'New Power Type'),
            action='./save',
            options={},
            value=kw,
        )

    @identity.require(identity.in_group("admin"))
    @expose(template='bkr.server.templates.form')
    def edit(self, **kw):
        title = _(u'New Power Type')
        values = []
        if kw.get('id'):
            powertype = PowerType.by_id(kw['id'])
            title = powertype.name
            values = dict(
                id=powertype.id,
                name=powertype.name,
            )

        return dict(
            form=self.form,
            title=title,
            action='./save',
            options={},
            value=values,
        )

    @identity.require(identity.in_group("admin"))
    @expose()
    @error_handler(edit)
    def save(self, **kw):
        if kw['id']:
            edit = PowerType.by_id(kw['id'])
            edit.name = kw['name']
        elif kw.get('name'):
            new = PowerType(name=kw['name'])
            session.add(new)
        else:
            flash(_(u"Invalid Power Type entry"))
            redirect(".")
        flash(_(u"OK"))
        redirect(".")

    @expose(format='json')
    def by_name(self, input, *args, **kw):
        if 'anywhere' in kw:
            search = PowerType.list_by_name(input, find_anywhere=True)
        else:
            search = PowerType.list_by_name(input)

        powers = [elem.name for elem in search]
        return dict(matches=powers)

    @expose(template="bkr.server.templates.admin_grid")
    @paginate('list', default_order='name', limit=20)
    def index(self, *args, **kw):
        powertypes = session.query(PowerType)
        list_by_letters = set(
            [elem.name[0].capitalize() for elem in powertypes if elem.name])
        results = self.process_search(**kw)
        if results:
            powertypes = results
        can_edit = identity.current.user and identity.current.user.is_admin()
        powertypes_grid = myPaginateDataGrid(
            fields=[
                ('Power Type', lambda x: make_edit_link(x.name, x.id)
                 if can_edit else x.name),
                (' ', lambda x: make_remove_link(x.id) if can_edit else None),
            ],
            add_action='./new' if can_edit else None)

        return dict(title="Power Types",
                    grid=powertypes_grid,
                    search_widget=self.search_widget_form,
                    alpha_nav_bar=AlphaNavBar(list_by_letters, 'power'),
                    list=powertypes)

    @identity.require(identity.in_group("admin"))
    @expose()
    def remove(self, **kw):
        remove = PowerType.by_id(kw['id'])
        session.delete(remove)
        flash(_(u"%s Deleted") % remove.name)
        raise redirect(".")