Package CedarBackup3 :: Package extend :: Module mysql
[hide private]
[frames] | no frames]

Source Code for Module CedarBackup3.extend.mysql

  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,2015 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 3 (>= 3.4) 
 29  # Project  : Official Cedar Backup Extensions 
 30  # Purpose  : Provides an extension to back up MySQL databases. 
 31  # 
 32  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
 33   
 34  ######################################################################## 
 35  # Module documentation 
 36  ######################################################################## 
 37   
 38  """ 
 39  Provides an extension to back up MySQL databases. 
 40   
 41  This is a Cedar Backup extension used to back up MySQL databases via the Cedar 
 42  Backup command line.  It requires a new configuration section <mysql> and is 
 43  intended to be run either immediately before or immediately after the standard 
 44  collect action.  Aside from its own configuration, it requires the options and 
 45  collect configuration sections in the standard Cedar Backup configuration file. 
 46   
 47  The backup is done via the C{mysqldump} command included with the MySQL 
 48  product.  Output can be compressed using C{gzip} or C{bzip2}.  Administrators 
 49  can configure the extension either to back up all databases or to back up only 
 50  specific databases.  Note that this code always produces a full backup.  There 
 51  is currently no facility for making incremental backups.  If/when someone has a 
 52  need for this and can describe how to do it, I'll update this extension or 
 53  provide another. 
 54   
 55  The extension assumes that all configured databases can be backed up by a 
 56  single user.  Often, the "root" database user will be used.  An alternative is 
 57  to create a separate MySQL "backup" user and grant that user rights to read 
 58  (but not write) various databases as needed.  This second option is probably 
 59  the best choice. 
 60   
 61  The extension accepts a username and password in configuration.  However, you 
 62  probably do not want to provide those values in Cedar Backup configuration. 
 63  This is because Cedar Backup will provide these values to C{mysqldump} via the 
 64  command-line C{--user} and C{--password} switches, which will be visible to 
 65  other users in the process listing. 
 66   
 67  Instead, you should configure the username and password in one of MySQL's 
 68  configuration files.  Typically, that would be done by putting a stanza like 
 69  this in C{/root/.my.cnf}:: 
 70   
 71     [mysqldump] 
 72     user     = root 
 73     password = <secret> 
 74   
 75  Regardless of whether you are using C{~/.my.cnf} or C{/etc/cback3.conf} to store 
 76  database login and password information, you should be careful about who is 
 77  allowed to view that information.  Typically, this means locking down 
 78  permissions so that only the file owner can read the file contents (i.e. use 
 79  mode C{0600}). 
 80   
 81  @author: Kenneth J. Pronovici <pronovic@ieee.org> 
 82  """ 
 83   
 84  ######################################################################## 
 85  # Imported modules 
 86  ######################################################################## 
 87   
 88  # System modules 
 89  import os 
 90  import logging 
 91  from gzip import GzipFile 
 92  from bz2 import BZ2File 
 93  from functools import total_ordering 
 94   
 95  # Cedar Backup modules 
 96  from CedarBackup3.xmlutil import createInputDom, addContainerNode, addStringNode, addBooleanNode 
 97  from CedarBackup3.xmlutil import readFirstChild, readString, readStringList, readBoolean 
 98  from CedarBackup3.config import VALID_COMPRESS_MODES 
 99  from CedarBackup3.util import resolveCommand, executeCommand 
100  from CedarBackup3.util import ObjectTypeList, changeOwnership 
101   
102   
103  ######################################################################## 
104  # Module-wide constants and variables 
105  ######################################################################## 
106   
107  logger = logging.getLogger("CedarBackup3.log.extend.mysql") 
108  MYSQLDUMP_COMMAND = [ "mysqldump", ] 
109 110 111 ######################################################################## 112 # MysqlConfig class definition 113 ######################################################################## 114 115 @total_ordering 116 -class MysqlConfig(object):
117 118 """ 119 Class representing MySQL configuration. 120 121 The MySQL configuration information is used for backing up MySQL databases. 122 123 The following restrictions exist on data in this class: 124 125 - The compress mode must be one of the values in L{VALID_COMPRESS_MODES}. 126 - The 'all' flag must be 'Y' if no databases are defined. 127 - The 'all' flag must be 'N' if any databases are defined. 128 - Any values in the databases list must be strings. 129 130 @sort: __init__, __repr__, __str__, __cmp__, __eq__, __lt__, __gt__, user, 131 password, all, databases 132 """ 133
134 - def __init__(self, user=None, password=None, compressMode=None, all=None, databases=None): # pylint: disable=W0622
135 """ 136 Constructor for the C{MysqlConfig} class. 137 138 @param user: User to execute backup as. 139 @param password: Password associated with user. 140 @param compressMode: Compress mode for backed-up files. 141 @param all: Indicates whether to back up all databases. 142 @param databases: List of databases to back up. 143 """ 144 self._user = None 145 self._password = None 146 self._compressMode = None 147 self._all = None 148 self._databases = None 149 self.user = user 150 self.password = password 151 self.compressMode = compressMode 152 self.all = all 153 self.databases = databases
154
155 - def __repr__(self):
156 """ 157 Official string representation for class instance. 158 """ 159 return "MysqlConfig(%s, %s, %s, %s)" % (self.user, self.password, self.all, self.databases)
160
161 - def __str__(self):
162 """ 163 Informal string representation for class instance. 164 """ 165 return self.__repr__()
166
167 - def __eq__(self, other):
168 """Equals operator, iplemented in terms of original Python 2 compare operator.""" 169 return self.__cmp__(other) == 0
170
171 - def __lt__(self, other):
172 """Less-than operator, iplemented in terms of original Python 2 compare operator.""" 173 return self.__cmp__(other) < 0
174
175 - def __gt__(self, other):
176 """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" 177 return self.__cmp__(other) > 0
178
179 - def __cmp__(self, other):
180 """ 181 Original Python 2 comparison operator. 182 @param other: Other object to compare to. 183 @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other. 184 """ 185 if other is None: 186 return 1 187 if self.user != other.user: 188 if str(self.user or "") < str(other.user or ""): 189 return -1 190 else: 191 return 1 192 if self.password != other.password: 193 if str(self.password or "") < str(other.password or ""): 194 return -1 195 else: 196 return 1 197 if self.compressMode != other.compressMode: 198 if str(self.compressMode or "") < str(other.compressMode or ""): 199 return -1 200 else: 201 return 1 202 if self.all != other.all: 203 if self.all < other.all: 204 return -1 205 else: 206 return 1 207 if self.databases != other.databases: 208 if self.databases < other.databases: 209 return -1 210 else: 211 return 1 212 return 0
213
214 - def _setUser(self, value):
215 """ 216 Property target used to set the user value. 217 """ 218 if value is not None: 219 if len(value) < 1: 220 raise ValueError("User must be non-empty string.") 221 self._user = value
222
223 - def _getUser(self):
224 """ 225 Property target used to get the user value. 226 """ 227 return self._user
228
229 - def _setPassword(self, value):
230 """ 231 Property target used to set the password value. 232 """ 233 if value is not None: 234 if len(value) < 1: 235 raise ValueError("Password must be non-empty string.") 236 self._password = value
237
238 - def _getPassword(self):
239 """ 240 Property target used to get the password value. 241 """ 242 return self._password
243
244 - def _setCompressMode(self, value):
245 """ 246 Property target used to set the compress mode. 247 If not C{None}, the mode must be one of the values in L{VALID_COMPRESS_MODES}. 248 @raise ValueError: If the value is not valid. 249 """ 250 if value is not None: 251 if value not in VALID_COMPRESS_MODES: 252 raise ValueError("Compress mode must be one of %s." % VALID_COMPRESS_MODES) 253 self._compressMode = value
254
255 - def _getCompressMode(self):
256 """ 257 Property target used to get the compress mode. 258 """ 259 return self._compressMode
260
261 - def _setAll(self, value):
262 """ 263 Property target used to set the 'all' flag. 264 No validations, but we normalize the value to C{True} or C{False}. 265 """ 266 if value: 267 self._all = True 268 else: 269 self._all = False
270
271 - def _getAll(self):
272 """ 273 Property target used to get the 'all' flag. 274 """ 275 return self._all
276
277 - def _setDatabases(self, value):
278 """ 279 Property target used to set the databases list. 280 Either the value must be C{None} or each element must be a string. 281 @raise ValueError: If the value is not a string. 282 """ 283 if value is None: 284 self._databases = None 285 else: 286 for database in value: 287 if len(database) < 1: 288 raise ValueError("Each database must be a non-empty string.") 289 try: 290 saved = self._databases 291 self._databases = ObjectTypeList(str, "string") 292 self._databases.extend(value) 293 except Exception as e: 294 self._databases = saved 295 raise e
296
297 - def _getDatabases(self):
298 """ 299 Property target used to get the databases list. 300 """ 301 return self._databases
302 303 user = property(_getUser, _setUser, None, "User to execute backup as.") 304 password = property(_getPassword, _setPassword, None, "Password associated with user.") 305 compressMode = property(_getCompressMode, _setCompressMode, None, "Compress mode to be used for backed-up files.") 306 all = property(_getAll, _setAll, None, "Indicates whether to back up all databases.") 307 databases = property(_getDatabases, _setDatabases, None, "List of databases to back up.") 308
309 310 ######################################################################## 311 # LocalConfig class definition 312 ######################################################################## 313 314 @total_ordering 315 -class LocalConfig(object):
316 317 """ 318 Class representing this extension's configuration document. 319 320 This is not a general-purpose configuration object like the main Cedar 321 Backup configuration object. Instead, it just knows how to parse and emit 322 MySQL-specific configuration values. Third parties who need to read and 323 write configuration related to this extension should access it through the 324 constructor, C{validate} and C{addConfig} methods. 325 326 @note: Lists within this class are "unordered" for equality comparisons. 327 328 @sort: __init__, __repr__, __str__, __cmp__, __eq__, __lt__, __gt__, mysql, 329 validate, addConfig 330 """ 331
332 - def __init__(self, xmlData=None, xmlPath=None, validate=True):
333 """ 334 Initializes a configuration object. 335 336 If you initialize the object without passing either C{xmlData} or 337 C{xmlPath} then configuration will be empty and will be invalid until it 338 is filled in properly. 339 340 No reference to the original XML data or original path is saved off by 341 this class. Once the data has been parsed (successfully or not) this 342 original information is discarded. 343 344 Unless the C{validate} argument is C{False}, the L{LocalConfig.validate} 345 method will be called (with its default arguments) against configuration 346 after successfully parsing any passed-in XML. Keep in mind that even if 347 C{validate} is C{False}, it might not be possible to parse the passed-in 348 XML document if lower-level validations fail. 349 350 @note: It is strongly suggested that the C{validate} option always be set 351 to C{True} (the default) unless there is a specific need to read in 352 invalid configuration from disk. 353 354 @param xmlData: XML data representing configuration. 355 @type xmlData: String data. 356 357 @param xmlPath: Path to an XML file on disk. 358 @type xmlPath: Absolute path to a file on disk. 359 360 @param validate: Validate the document after parsing it. 361 @type validate: Boolean true/false. 362 363 @raise ValueError: If both C{xmlData} and C{xmlPath} are passed-in. 364 @raise ValueError: If the XML data in C{xmlData} or C{xmlPath} cannot be parsed. 365 @raise ValueError: If the parsed configuration document is not valid. 366 """ 367 self._mysql = None 368 self.mysql = None 369 if xmlData is not None and xmlPath is not None: 370 raise ValueError("Use either xmlData or xmlPath, but not both.") 371 if xmlData is not None: 372 self._parseXmlData(xmlData) 373 if validate: 374 self.validate() 375 elif xmlPath is not None: 376 with open(xmlPath) as f: 377 xmlData = f.read() 378 self._parseXmlData(xmlData) 379 if validate: 380 self.validate()
381
382 - def __repr__(self):
383 """ 384 Official string representation for class instance. 385 """ 386 return "LocalConfig(%s)" % (self.mysql)
387
388 - def __str__(self):
389 """ 390 Informal string representation for class instance. 391 """ 392 return self.__repr__()
393
394 - def __eq__(self, other):
395 """Equals operator, iplemented in terms of original Python 2 compare operator.""" 396 return self.__cmp__(other) == 0
397
398 - def __lt__(self, other):
399 """Less-than operator, iplemented in terms of original Python 2 compare operator.""" 400 return self.__cmp__(other) < 0
401
402 - def __gt__(self, other):
403 """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" 404 return self.__cmp__(other) > 0
405
406 - def __cmp__(self, other):
407 """ 408 Original Python 2 comparison operator. 409 Lists within this class are "unordered" for equality comparisons. 410 @param other: Other object to compare to. 411 @return: -1/0/1 depending on whether self is C{<}, C{=} or C{>} other. 412 """ 413 if other is None: 414 return 1 415 if self.mysql != other.mysql: 416 if self.mysql < other.mysql: 417 return -1 418 else: 419 return 1 420 return 0
421
422 - def _setMysql(self, value):
423 """ 424 Property target used to set the mysql configuration value. 425 If not C{None}, the value must be a C{MysqlConfig} object. 426 @raise ValueError: If the value is not a C{MysqlConfig} 427 """ 428 if value is None: 429 self._mysql = None 430 else: 431 if not isinstance(value, MysqlConfig): 432 raise ValueError("Value must be a C{MysqlConfig} object.") 433 self._mysql = value
434
435 - def _getMysql(self):
436 """ 437 Property target used to get the mysql configuration value. 438 """ 439 return self._mysql
440 441 mysql = property(_getMysql, _setMysql, None, "Mysql configuration in terms of a C{MysqlConfig} object.") 442
443 - def validate(self):
444 """ 445 Validates configuration represented by the object. 446 447 The compress mode must be filled in. Then, if the 'all' flag I{is} set, 448 no databases are allowed, and if the 'all' flag is I{not} set, at least 449 one database is required. 450 451 @raise ValueError: If one of the validations fails. 452 """ 453 if self.mysql is None: 454 raise ValueError("Mysql section is required.") 455 if self.mysql.compressMode is None: 456 raise ValueError("Compress mode value is required.") 457 if self.mysql.all: 458 if self.mysql.databases is not None and self.mysql.databases != []: 459 raise ValueError("Databases cannot be specified if 'all' flag is set.") 460 else: 461 if self.mysql.databases is None or len(self.mysql.databases) < 1: 462 raise ValueError("At least one MySQL database must be indicated if 'all' flag is not set.")
463
464 - def addConfig(self, xmlDom, parentNode):
465 """ 466 Adds a <mysql> configuration section as the next child of a parent. 467 468 Third parties should use this function to write configuration related to 469 this extension. 470 471 We add the following fields to the document:: 472 473 user //cb_config/mysql/user 474 password //cb_config/mysql/password 475 compressMode //cb_config/mysql/compress_mode 476 all //cb_config/mysql/all 477 478 We also add groups of the following items, one list element per 479 item:: 480 481 database //cb_config/mysql/database 482 483 @param xmlDom: DOM tree as from C{impl.createDocument()}. 484 @param parentNode: Parent that the section should be appended to. 485 """ 486 if self.mysql is not None: 487 sectionNode = addContainerNode(xmlDom, parentNode, "mysql") 488 addStringNode(xmlDom, sectionNode, "user", self.mysql.user) 489 addStringNode(xmlDom, sectionNode, "password", self.mysql.password) 490 addStringNode(xmlDom, sectionNode, "compress_mode", self.mysql.compressMode) 491 addBooleanNode(xmlDom, sectionNode, "all", self.mysql.all) 492 if self.mysql.databases is not None: 493 for database in self.mysql.databases: 494 addStringNode(xmlDom, sectionNode, "database", database)
495
496 - def _parseXmlData(self, xmlData):
497 """ 498 Internal method to parse an XML string into the object. 499 500 This method parses the XML document into a DOM tree (C{xmlDom}) and then 501 calls a static method to parse the mysql configuration section. 502 503 @param xmlData: XML data to be parsed 504 @type xmlData: String data 505 506 @raise ValueError: If the XML cannot be successfully parsed. 507 """ 508 (xmlDom, parentNode) = createInputDom(xmlData) 509 self._mysql = LocalConfig._parseMysql(parentNode)
510 511 @staticmethod
512 - def _parseMysql(parentNode):
513 """ 514 Parses a mysql configuration section. 515 516 We read the following fields:: 517 518 user //cb_config/mysql/user 519 password //cb_config/mysql/password 520 compressMode //cb_config/mysql/compress_mode 521 all //cb_config/mysql/all 522 523 We also read groups of the following item, one list element per 524 item:: 525 526 databases //cb_config/mysql/database 527 528 @param parentNode: Parent node to search beneath. 529 530 @return: C{MysqlConfig} object or C{None} if the section does not exist. 531 @raise ValueError: If some filled-in value is invalid. 532 """ 533 mysql = None 534 section = readFirstChild(parentNode, "mysql") 535 if section is not None: 536 mysql = MysqlConfig() 537 mysql.user = readString(section, "user") 538 mysql.password = readString(section, "password") 539 mysql.compressMode = readString(section, "compress_mode") 540 mysql.all = readBoolean(section, "all") 541 mysql.databases = readStringList(section, "database") 542 return mysql
543
544 545 ######################################################################## 546 # Public functions 547 ######################################################################## 548 549 ########################### 550 # executeAction() function 551 ########################### 552 553 -def executeAction(configPath, options, config):
554 """ 555 Executes the MySQL backup action. 556 557 @param configPath: Path to configuration file on disk. 558 @type configPath: String representing a path on disk. 559 560 @param options: Program command-line options. 561 @type options: Options object. 562 563 @param config: Program configuration. 564 @type config: Config object. 565 566 @raise ValueError: Under many generic error conditions 567 @raise IOError: If a backup could not be written for some reason. 568 """ 569 logger.debug("Executing MySQL extended action.") 570 if config.options is None or config.collect is None: 571 raise ValueError("Cedar Backup configuration is not properly filled in.") 572 local = LocalConfig(xmlPath=configPath) 573 if local.mysql.all: 574 logger.info("Backing up all databases.") 575 _backupDatabase(config.collect.targetDir, local.mysql.compressMode, local.mysql.user, local.mysql.password, 576 config.options.backupUser, config.options.backupGroup, None) 577 else: 578 logger.debug("Backing up %d individual databases.", len(local.mysql.databases)) 579 for database in local.mysql.databases: 580 logger.info("Backing up database [%s].", database) 581 _backupDatabase(config.collect.targetDir, local.mysql.compressMode, local.mysql.user, local.mysql.password, 582 config.options.backupUser, config.options.backupGroup, database) 583 logger.info("Executed the MySQL extended action successfully.")
584
585 -def _backupDatabase(targetDir, compressMode, user, password, backupUser, backupGroup, database=None):
586 """ 587 Backs up an individual MySQL database, or all databases. 588 589 This internal method wraps the public method and adds some functionality, 590 like figuring out a filename, etc. 591 592 @param targetDir: Directory into which backups should be written. 593 @param compressMode: Compress mode to be used for backed-up files. 594 @param user: User to use for connecting to the database (if any). 595 @param password: Password associated with user (if any). 596 @param backupUser: User to own resulting file. 597 @param backupGroup: Group to own resulting file. 598 @param database: Name of database, or C{None} for all databases. 599 600 @return: Name of the generated backup file. 601 602 @raise ValueError: If some value is missing or invalid. 603 @raise IOError: If there is a problem executing the MySQL dump. 604 """ 605 (outputFile, filename) = _getOutputFile(targetDir, database, compressMode) 606 with outputFile: 607 backupDatabase(user, password, outputFile, database) 608 if not os.path.exists(filename): 609 raise IOError("Dump file [%s] does not seem to exist after backup completed." % filename) 610 changeOwnership(filename, backupUser, backupGroup)
611
612 #pylint: disable=R0204 613 -def _getOutputFile(targetDir, database, compressMode):
614 """ 615 Opens the output file used for saving the MySQL dump. 616 617 The filename is either C{"mysqldump.txt"} or C{"mysqldump-<database>.txt"}. The 618 C{".bz2"} extension is added if C{compress} is C{True}. 619 620 @param targetDir: Target directory to write file in. 621 @param database: Name of the database (if any) 622 @param compressMode: Compress mode to be used for backed-up files. 623 624 @return: Tuple of (Output file object, filename), file opened in binary mode for use with executeCommand() 625 """ 626 if database is None: 627 filename = os.path.join(targetDir, "mysqldump.txt") 628 else: 629 filename = os.path.join(targetDir, "mysqldump-%s.txt" % database) 630 if compressMode == "gzip": 631 filename = "%s.gz" % filename 632 outputFile = GzipFile(filename, "wb") 633 elif compressMode == "bzip2": 634 filename = "%s.bz2" % filename 635 outputFile = BZ2File(filename, "wb") 636 else: 637 outputFile = open(filename, "wb") 638 logger.debug("MySQL dump file will be [%s].", filename) 639 return (outputFile, filename)
640
641 642 ############################ 643 # backupDatabase() function 644 ############################ 645 646 -def backupDatabase(user, password, backupFile, database=None):
647 """ 648 Backs up an individual MySQL database, or all databases. 649 650 This function backs up either a named local MySQL database or all local 651 MySQL databases, using the passed-in user and password (if provided) for 652 connectivity. This function call I{always} results a full backup. There is 653 no facility for incremental backups. 654 655 The backup data will be written into the passed-in backup file. Normally, 656 this would be an object as returned from C{open()}, but it is possible to 657 use something like a C{GzipFile} to write compressed output. The caller is 658 responsible for closing the passed-in backup file. 659 660 Often, the "root" database user will be used when backing up all databases. 661 An alternative is to create a separate MySQL "backup" user and grant that 662 user rights to read (but not write) all of the databases that will be backed 663 up. 664 665 This function accepts a username and password. However, you probably do not 666 want to pass those values in. This is because they will be provided to 667 C{mysqldump} via the command-line C{--user} and C{--password} switches, 668 which will be visible to other users in the process listing. 669 670 Instead, you should configure the username and password in one of MySQL's 671 configuration files. Typically, this would be done by putting a stanza like 672 this in C{/root/.my.cnf}, to provide C{mysqldump} with the root database 673 username and its password:: 674 675 [mysqldump] 676 user = root 677 password = <secret> 678 679 If you are executing this function as some system user other than root, then 680 the C{.my.cnf} file would be placed in the home directory of that user. In 681 either case, make sure to set restrictive permissions (typically, mode 682 C{0600}) on C{.my.cnf} to make sure that other users cannot read the file. 683 684 @param user: User to use for connecting to the database (if any) 685 @type user: String representing MySQL username, or C{None} 686 687 @param password: Password associated with user (if any) 688 @type password: String representing MySQL password, or C{None} 689 690 @param backupFile: File use for writing backup. 691 @type backupFile: Python file object as from C{open()} or C{file()}. 692 693 @param database: Name of the database to be backed up. 694 @type database: String representing database name, or C{None} for all databases. 695 696 @raise ValueError: If some value is missing or invalid. 697 @raise IOError: If there is a problem executing the MySQL dump. 698 """ 699 args = [ "-all", "--flush-logs", "--opt", ] 700 if user is not None: 701 logger.warning("Warning: MySQL username will be visible in process listing (consider using ~/.my.cnf).") 702 args.append("--user=%s" % user) 703 if password is not None: 704 logger.warning("Warning: MySQL password will be visible in process listing (consider using ~/.my.cnf).") 705 args.append("--password=%s" % password) 706 if database is None: 707 args.insert(0, "--all-databases") 708 else: 709 args.insert(0, "--databases") 710 args.append(database) 711 command = resolveCommand(MYSQLDUMP_COMMAND) 712 result = executeCommand(command, args, returnOutput=False, ignoreStderr=True, doNotLog=True, outputFile=backupFile)[0] 713 if result != 0: 714 if database is None: 715 raise IOError("Error [%d] executing MySQL database dump for all databases." % result) 716 else: 717 raise IOError("Error [%d] executing MySQL database dump for database [%s]." % (result, database))
718