Re: No longer package -israwhidebroken
by Kamil Paral
----- "Josef Skladanka" <jskladan(a)redhat.com> wrote:
> Hello,
>
> Although I do not really understand all the spec files & makefile
> magic,
Me neither.
> it seems OK to me, and I do completely support the idea of not
> packaging
> the israwhide broken.
I'm another big supporter :)
> ----- Original Message -----
> From: "James Laska" <jlaska(a)redhat.com>
>
> Comments/concerns?
kparal@kparal:/home/autoqa (master)$ git am < patch
Applying: Drop israwhidebroken sub-package
error: patch failed: Makefile:32
error: Makefile: patch does not apply
Patch failed at 0001 Drop israwhidebroken sub-package
Maybe the patch was based upon a different version of Makefile (than the one
currently in master)?
I think we should also ensure that the latest code (source code and spec
file/Makefile stuff) is available in the 'israwhidebroken' branch, so we
can easily revive this stuff if needed without searching in git history
too much. (I just tried it, it merges cleanly with master, so no problem there).
13 years, 4 months
Re: [PATCH] Add support for a staging server.
by Kamil Paral
Replying for Martin, since he's long gone (at school, probably) :)
----- "James Laska" <jlaska(a)redhat.com> wrote:
> > ---
> > diff --git a/autoqa b/autoqa
> > index fd41503..190e63b 100755
> > --- a/autoqa
> > +++ b/autoqa
> > @@ -40,12 +40,14 @@ conf = {
> > 'testdir': '/usr/share/autotest/client/site_tests',
> > 'hookdir': '/usr/share/autoqa',
> > 'notification_email': '',
> > + 'send_notification_email': 'false',
>
> It feels a little weird having two ways to disable email notification
> 1)
> empty notication_email='' or 2) send_notification_email=false. I'd
> prefer just the first one since that seems simpler. Were the
> occasions
> where having send_notification_email *and* notification_email was
> needed?
send_notification_email is used just to disable notification emails (so you
can keep your notification_email values defined). True, this may be a little
overkill, it depends.
The idea was to have a set of options send_* which you can set to False or
True and therefore selectively disable/enable parts of the notification
system (emails, Bodhi comments, etc).
But it's good to receive similar feedback and step back a moment. Sure,
we can simplify it (revert it back) to just one option. That also holds
for "send_result_email" probably.
> > +# The 'resources' section specify access details to various
> external services
> > +[resources]
> > +# URL of Koji instance used for querying about new builds
> > +koji_url = http://koji.fedoraproject.org/kojihub
> > +# URL of repository of all the RPM packages built in Koji
> > +pkg_url = http://koji.fedoraproject.org/packages
> > +# URL of Bodhi instance used for communication about package
> updates
> > +# (leave blank for default)
> > +bodhi_server =
>
> To avoid having too many config sections, should we just move these
> into
> [general]? Or is there some thinking about how this section might
> grow
> over time?
It will surely grow ResultsDB URL very soon. I don't know about other
services.
We can move everything to "general" section :) From a source code perspective,
it doesn't really matter if we have one or more sections. From a system
admin perspective, it seemed to me that having several sections makes
the config file clearer to understand. But, again, no hard feelings in here.
>
> > diff --git a/hooks/post-koji-build/watch-koji-builds.py
> b/hooks/post-koji-build/watch-koji-builds.py
> > index 4cc2d7f..57f0aee 100755
> > --- a/hooks/post-koji-build/watch-koji-builds.py
> > +++ b/hooks/post-koji-build/watch-koji-builds.py
> > @@ -41,9 +41,6 @@ try:
> > except OSError, e:
> > if e.errno != 17: # already exists
> > raise
> > -# XXX configparser? /etc/koji.conf, section 'koji', item 'server'
> > -# alternately: read from e.g. /etc/autoqa/autoqa.conf
> > -kojiserver = 'http://koji.fedoraproject.org/kojihub'
>
> I like the goal of this change, consolidating config handling etc...
> I
> might offer that we still need kojiserver in this script for testing
> purposes when calling watch-koji-builds.py by hand. Let me try to
> explain my thoughts ...
>
> I *think* we want the callers to be responsible for 1) parsing
> arguments, 2) loading the configuration default options, and 3)
> passing
> it to the underlying modules. So for example, the autoqa script and
> the
> other callable .py scripts (e.g. watch-koji-builds.py) are
> responsible
> for processing command-line arguments, loading configuration defaults
> (autoqa.conf) and passing that on to the appropriate modules. I
> could
> be wrong, and it might not look appropriate when done ... but I
> wonder
> if ...
>
> 1. Add an --koji_server argument to watch-koji-builds.py so the
> caller can specify a different server
> 2. The --koji_server argument will default to the value
> specified
> in autoqa.conf
> 3. watch-koji-builds.py passes the value of --koji_server to
> koji_utils.SimpleKojiClientSession()
>
> Does this make sense?
It makes perfect sense. I just didn't see any use case for using different
Koji server (different from the one defined in autoqa.conf) when running
the watcher. I usually try to implement features only when I know that
I need them (the "keep it simple" principle). So I was considering the exactly
same thing as you propose, but I ended with "We'll do that when we need that."
What is our use case for that command-line option?
> > --- a/lib/python/bodhi_utils.py
> > +++ b/lib/python/bodhi_utils.py
> > @@ -28,6 +28,32 @@ from datetime import datetime
> > from util import SingleConfigParser, getbool
> > import ConfigParser
> >
> > +try:
> > + autoqa_conf = SingleConfigParser()
> > + autoqa_conf.read_single(['autoqa.conf',
> '/etc/autoqa/autoqa.conf'])
> > +
> > + server = autoqa_conf.get('resources', 'bodhi_server')
> > +except ConfigParser.Error, e:
> > + server = ''
>
> I'm not a fan of specifying config file paths in multiple places.
> Ideally, just <one module would handle all config file loading,
> instantiate the config object, and all other modules would use that
> object for access.
This is https://fedorahosted.org/autoqa/ticket/255. Certainly needed
soon, because as our libraries get more complex, we start to repeat the
config parsing code over and over again. Agreed.
> Long-term, we're moving this over to
> autoqa/config.py (or similar) to handle specifying the config file
> paths, right (https://fedorahosted.org/autoqa/ticket/253)?
Actually, ticket #253 is about something little different - ensuring that
all config files are loaded primarily from CWD and only after that from
/etc. I have it almost done.
>
> The above would then change to ...
>
> from autoqa.config import autoqa_cfg
> server = autoqa_cfg.get('resources', 'bodhi_server', '')
There are two issues mixed:
1. Do not repeat the parsing code - that is ticket #255. It is not a one-liner
and should not be part of this patch (let's keep issues separate, this
patch is about supporting staging server).
2. Do not create ConfigParser instance with the same file paths as input
over and over again in all modules - that is what
"from autoqa.config import autoqa_cfg"
solves. That is a one-liner and can be part of this patch, if you think
it's appropriate. Ticket #255 is a superset of this problem and will
replace this solution eventually. I've become so obsessed with "The One
And True Solution" that I forgot we can do smaller steps in the
meantime :)
> > --- a/lib/python/koji_utils.py
> > +++ b/lib/python/koji_utils.py
> > @@ -24,15 +24,16 @@ import koji
> > from repoinfo import repoinfo
> > import rpmUtils.miscutils
> > import sys
> > -
> > -# XXX fetch from /etc/koji.conf, section 'koji'
> > -kojiurl = 'http://koji.fedoraproject.org/kojihub'
> > -pkgurl = 'http://koji.fedoraproject.org/packages'
> > +from autoqa.bodhi_utils import SingleConfigParser
> >
> > class SimpleKojiClientSession(koji.ClientSession):
> > '''Convenience wrapper class for interacting with koji'''
> > - def __init__(self, server=kojiurl, opts=None):
> > - return koji.ClientSession.__init__(self, server, opts)
> > + def __init__(self, opts=None):
> > + cfg_parser = SingleConfigParser()
> > + cfg_parser.read_single(['autoqa.conf',
> '/etc/autoqa/autoqa.conf'])
>
> Same as earlier, too much duplication of config filepaths. Can we
> stuff
> this into autoqa/config.py to handle for us?
Yes, good idea.
>
> Thanks,
> James
Thank you for your review. Let's keep Martin on his toes, he likes
more work :)
Kamil
13 years, 5 months
Re: [PATCH] Add support for a staging server.
by Kamil Paral
----- "Martin Krizek" <mkrizek(a)redhat.com> wrote:
> Hey all,
>
> this patch solves ticket #241. It adds options in autoqa.conf to turn
> on/off
> optin, notifications and results e-mails. There are options containing
> urls of
> koji and bodhi instances that will be used.
>
> Also there are some alterations in bodhi_utils, there is only one
> creation of
> BodhiClient instance and one config files reading to simplify the
> code.
>
> You can find the patch in mkrizek-staging-ready branch.
Maybe I should also mention that autoqa.conf is completely restructured
and heavily commented now. This patch looks good to me. If there are no
objections against it, I'd like to push it to master tomorrow.
13 years, 5 months
Re: [PATCH] Added a support for sending comments into Bodhi, part 2
by Kamil Paral
----- "James Laska" <jlaska(a)redhat.com> wrote:
> > +def getbool(string):
> > + '''Converts a string into a boolean.
> > + Recognizes many usual strings: 1/0, yes/no, true/false,
> > on/off; also
> > + empty strings.
> > + Throws ValueError if the string can't be recognized.
> > + '''
> > + boolean_states = {'1': True, 'yes': True, 'true': True,
> 'on':
> > True,
> > + '0': False, 'no': False, 'false': False,
> 'off':
> > False}
> > + if not string:
> > + return False
> > +
> > + string = string.lower()
> > + if string not in boolean_states:
> > + raise ValueError, 'Not a boolean: %s' % string
> >
> > + return boolean_states[string]
>
> Everything looks good. Only one comment regarding the getbool()
> method.
> Doesn't SafeConfigParser provide different methods to return a value
> of
> a specific type? For example, getboolean(), getfloat(), getint().
> Should we be using (or overloading in the case of getboolean) those
> built-in methods instead?
>
> > http://docs.python.org/library/configparser.html
>
Great comment. If you look at ConfigParser source code, the implementation
is almost identical:
_boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
'0': False, 'no': False, 'false': False, 'off': False}
def getboolean(self, section, option):
v = self.get(section, option)
if v.lower() not in self._boolean_states:
raise ValueError, 'Not a boolean: %s' % v
return self._boolean_states[v.lower()]
Unfortunately we can't use their methods directly, because it requires section
names and option names and we sometimes need just the conversion. We also
should use the private attribute directly. So I basically copied that.
Of course, that creates the question - why do we in fact need to convert our
config options? Why don't we extract them with correct type right away? And
the answer is ticket #255 :)
https://fedorahosted.org/autoqa/ticket/255
In short, we shouldn't be using config files directly from our libraries.
We should have a Python class for it. getbool() is just a temporary solution
(hey, what's *not* a temporary solution?:o)) for making coding less
error-prone. It mainly helps with parsing the options obtained from
config_loader(autoqa_conf), which is not an ConfigParser object at all
(this should be changed, too).
Did I manage to explain it, or obscure it?
13 years, 5 months
[PATCH] Added a support for sending comments into Bodhi, part 2
by Martin Krizek
Hi all,
this is the patch from last week for sending comments to bodhi including some alterations based on the suggestions that were mentioned here, a list follows:
* split _bodhi_already_commented into two functions
* new config parser that supports alternative config files
* a new section bodhi in autoqa.conf allowing turning the support on/off and setting time span between posting the same comment/test result
* a few bug fixes
---
diff --git a/Makefile b/Makefile
index ac4ddeb..f3a14f9 100644
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,7 @@ install: build
install autoqa $(PREFIX)/usr/bin/
install -d $(PREFIX)/etc/autoqa
[ -f $(PREFIX)/etc/autoqa/autoqa.conf ] || install -m 0644 autoqa.conf $(PREFIX)/etc/autoqa/
+ [ -f $(PREFIX)/etc/autoqa/fas.conf ] || install -m 0640 -g autotest fas.conf $(PREFIX)/etc/autoqa
install -m 0644 repoinfo.conf $(PREFIX)/etc/autoqa/
install -d $(PREFIX)$(HOOK_DIR)
for h in hooks/*; do cp -a $$h $(PREFIX)$(HOOK_DIR); done
diff --git a/autoqa b/autoqa
index cb2d737..fd41503 100755
--- a/autoqa
+++ b/autoqa
@@ -1,7 +1,7 @@
#!/usr/bin/python
# autoqa-autotest: script to handle incoming requests from watchers and kick
# off appropriate autotest jobs.
-#
+#
# Copyright 2009, Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
@@ -30,8 +30,8 @@ import urlgrabber
import socket
import copy
import fnmatch
-from ConfigParser import *
from subprocess import call
+from autoqa.util import SingleConfigParser, getbool
cfgfile = '/etc/autoqa/autoqa.conf'
# Hardcoded defaults for the 'general' section
@@ -42,20 +42,11 @@ conf = {
'notification_email': '',
'autotest_server': socket.gethostname(),
}
-cfg_parser = SafeConfigParser() # used by prep_controlfile
-try:
- cfg_parser.read(cfgfile)
- # override defaults with configfile values
- for k,v in cfg_parser.items('general'):
- conf[k] = v
- # save final config state back to ConfigParser instance
- for k,v in conf.items():
- cfg_parser.set('general',k,v)
-except IOError: # no config file
- pass
-except (NoSectionError, DuplicateSectionError, MissingSectionHeaderError), e:
- print "ERROR: Could not parse %s: %s" % (cfgfile, str(e))
- sys.exit(1)
+
+cfg_parser = SingleConfigParser()
+cfg_parser.read(cfgfile)
+conf = cfg_parser.get_section('general', conf) # used by prep_controlfile
+# we don't need to catch errors here, bcz we want autoqa to crash for invalid config
def prep_controlfile(controlfile, extradata):
'''Returns the name of a temporary file containing the given controlfile,
@@ -97,7 +88,7 @@ def eval_test_vars(test, test_vars):
Arguments:
* test - name of the test for which control.autoqa should be executed
* test_vars - dictionary with test variables that will be used for input
- Returns: dictionary with test variables that have been evaluated (and
+ Returns: dictionary with test variables that have been evaluated (and
probably modified) by test's control.autoqa file
'''
cfile = open(os.path.join(conf['testdir'], test, 'control.autoqa'))
@@ -187,7 +178,7 @@ if not os.path.isdir(conf['hookdir']):
known_hooks = [d for d in os.listdir(conf['hookdir']) if os.path.isdir(os.path.join(conf['hookdir'], d))]
# Set up the option parser
-parser = optparse.OptionParser(usage="%prog HOOKNAME [options] ...",
+parser = optparse.OptionParser(usage="%prog HOOKNAME [options] ...",
add_help_option=False)
parser.add_option('-h', '--help', action='help',
help='show this help message (or hook help message if HOOKNAME given) and \
@@ -232,7 +223,7 @@ hook.extend_parser(parser)
args.pop(0) # dump hookname
# Run the tests locally, or schedule them through autotest?
-run_local = (opts.local or (conf['local'].lower() == 'true'))
+run_local = (opts.local or getbool(conf['local']))
if not opts.arch or run_local:
opts.arch = ['noarch']
diff --git a/autoqa.conf b/autoqa.conf
index ed8662a..69278fe 100644
--- a/autoqa.conf
+++ b/autoqa.conf
@@ -23,3 +23,12 @@ result_email =
mail_from = autoqa(a)fedoraproject.org
# hostname or hostname:port of smtp server / mailhub to use for sending email
smtpserver = localhost
+
+[bodhi]
+# If "true", test results (for tests utilizing this feature) will be sent
+# as comments to Fedora Update System (Bodhi). This requires that you have
+# Bodhi credentials filled in in fas.conf.
+send_bodhi_comments = false
+# how long (minutes) should we wait before posting the same comment to bodhi
+# by default 3 days (3*24*60 = 4320)
+bodhi_posting_comments_span = 4320
diff --git a/autoqa.spec b/autoqa.spec
index 261b20a..84b9efe 100644
--- a/autoqa.spec
+++ b/autoqa.spec
@@ -62,6 +62,7 @@ make build PYTHON=%{__python}
rm -rf $RPM_BUILD_ROOT
make install PREFIX=$RPM_BUILD_ROOT TEST_DIR=%{testdir} HOOK_DIR=%{hookdir} PYTHON=%{__python}
install -m 644 autoqa.conf repoinfo.conf $RPM_BUILD_ROOT%{_sysconfdir}/autoqa/
+install -m 640 -g autotest fas.conf $RPM_BUILD_ROOT%{_sysconfdir}/autoqa/
# front-ends/israwhidebroken
mv %{buildroot}%{_bindir}/start-israwhidebroken %{buildroot}%{_sbindir}/
mv %{buildroot}%{_bindir}/israwhidebroken.wsgi %{buildroot}%{_sbindir}/
@@ -78,6 +79,7 @@ rm -rf $RPM_BUILD_ROOT
%doc README LICENSE TODO autoqa.cron
%dir %{_sysconfdir}/autoqa
%config(noreplace) %{_sysconfdir}/autoqa/autoqa.conf
+%config(noreplace) %{_sysconfdir}/autoqa/fas.conf
%config %{_sysconfdir}/autoqa/repoinfo.conf
%config(noreplace) %{testdir}/rats_sanity/irb.cfg
%dir %attr(0775,root,autotest) %{_localstatedir}/cache/autoqa
diff --git a/doc/test_class.py.template b/doc/test_class.py.template
index 590bd16..c19074a 100644
--- a/doc/test_class.py.template
+++ b/doc/test_class.py.template
@@ -32,7 +32,7 @@ from autotest_lib.client.bin import utils
# Your class name must match file name (without .py) and also run_test line in
# its control file.
-class testclassname(AutoQATest): # <-- UPDATE Classname
+class testclassname(AutoQATest): # <-- UPDATE class name
version = 1 # increment this if setup() changes
# All methods below may receive arbitrary number of arguments that you
@@ -53,7 +53,7 @@ class testclassname(AutoQATest): # <-- UPDATE Classname
# method - if you don't need to initialize anything, delete this block.
#@ExceptionCatcher()
#def initialize(self, config, **kwargs): #**kwargs needs to stay
- # super(testclassname, self).initialize(config) # <-- UPDATE Classname
+ # super(testclassname, self).initialize(config) # <-- UPDATE class name
# #your extra initialization code goes here
# This is where the test code actually gets run. It's the only required
@@ -65,6 +65,7 @@ class testclassname(AutoQATest): # <-- UPDATE Classname
# self.highlights: important lines to notice (string or list of strings)
@ExceptionCatcher()
def run_once(self, some_params, **kwargs): #**kwargs needs to stay
+ super(testclassname, self).run_once() # <-- UPDATE class name
cmd = 'test_binary --param %s' % some_params
self.outputs = utils.system_output(cmd, retain_output=True)
diff --git a/fas.conf b/fas.conf
new file mode 100644
index 0000000..5fc322e
--- /dev/null
+++ b/fas.conf
@@ -0,0 +1,6 @@
+# FAS (Fedora Accounts System) credentials
+# These credentials are used when reporting results in the name of AutoQA,
+# i.e. posting a comment into Bodhi
+[fas]
+username =
+password =
diff --git a/hooks/post-tree-compose/watch-composes.py b/hooks/post-tree-compose/watch-composes.py
index 64a7fd3..a9d4e23 100755
--- a/hooks/post-tree-compose/watch-composes.py
+++ b/hooks/post-tree-compose/watch-composes.py
@@ -28,6 +28,7 @@ import sys
import subprocess
from autoqa.repoinfo import repoinfo
import optparse
+from autoqa.util import getbool
# Set up the option parser
parser = optparse.OptionParser(description='A utility to watch a set of \
@@ -40,7 +41,7 @@ parser.add_option('--dryrun', '--dry-run', action='store_true',
repoinfo.setarch('%%a') # two %% because of ConfigParser interpolation
# Get the list of repos to watch from repoinfo (see /etc/autoqa/repoinfo.conf)
-watchcomposes = [r for r in repoinfo.repos() if repoinfo.get(r,'composes') == 'yes']
+watchcomposes = [r for r in repoinfo.repos() if getbool(repoinfo.get(r,'composes'))]
# Setup a cache path
cachedir = '/var/cache/autoqa/watch-composes'
diff --git a/lib/python/bodhi_utils.py b/lib/python/bodhi_utils.py
index 9c0adbc..94e1a7d 100644
--- a/lib/python/bodhi_utils.py
+++ b/lib/python/bodhi_utils.py
@@ -18,9 +18,15 @@
#
# Authors:
# Will Woods <wwoods(a)redhat.com>
+# Martin Krizek <mkrizek(a)redhat.com>
import fedora.client
import time
+import sys
+import re
+from datetime import datetime
+from util import SingleConfigParser, getbool
+import ConfigParser
def bodhitime(timestamp):
'''Convert timestamp (seconds since Epoch, assumed to be local time) to a
@@ -46,3 +52,199 @@ def bodhi_list(params, limit=100):
updates += r['updates']
params['tg_paginate_no'] += 1
return updates
+
+def _bodhi_already_commented(update, user, testname, arch):
+ '''Check if the comment is already posted.
+
+ Args:
+ update -- The *title* of the update
+ user -- username that posted the comment
+ testname -- the name of the test
+ arch -- tested architecture
+
+ Returns:
+ Tuple containing old result and time when the last comment was posted,
+ if none comment is posted already, tuple will be empty.
+ '''
+ params = dict()
+ params['package'] = update
+ res = bodhi_list(params)
+ return _check_already_commented(res[0], user, testname, arch)
+
+def _check_already_commented(update, user, testname, arch):
+ '''Check if the comment is already posted.
+
+ Args:
+ update -- The *title* of the update
+ user -- username that posted the comment
+ testname -- the name of the test
+ arch -- tested architecture
+
+ Returns:
+ Tuple containing old result and time when the last comment was posted,
+ if none comment is posted already, tuple will be empty.
+ '''
+ comment_re = r'AutoQA:[\s]+%s[\s]+test[\s]+(\w+)[\s]+on[\s]+%s' % (testname, arch)
+ old_result = ''
+ comment_time = ''
+
+ for comment in update['comments']:
+ if comment['author'] == user:
+ m = re.match(comment_re, comment['text'])
+ if m == None:
+ continue
+ old_result = m.group(1)
+ comment_time = comment['timestamp']
+
+ return (old_result, comment_time)
+
+def _is_bodhi_testresult_needed(old_result, comment_time, result, time_span):
+ '''Check if the comment is meant to be posted.
+
+ Args:
+ old_result -- the result of the last test
+ comment_time -- the comment time of the last test
+ result -- the result of the test
+ time_span -- waiting period before posting the same comment
+
+ Returns:
+ True if the comment will be posted, False otherwise.
+ '''
+ # the first comment or a comment with different result, post it
+ if not old_result or old_result != result:
+ return True
+
+ # If we got here, it means that the comment with the same result has been
+ # already posted, we now need to determine whether we can post the
+ # comment again or not.
+ # If the previous result is *not* 'FAILED', we won't post it in order not to
+ # spam developers.
+ # If the previous result *is* 'FAILED', we will need to check whether given
+ # time span expired, if so, we will post the same comment again to remind
+ # a developer about the issue.
+
+ if result != 'FAILED':
+ return False
+
+ posted_datetime = datetime.strptime(comment_time, '%Y-%m-%d %H:%M:%S')
+ delta = (datetime.utcnow() - posted_datetime)
+ # total_seconds() is introduced in python 2.7, until 2.7 is everywhere...
+ total_seconds = (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10**6) / 10**6
+ minutes = total_seconds/60.0
+ if minutes < time_span:
+ return False
+
+ return True
+
+def bodhi_post_testresult(update, testname, result, url, config, arch = 'noarch', karma = 0):
+ '''Post comment and karma to bodhi
+
+ Args:
+ update -- the *title* of the update comment on
+ testname -- the name of the test
+ result -- the result of the test
+ url -- url of the result of the test
+ config -- autoqa config
+ arch -- tested architecture (default 'noarch')
+ karma -- karma points (default 0)
+
+ Returns:
+ True if comment was posted successfully or comment wasn't meant to be
+ posted (either posting is turned off or comment was already posted),
+ False otherwise.
+ '''
+ err_msg = 'Could not post a comment to bodhi'
+ conffiles = ['fas.conf', '/etc/autoqa/fas.conf']
+ try:
+ cfg_parser = SingleConfigParser()
+ cfg_parser.read_single(conffiles)
+ fas = cfg_parser.get_section('fas')
+ except (IOError, ConfigParser.Error), e:
+ print >> sys.stderr, "ERROR: Couldn't read none of config files: %s" % conffiles
+ print >> sys.stderr, e
+ return False
+
+ if not update or not testname or not result or url == None:
+ sys.stderr.write('Incomplete arguments!\n%s\n' % err_msg)
+ return False
+
+ try:
+ if not getbool(config.get('bodhi', 'send_bodhi_comments')):
+ print 'Sending bodhi comments is turned off. Test result will NOT be sent.'
+ return True
+ except KeyError:
+ print 'Sending bodhi comments is turned off. Test result will NOT be sent.'
+ # option missing -> it's false, do not send it (but return True since
+ # it's intentional, not an error)
+ return True
+
+ try:
+ user = fas['username']
+ pswd = fas['password']
+ if not user or not pswd:
+ raise KeyError
+ except KeyError:
+ sys.stderr.write('Conf file containing FAS credentials is incomplete!\n%s\n' % err_msg)
+ return False
+
+ comment = 'AutoQA: %s test %s on %s. The result can be found at: %s.' \
+ % (testname, result, arch, url)
+ try:
+ (old_result, comment_time) = _bodhi_already_commented(update, user, testname, arch)
+ time_span = int(config.get('bodhi', 'bodhi_posting_comments_span'))
+
+ if not _is_bodhi_testresult_needed(old_result, comment_time, result, time_span):
+ print 'The test result already posted to bodhi.'
+ return True
+
+ bodhi = fedora.client.BodhiClient(username=user, password=pswd)
+
+ if not bodhi.comment(update, comment, karma):
+ sys.stderr.write('%s\n') % err_msg
+ return False
+
+ print 'The test result was sent to bodhi successfully.'
+ except Exception, e:
+ sys.stderr.write('An error occured: %s\n' % e)
+ sys.stderr.write('Could not connect to bodhi!\n%s\n' % err_msg)
+ return False
+
+ return True
+
+def _self_test():
+ '''
+ Simple self test.
+ '''
+ from datetime import timedelta
+ from ConfigParser import SafeConfigParser
+ cfg_parser = SafeConfigParser()
+ cfg_parser.read('/etc/autoqa/autoqa.conf')
+ time_span = int(cfg_parser.get('bodhi', 'bodhi_posting_comments_span'))
+ try:
+ print '1. Test:',
+ assert _is_bodhi_testresult_needed('PASSED', datetime.now, 'PASSED', time_span) == False
+ print 'Passed'
+ print '2. Test:',
+ assert _is_bodhi_testresult_needed('FAILED', datetime.now, 'PASSED', time_span) == True
+ print 'Passed'
+ print '3. Test:',
+ assert _is_bodhi_testresult_needed('PASSED', datetime.now, 'FAILED', time_span) == True
+ print 'Passed'
+ print '4. Test:',
+ date = (datetime.utcnow() - timedelta(minutes=time_span)).\
+ strftime('%Y-%m-%d %H:%M:%S')
+ assert _is_bodhi_testresult_needed('FAILED', date, 'FAILED', time_span) == True
+ print 'Passed'
+ print '5. Test:',
+ date = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
+ assert _is_bodhi_testresult_needed('FAILED', date, 'FAILED', time_span) == False
+ print 'Passed'
+ print '6. Test:',
+ assert _is_bodhi_testresult_needed('', '', 'FAILED', time_span) == True
+ print 'Passed'
+ except AssertionError:
+ print 'Failed [!!!]'
+
+if __name__ == '__main__':
+ _self_test()
+
diff --git a/lib/python/test.py b/lib/python/test.py
index d59d765..29bb96c 100644
--- a/lib/python/test.py
+++ b/lib/python/test.py
@@ -46,7 +46,7 @@ class AutoQATest(test.test, object):
@ExceptionCatcher()
def run_once(self, **kwargs):
- pass
+ os.chdir(self.bindir) # easiest way for tests to find their test scripts, config files, etc
def process_exception(self, exc):
self._convert_list_variables()
diff --git a/lib/python/util.py b/lib/python/util.py
index b6d0013..b34259c 100644
--- a/lib/python/util.py
+++ b/lib/python/util.py
@@ -18,6 +18,7 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Author: Will Woods <wwoods(a)redhat.com>
+# Martin Krizek <mkrizek(a)redhat.com>
import os
import sys
@@ -29,6 +30,7 @@ import urlgrabber.progress
import config
import urllib
import socket
+import ConfigParser
def timestamp_to_compose_id(timestamp=None, serial=1):
if not timestamp:
@@ -180,4 +182,56 @@ def make_autotest_url(config):
autotest_url = "http://%s/results/%s/" % (autotest_server, jobtag)
return(autotest_url)
+class SingleConfigParser(ConfigParser.SafeConfigParser):
+ '''This is a subclassed SafeConfigParser. It has several additional methods
+ serving our needs.
+ '''
+ def read_single(self, filenames):
+ '''This is the same as the read() method, but it parsers only the first
+ available config file from the list, not all of them.
+ '''
+ if isinstance(filenames, str):
+ filenames = [filenames]
+ for f in filenames:
+ retval = self.read(f)
+ if retval:
+ # file read, don't read any other
+ return retval
+ else:
+ return []
+
+ def get_section(self, section, default_conf={}):
+ '''Read the whole section and convert it into a dictionary (with string
+ values).
+
+ Args:
+ section -- section of the config to be retrieved
+ default_conf -- default configuration values
+
+ Returns:
+ Dictionary containing retrieved data on success.
+
+ Throws:
+ ConfigParser.NoSectionError -- when no such section exists
+ '''
+ items = dict(self.items(section))
+ retval = default_conf.copy()
+ retval.update(items)
+ return retval
+
+def getbool(string):
+ '''Converts a string into a boolean.
+ Recognizes many usual strings: 1/0, yes/no, true/false, on/off; also
+ empty strings.
+ Throws ValueError if the string can't be recognized.
+ '''
+ boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
+ '0': False, 'no': False, 'false': False, 'off': False}
+ if not string:
+ return False
+
+ string = string.lower()
+ if string not in boolean_states:
+ raise ValueError, 'Not a boolean: %s' % string
+ return boolean_states[string]
diff --git a/tests/conflicts/conflicts.py b/tests/conflicts/conflicts.py
index 5a93a2b..c9a4667 100644
--- a/tests/conflicts/conflicts.py
+++ b/tests/conflicts/conflicts.py
@@ -34,11 +34,11 @@ class conflicts(AutoQATest):
@ExceptionCatcher()
def run_once(self, baseurl, parents, name, **kwargs):
+ super(conflicts, self).run_once()
if name:
name = "%s-%s" % (name, autoqa.util.get_basearch())
else:
name = baseurl
- os.chdir(self.bindir)
cmd = './potential_conflict.py --tempcache --newest'
cmd += ' --repofrompath=target,%s --repoid=target' % baseurl
count = 1
diff --git a/tests/helloworld/helloworld.py b/tests/helloworld/helloworld.py
index a0bd1d4..1a96ef5 100644
--- a/tests/helloworld/helloworld.py
+++ b/tests/helloworld/helloworld.py
@@ -25,6 +25,7 @@ class helloworld(AutoQATest):
@ExceptionCatcher()
def run_once(self, *args, **kwargs):
+ super(helloworld, self).run_once()
self.summary = 'Hello, World!'
self.outputs = "===Printing passed params===\n"
for arg in args:
diff --git a/tests/initscripts/initscripts.py b/tests/initscripts/initscripts.py
index e0f95bb..8e35e35 100644
--- a/tests/initscripts/initscripts.py
+++ b/tests/initscripts/initscripts.py
@@ -101,6 +101,7 @@ class initscripts(AutoQATest):
@ExceptionCatcher()
def run_once(self, kojitag, **kwargs):
+ super(initscripts, self).run_once()
if kwargs['hook'] == 'post-koji-build':
envrs = [kwargs['envr']]
update_id = kwargs['envr']
diff --git a/tests/rats_install/rats_install.py b/tests/rats_install/rats_install.py
index 552381a..23c2015 100644
--- a/tests/rats_install/rats_install.py
+++ b/tests/rats_install/rats_install.py
@@ -52,11 +52,11 @@ class rats_install(AutoQATest):
@ExceptionCatcher()
def run_once(self, baseurl, name, image_url="", boot_args="", **kwargs):
+ super(rats_install, self).run_once()
if name:
name = "%s-%s" % (name, util.get_basearch())
else:
name = baseurl
- os.chdir(self.bindir)
cmd = "./install.py -s %s -l %s" % (self.tmpdir, self.resultsdir)
if image_url != "":
cmd += " -i %s" % image_url
diff --git a/tests/rats_sanity/rats_sanity.py b/tests/rats_sanity/rats_sanity.py
index d6a6357..19116ef 100644
--- a/tests/rats_sanity/rats_sanity.py
+++ b/tests/rats_sanity/rats_sanity.py
@@ -40,11 +40,11 @@ class rats_sanity(AutoQATest):
@ExceptionCatcher()
def run_once(self, baseurl, parents, name, **kwargs):
+ super(rats_install, self).run_once()
if name:
name = "%s-%s" % (name, util.get_basearch())
else:
name = baseurl
- os.chdir(self.bindir)
cmd = "./sanity.py -s %s -l %s" % (self.tmpdir, self.resultsdir)
cmd += " %s" % baseurl
self.result = None
diff --git a/tests/repoclosure/repoclosure.py b/tests/repoclosure/repoclosure.py
index b27b684..f167722 100644
--- a/tests/repoclosure/repoclosure.py
+++ b/tests/repoclosure/repoclosure.py
@@ -31,6 +31,7 @@ class repoclosure(AutoQATest):
@ExceptionCatcher()
def run_once(self, baseurl, parents='', name='', **kwargs):
+ super(repoclosure, self).run_once()
if name:
name = "%s-%s" % (name, autoqa.util.get_basearch())
else:
diff --git a/tests/rpmguard/rpmguard.py b/tests/rpmguard/rpmguard.py
index 588a58f..bf973f0 100644
--- a/tests/rpmguard/rpmguard.py
+++ b/tests/rpmguard/rpmguard.py
@@ -43,6 +43,7 @@ class rpmguard(AutoQATest):
@ExceptionCatcher()
def run_once(self, kojitag, **kwargs):
+ super(rpmguard, self).run_once()
if kwargs['hook'] == 'post-koji-build':
envrs = [kwargs['envr']]
update_id = kwargs['envr']
diff --git a/tests/rpmlint/rpmlint.py b/tests/rpmlint/rpmlint.py
index 5944c78..1c06a8b 100644
--- a/tests/rpmlint/rpmlint.py
+++ b/tests/rpmlint/rpmlint.py
@@ -43,6 +43,7 @@ class rpmlint(AutoQATest):
@ExceptionCatcher()
def run_once(self, kojitag, **kwargs):
+ super(rpmlint, self).run_once()
if kwargs['hook'] == 'post-koji-build':
envrs = [kwargs['envr']]
update_id = kwargs['envr']
diff --git a/tests/upgradepath/upgradepath.py b/tests/upgradepath/upgradepath.py
index c53285f..a99ac2f 100755
--- a/tests/upgradepath/upgradepath.py
+++ b/tests/upgradepath/upgradepath.py
@@ -86,6 +86,7 @@ class upgradepath(AutoQATest):
@ExceptionCatcher()
def run_once(self, envrs, kojitag, **kwargs):
+ super(upgradepath, self).run_once()
update_id = kwargs['name'] or kwargs['id']
# Get a list of all repos we monitor (currently not -testing)
---
Martin
13 years, 5 months
Re: PATCH proposal - new Koji watcher
by Josef Skladanka
OK, gang, here it comes:
I created new git branch 'new_koji_watcher':
<http://git.fedorahosted.org/git/?p=autoqa.git;a=shortlog;h=refs/heads/new...>
The information from first email
<https://fedorahosted.org/pipermail/autoqa-devel/2010-December/001441.html>
still applies, although the batch part should be fixed (i.e. runs on my testing machine)/
Feel free to comment/throw stones :)
Joza
----- Original Message -----
From: "Kamil Paral" <kparal(a)redhat.com>
To: "AutoQA development" <autoqa-devel(a)lists.fedorahosted.org>
Sent: Monday, December 13, 2010 2:31:24 PM GMT +01:00 Amsterdam / Berlin / Bern / Rome / Stockholm / Vienna
Subject: Re: PATCH proposal - new Koji watcher
----- "Josef Skladanka" <jskladan(a)redhat.com> wrote:
> Thanks Kamil, that will teach me to post patches on late fridays :)
>
> /me hates typos & is fixing the patch right now
>
> Do you think it would be better to push the changes into new git
> branch,
Yes, yes, yes! :)
I encourage everyone with medium or large-size patch to also push it to some
branch if possible. It's just much easier to switch to that code, there are
no "cannot apply patch" problems and one can fix typos right away, if desired.
OTOH this might be overkill for smaller patches.
> or should I send yet another huuuuge patch?
>
Of course in-line patches are still a good idea, even if you have published
it in a branch, since it's easy to comment on specific parts of code this way.
> J.
>
_______________________________________________
autoqa-devel mailing list
autoqa-devel(a)lists.fedorahosted.org
https://fedorahosted.org/mailman/listinfo/autoqa-devel
13 years, 5 months
Re: PATCH proposal - new Koji watcher
by Josef Skladanka
Thanks Kamil, that will teach me to post patches on late fridays :)
/me hates typos & is fixing the patch right now
Do you think it would be better to push the changes into new git
branch, or should I send yet another huuuuge patch?
J.
----- Original Message -----
From: "Kamil Paral" <kparal(a)redhat.com>
To: "AutoQA development" <autoqa-devel(a)lists.fedorahosted.org>
Sent: Monday, December 13, 2010 1:15:00 PM GMT +01:00 Amsterdam / Berlin / Bern / Rome / Stockholm / Vienna
Subject: Re: PATCH proposal - new Koji watcher
----- "Kamil Paral" <kparal(a)redhat.com> wrote:
>
> It's too much new code to check for correctness, but I tried to run
> that,
> and I have a few tracebacks for you :)
Ok, some more:
http://fpaste.org/sVA9/
Those 'atest job create' failures are expected (no autotest server package
installed), the rest are not.
Maybe we should print out the command being run with verbose turned on even
if dryrun is turned off, what say you?
_______________________________________________
autoqa-devel mailing list
autoqa-devel(a)lists.fedorahosted.org
https://fedorahosted.org/mailman/listinfo/autoqa-devel
13 years, 5 months
Re: PATCH proposal - new Koji watcher
by Kamil Paral
----- "Kamil Paral" <kparal(a)redhat.com> wrote:
>
> It's too much new code to check for correctness, but I tried to run
> that,
> and I have a few tracebacks for you :)
Ok, some more:
http://fpaste.org/sVA9/
Those 'atest job create' failures are expected (no autotest server package
installed), the rest are not.
Maybe we should print out the command being run with verbose turned on even
if dryrun is turned off, what say you?
13 years, 5 months
Re: PATCH proposal - new Koji watcher
by Kamil Paral
----- "Josef Skladanka" <jskladan(a)redhat.com> wrote:
> Hi gang,
>
> according to #228 <https://fedorahosted.org/autoqa/ticket/228>,
> and discussions with kparal, I've rewritten the koji watcher,
> so it can check also the -pending tags in koji.
>
> For the -pendig tags, we're using quite a different querying model
> based on parsing the tagHistory (e.g.
> # koji list-tag-history --tag='dist-f14-updates-pending'
> )
> The benefit of this solution is, that we can catch the situations
> like
>
> 1) package Foo is built at date XYZ. It gains tag
> dist-f14-updates-pending
That should read dist-f14-updates-candidate I guess.
> 2) koji-watcher founds out "ha, new package, let's test it"
> 3) tests are OK
> 4) package Foo gains dist-f14-updates-testing-pending tag
> 5) we'd like to run tests like depcheck on it, but because the 'built
> at'
> date, which the actual watcher checks is not altered, we miss the
> change
>
> But parsing the tag history, we only check for new 'events' in the
> tag
> history (i.e. 'package foo got the -pending tag few minutes ago'),
> independently
> on the build time -> win :)
>
> The drawback is, that at the moment, querying for the tag history
> takes time
> because koji sends the whole tag history (a lot of data). I'm
> discussing a minor
> change which would allow us to specify "give me the history which is
> newer than
> date XYZ" (as is currently used for fetching new builds) with
> jkeating.
>
> Because of this, the non-pending repos are still handled the 'old'
> way.
>
> ....
>
> We also found us in need of 'batch' scheduling - e.g. we don't want to
> run
> depcheck for every package built, but we'd like just to inform autoqa
> "hey, there is new stuff in dist-f14-updates-pending tag, run stuff".
> So there is new watch-koji-builds-batch watcher.
>
> The thing is, that it is not really a watcher as such, only the
> hook.py
> file. The reason to do this is, that querying koji is time-consuming
> operation,
> and there is no need to do it twice, to get the same data. So there
> are two
> different 'schedule jobs' methods in the watch-koji-build hook, and
> one
> schedules 'per package' jobs, and the second does it as a 'batch'.
>
> (note that the batch one is not yet tested - I just wanted to post you
> the
> patch, so you can see the new 'concept'. The per-package part is
> tested,
> and considered to be working).
>
>
> Comments are more than welcomed - looking forward to hearing from
> you!
(master)$ git apply koji_watcher.patch
fatal: git apply: bad git-diff - expected /dev/null on line 4
Gosh, do I have git somehow broken today? (Or is it my hands?)
Edit: Ah, CRLF characters confused git. Good to know. Dos2unix fixed that.
It's too much new code to check for correctness, but I tried to run that,
and I have a few tracebacks for you :)
[root@aqd autoqa]# /usr/share/autoqa/post-koji-build/watch-koji-builds.py --dryrun
File "/usr/share/autoqa/post-koji-build/watch-koji-builds.py", line 376
exit status = 0
^
SyntaxError: invalid syntax
[root@aqd autoqa]# /usr/share/autoqa/post-koji-build/watch-koji-builds.py --dryrun
...
Traceback (most recent call last):
File "/usr/share/autoqa/post-koji-build/watch-koji-builds.py", line 459, in <module>
exit_status = watcher.run()
File "/usr/share/autoqa/post-koji-build/watch-koji-builds.py", line 434, in run
exit_status_2.append(self.schedule_jobs_batch(new_builds))
NameError: global name 'exit_status_2' is not defined
[root@aqd autoqa]# /usr/share/autoqa/post-koji-build/watch-koji-builds.py --dryrun
...
Traceback (most recent call last):
File "/usr/share/autoqa/post-koji-build/watch-koji-builds.py", line 459, in <module>
exit_status = watcher.run()
File "/usr/share/autoqa/post-koji-build/watch-koji-builds.py", line 434, in run
exit_status.append(self.schedule_jobs_batch(new_builds))
File "/usr/share/autoqa/post-koji-build/watch-koji-builds.py", line 404, in schedule_jobs_batch
print " ".join(harnescall)
TypeError: sequence item 4: expected string, list found
After correcting these problems, here is the sample output:
http://fpaste.org/mOtd/
This approach comes from my and Josef's discussion and I think it's the best
thing we can currently do to accommodate depcheck-style tests into our
framework.
13 years, 5 months
No longer package -israwhidebroken
by James Laska
Greetings,
I mentioned this idea to Kamil on IRC today, so I generated a small patchset to
disable packaging of autoqa-israwhidebroken. Rather than drop the code
entirely, this patch disables RPM package generation for the israwhidebroken
front-end. We aren't using the front-end anywhere, and it was originally a
proof-of-concept. I think the code is still worthwhile to keep around for a
bit longer (for example, I used it to create a wsgi git-commit watcher).
This patchset corrects a missing Makefile issue, as well as updates autoqa.spec
to disable -israwhidebroken sub-package generation. I have not added any
%obsoletes to forcibly remove that package from any systems.
Comments/concerns?
Thanks,
James
13 years, 5 months