Package CedarBackup2 :: Package extend :: Module sysinfo
[hide private]
[frames] | no frames]

Source Code for Module CedarBackup2.extend.sysinfo

  1  # -*- coding: iso-8859-1 -*- 
  2  # vim: set ft=python ts=3 sw=3 expandtab: 
  3  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
  4  # 
  5  #              C E D A R 
  6  #          S O L U T I O N S       "Software done right." 
  7  #           S O F T W A R E 
  8  # 
  9  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
 10  # 
 11  # Copyright (c) 2005,2010 Kenneth J. Pronovici. 
 12  # All rights reserved. 
 13  # 
 14  # This program is free software; you can redistribute it and/or 
 15  # modify it under the terms of the GNU General Public License, 
 16  # Version 2, as published by the Free Software Foundation. 
 17  # 
 18  # This program is distributed in the hope that it will be useful, 
 19  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 20  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 21  # 
 22  # Copies of the GNU General Public License are available from 
 23  # the Free Software Foundation website, http://www.gnu.org/. 
 24  # 
 25  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
 26  # 
 27  # Author   : Kenneth J. Pronovici <pronovic@ieee.org> 
 28  # Language : Python (>= 2.5) 
 29  # Project  : Official Cedar Backup Extensions 
 30  # Revision : $Id: sysinfo.py 1006 2010-07-07 21:03:57Z pronovic $ 
 31  # Purpose  : Provides an extension to save off important system recovery information. 
 32  # 
 33  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
 34   
 35  ######################################################################## 
 36  # Module documentation 
 37  ######################################################################## 
 38   
 39  """ 
 40  Provides an extension to save off important system recovery information. 
 41   
 42  This is a simple Cedar Backup extension used to save off important system 
 43  recovery information.  It saves off three types of information: 
 44   
 45     - Currently-installed Debian packages via C{dpkg --get-selections} 
 46     - Disk partition information via C{fdisk -l} 
 47     - System-wide mounted filesystem contents, via C{ls -laR} 
 48   
 49  The saved-off information is placed into the collect directory and is 
 50  compressed using C{bzip2} to save space. 
 51   
 52  This extension relies on the options and collect configurations in the standard 
 53  Cedar Backup configuration file, but requires no new configuration of its own. 
 54  No public functions other than the action are exposed since all of this is 
 55  pretty simple. 
 56   
 57  @note: If the C{dpkg} or C{fdisk} commands cannot be found in their normal 
 58  locations or executed by the current user, those steps will be skipped and a 
 59  note will be logged at the INFO level. 
 60   
 61  @author: Kenneth J. Pronovici <pronovic@ieee.org> 
 62  """ 
 63   
 64  ######################################################################## 
 65  # Imported modules 
 66  ######################################################################## 
 67   
 68  # System modules 
 69  import os 
 70  import logging 
 71  from bz2 import BZ2File 
 72   
 73  # Cedar Backup modules 
 74  from CedarBackup2.util import resolveCommand, executeCommand, changeOwnership 
 75   
 76   
 77  ######################################################################## 
 78  # Module-wide constants and variables 
 79  ######################################################################## 
 80   
 81  logger = logging.getLogger("CedarBackup2.log.extend.sysinfo") 
 82   
 83  DPKG_PATH      = "/usr/bin/dpkg" 
 84  FDISK_PATH     = "/sbin/fdisk" 
 85   
 86  DPKG_COMMAND   = [ DPKG_PATH, "--get-selections", ] 
 87  FDISK_COMMAND  = [ FDISK_PATH, "-l", ] 
 88  LS_COMMAND     = [ "ls", "-laR", "/", ] 
 89   
 90   
 91  ######################################################################## 
 92  # Public functions 
 93  ######################################################################## 
 94   
 95  ########################### 
 96  # executeAction() function 
 97  ########################### 
 98   
99 -def executeAction(configPath, options, config):
100 """ 101 Executes the sysinfo backup action. 102 103 @param configPath: Path to configuration file on disk. 104 @type configPath: String representing a path on disk. 105 106 @param options: Program command-line options. 107 @type options: Options object. 108 109 @param config: Program configuration. 110 @type config: Config object. 111 112 @raise ValueError: Under many generic error conditions 113 @raise IOError: If the backup process fails for some reason. 114 """ 115 logger.debug("Executing sysinfo extended action.") 116 if config.options is None or config.collect is None: 117 raise ValueError("Cedar Backup configuration is not properly filled in.") 118 _dumpDebianPackages(config.collect.targetDir, config.options.backupUser, config.options.backupGroup) 119 _dumpPartitionTable(config.collect.targetDir, config.options.backupUser, config.options.backupGroup) 120 _dumpFilesystemContents(config.collect.targetDir, config.options.backupUser, config.options.backupGroup) 121 logger.info("Executed the sysinfo extended action successfully.")
122
123 -def _dumpDebianPackages(targetDir, backupUser, backupGroup, compress=True):
124 """ 125 Dumps a list of currently installed Debian packages via C{dpkg}. 126 @param targetDir: Directory to write output file into. 127 @param backupUser: User which should own the resulting file. 128 @param backupGroup: Group which should own the resulting file. 129 @param compress: Indicates whether to compress the output file. 130 @raise IOError: If the dump fails for some reason. 131 """ 132 if not os.path.exists(DPKG_PATH): 133 logger.info("Not executing Debian package dump since %s doesn't seem to exist." % DPKG_PATH) 134 elif not os.access(DPKG_PATH, os.X_OK): 135 logger.info("Not executing Debian package dump since %s cannot be executed." % DPKG_PATH) 136 else: 137 (outputFile, filename) = _getOutputFile(targetDir, "dpkg-selections", compress) 138 try: 139 command = resolveCommand(DPKG_COMMAND) 140 result = executeCommand(command, [], returnOutput=False, ignoreStderr=True, doNotLog=True, outputFile=outputFile)[0] 141 if result != 0: 142 raise IOError("Error [%d] executing Debian package dump." % result) 143 finally: 144 outputFile.close() 145 if not os.path.exists(filename): 146 raise IOError("File [%s] does not seem to exist after Debian package dump finished." % filename) 147 changeOwnership(filename, backupUser, backupGroup)
148
149 -def _dumpPartitionTable(targetDir, backupUser, backupGroup, compress=True):
150 """ 151 Dumps information about the partition table via C{fdisk}. 152 @param targetDir: Directory to write output file into. 153 @param backupUser: User which should own the resulting file. 154 @param backupGroup: Group which should own the resulting file. 155 @param compress: Indicates whether to compress the output file. 156 @raise IOError: If the dump fails for some reason. 157 """ 158 if not os.path.exists(FDISK_PATH): 159 logger.info("Not executing partition table dump since %s doesn't seem to exist." % FDISK_PATH) 160 elif not os.access(FDISK_PATH, os.X_OK): 161 logger.info("Not executing partition table dump since %s cannot be executed." % FDISK_PATH) 162 else: 163 (outputFile, filename) = _getOutputFile(targetDir, "fdisk-l", compress) 164 try: 165 command = resolveCommand(FDISK_COMMAND) 166 result = executeCommand(command, [], returnOutput=False, ignoreStderr=True, outputFile=outputFile)[0] 167 if result != 0: 168 raise IOError("Error [%d] executing partition table dump." % result) 169 finally: 170 outputFile.close() 171 if not os.path.exists(filename): 172 raise IOError("File [%s] does not seem to exist after partition table dump finished." % filename) 173 changeOwnership(filename, backupUser, backupGroup)
174
175 -def _dumpFilesystemContents(targetDir, backupUser, backupGroup, compress=True):
176 """ 177 Dumps complete listing of filesystem contents via C{ls -laR}. 178 @param targetDir: Directory to write output file into. 179 @param backupUser: User which should own the resulting file. 180 @param backupGroup: Group which should own the resulting file. 181 @param compress: Indicates whether to compress the output file. 182 @raise IOError: If the dump fails for some reason. 183 """ 184 (outputFile, filename) = _getOutputFile(targetDir, "ls-laR", compress) 185 try: 186 # Note: can't count on return status from 'ls', so we don't check it. 187 command = resolveCommand(LS_COMMAND) 188 executeCommand(command, [], returnOutput=False, ignoreStderr=True, doNotLog=True, outputFile=outputFile) 189 finally: 190 outputFile.close() 191 if not os.path.exists(filename): 192 raise IOError("File [%s] does not seem to exist after filesystem contents dump finished." % filename) 193 changeOwnership(filename, backupUser, backupGroup)
194
195 -def _getOutputFile(targetDir, name, compress=True):
196 """ 197 Opens the output file used for saving a dump to the filesystem. 198 199 The filename will be C{name.txt} (or C{name.txt.bz2} if C{compress} is 200 C{True}), written in the target directory. 201 202 @param targetDir: Target directory to write file in. 203 @param name: Name of the file to create. 204 @param compress: Indicates whether to write compressed output. 205 206 @return: Tuple of (Output file object, filename) 207 """ 208 filename = os.path.join(targetDir, "%s.txt" % name) 209 if compress: 210 filename = "%s.bz2" % filename 211 logger.debug("Dump file will be [%s]." % filename) 212 if compress: 213 outputFile = BZ2File(filename, "w") 214 else: 215 outputFile = open(filename, "w") 216 return (outputFile, filename)
217