Package config_common :: Module rpc_wrapper
[hide private]
[frames] | no frames]

Source Code for Module config_common.rpc_wrapper

  1  # 
  2  # Copyright (c) 2008--2016 Red Hat, Inc. 
  3  # 
  4  # This software is licensed to you under the GNU General Public License, 
  5  # version 2 (GPLv2). There is NO WARRANTY for this software, express or 
  6  # implied, including the implied warranties of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 
  8  # along with this software; if not, see 
  9  # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 
 10  # 
 11  # Red Hat trademarks are not licensed under GPLv2. No permission is 
 12  # granted to use or replicate Red Hat trademarks that are incorporated 
 13  # in this software or its documentation. 
 14  # 
 15   
 16  import sys 
 17  from rhn import rpclib 
 18  from spacewalk.common.usix import raise_with_tb 
 19   
 20  try: 
 21      from socket import error, sslerror, herror, gaierror, timeout 
 22  except ImportError: 
 23      from socket import error 
 24      sslerror = error 
 25      herror = error 
 26      gaierror = error 
 27      timeout = error 
 28   
 29  try: # python2 
 30      import xmlrpclib 
 31      import urllib 
 32  except ImportError: # python3 
 33      import xmlrpc.client as xmlrpclib 
 34      import urllib.parse as urllib 
 35   
 36   
 37  #This is raised when the failover stuff has gone through every server in the server list 
 38  #and the error is still occurring. 
39 -class NoMoreServers(Exception):
40 pass
41 42 43 #This was supposed to be a wrapper that contained a reference to a rpclib.Server object and delegated calls to it, 44 #but it turns out that I needed to override _request() to make sure that all 45 #communication errors are caught and handled here, so I changed it to be a subclass of rpclib.Server. 46 #The problem that spurred that was when a xmlrpc function was called on an object that was an 47 #attribute of the server class it wasn't passing through the _request that I had written here, 48 #it was going directly to rpclib.Server's _request() and missing all of the failover logic that I had added. 49 #The short version is that I needed to make sure this class was in the inheritance hierarchy.
50 -class Server(rpclib.Server):
51 - def __init__(self, uri, transport=None, encoding=None, verbose=0, 52 proxy=None, username=None, password=None, refreshCallback=None, 53 progressCallback=None, server_list=None, rpc_handler=None):
54 self.list_of_uris = None #Contains all of the possible uris. 55 self.current_index = 0 #index of the uri that we're currently using. 56 57 #If server_list is None, then no failover systems were listed in the up2date config and 58 #we need to use the one that was put together in the rhncfg-* config, which was passed in as 59 #uri. 60 if server_list is None: 61 if type(uri) == type([]): 62 self.list_of_uris = uri 63 else: 64 self.list_of_uris = [uri] 65 else: 66 #If the server_url passed in is the same as the first element of the server_list, then 67 #that means all of the server info came from the up2date config (or is the same as the up2date config) 68 #and we should use the server_list. 69 #If they don't match then we should use the server_url passed in as uri, because it's a specific setting from 70 #the rhncfg-*.conf file. 71 if uri == server_list[0]: 72 self.list_of_uris = server_list 73 else: 74 self.list_of_uris = [uri] 75 76 self.rpc_handler = rpc_handler 77 78 #Grabs the initial uri that we're going to use. 79 init_uri = self._get_uri() 80 81 82 #self.rpc_args = { 83 # 'transport' : transport, 84 # 'encoding' : encoding, 85 # 'verbose' : verbose, 86 # 'proxy' : proxy, 87 # 'username' : username, 88 # 'password' : password, 89 # 'refreshCallback' : refreshCallback, 90 # 'progressCallback' : progressCallback, 91 92 # } 93 #Set up the rpclib.Server stuff with the first uri. 94 rpclib.Server.__init__(self, init_uri, transport=transport, encoding=encoding, verbose=verbose,\ 95 proxy=proxy, username=username, password=password, refreshCallback=refreshCallback,\ 96 progressCallback=progressCallback)
97 98 #Return the uri that we should be using.
99 - def _get_uri(self):
100 return self.list_of_uris[self.current_index]
101 102 #Returns the list of uris that could be used.
103 - def get_uri_list(self):
104 return self.list_of_uris
105 106 #This is called when we failover. It re-inits the server object to use the new uri. Most of this was cribbed from 107 #alikins' wrapper that does a similar thing for up2date.
108 - def init_server(self, myuri):
109 #Borrowed the following from rpcServer.py 110 #rpclib.Server.__init__(self, uri, transport=self.rpc_args['transport'], encoding=self.rpc_args['encoding'], verbose=self.rpc_args['verbose'],\ 111 # proxy=self.rpc_args['proxy'], username=self.rpc_args['username'],\ 112 # password=self.rpc_args['password'], refreshCallback=self.rpc_args['refreshCallback'],\ 113 # progressCallback=self.rpc_args['progressCallback']) 114 self._uri = myuri 115 typ, uri = urllib.splittype(self._uri) 116 typ = typ.lower() 117 if typ not in ("http", "https"): 118 raise InvalidRedirectionError( 119 "Redirected to unsupported protocol %s" % typ) 120 121 self._host, self._handler = urllib.splithost(uri) 122 self._orig_handler = self._handler 123 self._type = typ 124 if not self._handler: 125 self._handler = self.rpc_handler 126 self._allow_redirect = 1 127 del self._transport 128 self._transport = self.default_transport(typ, self._proxy, 129 self._username, self._password) 130 self.set_progress_callback(self._progressCallback) 131 self.set_refresh_callback(self._refreshCallback) 132 self.set_buffer_size(self._bufferSize) 133 self.setlang(self._lang) 134 135 if self._trusted_cert_files != [] and \ 136 hasattr(self._transport, "add_trusted_cert"): 137 for certfile in self._trusted_cert_files: 138 self._transport.add_trusted_cert(certfile)
139 140 #This is the logic for switching to a new server resides.
141 - def _failover(self):
142 #The print statements are from alikins rpcServer.py. 143 msg = "An error occurred talking to %s:\n" % self._get_uri() 144 msg = msg + "%s\n%s\n" % (sys.exc_info()[0], sys.exc_info()[1]) 145 146 #Increments the index to point to the next server in self.list_of_uris 147 self.current_index = self.current_index + 1 148 149 #Make sure we don't try to go past the end of the list. 150 if self.current_index > (len(self.list_of_uris) - 1): 151 raise NoMoreServers() 152 else: 153 failover_uri = self._get_uri() #Grab the uri of the new server to use. 154 msg = msg + "Trying the next serverURL: %s\n" % failover_uri 155 156 print(msg) 157 158 #Set up rpclib.Server to use the new uri. 159 self.init_server(failover_uri)
160 161 #This is where the magic happens. function is a function reference, arglist is the list of arguements 162 #that get passed to the function and kwargs is the list of named arguments that get passed into the function. 163 #I used apply() here so it will work with Python 1.5, which doesn't have the extended call syntax.
164 - def _call_function(self, function, arglist, kwargs={}):
165 succeed = 0 166 while succeed == 0: 167 try: 168 ret = function(*arglist, **kwargs) 169 except rpclib.InvalidRedirectionError: 170 raise 171 except xmlrpclib.Fault: 172 e = sys.exc_info()[1] 173 save_traceback = sys.exc_info()[2] 174 try: 175 self._failover() 176 except NoMoreServers: 177 f = sys.exc_info()[1] 178 raise_with_tb(e, save_traceback) #Don't raise the NoMoreServers error, raise the error that triggered the failover. 179 continue 180 except (error, sslerror, herror, gaierror, timeout): 181 e = sys.exc_info()[1] 182 save_traceback = sys.exc_info()[2] 183 try: 184 self._failover() 185 except NoMoreServers: 186 raise_with_tb(e, save_traceback) 187 continue 188 succeed = 1 #If we get here then the function call eventually succeeded and we don't need to try again. 189 return ret
190
191 - def _request(self, methodname, params):
192 return self._call_function(rpclib.Server._request, (self, methodname, params))
193
194 - def __getattr__(self, name):
195 return rpclib.xmlrpclib._Method(self._request, name)
196