flake8: apply E3 rules

This commit is contained in:
Yuming Zhu 2020-02-26 02:31:31 +08:00
parent 05340b146b
commit 0f727a2ab4
32 changed files with 534 additions and 70 deletions

View file

@ -1,5 +1,5 @@
[flake8]
select = I,C,F,E1,E2
select = I,C,F,E1,E2,E3
ignore = E266
exclude =
.git,

View file

@ -127,6 +127,7 @@ try:
except ImportError: # pragma: no cover
ozif_enabled = False
def main(options, session):
logger = logging.getLogger("koji.build")
logger.info('Starting up')
@ -140,8 +141,10 @@ def main(options, session):
for name in options.plugin:
logger.info('Loading plugin: %s' % name)
tm.scanPlugin(pt.load(name))
def shutdown(*args):
raise SystemExit
def restart(*args):
logger.warn("Initiating graceful restart")
tm.restart_pending = True
@ -590,7 +593,6 @@ class BuildRoot(object):
self.expire()
raise koji.BuildError("error building srpm, %s" % self._mockResult(rv))
def build_srpm(self, specfile, sourcedir, source_cmd):
self.session.host.setBuildRootState(self.id, 'BUILDING')
if source_cmd:
@ -1162,7 +1164,6 @@ class BuildTask(BaseTaskHandler):
raise koji.BuildError("No matching arches were found")
return to_list(archdict.keys())
def choose_taskarch(self, arch, srpm, build_tag):
"""Adjust the arch for buildArch subtask as needed"""
if koji.util.multi_fnmatch(arch, self.options.literal_task_arches):
@ -1203,7 +1204,6 @@ class BuildTask(BaseTaskHandler):
# otherwise, noarch is ok
return 'noarch'
def runBuilds(self, srpm, build_tag, archlist, repo_id, failany=True):
self.logger.debug("Spawning jobs for arches: %r" % (archlist))
subtasks = {}
@ -1456,6 +1456,7 @@ class BuildArchTask(BaseBuildTask):
return ret
class MavenTask(MultiPlatformTask):
Methods = ['maven']
@ -1542,6 +1543,7 @@ class MavenTask(MultiPlatformTask):
arch='noarch')
self.wait(tag_task_id)
class BuildMavenTask(BaseBuildTask):
Methods = ['buildMaven']
@ -1633,7 +1635,6 @@ class BuildMavenTask(BaseBuildTask):
logfile = self.workdir + '/checkout.log'
uploadpath = self.getUploadDir()
self.run_callbacks('preSCMCheckout', scminfo=scm.get_info(), build_tag=build_tag, scratch=opts.get('scratch'))
# Check out sources from the SCM
sourcedir = scm.checkout(scmdir, self.session, uploadpath, logfile)
@ -1767,6 +1768,7 @@ class BuildMavenTask(BaseBuildTask):
'logs': logs,
'files': output_files}
class WrapperRPMTask(BaseBuildTask):
"""Build a wrapper rpm around archives output from a Maven or Windows build.
May either be called as a subtask or as a separate
@ -2118,6 +2120,7 @@ class WrapperRPMTask(BaseBuildTask):
return results
class ChainMavenTask(MultiPlatformTask):
Methods = ['chainmaven']
@ -2328,6 +2331,7 @@ class ChainMavenTask(MultiPlatformTask):
# everything matches
return build
class TagBuildTask(BaseTaskHandler):
Methods = ['tagBuild']
@ -2352,6 +2356,7 @@ class TagBuildTask(BaseTaskHandler):
self.session.host.tagNotification(False, tag_id, fromtag, build_id, user_id, ignore_success, "%s: %s" % (exctype, value))
raise e
class BuildImageTask(MultiPlatformTask):
def initImageBuild(self, name, version, release, target_info, opts):
@ -2372,6 +2377,7 @@ class BuildImageTask(MultiPlatformTask):
"""return the next available release number for an N-V"""
return self.session.getNextRelease(dict(name=name, version=ver))
class BuildBaseImageTask(BuildImageTask):
Methods = ['image']
@ -2524,7 +2530,6 @@ class BuildApplianceTask(BuildImageTask):
if koji.canonArch(arch) not in tag_archlist:
raise koji.BuildError("Invalid arch for build tag: %s" % arch)
if not opts:
opts = {}
@ -2593,6 +2598,7 @@ class BuildApplianceTask(BuildImageTask):
report += 'appliance build results in: %s' % respath
return report
class BuildLiveCDTask(BuildImageTask):
Methods = ['livecd']
@ -2727,7 +2733,6 @@ class BuildLiveMediaTask(BuildImageTask):
canfail.append(subtasks[arch])
self.logger.debug("Tasks that can fail: %r", canfail)
self.logger.debug("Got image subtasks: %r", subtasks)
self.logger.debug("Waiting on livemedia subtasks...")
results = self.wait(to_list(subtasks.values()), all=True, failany=True, canfail=canfail)
@ -3136,6 +3141,8 @@ class ApplianceTask(ImageTask):
# via the livecd-build group. livecd-creator is then executed in the chroot
# to create the LiveCD image.
#
class LiveCDTask(ImageTask):
Methods = ['createLiveCD']
@ -3208,7 +3215,6 @@ class LiveCDTask(ImageTask):
return manifest
def handler(self, name, version, release, arch, target_info, build_tag, repo_info, ksfile, opts=None):
if opts == None:
@ -3288,7 +3294,6 @@ class LiveCDTask(ImageTask):
return imgdata
# livemedia-creator
class LiveMediaTask(ImageTask):
@ -3406,7 +3411,6 @@ class LiveMediaTask(ImageTask):
livemedia_log = broot.tmpdir(within=True) + '/lmc-logs/livemedia-out.log'
resultdir = broot.tmpdir(within=True) + '/lmc'
# Common LMC command setup, needs extending
cmd = ['/sbin/livemedia-creator',
'--ks', kskoji,
@ -3417,7 +3421,6 @@ class LiveMediaTask(ImageTask):
# '--tmp', '/tmp'
]
volid = opts.get('volid')
if not volid:
volid = self._shortenVolID(name, version, release)
@ -3437,7 +3440,6 @@ class LiveMediaTask(ImageTask):
'--releasever', version,
])
if arch == 'x86_64':
cmd.append('--macboot')
@ -3445,7 +3447,6 @@ class LiveMediaTask(ImageTask):
templates_dir = self.fetch_lorax_templates_from_scm(broot)
cmd.extend(['--lorax-templates', templates_dir])
# Run livemedia-creator
rv = broot.mock(['--cwd', broot.tmpdir(within=True), '--chroot', '--'] + cmd)
@ -3490,7 +3491,6 @@ class LiveMediaTask(ImageTask):
raise koji.LiveMediaError('could not find iso file in chroot')
isosrc = os.path.join(rootresultsdir, isofile)
# Generate the file manifest of the image, upload the results
manifest = os.path.join(broot.resultdir(), 'manifest.log')
self.genISOManifest(isosrc, manifest)
@ -3522,6 +3522,8 @@ class LiveMediaTask(ImageTask):
# A generic task for building disk images using Oz
# Other Oz-based image handlers should inherit this.
class OzImageTask(BaseTaskHandler):
Methods = []
@ -3757,7 +3759,6 @@ class OzImageTask(BaseTaskHandler):
raise koji.BuildError('Unknown or supported distro given: %s' % distro)
def fixImageXML(self, format, filename, xmltext):
"""
The XML generated by Oz/ImageFactory knows nothing about the name
or image format conversions Koji does. We fix those values in the
@ -3803,6 +3804,7 @@ class OzImageTask(BaseTaskHandler):
screenshot = found[0]
return screenshot
class BaseImageTask(OzImageTask):
Methods = ['createImage']
@ -4003,7 +4005,6 @@ class BaseImageTask(OzImageTask):
logerror=1)
return {'image': newimg}
def _buildTarGZ(self, format):
"""
Use tar and gzip to compress a raw disk image.
@ -4034,7 +4035,6 @@ class BaseImageTask(OzImageTask):
return {'image': newimg}
def _buildSquashfs(self, format):
"""
Use squashfs to wrap a raw disk image into liveimg compatible image.
@ -4309,6 +4309,7 @@ class BaseImageTask(OzImageTask):
# no need to delete anything since self.workdir will get scrubbed
return imgdata
class BuildIndirectionImageTask(OzImageTask):
Methods = ['indirectionimage']
@ -4550,7 +4551,6 @@ class BuildIndirectionImageTask(OzImageTask):
# reraise the exception
raise
def _do_indirection(self, opts, base_factory_image, utility_factory_image,
indirection_template, tlog, ozlog, fhandler, bld_info,
target_info, bd):
@ -4861,6 +4861,7 @@ class BuildSRPMFromSCMTask(BaseBuildTask):
'source': source,
}
class TagNotificationTask(BaseTaskHandler):
Methods = ['tagNotification']
@ -4952,6 +4953,7 @@ Status: %(status)s\r
return 'sent notification of tag operation %i to: %s' % (self.id, to_addrs)
class BuildNotificationTask(BaseTaskHandler):
Methods = ['buildNotification']
@ -5633,7 +5635,6 @@ class createDistRepoTask(BaseTaskHandler):
raise koji.GenericError('failed to create repo: %s' \
% parseStatus(status, ' '.join(cmd)))
def do_multilib_dnf(self, arch, ml_arch, conf):
repodir = koji.pathinfo.distrepo(self.rinfo['id'], self.rinfo['tag_name'])
mldir = os.path.join(repodir, koji.canonArch(ml_arch))
@ -5760,7 +5761,6 @@ enabled=1
rpminfo['_multilib'] = True
self.kojipkgs[bnp] = rpminfo
def do_multilib_yum(self, arch, ml_arch, conf):
repodir = koji.pathinfo.distrepo(self.rinfo['id'], self.rinfo['tag_name'])
mldir = os.path.join(repodir, koji.canonArch(ml_arch))
@ -6303,6 +6303,7 @@ def get_options():
return options
def quit(msg=None, code=1):
if msg:
logging.getLogger("koji.build").error(msg)
@ -6310,6 +6311,7 @@ def quit(msg=None, code=1):
sys.stderr.flush()
sys.exit(code)
if __name__ == "__main__":
koji.add_file_logger("koji", "/var/log/kojid.log")
# note we're setting logging params for all of koji*

View file

@ -57,6 +57,7 @@ MULTILIB_ARCHES = {
's390x': 's390'
}
def parse_args(args):
"""Parse our opts/args"""
usage = """
@ -337,6 +338,7 @@ class RepoMerge(object):
mdgen.doRepoMetadata()
mdgen.doFinalMove()
def main(args):
"""main"""
opts = parse_args(args)
@ -358,5 +360,6 @@ def main(args):
finally:
merge.close()
if __name__ == "__main__":
main(sys.argv[1:])

View file

@ -2617,6 +2617,7 @@ def anon_handle_list_groups(goptions, session, args):
groups = [x[1] for x in tmp_list]
tags_cache = {}
def get_cached_tag(tag_id):
if tag_id not in tags_cache:
tag = session.getTag(tag_id, strict=False)
@ -4311,6 +4312,7 @@ def _print_histline(entry, **kwargs):
dkey = key
print(" %s: %s" % (dkey, x[key]))
_table_keys = {
'user_perms': ['user_id', 'perm_id'],
'user_groups': ['user_id', 'group_id'],
@ -5635,7 +5637,6 @@ def handle_image_build_indirection(options, session, args):
parser.add_option("--noprogress", action="store_true",
help=_("Do not display progress of the upload"))
(task_options, args) = parser.parse_args(args)
_build_image_indirection(options, task_options, session, args)
@ -5694,7 +5695,6 @@ def _build_image_indirection(options, task_opts, session, args):
# Set the architecture
task_opts.arch = koji.canonArch(task_opts.arch)
# Upload the indirection template file to the staging area.
# If it's a URL, it's kojid's job to go get it when it does the checkout.
if not task_opts.indirection_template_url:
@ -6907,7 +6907,6 @@ def anon_handle_wait_repo(options, session, args):
return 1
tag_id = tag_info['id']
for nvr in builds:
data = session.getLatestBuilds(tag_id, package=nvr["name"])
if len(data) == 0:
@ -7141,6 +7140,7 @@ def handle_dist_repo(options, session, args):
_search_types = ('package', 'build', 'tag', 'target', 'user', 'host', 'rpm',
'maven', 'win')
def anon_handle_search(options, session, args):
"[search] Search the system"
usage = _("usage: %prog search [options] <search_type> <pattern>")

View file

@ -105,6 +105,7 @@ Available categories are: %(categories)s
def get_usage_str(usage):
return usage + _("\n(Specify the --help global option for a list of other help options)")
def ensure_connection(session):
try:
ret = session.getAPIVersion()
@ -380,7 +381,6 @@ def watch_logs(session, tasklist, opts, poll_interval):
lastlog = currlog
bytes_to_stdout(contents)
if opts.follow:
for child in session.getTaskChildren(task_id):
if child['id'] not in tasklist:

File diff suppressed because it is too large Load diff

View file

@ -379,6 +379,7 @@ def offline_reply(start_response, msg=None):
start_response('200 OK', headers)
return [response]
def load_config(environ):
"""Load configuration options
@ -509,6 +510,7 @@ def load_plugins(opts):
opts['OfflineMessage'] = 'configuration error'
return tracker
_default_policies = {
'build_from_srpm': '''
has_perm admin :: allow
@ -539,6 +541,7 @@ _default_policies = {
''',
}
def get_policy(opts, plugins):
if not opts.get('policy'):
return
@ -593,6 +596,7 @@ class HubFormatter(logging.Formatter):
record.user_name = None
return logging.Formatter.format(self, record)
def setup_logging1():
"""Set up basic logging, before options are loaded"""
global log_handler
@ -605,6 +609,7 @@ def setup_logging1():
log_handler.setLevel(logging.DEBUG)
logger.addHandler(log_handler)
def setup_logging2(opts):
global log_handler
"""Adjust logging based on configuration options"""
@ -659,6 +664,7 @@ def get_memory_usage():
size, res, shr, text, lib, data, dirty = statm
return res - shr
def server_setup(environ):
global opts, plugins, registry, policy
logger = logging.getLogger('koji')
@ -692,6 +698,7 @@ def server_setup(environ):
firstcall = True
firstcall_lock = threading.Lock()
def application(environ, start_response):
global firstcall
if firstcall:

View file

@ -91,12 +91,14 @@ except ImportError:
PROFILE_MODULES = {} # {module_name: module_instance}
def _(args):
"""Stub function for translation"""
return args # pragma: no cover
## Constants ##
RPM_HEADER_MAGIC = six.b('\x8e\xad\xe8')
RPM_TAG_HEADERSIGNATURES = 62
RPM_TAG_FILEDIGESTALGO = 5011
@ -130,6 +132,7 @@ for h in (
# BEGIN kojikamid dup #
class Enum(dict):
"""A simple class to track our enumerated constants
@ -178,6 +181,7 @@ class Enum(dict):
# END kojikamid dup #
API_VERSION = 1
TASK_STATES = Enum((
@ -290,10 +294,12 @@ DEFAULT_AUTH_TIMEOUT = 60
# Exceptions
PythonImportError = ImportError # will be masked by koji's one
class GenericError(Exception):
"""Base class for our custom exceptions"""
faultCode = 1000
fromFault = False
def __str__(self):
try:
return str(self.args[0]['args'][0])
@ -304,97 +310,120 @@ class GenericError(Exception):
return str(self.__dict__)
# END kojikamid dup #
class LockError(GenericError):
"""Raised when there is a lock conflict"""
faultCode = 1001
class AuthError(GenericError):
"""Raised when there is an error in authentication"""
faultCode = 1002
class TagError(GenericError):
"""Raised when a tagging operation fails"""
faultCode = 1003
class ActionNotAllowed(GenericError):
"""Raised when the session does not have permission to take some action"""
faultCode = 1004
# BEGIN kojikamid dup #
class BuildError(GenericError):
"""Raised when a build fails"""
faultCode = 1005
# END kojikamid dup #
class AuthLockError(AuthError):
"""Raised when a lock prevents authentication"""
faultCode = 1006
class AuthExpired(AuthError):
"""Raised when a session has expired"""
faultCode = 1007
class SequenceError(AuthError):
"""Raised when requests are received out of sequence"""
faultCode = 1008
class RetryError(AuthError):
"""Raised when a request is received twice and cannot be rerun"""
faultCode = 1009
class PreBuildError(BuildError):
"""Raised when a build fails during pre-checks"""
faultCode = 1010
class PostBuildError(BuildError):
"""Raised when a build fails during post-checks"""
faultCode = 1011
class BuildrootError(BuildError):
"""Raised when there is an error with the buildroot"""
faultCode = 1012
class FunctionDeprecated(GenericError):
"""Raised by a deprecated function"""
faultCode = 1013
class ServerOffline(GenericError):
"""Raised when the server is offline"""
faultCode = 1014
class LiveCDError(GenericError):
"""Raised when LiveCD Image creation fails"""
faultCode = 1015
class PluginError(GenericError):
"""Raised when there is an error with a plugin"""
faultCode = 1016
class CallbackError(PluginError):
"""Raised when there is an error executing a callback"""
faultCode = 1017
class ApplianceError(GenericError):
"""Raised when Appliance Image creation fails"""
faultCode = 1018
class ParameterError(GenericError):
"""Raised when an rpc call receives incorrect arguments"""
faultCode = 1019
class ImportError(GenericError):
"""Raised when an import fails"""
faultCode = 1020
class ConfigurationError(GenericError):
"""Raised when load of koji configuration fails"""
faultCode = 1021
class LiveMediaError(GenericError):
"""Raised when LiveMedia Image creation fails"""
faultCode = 1022
class MultiCallInProgress(object):
"""
Placeholder class to be returned by method calls when in the process of
@ -418,6 +447,7 @@ def convertFault(fault):
# otherwise...
return fault
def listFaults():
"""Return a list of faults
@ -442,6 +472,7 @@ def listFaults():
# functions for encoding/decoding optional arguments
def encode_args(*args, **opts):
"""The function encodes optional arguments as regular arguments.
@ -453,6 +484,7 @@ def encode_args(*args, **opts):
args = args + (opts,)
return args
def decode_args(*args):
"""Decodes optional arguments from a flat argument list
@ -468,6 +500,7 @@ def decode_args(*args):
args = args[:-1]
return args, opts
def decode_args2(args, names, strict=True):
"An alternate form of decode_args, returns a dictionary"
args, opts = decode_args(*args)
@ -477,6 +510,7 @@ def decode_args2(args, names, strict=True):
ret.update(opts)
return ret
def decode_int(n):
"""If n is not an integer, attempt to convert it"""
if isinstance(n, six.integer_types):
@ -486,6 +520,7 @@ def decode_int(n):
# commonly used functions
def safe_xmlrpc_loads(s):
"""Load xmlrpc data from a string, but catch faults"""
try:
@ -530,6 +565,7 @@ def ensuredir(directory):
# END kojikamid dup #
def daemonize():
"""Detach and run in background"""
pid = os.fork()
@ -553,6 +589,7 @@ def daemonize():
os.close(fd1)
os.close(fd2)
def multibyte(data):
"""Convert a list of bytes to an integer (network byte order)"""
sum = 0
@ -561,6 +598,7 @@ def multibyte(data):
sum += data[i] << (8 * (n - i - 1))
return sum
def find_rpm_sighdr(path):
"""Finds the offset and length of the signature header."""
# see Maximum RPM Appendix A: Format of the RPM File
@ -570,6 +608,7 @@ def find_rpm_sighdr(path):
sigsize = rpm_hdr_size(path, sig_start)
return (sig_start, sigsize)
def rpm_hdr_size(f, ofs=None):
"""Returns the length (in bytes) of the rpm header
@ -776,6 +815,7 @@ def rip_rpm_sighdr(src):
fo.close()
return sighdr
def rip_rpm_hdr(src):
"""Rip the main header out of an rpm"""
(start, size) = find_rpm_sighdr(src)
@ -787,6 +827,7 @@ def rip_rpm_hdr(src):
fo.close()
return hdr
def _ord(s):
# in python2 it is char/str, while in py3 it is already int/bytes
if isinstance(s, int):
@ -794,6 +835,7 @@ def _ord(s):
else:
return ord(s)
def __parse_packet_header(pgp_packet):
"""Parse pgp_packet header, return tag type and the rest of pgp_packet"""
byte0 = _ord(pgp_packet[0])
@ -828,6 +870,7 @@ def __parse_packet_header(pgp_packet):
raise ValueError('Invalid OpenPGP packet length')
return (tag, pgp_packet[offset:])
def __subpacket_key_ids(subs):
"""Parse v4 signature subpackets and return a list of issuer key IDs"""
res = []
@ -847,6 +890,7 @@ def __subpacket_key_ids(subs):
subs = subs[off + length:]
return res
def get_sigpacket_key_id(sigpacket):
"""Return ID of the key used to create sigpacket as a hexadecimal string"""
(tag, sigpacket) = __parse_packet_header(sigpacket)
@ -870,6 +914,7 @@ def get_sigpacket_key_id(sigpacket):
'Unknown PGP signature packet version %s' % _ord(sigpacket[0]))
return hex_string(key_id)
def get_sighdr_key(sighdr):
"""Parse the sighdr and return the sigkey"""
rh = RawHeader(sighdr)
@ -881,6 +926,7 @@ def get_sighdr_key(sighdr):
else:
return get_sigpacket_key_id(sig)
def splice_rpm_sighdr(sighdr, src, dst=None, bufsize=8192):
"""Write a copy of an rpm with signature header spliced in"""
(start, size) = find_rpm_sighdr(src)
@ -901,6 +947,7 @@ def splice_rpm_sighdr(sighdr, src, dst=None, bufsize=8192):
dst_fo.close()
return dst
def get_rpm_header(f, ts=None):
"""Return the rpm header."""
if rpm is None:
@ -998,6 +1045,7 @@ def get_header_fields(X, fields, src_arch=False):
ret[f] = get_header_field(hdr, f, src_arch=src_arch)
return ret
def parse_NVR(nvr):
"""split N-V-R into dictionary of data"""
ret = {}
@ -1018,6 +1066,7 @@ def parse_NVR(nvr):
ret['name'] = ret['name'][epochIndex + 1:]
return ret
def parse_NVRA(nvra):
"""split N-V-R.A.rpm into dictionary of data
@ -1061,6 +1110,7 @@ def check_NVR(nvr, strict=False):
else:
return False
def _check_NVR(nvr):
if isinstance(nvr, six.string_types):
nvr = parse_NVR(nvr)
@ -1107,6 +1157,7 @@ def is_debuginfo(name):
return (name.endswith('-debuginfo') or name.endswith('-debugsource') or
'-debuginfo-' in name)
def canonArch(arch):
"""Given an arch, return the "canonical" arch"""
# XXX - this could stand to be smarter, and we should probably
@ -1132,6 +1183,7 @@ def canonArch(arch):
else:
return arch
def parse_arches(arches, to_list=False, strict=False, allow_none=False):
"""Normalize user input for a list of arches.
@ -1195,8 +1247,10 @@ class POMHandler(xml.sax.handler.ContentHandler):
self.tag_content = None
self.values.clear()
ENTITY_RE = re.compile(r'&[A-Za-z0-9]+;')
def parse_pom(path=None, contents=None):
"""
Parse the Maven .pom file return a map containing information
@ -1236,6 +1290,7 @@ def parse_pom(path=None, contents=None):
raise GenericError('could not extract %s from POM: %s' % (field, (path or '<contents>')))
return values
def pom_to_maven_info(pominfo):
"""
Convert the output of parsing a POM into a format compatible
@ -1250,6 +1305,7 @@ def pom_to_maven_info(pominfo):
'version': pominfo['version']}
return maveninfo
def maven_info_to_nvr(maveninfo):
"""
Convert the maveninfo to NVR-compatible format.
@ -1264,6 +1320,7 @@ def maven_info_to_nvr(maveninfo):
nvr['package_name'] = nvr['name']
return nvr
def mavenLabel(maveninfo):
"""
Return a user-friendly label for the given maveninfo. maveninfo is
@ -1271,6 +1328,7 @@ def mavenLabel(maveninfo):
"""
return '%(group_id)s-%(artifact_id)s-%(version)s' % maveninfo
def hex_string(s):
"""Converts a string to a string of hex digits"""
return ''.join(['%02x' % _ord(x) for x in s])
@ -1339,6 +1397,7 @@ This is a meta-package that requires a defined group of packages
""")
return ''.join(data)
def generate_comps(groups, expand_groups=False):
"""Generate comps content from groups data"""
def boolean_text(x):
@ -1635,11 +1694,14 @@ name=build
return ''.join(parts)
def get_sequence_value(cursor, sequence):
cursor.execute("""SELECT nextval(%(sequence)s)""", locals())
return cursor.fetchone()[0]
# From Python Cookbook 2nd Edition, Recipe 8.6
def format_exc_plus():
""" Format the usual traceback information, followed by a listing of
all the local variables in each frame.
@ -1669,6 +1731,7 @@ def format_exc_plus():
rv += "<ERROR WHILE PRINTING VALUE>\n"
return rv
def openRemoteFile(relpath, topurl=None, topdir=None, tempdir=None):
"""Open a file on the main server (read-only)
@ -2085,6 +2148,7 @@ class PathInfo(object):
"""Return the output directory for the task with the given id"""
return self.work(volume=volume) + '/' + self.taskrelpath(task_id)
pathinfo = PathInfo()
@ -3171,6 +3235,7 @@ class DBHandler(logging.Handler):
A handler class which writes logging records, appropriately formatted,
to a database.
"""
def __init__(self, cnx, table, mapping=None):
"""
Initialize the handler.
@ -3221,6 +3286,7 @@ class DBHandler(logging.Handler):
except:
self.handleError(record)
def formatTime(value):
"""Format a timestamp so it looks nicer"""
if not value:
@ -3237,6 +3303,7 @@ def formatTime(value):
else:
return value
def formatTimeLong(value):
"""Format a timestamp to a more human-reable format, i.e.:
Sat, 07 Sep 2002 00:00:01 GMT
@ -3248,6 +3315,7 @@ def formatTimeLong(value):
localtime = time.mktime(time.strptime(formatTime(value), '%Y-%m-%d %H:%M:%S'))
return time.strftime('%a, %d %b %Y %H:%M:%S %Z', time.localtime(localtime))
def buildLabel(buildInfo, showEpoch=False):
"""Format buildInfo (dict) into a descriptive label."""
epoch = buildInfo.get('epoch')
@ -3262,6 +3330,7 @@ def buildLabel(buildInfo, showEpoch=False):
buildInfo.get('version'),
buildInfo.get('release'))
def _module_info(url):
module_info = ''
if '?' in url:
@ -3280,12 +3349,14 @@ def _module_info(url):
else:
return '%s:%s' % (repo_info, rev_info)
def taskLabel(taskInfo):
try:
return _taskLabel(taskInfo)
except Exception:
return "malformed task"
def _taskLabel(taskInfo):
"""Format taskInfo (dict) into a descriptive label."""
method = taskInfo['method']
@ -3405,10 +3476,13 @@ def _taskLabel(taskInfo):
else:
return '%s (%s)' % (method, arch)
CONTROL_CHARS = [chr(i) for i in range(32)]
NONPRINTABLE_CHARS = ''.join([c for c in CONTROL_CHARS if c not in '\r\n\t'])
if six.PY3:
NONPRINTABLE_CHARS_TABLE = dict.fromkeys(map(ord, NONPRINTABLE_CHARS), None)
def removeNonprintable(value):
# expects raw-encoded string, not unicode
if six.PY2:
@ -3506,12 +3580,14 @@ def add_file_logger(logger, fn):
handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s'))
logging.getLogger(logger).addHandler(handler)
def add_stderr_logger(logger):
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] {%(process)d} %(name)s:%(lineno)d %(message)s'))
handler.setLevel(logging.DEBUG)
logging.getLogger(logger).addHandler(handler)
def add_sys_logger(logger):
# For remote logging;
# address = ('host.example.com', logging.handlers.SysLogHandler.SYSLOG_UDP_PORT)
@ -3522,6 +3598,7 @@ def add_sys_logger(logger):
handler.setLevel(logging.INFO)
logging.getLogger(logger).addHandler(handler)
def add_mail_logger(logger, addr):
"""Adding e-mail logger
@ -3542,5 +3619,6 @@ def add_mail_logger(logger, addr):
handler.setLevel(logging.ERROR)
logging.getLogger(logger).addHandler(handler)
def remove_log_handler(logger, handler):
logging.getLogger(logger).removeHandler(handler)

View file

@ -97,6 +97,7 @@ _aux_vector = {
"hwcap": 0,
}
def legitMultiArchesInSameLib(arch=None):
# this is completely crackrock - if anyone has a better way I
# am all ears
@ -135,6 +136,8 @@ def canCoinstall(arch1, arch2):
return False
# this computes the difference between myarch and targetarch
def archDifference(myarch, targetarch):
if myarch == targetarch:
return 1
@ -145,9 +148,11 @@ def archDifference(myarch, targetarch):
return 0
return 0
def score(arch):
return archDifference(canonArch, arch)
def isMultiLibArch(arch=None):
"""returns true if arch is a multilib arch, false if not"""
if arch is None:
@ -164,6 +169,7 @@ def isMultiLibArch(arch=None):
return 0
def getBestArchFromList(archlist, myarch=None):
"""
return the best arch from the list for myarch if - myarch is not given,
@ -226,6 +232,7 @@ def getArchList(thisarch=None):
archlist.append('noarch')
return archlist
def _try_read_cpuinfo():
""" Try to read /proc/cpuinfo ... if we can't ignore errors (ie. proc not
mounted). """
@ -234,6 +241,7 @@ def _try_read_cpuinfo():
except:
return []
def _parse_auxv():
""" Read /proc/self/auxv and parse it into global dict for easier access
later on, very similar to what rpm does. """
@ -260,6 +268,7 @@ def _parse_auxv():
_aux_vector["hwcap"] = at_val
offset = offset + fmtlen
def getCanonX86Arch(arch):
#
if arch == "i586":
@ -285,6 +294,7 @@ def getCanonX86Arch(arch):
return arch
def getCanonARMArch(arch):
# the %{_target_arch} macro in rpm will let us know the abi we are using
target = rpm.expandMacro('%{_target_cpu}')
@ -294,6 +304,7 @@ def getCanonARMArch(arch):
return target
return arch
def getCanonPPCArch(arch):
# FIXME: should I do better handling for mac, etc?
if arch != "ppc64":
@ -324,6 +335,7 @@ def getCanonPPCArch(arch):
return "ppc64iseries"
return arch
def getCanonSPARCArch(arch):
# Deal with sun4v, sun4u, sun4m cases
SPARCtype = None
@ -348,6 +360,7 @@ def getCanonSPARCArch(arch):
return "sparcv8"
return arch
def getCanonX86_64Arch(arch):
if arch != "x86_64":
return arch
@ -366,6 +379,7 @@ def getCanonX86_64Arch(arch):
return "ia32e"
return arch
def getCanonArch(skipRpmPlatform=0):
if not skipRpmPlatform and os.access("/etc/rpm/platform", os.R_OK):
try:
@ -395,9 +409,12 @@ def getCanonArch(skipRpmPlatform=0):
return arch
canonArch = getCanonArch()
# this gets you the "compat" arch of a biarch pair
def getMultiArchInfo(arch=canonArch):
if arch in multilibArches:
return multilibArches[arch]
@ -408,6 +425,8 @@ def getMultiArchInfo(arch=canonArch):
# get the best usual userspace arch for the arch we're on. this is
# our arch unless we're on an arch that uses the secondary as its
# userspace (eg ppc64, sparc64)
def getBestArch(myarch=None):
if myarch:
arch = myarch
@ -422,6 +441,7 @@ def getBestArch(myarch=None):
return arch
def getBaseArch(myarch=None):
"""returns 'base' arch for myarch, if specified, or canonArch if not.
base arch is the arch before noarch in the arches dict if myarch is not
@ -467,6 +487,7 @@ def getBaseArch(myarch=None):
class ArchStorage(object):
"""class for keeping track of what arch we have set and doing various
permutations based on it"""
def __init__(self):
self.canonarch = None
self.basearch = None

View file

@ -546,7 +546,6 @@ class Session(object):
c.execute(q, {})
(session_id,) = c.fetchone()
# add session id to database
q = """
INSERT INTO sessions (id, user_id, key, hostip, authtype, master)
@ -799,6 +798,7 @@ def get_user_groups(user_id):
c.execute(q, locals())
return dict(c.fetchall())
def get_user_perms(user_id):
c = context.cnx.cursor()
q = """SELECT name
@ -808,6 +808,7 @@ def get_user_perms(user_id):
# return a list of permissions by name
return [row[0] for row in c.fetchall()]
def get_user_data(user_id):
c = context.cnx.cursor()
fields = ('name', 'status', 'usertype')
@ -818,28 +819,36 @@ def get_user_data(user_id):
return None
return dict(zip(fields, row))
def login(*args, **opts):
return context.session.login(*args, **opts)
def krbLogin(*args, **opts):
return context.session.krbLogin(*args, **opts)
def sslLogin(*args, **opts):
return context.session.sslLogin(*args, **opts)
def logout():
return context.session.logout()
def subsession():
return context.session.subsession()
def logoutChild(session_id):
return context.session.logoutChild(session_id)
def exclusiveSession(*args, **opts):
"""Make this session exclusive"""
return context.session.makeExclusive(*args, **opts)
def sharedSession():
"""Drop out of exclusive mode"""
return context.session.makeShared()

View file

@ -32,6 +32,7 @@ import six.moves._thread
class _data(object):
pass
class ThreadLocal(object):
def __init__(self):
object.__setattr__(self, '_tdict', {})

View file

@ -84,6 +84,7 @@ def incremental_upload(session, fname, fd, path, retries=5, logger=None):
logger.error("Error uploading file %s to %s at offset %d" % (fname, path, offset))
break
def fast_incremental_upload(session, fname, fd, path, retries, logger):
"""Like incremental_upload, but use the fast upload mechanism"""
@ -108,6 +109,7 @@ def fast_incremental_upload(session, fname, fd, path, retries, logger):
logger.error("Error uploading file %s to %s at offset %d" % (fname, path, offset))
break
def log_output(session, path, args, outfile, uploadpath, cwd=None, logerror=0, append=0, chroot=None, env=None):
"""Run command with output redirected. If chroot is not None, chroot to the directory specified
before running the command."""
@ -400,6 +402,7 @@ class SCM(object):
update_checkout_cmd = None
update_checkout_dir = None
env = None
def _run(cmd, chdir=None, fatal=False, log=True, _count=[0]):
if globals().get('KOJIKAMID'):
# we've been inserted into kojikamid, use its run()

View file

@ -55,6 +55,7 @@ _DBopts = None
# but play it safe anyway.
_DBconn = context.ThreadLocal()
class DBWrapper:
def __init__(self, cnx):
self.cnx = cnx
@ -151,13 +152,16 @@ def provideDBopts(**opts):
if _DBopts is None:
_DBopts = dict([i for i in opts.items() if i[1] is not None])
def setDBopts(**opts):
global _DBopts
_DBopts = opts
def getDBopts():
return _DBopts
def connect():
logger = logging.getLogger('koji.db')
global _DBconn

View file

@ -58,6 +58,7 @@ callbacks = {
'postSCMCheckout': [],
}
class PluginTracker(object):
def __init__(self, path=None, prefix='_koji_plugin__'):
@ -113,6 +114,7 @@ def export(f):
setattr(f, 'exported', True)
return f
def export_cli(f):
"""a decorator that marks a function as exported for CLI
@ -122,6 +124,7 @@ def export_cli(f):
setattr(f, 'exported_cli', True)
return f
def export_as(alias):
"""returns a decorator that marks a function as exported and gives it an alias
@ -133,6 +136,7 @@ def export_as(alias):
return f
return dec
def export_in(module, alias=None):
"""returns a decorator that marks a function as exported with a module prepended
@ -150,6 +154,7 @@ def export_in(module, alias=None):
return f
return dec
def callback(*cbtypes):
"""A decorator that indicates a function is a callback.
cbtypes is a list of callback types to register for. Valid
@ -162,6 +167,7 @@ def callback(*cbtypes):
return f
return dec
def ignore_error(f):
"""a decorator that marks a callback as ok to fail

View file

@ -50,12 +50,14 @@ class BaseSimpleTest(object):
class TrueTest(BaseSimpleTest):
name = 'true'
def run(self, data):
return True
class FalseTest(BaseSimpleTest):
name = 'false'
def run(self, data):
return False
@ -97,6 +99,7 @@ class BoolTest(BaseSimpleTest):
"""
name = 'bool'
field = None
def run(self, data):
args = self.str.split()[1:]
if self.field is None:
@ -121,6 +124,7 @@ class MatchTest(BaseSimpleTest):
"""
name = 'match'
field = None
def run(self, data):
args = self.str.split()[1:]
if self.field is None:

View file

@ -36,6 +36,7 @@ class BytesJSONEncoder(json.JSONEncoder):
return o.decode('utf-8')
return json.JSONEncoder.default(self, o)
class Rpmdiff:
# constants

View file

@ -19,8 +19,10 @@
# Authors:
# Mike McLean <mikem@redhat.com>
class ServerError(Exception):
"""Base class for our server-side-only exceptions"""
class ServerRedirect(ServerError):
"""Used to handle redirects"""

View file

@ -55,6 +55,7 @@ def scan_mounts(topdir):
mplist.sort(reverse=True)
return mplist
def umount_all(topdir):
"Unmount every mount under topdir"
logger = logging.getLogger("koji.build")
@ -69,6 +70,7 @@ def umount_all(topdir):
if remain:
raise koji.GenericError("Unmounting incomplete: %r" % remain)
def safe_rmtree(path, unmount=False, strict=True):
logger = logging.getLogger("koji.build")
if unmount:
@ -103,6 +105,7 @@ class ServerExit(Exception):
"""Raised to shutdown the server"""
pass
class ServerRestart(Exception):
"""Raised to restart the server"""
pass
@ -431,7 +434,6 @@ class BaseTaskHandler(object):
return dict(self.session.host.taskWaitResults(self.id, finished,
canfail=canfail))
def getUploadDir(self):
return koji.pathinfo.taskrelpath(self.id)
@ -579,7 +581,6 @@ class BaseTaskHandler(object):
repo_info = self.wait(task_id)[task_id]
return repo_info
def run_callbacks(self, plugin, *args, **kwargs):
if 'taskinfo' not in kwargs:
try:
@ -595,6 +596,7 @@ class BaseTaskHandler(object):
class FakeTask(BaseTaskHandler):
Methods = ['someMethod']
Foreground = True
def handler(self, *args):
self.logger.info("This is a fake task. Args: " + str(args))
return 42
@ -603,17 +605,21 @@ class FakeTask(BaseTaskHandler):
class SleepTask(BaseTaskHandler):
Methods = ['sleep']
_taskWeight = 0.25
def handler(self, n):
self.logger.info("Sleeping for %s seconds" % n)
time.sleep(n)
self.logger.info("Finished sleeping")
class ForkTask(BaseTaskHandler):
Methods = ['fork']
def handler(self, n=5, m=37):
for i in range(n):
os.spawnvp(os.P_NOWAIT, 'sleep', ['sleep', str(m)])
class WaitTestTask(BaseTaskHandler):
"""
Tests self.wait()
@ -624,6 +630,7 @@ class WaitTestTask(BaseTaskHandler):
"""
Methods = ['waittest']
_taskWeight = 0.1
def handler(self, count, seconds=10):
tasks = []
for i in range(count):
@ -638,6 +645,7 @@ class WaitTestTask(BaseTaskHandler):
class SubtaskTask(BaseTaskHandler):
Methods = ['subtask']
_taskWeight = 0.1
def handler(self, n=4):
if n > 0:
task_id = self.session.host.subtask(method='subtask',
@ -657,6 +665,7 @@ class DefaultTask(BaseTaskHandler):
"""Used when no matching method is found"""
Methods = ['default']
_taskWeight = 0.1
def handler(self, *args, **opts):
raise koji.GenericError("Invalid method: %s" % self.method)
@ -665,6 +674,7 @@ class ShutdownTask(BaseTaskHandler):
Methods = ['shutdown']
_taskWeight = 0.0
Foreground = True
def handler(self):
# note: this is a foreground task
raise ServerExit
@ -676,6 +686,7 @@ class RestartTask(BaseTaskHandler):
Methods = ['restart']
_taskWeight = 0.1
Foreground = True
def handler(self, host):
# note: this is a foreground task
if host['id'] != self.session.host.getID():
@ -690,6 +701,7 @@ class RestartVerifyTask(BaseTaskHandler):
Methods = ['restartVerify']
_taskWeight = 0.1
Foreground = True
def handler(self, task_id, host):
# note: this is a foreground task
tinfo = self.session.getTaskInfo(task_id)
@ -708,6 +720,7 @@ class RestartHostsTask(BaseTaskHandler):
Methods = ['restartHosts']
_taskWeight = 0.1
def handler(self, options=None):
if options is None:
options = {}
@ -784,6 +797,7 @@ class DependantTask(BaseTaskHandler):
if subtasks:
self.wait(subtasks, all=True)
class MultiPlatformTask(BaseTaskHandler):
def buildWrapperRPM(self, spec_url, build_task_id, build_target, build, repo_id, **opts):
task = self.session.getTaskInfo(build_task_id)

View file

@ -52,6 +52,7 @@ def deprecated(message):
warnings.simplefilter('always', DeprecationWarning)
warnings.warn(message, DeprecationWarning)
def _changelogDate(cldate):
return time.strftime('%a %b %d %Y', time.strptime(koji.formatTime(cldate), '%Y-%m-%d %H:%M:%S'))
@ -69,6 +70,7 @@ def formatChangelog(entries):
koji._fix_print(entry['text']))
return result
DATE_RE = re.compile(r'(\d+)-(\d+)-(\d+)')
TIME_RE = re.compile(r'(\d+):(\d+):(\d+)')
@ -498,7 +500,6 @@ def move_and_symlink(src, dst, relative=True, create_dir=False):
os.symlink(dst, src)
def joinpath(path, *paths):
"""A wrapper around os.path.join that limits directory traversal"""

View file

@ -55,7 +55,6 @@ if six.PY2:
ExtendedMarshaller.dispatch[long] = ExtendedMarshaller.dump_int # noqa: F821
def dumps(params, methodname=None, methodresponse=None, encoding=None,
allow_none=1, marshaller=None):
"""encode an xmlrpc request or response

View file

@ -22,6 +22,7 @@ from koji.plugin import callback, convert_datetime, ignore_error
CONFIG_FILE = '/etc/koji-hub/plugins/protonmsg.conf'
CONFIG = None
class TimeoutHandler(MessagingHandler):
def __init__(self, url, msgs, conf, *args, **kws):
super(TimeoutHandler, self).__init__(*args, **kws)
@ -151,6 +152,7 @@ def queue_msg(address, props, data):
body = json.dumps(data, default=json_serialize)
msgs.append((address, props, body))
@convert_datetime
@callback('postPackageListChange')
def prep_package_list_change(cbtype, *args, **kws):
@ -162,6 +164,7 @@ def prep_package_list_change(cbtype, *args, **kws):
'user': kws['user']['name']}
queue_msg(address, props, kws)
@convert_datetime
@callback('postTaskStateChange')
def prep_task_state_change(cbtype, *args, **kws):
@ -177,6 +180,7 @@ def prep_task_state_change(cbtype, *args, **kws):
'new': kws['new']}
queue_msg(address, props, kws)
@convert_datetime
@callback('postBuildStateChange')
def prep_build_state_change(cbtype, *args, **kws):
@ -196,6 +200,7 @@ def prep_build_state_change(cbtype, *args, **kws):
'new': new}
queue_msg(address, props, kws)
@convert_datetime
@callback('postImport')
def prep_import(cbtype, *args, **kws):
@ -207,6 +212,7 @@ def prep_import(cbtype, *args, **kws):
'release': kws['build']['release']}
queue_msg(address, props, kws)
@convert_datetime
@callback('postRPMSign')
def prep_rpm_sign(cbtype, *args, **kws):
@ -224,6 +230,7 @@ def prep_rpm_sign(cbtype, *args, **kws):
'rpm_arch': kws['rpm']['arch']}
queue_msg(address, props, kws)
def _prep_tag_msg(address, cbtype, kws):
build = kws['build']
props = {'type': cbtype[4:],
@ -234,16 +241,19 @@ def _prep_tag_msg(address, cbtype, kws):
'user': kws['user']['name']}
queue_msg(address, props, kws)
@convert_datetime
@callback('postTag')
def prep_tag(cbtype, *args, **kws):
_prep_tag_msg('build.tag', cbtype, kws)
@convert_datetime
@callback('postUntag')
def prep_untag(cbtype, *args, **kws):
_prep_tag_msg('build.untag', cbtype, kws)
@convert_datetime
@callback('postRepoInit')
def prep_repo_init(cbtype, *args, **kws):
@ -253,6 +263,7 @@ def prep_repo_init(cbtype, *args, **kws):
'repo_id': kws['repo_id']}
queue_msg(address, props, kws)
@convert_datetime
@callback('postRepoDone')
def prep_repo_done(cbtype, *args, **kws):
@ -263,6 +274,7 @@ def prep_repo_done(cbtype, *args, **kws):
'expire': kws['expire']}
queue_msg(address, props, kws)
@ignore_error
@convert_datetime
@callback('postCommit')

View file

@ -27,6 +27,7 @@ def get_channel_arches(channel):
ret[koji.canonArch(a)] = 1
return ret
@export
def runroot(tagInfo, arch, command, channel=None, **opts):
""" Create a runroot task """

View file

@ -33,6 +33,7 @@ def get_install_requires():
return requires
setup(
name="koji",
version="1.20.0",

View file

@ -40,6 +40,7 @@ def _(args):
"""Stub function for translation"""
return args
def get_options():
"""process options from command line and config file"""
@ -238,6 +239,7 @@ def get_options():
return options, args
def check_tag(name):
"""Check tag name against options and determine if we should process it
@ -258,6 +260,7 @@ def check_tag(name):
# not ignored and no filter specified
return True
def check_package(name):
"""Check package name against options and determine if we should process it
@ -273,6 +276,7 @@ def check_package(name):
# no filter specified
return True
time_units = {
'second': 1,
'minute': 60,
@ -288,11 +292,14 @@ time_unit_aliases = [
['minute', 'minutes', 'min', 'mins'],
['second', 'seconds', 'sec', 'secs', 's'],
]
def parse_duration(str):
"""Parse time duration from string, returns duration in seconds"""
ret = 0
n = None
unit = None
def parse_num(s):
try:
return int(s)
@ -338,16 +345,19 @@ def parse_duration(str):
unit = None
return ret
def error(msg=None, code=1):
if msg:
sys.stderr.write(msg + "\n")
sys.stderr.flush()
sys.exit(code)
def warn(msg):
sys.stderr.write(msg + "\n")
sys.stderr.flush()
def ensure_connection(session):
try:
ret = session.getAPIVersion()
@ -356,6 +366,7 @@ def ensure_connection(session):
if ret != koji.API_VERSION:
warn(_("WARNING: The server is at API version %d and the client is at %d" % (ret, koji.API_VERSION)))
def has_krb_creds():
if krbV is None:
return False
@ -367,6 +378,7 @@ def has_krb_creds():
except krbV.Krb5Error:
return False
def activate_session(session):
"""Test and login the session is applicable"""
global options
@ -395,6 +407,7 @@ def activate_session(session):
if options.debug:
print("successfully connected to hub")
def send_warning_notice(owner_name, builds):
if not options.mail:
return
@ -587,6 +600,7 @@ def handle_trash():
# run all packageListAdd/tagBuildBypass finally
mcall.call_all()
def protected_sig(keys):
"""Check list of keys and see if any are protected
@ -612,6 +626,7 @@ def handle_salvage():
run this action only."""
return handle_delete(just_salvage=True)
def salvage_build(binfo):
"""Removes trashcan tag from a build and prints a message"""
if options.test:
@ -621,6 +636,7 @@ def salvage_build(binfo):
print("Untagging from trashcan: %(nvr)s" % binfo)
session.untagBuildBypass(options.trashcan_tag, binfo['id'], force=True)
def handle_delete(just_salvage=False):
"""Delete builds that have been in the trashcan for long enough
@ -793,6 +809,7 @@ def read_policies(fn=None):
fo.close()
return ret
def scan_policies(str):
"""Read tag gc policies from a string
@ -802,8 +819,10 @@ def scan_policies(str):
tests = koji.policy.findSimpleTests(globals())
return koji.policy.SimpleRuleSet(str.splitlines(), tests)
build_sig_cache = {}
def get_build_sigs(build, cache=False):
if cache and build in build_sig_cache:
return build_sig_cache[build]
@ -825,6 +844,7 @@ def get_build_sigs(build, cache=False):
ret = build_sig_cache[build] = to_list(keys.keys())
return ret
def handle_prune():
"""Untag old builds according to policy
@ -966,6 +986,7 @@ def handle_prune():
# server issue
pass
if __name__ == "__main__":
options, args = get_options()

View file

@ -60,16 +60,19 @@ def _(args):
"""Stub function for translation"""
return args
def log(str):
global logfile
print("%s" % str)
if logfile is not None:
os.write(logfile, "%s\n" % str)
class SubOption(object):
"""A simple container to help with tracking ConfigParser data"""
pass
def get_options():
"""process options from command line and config file"""
@ -189,6 +192,7 @@ def get_options():
return options, args
time_units = {
'second': 1,
'minute': 60,
@ -204,11 +208,14 @@ time_unit_aliases = [
['minute', 'minutes', 'min', 'mins'],
['second', 'seconds', 'sec', 'secs', 's'],
]
def parse_duration(str):
"""Parse time duration from string, returns duration in seconds"""
ret = 0
n = None
unit = None
def parse_num(s):
try:
return int(s)
@ -254,16 +261,19 @@ def parse_duration(str):
unit = None
return ret
def error(msg=None, code=1):
if msg:
sys.stderr.write(msg + "\n")
sys.stderr.flush()
sys.exit(code)
def warn(msg):
sys.stderr.write(msg + "\n")
sys.stderr.flush()
def ensure_connection(session):
try:
ret = session.getAPIVersion()
@ -273,6 +283,7 @@ def ensure_connection(session):
warn(_("WARNING: The server is at API version %d and the client is at "
"%d" % (ret, koji.API_VERSION)))
def activate_session(session):
"""Test and login the session is applicable"""
global options
@ -307,6 +318,7 @@ def activate_session(session):
if options.debug:
log("successfully connected to hub")
def _unique_path(prefix):
"""Create a unique path fragment by appending a path component
to prefix. The path component will consist of a string of letter and numbers

View file

@ -193,7 +193,6 @@ if __name__ == "__main__":
if opts['DBHost'] is None:
opts['DBHost'] = opts['DBhost']
koji.db.provideDBopts(database=opts["DBName"],
user=opts["DBUser"],
password=opts.get("DBPass", None),

View file

@ -43,6 +43,7 @@ from koji.util import parseStatus, rmtree, to_list
tag_cache = {}
def getTag(session, tag, event=None):
"""A caching version of the hub call"""
cache = tag_cache
@ -724,7 +725,6 @@ class RepoManager(object):
self.logger.info('Needed tags count went from %i to %i', n_need,
len(self.needed_tags))
def regenRepos(self):
"""Trigger newRepo tasks for needed tags"""
@ -822,6 +822,7 @@ def start_regen_loop(session, repomgr):
def main(options, session):
repomgr = RepoManager(options, session)
repomgr.readCurrentRepos()
def shutdown(*args):
raise SystemExit
signal.signal(signal.SIGTERM, shutdown)
@ -864,6 +865,7 @@ def main(options, session):
finally:
session.logout()
def get_options():
"""process options from command line and config file"""
# parse command line args
@ -976,6 +978,7 @@ def get_options():
setattr(options, name, fn)
return options
def quit(msg=None, code=1):
if msg:
logging.getLogger("koji.repo").error(msg)
@ -983,6 +986,7 @@ def quit(msg=None, code=1):
sys.stderr.flush()
sys.exit(code)
if __name__ == "__main__":
options = get_options()

View file

@ -56,14 +56,17 @@ KOJIKAMID = True
# INSERT kojikamid dup #
class fakemodule(object):
pass
# make parts of the above insert accessible as koji.X
koji = fakemodule()
koji.GenericError = GenericError # noqa: F821
koji.BuildError = BuildError # noqa: F821
def encode_int(n):
"""If n is too large for a 32bit signed, convert it to a string"""
if n <= 2147483647:
@ -71,6 +74,7 @@ def encode_int(n):
# else
return str(n)
class WindowsBuild(object):
LEADING_CHAR = re.compile('^[^A-Za-z_]')
@ -529,6 +533,7 @@ class WindowsBuild(object):
self.expireBuildroot()
return self.gatherResults()
def run(cmd, chdir=None, fatal=False, log=True):
global logfd
output = ''
@ -558,6 +563,7 @@ def run(cmd, chdir=None, fatal=False, log=True):
raise BuildError(msg) # noqa: F821
return ret, output
def find_net_info():
"""
Find the network gateway configured for this VM.
@ -586,6 +592,7 @@ def find_net_info():
gateway = None
return macaddr, gateway
def upload_file(server, prefix, path):
"""upload a single file to the vmd"""
logger = logging.getLogger('koji.vm')
@ -606,6 +613,7 @@ def upload_file(server, prefix, path):
server.verifyChecksum(path, digest, 'md5')
logger.info('Uploaded %s (%s bytes, md5: %s)', destpath, offset, digest)
def get_mgmt_server():
"""Get a ServerProxy object we can use to retrieve task info"""
logger = logging.getLogger('koji.vm')
@ -624,6 +632,7 @@ def get_mgmt_server():
logger.debug('found task-specific port %s', task_port)
return six.moves.xmlrpc_client.ServerProxy('http://%s:%s/' % (gateway, task_port), allow_none=True)
def get_options():
"""handle usage and parse options"""
usage = """%prog [options]
@ -637,6 +646,7 @@ def get_options():
(options, args) = parser.parse_args()
return options
def setup_logging(opts):
global logfile, logfd
logger = logging.getLogger('koji.vm')
@ -651,11 +661,13 @@ def setup_logging(opts):
logger.addHandler(handler)
return handler
def log_local(msg):
tb = ''.join(traceback.format_exception(*sys.exc_info()))
sys.stderr.write('%s: %s\n' % (time.ctime(), msg))
sys.stderr.write(tb)
def stream_logs(server, handler, builds):
"""Stream logs incrementally to the server.
The global logfile will always be streamed.
@ -693,6 +705,7 @@ def stream_logs(server, handler, builds):
log_local('error uploading %s' % relpath)
time.sleep(1)
def fail(server, handler):
"""do the right thing when a build fails"""
global logfile, logfd
@ -719,6 +732,7 @@ def fail(server, handler):
logfile = '/tmp/build.log'
logfd = None
def main():
prog = os.path.basename(sys.argv[0])
opts = get_options()

View file

@ -68,6 +68,8 @@ def libvirt_callback(ignore, err):
if err[3] != libvirt.VIR_ERR_ERROR:
# Don't log libvirt errors: global error handler will do that
logging.warn("Non-error from libvirt: '%s'", err[2])
libvirt.registerErrorHandler(f=libvirt_callback, ctx=None)
@ -191,6 +193,7 @@ def get_options():
return options
def quit(msg=None, code=1):
if msg:
logging.getLogger("koji.vm").error(msg)
@ -198,6 +201,7 @@ def quit(msg=None, code=1):
sys.stderr.flush()
sys.exit(code)
def main(options, session):
logger = logging.getLogger("koji.vm")
logger.info('Starting up')
@ -209,8 +213,10 @@ def main(options, session):
for name in options.plugin:
logger.info('Loading plugin: %s', name)
tm.scanPlugin(pt.load(name))
def shutdown(*args):
raise SystemExit
def restart(*args):
logger.warn("Initiating graceful restart")
tm.restart_pending = True
@ -416,6 +422,7 @@ class WinBuildTask(MultiPlatformTask):
parent=self.id)
self.wait(tag_task_id)
class VMExecTask(BaseTaskHandler):
"""
Handles the startup, state-tracking, and shutdown of a VM
@ -883,6 +890,7 @@ class VMExecTask(BaseTaskHandler):
else:
raise koji.BuildError(self.output)
class VMTaskManager(TaskManager):
def __init__(self, options, session):
super(VMTaskManager, self).__init__(options, session)

View file

@ -48,6 +48,7 @@ _sortbyname = lambda x: x['name']
# loggers
authlogger = logging.getLogger('koji.auth')
def _setUserCookie(environ, user):
options = environ['koji.options']
# include the current time in the cookie so we can verify that
@ -72,6 +73,7 @@ def _setUserCookie(environ, user):
environ['koji.headers'].append(['Set-Cookie', out])
environ['koji.headers'].append(['Cache-Control', 'no-cache="set-cookie"'])
def _clearUserCookie(environ):
cookies = six.moves.http_cookies.SimpleCookie()
cookies['user'] = ''
@ -81,6 +83,7 @@ def _clearUserCookie(environ):
out = c.OutputString()
environ['koji.headers'].append(['Set-Cookie', out])
def _getUserCookie(environ):
options = environ['koji.options']
cookies = six.moves.http_cookies.SimpleCookie(environ.get('HTTP_COOKIE', ''))
@ -118,6 +121,7 @@ def _getUserCookie(environ):
# Otherwise, cookie is valid and current
return user
def _krbLogin(environ, session, principal):
options = environ['koji.options']
wprinc = options['WebPrincipal']
@ -126,6 +130,7 @@ def _krbLogin(environ, session, principal):
return session.krb_login(principal=wprinc, keytab=keytab,
ccache=ccache, proxyuser=principal)
def _sslLogin(environ, session, username):
options = environ['koji.options']
client_cert = options['WebCert']
@ -134,6 +139,7 @@ def _sslLogin(environ, session, username):
return session.ssl_login(client_cert, None, server_ca,
proxyuser=username)
def _assertLogin(environ):
session = environ['koji.session']
options = environ['koji.options']
@ -165,6 +171,7 @@ def _assertLogin(environ):
_redirect(environ, 'login')
assert False # pragma: no cover
def _getServer(environ):
opts = environ['koji.options']
s_opts = {'krbservice': opts['KrbService'],
@ -186,6 +193,7 @@ def _getServer(environ):
environ['koji.session'] = session
return session
def _construct_url(environ, page):
port = environ['SERVER_PORT']
host = environ['SERVER_NAME']
@ -195,14 +203,17 @@ def _construct_url(environ, page):
return "%s://%s%s" % (url_scheme, host, page)
return "%s://%s:%s%s" % (url_scheme, host, port, page)
def _getBaseURL(environ):
base = environ['SCRIPT_NAME']
return _construct_url(environ, base)
def _redirect(environ, location):
environ['koji.redirect'] = location
raise ServerRedirect
def _redirectBack(environ, page, forceSSL):
if page:
# We'll work with the page we were given
@ -227,6 +238,7 @@ def _redirectBack(environ, page, forceSSL):
# and redirect to the page
_redirect(environ, page)
def login(environ, page=None):
session = _getServer(environ)
options = environ['koji.options']
@ -270,6 +282,7 @@ def login(environ, page=None):
# To protect the session cookie, we must forceSSL
_redirectBack(environ, page, forceSSL=True)
def logout(environ, page=None):
user = _getUserCookie(environ)
_clearUserCookie(environ)
@ -278,6 +291,7 @@ def logout(environ, page=None):
_redirectBack(environ, page, forceSSL=False)
def index(environ, packageOrder='package_name', packageStart=None):
values = _initValues(environ)
server = _getServer(environ)
@ -326,6 +340,7 @@ def index(environ, packageOrder='package_name', packageStart=None):
return _genHTML(environ, 'index.chtml')
def notificationedit(environ, notificationID):
server = _getServer(environ)
_assertLogin(environ)
@ -371,6 +386,7 @@ def notificationedit(environ, notificationID):
return _genHTML(environ, 'notificationedit.chtml')
def notificationcreate(environ):
server = _getServer(environ)
_assertLogin(environ)
@ -415,6 +431,7 @@ def notificationcreate(environ):
return _genHTML(environ, 'notificationedit.chtml')
def notificationdelete(environ, notificationID):
server = _getServer(environ)
_assertLogin(environ)
@ -428,6 +445,7 @@ def notificationdelete(environ, notificationID):
_redirect(environ, 'index')
# All Tasks
_TASKS = ['build',
'buildSRPMFromSCM',
@ -463,6 +481,7 @@ _TOPLEVEL_TASKS = ['build', 'buildNotification', 'chainbuild', 'maven', 'chainma
# Tasks that can have children
_PARENT_TASKS = ['build', 'chainbuild', 'maven', 'chainmaven', 'winbuild', 'newRepo', 'distRepo', 'wrapperRPM', 'livecd', 'appliance', 'image', 'livemedia']
def tasks(environ, owner=None, state='active', view='tree', method='all', hostID=None, channelID=None, start=None, order='-id'):
values = _initValues(environ, 'Tasks', 'tasks')
server = _getServer(environ)
@ -566,6 +585,7 @@ def tasks(environ, owner=None, state='active', view='tree', method='all', hostID
return _genHTML(environ, 'tasks.chtml')
def taskinfo(environ, taskID):
server = _getServer(environ)
values = _initValues(environ, 'Task Info', 'tasks')
@ -712,6 +732,7 @@ def taskinfo(environ, taskID):
values['params_parsed'] = None
return _genHTML(environ, 'taskinfo.chtml')
def taskstatus(environ, taskID):
server = _getServer(environ)
@ -726,6 +747,7 @@ def taskstatus(environ, taskID):
output += '%s:%s:%s\n' % (volume, filename, file_stats['st_size'])
return output
def resubmittask(environ, taskID):
server = _getServer(environ)
_assertLogin(environ)
@ -734,6 +756,7 @@ def resubmittask(environ, taskID):
newTaskID = server.resubmitTask(taskID)
_redirect(environ, 'taskinfo?taskID=%i' % newTaskID)
def canceltask(environ, taskID):
server = _getServer(environ)
_assertLogin(environ)
@ -742,11 +765,13 @@ def canceltask(environ, taskID):
server.cancelTask(taskID)
_redirect(environ, 'taskinfo?taskID=%i' % taskID)
def _sortByExtAndName(item):
"""Sort filename tuples key function, first by extension, and then by name."""
kRoot, kExt = os.path.splitext(os.path.basename(item[1]))
return (kExt, kRoot)
def getfile(environ, taskID, name, volume='DEFAULT', offset=None, size=None):
server = _getServer(environ)
taskID = int(taskID)
@ -810,6 +835,7 @@ def _chunk_file(server, environ, taskID, name, offset, size, volume):
offset += content_length
remaining -= content_length
def tags(environ, start=None, order=None, childID=None):
values = _initValues(environ, 'Tags', 'tags')
server = _getServer(environ)
@ -830,8 +856,10 @@ def tags(environ, start=None, order=None, childID=None):
return _genHTML(environ, 'tags.chtml')
_PREFIX_CHARS = [chr(char) for char in list(range(48, 58)) + list(range(97, 123))]
def packages(environ, tagID=None, userID=None, order='package_name', start=None, prefix=None, inherited='1'):
values = _initValues(environ, 'Packages', 'packages')
server = _getServer(environ)
@ -866,6 +894,7 @@ def packages(environ, tagID=None, userID=None, order='package_name', start=None,
return _genHTML(environ, 'packages.chtml')
def packageinfo(environ, packageID, tagOrder='name', tagStart=None, buildOrder='-completion_time', buildStart=None):
values = _initValues(environ, 'Package Info', 'packages')
server = _getServer(environ)
@ -888,6 +917,7 @@ def packageinfo(environ, packageID, tagOrder='name', tagStart=None, buildOrder='
return _genHTML(environ, 'packageinfo.chtml')
def taginfo(environ, tagID, all='0', packageOrder='package_name', packageStart=None, buildOrder='-completion_time', buildStart=None, childID=None):
values = _initValues(environ, 'Tag Info', 'tags')
server = _getServer(environ)
@ -943,6 +973,7 @@ def taginfo(environ, tagID, all='0', packageOrder='package_name', packageStart=N
return _genHTML(environ, 'taginfo.chtml')
def tagcreate(environ):
server = _getServer(environ)
_assertLogin(environ)
@ -978,6 +1009,7 @@ def tagcreate(environ):
return _genHTML(environ, 'tagedit.chtml')
def tagedit(environ, tagID):
server = _getServer(environ)
_assertLogin(environ)
@ -1020,6 +1052,7 @@ def tagedit(environ, tagID):
return _genHTML(environ, 'tagedit.chtml')
def tagdelete(environ, tagID):
server = _getServer(environ)
_assertLogin(environ)
@ -1033,6 +1066,7 @@ def tagdelete(environ, tagID):
_redirect(environ, 'tags')
def tagparent(environ, tagID, parentID, action):
server = _getServer(environ)
_assertLogin(environ)
@ -1096,6 +1130,7 @@ def tagparent(environ, tagID, parentID, action):
_redirect(environ, 'taginfo?tagID=%i' % tag['id'])
def externalrepoinfo(environ, extrepoID):
values = _initValues(environ, 'External Repo Info', 'tags')
server = _getServer(environ)
@ -1111,6 +1146,7 @@ def externalrepoinfo(environ, extrepoID):
return _genHTML(environ, 'externalrepoinfo.chtml')
def buildinfo(environ, buildID):
values = _initValues(environ, 'Build Info', 'builds')
server = _getServer(environ)
@ -1236,6 +1272,7 @@ def buildinfo(environ, buildID):
values['pathinfo'] = pathinfo
return _genHTML(environ, 'buildinfo.chtml')
def builds(environ, userID=None, tagID=None, packageID=None, state=None, order='-build_id', start=None, prefix=None, inherited='1', latest='1', type=None):
values = _initValues(environ, 'Builds', 'builds')
server = _getServer(environ)
@ -1319,6 +1356,7 @@ def builds(environ, userID=None, tagID=None, packageID=None, state=None, order='
return _genHTML(environ, 'builds.chtml')
def users(environ, order='name', start=None, prefix=None):
values = _initValues(environ, 'Users', 'users')
server = _getServer(environ)
@ -1338,6 +1376,7 @@ def users(environ, order='name', start=None, prefix=None):
return _genHTML(environ, 'users.chtml')
def userinfo(environ, userID, packageOrder='package_name', packageStart=None, buildOrder='-completion_time', buildStart=None):
values = _initValues(environ, 'User Info', 'users')
server = _getServer(environ)
@ -1360,6 +1399,7 @@ def userinfo(environ, userID, packageOrder='package_name', packageStart=None, bu
return _genHTML(environ, 'userinfo.chtml')
def rpminfo(environ, rpmID, fileOrder='name', fileStart=None, buildrootOrder='-id', buildrootStart=None):
values = _initValues(environ, 'RPM Info', 'builds')
server = _getServer(environ)
@ -1413,6 +1453,7 @@ def rpminfo(environ, rpmID, fileOrder='name', fileStart=None, buildrootOrder='-i
return _genHTML(environ, 'rpminfo.chtml')
def archiveinfo(environ, archiveID, fileOrder='name', fileStart=None, buildrootOrder='-id', buildrootStart=None):
values = _initValues(environ, 'Archive Info', 'builds')
server = _getServer(environ)
@ -1451,6 +1492,7 @@ def archiveinfo(environ, archiveID, fileOrder='name', fileStart=None, buildrootO
return _genHTML(environ, 'archiveinfo.chtml')
def fileinfo(environ, filename, rpmID=None, archiveID=None):
values = _initValues(environ, 'File Info', 'builds')
server = _getServer(environ)
@ -1485,6 +1527,7 @@ def fileinfo(environ, filename, rpmID=None, archiveID=None):
return _genHTML(environ, 'fileinfo.chtml')
def cancelbuild(environ, buildID):
server = _getServer(environ)
_assertLogin(environ)
@ -1500,6 +1543,7 @@ def cancelbuild(environ, buildID):
_redirect(environ, 'buildinfo?buildID=%i' % build['id'])
def hosts(environ, state='enabled', start=None, order='name'):
values = _initValues(environ, 'Hosts', 'hosts')
server = _getServer(environ)
@ -1530,6 +1574,7 @@ def hosts(environ, state='enabled', start=None, order='name'):
return _genHTML(environ, 'hosts.chtml')
def hostinfo(environ, hostID=None, userID=None):
values = _initValues(environ, 'Host Info', 'hosts')
server = _getServer(environ)
@ -1570,6 +1615,7 @@ def hostinfo(environ, hostID=None, userID=None):
return _genHTML(environ, 'hostinfo.chtml')
def hostedit(environ, hostID):
server = _getServer(environ)
_assertLogin(environ)
@ -1619,6 +1665,7 @@ def hostedit(environ, hostID):
return _genHTML(environ, 'hostedit.chtml')
def disablehost(environ, hostID):
server = _getServer(environ)
_assertLogin(environ)
@ -1629,6 +1676,7 @@ def disablehost(environ, hostID):
_redirect(environ, 'hostinfo?hostID=%i' % host['id'])
def enablehost(environ, hostID):
server = _getServer(environ)
_assertLogin(environ)
@ -1639,6 +1687,7 @@ def enablehost(environ, hostID):
_redirect(environ, 'hostinfo?hostID=%i' % host['id'])
def channelinfo(environ, channelID):
values = _initValues(environ, 'Channel Info', 'hosts')
server = _getServer(environ)
@ -1665,6 +1714,7 @@ def channelinfo(environ, channelID):
return _genHTML(environ, 'channelinfo.chtml')
def buildrootinfo(environ, buildrootID, builtStart=None, builtOrder=None, componentStart=None, componentOrder=None):
values = _initValues(environ, 'Buildroot Info', 'hosts')
server = _getServer(environ)
@ -1688,6 +1738,7 @@ def buildrootinfo(environ, buildrootID, builtStart=None, builtOrder=None, compon
return _genHTML(environ, template)
def rpmlist(environ, type, buildrootID=None, imageID=None, start=None, order='nvr'):
"""
rpmlist requires a buildrootID OR an imageID to be passed in. From one
@ -1739,6 +1790,7 @@ def rpmlist(environ, type, buildrootID=None, imageID=None, start=None, order='nv
return _genHTML(environ, 'rpmlist.chtml')
def archivelist(environ, type, buildrootID=None, imageID=None, start=None, order='filename'):
values = _initValues(environ, 'Archive List', 'hosts')
server = _getServer(environ)
@ -1777,6 +1829,7 @@ def archivelist(environ, type, buildrootID=None, imageID=None, start=None, order
return _genHTML(environ, 'archivelist.chtml')
def buildtargets(environ, start=None, order='name'):
values = _initValues(environ, 'Build Targets', 'buildtargets')
server = _getServer(environ)
@ -1792,6 +1845,7 @@ def buildtargets(environ, start=None, order='name'):
return _genHTML(environ, 'buildtargets.chtml')
def buildtargetinfo(environ, targetID=None, name=None):
values = _initValues(environ, 'Build Target Info', 'buildtargets')
server = _getServer(environ)
@ -1821,6 +1875,7 @@ def buildtargetinfo(environ, targetID=None, name=None):
return _genHTML(environ, 'buildtargetinfo.chtml')
def buildtargetedit(environ, targetID):
server = _getServer(environ)
_assertLogin(environ)
@ -1860,6 +1915,7 @@ def buildtargetedit(environ, targetID):
return _genHTML(environ, 'buildtargetedit.chtml')
def buildtargetcreate(environ):
server = _getServer(environ)
_assertLogin(environ)
@ -1894,6 +1950,7 @@ def buildtargetcreate(environ):
return _genHTML(environ, 'buildtargetedit.chtml')
def buildtargetdelete(environ, targetID):
server = _getServer(environ)
_assertLogin(environ)
@ -1908,11 +1965,13 @@ def buildtargetdelete(environ, targetID):
_redirect(environ, 'buildtargets')
def reports(environ):
_getServer(environ)
_initValues(environ, 'Reports', 'reports')
return _genHTML(environ, 'reports.chtml')
def buildsbyuser(environ, start=None, order='-builds'):
values = _initValues(environ, 'Builds by User', 'reports')
server = _getServer(environ)
@ -1940,6 +1999,7 @@ def buildsbyuser(environ, start=None, order='-builds'):
return _genHTML(environ, 'buildsbyuser.chtml')
def rpmsbyhost(environ, start=None, order=None, hostArch=None, rpmArch=None):
values = _initValues(environ, 'RPMs by Host', 'reports')
server = _getServer(environ)
@ -1981,6 +2041,7 @@ def rpmsbyhost(environ, start=None, order=None, hostArch=None, rpmArch=None):
return _genHTML(environ, 'rpmsbyhost.chtml')
def packagesbyuser(environ, start=None, order=None):
values = _initValues(environ, 'Packages by User', 'reports')
server = _getServer(environ)
@ -2010,6 +2071,7 @@ def packagesbyuser(environ, start=None, order=None):
return _genHTML(environ, 'packagesbyuser.chtml')
def tasksbyhost(environ, start=None, order='-tasks', hostArch=None):
values = _initValues(environ, 'Tasks by Host', 'reports')
server = _getServer(environ)
@ -2046,6 +2108,7 @@ def tasksbyhost(environ, start=None, order='-tasks', hostArch=None):
return _genHTML(environ, 'tasksbyhost.chtml')
def tasksbyuser(environ, start=None, order='-tasks'):
values = _initValues(environ, 'Tasks by User', 'reports')
server = _getServer(environ)
@ -2074,6 +2137,7 @@ def tasksbyuser(environ, start=None, order='-tasks'):
return _genHTML(environ, 'tasksbyuser.chtml')
def buildsbystatus(environ, days='7'):
values = _initValues(environ, 'Builds by Status', 'reports')
server = _getServer(environ)
@ -2109,6 +2173,7 @@ def buildsbystatus(environ, days='7'):
return _genHTML(environ, 'buildsbystatus.chtml')
def buildsbytarget(environ, days='7', start=None, order='-builds'):
values = _initValues(environ, 'Builds by Target', 'reports')
server = _getServer(environ)
@ -2148,12 +2213,14 @@ def buildsbytarget(environ, days='7', start=None, order='-builds'):
return _genHTML(environ, 'buildsbytarget.chtml')
def _filter_hosts_by_arch(hosts, arch):
if arch == '__all__':
return hosts
else:
return [h for h in hosts if arch in h['arches'].split()]
def clusterhealth(environ, arch='__all__'):
values = _initValues(environ, 'Cluster health', 'reports')
server = _getServer(environ)
@ -2204,6 +2271,7 @@ def clusterhealth(environ, arch='__all__'):
values['channels'] = sorted(channels, key=lambda x: x['name'])
return _genHTML(environ, 'clusterhealth.chtml')
def recentbuilds(environ, user=None, tag=None, package=None):
values = _initValues(environ, 'Recent Build RSS')
server = _getServer(environ)
@ -2273,6 +2341,7 @@ def recentbuilds(environ, user=None, tag=None, package=None):
environ['koji.headers'].append(['Content-Type', 'text/xml'])
return _genHTML(environ, 'recentbuilds.chtml')
_infoURLs = {'package': 'packageinfo?packageID=%(id)i',
'build': 'buildinfo?buildID=%(id)i',
'tag': 'taginfo?tagID=%(id)i',
@ -2299,6 +2368,7 @@ _DEFAULT_SEARCH_ORDER = {
# any type not listed will default to 'name'
}
def search(environ, start=None, order=None):
values = _initValues(environ, 'Search', 'search')
server = _getServer(environ)

View file

@ -248,7 +248,6 @@ class Dispatcher(object):
# TODO (warning in header or something?)
return func, data
def _setup(self, environ):
global kojiweb_handlers
global kojiweb

View file

@ -41,6 +41,7 @@ import koji
class NoSuchException(Exception):
pass
try:
# pyOpenSSL might not be around
from OpenSSL.SSL import Error as SSL_Error
@ -51,6 +52,7 @@ except:
themeInfo = {}
themeCache = {}
def _initValues(environ, title='Build System Info', pageID='summary'):
global themeInfo
global themeCache
@ -69,6 +71,7 @@ def _initValues(environ, title='Build System Info', pageID='summary'):
return values
def themePath(path, local=False):
global themeInfo
global themeCache
@ -95,6 +98,7 @@ def themePath(path, local=False):
themeCache[path, local] = ret
return ret
class DecodeUTF8(Cheetah.Filters.Filter):
def filter(self, *args, **kw):
"""Convert all strs to unicode objects"""
@ -106,6 +110,8 @@ class DecodeUTF8(Cheetah.Filters.Filter):
return result
# Escape ampersands so the output can be valid XHTML
class XHTMLFilter(DecodeUTF8):
def filter(self, *args, **kw):
result = super(XHTMLFilter, self).filter(*args, **kw)
@ -116,8 +122,10 @@ class XHTMLFilter(DecodeUTF8):
result = result.replace('&amp;gt;', '&gt;')
return result
TEMPLATES = {}
def _genHTML(environ, fileName):
reqdir = os.path.dirname(environ['SCRIPT_FILENAME'])
if os.getcwd() != reqdir:
@ -154,11 +162,13 @@ def _genHTML(environ, fileName):
else:
return tmpl_inst.respond()
def _truncTime():
now = datetime.datetime.now()
# truncate to the nearest 15 minutes
return now.replace(minute=(now.minute // 15 * 15), second=0, microsecond=0)
def _genToken(environ, tstamp=None):
if 'koji.currentLogin' in environ and environ['koji.currentLogin']:
user = environ['koji.currentLogin']
@ -171,6 +181,7 @@ def _genToken(environ, tstamp=None):
value = value.encode('utf-8')
return hashlib.md5(value).hexdigest()[-8:]
def _getValidTokens(environ):
tokens = []
now = _truncTime()
@ -181,6 +192,7 @@ def _getValidTokens(environ):
tokens.append(token)
return tokens
def toggleOrder(template, sortKey, orderVar='order'):
"""
If orderVar equals 'sortKey', return '-sortKey', else
@ -191,6 +203,7 @@ def toggleOrder(template, sortKey, orderVar='order'):
else:
return sortKey
def toggleSelected(template, var, option, checked=False):
"""
If the passed in variable var equals the literal value in option,
@ -206,6 +219,7 @@ def toggleSelected(template, var, option, checked=False):
else:
return ''
def sortImage(template, sortKey, orderVar='order'):
"""
Return an html img tag suitable for inclusion in the sortKey of a sortable table,
@ -219,6 +233,7 @@ def sortImage(template, sortKey, orderVar='order'):
else:
return ''
def passthrough(template, *vars):
"""
Construct a string suitable for use as URL
@ -239,6 +254,7 @@ def passthrough(template, *vars):
else:
return ''
def passthrough_except(template, *exclude):
"""
Construct a string suitable for use as URL
@ -255,6 +271,7 @@ def passthrough_except(template, *exclude):
passvars.append(var)
return passthrough(template, *passvars)
def sortByKeyFuncNoneGreatest(key):
"""Return a function to sort a list of maps by the given key.
None will sort higher than all other values (instead of lower).
@ -265,6 +282,7 @@ def sortByKeyFuncNoneGreatest(key):
return (v is None, v)
return internal_key
def paginateList(values, data, start, dataName, prefix=None, order=None, noneGreatest=False, pageSize=50):
"""
Slice the 'data' list into one page worth. Start at offset
@ -296,6 +314,7 @@ def paginateList(values, data, start, dataName, prefix=None, order=None, noneGre
return data
def paginateMethod(server, values, methodName, args=None, kw=None,
start=None, dataName=None, prefix=None, order=None, pageSize=50):
"""Paginate the results of the method with the given name when called with the given args and kws.
@ -324,6 +343,7 @@ def paginateMethod(server, values, methodName, args=None, kw=None,
return data
def paginateResults(server, values, methodName, args=None, kw=None,
start=None, dataName=None, prefix=None, order=None, pageSize=50):
"""Paginate the results of the method with the given name when called with the given args and kws.
@ -352,6 +372,7 @@ def paginateResults(server, values, methodName, args=None, kw=None,
return data
def _populateValues(values, dataName, prefix, data, totalRows, start, count, pageSize, order):
"""Populate the values list with the data about the list provided."""
values[dataName] = data
@ -372,21 +393,25 @@ def _populateValues(values, dataName, prefix, data, totalRows, start, count, pag
pages = [page for page in range(0, totalPages) if (abs(page - currentPage) < 100 or ((page + 1) % 100 == 0))]
values[(prefix and prefix + 'Pages') or 'pages'] = pages
def stateName(stateID):
"""Convert a numeric build state into a readable name."""
return koji.BUILD_STATES[stateID].lower()
def imageTag(name):
"""Return an img tag that loads an icon with the given name"""
return '<img class="stateimg" src="%s" title="%s" alt="%s"/>' \
% (themePath("images/%s.png" % name), name, name)
def stateImage(stateID):
"""Return an IMG tag that loads an icon appropriate for
the given state"""
name = stateName(stateID)
return imageTag(name)
def brStateName(stateID):
"""Convert a numeric buildroot state into a readable name."""
if stateID is None:
@ -414,14 +439,17 @@ def repoStateName(stateID):
else:
return 'unknown'
def taskState(stateID):
"""Convert a numeric task state into a readable name"""
return koji.TASK_STATES[stateID].lower()
formatTime = koji.formatTime
formatTimeRSS = koji.formatTimeLong
formatTimeLong = koji.formatTimeLong
def formatTimestampDifference(start_ts, end_ts):
diff = end_ts - start_ts
seconds = diff % 60
@ -431,6 +459,7 @@ def formatTimestampDifference(start_ts, end_ts):
hours = diff
return "%d:%02d:%02d" % (hours, minutes, seconds)
def formatDep(name, version, flags):
"""Format dependency information into
a human-readable format. Copied from
@ -451,6 +480,7 @@ def formatDep(name, version, flags):
s = "%s %s" % (s, version)
return s
def formatMode(mode):
"""Format a numeric mode into a ls-like string describing the access mode."""
if stat.S_ISREG(mode):
@ -485,9 +515,11 @@ def formatMode(mode):
return result
def formatThousands(value):
return '{:,}'.format(value)
def rowToggle(template):
"""If the value of template._rowNum is even, return 'row-even';
if it is odd, return 'row-odd'. Increment the value before checking it.
@ -529,6 +561,7 @@ _fileFlags = {1: 'configuration',
1024: 'unpatched',
2048: 'public key'}
def formatFileFlags(flags):
"""Format rpm fileflags for display. Returns
a list of human-readable strings specifying the
@ -539,6 +572,7 @@ def formatFileFlags(flags):
results.append(desc)
return results
def escapeHTML(value):
"""Replace special characters to the text can be displayed in
an HTML page correctly.
@ -554,6 +588,7 @@ def escapeHTML(value):
replace('<', '&lt;').\
replace('>', '&gt;')
def authToken(template, first=False, form=False):
"""Return the current authToken if it exists.
If form is True, return it enclosed in a hidden input field.
@ -571,6 +606,7 @@ def authToken(template, first=False, form=False):
else:
return ''
def explainError(error):
"""Explain an exception in user-consumable terms
@ -643,6 +679,7 @@ class TaskResultFragment(object):
- composer
- empty_str_placeholder
"""
def __init__(self, text='', size=None, need_escape=None, begin_tag='',
end_tag='', composer=None, empty_str_placeholder=None):
self.text = text
@ -688,6 +725,7 @@ class TaskResultLine(object):
- end_tag
- composer
"""
def __init__(self, fragments=None, need_escape=None, begin_tag='',
end_tag='<br />', composer=None):
if fragments is None:
@ -755,6 +793,7 @@ def _parse_value(key, value, sep=', '):
return TaskResultFragment(text=_str, need_escape=need_escape,
begin_tag=begin_tag, end_tag=end_tag)
def task_result_to_html(result=None, exc_class=None,
max_abbr_lines=None, max_abbr_len=None,
abbr_postscript=None):