def maketemp(prefix=None, directory=None, symlink=None): """Creates a temporary file (guaranteed to be new), using the specified prefix. Returns the filename and an open stream """ if not directory: directory = tempfile.gettempdir() dirs_created = None if not os.path.exists(directory): dirs_created = utils.mkdir_p(directory) if not prefix: # Create the file in /tmp by default prefix = 'rhncfg-tempfile' file_prefix = "%s-%s-" % (prefix, os.getpid()) (fd, filename) = tempfile.mkstemp(prefix=file_prefix, dir=directory) if symlink: os.unlink(filename) os.symlink(symlink, filename) open_file = None else: open_file = os.fdopen(fd, "w+") return filename, dirs_created, open_file
def maketemp(prefix=None, directory=None): """Creates a temporary file (guaranteed to be new), using the specified prefix. Returns the filename and a file descriptor """ if not directory: directory = tempfile.gettempdir() dirs_created = None if not os.path.exists(directory): dirs_created = utils.mkdir_p(directory) if not prefix: # Create the file in /tmp by default prefix = 'rhncfg-tempfile' file_prefix = "%s-%s-" % (prefix, os.getpid()) (fd, filename) = tempfile.mkstemp(prefix=file_prefix, dir=directory) return filename, dirs_created, fd
def process(self, file_struct, directory=None, strict_ownership=1): # Older servers will not return directories; if filetype is missing, # assume file if file_struct.get('filetype') == 'directory': if directory is None: directory = "" return None, utils.mkdir_p(directory + file_struct['path']) if directory: directory += os.path.split(file_struct['path'])[0] if file_struct.get('filetype') == 'symlink': if 'symlink' not in file_struct: raise Exception("Missing key symlink") (fullpath, dirs_created, fh) = maketemp(prefix=".rhn-cfg-tmp", directory=directory, symlink=file_struct['symlink']) return fullpath, dirs_created for k in self.file_struct_fields.keys(): if k not in file_struct: # XXX raise Exception("Missing key %s" % k) encoding = '' if 'encoding' in file_struct: encoding = file_struct['encoding'] contents = file_struct['file_contents'] if contents and (encoding == 'base64'): contents = base64.decodestring(bstr(contents)) delim_start = file_struct['delim_start'] delim_end = file_struct['delim_end'] if ('checksum' in file_struct and 'checksum_type' in file_struct and 'verify_contents' in file_struct and file_struct['verify_contents']): if file_struct['checksum'] != utils.getContentChecksum( file_struct['checksum_type'], contents): raise Exception( "Corrupt file received: Content checksums do not match!") elif ('md5sum' in file_struct and 'verify_contents' in file_struct and file_struct['verify_contents']): if file_struct['md5sum'] != utils.getContentChecksum( 'md5', contents): raise Exception( "Corrupt file received: Content checksums do not match!") elif ('verify_contents' in file_struct and file_struct['verify_contents']): raise Exception( "Corrupt file received: missing checksum information!") fh = None (fullpath, dirs_created, fh) = maketemp(prefix=".rhn-cfg-tmp", directory=directory) try: fh.write(sstr(contents)) except Exception: if fh: fh.close() # don't leak fds... raise else: fh.close() # try to set mtime and ctime of the file to # the last modified time on the server if 'modified' in file_struct: try: modified = xmlrpc_time(file_struct['modified'].value) epoch_time = time.mktime(modified) os.utime(fullpath, (epoch_time, epoch_time)) except (ValueError, AttributeError): # we can't parse modified time pass return fullpath, dirs_created
def process(self, file_struct, directory=None, strict_ownership=1): # Older servers will not return directories; if filetype is missing, # assume file if file_struct.get('filetype') == 'directory': if directory is None: directory = "" return None, utils.mkdir_p(directory + file_struct['path']) if directory: directory += os.path.split(file_struct['path'])[0] if file_struct.get('filetype') == 'symlink': if 'symlink' not in file_struct: raise Exception("Missing key symlink") (fullpath, dirs_created, fd) = maketemp(prefix=".rhn-cfg-tmp", directory=directory) os.close(fd) os.unlink(fullpath) os.symlink(file_struct['symlink'], fullpath) return fullpath, dirs_created for k in self.file_struct_fields.keys(): if k not in file_struct: # XXX raise Exception("Missing key %s" % k) encoding = '' if 'encoding' in file_struct: encoding = file_struct['encoding'] contents = file_struct['file_contents'] if contents and (encoding == 'base64'): contents = base64.decodestring(bstr(contents)) delim_start = file_struct['delim_start'] delim_end = file_struct['delim_end'] if ('checksum' in file_struct and 'checksum_type' in file_struct and 'verify_contents' in file_struct and file_struct['verify_contents']): if file_struct['checksum'] != utils.getContentChecksum( file_struct['checksum_type'], contents): raise Exception("Corrupt file received: Content checksums do not match!") elif ('md5sum' in file_struct and 'verify_contents' in file_struct and file_struct['verify_contents']): if file_struct['md5sum'] != utils.getContentChecksum( 'md5', contents): raise Exception("Corrupt file received: Content checksums do not match!") elif ('verify_contents' in file_struct and file_struct['verify_contents']): raise Exception("Corrupt file received: missing checksum information!") (fullpath, dirs_created, fd) = maketemp(prefix=".rhn-cfg-tmp", directory=directory) try: os.write(fd, bstr(contents)) except Exception: raise finally: os.close(fd) # try to set mtime and ctime of the file to # the last modified time on the server if 'modified' in file_struct: try: modified = xmlrpc_time(file_struct['modified'].value) epoch_time = time.mktime(modified) os.utime(fullpath, (epoch_time, epoch_time)) except (ValueError, AttributeError): # we can't parse modified time pass return fullpath, dirs_created
def deploy(self): """attempt deployment; will rollback if auto_rollback is set""" fp = file_utils.FileProcessor() log_debug(3, "deploying transaction") for dep_file in self.files: if dep_file['filetype'] == 'symlink': self.symlinks.append(dep_file) # 0. handle any dirs we need to create first # a) if the dir exists, then just change the mode and owners, # else create it and then make sure the mode and owners are correct. # b) if there are files, then continue # 1. write new version (tmp) # a) if anything breaks, remove all tmp versions and error out # 2. rename old version to backup # a) if anything breaks, rename all backed up files to original name, # then do 1-a. # 3. rename tmp to target name # a) if anything breaks, remove all deployed files, then do 2-a. # # (yes, this leaves the backup version on disk...) try: # 0. if self.dirs: for directory in self.dirs: dirname = self._normalize_path_to_root(directory['path']) dirmode = directory['filemode'] if os.path.isfile(dirname): raise cfg_exceptions.DirectoryEntryIsFile(dirname) if os.path.isdir(dirname): s = os.stat(dirname) entry = { 'filemode': "%o" % (s[0] & int('07777', 8)), 'uid': s[4], 'gid': s[5], 'filetype': 'directory', } self.changed_dir_info[dirname] = entry log_debug( 3, "directory found, chowning and chmoding to %s as needed: %s" % (dirmode, dirname)) self._chown_chmod_chcon(dirname, dirname, directory) else: log_debug( 3, "directory not found, creating: %s" % dirname) dirs_created = utils.mkdir_p(dirname, None, self.symlinks, self.files) self.new_dirs.extend(dirs_created) self._chown_chmod_chcon(dirname, dirname, directory) if self.deployment_cb: self.deployment_cb(dirname) log_debug(6, "changed_dir_info: %s" % self.changed_dir_info) log_debug(4, "new_dirs: ", self.new_dirs) if not self.newtemp_by_path and not self.files: log_debug( 4, "directory creation complete, no files found to create") return else: log_debug(4, "done with directory creation, moving on to files") # 1. for dep_file in self.files: path = dep_file['path'] log_debug(6, "writing new version of %s to tmp file ..." % path) # make any directories needed... # # TODO: it'd be nice if this had a hook for letting me know # which ones are created... then i could clean created # dirs on rollback (directory, filename) = os.path.split(path) if os.path.isdir(path) and not os.path.islink(path): raise cfg_exceptions.FileEntryIsDirectory(path) if not os.path.exists( directory): # and os.path.isdir(directory): log_debug(7, "creating directories for %s ..." % directory) dirs_created = utils.mkdir_p(directory, None, self.symlinks, self.files) self.new_dirs.extend(dirs_created) log_debug( 7, "directories created and added to list for rollback") # write the new contents to a tmp file, and store the path of the # new tmp file by it's eventual target path self.newtemp_by_path[path], temp_new_dirs = fp.process( dep_file, os.path.sep) self.new_dirs.extend(temp_new_dirs or []) # properly chown and chmod it self._chown_chmod_chcon(self.newtemp_by_path[path], path, dep_file) log_debug(9, "tempfile written: %s" % self.newtemp_by_path[path]) #paths = map(lambda x: x['path'], self.files) paths = list(self.newtemp_by_path.keys()) # 2. for path in paths: if os.path.isdir(path) and not os.path.islink(path): raise cfg_exceptions.FileEntryIsDirectory(path) else: self._rename_to_backup(path) if path in self.backup_by_path: log_debug( 9, "backup file %s written" % self.backup_by_path[path]) # 3. paths.sort(key=lambda s: s.count(os.path.sep)) for path in paths: if self.deployment_cb: self.deployment_cb(path) log_debug(6, "deploying %s ..." % path) os.rename(self.newtemp_by_path[path], path) # race del self.newtemp_by_path[path] log_debug(9, "new version of %s deployed" % path) log_debug(3, "deploy transaction successful") except Exception: #log_debug(1, traceback.format_exception_only(SyntaxError, e)) #traceback.print_exc() if self.auto_rollback: self.rollback() raise
def deploy(self): """attempt deployment; will rollback if auto_rollback is set""" fp = file_utils.FileProcessor() log_debug(3, "deploying transaction") for dep_file in self.files: if dep_file['filetype'] == 'symlink': self.symlinks.append(dep_file) # 0. handle any dirs we need to create first # a) if the dir exists, then just change the mode and owners, # else create it and then make sure the mode and owners are correct. # b) if there are files, then continue # 1. write new version (tmp) # a) if anything breaks, remove all tmp versions and error out # 2. rename old version to backup # a) if anything breaks, rename all backed up files to original name, # then do 1-a. # 3. rename tmp to target name # a) if anything breaks, remove all deployed files, then do 2-a. # # (yes, this leaves the backup version on disk...) try: # 0. if self.dirs: for directory in self.dirs: dirname = self._normalize_path_to_root(directory['path']) dirmode = directory['filemode'] if os.path.isfile(dirname): raise cfg_exceptions.DirectoryEntryIsFile(dirname) if os.path.isdir(dirname): s = os.stat(dirname) entry = { 'filemode': "%o" % (s[0] & 07777), 'uid': s[4], 'gid': s[5], 'filetype': 'directory', } self.changed_dir_info[dirname] = entry log_debug(3, "directory found, chowning and chmoding to %s as needed: %s" % (dirmode, dirname)) self._chown_chmod_chcon(dirname, dirname, directory) else: log_debug(3, "directory not found, creating: %s" % dirname) dirs_created = utils.mkdir_p(dirname, None, self.symlinks, self.files) self.new_dirs.extend(dirs_created) self._chown_chmod_chcon(dirname, dirname, directory) if self.deployment_cb: self.deployment_cb(dirname) log_debug(6, "changed_dir_info: %s" % self.changed_dir_info) log_debug(4, "new_dirs: ", self.new_dirs) if not self.newtemp_by_path and not self.files: log_debug(4, "directory creation complete, no files found to create") return else: log_debug(4, "done with directory creation, moving on to files") # 1. for dep_file in self.files: path = dep_file['path'] log_debug(6, "writing new version of %s to tmp file ..." % path) # make any directories needed... # # TODO: it'd be nice if this had a hook for letting me know # which ones are created... then i could clean created # dirs on rollback (directory, filename) = os.path.split(path) if os.path.isdir(path) and not os.path.islink(path): raise cfg_exceptions.FileEntryIsDirectory(path) if not os.path.exists(directory):# and os.path.isdir(directory): log_debug(7, "creating directories for %s ..." % directory) dirs_created = utils.mkdir_p(directory, None, self.symlinks, self.files) self.new_dirs.extend(dirs_created) log_debug(7, "directories created and added to list for rollback") # write the new contents to a tmp file, and store the path of the # new tmp file by it's eventual target path self.newtemp_by_path[path], temp_new_dirs = fp.process(dep_file, os.path.sep) self.new_dirs.extend(temp_new_dirs or []) # properly chown and chmod it self._chown_chmod_chcon(self.newtemp_by_path[path], path, dep_file) log_debug(9, "tempfile written: %s" % self.newtemp_by_path[path]) #paths = map(lambda x: x['path'], self.files) paths = self.newtemp_by_path.keys() # 2. for path in paths: if os.path.isdir(path) and not os.path.islink(path): raise cfg_exceptions.FileEntryIsDirectory(path) else: self._rename_to_backup(path) if self.backup_by_path.has_key(path): log_debug(9, "backup file %s written" % self.backup_by_path[path]) # 3. paths.sort(key = lambda s: string.count(s, os.path.sep)) for path in paths: if self.deployment_cb: self.deployment_cb(path) log_debug(6, "deploying %s ..." % path) os.rename(self.newtemp_by_path[path], path) # race del self.newtemp_by_path[path] log_debug(9, "new version of %s deployed" % path) log_debug(3, "deploy transaction successful") except Exception: #log_debug(1, traceback.format_exception_only(SyntaxError, e)) #traceback.print_exc() if self.auto_rollback: self.rollback() raise