From 32c4a7b4463da483d26ca6ac591bd9a3577e270f Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Mar 02 2016 08:49:08 +0000 Subject: drop 'nightly', handle fedfind 2 and pungi 4 nightlies First steps in adapting relval to fedfind 2 and Pungi 4. We drop the 'nightly' subcommand entirely because all that woo is gonna move into a new dedicated fedmsg consumer for creating events when new composes appear. So now we just use 'compose' for creating nightly events as well, when/if we ever need to do it manually. We also allow creating events by passing a Pungi compose URL rather than release/milestone/compose, using the wikitcms get_validation_event_url code. size_check gets adapted to fedfind 2.x, the conversion wasn't too painful happily enough. --- diff --git a/relval/cli.py b/relval/cli.py index decc566..03ab8a1 100755 --- a/relval/cli.py +++ b/relval/cli.py @@ -238,90 +238,39 @@ class Cli(object): subparsers = parser.add_subparsers() parser_compose = subparsers.add_parser( - 'compose', description="Create Fedora TC/RC release validation " - "event result pages. You must specify --milestone and " - "--compose. This command requires login; see main help " - "information about user name and password.") - parser_compose.add_argument('-r', '--release', help=release_help, - type=int, required=False, - choices=range(22, 100), metavar="22-99") - parser_compose.add_argument('-m', '--milestone', required=True, - help=milestone_help_specific, - choices=['Alpha', 'Beta', 'Final']) - parser_compose.add_argument('-c', '--compose', - help=compose_help_specific, - required=True, metavar="{T,R}C1-19") - parser_compose.add_argument('-y', '--testtype', help=testtype_help, - required=False, default='all') - parser_compose.add_argument('-u', '--username', help=username_help, - required=False) - parser_compose.add_argument('-f', '--force', help=force_help, - required=False, default=True, - action='store_const', const=None) - parser_compose.add_argument('-t', '--test', help=test_help, - required=False, action='store_true') - parser_compose.add_argument('-n', '--no-current', help=current_help, - required=False, action='store_true') - parser_compose.add_argument('-w', '--download-only', required=False, - help = download_help, action='store_true') - parser_compose.set_defaults(func=self.tcrc_compose, login=True, - guessrel=True, interactive=True) - - parser_nightly = subparsers.add_parser( - 'nightly', description="Create Fedora nightly release " - "validation event result pages. If you do not specify --date, " - "today's date will be used. If you do not specify --release, " - "the 'next' release according to the wiki FedoraVersionNumber " - "template will be used. If you do not specify --build, relval " - "will try to guess the correct type based on the release " - "chosen and the properties of the available trees, and quit " - "if it cannot make a good enough guess. This command requires " - "login; see main help information about user name and " - "password.") - parser_nightly.add_argument( - '-c', '--compose', '-d', '--date', help="The date of the nightly " - "compose to operate on (20141208, 20150123...)", metavar="DATE", - type=rrs.datecheck, required=False, - default=datetime.datetime.now().strftime('%Y%m%d')) - parser_nightly.add_argument( - '-r', '--release', help=release_help, type=int, required=False, - choices=range(22, 100), metavar="22-99") - parser_nightly.add_argument( - '-m', '--milestone', '-b', '--build', help="The milestone of the " - "compose (Rawhide or Branched)", required=False, - choices=(['Rawhide', 'Branched'])) - parser_nightly.add_argument( - '-y', '--testtype', help=testtype_help, required=False, - default='all') - parser_nightly.add_argument( - '-u', '--username', help=username_help, required=False) - parser_nightly.add_argument( - '-f', '--force', help=force_help, required=False, default=True, - action='store_const', const=None) - parser_nightly.add_argument( - '-t', '--test', help=test_help, required=False, - action='store_true') - parser_nightly.add_argument( - '-n', '--no-current', help=current_help, required=False, - action='store_true') - parser_nightly.add_argument( - '-i', '--if-needed', help="Only create the event if it is " - "'needed', compared to the current test event", required=False, - action='store_true') - parser_nightly.add_argument( - '-e', '--email', help="Send an announcement email for the event " - "from the given address, if it is created", required=False) - parser_nightly.add_argument( - '-w', '--download-only', required=False, help=download_help, - action='store_true') - parser_nightly.add_argument( - '-a', '--wait', required=False, type=int, help="Wait this number " - "of minutes for the compose to complete before creating the " - "event. You may want to do this to make sure the download table " - "contains all the images, especially when running unattended/" - "scheduled.") - parser_nightly.set_defaults( - func=self.nightly_compose, login=True, guessrel=True, + 'compose', description="Create Fedora release validation " + "event result pages. You must specify either --milestone and " + "--compose or --url; if --url is specified, any --release, " + "--milestone and --compose setting will be ignored. This " + "command requires login; see main help information about user " + "name and password.") + parser_compose.add_argument( + '-r', '--release', help=release_help, type=int, metavar="22-99", + choices=range(22, 100)) + parser_compose.add_argument( + '-m', '--milestone', help=milestone_help, choices=['Alpha', 'Beta', + 'Final', 'Branched', 'Rawhide']) + parser_compose.add_argument( + '-c', '--compose', help=compose_help, metavar="{T,R}C1-19 or " + "20160301") + parser_compose.add_argument( + '-l', '--url', help="URL of a Pungi 4 compose to create a " + "validation event for") + parser_compose.add_argument( + '-y', '--testtype', help=testtype_help, default='all') + parser_compose.add_argument( + '-u', '--username', help=username_help) + parser_compose.add_argument( + '-f', '--force', help=force_help, default=True, const=None, + action='store_const') + parser_compose.add_argument( + '-t', '--test', help=test_help, action='store_true') + parser_compose.add_argument( + '-n', '--no-current', help=current_help, action='store_true') + parser_compose.add_argument( + '-w', '--download-only', help=download_help, action='store_true') + parser_compose.set_defaults( + func=self.create_compose, login=True, guessrel=True, interactive=True) parser_userstats = subparsers.add_parser( @@ -502,12 +451,12 @@ class Cli(object): required=False, choices=range(1, 100), metavar="1-99") parser_sizecheck.add_argument( '-m', '--milestone', help="A milestone to check (e.g. Alpha or " - "Beta)", choices=['Alpha', 'Beta', 'Final'], required=False) + "Beta)", choices=['Alpha', 'Beta', 'Final', 'Branched', 'Rawhide']) parser_sizecheck.add_argument( '-c', '--compose', help="A compose to check (e.g. TC1 or RC3)", required=False, metavar="{T,R}C1-19, 20150213") parser_sizecheck.add_argument( - '--test', help=test_help, required=False, action='store_true') + '-t', '--test', help=test_help, action='store_true') parser_sizecheck.set_defaults( func=self.size_check, login=True, guessrel=False, interactive=True) @@ -521,20 +470,38 @@ class Cli(object): site = self.setup_site(args, creds) # guessrel is kinda ugly, but some sub-functions don't want release # guessing - if args.guessrel and not args.release: + if args.guessrel and not args.release and not args.url: args.release = self.guess_release(site, args) return (args, site) ## SUB-COMMAND METHODS ## - def create_compose(self, args, site, event, eventpages): + def create_compose(self, args, site): """Create the result pages for a given event (and, optionally, - test type). Uses parameters passed in from the command line and - from relevant sub-command functions, implements bits that are - the same whatever the type of compose for which the pages are - being created. Shared by tcrc_compose() and nightly_compose(). + test type). Uses parameters passed in from the command line. """ + try: + if args.url: + event = site.get_validation_event_url(args.url) + else: + if not args.milestone and args.compose: + sys.exit("You must specify --url or both --milestone " + "and --compose!") + event = site.get_validation_event( + release=args.release, milestone=args.milestone, + compose=args.compose) + except ValueError as err: + sys.exit(err) + testtypes = self.get_testtypes(site, args) + + if isinstance(event, ev.NightlyEvent): + pageobj = pg.NightlyPage + else: + pageobj = pg.ComposePage + eventpages = [pageobj(site, event.release, typ, event.milestone, + event.compose) + for typ in testtypes] print("Creating pages for event {0}".format(event.version)) if args.download_only: print("Just handling Download page...") @@ -576,214 +543,6 @@ class Cli(object): print("Unhandled error writing category page!") raise - def tcrc_compose(self, args, site): - """Create the result pages for a TC/RC compose event, by - getting the TC/RC compose type specific bits and passing them - along to create_compose(). - """ - try: - event = site.get_validation_event( - release=args.release, milestone=args.milestone, - compose=args.compose) - except ValueError: - sys.exit("Compose must be a TC/RC identifier (TC1, RC3...)") - testtypes = self.get_testtypes(site, args) - eventpages = [pg.ComposePage(site, args.release, t, args.milestone, - args.compose) - for t in testtypes] - self.create_compose(args, site, event, eventpages) - - def nightly_compose(self, args, site): - """Create the result pages for a nightly compose test event, - by getting the nightly compose type specific bits and passing - them along to create_compose(). - """ - curr = None - nocompose = True - if args.milestone: - tree = args.milestone - event = ev.NightlyEvent( - site, args.release, args.milestone, args.compose) - if event.compose_exists: - nocompose = False - else: - # We weren't told which tree (Rawhide or Branched) to use, so we're - # going to have to guess. The following encapsulates some specific - # knowledge of how the Fedora release cycle works, and may need - # changing if it changes. The point of this is to make it viable to - # have 'relval nightly --if-needed' running daily on a cron job - # somewhere, and have it only create an event when it's sure it's - # doing the right thing. For this guessing to work, the nightly - # compose(s) must at least have *started* when the command is run. - curr = site.current_compose - if curr['release'] and args.release == int(curr['release']) + 1: - # First nightly event after a release should always be Rawhide - event = ev.NightlyEvent(site, args.release, 'Rawhide', - args.compose) - if event.compose_exists: - tree = 'Rawhide' - nocompose = False - elif (curr['milestone'] and curr['milestone'] == 'Branched' and - curr['release'] and args.release == int(curr['release'])): - # Branched should never be directly succeeded by Rawhide - event = ev.NightlyEvent(site, args.release, 'Branched', - args.compose) - if event.compose_exists: - tree = 'Branched' - nocompose = False - elif (curr['milestone'] and curr['milestone'] == 'Rawhide' and - curr['release'] and args.release == int(curr['release'])): - # if current is Rawhide, prefer Branched if it exists (this - # covers the branch point), otherwise another Rawhide is OK - event = ev.NightlyEvent(site, args.release, 'Branched', - args.compose) - if event.compose_exists: - tree = 'Branched' - nocompose = False - else: - event = ev.NightlyEvent(site, args.release, 'Rawhide', - args.compose) - if event.compose_exists: - tree = 'Rawhide' - nocompose = False - else: - sys.exit("You didn't specify --milestone, and relval " - "can't figure out where we are in the release cycle. " - "It is not safe for relval to guess how to proceed. " - "Please specify a milestone.") - - if nocompose: - sys.exit("Could not find a compose for the specified or detected " - "milestone and date {0}!".format(event.version)) - - if args.wait: - try: - event.ff_release.wait(args.wait) - except fedfind.exceptions.WaitError: - sys.exit("Timed out waiting for compose to complete!") - - if not event.has_bootiso: - sys.exit("Specified or detected compose {0} has no boot.iso! It is " - "not testable.".format(event.version)) - - if args.if_needed: - print("Checking if new nightly test event needed for {0}".format( - event.version)) - newrelease = False - curr = site.current_compose - if curr['release'] and args.release > int(curr['release']): - newrelease = True - print("Requested release is newer than release of current test" - " event! Proceeding.") - else: - try: - currdate = datetime.datetime.strptime(curr['date'], - '%Y%m%d') - except: - if curr['compose']: - sys.exit("Current test event is for a TC/RC of the same" - " release! --needed will not succeed a TC/RC " - "test event for the same release.") - else: - sys.exit("Cannot determine date of current nightly test" - " event!") - newdate = datetime.datetime.strptime(args.compose, '%Y%m%d') - delta = newdate - currdate - if delta.days < 3: - sys.exit("Less than three days since the last nightly test " - "event.") - if delta.days > 14: - print("More than two weeks since last nightly test event!") - else: - print("Comparing package versions...") - packages = ['anaconda', 'python-blivet', 'pyparted', - 'lorax', 'pungi', 'parted', 'pykickstart'] - currevent = ev.NightlyEvent(site, curr['release'], - curr['milestone'], curr['date']) - try: - currpacks = currevent.get_package_versions(packages) - newpacks = event.get_package_versions(packages) - except: - sys.exit("Package version check failed!") - if not currpacks: - sys.exit("Could not do package version check on current " - "compose!") - if not newpacks: - sys.exit("Could not do package version check on new " - "compose!") - for key in currpacks.keys(): - if not currpacks[key] or not newpacks[key]: - sys.exit("Could not find versions for all " - "significant packages in both composes!") - if currpacks == newpacks: - sys.exit("No significant package changes since last " - " nightly test event.") - else: - print("Significant package changes since last nightly " - "test event!") - diff = '' - for key in currpacks.keys(): - if currpacks[key] != newpacks[key]: - diff += ("{0} - {1}: {2}, {3}: {4}\n".format( - key, curr['date'], currpacks[key], - args.compose, newpacks[key])) - print(diff) - - testtypes = self.get_testtypes(site, args) - eventpages = [pg.NightlyPage(site, args.release, t, tree, args.compose) - for t in testtypes] - self.create_compose(args, site, event, eventpages) - - if args.email: - print("Sending announcement email...") - if args.test: - dest = args.email - else: - dest = "test-announce@lists.fedoraproject.org" - urltmpl = "https://fedoraproject.org/wiki/{0}" - summurl = urltmpl.format(event.summary_page.name.replace(' ', '_')) - pageurls = [urltmpl.format(p.name.replace(' ', '_')) for p in - eventpages if u'Summary' not in p.name] - try: - difftext = '\nNotable package version changes:\n{0}'.format( - diff) - except: - difftext = '' - tmpl = ("""From: {0} -To: {1} -Subject: Fedora {2} nightly compose nominated for testing - -Announcing the creation of a new nightly release validation test event -for Fedora {2}. Please help run some tests for this -nightly compose if you have time. For more information on nightly -release validation testing, see: -https://fedoraproject.org/wiki/QA:Release_validation_test_plan -{3} -Test coverage information for the current release can be seen at: -https://www.happyassassin.net/testcase_stats/{4} - -You can see all results, find testing instructions and image download -locations, and enter results on the Summary page: - -{5} - -The individual test result pages are: - -{6} - -Thank you for testing! --- -Mail generated by relval: https://www.happyassassin.net/wikitcms/ -On behalf of: {7} -""") - msg = tmpl.format(args.email, dest, event.version, difftext, - event.release, summurl, '\n'.join(pageurls), - args.username) - server = smtplib.SMTP('localhost') - server.sendmail(args.email, [dest], msg) - print("Announcement mail sent from {0} to {1}!".format(args.email, - dest)) - def user_stats(self, args, site): """Print HTML-formatted statistics on the number of results reported by each tester for a given set of result pages. @@ -940,7 +699,8 @@ On behalf of: {7} event = site.get_validation_event( args.release, args.milestone, args.compose) ffrel = event.ff_release - print("Checking sizes for: {0}").format(event.version) + print("Checking sizes for event: " + "{0}, compose ID: {1}".format(event.version, ffrel.cid)) except ValueError: sys.exit("Could not find validation test event!") results = rsc.size_check(ffrel) @@ -955,8 +715,11 @@ On behalf of: {7} True) else: # Fail - construct a comment with details - comms = ("{0}, size {1}, max {2}".format(fail[0].desc, - fail[1], fail[2]) for fail in result) + comms = [] + for fail in result: + desc = ' '.join((fail[0]['payload'], fail[0]['type'])) + comms.append("{0}, size {1}, max {2}".format(desc, fail[1], + fail[2])) comment = "{0}".format(''.join(comms)) # Mark this as a bot result as it's an automated test. restup = wk.ResTuple( @@ -982,7 +745,7 @@ On behalf of: {7} args, site = self.parse_args() args.func(args, site) -## END OF Cli CLASS ## +## END OF ClI CLASS ## def main(): '''Main loop''' diff --git a/relval/size_check.py b/relval/size_check.py index 2d99fa6..d5b4a13 100644 --- a/relval/size_check.py +++ b/relval/size_check.py @@ -21,8 +21,8 @@ # functions that help implement the 'size_check' sub-command of relval, # which checks the sizes of images and reports the results. +import fedfind.helpers import fedfind.release -import requests # https://fedoraproject.org/wiki/QA:Testcase_Mediakit_ISO_Size # https://fedoraproject.org/wiki/Releases/22/Spins @@ -31,7 +31,7 @@ MAXSIZES = dict( boot = 734003200, cloud = 734003200, # This is the same as cloud_atomic_canned, it's a DVD-size image - cloud_atomic_boot = 4700000000, + atomic_boot = 4700000000, kde_live = 1400000000, workstation_live = 2000000000, design_suite_live = 2000000000, @@ -44,28 +44,16 @@ MAXSIZES = dict( STDTYPES = ('netinst', 'boot', 'dvd', 'live') -# These are the 'environments' (i.e. columns) for the test case we're -# testing here, in the Installation results page. The value for each -# key is an iterable of fedfind queries to find the images that should -# be tested to decide if that env 'passes' or 'fails'. -ENVS = dict( - Server = (fedfind.release.Query('flavor', ('server',)), - fedfind.release.Query('imagetype', STDTYPES)), - Workstation = (fedfind.release.Query('flavor', ('workstation',)), - fedfind.release.Query('imagetype', STDTYPES)), - Cloud = (fedfind.release.Query('flavor', ('cloud',)), - fedfind.release.Query('imagetype', STDTYPES)), - Spins = (fedfind.release.Query('flavor', ('',)), - fedfind.release.Query('imagetype', ('live',))) -) - def size_check(ffrel): """Do the thing! Check the things! CHECK ALL THE THINGS""" results = dict() - for (env, queries) in ENVS.items(): - print("Checking {0}...".format(env)) + for variant in ("Server", "Workstation", "Cloud", "Atomic", "Spins"): + print("Checking {0}...".format(variant)) fails = list() - imgs = ffrel.find_images(queries) + imgs = (img for img in ffrel.all_images if img['variant'] == variant) + imgs = (img for img in imgs if img['type'] in STDTYPES) + if variant == "Spins": + imgs = (img for img in imgs if img['type'] == "live") if not imgs: print("No images found!") continue @@ -73,27 +61,32 @@ def size_check(ffrel): for img in imgs: maxsize = 4700000000 try: - maxsize = MAXSIZES[img.imagetype] + maxsize = MAXSIZES[img['type']] except KeyError: pass try: - maxsize = MAXSIZES[img.payload] + maxsize = MAXSIZES[img['payload'].lower()] except KeyError: pass try: - maxsize = MAXSIZES['{0}_{1}'.format(img.payload, img.imagetype)] + maxsize = MAXSIZES['_'.join((img['payload'].lower(), img['type']))] except KeyError: pass - resp = requests.head(img.url) - imgsize = int(resp.headers['content-length']) + url = '/'.join((ffrel.location, img['path'])) + imgsize = fedfind.helpers.get_size(url) if imgsize > maxsize: fails.append((img, imgsize, maxsize)) + + # file Atomic results in Cloud for now + if variant == "Atomic": + variant = "Cloud" if fails: - results[env] = fails + results[variant] = fails for fail in fails: - print("FAIL: {0}, size {1}, max {2}".format(fail[0].filename, - fail[1], fail[2])) + filename = fail[0]['path'].split('/')[-1] + print("FAIL: {0}, size {1}, max {2}".format(filename, + fail[1], fail[2])) else: - results[env] = 'pass' + results[variant] = 'pass' print("PASS!") return results