11import os
2+ import re
3+ import requests
4+ from bs4 import BeautifulSoup
5+ from datetime import datetime
26
37header = '''# GENERATED FILE - DO NOT EDIT
48set -e
@@ -306,10 +310,100 @@ def openqa_call_start_meta_variables(meta_variables):
306310def pre_openqa_call_start (repos ):
307311 return ''
308312
309- openqa_call_start = lambda distri , version , archs , staging , news , news_archs , flavor_distri , meta_variables , assets_flavor , repo0folder , openqa_cli : '''
313+ def _find_latest_livepatches (url ):
314+ """
315+ Scrapes a URL to find all kernel-livepatch-* with the
316+ most recent date.
317+ """
318+ try :
319+ response = requests .get (url )
320+ response .raise_for_status ()
321+ except requests .exceptions .RequestException as e :
322+ print (f"Error fetching URL: { e } " )
323+ return []
324+
325+ soup = BeautifulSoup (response .text , 'html.parser' )
326+
327+ livepatches_unfiltered = []
328+ latest_date = None
329+ lp_regex = r'kernel-livepatch-((?:\d+_?)+-\d+)'
330+ #pdb.set_trace()
331+ for link in soup .find_all ('a' ):
332+ if (link and link .text .startswith ('kernel-livepatch-' )):
333+ #and '-rt' in link.text):
334+ #pdb.set_trace()
335+ if link .next_sibling and isinstance (link .next_sibling , str ):
336+ date_str = link .next_sibling .strip ().split ()[0 ]
337+
338+ try :
339+ # NOTE: The date format on dist.suse.de is YYYY-MM-DD.
340+ current_date = datetime .strptime (date_str , '%Y-%m-%d' )
341+ livepatches_unfiltered .append ({'name' : link .text , 'date' : current_date })
342+
343+ if latest_date is None or current_date > latest_date :
344+ latest_date = current_date
345+ except (ValueError , IndexError ):
346+ # Continue if the date format is unexpected or not present
347+ continue
348+
349+ if latest_date is None :
350+ return []
351+ # to list only the recent ones
352+ latest_patches_list = []
353+ for patch in livepatches_unfiltered :
354+ if patch ['date' ] == latest_date :
355+ kernel_version = re .search (lp_regex , patch ['name' ])
356+ latest_patches_list .append (kernel_version .group (1 ))
357+
358+ return latest_patches_list
359+
360+ def _find_product_folder (p ):
361+ """
362+ Tries to find the product name looking it up in the server's repo dirs
363+ :param p: The local path as defined in ActionGenerator.envdir
364+ :return: The matched product name
365+ """
366+ uri , prod = os .path .split (p )
367+ target_filename = "files_iso.lst"
368+ iso_file_found = None
369+
370+ for root , dirs , files in os .walk (p ):
371+ if target_filename in files :
372+ iso_file_found = os .path .join (root , target_filename )
373+ break # found
374+
375+ if iso_file_found :
376+ with open (iso_file_found , 'r' ) as f :
377+ iso_file = f .readline ().strip ()
378+ if iso_file .endswith ("spdx.json" ):
379+ prod_folder_pattern = r'(.*)-(?:aarch64|x86_64|s390x|ppc64le)'
380+ prod_folder = re .match (prod_folder_pattern , iso_file )
381+ if prod_folder :
382+ return prod_folder .group (1 )
383+ return ""
384+
385+ def openqa_fetch_livepatch_list (ag_vars ):
386+ kpatches = {}
387+ productpath_local = ag_vars .envdir
388+ prod_build_name = _find_product_folder (productpath_local )
389+ if not prod_build_name :
390+ return # no spdx.json, return and do what you do
391+ repo_path = ag_vars .productrepopath ().replace ('::' , '/' , 1 )
392+ repo_path = repo_path .replace ("repos" , ag_vars .brand )
393+ repo_path = f"https://{ repo_path } "
394+ for arch in ['x86_64' ,'aarch64' ,'s390x' ,'ppc64le' ]:
395+ totest_url = f"{ repo_path } /{ prod_build_name } -{ arch } /{ arch } /"
396+ latest_kernel_livepatches = _find_latest_livepatches (totest_url )
397+
398+ if latest_kernel_livepatches :
399+ kpatches .update ({arch : latest_kernel_livepatches })
400+ return kpatches
401+
402+ openqa_call_start = lambda distri , version , archs , staging , news , news_archs , flavor_distri , meta_variables , assets_flavor , repo0folder , openqa_cli , livepatches = {},: '''
310403archs=(ARCHITECTURS)
311404[ ! -f __envsub/files_repo.lst ] || ! grep -q -- "-POOL-" __envsub/files_repo.lst || additional_repo_suffix=-POOL
312-
405+ declare -a livepatches
406+ livepatches=(NONE)
313407for flavor in {FLAVORALIASLIST,}; do
314408 for arch in "${archs[@]}"; do
315409 filter=$flavor
@@ -326,6 +420,11 @@ def pre_openqa_call_start(repos):
326420 [ -n "$iso" ] || [ "$flavor" != "''' + assets_flavor + r'''" ] || buildex=$(grep -o -E '(Build|Snapshot)[^-]*' __envsub/files_asset.lst | head -n 1)
327421 [ -n "$iso$build" ] || build=$(grep -h -o -E '(Build|Snapshot)[^-]*' __envsub/Media1*.lst 2>/dev/null | head -n 1 | grep -o -E '[0-9]\.?[0-9]+(\.[0-9]+)*')|| :
328422 [ -n "$build" ] || continue
423+ livepatches_all="''' + str (livepatches ) + r'''"
424+ livepatches=($(echo "$livepatches_all" | sed "s/[{}']//g" | sed 's/, /\n/g' | awk -F':' -v arch="$arch" '$1 == arch {print $2}'))
425+ if [ ${#livepatches[@]} -eq 0 ]; then
426+ livepatches=(NONE)
427+ fi
329428 buildex=${buildex/.install.iso/}
330429 buildex=${buildex/.iso/}
331430 buildex=${buildex/.raw.xz/}
@@ -342,7 +441,12 @@ def pre_openqa_call_start(repos):
342441 }
343442 fi
344443 # test "$destiso" != "" || continue
444+ for livepatch in "${livepatches[@]}"; do
345445 echo "''' + openqa_cli + ''' \\ \\ \"
446+ if [[ "${livepatch}" != "NONE" ]]; then
447+ echo \" KGRAFT=1 \\ \\
448+ KERNEL_VERSION=${livepatch} \\ \\ \"
449+ fi
346450(
347451 echo \" DISTRI=$distri \\ \\
348452 ARCH=$arch \\ \\
@@ -583,6 +687,7 @@ def openqa_call_end(version):
583687 echo " FLAVOR=${flavor//Tumbleweed-/} \\ \\ "
584688) | LANG=C.UTF-8 sort
585689 echo ""
690+ done
586691 done
587692done
588693'''
0 commit comments