#!/usr/bin/python # vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:tw=0: ############################################################################# # # Dual Licenced under GNU GPL and OSL # # Copyright (C) 2005 - 2006 Dell Inc. # by Michael Brown # Jay Perusse # Licensed under the Open Software License version 2.1 or later # # Alternatively, you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; either version 2 of the License, # or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. # # See the files COPYING-GPL and COPYING-OSL for full license # terms # --------------------------------------------------------------- # # version history # 1.2.1 03-June-2007 Michael Brown # a) drop osname symlinks as mirrors.pl now takes care of this # b) add repository signatures # 1.2.0 11-April-2007 Michael Brown # a) add --yum, --omsa cmdline params # b) add system id symlinks # c) add osnamemap symlinks # 1.1.1 31-March-2006 Pritesh Prabhu # a) code review changes # b) usability-related changes # 1.1.0 28-March-2006 Michael Brown # a) code review cleanups # b) add hardlink tree option # 1.0.3 04-January-2006 Jay Perusse # a) Adding version history and inline comments # b) Changed syntax output to show windows syntax for windows and # linux syntax for syntax. # c) Change -o & --output_dir to -d & --dest_dir & all varibles used # d) Changed -s to -o for OS input # e) If --input_dir is provided default to --info and if --dest_dir is # provided provide feedback the user to use --extract to extract # f) Changed OEMPnPDriversPath=; to OEMPnPDriversPath=drivers; and # changed forward slash (/) to back slash (\) when ran on a linux # system # g) When ran on a windows system remove read only file attributes # from the files copied from the DSA source to the hard disk so # the script can be re-run # 1.0.2 04-January-2006 Michael Brown Added pnpdriverpath.txt # creation when a windows OS is selected. # 1.0.1 14-December-2005 Jay Perusse Changed user output and # adding a back slash (\) when --input_dir or --dest_dir is give as a # drive letter only (d: changed to d:\) # 1.0.0 13-December-2005 Michael Brown Initial release # ############################################################################# """ make_driver_dir: usage: -h | --help prints this message -d | --dest_dir destination directory to extract drivers to * -i | --input_dir source Server Assistant CD -p | --platform limits the drivers extracted to the specified platform -o | --os extracts drivers for specified operating system only -v | --verbose enable verbose output -q | --quiet suppress verbose output -y | --yum create yum repo from resulting dest (for extract only) Requires /usr/bin/createrepo for new format yum repo and /usr/bin/yum-arch for old format yum repo. Will run both binaries if found. -u | --local-user sign repositories (only useful with --yum) --omsa extract OMSA and put into repositories as well --extract extract drivers --info provides information about platform/operating system support (default) --hardlink hardlink all destination files (saves space, only works if operating system supports hardlinks) Action to take is a required parameter. Specify one of: [--extract | --info] -- Required parameters are denoted by an asterisk (*) Example Syntax: """ #-- future stuff needs to be first from __future__ import generators windowsSyntax = "C:\> make_driver_dir -i d:\ -d c:\drv -p pe1855 -o w2003 --extract\n" linuxSyntax = "$ ./make_driver_dir.py -i /media/cdrom -d ~/drivers/ -p pe1855 -o rh40 --extract\n" PROGRAM_NAME="make_driver_dir" VERSION="1.2.1" PROGRAM_BANNER="""%s %s Copyright 2005-2006 Dell Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. """ % (PROGRAM_NAME, VERSION) # map OMSA RPM directory names to DSA OS dirs to copy to OMSA_osList={ "RHEL3": ("rh30", "rh30_64",), "RHEL4": ("rh40", "rh40_64",), "RHEL5": ("rh50", "rh50_64",), "SLES9": ("suse9_64",), "SLES10": ("suse10_64",), } #-- import arranged alphabetically import getopt import glob import os import re import sys import ConfigParser import shutil verbose = 1 class unsuccessfulExit(Exception): pass def main(): """ NAME main() DESCRIPTION Displays DSA driver information and can copy the drivers to a user specified directory CHANGELOG 0.2 04-January-2006 a) Change -o & --output_dir to -d & --dest_dir & all varibles used b) Changed -s to -o for OS input c) If --input_dir is provided default to --info and if --dest_dir is provided provide feedback the user to use --extract to extract d) When ran on a windows system remove read only file attributes from the files copied from the DSA source to the hard disk so the script can be re-run 0.1 03-January-2006 Initial release AUTHOR Michael Brown & Jay Perusse """ inputDir=None destDir=None ini = None selectPlatform = [] selectOs = [] global verbose action = [] doYum = 0 doOmsa=0 signingKeyName=None try: opts, args = getopt.getopt(sys.argv[1:], "hi:d:vqp:o:yu:", [ "help", "input_dir=", "dest_dir=", "platform=", "os=", "verbose", "quiet", "info", "extract", "version", "hardlink", "yum", "omsa", "local-user=" ]) for option, argument in opts: if option in ("-h", "--help"): printHelp() sys.exit(0) if option in ("-d", "--dest_dir"): if re.search(':$', argument): argument = argument.replace(":", ":\\") destDir = os.path.realpath(argument) if option in ("-i", "--input_dir"): if re.search(':$', argument): argument = argument.replace(":", ":\\") inputDir = os.path.realpath(argument) if option in ("-p", "--platform"): selectPlatform.append(argument) if option in ("-o", "--os"): selectOs.append(argument) if option in ("-v", "--verbose"): verbose = 2 action.append("verbose"); if option in ("-q", "--quiet"): verbose = 0 action.append("quiet"); if option in ("--info",): action.append("info") if option in ("--extract",): action.append("extract") if option in ("--hardlink",): action.append("hardlink") if option in ("--omsa",): doOmsa=1 if option in ("-y", "--yum"): doYum=1 if option in ("-u", "--local-user"): signingKeyName=argument if option in ("--version",): print PROGRAM_BANNER sys.exit(0) if args: raise getopt.GetoptError("Extra command-line parameters were specified that were not understood: %s" % args[0], ""); if inputDir is None: raise getopt.GetoptError("Missing required parameter: input directory", "") if "extract" in action and "info" in action: raise getopt.GetoptError("--info and --extract cannot be specified together.", "") if "verbose" in action and "quiet" in action: raise getopt.GetoptError("--verbose and --quiet cannot be specified together.", "") if not os.path.exists(os.path.join(inputDir, "dsa", "oslist-master.ini")): raise getopt.GetoptError("Input directory does not exist, or is not a Server Assistant CD.", "") if not os.path.exists(os.path.join(inputDir, "server_assistant", "drivers")): raise getopt.GetoptError("Input directory appears to be a Server Assistant CD that is an earlier unsupported version.", "") if "extract" in action and destDir is None: raise getopt.GetoptError("--extract specified, but no destination directory given. ", "") if doYum and "extract" not in action: raise getopt.GetoptError("--yum specified, but --extract not specified. ", "") infoArray = [] if not "info" in action and not "extract" in action: infoArray.append("No action specified. --info assumed"); action.append("info"); if "info" in action and destDir: infoArray.append("--dest_dir ignored for --info action"); if "info" in action and "hardlink" in action: infoArray.append("--hardlink ignored for --info action"); if infoArray: print "\n** INFO **" for i in infoArray: print i print oslistIni = ConfigParser.ConfigParser() oslistIni.read( glob.glob(os.path.join(inputDir, "dsa", "oslist-*.ini"))) printOsDescriptions(oslistIni, selectPlatform, selectOs) printPlatformSupportMatrix(oslistIni, selectPlatform, selectOs) if verbose > 0: print "\nDrivers:" if verbose>1: print "="*80 try: for i in glob.glob(os.path.join(inputDir, "server_assistant", "drivers", "r*")): getDriversFromDir(oslistIni, action, i, destDir, selectPlatform, selectOs) if doOmsa: print "\n" if verbose>1: print "="*80 omsaExtract(oslistIni, action, inputDir, destDir) if destDir and doYum: doYumRepo(destDir, signingKeyName) except (OSError), e: if e.errno == 13: # permission denied print "\n** ERROR **\nYou do not have permissions to create the destination directory: %s" % destDir else: print "\n** ERROR **\nThere was an error trying to open or create the output directory:\n\t%s" % e raise unsuccessfulExit() except unsuccessfulExit, e: sys.exit(4) except (getopt.GetoptError), e: print "\n** ERROR **\n%s" % e print "Try `--help' for more information" # printHelp() sys.exit(2) except (KeyboardInterrupt): sys.exit(3) except (Exception), e: print "There was an unhandled error. The message was: %s" % e sys.exit(5) return 0 #shell logic def printHelp(): """ NAME printHelp() DESCRIPTION Print windows or linux syntax for help CHANGELOG 0.1 03-January-2006 Initial release AUTHOR Michael Brown & Jay Perusse """ print __doc__ if os.name == 'nt': print " %s" % windowsSyntax else: print " %s" % linuxSyntax def eatException(excSpec, func, *args, **kargs): try: func(*args, **kargs) except excSpec: pass def omsaExtract(oslistIni, action, inputDir, destDir): if "extract" not in action: return omsaSysidIni = ConfigParser.ConfigParser() omsaSysidIni.read(os.path.join(inputDir, "srvadmin", "linux", "supportscripts", "prereqcheck", "syslist.txt")) needCr=0 for omsa, dsaDirs in OMSA_osList.items(): omsaRpms = os.path.join(inputDir, "srvadmin", "linux", "RPMS", omsa) if not os.path.exists(omsaRpms): continue omsaMeta = os.path.join(inputDir, "srvadmin", "linux", "RPMS", "supportRPMS", "metaRPMS") print "Copying OMSA for OS: %s" % omsa firstDest = None for dsaDir in dsaDirs: print " into repo dir: %s" % dsaDir if verbose>1: print " systems:", needComma=0 for system in omsaSysidIni.options("SystemsSupported"): system = "0x%s" % system system = int(system,0) if verbose>1: if needComma: sys.stdout.write(",") needComma=1 if oslistIni.has_option("id_to_name", "0x%04x" % system): sys.stdout.write( " %s" % oslistIni.get("id_to_name", "0x%04x" % system)) else: sys.stdout.write(" 0x%04x" % system) sys.stdout.flush() dest = os.path.join(destDir, "system.ven_0x1028.dev_0x%04x" % system, dsaDir, "srvadmin") # copytree below has to have all dirs except last or it dies eatException(OSError, os.makedirs, dest) eatException(OSError, os.makedirs, dest + "-meta") shutil.rmtree(dest) shutil.rmtree(dest + "-meta") if firstDest: hardLinkTree(firstDest, dest) hardLinkTree(firstDest+"-meta", dest+"-meta") else: firstDest = dest shutil.copytree(omsaRpms, firstDest) shutil.copytree(omsaMeta, firstDest + "-meta") if verbose>1: print "\n" if verbose>1: print "\n" + "="*80 def doYumRepo(destDir, keyname = None): doCreaterepo = 0 doYumArch = 0 if os.path.exists("/usr/bin/createrepo"): doCreaterepo = 1 if os.path.exists("/usr/bin/yum-arch"): doYumArch = 1 if not (doYumArch or doCreaterepo): raise getopt.GetoptError("The --yum option was specified, but I could not find yum-arch or createrepo.","") print platforms = glob.glob(os.path.join(destDir, "*")) platforms.sort() for platformDir in platforms: if os.path.islink(platformDir): continue if not os.path.isdir(platformDir): continue repos = glob.glob(os.path.join(platformDir, "*")) repos.sort() for repo in repos: if os.path.islink(repo): continue if not os.path.isdir(repo): continue print "Yum repo for %s: " % repo, sys.stdout.flush() if doCreaterepo: print "createrepo ", sys.stdout.flush() status = os.system("createrepo %s >/dev/null 2>&1" % repo) if os.WTERMSIG(status) == 2: raise KeyboardInterrupt() if keyname: print "sign ", sys.stdout.flush try: os.unlink( "%s/repodata/repomd.xml.asc" % repo ) except OSError, e: pass try: os.unlink( "%s/repodata/repomd.xml.key" % repo ) except OSError, e: pass os.system("gpg --batch --no-tty -u %s -ab %s/repodata/repomd.xml" % (keyname, repo)) os.system("gpg -o %s/repodata/repomd.xml.key --batch --no-tty -a --export %s" % (repo, keyname)) if doYumArch: print "yum-arch", sys.stdout.flush() status = os.system("yum-arch %s >/dev/null 2>&1" % repo) if os.WTERMSIG(status) == 2: raise KeyboardInterrupt() print def printOsDescriptions(ini, selectPlatform, selectOs): """ NAME printOsDescriptions() DESCRIPTION Print list of OS tokens and the display name for each. Only print for OSes on selectPlatform or in selectOs CHANGELOG 1.1 28-March-2006 Initial release AUTHOR Michael Brown """ global verbose if verbose > 0: print "\nOS Descriptions:\n" osList = {} platforms = ini.options( "platforms" ) for p in platforms: if selectPlatform and p not in selectPlatform: continue if p == "unknown": continue hasOs = 0 for o in eval(ini.get("platforms", p)): if selectOs and o not in selectOs: continue osList[o] = 1 for osName in osList.keys(): print "%011s: %s" % (osName, ini.get(osName, "display_name")) def printPlatformSupportMatrix(ini, selectPlatform, selectOs): """ NAME printPlatformSupportMatrix(str, array, array) DESCRIPTION Displays platform and OS supported ARGUMENTS str = DSA source array = Selected platform(s) array = Selected OS(s) CHANGELOG 0.1 03-January-2006 Initial release AUTHOR Michael Brown """ global verbose if verbose > 0: print "\nPlatform support matrix:\n" platforms = ini.options( "platforms" ) for p in platforms: if selectPlatform and p not in selectPlatform: continue if p == "unknown": continue outLine = "%s: " % p hasOs = 0 for o in eval(ini.get("platforms", p)): if selectOs and o not in selectOs: continue outLine = outLine + " %s" % o hasOs = 1 if (hasOs and verbose==1) or verbose==2: print outLine fixDriverType=re.compile(r"[^a-zA-Z-_]") def getDriversFromDir(oslistIni, action, inputDir, destDir, selectPlatform, selectOs): """ NAME getDriversFromDir(str, str, str, array, array) DESCRIPTION Prints information about and copies driver from the DSA source to the destination dir ARGUMENTS array = Select action like --extract or --info str = DSA source str = DSA source array = Selected platform(s) array = Selected OS(s) CHANGELOG 0.1 03-January-2006 Initial release 0.2 04-April-2006 Updated Argument to make first argument an array AUTHOR Michael Brown """ if not os.path.exists( os.path.join( inputDir, "readme.txt" )) and verbose > 1: print "\n** INFO **\n No config file found, skipping %s" % inputDir return 0 configFile = ConfigParser.ConfigParser() configFile.optionxform = str matrix = {} configFile.read(os.path.join( inputDir, "readme.txt" )) if configFile.has_option("driver_matrix", "supported"): matrix = eval(configFile.get( "driver_matrix", "supported" )) driverType = "unknown" if configFile.has_option("release_details", "driver_type"): driverType = eval(configFile.get( "release_details", "driver_type" )) driverType = fixDriverType.sub("_", driverType).lower() driverName = "unknown" if configFile.has_option("release_details", "driver_name"): driverName = eval(configFile.get( "release_details", "driver_name" )) if verbose > 0: print " %s = %s (%s)" % (os.path.basename(inputDir), driverType, driverName) haveCopiedDriver = None for opSys in matrix.keys(): if selectOs and opSys not in selectOs: continue if verbose > 1: needComma=0 sys.stdout.write(" %s:" % opSys) for platform in matrix[opSys]: if selectPlatform and platform not in selectPlatform: continue if verbose > 1: if needComma: sys.stdout.write(",") needComma=1 sys.stdout.write(" %s" % platform) sys.stdout.flush() # don't go past this point if we are not extracting if "extract" not in action: continue platformDir = os.path.join( destDir, platform, opSys ) relDir = os.path.join(platformDir, driverType, os.path.basename(inputDir)) if not os.path.exists( relDir ): os.makedirs(relDir) if os.path.exists( relDir ): shutil.rmtree(relDir) if not haveCopiedDriver or "hardlink" not in action: shutil.copytree(inputDir, relDir) haveCopiedDriver = relDir else: hardLinkTree(haveCopiedDriver, relDir) if oslistIni.has_section("name_to_id"): if oslistIni.has_option("name_to_id", platform.lower()): sysid=oslistIni.get("name_to_id", platform.lower()) venid="0x1028" try: os.unlink(os.path.join(destDir, "system.ven_%s.dev_%s"%(venid,sysid))) except: pass try: # not all platforms support symlinks os.symlink(platform, os.path.join(destDir, "system.ven_%s.dev_%s"%(venid,sysid))) except: pass #-- Change the read only attributes on the files for window systems so script can be re-run if os.name == 'nt': for (d, dirs, files) in walkPath(relDir): for file in files: os.chmod(os.path.join(d, file), 0755) if opSys in ("w2000", "w2000sbs", "w2003", "w2003_64", "w2003sbs"): genPnpDriversPath( platformDir ) if verbose > 1: print "\n" if verbose > 1: print "\n" + "="*80 def genPnpDriversPath(topdir): """ NAME genPnpDriversPath(str) DESCRIPTION Generates a pnpdriverpath.txt when a windows OS is selected ARGUMENTS str = Top level directory CHANGELOG 0.2 03-January-2006 a) Changed OEMPnPDriversPath=; to OEMPnPDriversPath=drivers; and changed forward slash (/) to back slash (\) when ran on a linux system 0.1 03-January-2006 Initial release AUTHOR Michael Brown & Jay Perusse """ outFile = os.path.join(topdir, "pnpdriverpath.txt") driverpath="drivers" for (d, dirs, files) in walkPath(topdir): for lfd in dirs: driverpath="%s;%s" % (driverpath, os.path.join(d,lfd)) #-- Replacing forward slash (/) with a back slash (\) - File is for Windows installation driverpath=driverpath.replace("/", "\\") fh = open(outFile, "w+") fh.write("oempnpdriverspath=%s" % driverpath) fh.close() def hardLinkTree(source, dest): """ NAME hardLinkTree(str) DESCRIPTION Generates dest path as a tree of hardlinks to source path ARGUMENTS source = string. directory path. dest = string. directory path. CHANGELOG 1.0 28-March-2006 Initial release AUTHOR Michael Brown """ if not os.path.exists(dest): os.mkdir(dest) for d, dirs, files in walkPath(source): destPath = d.replace(source,dest) for newDir in dirs: os.mkdir( os.path.join( destPath, newDir )) for newFile in files: os.link( os.path.join(d,newFile), os.path.join(destPath,newFile) ) def walkPath(topdir, direction=0): """ NAME walkPath(str, int=0) DESCRIPTION Generator function -- emulates the os.walk() generator in python 2.3 (mostly) ARGUEMENTS str = Top level directory int = default to 0 (forward) RETURN (path, dirs, files) foreach dir CHANGELOG 0.1 03-January-2006 Initial release AUTHOR Michael Brown """ rawFiles = os.listdir(topdir) files=[f for f in rawFiles if os.path.isfile(os.path.join(topdir,f))] dirs =[f for f in rawFiles if os.path.isdir (os.path.join(topdir,f))] if direction == 0: yield (topdir, dirs, files) for d in dirs: if not os.path.islink(os.path.join(topdir,d)): for (newtopdir, newdirs, newfiles) in walkPath(os.path.join(topdir,d)): yield (newtopdir, newdirs, newfiles) if direction == 1: yield (topdir, dirs, files) if __name__ == "__main__": sys.exit( main() )