Source code for bkr.server.distro

from turbogears.database import session
from turbogears import controllers, expose, flash, widgets, validate, error_handler, validators, redirect, paginate, url
from turbogears.widgets import AutoCompleteField, HiddenField
from turbogears import identity, redirect
from cherrypy import request, response
from kid import Element
from bkr.server.xmlrpccontroller import RPCRoot
from bkr.server.widgets import DistroTags, SearchBar
from bkr.server.widgets import TaskSearchForm
from bkr.server.widgets import myPaginateDataGrid
from bkr.server.model import System
from bkr.server.helpers import *
from bkr.server.controller_utilities import Utility
from bkr.server import search_utility 

import cherrypy

from BasicAuthTransport import BasicAuthTransport
import xmlrpclib

# from bkr.server import json
# import logging
# log = logging.getLogger("bkr.server.controllers")
#import model
from model import *
import string

__all__ = ['Distros']

class Distros(RPCRoot):
    # For XMLRPC methods in this class.
    exposed = True

    tag_form = DistroTags(name='tags')

    @expose(template="bkr.server.templates.distro")
    def view(self, id=None, *args, **kw):
        try:
            distro = Distro.by_id(id)
        except InvalidRequestError:
            flash(_(u"Invalid distro id %s" % id))
            redirect(".")
        is_admin = identity.current.user and identity.current.user.is_admin() or False
        task_form = TaskSearchForm(hidden=dict(distro=True, osmajor_id=True))
        return dict(title       = 'Distro',
                    value       = distro,
                    value_task  = dict(distro_id = distro.id),
                    form        = self.tag_form,
                    form_task   = task_form,
                    action      = './save_tag',
                    action_task = '/tasks/do_search',
                    options   = dict(tags = distro.tags,
                                    readonly = not is_admin))

    @expose()
    def get_osmajors(self, tags=None):
        """
        Returns a list of all distro families. If *tags* is given, limits to
        distros with at least one of the given tags.
        """
        osmajors = session.query(OSMajor.osmajor)
        if tags:
            osmajors = osmajors\
                .join(OSMajor.osversion, OSVersion.distros, Distro.trees)\
                .filter(DistroTree.lab_controller_assocs.any())\
                .filter(Distro._tags.any(DistroTag.tag.in_(tags)))
        return [osmajor for osmajor, in osmajors.distinct()]

    @expose()
    def get_osmajor(self, distro):
        """ pass in a distro name and get back the osmajor is belongs to.
        """
        try:
            osmajor = '%s' % Distro.by_name(distro).osversion.osmajor
        except AttributeError:
            raise BX(_('Invalid Distro: %s' % distro))
        return osmajor

    get_family = get_osmajor

    @expose()
    def get_arch(self, filter):
        """ pass in a dict() with either distro or osmajor to get possible arches
        """
        if 'distro' in filter:
            # look up distro
            try:
                arches = [arch.arch for arch in Distro.by_name(filter['distro']).osversion.arches]
            except AttributeError:
                raise BX(_('Invalid Distro: %s' % filter['distro']))
        elif 'osmajor' in filter:
            # look up osmajor
            try:
                arches = [arch.arch for arch in OSMajor.by_name(filter['osmajor']).osminor[0].arches]
            except InvalidRequestError:
                raise BX(_('Invalid OSMajor: %s' % filter['osmajor']))
        return arches

    @expose()
    @identity.require(identity.has_permission('tag_distro'))
    def save_tag(self, id=None, tag=None, *args, **kw):
        try:
            distro = Distro.by_id(id)
        except InvalidRequestError:
            flash(_(u"Invalid distro id %s" % id))
            redirect(".")
        if tag['text']:
            distro.tags.append(tag['text'])
            distro.activity.append(DistroActivity(
                    user=identity.current.user, service=u'WEBUI',
                    action=u'Added', field_name=u'Tag',
                    old_value=None, new_value=tag['text']))
        flash(_(u"Added Tag %s" % tag['text']))
        redirect("./view?id=%s" % id)

    @expose()
    @identity.require(identity.has_permission('tag_distro'))
    def tag_remove(self, id=None, tag=None, *args, **kw):
        try:
            distro = Distro.by_id(id)
        except InvalidRequestError:
            flash(_(u"Invalid distro id %s" % id))
            redirect(".")
        if tag:
            for dtag in distro.tags:
                if dtag == tag:
                    distro.tags.remove(dtag)
                    distro.activity.append(DistroActivity(
                            user=identity.current.user, service=u'WEBUI',
                            action=u'Removed', field_name=u'Tag',
                            old_value=tag, new_value=None))
                    flash(_(u"Removed Tag %s" % tag))
        redirect("./view?id=%s" % id)

    def _distros(self,distro,**kw):
        return_dict = {}
        if 'simplesearch' in kw:
            simplesearch = kw['simplesearch']
            kw['distrosearch'] = [{'table' : 'Name',
                                   'operation' : 'contains',
                                   'value' : kw['simplesearch']}]
        else:
            simplesearch = None

        return_dict.update({'simplesearch':simplesearch})
        if kw.get("distrosearch"):
            searchvalue = kw['distrosearch']
            distros_found = self._distro_search(distro,**kw)
            return_dict.update({'distros_found':distros_found})
            return_dict.update({'searchvalue':searchvalue})
        return return_dict

    def _distro_search(self,distro,**kw):
        distro_search = search_utility.Distro.search(distro)
        for search in kw['distrosearch']:
            col = search['table']
            distro_search.append_results(search['value'],col,search['operation'],**kw)
        return distro_search.return_results()

    @expose(template="bkr.server.templates.grid")
    @paginate('list',default_order='-date_created', limit=50)
    def index(self,*args,**kw):
        distro_q = session.query(Distro).join(OSVersion, OSMajor)\
                .filter(Distro.trees.any(DistroTree.lab_controller_assocs.any()))
        return self.distros(distros=distro_q, *args, **kw)

    @expose(template="bkr.server.templates.grid")
    @paginate('list',default_order='-date_created', limit=50)
    def name(self,*args,**kw):
        distro_q = session.query(Distro).join(OSVersion, OSMajor)\
                .filter(Distro.trees.any(DistroTree.lab_controller_assocs.any()))\
                .filter(Distro.name.like(kw['name']))
        return self.distros(distros=distro_q, action='./name')

    def distros(self, distros,action='.',*args, **kw):
        distros_return = self._distros(distros,**kw) 
        searchvalue = None
        hidden_fields = None
        search_options = {}
        if distros_return:
            if 'distros_found' in distros_return:
                distros = distros_return['distros_found']
            if 'searchvalue' in distros_return:
                searchvalue = distros_return['searchvalue']
            if 'simplesearch' in distros_return:
                search_options['simplesearch'] = distros_return['simplesearch']

        distros_grid =  myPaginateDataGrid(fields=[
                                  myPaginateDataGrid.Column(name='id', getter=lambda x: make_link(url = '/distros/view?id=%s' % x.id, text = x.id), title='ID', options=dict(sortable=True)),
                                  myPaginateDataGrid.Column(name='name',
                                    getter=lambda x: make_link(url='/distros/view?id=%s' % x.id, text=x.name),
                                    title='Name', options=dict(sortable=True)),
                                  myPaginateDataGrid.Column(name='osversion.osmajor.osmajor', getter=lambda x: x.osversion.osmajor, title='OS Major Version', options=dict(sortable=True)),
                                  myPaginateDataGrid.Column(name='osversion.osminor', getter=lambda x: x.osversion.osminor, title='OS Minor Version', options=dict(sortable=True)),
                                  myPaginateDataGrid.Column(name='date_created',
                                    getter=lambda x: x.date_created,
                                    title='Date Created',
                                    options=dict(sortable=True, datetime=True)),
                              ])

        if 'tag' in kw: 
            hidden_fields = [('tag',kw['tag'])]

        search_bar = SearchBar(name='distrosearch',
                           label=_(u'Distro Search'),    
                           table=search_utility.Distro.search.create_search_table(), 
                           complete_data = search_utility.Distro.search.create_complete_search_table(),
                           search_controller=url("/get_search_options_distros"), 
                           extra_hiddens=hidden_fields
                           )

        return dict(title="Distros", 
                    grid=distros_grid,
                    search_bar=search_bar,
                    object_count = distros.count(),
                    action=action,
                    options=search_options,
                    searchvalue=searchvalue,
                    list=distros)

    #XMLRPC method for listing distros
    @cherrypy.expose
    def filter(self, filter):
        """
        .. seealso:: :meth:`distrotrees.filter`

        Returns a list of details for distros filtered by the given criteria.

        The *filter* argument must be an XML-RPC structure (dict) specifying 
        filter criteria. The following keys are recognised:

            'name'
                Distro name. May include % SQL wildcards, for example 
                ``'%20101121.nightly'``.
            'family'
                Distro family name, for example ``'RedHatEnterpriseLinuxServer5'``. 
                Matches are exact.
            'tags'
                List of distro tags, for example ``['STABLE', 'RELEASED']``. All given 
                tags must be present on the distro for it to match.
            'limit'
                Integer limit to number of distros returned.

        The return value is an array with one element per distro (up to the 
        maximum number of distros given by 'limit'). Each element is an XML-RPC 
        structure (dict) describing a distro.

        .. versionchanged:: 0.9
           Some return columns were removed, because they no longer apply to 
           distros in Beaker. Use the new :meth:`distrotrees.filter` method 
           to fetch details of distro trees.
        """
        distros = session.query(Distro)
        name = filter.get('name', None)
        family = filter.get('family', None)
        tags = filter.get('tags', None) or []
        limit = filter.get('limit', None)
        for tag in tags:
            distros = distros.filter(Distro._tags.any(DistroTag.tag == tag))
        if name:
            distros = distros.filter(Distro.name.like('%s' % name))
        if family:
            distros = distros.join(Distro.osversion, OSVersion.osmajor)
            distros = distros.filter(OSMajor.osmajor == '%s' % family)
        # we only want distros that are active in at least one lab controller
        distros = distros.filter(Distro.trees.any(DistroTree.lab_controller_assocs.any()))
        distros = distros.order_by(Distro.date_created.desc())
        if limit:
            distros = distros[:limit]
        return [{'distro_id': distro.id,
                 'distro_name': distro.name,
                 'distro_version': unicode(distro.osversion),
                 'distro_tags': [unicode(tag) for tag in distro.tags],
                } for distro in distros]

    @cherrypy.expose
    @identity.require(identity.not_anonymous())
    def edit_version(self, name, version):
        """
        Updates the version for all distros with the given name.

        :param name: name of distros to be updated, for example 
            'RHEL5.6-Server-20101110.0'
        :type name: string
        :param version: new version to be applied, for example 
            'RedHatEnterpriseLinuxServer5.6' or 'Fedora14'
        :type version: string
        """
        distros = session.query(Distro).filter(distro_table.c.name.like('%s' % name))
        edited = []

        os_major = version.split('.')[0]

        # Try and split OSMinor
        try:
            os_minor = version.split('.')[1]
        except IndexError:
            os_minor = '0'

        # Try and find OSMajor
        try:
            osmajor = OSMajor.by_name(os_major)
        except InvalidRequestError: 
            osmajor = OSMajor(os_major)

        # Try and find OSVersion
        try:
            osversion = OSVersion.by_name(osmajor,os_minor)
        except InvalidRequestError: 
            osversion = OSVersion(osmajor,os_minor)

        # Check each Distro
        for distro in distros:
            if osversion != distro.osversion:
                edited.append('%s' % distro.name)
                distro.activity.append(DistroActivity(user=identity.current.user,
                        service=u'XMLRPC', field_name=u'osversion', action=u'Changed',
                        old_value=unicode(distro.osversion),
                        new_value=unicode(osversion)))
                distro.osversion = osversion
        return edited


    @cherrypy.expose
    @identity.require(identity.has_permission('tag_distro'))
    def tag(self, name, tag):
        """
        Applies the given tag to all matching distros.

        :param name: distro name to filter by (may include SQL wildcards)
        :type name: string or nil
        :param tag: tag to be applied
        :type tag: string
        :returns: list of distro names which have been modified

        .. versionchanged:: 0.9
           Removed *arch* parameter. Tags apply to distros and not distro trees.
        """
        added = []
        distros = Distro.query.filter(Distro.name.like('%s' % name))
        for distro in distros:
            if tag not in distro.tags:
                added.append('%s' % distro.name)
                distro.activity.append(DistroActivity(
                        user=identity.current.user, service=u'XMLRPC',
                        action=u'Added', field_name=u'Tag',
                        old_value=None, new_value=tag))
                distro.tags.append(tag)
        return added

    @cherrypy.expose
    @identity.require(identity.has_permission('tag_distro'))
    def untag(self, name, tag):
        """
        Like :meth:`distros.tag` but the opposite.
        """
        removed = []
        distros = Distro.query.filter(Distro.name.like('%s' % name))
        for distro in distros:
            if tag in distro.tags:
                removed.append('%s' % distro.name)
                distro.activity.append(DistroActivity(
                        user=identity.current.user, service=u'XMLRPC',
                        action=u'Removed', field_name=u'Tag',
                        old_value=tag, new_value=None))
                distro.tags.remove(tag)
        return removed

    default = index

# for sphinx
distros = Distros