def __save(self): """Serializes the current history information and writes it to a file in self.path/{operation_start_time}-{sequence}.xml. """ d = xmini.Document() d.appendChild(d.createElement("history")) self.__serialize_client_data(d) self.__serialize_operation_data(d) if not os.path.exists(self.path): try: # Only the right-most directory should be # created. Assume that if the parent structure # does not exist, it shouldn't be created. os.mkdir(self.path, misc.PKG_DIR_MODE) except EnvironmentError as e: if e.errno not in (errno.EROFS, errno.EACCES, errno.ENOENT): # Ignore read-only file system and # access errors as it isn't critical # to the image that this data is # written. raise apx.HistoryStoreException(e) # Return, since without the directory, the rest # of this will fail. return except KeyboardInterrupt: raise except Exception as e: raise apx.HistoryStoreException(e) # Repeatedly attempt to write the history (only if it's because # the file already exists). This is necessary due to multiple # operations possibly occuring within the same second (but not # microsecond). pathname = self.pathname for i in range(1, 100): try: f = os.fdopen( os.open(pathname, os.O_CREAT | os.O_EXCL | os.O_WRONLY, misc.PKG_FILE_MODE), "w") d.writexml(f, encoding=sys.getdefaultencoding()) f.close() return except EnvironmentError as e: if e.errno == errno.EEXIST: name, ext = os.path.splitext(os.path.basename(pathname)) name = name.split("-", 1)[0] # Pick the next name in our sequence # and try again. pathname = os.path.join( self.path, "{0}-{1:>02d}{2}".format(name, i + 1, ext)) continue elif e.errno not in (errno.EROFS, errno.EACCES): # Ignore read-only file system and # access errors as it isn't critical # to the image that this data is # written. raise apx.HistoryStoreException(e) # For all other failures, return, and avoid any # further attempts. return except KeyboardInterrupt: raise except Exception as e: raise apx.HistoryStoreException(e)
def __serialize_operation_data(self, d): """Internal function used to serialize current operation data using the supplied 'd' (xml.dom.minidom) object. """ if self.operation_userid is None: raise apx.HistoryStoreException( "Unable to determine " "the id of the user that performed the current " "operation; unable to store history information.") elif self.operation_username is None: raise apx.HistoryStoreException( "Unable to determine " "the username of the user that performed the " "current operation; unable to store history " "information.") root = d.documentElement op = d.createElement("operation") op.setAttribute("name", self.operation_name) # Must explictly convert values to a string due to minidom bug # that causes a fatal whenever using types other than str. op.setAttribute("username", str(self.operation_username)) op.setAttribute("userid", str(self.operation_userid)) op.setAttribute("result", ", ".join(self.operation_result)) op.setAttribute("start_time", self.operation_start_time) op.setAttribute("end_time", self.operation_end_time) if self.operation_be: op.setAttribute("be", self.operation_be) if self.operation_be_uuid: op.setAttribute("be_uuid", self.operation_be_uuid) if self.operation_new_be: op.setAttribute("new_be", self.operation_new_be) if self.operation_new_be_uuid: op.setAttribute("new_be_uuid", self.operation_new_be_uuid) if self.operation_snapshot: op.setAttribute("snapshot", self.operation_snapshot) if self.operation_release_notes: op.setAttribute("release-notes", self.operation_release_notes) root.appendChild(op) if self.operation_start_state: state = d.createElement("start_state") op.appendChild(state) state.appendChild( d.createCDATASection(str(self.operation_start_state))) if self.operation_end_state: state = d.createElement("end_state") op.appendChild(state) state.appendChild( d.createCDATASection(str(self.operation_end_state))) if self.operation_errors: errors = d.createElement("errors") op.appendChild(errors) for entry in self.operation_errors: error = d.createElement("error") errors.appendChild(error) error.appendChild(d.createCDATASection(str(entry)))