Package actions :: Module configfiles
[hide private]
[frames] | no frames]

Source Code for Module actions.configfiles

  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 os 
 17  import time 
 18  import sys 
 19   
 20  from config_common import local_config, file_utils, utils, cfg_exceptions 
 21  from config_common.rhn_log import set_debug_level, get_debug_level, set_logfile, log_to_file 
 22  from config_common.transactions import DeployTransaction, FailedRollback 
 23   
 24  from config_client import rpc_cli_repository 
 25   
 26  from up2date_client import config 
 27   
 28  from spacewalk.common.usix import StringType, UnicodeType 
 29   
 30   
 31  # this is a list of the methods that get exported by a module 
 32  __rhnexport__ = [ 
 33      'mtime_upload', 
 34      'upload', 
 35      'deploy', 
 36      'verify', 
 37      'diff', 
 38  ] 
 39   
 40   
 41  # action version we understand 
 42  ACTION_VERSION = 2 
 43   
 44  # when we do this within up2date, should just have something like 
 45  # __rhn_require_local_permission__ that flags the methods requiring the touched 
 46  # file 
 47  _permission_root_dir = '/etc/sysconfig/rhn/allowed-actions' 
48 -def _local_permission_check(action_type):
49 # action_type ala configfiles.deploy 50 atype_structure = action_type.split('.') 51 52 for i in range(len(atype_structure)): 53 all_structure = atype_structure[:i] 54 all_structure.append('all') 55 56 if os.path.exists(os.path.join(_permission_root_dir, "script/all")) and action_type == "script.run": 57 os.rename(os.path.join(_permission_root_dir, "script/all"),os.path.join(_permission_root_dir, "script/run")) 58 59 potential_all_path = os.path.join(*all_structure) 60 if os.path.exists(os.path.join(_permission_root_dir, potential_all_path)): 61 return 1 62 63 action_path = os.path.join(*atype_structure) 64 return os.path.exists(os.path.join(_permission_root_dir, action_path))
65
66 -def _perm_error(action_type):
67 return (42, "Local permission not set for action type %s" % action_type, {})
68 69
70 -def _visit_dir(params, dirname, names):
71 72 matches = params['matches'] 73 info = params['info'] 74 ignore_dirs = params['ignore'] 75 now = params['now'] 76 77 i = 0 78 while i < len(names): 79 full_path = os.path.join(dirname, names[i]) 80 is_dir = os.path.isdir(full_path) 81 82 if is_dir: 83 if full_path in ignore_dirs: 84 # don't consider the entire subtree on subsequent runs of 85 # visit 86 del names[i] 87 else: 88 i = i + 1 89 # since we can have multiple search paths hitting the same subdir, 90 # filter 'em out after the first pass 91 ignore_dirs[full_path] = None 92 continue 93 94 if not os.path.exists(full_path): 95 i = i + 1 96 continue 97 98 mtime = os.path.getmtime(full_path) 99 100 # do it via delta... 101 if (now - mtime) <= (info['now'] - info['start_date']): 102 if info['end_date']: 103 if (now - mtime) >= (info['now'] - info['end_date']): 104 matches.append(full_path) 105 else: 106 matches.append(full_path) 107 108 i = i + 1
109 110
111 -def format_result(result, files):
112 files_too_large = result.get('files_too_large') or [] 113 quota_failed = result.get('failed_due_to_quota') or [] 114 missing_files = result.get('missing_files') or [] 115 116 extras = { 'attempted_paths' : files } 117 118 if missing_files: 119 extras['missing_files'] = missing_files 120 if files_too_large: 121 extras['files_too_large'] = files_too_large 122 if quota_failed: 123 extras['quota_failed'] = quota_failed 124 125 num_files = len(files) 126 num_uploaded = num_files - (len(missing_files) + len(files_too_large) + len(quota_failed)) 127 128 if num_uploaded == num_files: 129 return 0, "All files successfully uploaded", extras 130 else: 131 return -1, "Some files failed to upload", extras
132 133 134 ## foo = {'ignore': ['\n', '/home/bretm/rhn/build/', '/home/bretm/rhn/sql'], 135 ## 'info': {'now': 1071722611.0, 'import_contents': 'N', 'start_date': 1071290580.0, 'end_date': ''}, 136 ## 'search': ['/home/bretm/'], 137 ## }
138 -def mtime_upload(action_id, params, cache_only=None):
139 if cache_only: 140 return (0, "no-ops for caching", {}) 141 142 action_type = 'configfiles.mtime_upload' 143 if not _local_permission_check(action_type): 144 log_to_file(0, "permissions error: " + str(action_type)) 145 return _perm_error(action_type) 146 147 _init() 148 149 file_matches = [] 150 now = time.time() 151 upload_contents = None 152 ignore_dirs = {'/proc':None, '/dev':None} 153 154 if params['info']['import_contents'] == 'Y': 155 upload_contents = 1 156 157 for to_ignore in params['ignore']: 158 ignore_dirs[utils.normalize_path(to_ignore)] = 1 159 160 for search_path in params['search']: 161 for dirname, dirs, names in os.walk(utils.normalize_path(search_path)): 162 _visit_dir({ 163 'matches' : file_matches, 164 'info' : params['info'], 165 'ignore' : ignore_dirs, 166 'now' : now, 167 }, dirname, names) 168 169 if not file_matches: 170 return 0, "No files found", {} 171 172 r = rpc_cli_repository.ClientRepository() 173 result = r.put_files(action_id, file_matches, upload_contents=upload_contents) 174 175 formatted_result = format_result(result, file_matches) 176 log_to_file(0, formatted_result) 177 return formatted_result
178 179
180 -def upload(action_id, params, cache_only=None):
181 if cache_only: 182 return (0, "no-ops for caching", {}) 183 184 action_type = 'configfiles.upload' 185 if not _local_permission_check(action_type): 186 log_to_file(0, "permissions error: " + str(action_type)) 187 return _perm_error(action_type) 188 189 _init() 190 191 files = params or [] 192 193 r = rpc_cli_repository.ClientRepository() 194 result = r.put_files(action_id, files) 195 196 formatted_result = format_result(result, files) 197 log_to_file(0, formatted_result) 198 199 return formatted_result
200 201
202 -def deploy(params, topdir=None, cache_only=None):
203 if cache_only: 204 return (0, "no-ops for caching", {}) 205 206 action_type = 'configfiles.deploy' 207 if not _local_permission_check(action_type): 208 log_to_file(0, "permissions error: " + str(action_type)) 209 return _perm_error(action_type) 210 211 _init() 212 files = params.get('files') or [] 213 dep_trans = DeployTransaction(transaction_root=topdir, auto_rollback=0) 214 215 for file in files: 216 dep_trans.add(file) 217 218 try: 219 dep_trans.deploy() 220 #5/3/05 wregglej - 135415 Adding stuff for missing user info 221 except cfg_exceptions.UserNotFound: 222 e = sys.exc_info()[1] 223 try: 224 dep_trans.rollback() 225 except FailedRollback: 226 log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) 227 return (44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) 228 #5/3/05 wregglej - 136415 Adding some more exceptions to handle 229 except cfg_exceptions.UserNotFound: 230 f = sys.exc_info()[1] 231 log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) 232 return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) 233 #5/5/05 wregglej - 136415 Adding exception handling for unknown group, 234 except cfg_exceptions.GroupNotFound: 235 f = sys.exc_info()[1] 236 log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],)) 237 return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {}) 238 else: 239 log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) 240 return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) 241 except cfg_exceptions.GroupNotFound: 242 e = sys.exc_info()[1] 243 try: 244 dep_trans.rollback() 245 except FailedRollback: 246 log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], )) 247 return (44, "Failed deployment and rollback, information on user '%s' could not be found" % (e[0], ), {}) 248 #5/3/05 wregglej - 136415 Adding some more exceptions to handle 249 except cfg_exceptions.UserNotFound: 250 f = sys.exc_info()[1] 251 log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ) ) 252 return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) 253 #5/5/05 wregglej - 136415 Adding exception handling for unknown group, 254 except cfg_exceptions.GroupNotFound: 255 f = sys.exc_info()[1] 256 log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],)) 257 return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {}) 258 else: 259 log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (e[0], )) 260 return (51, "Failed deployment and rollback, group '%s' could not be found" % (e[0], ), {}) 261 except cfg_exceptions.FileEntryIsDirectory: 262 e = sys.exc_info()[1] 263 try: 264 dep_trans.rollback() 265 except FailedRollback: 266 log_to_file(0, "Failed deployment and rollback, %s already exists as a directory" % (e[0], )) 267 return (44, "Failed deployment and rollback, %s already exists as a directory" % (e[0], ), {}) 268 #5/3/05 wregglej - 136415 Adding some more exceptions to handle 269 except cfg_exceptions.UserNotFound: 270 f = sys.exc_info()[1] 271 log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) 272 return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) 273 #5/5/05 wregglej - 136415 Adding exception handling for unknown group, 274 except cfg_exceptions.GroupNotFound: 275 f = sys.exc_info()[1] 276 log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],)) 277 return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {}) 278 else: 279 log_to_file(0, "Failed deployment, %s already exists as a directory" % (e[0], )) 280 return (45, "Failed deployment, %s already exists as a directory" % (e[0], ), {}) 281 except cfg_exceptions.DirectoryEntryIsFile: 282 e = sys.exc_info()[1] 283 try: 284 dep_trans.rollback() 285 except FailedRollback: 286 log_to_file(0, "Failed deployment and rollback, %s already exists as a file" % (e[0], )) 287 return (46, "Failed deployment and rollback, %s already exists as a file" % (e[0], ), {}) 288 #5/3/05 wregglej - 136415 Adding exceptions for missing user 289 except cfg_exceptions.UserNotFound: 290 f = sys.exc_info()[1] 291 log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], )) 292 return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0], ), {}) 293 #5/5/05 wregglej - 136415 Adding exception handling for unknown group, 294 except cfg_exceptions.GroupNotFound: 295 f = sys.exc_info()[1] 296 log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],)) 297 return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {}) 298 else: 299 log_to_file(0, "Failed deployment, %s already exists as a file" % (e[0], )) 300 return (47, "Failed deployment, %s already exists as a file" % (e[0], ), {}) 301 302 except Exception: 303 e = sys.exc_info()[1] 304 print(e) 305 try: 306 dep_trans.rollback() 307 except FailedRollback: 308 e2 = sys.exc_info()[1] 309 log_to_file(0, "Failed deployment, failed rollback: %s" % e2) 310 return (48, "Failed deployment, failed rollback: %s" % e2, {}) 311 #5/3/05 wregglej - 135415 Add exception handling for missing user. 312 except cfg_exceptions.UserNotFound: 313 f = sys.exc_info()[1] 314 log_to_file(0, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0])) 315 return (50, "Failed deployment and rollback, information on user '%s' could not be found" % (f[0]), {}) 316 #5/5/05 wregglej - 136415 Adding exception handling for unknown group, 317 except cfg_exceptions.GroupNotFound: 318 f = sys.exc_info()[1] 319 log_to_file(0, "Failed deployment and rollback, group '%s' could not be found" % (f[0],)) 320 return (51, "Failed deployment and rollback, group '%s' could not be found" % (f[0],), {}) 321 else: 322 log_to_file(0, "Failed deployment, rolled back: %s" % e) 323 return (49, "Failed deployment, rolled back: %s" % e, {}) 324 325 extras = {} 326 log_to_file(0, "Files successfully deployed: %s %s" % (format_file_string(files, create_key_list()), str(extras))) 327 return 0, "Files successfully deployed", extras
328 329
330 -def diff(params, cache_only=None):
331 def is_utf8(in_string): 332 """Returns true if input is a valid UTF-8 string, False otherwise.""" 333 if isinstance(in_string, UnicodeType): 334 return True 335 elif isinstance(in_string, StringType): 336 try: 337 in_string.decode('utf-8') 338 return True 339 except UnicodeDecodeError: 340 return False 341 return False
342 343 if cache_only: 344 return (0, "no-ops for caching", {}) 345 346 action_type = 'configfiles.diff' 347 if not _local_permission_check(action_type): 348 log_to_file(0, "permissions error: " + str(action_type)) 349 return _perm_error(action_type) 350 351 _init() 352 files = params.get('files') or [] 353 fp = file_utils.FileProcessor() 354 missing_files = [] 355 diffs = {} 356 exists = hasattr(os.path, 'lexists') and os.path.lexists or os.path.exists 357 for file in files: 358 path = file['path'] 359 if not exists(path): 360 missing_files.append(path) 361 continue 362 363 diff = fp.diff(file) 364 if diff: 365 diffs[path] = diff 366 367 extras = {} 368 if missing_files: 369 extras['missing_files'] = missing_files 370 371 if diffs: 372 for file in diffs.keys(): 373 if not is_utf8(diffs[file]): 374 diffs[file] = "%s: binary files differ" % file 375 extras['diffs'] = diffs 376 377 log_to_file(0, "Files successfully diffed: %s %s" % (format_file_string(files, create_key_list()), str(extras))) 378 return 0, "Files successfully diffed", extras 379 380 verify = diff 381 382 #The format_file_string and create_key_list functions can be used together to create a string 383 #containing information about the files in file_list. Use sparingly. 384 385 #file_list is a list of dictionaries containing file information. 386 #keylist is a list of strings containing the keys of the information in file_list that you wish to print out.
387 -def format_file_string(file_list, keylist):
388 outstr = "" 389 for afile in file_list: 390 outstr 391 for key in keylist: 392 formatstr = "\n%s: %s" 393 if key in afile: 394 outstr = outstr + formatstr % (key, afile[key]) 395 outstr = outstr + "\n" 396 return outstr
397 398 #Returns a list of strings. Each string is a key in the dictionary containing file information. 399 #The number of keys returned corresponds to the debug_level. The higher the debug_level, the longer the 400 #list of keys.
401 -def create_key_list():
402 #The list of keys. The order of the keys determines what debug_level they will be returned in. 403 #For example, at debug level 0 only the path and revision will be included. At level 1, the path, revision, 404 #config_channel, and filemode keys should be included. 405 key_list = [ 406 'path', 407 'revision', 408 'config_channel', 409 'filemode', 410 'filetype', 411 'encoding', 412 'username', 413 'groupname', 414 'delim_start', 415 'delim_end', 416 'md5sum', 417 'checksum_type', 418 'checksum', 419 'verify_contents', 420 'file_contents', 421 ] 422 #This dictionary associates each debug level (the key) with the index into key_list (the value) at which 423 #we should stop including keys in the returned list. 424 debug_levels = { 425 0 : 2, 426 1 : 4, 427 2 : 6, 428 3 : 8, 429 4 : 10, 430 5 : 15, 431 } 432 curr_debug = get_debug_level() 433 if curr_debug > 5: 434 curr_debug = 5 435 if curr_debug < 0: 436 curr_debug = 0 437 if not curr_debug in debug_levels.keys(): 438 curr_debug = 0 439 return key_list[:debug_levels[curr_debug]]
440
441 -def _init():
442 cfg = config.initUp2dateConfig() 443 cfg_dict = dict(cfg.items()) 444 server_url = config.getServerlURL() 445 cfg_dict['proto'], cfg_dict['server_name'] = utils.parse_url(server_url[0], scheme="https")[:2] 446 if len(server_url) > 1: 447 cfg_dict['server_list'] = server_url 448 local_config.init('rhncfg-client', defaults=cfg_dict) 449 set_debug_level(int(local_config.get('debug_level') or 0)) 450 set_logfile("/var/log/rhncfg-actions")
451