Module rhn_check
[hide private]
[frames] | no frames]

Source Code for Module rhn_check

  1  #!/usr/bin/python2 
  2  # 
  3  # Python client for checking periodically for posted actions 
  4  # on the Spacewalk servers. 
  5  # 
  6  # Copyright (c) 2000--2018 Red Hat, Inc. 
  7  # 
  8  # This software is licensed to you under the GNU General Public License, 
  9  # version 2 (GPLv2). There is NO WARRANTY for this software, express or 
 10  # implied, including the implied warranties of MERCHANTABILITY or FITNESS 
 11  # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 
 12  # along with this software; if not, see 
 13  # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 
 14  # 
 15  # Red Hat trademarks are not licensed under GPLv2. No permission is 
 16  # granted to use or replicate Red Hat trademarks that are incorporated 
 17  # in this software or its documentation. 
 18  # 
 19  # In addition, as a special exception, the copyright holders give 
 20  # permission to link the code of portions of this program with the 
 21  # OpenSSL library under certain conditions as described in each 
 22  # individual source file, and distribute linked combinations 
 23  # including the two. 
 24  # You must obey the GNU General Public License in all respects 
 25  # for all of the code used other than OpenSSL.  If you modify 
 26  # file(s) with this exception, you may extend this exception to your 
 27  # version of the file(s), but you are not obligated to do so.  If you 
 28  # do not wish to do so, delete this exception statement from your 
 29  # version.  If you delete this exception statement from all source 
 30  # files in the program, then also delete it here. 
 31   
 32  import base64 
 33  import os 
 34  import sys 
 35  import socket 
 36   
 37  import gettext 
 38  t = gettext.translation('rhn-client-tools', fallback=True) 
 39  # Python 3 translations don't have a ugettext method 
 40  if not hasattr(t, 'ugettext'): 
 41      t.ugettext = t.gettext 
 42  _ = t.ugettext 
 43   
 44  import OpenSSL 
 45   
 46  # disable sgmlop module 
 47  # it breaks rhn_check when loaded during xmlrpclib import 
 48  sys.modules['sgmlop'] = None 
 49   
 50  from up2date_client import getMethod 
 51  from up2date_client import up2dateErrors 
 52  from up2date_client import up2dateAuth 
 53  from up2date_client import up2dateLog 
 54  from up2date_client import rpcServer 
 55  from up2date_client import config 
 56  from up2date_client import clientCaps 
 57  from up2date_client import capabilities 
 58  from up2date_client import rhncli, rhnserver 
 59   
 60  from rhn import SSL 
 61  from rhn import rhnLockfile 
 62  from rhn.i18n import bstr, sstr 
 63  from rhn.tb import raise_with_tb 
 64   
 65  try: # python2 
 66      import xmlrpclib 
 67  except ImportError: # python3 
 68      import xmlrpc.client as xmlrpclib 
 69      long = int 
 70   
 71  if 'sgmlop' in sys.modules: 
 72      del sys.modules['sgmlop'] 
 73   
 74  cfg = config.initUp2dateConfig() 
 75  log = up2dateLog.initLog() 
 76   
 77  # action version we understand 
 78  ACTION_VERSION = 2 
 79   
 80  # lock file to check if we're disabled at the server's request 
 81  DISABLE_FILE = "/etc/sysconfig/rhn/disable" 
 82   
 83  # Actions that will run each time we execute. 
 84  LOCAL_ACTIONS = [("packages.checkNeedUpdate", ("rhnsd=1",))] 
85 86 87 -class CheckCli(rhncli.RhnCli):
88
89 - def __init__(self):
90 super(CheckCli, self).__init__() 91 92 self.rhns_ca_cert = cfg['sslCACert'] 93 self.server = None
94
95 - def main(self):
96 """ Process all the actions we have in the queue. """ 97 CheckCli.__check_instance_lock() 98 CheckCli.__check_rhn_disabled() 99 CheckCli.__check_has_system_id() 100 101 self.server = CheckCli.__get_server() 102 103 CheckCli.__update_system_id() 104 105 self.__run_remote_actions() 106 CheckCli.__run_local_actions() 107 108 s = rhnserver.RhnServer() 109 if s.capabilities.hasCapability('staging_content', 1) and cfg['stagingContent'] != 0: 110 self.__check_future_actions() 111 112 sys.exit(0)
113
114 - def __get_action(self, status_report):
115 try: 116 action = self.server.queue.get(up2dateAuth.getSystemId(), 117 ACTION_VERSION, status_report) 118 119 return action 120 except xmlrpclib.Fault: 121 f = sys.exc_info()[1] 122 if f.faultCode == -31: 123 raise_with_tb(up2dateErrors.InsuffMgmntEntsError(f.faultString)) 124 else: 125 print("Could not retrieve action item from server %s" % self.server) 126 print("Error code: %d%s" % (f.faultCode, f.faultString)) 127 sys.exit(-1) 128 # XXX: what if no SSL in socket? 129 except SSL.socket_error: 130 print("ERROR: SSL handshake to %s failed" % self.server) 131 print(""" 132 This could signal that you are *NOT* talking to a server 133 whose certificate was signed by a Certificate Authority 134 listed in the %s file or that the 135 RHNS-CA-CERT file is invalid.""" % self.rhns_ca_cert) 136 sys.exit(-1) 137 except socket.error: 138 print("Could not retrieve action from %s.\n"\ 139 "Possible networking problem?" % str(self.server)) 140 sys.exit(-1) 141 except up2dateErrors.ServerCapabilityError: 142 print(sys.exc_info()[1]) 143 sys.exit(1) 144 except OpenSSL.SSL.Error: 145 print("ERROR: SSL errors detected") 146 print("%s" % sys.exc_info()[1]) 147 sys.exit(-1)
148
149 - def __query_future_actions(self, time_window):
150 try: 151 actions = self.server.queue.get_future_actions(up2dateAuth.getSystemId(), 152 time_window) 153 return actions 154 except xmlrpclib.Fault: 155 f = sys.exc_info()[1] 156 if f.faultCode == -31: 157 raise_with_tb(up2dateErrors.InsuffMgmntEntsError(f.faultString)) 158 else: 159 print("Could not retrieve action item from server %s" % self.server) 160 print("Error code: %d%s" % (f.faultCode, f.faultString)) 161 sys.exit(-1) 162 # XXX: what if no SSL in socket? 163 except SSL.socket_error: 164 print("ERROR: SSL handshake to %s failed" % self.server) 165 print(""" 166 This could signal that you are *NOT* talking to a server 167 whose certificate was signed by a Certificate Authority 168 listed in the %s file or that the 169 RHNS-CA-CERT file is invalid.""" % self.rhns_ca_cert) 170 sys.exit(-1) 171 except socket.error: 172 print("Could not retrieve action from %s.\n"\ 173 "Possible networking problem?" % str(self.server)) 174 sys.exit(-1) 175 except up2dateErrors.ServerCapabilityError: 176 print(sys.exc_info()[1]) 177 sys.exit(1) 178 except SSL.Error: 179 print("ERROR: SSL errors detected") 180 print("%s" % sys.exc_info()[1]) 181 sys.exit(-1)
182
183 - def __fetch_future_action(self, action):
184 """ Fetch one specific action from rhnParent """ 185 # TODO 186 pass
187
188 - def __check_future_actions(self):
189 """ Retrieve scheduled actions and cache them if possible """ 190 time_window = cfg['stagingContentWindow'] or 24; 191 actions = self.__query_future_actions(time_window) 192 for action in actions: 193 self.handle_action(action, cache_only=1)
194
195 - def __run_remote_actions(self):
196 # the list of caps the client needs 197 caps = capabilities.Capabilities() 198 199 status_report = CheckCli.__build_status_report() 200 201 action = self.__get_action(status_report) 202 while action != "" and action != {}: 203 self.__verify_server_capabilities(caps) 204 205 if self.is_valid_action(action): 206 try: 207 up2dateAuth.updateLoginInfo() 208 except up2dateErrors.ServerCapabilityError: 209 print(sys.exc_info()[1]) 210 sys.exit(1) 211 self.handle_action(action) 212 213 action = self.__get_action(status_report)
214
215 - def __verify_server_capabilities(self, caps):
216 response_headers = self.server.get_response_headers() 217 caps.populate(response_headers) 218 # do we actually want to validte here? 219 try: 220 caps.validate() 221 except up2dateErrors.ServerCapabilityError: 222 print(sys.exc_info()[1]) 223 sys.exit(1)
224
225 - def __parse_action_data(self, action):
226 """ Parse action data and returns (method, params) """ 227 data = action['action'] 228 parser, decoder = xmlrpclib.getparser() 229 parser.feed(bstr(data)) 230 parser.close() 231 params = decoder.close() 232 method = decoder.getmethodname() 233 return (method, params)
234
235 - def submit_response(self, action_id, status, message, data):
236 """ Submit a response for an action_id. """ 237 238 # get a new server object with fresh headers 239 self.server = CheckCli.__get_server() 240 241 try: 242 ret = self.server.queue.submit(up2dateAuth.getSystemId(), 243 action_id, status, message, data) 244 except xmlrpclib.Fault: 245 f = sys.exc_info()[1] 246 print("Could not submit results to server %s" % self.server) 247 print("Error code: %d%s" % (f.faultCode, f.faultString)) 248 sys.exit(-1) 249 # XXX: what if no SSL in socket? 250 except SSL.socket_error: 251 print("ERROR: SSL handshake to %s failed" % self.server) 252 print(""" 253 This could signal that you are *NOT* talking to a server 254 whose certificate was signed by a Certificate Authority 255 listed in the %s file or that the 256 RHNS-CA-CERT file is invalid.""" % self.rhns_ca_cert) 257 sys.exit(-1) 258 except socket.error: 259 print("Could not submit to %s.\n"\ 260 "Possible networking problem?" % str(self.server)) 261 sys.exit(-1) 262 return ret
263
264 - def handle_action(self, action, cache_only=None):
265 """ Wrapper handler for the action we're asked to do. """ 266 log.log_debug("handle_action", action) 267 log.log_debug("handle_action actionid = %s, version = %s" % ( 268 action['id'], action['version'])) 269 270 data = {} 271 action_lock = '/var/lib/up2date/action.%s' % str(action['id']) 272 if os.path.exists(action_lock): 273 ret = 255 274 if not cache_only: 275 if os.path.getsize(action_lock) > 0: 276 data['base64enc'] = 1 277 data['return_code'] = 255 278 data['process_start'] = '1970-01-01 00:00:00' # dummy values as we have no idea of start 279 data['process_end'] = '1970-01-01 00:00:00' # and especially about the end 280 with open(action_lock) as f: 281 data['output'] = base64.encodestring(f.read()) 282 log.log_debug("Sending back response", (255, "Previous run of action didn't completed sucessfully, aborting.", data)) 283 ret = self.submit_response(action['id'], 255, "Previous run of action didn't completed sucessfully, aborting.", data) 284 os.remove(action_lock) 285 return ret 286 287 open(action_lock, 'a').close() 288 289 (method, params) = self.__parse_action_data(action) 290 (status, message, data) = CheckCli.__run_action(method, params, {'cache_only': cache_only}) 291 ret = 0 292 if not cache_only: 293 log.log_debug("Sending back response", (status, message, data)) 294 ret = self.submit_response(action['id'], status, message, data) 295 os.remove(action_lock) 296 return ret
297 298
299 - def is_valid_action(self, action):
300 log.log_debug("check_action", action) 301 302 # be very paranoid of what we get back 303 if type(action) != type({}): 304 print("Got unparseable action response from server") 305 sys.exit(-1) 306 307 for key in ['id', 'version', 'action']: 308 if not key in action: 309 print("Got invalid response - missing '%s'" % key) 310 sys.exit(-1) 311 try: 312 ver = int(action['version']) 313 except ValueError: 314 ver = -1 315 if ver > ACTION_VERSION or ver < 0: 316 print("Got unknown action version %d" % ver) 317 print(action) 318 # the -99 here is kind of magic 319 self.submit_response(action["id"], 320 xmlrpclib.Fault(-99, "Can not handle this version")) 321 return False 322 return True
323 324 @staticmethod
325 - def __get_server():
326 """ Initialize a server connection and set up capability info. """ 327 server = rpcServer.getServer() 328 329 # load the new client caps if they exist 330 clientCaps.loadLocalCaps() 331 332 headerlist = clientCaps.caps.headerFormat() 333 for (headerName, value) in headerlist: 334 server.add_header(headerName, value) 335 336 return server
337 338 @staticmethod
339 - def __update_system_id():
340 try: 341 up2dateAuth.maybeUpdateVersion() 342 except up2dateErrors.CommunicationError: 343 print(sys.exc_info()[1]) 344 sys.exit(1)
345 346 @staticmethod
348 status_report = {} 349 status_report["uname"] = list(os.uname()) 350 351 if os.access("/proc/uptime", os.R_OK): 352 uptime = open("/proc/uptime", "r").read().split() 353 try: 354 status_report["uptime"] = [int(float(a)) for a in uptime] 355 except (TypeError, ValueError): 356 status_report["uptime"] = [a[:-3] for a in uptime] 357 except: 358 pass 359 360 # We need to fit into xmlrpc's integer limits 361 if status_report['uptime'][1] > long(2)**31-1: 362 status_report['uptime'][1] = -1 363 364 return status_report
365 366 @staticmethod
368 """ 369 Hit any actions that we want to always run. 370 371 If we want to run any actions everytime rhnsd runs rhn_check, 372 we can add them to the list LOCAL_ACTIONS 373 """ 374 375 for method_params in LOCAL_ACTIONS: 376 method = method_params[0] 377 params = method_params[1] 378 (status, message, data) = CheckCli.__run_action(method, params) 379 log.log_debug("local action status: ", (status, message, data))
380 381 @staticmethod
382 - def __do_call(method, params, kwargs={}):
383 log.log_debug("do_call ", method, params, kwargs) 384 385 method = getMethod.getMethod(method, "rhn.actions") 386 retval = method(*params, **kwargs) 387 388 return retval
389 390 @staticmethod
391 - def __run_action(method, params, kwargs={}):
392 try: 393 (status, message, data) = CheckCli.__do_call(method, params, kwargs) 394 except getMethod.GetMethodException: 395 log.log_debug("Attempt to call an unsupported action ", method, 396 params) 397 status = 6 398 message = "Invalid function call attempted" 399 data = {} 400 except: 401 log.log_exception(*sys.exc_info()) 402 # The action code failed in some way. let's let the server know. 403 status = 6, 404 message = "Fatal error in Python code occurred" 405 data = {} 406 return (status, message, data)
407 408 @staticmethod
410 """ If we're disabled, go down (almost) quietly. """ 411 if os.path.exists(DISABLE_FILE): 412 print("RHN service is disabled. Check %s" % DISABLE_FILE) 413 sys.exit(0)
414 415 @staticmethod
417 """ Retrieve the system_id. This is required. """ 418 if not up2dateAuth.getSystemId(): 419 print("ERROR: unable to read system id.") 420 sys.exit(-1)
421 422 @staticmethod
424 lock = None 425 try: 426 lock = rhnLockfile.Lockfile('/var/run/rhn_check.pid') 427 except rhnLockfile.LockfileLockedException: 428 sys.stderr.write(sstr(_("Attempting to run more than one instance of rhn_check. Exiting.\n"))) 429 sys.exit(0)
430 431 if __name__ == "__main__": 432 cli = CheckCli() 433 cli.run() 434