[Yum] failover in yum

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



--UlVJffcvxoiEqYs2
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Folks,

I want failover.

I make yum smart.

This patch makes baseurl in the config file into a list.  Uses spaces as
delimiters.  Also adds a "failovermethod" to each server section to
select the failover method.  If you leave it out it does round
robin...which is not really round robin right now...but yeah...  Your
config files will work without making any changes...this just adds a few
extras options to them.

Attached are two things....a diff against cvs of the 4_2 branch and the
failover module I added.  The basic idea is that we have a base failover
method class that gets inherited by other classes so folks can define
their own failover methods....sence I've seen lots of ideas suggested.

Mainly I need to touch up the failover classes but it works as a proof
of concept.  Note that clientStuff has its own grab() function now.

go-go-gadget asbestos
-- 
Jack Neely <slack@xxxxxxxxxxxxxxx>
Linux Realm Kit Administration and Development 
PAMS Computer Operations at NC State University
GPG Fingerprint: 1917 5AC1 E828 9337 7AA4  EA6B 213B 765F 3B6A 5B89

--UlVJffcvxoiEqYs2
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="failover.patch"

? archwork.pyc
? callback.pyc
? clientStuff.pyc
? comps.pyc
? config.pyc
? failover.patch
? failover.py
? failover.pyc
? i18n.pyc
? keepalive.pyc
? logger.pyc
? nevral.pyc
? pkgaction.pyc
? rpmUtils.pyc
? urlgrabber.pyc
? yum
? yumcomps.pyc
? yumlock.pyc
? yummain.pyc
Index: clientStuff.py
===================================================================
RCS file: /home//groups/yum/cvs/yum/clientStuff.py,v
retrieving revision 1.47.2.45
diff -u -r1.47.2.45 clientStuff.py
--- clientStuff.py	13 May 2003 05:24:03 -0000	1.47.2.45
+++ clientStuff.py	14 May 2003 02:32:21 -0000
@@ -599,7 +599,7 @@
         if not conf.cache:
             log(3, 'getting groups from server: %s' % serverid)
             try:
-                localgroupfile = retrygrab(remotegroupfile, localgroupfile, copy_local=1)
+                localgroupfile = grab(serverid, remotegroupfile, localgroupfile, copy_local=1)
             except URLGrabError, e:
                 log(3, 'Error getting file %s' % remotegroupfile)
                 log(3, '%s' % e)
@@ -634,7 +634,7 @@
         if not conf.cache:
             log(3, 'Getting header.info from server')
             try:
-                headerinfofn = retrygrab(serverheader, localheaderinfo, copy_local=1)
+                headerinfofn = grab(serverid, serverheader, localheaderinfo, copy_local=1)
             except URLGrabError, e:
                 errorlog(0, 'Error getting file %s' % serverheader)
                 errorlog(0, '%s' % e)
@@ -657,7 +657,8 @@
     for (name, arch) in nulist:
         LocalHeaderFile = HeaderInfo.localHdrPath(name, arch)
         RemoteHeaderFile = HeaderInfo.remoteHdrUrl(name, arch)
-
+        serverid = HeaderInfo.serverid(name, arch)
+        
         # if we have one cached, check it, if it fails, unlink it and continue
         # as if it never existed
         # else move along
@@ -681,8 +682,8 @@
         if not conf.cache:
             log(2, 'getting %s' % LocalHeaderFile)
             try:
-                hdrfn = retrygrab(RemoteHeaderFile, LocalHeaderFile, copy_local=1,
-                                  checkfunc=(rpmUtils.checkheader, (name, arch), {}))
+                hdrfn = grab(serverid, RemoteHeaderFile, LocalHeaderFile, copy_local=1,
+                             checkfunc=(rpmUtils.checkheader, (name, arch), {}))
             except URLGrabError, e:
                 errorlog(0, 'Error getting file %s' % RemoteHeaderFile)
                 errorlog(0, '%s' % e)
@@ -843,7 +844,7 @@
             else:
                 log(2, 'Getting %s' % (os.path.basename(rpmloc)))
                 try:
-                    localrpmpath = retrygrab(tsInfo.remoteRpmUrl(name, arch), rpmloc, copy_local=0) 
+                    localrpmpath = grab(serverid, tsInfo.remoteRpmUrl(name, arch), rpmloc, copy_local=0) 
                 except URLGrabError, e:
                     errorlog(0, 'Error getting file %s' % tsInfo.remoteRpmUrl(name, arch))
                     errorlog(0, '%s' % e)
@@ -922,3 +923,50 @@
     else:
         size = size / 1000000000.0
         return "%.2f GB" % size
+        
+        
+def grab(serverID, url, filename=None, copy_local=0, close_connection=0,
+          progress_obj=None, throttle=None, bandwidth=None,
+          numtries=3, retrycodes=[-1,2,4,5,6,7], checkfunc=None):
+              
+    """Wrap retry grab and add in failover stuff.  This needs access to
+    the conf class as well as the serverID.
+        
+    We do look at retrycodes here to see if we should return or failover.
+    On fail we will raise the last exception that we got."""
+        
+    fc = conf.get_failClass(serverID)
+    base = ''
+    for root in conf.serverurl[serverID]:
+        if string.find(url, root) == 0:
+            # We found the current base this url is made of
+            base = root
+            break
+    if base == '':
+        # We didn't find the base...something is wrong
+        raise Exception, "%s isn't made from a base URL I know about" % url
+    url = url[len(base):]
+    log(3, "failover: baseURL = " + base)
+    log(3, "failover: path = " + url)
+    
+    # don't trust the base that the user supplied
+    base = fc.get_serverurl()
+    while base != None:
+        # Loop over baseURLs until one works or all are dead
+        try:
+            # XXX need some urlparse magic to keep this from breaking
+            return retrygrab(base+url, filename, copy_local,
+                             close_connection, progress_obj, throttle,
+                             bandwidth, numtries, retrycodes, checkfunc)
+            # What?  We were successful?
+        except URLGrabError, e:
+            if e.errno in retrycodes:
+                log(2, "retrygrab() failed -- executing failover method")
+                fc.server_failed()
+                base = fc.get_serverurl()
+                if base == None:
+                    log(1, "failover: out of servers to try")
+                    raise
+            else:
+                raise
+        
Index: config.py
===================================================================
RCS file: /home//groups/yum/cvs/yum/config.py,v
retrieving revision 1.14.2.13
diff -u -r1.14.2.13 config.py
--- config.py	11 May 2003 06:04:03 -0000	1.14.2.13
+++ config.py	14 May 2003 02:32:21 -0000
@@ -23,6 +23,7 @@
 import urllib
 import rpm
 import re
+import failover
 import archwork
 import rpmUtils
 
@@ -53,8 +54,9 @@
         self.serverpkgdir = {}
         self.serverhdrdir = {}
         self.servercache = {}
-        self.servergpgcheck= {}
-        self.excludes= []
+        self.servergpgcheck={}
+        self.failoverclass = {}
+        self.excludes=[]
         
         #defaults
         self.cachedir = '/var/cache/yum'
@@ -117,23 +119,31 @@
             for section in self.cfg.sections(): # loop through the list of sections
                 if section != 'main': # must be a serverid
                     name = self._getoption(section,'name')
-                    url = self._getoption(section,'baseurl')
-                    if name != None and url != None:
+                    urls = self._doreplace(self._getoption(section,'baseurl'))
+                    url = string.split(urls, ' ')
+                    if name != None and url != []:
                         self.servers.append(section)
                         name = self._doreplace(name)
-                        url = self._doreplace(url)
                         self.servername[section] = name
                         self.serverurl[section] = url
+                        failmeth = self._getoption(section,'failovermethod')
+                        if failmeth == 'roundrobin':
+                            failclass = failover.roundRobin(self, section)
+                        else:
+                            failclass = failover.roundRobin(self, section)
+                        self.failoverclass[section] = failclass
                         if self._getoption(section,'gpgcheck') != None:
                             self.servergpgcheck[section]=self.cfg.getboolean(section,'gpgcheck')
                         else:
                             self.servergpgcheck[section]=0
-                        
-                        (s,b,p,q,f,o) = urlparse.urlparse(self.serverurl[section])
-                        # currently only allowing http, ftp and file url types
-                        if s not in ['http', 'ftp', 'file']:
-                            print _('Not using ftp, http or file for servers, Aborting - %s') % (self.serverurl[section])
-                            sys.exit(1)
+
+                        for url in self.serverurl[section]:
+                            (s,b,p,q,f,o) = urlparse.urlparse(url)
+                            # currently only allowing http and ftp servers 
+                            if s not in ['http', 'ftp', 'file', 'https']:
+                                print _('using ftp, http[s], or file for servers, Aborting - %s') % (url)
+                                sys.exit(1)
+
                         cache = os.path.join(self.cachedir,section)
                         pkgdir = os.path.join(cache, 'packages')
                         hdrdir = os.path.join(cache, 'headers')
@@ -155,13 +165,19 @@
             return None
             
     def remoteGroups(self, serverid):
-        return os.path.join(self.serverurl[serverid], 'yumgroups.xml')
+        return os.path.join(self.baseURL(serverid), 'yumgroups.xml')
     
     def localGroups(self, serverid):
         return os.path.join(self.servercache[serverid], 'yumgroups.xml')
         
     def baseURL(self, serverid):
-        return self.serverurl[serverid]
+        return self.get_failClass(serverid).get_serverurl()
+        
+    def server_failed(self, serverid):
+        self.failoverclass[serverid].server_failed()
+    
+    def get_failClass(self, serverid):
+        return self.failoverclass[serverid]
         
     def remoteHeader(self, serverid):
         return os.path.join(self.baseURL(serverid), 'headers/header.info')
Index: nevral.py
===================================================================
RCS file: /home//groups/yum/cvs/yum/nevral.py,v
retrieving revision 1.28.2.24
diff -u -r1.28.2.24 nevral.py
--- nevral.py	6 May 2003 03:00:05 -0000	1.28.2.24
+++ nevral.py	14 May 2003 02:32:21 -0000
@@ -144,7 +144,7 @@
         if l == 'in_rpm_db':
             return l
         hdrfn = self.hdrfn(name,arch)
-        base = conf.serverurl[i]
+        base = conf.baseURL(i)
         return base + '/headers/' + hdrfn
     
     def localHdrPath(self, name, arch=None):
@@ -175,7 +175,7 @@
             return None
         if l == 'in_rpm_db':
             return l
-        base = conf.serverurl[i]
+        base = conf.baseURL(i)
         return base +'/'+ l
     
     def localRpmPath(self, name, arch=None):

--UlVJffcvxoiEqYs2
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="failover.py"

#!/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 2003 Jack Neely, NC State University

# Here we define a base class for failover methods.  The idea here is that each
# failover method uses a class derived from the base class so yum only has to
# worry about calling get_serverurl() and server_failed() and these classes will 
# figure out which URL to cough up based on the failover method.

class baseFailOverMethod:

    def __init__(self, conf, serverID):
        # the yum conf structure
        self.conf = conf
        self.serverID = serverID
        self.failures = 0
    
    def get_serverurl(self):
        "Returns a serverurl based on this failover method or None if complete failure."
        return None
        
    def server_failed(self):
        "Tells the failover method that the current server is failed."
        self.failures = self.failures + 1
        
    def reset(self):
        "Reset the failures counter."
        self.failures = 0
        
            

class roundRobin(baseFailOverMethod):

    """Chooses server based on a round robin.  The first in the list is always
    first."""
    
    def get_serverurl(self):
        "Returns a serverurl based on this failover method or None if complete failure."
        
        if self.failures >= len(self.conf.serverurl[self.serverID]):
            return None
        
        return self.conf.serverurl[self.serverID][self.failures]
        
        
        

--UlVJffcvxoiEqYs2--


[Index of Archives]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]

  Powered by Linux