1749 lines
58 KiB
Python
1749 lines
58 KiB
Python
import os
|
|
import os.path
|
|
import re
|
|
import sys
|
|
import mod_python
|
|
import mod_python.Cookie
|
|
import Cheetah.Filters
|
|
import Cheetah.Template
|
|
import datetime
|
|
import time
|
|
import koji
|
|
import kojiweb.util
|
|
|
|
# Convenience definition of a commonly-used sort function
|
|
_sortbyname = kojiweb.util.sortByKeyFunc('name')
|
|
|
|
def _setUserCookie(req, user):
|
|
options = req.get_options()
|
|
cookie = mod_python.Cookie.SignedCookie('user', user,
|
|
secret=options['Secret'],
|
|
secure=True,
|
|
path=os.path.dirname(req.uri),
|
|
expires=(time.time() + (int(options['LoginTimeout']) * 60 * 60)))
|
|
mod_python.Cookie.add_cookie(req, cookie)
|
|
|
|
def _clearUserCookie(req):
|
|
cookie = mod_python.Cookie.Cookie('user', '',
|
|
path=os.path.dirname(req.uri),
|
|
expires=0)
|
|
mod_python.Cookie.add_cookie(req, cookie)
|
|
|
|
def _getUserCookie(req):
|
|
options = req.get_options()
|
|
cookies = mod_python.Cookie.get_cookies(req,
|
|
mod_python.Cookie.SignedCookie,
|
|
secret=options['Secret'])
|
|
if cookies.has_key('user') and \
|
|
(type(cookies['user']) is mod_python.Cookie.SignedCookie):
|
|
return cookies['user'].value
|
|
else:
|
|
return None
|
|
|
|
def _krbLogin(req, session, principal):
|
|
options = req.get_options()
|
|
wprinc = options['WebPrincipal']
|
|
keytab = options['WebKeytab']
|
|
ccache = options['WebCCache']
|
|
return session.krb_login(principal=wprinc, keytab=keytab,
|
|
ccache=ccache, proxyuser=principal)
|
|
|
|
def _sslLogin(req, session, username):
|
|
options = req.get_options()
|
|
client_cert = options['WebCert']
|
|
client_ca = options['ClientCA']
|
|
server_ca = options['KojiHubCA']
|
|
|
|
return session.ssl_login(client_cert, client_ca, server_ca,
|
|
proxyuser=username)
|
|
|
|
def _assertLogin(req):
|
|
session = req._session
|
|
options = req.get_options()
|
|
if not (hasattr(req, 'currentLogin') and
|
|
hasattr(req, 'currentUser')):
|
|
raise StandardError, '_getServer() must be called before _assertLogin()'
|
|
elif req.currentLogin and req.currentUser:
|
|
if options.get('WebCert'):
|
|
if not _sslLogin(req, session, req.currentLogin):
|
|
raise koji.AuthError, 'could not login %s via SSL' % req.currentLogin
|
|
elif options.get('WebPrincipal'):
|
|
if not _krbLogin(req, req._session, req.currentLogin):
|
|
raise koji.AuthError, 'could not login using principal: %s' % req.currentLogin
|
|
else:
|
|
raise koji.AuthError, 'KojiWeb is incorrectly configured for authentication, contact the system administrator'
|
|
else:
|
|
mod_python.util.redirect(req, 'login')
|
|
assert False
|
|
|
|
def _initValues(req, title='Build System Info', pageID='summary'):
|
|
values = {}
|
|
values['title'] = title
|
|
values['pageID'] = pageID
|
|
values['currentDate'] = str(datetime.datetime.now())
|
|
|
|
req._values = values
|
|
|
|
return values
|
|
|
|
# Escape ampersands so the output can be valid XHTML
|
|
class XHTMLFilter(Cheetah.Filters.EncodeUnicode):
|
|
def filter(self, *args, **kw):
|
|
result = super(XHTMLFilter, self).filter(*args, **kw)
|
|
result = result.replace('&', '&')
|
|
result = result.replace(' ', ' ')
|
|
result = result.replace('&lt;', '<')
|
|
result = result.replace('&gt;', '>')
|
|
return result
|
|
|
|
def _genHTML(req, fileName):
|
|
os.chdir(os.path.dirname(req.filename))
|
|
|
|
if hasattr(req, 'currentUser'):
|
|
req._values['currentUser'] = req.currentUser
|
|
else:
|
|
req._values['currentUser'] = None
|
|
|
|
return Cheetah.Template.Template(file=fileName, searchList=[req._values], filter=XHTMLFilter).respond()
|
|
|
|
def _getServer(req):
|
|
serverURL = req.get_options().get('KojiHubURL', 'http://localhost/kojihub')
|
|
session = koji.ClientSession(serverURL)
|
|
|
|
req.currentLogin = _getUserCookie(req)
|
|
if req.currentLogin:
|
|
req.currentUser = session.getUser(req.currentLogin)
|
|
if not req.currentUser:
|
|
raise koji.AuthError, 'could not get user for principal: %s' % req.currentLogin
|
|
_setUserCookie(req, req.currentLogin)
|
|
else:
|
|
req.currentUser = None
|
|
|
|
req._session = session
|
|
return session
|
|
|
|
def _construct_url(req, page):
|
|
port = req.connection.local_addr[1]
|
|
url_scheme = 'http'
|
|
env = req.subprocess_env
|
|
if env.get('HTTPS') == 'on':
|
|
url_scheme = 'https'
|
|
if (url_scheme == 'https' and port == 443) or \
|
|
(url_scheme == 'http' and port == 80):
|
|
return "%s://%s%s" % (url_scheme, req.hostname, page)
|
|
return "%s://%s:%d%s" % (url_scheme, req.hostname, port, page)
|
|
|
|
def _getBaseURL(req):
|
|
pieces = req.uri.split('/')
|
|
base = '/'.join(pieces[:-1])
|
|
return _construct_url(req, base)
|
|
|
|
def _redirectBack(req, page, forceSSL):
|
|
if page:
|
|
# We'll work with the page we were given
|
|
pass
|
|
elif req.headers_in.get('Referer'):
|
|
page = req.headers_in.get('Referer')
|
|
else:
|
|
page = 'index'
|
|
|
|
# Modify the scheme if necessary
|
|
if page.startswith('http'):
|
|
pass
|
|
elif page.startswith('/'):
|
|
page = _construct_url(req, page)
|
|
else:
|
|
page = _getBaseURL(req) + '/' + page
|
|
if forceSSL:
|
|
page = page.replace('http:', 'https:')
|
|
else:
|
|
page = page.replace('https:', 'http:')
|
|
|
|
# and redirect to the page
|
|
mod_python.util.redirect(req, page)
|
|
|
|
def login(req, page=None):
|
|
session = _getServer(req)
|
|
options = req.get_options()
|
|
|
|
# try SSL first, fall back to Kerberos
|
|
if options.get('WebCert'):
|
|
req.add_common_vars()
|
|
env = req.subprocess_env
|
|
if not env.get('HTTPS') == 'on':
|
|
dest = 'login'
|
|
if page:
|
|
dest = dest + '?page=' + page
|
|
_redirectBack(req, dest, forceSSL=True)
|
|
return
|
|
|
|
if env.get('SSL_CLIENT_VERIFY') != 'SUCCESS':
|
|
raise koji.AuthError, 'could not verify client: %s' % env.get('SSL_CLIENT_VERIFY')
|
|
|
|
# use the subject's common name as their username
|
|
username = env.get('SSL_CLIENT_S_DN_CN')
|
|
if not username:
|
|
raise koji.AuthError, 'unable to get user information from client certificate'
|
|
|
|
if not _sslLogin(req, session, username):
|
|
raise koji.AuthError, 'could not login %s using SSL certificates' % username
|
|
|
|
elif options.get('WebPrincipal'):
|
|
principal = req.user
|
|
if not principal:
|
|
raise koji.AuthError, 'configuration error: mod_auth_kerb should have performed authentication before presenting this page'
|
|
|
|
if not _krbLogin(req, session, principal):
|
|
raise koji.AuthError, 'could not login using principal: %s' % principal
|
|
|
|
username = principal
|
|
else:
|
|
raise koji.AuthError, 'KojiWeb is incorrectly configured for authentication, contact the system administrator'
|
|
|
|
_setUserCookie(req, username)
|
|
# To protect the session cookie, we must forceSSL
|
|
_redirectBack(req, page, forceSSL=True)
|
|
|
|
def logout(req, page=None):
|
|
_clearUserCookie(req)
|
|
|
|
_redirectBack(req, page, forceSSL=False)
|
|
|
|
def index(req, packageOrder='package_name', packageStart=None, buildOrder='-completion_time', buildStart=None, taskOrder='-completion_time', taskStart=None):
|
|
values = _initValues(req)
|
|
server = _getServer(req)
|
|
|
|
user = req.currentUser
|
|
|
|
builds = kojiweb.util.paginateMethod(server, values, 'listBuilds', kw={'userID': (user and user['id'] or None)},
|
|
start=buildStart, dataName='builds', prefix='build', order=buildOrder, pageSize=10)
|
|
|
|
taskOpts = {'parent': None, 'decode': True}
|
|
if user:
|
|
taskOpts['owner'] = user['id']
|
|
tasks = kojiweb.util.paginateMethod(server, values, 'listTasks', kw={'opts': taskOpts},
|
|
start=taskStart, dataName='tasks', prefix='task', order=taskOrder, pageSize=10)
|
|
|
|
if user:
|
|
packages = kojiweb.util.paginateResults(server, values, 'listPackages', kw={'userID': user['id'], 'with_dups': True},
|
|
start=packageStart, dataName='packages', prefix='package', order=packageOrder, pageSize=10)
|
|
|
|
notifs = server.getBuildNotifications(user['id'])
|
|
notifs.sort(kojiweb.util.sortByKeyFunc('id'))
|
|
# XXX Make this a multicall
|
|
for notif in notifs:
|
|
notif['package'] = None
|
|
if notif['package_id']:
|
|
notif['package'] = server.getPackage(notif['package_id'])
|
|
|
|
notif['tag'] = None
|
|
if notif['tag_id']:
|
|
notif['tag'] = server.getTag(notif['tag_id'])
|
|
values['notifs'] = notifs
|
|
|
|
values['user'] = user
|
|
|
|
return _genHTML(req, 'index.chtml')
|
|
|
|
def notificationedit(req, notificationID):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
notificationID = int(notificationID)
|
|
notification = server.getBuildNotification(notificationID)
|
|
if notification == None:
|
|
raise koji.GenericError, 'no notification with ID: %i' % notificationID
|
|
|
|
form = req.form
|
|
|
|
if form.has_key('save'):
|
|
package_id = form['package']
|
|
if package_id == 'all':
|
|
package_id = None
|
|
else:
|
|
package_id = int(package_id)
|
|
|
|
tag_id = form['tag']
|
|
if tag_id == 'all':
|
|
tag_id = None
|
|
else:
|
|
tag_id = int(tag_id)
|
|
|
|
if form.has_key('success_only'):
|
|
success_only = True
|
|
else:
|
|
success_only = False
|
|
|
|
server.updateNotification(notification['id'], package_id, tag_id, success_only)
|
|
|
|
mod_python.util.redirect(req, 'index')
|
|
elif form.has_key('cancel'):
|
|
mod_python.util.redirect(req, 'index')
|
|
else:
|
|
values = _initValues(req, 'Edit Notification')
|
|
|
|
values['notif'] = notification
|
|
packages = server.listPackages()
|
|
packages.sort(kojiweb.util.sortByKeyFunc('package_name'))
|
|
values['packages'] = packages
|
|
tags = server.listTags(queryOpts={'order': 'name'})
|
|
values['tags'] = tags
|
|
|
|
return _genHTML(req, 'notificationedit.chtml')
|
|
|
|
def notificationcreate(req):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
form = req.form
|
|
|
|
if form.has_key('add'):
|
|
user = req.currentUser
|
|
if not user:
|
|
raise koji.GenericError, 'not logged-in'
|
|
|
|
package_id = form['package']
|
|
if package_id == 'all':
|
|
package_id = None
|
|
else:
|
|
package_id = int(package_id)
|
|
|
|
tag_id = form['tag']
|
|
if tag_id == 'all':
|
|
tag_id = None
|
|
else:
|
|
tag_id = int(tag_id)
|
|
|
|
if form.has_key('success_only'):
|
|
success_only = True
|
|
else:
|
|
success_only = False
|
|
|
|
server.createNotification(user['id'], package_id, tag_id, success_only)
|
|
|
|
mod_python.util.redirect(req, 'index')
|
|
elif form.has_key('cancel'):
|
|
mod_python.util.redirect(req, 'index')
|
|
else:
|
|
values = _initValues(req, 'Edit Notification')
|
|
|
|
values['notif'] = None
|
|
packages = server.listPackages()
|
|
packages.sort(kojiweb.util.sortByKeyFunc('package_name'))
|
|
values['packages'] = packages
|
|
tags = server.listTags(queryOpts={'order': 'name'})
|
|
values['tags'] = tags
|
|
|
|
return _genHTML(req, 'notificationedit.chtml')
|
|
|
|
def notificationdelete(req, notificationID):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
notificationID = int(notificationID)
|
|
notification = server.getBuildNotification(notificationID)
|
|
if not notification:
|
|
raise koji.GenericError, 'no notification with ID: %i' % notificationID
|
|
|
|
server.deleteNotification(notification['id'])
|
|
|
|
mod_python.util.redirect(req, 'index')
|
|
|
|
def hello(req):
|
|
return _getServer(req).hello()
|
|
|
|
def showSession(req):
|
|
return _getServer(req).showSession()
|
|
|
|
# Tasks that can exist without a parent
|
|
_TOPLEVEL_TASKS = ['build', 'buildNotification', 'chainbuild', 'newRepo', 'tagBuild', 'tagNotification', 'waitrepo']
|
|
# Tasks that can have children
|
|
_PARENT_TASKS = ['build', 'chainbuild', 'newRepo']
|
|
|
|
def tasks(req, owner=None, state='active', view='tree', method='all', hostID=None, start=None, order='-completion_time'):
|
|
values = _initValues(req, 'Tasks', 'tasks')
|
|
server = _getServer(req)
|
|
|
|
opts = {'decode': True}
|
|
if owner:
|
|
if owner.isdigit():
|
|
owner = int(owner)
|
|
ownerObj = server.getUser(owner, strict=True)
|
|
opts['owner'] = ownerObj['id']
|
|
values['owner'] = ownerObj['name']
|
|
values['ownerObj'] = ownerObj
|
|
else:
|
|
values['owner'] = None
|
|
values['ownerObj'] = None
|
|
|
|
values['users'] = server.listUsers(queryOpts={'order': 'name'})
|
|
|
|
treeEnabled = True
|
|
if hostID or (method not in ['all'] + _PARENT_TASKS):
|
|
# force flat view if we're filtering by a hostID or a task that never has children
|
|
if view == 'tree':
|
|
view = 'flat'
|
|
# don't let them choose tree view either
|
|
treeEnabled = False
|
|
values['treeEnabled'] = treeEnabled
|
|
|
|
toplevelEnabled = True
|
|
if method not in ['all'] + _TOPLEVEL_TASKS:
|
|
# force flat view if we're viewing a task that is never a top-level task
|
|
if view == 'toplevel':
|
|
view = 'flat'
|
|
toplevelEnabled = False
|
|
values['toplevelEnabled'] = toplevelEnabled
|
|
|
|
values['view'] = view
|
|
|
|
if view == 'tree':
|
|
treeDisplay = True
|
|
else:
|
|
treeDisplay = False
|
|
values['treeDisplay'] = treeDisplay
|
|
|
|
if method != 'all':
|
|
opts['method'] = method
|
|
values['method'] = method
|
|
|
|
if view in ('tree', 'toplevel'):
|
|
opts['parent'] = None
|
|
|
|
if state == 'active':
|
|
opts['state'] = [koji.TASK_STATES['FREE'], koji.TASK_STATES['OPEN'], koji.TASK_STATES['ASSIGNED']]
|
|
elif state == 'all':
|
|
pass
|
|
else:
|
|
# Assume they've passed in a state name
|
|
opts['state'] = [koji.TASK_STATES[state.upper()]]
|
|
values['state'] = state
|
|
|
|
if hostID:
|
|
hostID = int(hostID)
|
|
host = server.getHost(hostID, strict=True)
|
|
opts['host_id'] = host['id']
|
|
values['host'] = host
|
|
values['hostID'] = host['id']
|
|
else:
|
|
values['host'] = None
|
|
values['hostID'] = None
|
|
|
|
loggedInUser = req.currentUser
|
|
values['loggedInUser'] = loggedInUser
|
|
|
|
values['order'] = order
|
|
|
|
tasks = kojiweb.util.paginateMethod(server, values, 'listTasks', kw={'opts': opts},
|
|
start=start, dataName='tasks', prefix='task', order=order)
|
|
|
|
if view == 'tree':
|
|
server.multicall = True
|
|
for task in tasks:
|
|
server.getTaskDescendents(task['id'], request=True)
|
|
descendentList = server.multiCall()
|
|
for task, [descendents] in zip(tasks, descendentList):
|
|
task['descendents'] = descendents
|
|
|
|
return _genHTML(req, 'tasks.chtml')
|
|
|
|
def taskinfo(req, taskID):
|
|
server = _getServer(req)
|
|
values = _initValues(req, 'Task Info', 'tasks')
|
|
|
|
taskID = int(taskID)
|
|
task = server.getTaskInfo(taskID, request=True)
|
|
if not task:
|
|
raise koji.GenericError, 'invalid task ID: %s' % taskID
|
|
|
|
values['title'] = koji.taskLabel(task) + ' | Task Info'
|
|
|
|
values['task'] = task
|
|
params = task['request']
|
|
values['params'] = params
|
|
|
|
if task['channel_id']:
|
|
channel = server.getChannel(task['channel_id'])
|
|
values['channelName'] = channel['name']
|
|
else:
|
|
values['channelName'] = None
|
|
if task['host_id']:
|
|
host = server.getHost(task['host_id'])
|
|
values['hostName'] = host['name']
|
|
else:
|
|
values['hostName'] = None
|
|
if task['owner']:
|
|
owner = server.getUser(task['owner'])
|
|
values['owner'] = owner
|
|
else:
|
|
values['owner'] = None
|
|
if task['parent']:
|
|
parent = server.getTaskInfo(task['parent'], request=True)
|
|
values['parent'] = parent
|
|
else:
|
|
values['parent'] = None
|
|
|
|
descendents = server.getTaskDescendents(task['id'], request=True)
|
|
values['descendents'] = descendents
|
|
|
|
builds = server.listBuilds(taskID=task['id'])
|
|
if builds:
|
|
values['taskBuild'] = builds[0]
|
|
else:
|
|
values['taskBuild'] = None
|
|
|
|
buildroots = server.listBuildroots(taskID=task['id'])
|
|
values['buildroots'] = buildroots
|
|
|
|
if task['method'] == 'buildArch':
|
|
buildTag = server.getTag(params[1])
|
|
values['buildTag'] = buildTag
|
|
elif task['method'] == 'tagBuild':
|
|
destTag = server.getTag(params[0])
|
|
build = server.getBuild(params[1])
|
|
values['destTag'] = destTag
|
|
values['build'] = build
|
|
elif task['method'] == 'newRepo':
|
|
tag = server.getTag(params[0])
|
|
values['tag'] = tag
|
|
elif task['method'] == 'tagNotification':
|
|
destTag = None
|
|
if params[2]:
|
|
destTag = server.getTag(params[2])
|
|
srcTag = None
|
|
if params[3]:
|
|
srcTag = server.getTag(params[3])
|
|
build = server.getBuild(params[4])
|
|
user = server.getUser(params[5])
|
|
values['destTag'] = destTag
|
|
values['srcTag'] = srcTag
|
|
values['build'] = build
|
|
values['user'] = user
|
|
elif task['method'] == 'dependantTask':
|
|
deps = [server.getTaskInfo(depID, request=True) for depID in params[0]]
|
|
values['deps'] = deps
|
|
|
|
if task['state'] in (koji.TASK_STATES['CLOSED'], koji.TASK_STATES['FAILED']):
|
|
try:
|
|
result = server.getTaskResult(task['id'])
|
|
values['result'] = result
|
|
values['excClass'] = None
|
|
except:
|
|
excClass, exc = sys.exc_info()[:2]
|
|
values['result'] = exc
|
|
values['excClass'] = excClass
|
|
# clear the exception, since we're just using
|
|
# it for display purposes
|
|
sys.exc_clear()
|
|
else:
|
|
values['result'] = None
|
|
values['excClass'] = None
|
|
|
|
output = server.listTaskOutput(task['id'])
|
|
output.sort(_sortByExtAndName)
|
|
values['output'] = output
|
|
if req.currentUser:
|
|
values['perms'] = server.getUserPerms(req.currentUser['id'])
|
|
else:
|
|
values['perms'] = []
|
|
|
|
return _genHTML(req, 'taskinfo.chtml')
|
|
|
|
def resubmittask(req, taskID):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
taskID = int(taskID)
|
|
newTaskID = server.resubmitTask(taskID)
|
|
mod_python.util.redirect(req, 'taskinfo?taskID=%i' % newTaskID)
|
|
|
|
def canceltask(req, taskID):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
taskID = int(taskID)
|
|
server.cancelTask(taskID)
|
|
mod_python.util.redirect(req, 'taskinfo?taskID=%i' % taskID)
|
|
|
|
def _sortByExtAndName(a, b):
|
|
"""Sort two filenames, first by extension, and then by name."""
|
|
aRoot, aExt = os.path.splitext(a)
|
|
bRoot, bExt = os.path.splitext(b)
|
|
return cmp(aExt, bExt) or cmp(aRoot, bRoot)
|
|
|
|
def getfile(req, taskID, name):
|
|
server = _getServer(req)
|
|
taskID = int(taskID)
|
|
|
|
output = server.listTaskOutput(taskID, stat=True)
|
|
file_info = output.get(name)
|
|
if not file_info:
|
|
raise koji.GenericError, 'no file "%s" output by task %i' % (name, taskID)
|
|
|
|
if name.endswith('.rpm'):
|
|
req.content_type = 'application/x-rpm'
|
|
req.headers_out['Content-Disposition'] = 'attachment; filename=%s' % name
|
|
elif name.endswith('.log'):
|
|
req.content_type = 'text/plain'
|
|
req.set_content_length(file_info['st_size'])
|
|
|
|
offset = 0
|
|
while True:
|
|
content = server.downloadTaskOutput(taskID, name, offset=offset, size=65536)
|
|
if not content:
|
|
break
|
|
req.write(content)
|
|
offset += len(content)
|
|
|
|
def tags(req, start=None, order=None, childID=None):
|
|
values = _initValues(req, 'Tags', 'tags')
|
|
server = _getServer(req)
|
|
|
|
if order == None:
|
|
order = 'name'
|
|
values['order'] = order
|
|
|
|
tags = kojiweb.util.paginateMethod(server, values, 'listTags', kw=None,
|
|
start=start, dataName='tags', prefix='tag', order=order)
|
|
|
|
if req.currentUser:
|
|
values['perms'] = server.getUserPerms(req.currentUser['id'])
|
|
else:
|
|
values['perms'] = []
|
|
|
|
values['childID'] = childID
|
|
|
|
return _genHTML(req, 'tags.chtml')
|
|
|
|
def packages(req, tagID=None, userID=None, order='package_name', start=None, prefix=None, inherited='1'):
|
|
values = _initValues(req, 'Packages', 'packages')
|
|
server = _getServer(req)
|
|
tag = None
|
|
if tagID != None:
|
|
tagID = int(tagID)
|
|
tag = server.getTag(tagID)
|
|
values['tagID'] = tagID
|
|
values['tag'] = tag
|
|
user = None
|
|
if userID != None:
|
|
userID = int(userID)
|
|
user = server.getUser(userID)
|
|
values['userID'] = userID
|
|
values['user'] = user
|
|
values['order'] = order
|
|
if prefix:
|
|
prefix = prefix.lower()
|
|
values['prefix'] = prefix
|
|
inherited = int(inherited)
|
|
values['inherited'] = inherited
|
|
|
|
packages = kojiweb.util.paginateResults(server, values, 'listPackages',
|
|
kw={'tagID': tagID, 'userID': userID, 'prefix': prefix, 'inherited': bool(inherited)},
|
|
start=start, dataName='packages', prefix='package', order=order)
|
|
|
|
values['chars'] = [chr(char) for char in range(48, 58) + range(97, 123)]
|
|
|
|
return _genHTML(req, 'packages.chtml')
|
|
|
|
def packageinfo(req, packageID, tagOrder='name', tagStart=None, buildOrder='-completion_time', buildStart=None):
|
|
values = _initValues(req, 'Package Info', 'packages')
|
|
server = _getServer(req)
|
|
|
|
if packageID.isdigit():
|
|
packageID = int(packageID)
|
|
package = server.getPackage(packageID)
|
|
if package == None:
|
|
raise koji.GenericError, 'invalid package ID: %s' % packageID
|
|
|
|
values['title'] = package['name'] + ' | Package Info'
|
|
|
|
values['package'] = package
|
|
values['packageID'] = package['id']
|
|
|
|
tags = kojiweb.util.paginateMethod(server, values, 'listTags', kw={'package': package['id']},
|
|
start=tagStart, dataName='tags', prefix='tag', order=tagOrder)
|
|
builds = kojiweb.util.paginateMethod(server, values, 'listBuilds', kw={'packageID': package['id']},
|
|
start=buildStart, dataName='builds', prefix='build', order=buildOrder)
|
|
|
|
return _genHTML(req, 'packageinfo.chtml')
|
|
|
|
def taginfo(req, tagID, all='0', packageOrder='package_name', packageStart=None, buildOrder='-completion_time', buildStart=None, childID=None):
|
|
values = _initValues(req, 'Tag Info', 'tags')
|
|
server = _getServer(req)
|
|
|
|
if tagID.isdigit():
|
|
tagID = int(tagID)
|
|
tag = server.getTag(tagID, strict=True)
|
|
|
|
values['title'] = tag['name'] + ' | Tag Info'
|
|
|
|
all = int(all)
|
|
|
|
numPackages = server.count('listPackages', tagID=tag['id'], inherited=True)
|
|
numBuilds = server.count('listTagged', tag=tag['id'], inherit=True)
|
|
values['numPackages'] = numPackages
|
|
values['numBuilds'] = numBuilds
|
|
|
|
inheritance = server.getFullInheritance(tag['id'])
|
|
tagsByChild = {}
|
|
for parent in inheritance:
|
|
child_id = parent['child_id']
|
|
if not tagsByChild.has_key(child_id):
|
|
tagsByChild[child_id] = []
|
|
tagsByChild[child_id].append(child_id)
|
|
|
|
srcTargets = server.getBuildTargets(buildTagID=tag['id'])
|
|
srcTargets.sort(_sortbyname)
|
|
destTargets = server.getBuildTargets(destTagID=tag['id'])
|
|
destTargets.sort(_sortbyname)
|
|
|
|
values['tag'] = tag
|
|
values['tagID'] = tag['id']
|
|
values['inheritance'] = inheritance
|
|
values['tagsByChild'] = tagsByChild
|
|
values['srcTargets'] = srcTargets
|
|
values['destTargets'] = destTargets
|
|
values['all'] = all
|
|
values['repo'] = server.getRepo(tag['id'], state=koji.REPO_READY)
|
|
|
|
child = None
|
|
if childID != None:
|
|
child = server.getTag(int(childID), strict=True)
|
|
values['child'] = child
|
|
|
|
if req.currentUser:
|
|
values['perms'] = server.getUserPerms(req.currentUser['id'])
|
|
else:
|
|
values['perms'] = []
|
|
permList = server.getAllPerms()
|
|
allPerms = dict([(perm['id'], perm['name']) for perm in permList])
|
|
values['allPerms'] = allPerms
|
|
|
|
return _genHTML(req, 'taginfo.chtml')
|
|
|
|
def tagcreate(req):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
form = req.form
|
|
|
|
if form.has_key('add'):
|
|
name = form['name'].value
|
|
arches = form['arches'].value
|
|
if form.has_key('locked'):
|
|
locked = True
|
|
else:
|
|
locked = False
|
|
permission = form['permission'].value
|
|
if permission == 'none':
|
|
permission = None
|
|
else:
|
|
permission = int(permission)
|
|
|
|
server.createTag(name)
|
|
tag = server.getTag(name)
|
|
|
|
if tag == None:
|
|
raise koji.GenericError, 'error creating tag "%s"' % name
|
|
|
|
server.editTag(tag['id'], name, arches, locked, permission)
|
|
|
|
mod_python.util.redirect(req, 'taginfo?tagID=%i' % tag['id'])
|
|
elif form.has_key('cancel'):
|
|
mod_python.util.redirect(req, 'tags')
|
|
else:
|
|
values = _initValues(req, 'Add Tag', 'tags')
|
|
|
|
values['tag'] = None
|
|
values['permissions'] = server.getAllPerms()
|
|
|
|
return _genHTML(req, 'tagedit.chtml')
|
|
|
|
def tagedit(req, tagID):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
tagID = int(tagID)
|
|
tag = server.getTag(tagID)
|
|
if tag == None:
|
|
raise koji.GenericError, 'no tag with ID: %i' % tagID
|
|
|
|
form = req.form
|
|
|
|
if form.has_key('save'):
|
|
name = form['name'].value
|
|
arches = form['arches'].value
|
|
locked = bool(form.has_key('locked'))
|
|
permission = form['permission'].value
|
|
if permission == 'none':
|
|
permission = None
|
|
else:
|
|
permission = int(permission)
|
|
|
|
server.editTag(tag['id'], name, arches, locked, permission)
|
|
|
|
mod_python.util.redirect(req, 'taginfo?tagID=%i' % tag['id'])
|
|
elif form.has_key('cancel'):
|
|
mod_python.util.redirect(req, 'taginfo?tagID=%i' % tag['id'])
|
|
else:
|
|
values = _initValues(req, 'Edit Tag', 'tags')
|
|
|
|
values['tag'] = tag
|
|
values['permissions'] = server.getAllPerms()
|
|
|
|
return _genHTML(req, 'tagedit.chtml')
|
|
|
|
def tagdelete(req, tagID):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
tagID = int(tagID)
|
|
tag = server.getTag(tagID)
|
|
if tag == None:
|
|
raise koji.GenericError, 'no tag with ID: %i' % tagID
|
|
|
|
server.deleteTag(tag['id'])
|
|
|
|
mod_python.util.redirect(req, 'tags')
|
|
|
|
def tagparent(req, tagID, parentID, action):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
tag = server.getTag(int(tagID), strict=True)
|
|
parent = server.getTag(int(parentID), strict=True)
|
|
|
|
if action in ('add', 'edit'):
|
|
form = req.form
|
|
|
|
if form.has_key('add') or form.has_key('save'):
|
|
newDatum = {}
|
|
newDatum['parent_id'] = parent['id']
|
|
newDatum['priority'] = int(form['priority'])
|
|
maxdepth = form['maxdepth']
|
|
maxdepth = len(maxdepth) > 0 and int(maxdepth) or None
|
|
newDatum['maxdepth'] = maxdepth
|
|
newDatum['intransitive'] = bool(form.has_key('intransitive'))
|
|
newDatum['noconfig'] = bool(form.has_key('noconfig'))
|
|
newDatum['pkg_filter'] = form['pkg_filter'].value
|
|
|
|
data = server.getInheritanceData(tag['id'])
|
|
data.append(newDatum)
|
|
|
|
server.setInheritanceData(tag['id'], data)
|
|
elif form.has_key('cancel'):
|
|
pass
|
|
else:
|
|
values = _initValues(req, action.capitalize() + ' Parent Tag', 'tags')
|
|
values['tag'] = tag
|
|
values['parent'] = parent
|
|
|
|
inheritanceData = server.getInheritanceData(tag['id'])
|
|
maxPriority = 0
|
|
for datum in inheritanceData:
|
|
if datum['priority'] > maxPriority:
|
|
maxPriority = datum['priority']
|
|
values['maxPriority'] = maxPriority
|
|
inheritanceData = [datum for datum in inheritanceData \
|
|
if datum['parent_id'] == parent['id']]
|
|
if len(inheritanceData) == 0:
|
|
values['inheritanceData'] = None
|
|
elif len(inheritanceData) == 1:
|
|
values['inheritanceData'] = inheritanceData[0]
|
|
else:
|
|
raise koji.GenericError, 'tag %i has tag %i listed as a parent more than once' % (tag['id'], parent['id'])
|
|
|
|
return _genHTML(req, 'tagparent.chtml')
|
|
elif action == 'remove':
|
|
data = server.getInheritanceData(tag['id'])
|
|
for datum in data:
|
|
if datum['parent_id'] == parent['id']:
|
|
datum['delete link'] = True
|
|
break
|
|
else:
|
|
raise koji.GenericError, 'tag %i is not a parent of tag %i' % (parent['id'], tag['id'])
|
|
|
|
server.setInheritanceData(tag['id'], data)
|
|
else:
|
|
raise koji.GenericError, 'unknown action: %s' % action
|
|
|
|
mod_python.util.redirect(req, 'taginfo?tagID=%i' % tag['id'])
|
|
|
|
def buildinfo(req, buildID):
|
|
values = _initValues(req, 'Build Info', 'builds')
|
|
server = _getServer(req)
|
|
|
|
buildID = int(buildID)
|
|
|
|
build = server.getBuild(buildID)
|
|
|
|
values['title'] = koji.buildLabel(build) + ' | Build Info'
|
|
|
|
tags = server.listTags(build['id'])
|
|
tags.sort(_sortbyname)
|
|
rpms = server.listBuildRPMs(build['id'])
|
|
rpms.sort(_sortbyname)
|
|
|
|
rpmsByArch = {}
|
|
debuginfoByArch = {}
|
|
for rpm in rpms:
|
|
canon_arch = koji.canonArch(rpm['arch'])
|
|
if rpm['name'].endswith('-debuginfo') or rpm['name'].endswith('-debuginfo-common'):
|
|
debuginfoByArch.setdefault(canon_arch, []).append(rpm)
|
|
else:
|
|
rpmsByArch.setdefault(canon_arch, []).append(rpm)
|
|
|
|
if build['task_id']:
|
|
task = server.getTaskInfo(build['task_id'], request=True)
|
|
else:
|
|
task = None
|
|
|
|
values['build'] = build
|
|
values['tags'] = tags
|
|
values['rpmsByArch'] = rpmsByArch
|
|
values['debuginfoByArch'] = debuginfoByArch
|
|
values['task'] = task
|
|
if req.currentUser:
|
|
values['perms'] = server.getUserPerms(req.currentUser['id'])
|
|
else:
|
|
values['perms'] = []
|
|
values['changelog'] = server.getChangelogEntries(build['id'])
|
|
if build['state'] == koji.BUILD_STATES['BUILDING']:
|
|
avgDuration = server.getAverageBuildDuration(build['package_id'])
|
|
if avgDuration != None:
|
|
avgDelta = datetime.timedelta(seconds=avgDuration)
|
|
startTime = datetime.datetime.fromtimestamp(
|
|
time.mktime(time.strptime(koji.formatTime(build['creation_time']), '%Y-%m-%d %H:%M:%S'))
|
|
)
|
|
values['estCompletion'] = startTime + avgDelta
|
|
else:
|
|
values['estCompletion'] = None
|
|
|
|
values['downloadBase'] = req.get_options().get('KojiPackagesURL', 'http://localhost/packages')
|
|
|
|
return _genHTML(req, 'buildinfo.chtml')
|
|
|
|
def builds(req, userID=None, tagID=None, packageID=None, state=None, order='-completion_time', start=None, prefix=None, inherited='1'):
|
|
values = _initValues(req, 'Builds', 'builds')
|
|
server = _getServer(req)
|
|
|
|
user = None
|
|
if userID:
|
|
if userID.isdigit():
|
|
userID = int(userID)
|
|
user = server.getUser(userID, strict=True)
|
|
values['userID'] = userID
|
|
values['user'] = user
|
|
|
|
loggedInUser = req.currentUser
|
|
values['loggedInUser'] = loggedInUser
|
|
|
|
values['users'] = server.listUsers(queryOpts={'order': 'name'})
|
|
|
|
tag = None
|
|
if tagID:
|
|
if tagID.isdigit():
|
|
tagID = int(tagID)
|
|
tag = server.getTag(tagID, strict=True)
|
|
values['tagID'] = tagID
|
|
values['tag'] = tag
|
|
|
|
package = None
|
|
if packageID:
|
|
if packageID.isdigit():
|
|
packageID = int(packageID)
|
|
package = server.getPackage(packageID, strict=True)
|
|
values['packageID'] = packageID
|
|
values['package'] = package
|
|
|
|
if state == 'all':
|
|
state = None
|
|
elif state != None:
|
|
state = int(state)
|
|
values['state'] = state
|
|
|
|
if prefix:
|
|
prefix = prefix.lower()
|
|
values['prefix'] = prefix
|
|
|
|
values['order'] = order
|
|
inherited = int(inherited)
|
|
values['inherited'] = inherited
|
|
|
|
if tag:
|
|
# don't need to consider 'state' here, since only completed builds would be tagged
|
|
builds = kojiweb.util.paginateResults(server, values, 'listTagged', kw={'tag': tag['id'], 'package': (package and package['name'] or None),
|
|
'owner': (user and user['name'] or None),
|
|
'inherit': bool(inherited), 'prefix': prefix},
|
|
start=start, dataName='builds', prefix='build', order=order)
|
|
else:
|
|
builds = kojiweb.util.paginateMethod(server, values, 'listBuilds', kw={'userID': (user and user['id'] or None), 'packageID': (package and package['id'] or None),
|
|
'state': state, 'prefix': prefix},
|
|
start=start, dataName='builds', prefix='build', order=order)
|
|
|
|
values['chars'] = [chr(char) for char in range(48, 58) + range(97, 123)]
|
|
|
|
return _genHTML(req, 'builds.chtml')
|
|
|
|
def users(req, order='name', start=None, prefix=None):
|
|
values = _initValues(req, 'Users', 'users')
|
|
server = _getServer(req)
|
|
|
|
if prefix:
|
|
prefix = prefix.lower()
|
|
values['prefix'] = prefix
|
|
|
|
values['order'] = order
|
|
|
|
users = kojiweb.util.paginateMethod(server, values, 'listUsers', kw={'prefix': prefix},
|
|
start=start, dataName='users', prefix='user', order=order)
|
|
|
|
values['chars'] = [chr(char) for char in range(48, 58) + range(97, 123)]
|
|
|
|
return _genHTML(req, 'users.chtml')
|
|
|
|
def userinfo(req, userID, packageOrder='package_name', packageStart=None, buildOrder='-completion_time', buildStart=None):
|
|
values = _initValues(req, 'User Info', 'users')
|
|
server = _getServer(req)
|
|
|
|
if userID.isdigit():
|
|
userID = int(userID)
|
|
user = server.getUser(userID, strict=True)
|
|
|
|
values['title'] = user['name'] + ' | User Info'
|
|
|
|
values['user'] = user
|
|
values['userID'] = userID
|
|
|
|
packages = kojiweb.util.paginateResults(server, values, 'listPackages', kw={'userID': user['id'], 'with_dups': True},
|
|
start=packageStart, dataName='packages', prefix='package', order=packageOrder, pageSize=10)
|
|
|
|
builds = kojiweb.util.paginateMethod(server, values, 'listBuilds', kw={'userID': user['id']},
|
|
start=buildStart, dataName='builds', prefix='build', order=buildOrder, pageSize=10)
|
|
|
|
return _genHTML(req, 'userinfo.chtml')
|
|
|
|
def rpminfo(req, rpmID, fileOrder='name', fileStart=None):
|
|
values = _initValues(req, 'RPM Info', 'builds')
|
|
server = _getServer(req)
|
|
|
|
rpmID = int(rpmID)
|
|
rpm = server.getRPM(rpmID)
|
|
|
|
values['title'] = '%(name)s-%%s%(version)s-%(release)s.%(arch)s.rpm' % rpm + ' | RPM Info'
|
|
epochStr = ''
|
|
if rpm['epoch'] != None:
|
|
epochStr = '%s:' % rpm['epoch']
|
|
values['title'] = values['title'] % epochStr
|
|
|
|
build = server.getBuild(rpm['build_id'])
|
|
builtInRoot = None
|
|
if rpm['buildroot_id'] != None:
|
|
builtInRoot = server.getBuildroot(rpm['buildroot_id'])
|
|
requires = server.getRPMDeps(rpm['id'], koji.DEP_REQUIRE)
|
|
requires.sort(_sortbyname)
|
|
provides = server.getRPMDeps(rpm['id'], koji.DEP_PROVIDE)
|
|
provides.sort(_sortbyname)
|
|
obsoletes = server.getRPMDeps(rpm['id'], koji.DEP_OBSOLETE)
|
|
obsoletes.sort(_sortbyname)
|
|
conflicts = server.getRPMDeps(rpm['id'], koji.DEP_CONFLICT)
|
|
conflicts.sort(_sortbyname)
|
|
buildroots = server.listBuildroots(rpmID=rpm['id'])
|
|
buildroots.sort(kojiweb.util.sortByKeyFunc('-create_event_time'))
|
|
|
|
values['rpmID'] = rpmID
|
|
values['rpm'] = rpm
|
|
values['build'] = build
|
|
values['builtInRoot'] = builtInRoot
|
|
values['requires'] = requires
|
|
values['provides'] = provides
|
|
values['obsoletes'] = obsoletes
|
|
values['conflicts'] = conflicts
|
|
values['buildroots'] = buildroots
|
|
|
|
files = kojiweb.util.paginateMethod(server, values, 'listRPMFiles', args=[rpm['id']],
|
|
start=fileStart, dataName='files', prefix='file', order=fileOrder)
|
|
|
|
return _genHTML(req, 'rpminfo.chtml')
|
|
|
|
def fileinfo(req, rpmID, filename):
|
|
values = _initValues(req, 'File Info', 'builds')
|
|
server = _getServer(req)
|
|
|
|
rpmID = int(rpmID)
|
|
rpm = server.getRPM(rpmID)
|
|
if not rpm:
|
|
raise koji.GenericError, 'invalid RPM ID: %i' % rpmID
|
|
file = server.getRPMFile(rpmID, filename)
|
|
if not file:
|
|
raise koji.GenericError, 'no file %s in RPM %i' % (filename, rpmID)
|
|
|
|
values['title'] = file['name'] + ' | File Info'
|
|
|
|
values['rpm'] = rpm
|
|
values['file'] = file
|
|
|
|
return _genHTML(req, 'fileinfo.chtml')
|
|
|
|
def cancelbuild(req, buildID):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
buildID = int(buildID)
|
|
build = server.getBuild(buildID)
|
|
if build == None:
|
|
raise koji.GenericError, 'unknown build ID: %i' % buildID
|
|
|
|
result = server.cancelBuild(build['id'])
|
|
if not result:
|
|
raise koji.GenericError, 'unable to cancel build'
|
|
|
|
mod_python.util.redirect(req, 'buildinfo?buildID=%i' % build['id'])
|
|
|
|
def hosts(req, start=None, order='name'):
|
|
values = _initValues(req, 'Hosts', 'hosts')
|
|
server = _getServer(req)
|
|
|
|
values['order'] = order
|
|
|
|
hosts = server.listHosts()
|
|
|
|
server.multicall = True
|
|
for host in hosts:
|
|
server.getLastHostUpdate(host['id'])
|
|
updates = server.multiCall()
|
|
for host, [lastUpdate] in zip(hosts, updates):
|
|
host['last_update'] = lastUpdate
|
|
|
|
# Paginate after retrieving last update info so we can sort on it
|
|
kojiweb.util.paginateList(values, hosts, start, 'hosts', 'host', order)
|
|
|
|
return _genHTML(req, 'hosts.chtml')
|
|
|
|
def hostinfo(req, hostID=None, userID=None):
|
|
values = _initValues(req, 'Host Info', 'hosts')
|
|
server = _getServer(req)
|
|
|
|
if hostID:
|
|
if hostID.isdigit():
|
|
hostID = int(hostID)
|
|
host = server.getHost(hostID)
|
|
if host == None:
|
|
raise koji.GenericError, 'invalid host ID: %s' % hostID
|
|
elif userID:
|
|
userID = int(userID)
|
|
hosts = server.listHosts(userID=userID)
|
|
host = None
|
|
if hosts:
|
|
host = hosts[0]
|
|
if host == None:
|
|
raise koji.GenericError, 'invalid host ID: %s' % userID
|
|
else:
|
|
raise koji.GenericError, 'hostID or userID must be provided'
|
|
|
|
values['title'] = host['name'] + ' | Host Info'
|
|
|
|
channels = server.listChannels(host['id'])
|
|
channels.sort(_sortbyname)
|
|
buildroots = server.listBuildroots(hostID=host['id'],
|
|
state=[state[1] for state in koji.BR_STATES.items() if state[0] != 'EXPIRED'])
|
|
buildroots.sort(kojiweb.util.sortByKeyFunc('-create_event_time'))
|
|
|
|
values['host'] = host
|
|
values['channels'] = channels
|
|
values['buildroots'] = buildroots
|
|
values['lastUpdate'] = server.getLastHostUpdate(host['id'])
|
|
if req.currentUser:
|
|
values['perms'] = server.getUserPerms(req.currentUser['id'])
|
|
else:
|
|
values['perms'] = []
|
|
|
|
return _genHTML(req, 'hostinfo.chtml')
|
|
|
|
def disablehost(req, hostID):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
hostID = int(hostID)
|
|
host = server.getHost(hostID, strict=True)
|
|
server.disableHost(host['name'])
|
|
|
|
mod_python.util.redirect(req, 'hostinfo?hostID=%i' % host['id'])
|
|
|
|
def enablehost(req, hostID):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
hostID = int(hostID)
|
|
host = server.getHost(hostID, strict=True)
|
|
server.enableHost(host['name'])
|
|
|
|
mod_python.util.redirect(req, 'hostinfo?hostID=%i' % host['id'])
|
|
|
|
def channelinfo(req, channelID):
|
|
values = _initValues(req, 'Channel Info', 'hosts')
|
|
server = _getServer(req)
|
|
|
|
channelID = int(channelID)
|
|
channel = server.getChannel(channelID)
|
|
if channel == None:
|
|
raise koji.GenericError, 'invalid channel ID: %i' % channelID
|
|
|
|
values['title'] = channel['name'] + ' | Channel Info'
|
|
|
|
hosts = server.listHosts(channelID=channelID)
|
|
hosts.sort(_sortbyname)
|
|
|
|
values['channel'] = channel
|
|
values['hosts'] = hosts
|
|
|
|
return _genHTML(req, 'channelinfo.chtml')
|
|
|
|
def buildrootinfo(req, buildrootID, builtStart=None, builtOrder=None, componentStart=None, componentOrder=None):
|
|
values = _initValues(req, 'Buildroot Info', 'hosts')
|
|
server = _getServer(req)
|
|
|
|
buildrootID = int(buildrootID)
|
|
buildroot = server.getBuildroot(buildrootID)
|
|
|
|
values['title'] = '%(tag_name)s-%(id)i-%(repo_id)i' % buildroot + ' | Buildroot Info'
|
|
|
|
if buildroot == None:
|
|
raise koji.GenericError, 'unknown buildroot ID: %i' % buildrootID
|
|
|
|
task = server.getTaskInfo(buildroot['task_id'], request=True)
|
|
|
|
values['buildroot'] = buildroot
|
|
values['task'] = task
|
|
|
|
return _genHTML(req, 'buildrootinfo.chtml')
|
|
|
|
def rpmlist(req, buildrootID, type, start=None, order='nvr'):
|
|
values = _initValues(req, 'RPM List', 'hosts')
|
|
server = _getServer(req)
|
|
|
|
buildrootID = int(buildrootID)
|
|
buildroot = server.getBuildroot(buildrootID)
|
|
if buildroot == None:
|
|
raise koji.GenericError, 'unknown buildroot ID: %i' % buildrootID
|
|
|
|
rpms = None
|
|
if type == 'component':
|
|
rpms = kojiweb.util.paginateMethod(server, values, 'listRPMs', kw={'componentBuildrootID': buildroot['id']},
|
|
start=start, dataName='rpms', prefix='rpm', order=order)
|
|
elif type == 'built':
|
|
rpms = kojiweb.util.paginateMethod(server, values, 'listRPMs', kw={'buildrootID': buildroot['id']},
|
|
start=start, dataName='rpms', prefix='rpm', order=order)
|
|
|
|
values['buildroot'] = buildroot
|
|
values['type'] = type
|
|
|
|
values['order'] = order
|
|
|
|
return _genHTML(req, 'rpmlist.chtml')
|
|
|
|
def buildtargets(req, start=None, order='name'):
|
|
values = _initValues(req, 'Build Targets', 'buildtargets')
|
|
server = _getServer(req)
|
|
|
|
targets = kojiweb.util.paginateMethod(server, values, 'getBuildTargets',
|
|
start=start, dataName='targets', prefix='target', order=order)
|
|
|
|
values['order'] = order
|
|
if req.currentUser:
|
|
values['perms'] = server.getUserPerms(req.currentUser['id'])
|
|
else:
|
|
values['perms'] = []
|
|
|
|
return _genHTML(req, 'buildtargets.chtml')
|
|
|
|
def buildtargetinfo(req, targetID=None, name=None):
|
|
values = _initValues(req, 'Build Target Info', 'buildtargets')
|
|
server = _getServer(req)
|
|
|
|
target = None
|
|
if targetID != None:
|
|
targetID = int(targetID)
|
|
target = server.getBuildTarget(targetID)
|
|
elif name != None:
|
|
target = server.getBuildTarget(name)
|
|
|
|
if target == None:
|
|
raise koji.GenericError, 'invalid build target: %s' % (targetID or name)
|
|
|
|
values['title'] = target['name'] + ' | Build Target Info'
|
|
|
|
buildTag = server.getTag(target['build_tag'])
|
|
destTag = server.getTag(target['dest_tag'])
|
|
|
|
values['target'] = target
|
|
values['buildTag'] = buildTag
|
|
values['destTag'] = destTag
|
|
if req.currentUser:
|
|
values['perms'] = server.getUserPerms(req.currentUser['id'])
|
|
else:
|
|
values['perms'] = []
|
|
|
|
return _genHTML(req, 'buildtargetinfo.chtml')
|
|
|
|
def buildtargetedit(req, targetID):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
targetID = int(targetID)
|
|
|
|
target = server.getBuildTarget(targetID)
|
|
if target == None:
|
|
raise koji.GenericError, 'invalid build target: %s' % targetID
|
|
|
|
form = req.form
|
|
|
|
if form.has_key('save'):
|
|
name = form['name'].value
|
|
buildTagID = int(form['buildTag'])
|
|
buildTag = server.getTag(buildTagID)
|
|
if buildTag == None:
|
|
raise koji.GenericError, 'invalid tag ID: %i' % buildTagID
|
|
|
|
destTagID = int(form['destTag'])
|
|
destTag = server.getTag(destTagID)
|
|
if destTag == None:
|
|
raise koji.GenericError, 'invalid tag ID: %i' % destTagID
|
|
|
|
server.editBuildTarget(target['id'], name, buildTag['id'], destTag['id'])
|
|
|
|
mod_python.util.redirect(req, 'buildtargetinfo?targetID=%i' % target['id'])
|
|
elif form.has_key('cancel'):
|
|
mod_python.util.redirect(req, 'buildtargetinfo?targetID=%i' % target['id'])
|
|
else:
|
|
values = _initValues(req, 'Edit Build Target', 'buildtargets')
|
|
tags = server.listTags()
|
|
tags.sort(_sortbyname)
|
|
|
|
values['target'] = target
|
|
values['tags'] = tags
|
|
|
|
return _genHTML(req, 'buildtargetedit.chtml')
|
|
|
|
def buildtargetcreate(req):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
form = req.form
|
|
|
|
if form.has_key('add'):
|
|
# Use the str .value field of the StringField object,
|
|
# since xmlrpclib doesn't know how to marshal the StringFields
|
|
# returned by mod_python
|
|
name = form['name'].value
|
|
buildTagID = int(form['buildTag'])
|
|
destTagID = int(form['destTag'])
|
|
|
|
server.createBuildTarget(name, buildTagID, destTagID)
|
|
target = server.getBuildTarget(name)
|
|
|
|
if target == None:
|
|
raise koji.GenericError, 'error creating build target "%s"' % name
|
|
|
|
mod_python.util.redirect(req, 'buildtargetinfo?targetID=%i' % target['id'])
|
|
elif form.has_key('cancel'):
|
|
mod_python.util.redirect(req, 'buildtargets')
|
|
else:
|
|
values = _initValues(req, 'Add Build Target', 'builtargets')
|
|
|
|
tags = server.listTags()
|
|
tags.sort(_sortbyname)
|
|
|
|
values['target'] = None
|
|
values['tags'] = tags
|
|
|
|
return _genHTML(req, 'buildtargetedit.chtml')
|
|
|
|
def buildtargetdelete(req, targetID):
|
|
server = _getServer(req)
|
|
_assertLogin(req)
|
|
|
|
targetID = int(targetID)
|
|
|
|
target = server.getBuildTarget(targetID)
|
|
if target == None:
|
|
raise koji.GenericError, 'invalid build target: %i' % targetID
|
|
|
|
server.deleteBuildTarget(target['id'])
|
|
|
|
mod_python.util.redirect(req, 'buildtargets')
|
|
|
|
def reports(req):
|
|
server = _getServer(req)
|
|
values = _initValues(req, 'Reports', 'reports')
|
|
return _genHTML(req, 'reports.chtml')
|
|
|
|
def buildsbyuser(req, start=None, order='-builds'):
|
|
values = _initValues(req, 'Builds by User', 'reports')
|
|
server = _getServer(req)
|
|
|
|
maxBuilds = 1
|
|
users = server.listUsers()
|
|
|
|
server.multicall = True
|
|
for user in users:
|
|
server.listBuilds(userID=user['id'], queryOpts={'countOnly': True})
|
|
buildCounts = server.multiCall()
|
|
|
|
for user, [numBuilds] in zip(users, buildCounts):
|
|
user['builds'] = numBuilds
|
|
if numBuilds > maxBuilds:
|
|
maxBuilds = numBuilds
|
|
|
|
values['order'] = order
|
|
|
|
graphWidth = 400.0
|
|
values['graphWidth'] = graphWidth
|
|
values['maxBuilds'] = maxBuilds
|
|
values['increment'] = graphWidth / maxBuilds
|
|
kojiweb.util.paginateList(values, users, start, 'userBuilds', 'userBuild', order)
|
|
|
|
return _genHTML(req, 'buildsbyuser.chtml')
|
|
|
|
def rpmsbyhost(req, start=None, order=None, hostArch=None, rpmArch=None):
|
|
values = _initValues(req, 'RPMs by Host', 'reports')
|
|
server = _getServer(req)
|
|
|
|
maxRPMs = 1
|
|
hostArchFilter = hostArch
|
|
if hostArchFilter == 'ix86':
|
|
hostArchFilter = ['i386', 'i486', 'i586', 'i686']
|
|
hosts = server.listHosts(arches=hostArchFilter)
|
|
rpmArchFilter = rpmArch
|
|
if rpmArchFilter == 'ix86':
|
|
rpmArchFilter = ['i386', 'i486', 'i586', 'i686']
|
|
|
|
server.multicall = True
|
|
for host in hosts:
|
|
server.listRPMs(hostID=host['id'], arches=rpmArchFilter, queryOpts={'countOnly': True})
|
|
rpmCounts = server.multiCall()
|
|
|
|
for host, [numRPMs] in zip(hosts, rpmCounts):
|
|
host['rpms'] = numRPMs
|
|
if numRPMs > maxRPMs:
|
|
maxRPMs = numRPMs
|
|
|
|
values['hostArch'] = hostArch
|
|
hostArchList = server.getAllArches()
|
|
hostArchList.sort()
|
|
values['hostArchList'] = hostArchList
|
|
values['rpmArch'] = rpmArch
|
|
values['rpmArchList'] = hostArchList + ['noarch', 'src']
|
|
|
|
if order == None:
|
|
order = '-rpms'
|
|
values['order'] = order
|
|
|
|
graphWidth = 400.0
|
|
values['graphWidth'] = graphWidth
|
|
values['maxRPMs'] = maxRPMs
|
|
values['increment'] = graphWidth / maxRPMs
|
|
kojiweb.util.paginateList(values, hosts, start, 'hosts', 'host', order)
|
|
|
|
return _genHTML(req, 'rpmsbyhost.chtml')
|
|
|
|
def packagesbyuser(req, start=None, order=None):
|
|
values = _initValues(req, 'Packages by User', 'reports')
|
|
server = _getServer(req)
|
|
|
|
maxPackages = 1
|
|
users = server.listUsers()
|
|
|
|
server.multicall = True
|
|
for user in users:
|
|
server.count('listPackages', userID=user['id'], with_dups=True)
|
|
packageCounts = server.multiCall()
|
|
|
|
for user, [numPackages] in zip(users, packageCounts):
|
|
user['packages'] = numPackages
|
|
if numPackages > maxPackages:
|
|
maxPackages = numPackages
|
|
|
|
if order == None:
|
|
order = '-packages'
|
|
values['order'] = order
|
|
|
|
graphWidth = 400.0
|
|
values['graphWidth'] = graphWidth
|
|
values['maxPackages'] = maxPackages
|
|
values['increment'] = graphWidth / maxPackages
|
|
kojiweb.util.paginateList(values, users, start, 'users', 'user', order)
|
|
|
|
return _genHTML(req, 'packagesbyuser.chtml')
|
|
|
|
def tasksbyhost(req, start=None, order='-tasks', hostArch=None):
|
|
values = _initValues(req, 'Tasks by Host', 'reports')
|
|
server = _getServer(req)
|
|
|
|
maxTasks = 1
|
|
|
|
hostArchFilter = hostArch
|
|
if hostArchFilter == 'ix86':
|
|
hostArchFilter = ['i386', 'i486', 'i586', 'i686']
|
|
|
|
hosts = server.listHosts(arches=hostArchFilter)
|
|
|
|
server.multicall = True
|
|
for host in hosts:
|
|
server.listTasks(opts={'host_id': host['id']}, queryOpts={'countOnly': True})
|
|
taskCounts = server.multiCall()
|
|
|
|
for host, [numTasks] in zip(hosts, taskCounts):
|
|
host['tasks'] = numTasks
|
|
if numTasks > maxTasks:
|
|
maxTasks = numTasks
|
|
|
|
values['hostArch'] = hostArch
|
|
hostArchList = server.getAllArches()
|
|
hostArchList.sort()
|
|
values['hostArchList'] = hostArchList
|
|
|
|
values['order'] = order
|
|
|
|
graphWidth = 400.0
|
|
values['graphWidth'] = graphWidth
|
|
values['maxTasks'] = maxTasks
|
|
values['increment'] = graphWidth / maxTasks
|
|
kojiweb.util.paginateList(values, hosts, start, 'hosts', 'host', order)
|
|
|
|
return _genHTML(req, 'tasksbyhost.chtml')
|
|
|
|
def tasksbyuser(req, start=None, order='-tasks'):
|
|
values = _initValues(req, 'Tasks by User', 'reports')
|
|
server = _getServer(req)
|
|
|
|
maxTasks = 1
|
|
|
|
users = server.listUsers()
|
|
|
|
server.multicall = True
|
|
for user in users:
|
|
server.listTasks(opts={'owner': user['id']}, queryOpts={'countOnly': True})
|
|
taskCounts = server.multiCall()
|
|
|
|
for user, [numTasks] in zip(users, taskCounts):
|
|
user['tasks'] = numTasks
|
|
if numTasks > maxTasks:
|
|
maxTasks = numTasks
|
|
|
|
values['order'] = order
|
|
|
|
graphWidth = 400.0
|
|
values['graphWidth'] = graphWidth
|
|
values['maxTasks'] = maxTasks
|
|
values['increment'] = graphWidth / maxTasks
|
|
kojiweb.util.paginateList(values, users, start, 'users', 'user', order)
|
|
|
|
return _genHTML(req, 'tasksbyuser.chtml')
|
|
|
|
def buildsbystatus(req, days='7'):
|
|
values = _initValues(req, 'Builds by Status', 'reports')
|
|
server = _getServer(req)
|
|
|
|
days = int(days)
|
|
if days != -1:
|
|
seconds = 60 * 60 * 24 * days
|
|
dateAfter = time.time() - seconds
|
|
else:
|
|
dateAfter = None
|
|
values['days'] = days
|
|
|
|
server.multicall = True
|
|
# use taskID=-1 to filter out builds with a null task_id (imported rather than built in koji)
|
|
server.listBuilds(completeAfter=dateAfter, state=koji.BUILD_STATES['COMPLETE'], taskID=-1, queryOpts={'countOnly': True})
|
|
server.listBuilds(completeAfter=dateAfter, state=koji.BUILD_STATES['FAILED'], taskID=-1, queryOpts={'countOnly': True})
|
|
server.listBuilds(completeAfter=dateAfter, state=koji.BUILD_STATES['CANCELED'], taskID=-1, queryOpts={'countOnly': True})
|
|
[[numSucceeded], [numFailed], [numCanceled]] = server.multiCall()
|
|
|
|
values['numSucceeded'] = numSucceeded
|
|
values['numFailed'] = numFailed
|
|
values['numCanceled'] = numCanceled
|
|
|
|
maxBuilds = 1
|
|
for value in (numSucceeded, numFailed, numCanceled):
|
|
if value > maxBuilds:
|
|
maxBuilds = value
|
|
|
|
graphWidth = 400.0
|
|
values['graphWidth'] = graphWidth
|
|
values['maxBuilds'] = maxBuilds
|
|
values['increment'] = graphWidth / maxBuilds
|
|
|
|
return _genHTML(req, 'buildsbystatus.chtml')
|
|
|
|
def buildsbytarget(req, days='7', start=None, order='-builds'):
|
|
values = _initValues(req, 'Builds by Target', 'reports')
|
|
server = _getServer(req)
|
|
|
|
days = int(days)
|
|
if days != -1:
|
|
seconds = 60 * 60 * 24 * days
|
|
dateAfter = time.time() - seconds
|
|
else:
|
|
dateAfter = None
|
|
values['days'] = days
|
|
|
|
targets = {}
|
|
maxBuilds = 1
|
|
|
|
tasks = server.listTasks(opts={'method': 'build', 'completeAfter': dateAfter, 'decode': True})
|
|
|
|
for task in tasks:
|
|
targetName = task['request'][1]
|
|
target = targets.get(targetName)
|
|
if not target:
|
|
target = {'name': targetName}
|
|
targets[targetName] = target
|
|
builds = target.get('builds', 0) + 1
|
|
target['builds'] = builds
|
|
if builds > maxBuilds:
|
|
maxBuilds = builds
|
|
|
|
kojiweb.util.paginateList(values, targets.values(), start, 'targets', 'target', order)
|
|
|
|
values['order'] = order
|
|
|
|
graphWidth = 400.0
|
|
values['graphWidth'] = graphWidth
|
|
values['maxBuilds'] = maxBuilds
|
|
values['increment'] = graphWidth / maxBuilds
|
|
|
|
return _genHTML(req, 'buildsbytarget.chtml')
|
|
|
|
def recentbuilds(req, user=None, tag=None, package=None):
|
|
values = _initValues(req, 'Recent Build RSS')
|
|
server = _getServer(req)
|
|
|
|
tagObj = None
|
|
if tag != None:
|
|
if tag.isdigit():
|
|
tag = int(tag)
|
|
tagObj = server.getTag(tag)
|
|
|
|
userObj = None
|
|
if user != None:
|
|
if user.isdigit():
|
|
user = int(user)
|
|
userObj = server.getUser(user)
|
|
|
|
packageObj = None
|
|
if package:
|
|
if package.isdigit():
|
|
package = int(package)
|
|
packageObj = server.getPackage(package)
|
|
|
|
if tagObj != None:
|
|
builds = server.listTagged(tagObj['id'], inherit=True, package=(packageObj and packageObj['name'] or None),
|
|
owner=(userObj and userObj['name'] or None))
|
|
builds.sort(kojiweb.util.sortByKeyFunc('-completion_time', noneGreatest=True))
|
|
builds = builds[:20]
|
|
else:
|
|
kwargs = {}
|
|
if userObj:
|
|
kwargs['userID'] = userObj['id']
|
|
if packageObj:
|
|
kwargs['packageID'] = packageObj['id']
|
|
builds = server.listBuilds(queryOpts={'order': '-completion_time', 'limit': 20}, **kwargs)
|
|
|
|
server.multicall = True
|
|
for build in builds:
|
|
if build['task_id']:
|
|
server.getTaskInfo(build['task_id'], request=True)
|
|
else:
|
|
server.echo(None)
|
|
tasks = server.multiCall()
|
|
|
|
server.multicall = True
|
|
queryOpts = {'limit': 3}
|
|
for build in builds:
|
|
if build['state'] == koji.BUILD_STATES['COMPLETE']:
|
|
server.getChangelogEntries(build['build_id'], queryOpts=queryOpts)
|
|
else:
|
|
server.echo(None)
|
|
clogs = server.multiCall()
|
|
|
|
for i in range(len(builds)):
|
|
task = tasks[i][0]
|
|
if isinstance(task, list):
|
|
# this is the output of server.echo(None) above
|
|
task = None
|
|
builds[i]['task'] = task
|
|
builds[i]['changelog'] = clogs[i][0]
|
|
|
|
values['tag'] = tagObj
|
|
values['user'] = userObj
|
|
values['package'] = packageObj
|
|
values['builds'] = builds
|
|
values['weburl'] = _getBaseURL(req)
|
|
|
|
req.content_type = 'text/xml'
|
|
return _genHTML(req, 'recentbuilds.chtml')
|
|
|
|
_infoURLs = {'package': 'packageinfo?packageID=%(id)i',
|
|
'build': 'buildinfo?buildID=%(id)i',
|
|
'tag': 'taginfo?tagID=%(id)i',
|
|
'target': 'buildtargetinfo?targetID=%(id)i',
|
|
'user': 'userinfo?userID=%(id)i',
|
|
'host': 'hostinfo?hostID=%(id)i',
|
|
'rpm': 'rpminfo?rpmID=%(id)i',
|
|
'file': 'fileinfo?rpmID=%(id)i&filename=%(name)s'}
|
|
|
|
def search(req, start=None, order='name'):
|
|
values = _initValues(req, 'Search', 'search')
|
|
server = _getServer(req)
|
|
|
|
form = req.form
|
|
if form.has_key('terms') and form['terms']:
|
|
terms = form['terms'].value
|
|
type = form['type'].value
|
|
match = form['match'].value
|
|
values['terms'] = terms
|
|
values['type'] = type
|
|
values['match'] = match
|
|
|
|
if match == 'regexp':
|
|
try:
|
|
re.compile(terms)
|
|
except:
|
|
raise koji.GenericError, 'invalid regular expression: %s' % terms
|
|
|
|
infoURL = _infoURLs.get(type)
|
|
if not infoURL:
|
|
raise koji.GenericError, 'unknown search type: %s' % type
|
|
values['infoURL'] = infoURL
|
|
values['order'] = order
|
|
|
|
results = kojiweb.util.paginateMethod(server, values, 'search', args=(terms, type, match),
|
|
start=start, dataName='results', prefix='result', order=order)
|
|
if not start and len(results) == 1:
|
|
# if we found exactly one result, skip the result list and redirect to the info page
|
|
# (you're feeling lucky)
|
|
mod_python.util.redirect(req, infoURL % results[0])
|
|
else:
|
|
return _genHTML(req, 'searchresults.chtml')
|
|
else:
|
|
return _genHTML(req, 'search.chtml')
|
|
|
|
def watchlogs(req, taskID):
|
|
html = """
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
<html>
|
|
<head>
|
|
<script type="text/javascript" src="/koji-static/js/jsolait/init.js"></script>
|
|
<script type="text/javascript" src="/koji-static/js/watchlogs.js"></script>
|
|
<title>Logs for task %i | Koji</title>
|
|
</head>
|
|
<body onload="main()">
|
|
<pre id="logs">
|
|
Loading logs...
|
|
</pre>
|
|
</body>
|
|
</html>
|
|
""" % int(taskID)
|
|
return html
|