#!/usr/bin/python # This program is free software; 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 Library General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Copyright Red Hat Inc. 2007, 2008 # # Author: Izhar Firdaus # Examples: # # yum oci-install oneclickinstall-XML-metadata-file # yum oci-query details oneclickinstall-XML-metadata-file # yum oci-query repositories oneclickinstall-XML-metadata-file # yum oci-query packages oneclickinstall-XML-metadata-file # from xml.etree.ElementTree import ElementTree,Element import re from yum.plugins import PluginYumExit,TYPE_CORE,TYPE_INTERACTIVE from yum import logginglevels import yum.i18n, tempfile _ = yum.i18n._ ocins='http://opensuse.org/Standards/One_Click_Install' #distro='fedora' #release='9' ## Use this for now distro='openSUSE' release='Factory' requires_api_version = '2.5' plugin_type = (TYPE_INTERACTIVE,) def attrsfilter(elements,keywords,operation='or'): """ Function to filter elements by its attributes @param elements A list of elements to be filtered @param keywords A dictionary containing required tags and its values @param operation A string, either 'or' or 'and', for operation selection @return A list of filtered elements by attributes """ retval = [] if not keywords: return elements if operation == 'or': for element in elements: for key in keywords: if element.attrib.has_key(key): if element.attrib[key] == keywords[key]: retval.append(element) break elif operation == 'and': for element in elements: for key in keywords: if not element.attrib.has_key(key): break if element.attrib[key] != keywords[key]: break retval.append(element) return retval def get_element_tag(element): """ Function to get the element tag without namespace @param element An element which will be extracted the tag name @return A string, containing the tag name without namespace """ tag = re.match('{.*?}(.*)',element.tag) if tag: return tag.group(1) else: return tag def get_child_value(element,childname): """ Function to get the value of an element's child element @param element An element to be queried @param childname A string, containing the tagname of the child @return A string containing child's text value if child is a leaf element, or a ElementTree element if child is a parent to 1 or more elements """ for c in element.getchildren(): if get_element_tag(c) == childname: if c.getchildren(): return c else: return c.text def add_tmp_repo(base,name,repourls): """ Function to add temporary repository @param base The YumBase object for the operation @param name The repository name @param repourls A list of urls for the repository @return None """ tfo = tempfile.NamedTemporaryFile() repoid = name repoconfig = """ [%s] name=%s failovermethod=priority baseurl=%s enabled=1 """ % (name,name,'\n'.join(repourls)) tfo.file.write(repoconfig) tfo.file.close() base.getReposFromConfigFile(tfo.name) class OCIMetapackage: """ Parser class for OCI Metapackage XML. """ def __init__(self,element,distversion): """ @param element ElementTree object which stores the XML file @param distversion A string containing '$distro $release' @return None """ self.element = element self.distversion = distversion def getGroup(self): """ Function to get Group element from OCI Metadata @return 'group' Element of the current distversion """ for group in self.element.getiterator('{%s}%s' % (ocins,'group')): if group.attrib['distversion'] == self.distversion: return group def getRepositories(self,*args,**kwargs): """ Function to get 'repository' elements from the current distversion group @kwargs Keyword arguments for filtering results @return A list containing a filtered list of 'repository' Elements """ repos = self.getGroup().getiterator('{%s}%s' % (ocins,'repository')) return attrsfilter(repos,kwargs) def getProducts(self,*args,**kwargs): """ Function to get 'product' elements from the current distversion group @kwargs Keyword arguments for filtering results @return A list containing a filtered list of 'product' Elements """ products = self.getGroup().getiterator('{%s}%s' % (ocins,'product')) return attrsfilter(products,kwargs) def getRepoURL(self,reponame): """ Function to get 'url' values from current distversion group, and requested reponame @param reponame The repository name @return A list of URLs from the requested repository, sorted higher score first """ repos = self.getRepositories() urls = [] for r in repos: if get_child_value(r,'name') == reponame: for url in r.getiterator('{%s}%s' % (ocins,'url')): priority = 0 if url.attrib.has_key('score'): priority = int(url.attrib['score']) urls.append((url.text.strip(),priority)) def sorturl(x,y): if x[1] <= y[1]: return +1 else: return -1 urls.sort(sorturl) return [ url[0] for url in urls ] class OCIQuery: def __init__(self,distro,release): self.distro = distro self.release = release def getNames(self): return ['oci-query'] def getUsage(self): return "[details|repositories|packages] [OCI file]" def getSummary(self): return "Query data from OneClickInstall metadata" def doCheck(self, base, basecmd, extcmds): pass def getRepos(self): # so we can act as a "conduit" return self.repos def show_pkg(self, msg, pkg, md, disp=None): print msg, pkg, md def show_pkg_exit(self): pass def doCommand(self, base, basecmd, extcmds): action = extcmds[0] param = extcmds[1:] metapackages = [] for mp in param: et = ElementTree(Element('metapackage'),open(mp)) metapackages.append(OCIMetapackage(et,'%s %s' % (self.distro,self.release))) if action == 'details': for oci in metapackages: print '=======================' print 'Metadata' print '=======================' for c in oci.getGroup().getchildren(): tagname = get_element_tag(c) if tagname in ['name','summary','description']: print tagname,':',c.text if action == 'repositories': for oci in metapackages: print '========================' print 'Repositories' print '========================' for r in oci.getRepositories(): print get_child_value(r,'name') print oci.getRepoURL(get_child_value(r,'name')) if action == 'packages': for oci in metapackages: print '========================' print 'Packages' print '========================' for p in oci.getProducts(): print p.getchildren()[0].text print '' return 0, [''] class OCIInstall: def __init__(self,distro,release): self.distro = distro self.release = release def getNames(self): return ['oci-install'] def getUsage(self): return "[OCI file]" def getSummary(self): return "Install packages from OneClickInstall metadata" def doCheck(self, base, basecmd, extcmds): pass def getRepos(self): # so we can act as a "conduit" return self.repos def show_pkg(self, msg, pkg, md, disp=None): print msg, pkg, md def show_pkg_exit(self): pass def doCommand(self, base, basecmd, extcmds): packages = [] repositories = [] for mp in extcmds: et = ElementTree(Element('metapackage'),open(mp)) oci = OCIMetapackage(et,'%s %s' % (self.distro,self.release)) for p in oci.getProducts(): packages.append(get_child_value(p,'name')) for r in oci.getRepositories(): repositories.append((get_child_value(r,'name'),oci.getRepoURL(get_child_value(r,'name')))) print packages print repositories base.verbose_logger.log(logginglevels.INFO_2, _("Setting up Install Process")) try: return base.installPkgs(packages) except yum.Errors.YumBaseError, e: return 1, [str(e)] def config_hook(conduit): conduit.registerCommand(OCIQuery(distro,release)) conduit.registerCommand(OCIInstall(distro,release))