#!/usr/bin/env python2 # -*- encoding=utf-8 -*- import os,sys from subprocess import * import re import shutil import traceback import hashlib import time from xml.dom import minidom from xml.etree import ElementTree as ET import logging import logging.config source_host = '/srv/obs' publishedhook_workspace = '/usr/lib/obs/source_md5' logging.config.fileConfig("/usr/lib/obs/service/logger.conf") logger = logging.getLogger("publishedhook_depends_update") def shell_cmd(s_cmd_line, inmsg=None): p = Popen(s_cmd_line, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True) if inmsg: p.stdin.write(inmsg) out, err = p.communicate() return p.returncode, out, err def parse_args(argv): if len(argv) == 0: logger.error("Usage:EulerOS:Update/standard_aarch64 /srv/obs/repos/EulerOS:/Update/standard_aarch64 aarch64/wget-1.14-15.1.h2.aarch64.rpm") return False for line in argv: logger.info( "%s" % line ) publish_rpmlist = [] archs=[] project_build_path = '/srv/obs/build/%s' % argv[0] (prj,repo)=argv[0].split('/') base_project = '/srv/obs/build/%s_baseline' % prj if not os.path.exists( base_project ): logger.info( "Found none %s_baseline project. skipped ..." % prj ) return True if len(argv) > 2: for line in argv[2:]: (arch_name,pkgname)=line.split('/') if arch_name not in ['src','noarch']: if not os.path.exists( os.path.join(project_build_path, arch_name) ): cmd = "find %s -name %s | sed -n '1p'" % (project_build_path, pkgname) ret, out, err = shell_cmd(cmd) if ret != 0: logger.error( "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err) ) return False else: logger.info("%s" % out) arch_name = out.split('/')[6] if arch_name not in archs: archs.append(arch_name) if not re.match( r'.*src\.rpm', pkgname ): publish_rpmlist.append( pkgname ) if len(archs) == 0: archs = filter( lambda x: os.path.isdir( os.path.join(project_build_path,x) ) , os.listdir(project_build_path) ) for xxarch in archs: rpmlist = [] for rpm in publish_rpmlist: if rpm.split('.')[-2] in [ 'noarch', xxarch ]: rpmlist.append(rpm) project = (prj,repo,xxarch,rpmlist) projects.append(project) return True def scan_update_change(project,pkg_dict,prj_base_dict): publish_info = [] ls = os.linesep xml_head = '' project_build_path = '/srv/obs/build/%s/%s/%s' % (project[0], project[1], project[2]) publish_info.append( xml_head ) publish_info.append('') packages = filter( lambda x: not re.match(r'^:.*',x), os.listdir(project_build_path) ) pkg_rev_path = '%s/projects/%s.pkg' % ( source_host, project[0] ) pkg_src_path = '%s/trees/%s' % (source_host, project[0] ) pkg_hash_path = '%s/trees/%s' % (source_host, project[0] ) for package in packages: cmd = "sed -n '$p' %s/%s.rev" % ( pkg_rev_path, package ) ret, out1, err = shell_cmd(cmd) if ret != 0: logger.error( "run: %s : failed,the out is %s,the err is %s" % (cmd, out1, err) ) return False cmd = "sed -n '1p' %s/%s/%s-MD5SUMS" % ( pkg_src_path, package, out1.split('|')[2] ) ret, out2, err = shell_cmd(cmd) if ret != 0: logger.error( "run: %s : failed,the out is %s,the err is %s" % (cmd, out2, err) ) return False if out2.find('/SERVICE') != -1: srcfile = '%s/%s/%s-MD5SUMS' % (pkg_hash_path, package, out2.split(' ')[0]) if os.path.exists(srcfile): cmd = "cat %s | grep '_service:'" % srcfile else: cmd = "echo 'E1001'" else: cmd = "cat %s/%s/%s-MD5SUMS" % ( pkg_src_path, package, out1.split('|')[2] ) ret, out3, err = shell_cmd(cmd) if ret != 0: logger.error( "run: %s : failed,the out is %s,the err is %s" % (cmd, out3, err) ) return False if re.match(r'E100.*', out3): logger.info( '%s pending %s skip ...' % (package,out3) ) pkg_hash = prj_base_dict[package][0] rpms = prj_base_dict[package][1] else: digest = hashlib.md5() digest.update(out3) rpms = filter( lambda x: re.match(r'.*\.rpm',x), os.listdir( os.path.join(project_build_path, package) ) ) rpms.sort() digest.update( ' '.join(rpms) ) pkg_hash = digest.hexdigest() rpmsStr = ' '.join(rpms) update_info = '' % (package, pkg_hash, rpmsStr) pkg_dict[package] = (pkg_hash,rpms) publish_info.append( update_info ) cmd = "sed -n '$p' %s/_project.rev | awk -F'|' '{print $3}';sed -n '$p' %s/_project.mrev | awk -F'|' '{print $3}'" % ( pkg_rev_path, pkg_rev_path ) ret, out, err = shell_cmd(cmd) if ret != 0: logger.error( "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err) ) return False pdigest = hashlib.md5() pdigest.update(out) pkg_hash = pdigest.hexdigest() update_info = '' % ( pkg_hash ) pkg_dict['_project'] = (pkg_hash,'') publish_info.append( update_info ) publish_info.append('') update_info_dir = '%s/%s/%s/%s' % (publishedhook_workspace, project[0], project[1], project[2]) if not os.path.exists( update_info_dir ): os.makedirs( update_info_dir ) update_info_file = '%s/update.xml' % (update_info_dir) fobj_update_info_file = open(update_info_file, 'w') fobj_update_info_file.writelines( ['%s%s' % ( update_info, ls ) for update_info in publish_info ] ) fobj_update_info_file.close() return True def check_base(project): check_flag = 0 build_path='/srv/obs/build/%s_baseline/%s/%s/:full' %(project[0],project[1],project[2]) repo_ppath='/srv/obs/build/%s/%s/%s/:repo' %(project[0],project[1],project[2]) build_rpms = os.listdir( build_path ) repos_rpms = filter( lambda x: not re.match(r'.*src\.rpm',x), os.listdir( repo_ppath ) ) for rpm in build_rpms: if rpm not in repos_rpms: build_file=os.path.join(build_path,rpm) os.remove(build_file) check_flag = 1 logger.info( "check: should delete %s" % rpm ) for rpm in repos_rpms: if rpm not in build_rpms: repo_file=os.path.join(repo_ppath,rpm) shutil.copy(repo_file,build_path) check_flag = 1 logger.info( "check: shoud add %s" % rpm ) if check_flag == 1: return False else: return True def depends_update(project, prj_update_dict, prj_base_dict): diff_add_rpm = [] diff_delete_rpm = [] for key,value in prj_update_dict.items(): if prj_base_dict.has_key(key): if value[0].decode("utf-8") == prj_base_dict[key][0]: continue else: ##Update add for rpm in value[1]: if rpm not in diff_add_rpm: if not re.match( r'.*src\.rpm', rpm ) and re.match( r'.*\.rpm', rpm ): diff_add_rpm.append(rpm) logger.info( "source change add %s" % rpm ) #Update del for rpm in prj_base_dict[key][1]: if rpm not in diff_delete_rpm: if not re.match( r'.*src\.rpm', rpm ) and re.match( r'.*\.rpm', rpm ): diff_delete_rpm.append(rpm) logger.info( "source change delete %s" % rpm ) #New add else: for rpm in value[1]: if rpm not in diff_add_rpm: if not re.match( r'.*src\.rpm', rpm ) and re.match( r'.*\.rpm', rpm ): diff_add_rpm.append(rpm) logger.info( "new package add %s" % rpm ) for key, value in prj_base_dict.items(): if prj_update_dict.has_key(key): continue #New del else: for rpm in value[1]: if rpm not in diff_delete_rpm: if not re.match( r'.*src\.rpm', rpm ) and re.match( r'.*\.rpm', rpm ): diff_delete_rpm.append(rpm) logger.info( "deleted package delete %s" % rpm ) #global config change if prj_base_dict.has_key('_project') and prj_base_dict['_project'][0] != prj_update_dict['_project'][0]: logger.info( "%s project prjconfig or meta change" % project[0] ) for rpm in project[3]: if rpm not in diff_add_rpm: diff_add_rpm.append(rpm) logger.info( "project config change update %s" % rpm ) baseline_path = '/srv/obs/build/%s_baseline/%s/%s/:full' % ( project[0], project[1], project[2] ) if not os.path.exists( baseline_path ): os.makedirs( baseline_path ) #delete first for rpm in diff_delete_rpm: rpm_path = os.path.join( baseline_path, rpm ) if os.path.exists( rpm_path ) and re.match( r'.*\.rpm', rpm_path ): os.remove( rpm_path ) for rpm in diff_add_rpm: rpm_path = '/srv/obs/repos/%s/%s/%s/%s' % ( project[0].replace(':',':/'), project[1], rpm.split('.')[-2], rpm ) base_rpm = os.path.join(baseline_path,rpm) if os.path.exists( base_rpm ) and re.match( r'.*\.rpm', base_rpm): os.remove( base_rpm ) shutil.copy( rpm_path, baseline_path ) update_baseline_info_dir = '%s/%s_baseline/%s/%s' %( publishedhook_workspace, project[0], project[1], project[2] ) update_baseline_info_file = '%s/%s_baseline/%s/%s/update.xml' %( publishedhook_workspace, project[0], project[1], project[2] ) update_info_file = '%s/%s/%s/%s/update.xml' %( publishedhook_workspace, project[0], project[1], project[2] ) if not os.path.exists( update_baseline_info_dir ): os.makedirs( update_baseline_info_dir ) shutil.copy( update_info_file, update_baseline_info_dir ) return True def rescan_repository(projects): for project in projects: cmd = '/usr/lib/obs/server/bs_admin --rescan-repository %s_baseline %s %s' % ( project[0], project[1], project[2] ) logger.info( "%s" % cmd) ret, out, err = shell_cmd(cmd) if ret != 0: logger.error( "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err) ) return False return True def parse_base_change( project, pkg_dict ): xml_file = '%s/%s_baseline/%s/%s/update.xml' %( publishedhook_workspace, project[0], project[1], project[2] ) if not os.path.exists(xml_file): return True if os.path.isfile(xml_file) == False: logger.error(" %s is not a file" % xml_file ) return False try: xmldoc = minidom.parse(xml_file) except: logger.error("Can't parse Xml File of %s." % (xml_file) ) return False package = xmldoc.getElementsByTagName('package') for i in range(0, len(package)): pkg_dict[package[i].attributes['name'].value] = (package[i].attributes['hash'].value,package[i].attributes['binary'].value.split(' ')) return True def update_baseline(projects): for project in projects: prj_update_dict = {} prj_base_dict = {} logger.info(" %s %s %s" % (project[0], project[1], project[2])) if not parse_base_change(project, prj_base_dict): logger.error("parse_base_change error") return False if not scan_update_change(project, prj_update_dict, prj_base_dict): logger.error("scan_update_change error") return False if not depends_update(project, prj_update_dict, prj_base_dict): logger.error("depends_update error") return False if not check_base(project): logger.error("check_base error") return False return True if __name__ == '__main__': projects = [] try: if not parse_args(sys.argv[1:]): raise Exception("parse_args failed") except Exception as ie: logger.info( "%s" % ie ) ret, out, err = shell_cmd(notice_cmd) if ret != 0: logger.error( "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err) ) sys.exit(1) else: try: if not update_baseline(projects): raise Exception("update_baseline failed") except Exception as ie: traceback.print_exc() logger.info( "%s" % ie ) ret, out, err = shell_cmd(notice_cmd) if ret != 0: logger.error( "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err) ) sys.exit(1) finally: if not rescan_repository(projects): if not rescan_repository(projects): if not rescan_repository(projects): ret, out, err = shell_cmd(notice_cmd) if ret != 0: logger.error( "run: %s : failed,the out is %s,the err is %s" % (cmd, out, err) ) sys.exit(1)