Package backend :: Package server :: Package action_extra_data :: Module packages
[hide private]
[frames] | no frames]

Source Code for Module backend.server.action_extra_data.packages

  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   
 17  import re 
 18  import sys 
 19   
 20  from spacewalk.common.usix import ListType, IntType 
 21   
 22  from spacewalk.common import rhnFlags 
 23  from spacewalk.common.rhnLog import log_debug, log_error 
 24  from spacewalk.common.rhnException import rhnException 
 25  from spacewalk.server import rhnSQL 
 26  from spacewalk.server.rhnServer import server_kickstart 
 27   
 28  # the "exposed" functions 
 29  __rhnexport__ = ['remove', 
 30                   'update', 
 31                   'refresh_list', 
 32                   'delta', 
 33                   'runTransaction', 
 34                   'verify'] 
 35   
 36   
37 -class InvalidDep(Exception):
38 pass
39 40 _query_insert_attribute_verify_results = rhnSQL.Statement(""" 41 insert into rhnServerActionVerifyResult ( 42 server_id, action_id, 43 package_name_id, 44 package_evr_id, 45 package_arch_id, 46 package_capability_id, 47 attribute, size_differs, mode_differs, checksum_differs, 48 devnum_differs, readlink_differs, uid_differs, 49 gid_differs, mtime_differs 50 ) 51 values ( 52 :server_id, :action_id, 53 lookup_package_name(:package_name), 54 lookup_evr(:epoch || '', :version, :release), 55 lookup_package_arch(:arch), 56 lookup_package_capability(:filename), 57 :attrib, :test_S, :test_M, :test_5, 58 :test_D, :test_L, :test_U, 59 :test_G, :test_T 60 ) 61 """) 62 63 _query_insert_missing_verify_results = rhnSQL.Statement(""" 64 insert into rhnServerActionVerifyMissing ( 65 server_id, 66 action_id, 67 package_name_id, 68 package_evr_id, 69 package_arch_id, 70 package_capability_id 71 ) 72 values ( 73 :server_id, 74 :action_id, 75 lookup_package_name(:package_name), 76 lookup_evr(:epoch || '', :version, :release), 77 lookup_package_arch(:arch), 78 lookup_package_capability(:filename) 79 ) 80 """) 81 82 _query_delete_verify_results = rhnSQL.Statement(""" 83 delete from rhnServerActionVerifyResult 84 where server_id = :server_id 85 and action_id = :action_id 86 """) 87 88 _query_delete_verify_missing = rhnSQL.Statement(""" 89 delete from rhnServerActionVerifyMissing 90 where server_id = :server_id 91 and action_id = :action_id 92 """) 93 94
95 -def verify(server_id, action_id, data={}):
96 log_debug(3, action_id) 97 98 if (not data) or ('verify_info' not in data): 99 # some data should have been passed back... 100 log_error("Insufficient package verify information returned", 101 server_id, action_id, data) 102 return 103 104 log_debug(4, "pkg verify data", data) 105 106 # Remove old results 107 h = rhnSQL.prepare(_query_delete_verify_results) 108 h.execute(server_id=server_id, action_id=action_id) 109 110 h = rhnSQL.prepare(_query_delete_verify_missing) 111 h.execute(server_id=server_id, action_id=action_id) 112 113 attrib_tests = ['S', 'M', '5', 'D', 'L', 'U', 'G', 'T'] 114 115 # Store the values for executemany() for the attribute-failures 116 verify_attribs = {'server_id': [], 'action_id': [], 'package_name': [], 117 'epoch': [], 'version': [], 'release': [], 'arch': [], 118 'filename': [], 'attrib': [], } 119 for test in attrib_tests: 120 verify_attribs["test_" + test] = [] 121 122 # Store the "missing xxxx" results for executemany() 123 missing_files = {'server_id': [], 'action_id': [], 'package_name': [], 124 'epoch': [], 'version': [], 'release': [], 'arch': [], 125 'filename': []} 126 127 # Uniquify the packages 128 uq_packages = {} 129 130 for package_spec, responses in data['verify_info']: 131 package_spec = list(package_spec) 132 # Fix the epoch 133 if package_spec[3] == '': 134 package_spec[3] = None 135 package_spec = tuple(package_spec) 136 if package_spec in uq_packages: 137 # Been here already 138 continue 139 140 # We need to uniquify the file names within a package too 141 hash = {} 142 for response in responses: 143 try: 144 dict = _parse_response_line(response, attrib_tests) 145 except InvalidResponseLine: 146 log_error("packages.verify: (%s, %s): invalid line %s" 147 % (server_id, action_id, response)) 148 continue 149 150 hash[dict['filename']] = dict 151 152 # Add the rest of the variables to the dictionaries 153 for filename, dict in hash.items(): 154 dict['server_id'] = server_id 155 dict['action_id'] = action_id 156 157 dict['package_name'] = package_spec[0] 158 dict['version'] = package_spec[1] 159 dict['release'] = package_spec[2] 160 dict['epoch'] = package_spec[3] 161 dict['arch'] = package_spec[4] 162 163 if 'missing' not in dict: 164 _hash_append(verify_attribs, dict) 165 else: 166 _hash_append(missing_files, dict) 167 168 # This package was visited, store it 169 uq_packages[package_spec] = None 170 171 if verify_attribs['action_id']: 172 h = rhnSQL.prepare(_query_insert_attribute_verify_results) 173 h.executemany(**verify_attribs) 174 175 if missing_files['action_id']: 176 h = rhnSQL.prepare(_query_insert_missing_verify_results) 177 h.executemany(**missing_files) 178 179 rhnSQL.commit()
180 181 # Exception raised when an invalid line is found 182 183
184 -class InvalidResponseLine(Exception):
185 pass
186 187
188 -def _parse_response_line(response, tests):
189 # Parses a single line of output from rpmverify 190 # Returns a dictionary of values that can be plugged into the SQL query 191 192 # response looks like: 193 # 'S.5....T c /usr/share/rhn/up2date_client/iutil.pyc' 194 # or 195 # '....L... /var/www/html' 196 # or 197 # 'missing /usr/include/curl/types.h' 198 # or 199 # 'missing c /var/www/html/index.html' 200 # 201 # 202 # or something like S.5....T. /usr/lib/anaconda-runtime/boot/boot.msg 203 # with the last line being a . or a C, depending on selinux context 204 # see #155952 205 # 206 207 res_re = re.compile("^(?P<ts>[\S]+)\s+(?P<attr>[cdglr]?)\s* (?P<filename>[\S]+)$") 208 209 m = res_re.match(response) 210 211 if not m: 212 raise InvalidResponseLine 213 214 ts, attr, filename = m.groups() 215 # clean up attr, as it can get slightly fudged in the 216 217 if ts == 'missing': 218 return {'filename': filename, 'missing': None} 219 220 # bug 155952: SELinux will return an extra flag 221 # FIXME: need to support the extra selinux context flag 222 # I think this is just being paranoid, but to avoid changing schema for 223 # bug 155952 we going to remove the 9th char if we get it 224 # ahem, ignore the last flag if we 9 chars 225 if len(ts) < len(tests): 226 raise InvalidResponseLine 227 228 if not filename: 229 raise InvalidResponseLine 230 231 dict = { 232 'attrib': attr or None, # convert empty attribute to None 233 'filename': filename, 234 } 235 # Add the tests 236 for i in range(len(tests)): 237 val = ts[i] 238 t_name = tests[i] 239 if val == t_name: 240 val = 'Y' 241 elif val == '.': 242 val = 'N' 243 elif val != '?': 244 raise InvalidResponseLine 245 dict["test_" + t_name] = val 246 247 return dict
248 249
250 -def _hash_append(dst, src):
251 # Append the values of src to dst 252 for k, list in dst.items(): 253 list.append(src[k])
254 255
256 -def update(server_id, action_id, data={}):
257 log_debug(3, server_id, action_id) 258 259 action_status = rhnFlags.get('action_status') 260 261 if action_status == 3: 262 # Action failed 263 kickstart_state = 'failed' 264 next_action_type = None 265 else: 266 kickstart_state = 'deployed' 267 268 # This is horrendous, but in order to fix it I would have to change almost all of the 269 # actions code, which we don't have time to do for the 500 beta. --wregglej 270 try: 271 ks_session_type = server_kickstart.get_kickstart_session_type(server_id, action_id) 272 except rhnException: 273 re = sys.exc_info()[1] 274 ks_session_type = None 275 276 if ks_session_type is None: 277 next_action_type = "None" 278 elif ks_session_type == 'para_guest': 279 next_action_type = 'kickstart_guest.initiate' 280 else: 281 next_action_type = 'kickstart.initiate' 282 283 log_debug(4, "next_action_type: %s" % next_action_type) 284 285 # More hideous hacked together code to get around our inflexible actions "framework". 286 # If next_action_type is "None", we're assuming that we're *not* in a kickstart session 287 # at this point, so we don't want to update a non-existant kickstart session. 288 # I feel so dirty. --wregglej 289 if next_action_type != "None": 290 server_kickstart.update_kickstart_session(server_id, action_id, 291 action_status, kickstart_state=kickstart_state, 292 next_action_type=next_action_type) 293 294 _mark_dep_failures(server_id, action_id, data)
295 296
297 -def remove(server_id, action_id, data={}):
298 log_debug(3, action_id, data.get('name')) 299 _mark_dep_failures(server_id, action_id, data)
300 301 302 _query_delete_dep_failures = rhnSQL.Statement(""" 303 delete from rhnActionPackageRemovalFailure 304 where server_id = :server_id and action_id = :action_id 305 """) 306 _query_insert_dep_failures = rhnSQL.Statement(""" 307 insert into rhnActionPackageRemovalFailure ( 308 server_id, action_id, name_id, evr_id, capability_id, 309 flags, suggested, sense) 310 values ( 311 :server_id, :action_id, LOOKUP_PACKAGE_NAME(:name), 312 LOOKUP_EVR(:epoch, :version, :release), 313 LOOKUP_PACKAGE_CAPABILITY(:needs_name, :needs_version), 314 :flags, LOOKUP_PACKAGE_NAME(:suggested, :ignore_null), :sense) 315 """) 316 317
318 -def _mark_dep_failures(server_id, action_id, data):
319 if not data: 320 log_debug(4, "Nothing to do") 321 return 322 failed_deps = data.get('failed_deps') 323 if not failed_deps: 324 log_debug(4, "No failed deps") 325 return 326 327 if not isinstance(failed_deps, ListType): 328 # Not the right format 329 log_error("action_extra_data.packages.remove: server %s, action %s: " 330 "wrong type %s" % (server_id, action_id, type(failed_deps))) 331 return 332 333 inserts = {} 334 for f in ('server_id', 'action_id', 335 'name', 'version', 'release', 'epoch', 336 'needs_name', 'needs_version', 'ignore_null', 337 'flags', 'suggested', 'sense'): 338 inserts[f] = [] 339 340 for failed_dep in failed_deps: 341 try: 342 pkg, needs_pkg, flags, suggested, sense = _check_dep(server_id, 343 action_id, failed_dep) 344 except InvalidDep: 345 continue 346 347 inserts['server_id'].append(server_id) 348 inserts['action_id'].append(action_id) 349 inserts['name'] .append(pkg[0]) 350 inserts['version'].append(pkg[1]) 351 inserts['release'].append(pkg[2]) 352 inserts['epoch'].append(None) 353 354 inserts['needs_name'].append(needs_pkg[0]) 355 inserts['needs_version'].append(needs_pkg[1]) 356 357 inserts['flags'].append(flags) 358 inserts['suggested'].append(suggested) 359 inserts['ignore_null'].append(1) 360 inserts['sense'].append(sense) 361 362 h = rhnSQL.prepare(_query_delete_dep_failures) 363 rowcount = h.execute(server_id=server_id, action_id=action_id) 364 log_debug(5, "Removed old rows", rowcount) 365 366 h = rhnSQL.prepare(_query_insert_dep_failures) 367 368 rowcount = h.execute_bulk(inserts) 369 log_debug(5, "Inserted rows", rowcount)
370 371
372 -def _check_dep(server_id, action_id, failed_dep):
373 log_debug(5, failed_dep) 374 if not failed_dep: 375 return 376 if not isinstance(failed_dep, ListType): 377 # Not the right format 378 log_error("action_extra_data.packages.remove: server %s, action %s: " 379 "failed dep type error: %s" % ( 380 server_id, action_id, type(failed_dep))) 381 raise InvalidDep 382 383 # This is boring, but somebody's got to do it 384 if len(failed_dep) < 5: 385 log_error("action_extra_data.packages.remove: server %s, action %s: " 386 "failed dep: not enough entries: %s" % ( 387 server_id, action_id, len(failed_dep))) 388 raise InvalidDep 389 390 pkg, needs_pkg, flags, suggested, sense = failed_dep[:5] 391 392 if not isinstance(pkg, ListType) or len(pkg) < 3: 393 log_error("action_extra_data.packages.remove: server %s, action %s: " 394 "failed dep: bad package spec %s (type %s, len %s)" % ( 395 server_id, action_id, pkg, type(pkg), len(pkg))) 396 raise InvalidDep 397 pkg = list(map(str, pkg[:3])) 398 399 if not isinstance(needs_pkg, ListType) or len(needs_pkg) < 2: 400 log_error("action_extra_data.packages.remove: server %s, action %s: " 401 "failed dep: bad needs package spec %s (type %s, len %s)" % ( 402 server_id, action_id, needs_pkg, type(needs_pkg), 403 len(needs_pkg))) 404 raise InvalidDep 405 needs_pkg = list(map(str, needs_pkg[:2])) 406 407 if not isinstance(flags, IntType): 408 log_error("action_extra_data.packages.remove: server %s, action %s: " 409 "failed dep: bad flags type %s" % (server_id, action_id, type(flags))) 410 raise InvalidDep 411 412 if not isinstance(sense, IntType): 413 log_error("action_extra_data.packages.remove: server %s, action %s: " 414 "failed dep: bad sense type %s" % (server_id, action_id, type(sense))) 415 raise InvalidDep 416 417 return pkg, needs_pkg, flags, str(suggested), sense
418 419
420 -def refresh_list(server_id, action_id, data={}):
421 if not data: 422 return 423 log_debug(2, "action_extra_data.packages.refresh_list: Should do something " 424 "useful with this data", server_id, action_id, data)
425 426
427 -def delta(server_id, action_id, data={}):
428 if not data: 429 return 430 log_debug(2, "action_extra_data.packages.delta: Should do something " 431 "useful with this data", server_id, action_id, data)
432 433
434 -def runTransaction(server_id, action_id, data={}):
435 log_debug(3, action_id) 436 437 # If it's a kickstart-related transaction, mark the kickstart session as 438 # completed 439 action_status = rhnFlags.get('action_status') 440 ks_session_id = _next_kickstart_step(server_id, action_id, action_status) 441 442 # Cleanup package profile 443 server_kickstart.cleanup_profile(server_id, action_id, ks_session_id, 444 action_status) 445 446 _mark_dep_failures(server_id, action_id, data)
447 448 # Determine the next step to be executed in the kickstart code 449 450
451 -def _next_kickstart_step(server_id, action_id, action_status):
452 if action_status == 3: # Failed 453 # Nothing more to do here 454 return server_kickstart.update_kickstart_session(server_id, 455 action_id, action_status, kickstart_state='complete', 456 next_action_type=None) 457 458 # Fetch kickstart session id 459 ks_session_id = server_kickstart.get_kickstart_session_id(server_id, 460 action_id) 461 462 if ks_session_id is None: 463 return server_kickstart.update_kickstart_session(server_id, 464 action_id, action_status, kickstart_state='complete', 465 next_action_type=None) 466 467 # Get the current server profile 468 server_profile = server_kickstart.get_server_package_profile(server_id) 469 470 server_kickstart.schedule_config_deploy(server_id, action_id, 471 ks_session_id, server_profile=server_profile) 472 return ks_session_id
473