[Yum-devel] RHN Support

Jack Neely jjneely at pams.ncsu.edu
Tue Mar 22 03:06:36 UTC 2005


Folks,

So, apperently, that big, long traceback was urllib2's way of telling me
that it doesn't like non-string headers.

Attached is an actual working rhn.py.  It can query and fetch files from
either Current (which I found some neat protocall brokenness in) and
RHN/RHN Proxies.

Have fun.
Jack
-- 
Jack Neely <slack at quackmaster.net>
Realm Linux Administration and Development
PAMS Computer Operations at NC State University
GPG Fingerprint: 1917 5AC1 E828 9337 7AA4  EA6B 213B 765F 3B6A 5B89
-------------- next part --------------
import Errors
import xmlrpclib
import os
import os.path
import urlgrabber
import string
import xml
import gzip
import zlib
import base64

class RHNRepoError(Errors.RepoError):
    pass


class RHN(object):
    """Handle Connecting to an RHN API"""

    def __init__(self, xmlrpc):
        # xmlrpc = URL to RHN API interface
        self.rhn = xmlrpclib.ServerProxy(xmlrpc)
        self.url = xmlrpc

        # systemid -- Identifies your system to RHN
        self.sysid = None

        # headers needed for the GET requests
        self.headers = None

        # A list of dicts describing each channel we have access to.
        # The most important of which is the 'label' key
        self.channels = {}

        # Urlgrabber object
        self.urlgrabber = None


    def checkRegistration(self, systemid="/etc/sysconfig/rhn/systemid"):
        """Return true if the system is registered.  You may specify path
           to the systemid file."""
        
        if os.access(systemid, os.F_OK) and not os.access(systemid, os.R_OK):
            raise RHNRepoError("Permissions error reading %s" % systemid)
        elif not os.access(systemid, os.F_OK):
            return False

        if self.sysid == None:
            fd = open(systemid)
            self.sysid = fd.read()
            fd.close()

        return True

        
    def login(self):
        "Login to RHN and get channel information"
        
        self.headers = self.doCall(self.rhn.up2date.login, (self.sysid,))
        
        self.channels = self.doCall(self.rhn.up2date.listChannels, 
                            (self.sysid,))


    def doCall(self, method, params):
        "Wrapper for all XMLRPC calls"

        try:
            ret = method(*params, **{})
        except xmlrpclib.ProtocolError, e:
            # XXX: Error checking here
            raise

        return ret


    def __getURLGrabber(self):
        "Setup the urlgrabber object for RHN"

        if self.headers == None:
            raise RHNRepoError("You must call RHN.login() before this functon.")

        if not self.urlgrabber == None:
            return self.urlgrabber

        t = []
        for header in self.headers.keys():
            if header == "X-RHN-Auth-Channels":
                l = []
                for chan in self.headers[header]:
                    l.append(str(chan))
                t.append((header, string.join(l, ",")))
            else:
                t.append((header, str(self.headers[header])))
        
        self.urlgrabber = urlgrabber.grabber.URLGrabber(http_headers=t)
        return self.urlgrabber

    
    def _getEncoding(self, fd):
        "Return the Content-Encoding of the HTTP stream"

        if not isinstance(fd, urlgrabber.grabber.URLGrabberFileObject):
            raise RHNRepoError("Got passed something that was not a " \
                               "URLGrabberFileObject")

        for line in str(fd.info()).split('\n'):
            if line[0:17] in ["Content-Encoding:", "content-encoding:"]:
                return string.strip(line[18:])

        return "" # What's the default encoding?


    def __loads(self, url):
        "Make like xmlrpclib.loads() with a url"

        grab = self.__getURLGrabber()
        
        fd = grab.urlopen(url)
        encoding = self._getEncoding(fd)

        if encoding == "x-zlib":
            # RHN
            bin = base64.decodestring(fd.read())
            params, methodname = xmlrpclib.loads(zlib.decompress(bin))
        elif encoding == "x-gzip":
            # Need a real file object -- and GzipFile bitches about
            # python's tmpfile() objects
            tmp = open("/tmp/rhncrap", "w")
            tmp.write(fd.read())
            tmp.close()
            fd = gzip.GzipFile("/tmp/rhncrap")
            params, methodname = xmlrpclib.loads(fd.read())
            os.unlink("/tmp/rhncrap")
        else:
            # Ummm...try real hard
            params, methodname = xmlrpclib.loads(fd.read())

        return params, methodname
     
     
    def getLastModification(self, channel):
        "Return the last modification time for the given channel label."

        for chan in self.headers['X-RHN-Auth-Channels']:
            if chan[0] == channel:
                return chan[1]

        raise RHNRepoError("Channel %s not found." % channel)


    def listPackages(self, channel):
        "Return the package list for the given channel label"

        m = self.getLastModification(channel)
        url = os.path.join(self.url, "$RHN", channel, "listPackages", m)

        params, methodname = self.__loads(url)

        return params[0]


    def getObsoletes(self, channel):
        "Return the obsoletes list from the given channel label"

        m = self.getLastModification(channel)
        url = os.path.join(self.url, "$RHN", channel, "getObsoletes", m)

        params, methodname = self.__loads(url)

        return params[0]


    def getPackage(self, channel, filename):
        return self.__getFile("getPackage", channel, filename)


    def getPackageHeader(self, channel, filename):
        return self.__getFile("getPackageHeader", channel, filename)


    def getPackageSource(self, channel, filename):
        return self.__getFile("getPackageSource", channel, filename)

   
    def solveDependencies(self, unknowns):
        "Returns a dict of package lists keyed on the list of unknowns given"

        params = (self.sysid, unknowns)
        return self.doCall(self.rhn.up2date.solveDependencies, params)

    
    def __getFile(self, apicall, channel, filename):
        "Return a file something or other..."

        url = os.path.join(self.url, "$RHN", channel, apicall, filename)
        grab = self.__getURLGrabber()
        return grab.urlgrab(url)


    def __fileBase(self, package):
        
        # This is directly from up2date's code
        # If up2date does it this way it can't be wrong, can it?
        
        #  package list format
        # 0        1        2       3     4     5      6
        # name, version, release, epoch, arch, size, channel
                        
        return "%s-%s-%s.%s" % (package[0], package[1], package[2],
                            package[4])

        
    def getHeaderName(self, package):
        return self.__fileBase(package) + ".hdr"


    def getRPMName(self, package):
        return self.__fileBase(package) + ".rpm"


def main():
    r = RHN("https://localhost/XMLRPC")
    if r.checkRegistration():
        print "System is Registered"
    else:
        print "System is NOT registered!!"
        return

    r.login()
    print r.headers
    print r.channels

    packages =  r.listPackages("rawhide")
    #print r.getObsoletes("rhel-i386-ws-4")

    print r.getRPMName(packages[3])
    print packages[3]
    #print r._RHN__getFile("getPackage", "rhel-i386-ws-4", r.getRPMName(packages[4]))

    print r.getPackage("rawhide", "Canna-libs-3.7p3-10.i386.rpm")

    print r.solveDependencies(['/etc/ntp.conf'])


if __name__ == "__main__":
    main()



More information about the Yum-devel mailing list