• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pronovic / cedar-backup3 / 17816521225

18 Sep 2025 02:53AM UTC coverage: 73.39% (+0.08%) from 73.311%
17816521225

Pull #56

github

web-flow
Merge 227681f67 into 50daef4a0
Pull Request #56: Address all Ruff linter errors, either with code changes or exclusions

173 of 375 new or added lines in 29 files covered. (46.13%)

2 existing lines in 2 files now uncovered.

7907 of 10774 relevant lines covered (73.39%)

2.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.93
/src/CedarBackup3/extend/postgresql.py
1
# vim: set ft=python ts=4 sw=4 expandtab:
2
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
3
#
4
#              C E D A R
5
#          S O L U T I O N S       "Software done right."
6
#           S O F T W A R E
7
#
8
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
9
#
10
# Copyright (c) 2006,2010,2015 Kenneth J. Pronovici.
11
# Copyright (c) 2006 Antoine Beaupre.
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
#            Antoine Beaupre <anarcat@koumbit.org>
29
# Language : Python 3
30
# Project  : Official Cedar Backup Extensions
31
# Purpose  : Provides an extension to back up PostgreSQL databases.
32
#
33
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
34
# This file was created with a width of 132 characters, and NO tabs.
35

36
########################################################################
37
# Module documentation
38
########################################################################
39

40
"""
41
Provides an extension to back up PostgreSQL databases.
42

43
This is a Cedar Backup extension used to back up PostgreSQL databases via the
44
Cedar Backup command line.  It requires a new configurations section
45
<postgresql> and is intended to be run either immediately before or immediately
46
after the standard collect action.  Aside from its own configuration, it
47
requires the options and collect configuration sections in the standard Cedar
48
Backup configuration file.
49

50
The backup is done via the ``pg_dump`` or ``pg_dumpall`` commands included with
51
the PostgreSQL product.  Output can be compressed using ``gzip`` or ``bzip2``.
52
Administrators can configure the extension either to back up all databases or
53
to back up only specific databases.  The extension assumes that the current
54
user has passwordless access to the database since there is no easy way to pass
55
a password to the ``pg_dump`` client. This can be accomplished using appropriate
56
voodoo in the ``pg_hda.conf`` file.
57

58
Note that this code always produces a full backup.  There is currently no
59
facility for making incremental backups.
60

61
You should always make ``/etc/cback3.conf`` unreadble to non-root users once you
62
place postgresql configuration into it, since postgresql configuration will
63
contain information about available PostgreSQL databases and usernames.
64

65
Use of this extension *may* expose usernames in the process listing (via
66
``ps``) when the backup is running if the username is specified in the
67
configuration.
68

69
:author: Kenneth J. Pronovici <pronovic@ieee.org>
70
:author: Antoine Beaupre <anarcat@koumbit.org>
71
"""
72

73
########################################################################
74
# Imported modules
75
########################################################################
76

77
import logging
4✔
78
import os
4✔
79
from bz2 import BZ2File
4✔
80
from functools import total_ordering
4✔
81
from gzip import GzipFile
4✔
82

83
from CedarBackup3.config import VALID_COMPRESS_MODES
4✔
84
from CedarBackup3.util import ObjectTypeList, changeOwnership, executeCommand, pathJoin, resolveCommand
4✔
85
from CedarBackup3.xmlutil import (
4✔
86
    addBooleanNode,
87
    addContainerNode,
88
    addStringNode,
89
    createInputDom,
90
    readBoolean,
91
    readFirstChild,
92
    readString,
93
    readStringList,
94
)
95

96
########################################################################
97
# Module-wide constants and variables
98
########################################################################
99

100
logger = logging.getLogger("CedarBackup3.log.extend.postgresql")
4✔
101
POSTGRESQLDUMP_COMMAND = ["pg_dump"]
4✔
102
POSTGRESQLDUMPALL_COMMAND = ["pg_dumpall"]
4✔
103

104

105
########################################################################
106
# PostgresqlConfig class definition
107
########################################################################
108

109

110
@total_ordering
4✔
111
class PostgresqlConfig:
4✔
112
    """
113
    Class representing PostgreSQL configuration.
114

115
    The PostgreSQL configuration information is used for backing up PostgreSQL databases.
116

117
    The following restrictions exist on data in this class:
118

119
       - The compress mode must be one of the values in :any:`VALID_COMPRESS_MODES`.
120
       - The 'all' flag must be 'Y' if no databases are defined.
121
       - The 'all' flag must be 'N' if any databases are defined.
122
       - Any values in the databases list must be strings.
123

124
    """
125

126
    def __init__(self, user=None, compressMode=None, all=None, databases=None):  # noqa: A002
4✔
127
        """
128
        Constructor for the ``PostgresqlConfig`` class.
129

130
        Args:
131
           user: User to execute backup as
132
           compressMode: Compress mode for backed-up files
133
           all: Indicates whether to back up all databases
134
           databases: List of databases to back up
135
        """
136
        self._user = None
4✔
137
        self._compressMode = None
4✔
138
        self._all = None
4✔
139
        self._databases = None
4✔
140
        self.user = user
4✔
141
        self.compressMode = compressMode
4✔
142
        self.all = all
4✔
143
        self.databases = databases
4✔
144

145
    def __repr__(self):
4✔
146
        """
147
        Official string representation for class instance.
148
        """
149
        return "PostgresqlConfig(%s, %s, %s)" % (self.user, self.all, self.databases)
4✔
150

151
    def __str__(self):
4✔
152
        """
153
        Informal string representation for class instance.
154
        """
155
        return self.__repr__()
4✔
156

157
    def __eq__(self, other):
4✔
158
        """Equals operator, iplemented in terms of original Python 2 compare operator."""
159
        return self.__cmp__(other) == 0
4✔
160

161
    def __lt__(self, other):
4✔
162
        """Less-than operator, iplemented in terms of original Python 2 compare operator."""
163
        return self.__cmp__(other) < 0
4✔
164

165
    def __gt__(self, other):
4✔
166
        """Greater-than operator, iplemented in terms of original Python 2 compare operator."""
167
        return self.__cmp__(other) > 0
4✔
168

169
    def __cmp__(self, other):
4✔
170
        """
171
        Original Python 2 comparison operator.
172
        Args:
173
           other: Other object to compare to
174
        Returns:
175
            -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other
176
        """
177
        if other is None:
4✔
178
            return 1
4✔
179
        if self.user != other.user:
4✔
180
            if str(self.user or "") < str(other.user or ""):
4✔
181
                return -1
4✔
182
            else:
183
                return 1
×
184
        if self.compressMode != other.compressMode:
4✔
185
            if str(self.compressMode or "") < str(other.compressMode or ""):
4✔
186
                return -1
4✔
187
            else:
188
                return 1
×
189
        if self.all != other.all:
4✔
190
            if self.all < other.all:
4✔
191
                return -1
4✔
192
            else:
193
                return 1
×
194
        if self.databases != other.databases:
4✔
195
            if self.databases < other.databases:
4✔
196
                return -1
4✔
197
            else:
198
                return 1
4✔
199
        return 0
4✔
200

201
    def _setUser(self, value):
4✔
202
        """
203
        Property target used to set the user value.
204
        """
205
        if value is not None:
4✔
206
            if len(value) < 1:
4✔
207
                raise ValueError("User must be non-empty string.")
4✔
208
        self._user = value
4✔
209

210
    def _getUser(self):
4✔
211
        """
212
        Property target used to get the user value.
213
        """
214
        return self._user
4✔
215

216
    def _setCompressMode(self, value):
4✔
217
        """
218
        Property target used to set the compress mode.
219
        If not ``None``, the mode must be one of the values in :any:`VALID_COMPRESS_MODES`.
220
        Raises:
221
           ValueError: If the value is not valid
222
        """
223
        if value is not None:
4✔
224
            if value not in VALID_COMPRESS_MODES:
4✔
225
                raise ValueError("Compress mode must be one of %s." % VALID_COMPRESS_MODES)
4✔
226
        self._compressMode = value
4✔
227

228
    def _getCompressMode(self):
4✔
229
        """
230
        Property target used to get the compress mode.
231
        """
232
        return self._compressMode
4✔
233

234
    def _setAll(self, value):
4✔
235
        """
236
        Property target used to set the 'all' flag.
237
        No validations, but we normalize the value to ``True`` or ``False``.
238
        """
239
        if value:
4✔
240
            self._all = True
4✔
241
        else:
242
            self._all = False
4✔
243

244
    def _getAll(self):
4✔
245
        """
246
        Property target used to get the 'all' flag.
247
        """
248
        return self._all
4✔
249

250
    def _setDatabases(self, value):
4✔
251
        """
252
        Property target used to set the databases list.
253
        Either the value must be ``None`` or each element must be a string.
254
        Raises:
255
           ValueError: If the value is not a string
256
        """
257
        if value is None:
4✔
258
            self._databases = None
4✔
259
        else:
260
            for database in value:
4✔
261
                if len(database) < 1:
4✔
262
                    raise ValueError("Each database must be a non-empty string.")
4✔
263
            try:
4✔
264
                saved = self._databases
4✔
265
                self._databases = ObjectTypeList(str, "string")
4✔
266
                self._databases.extend(value)
4✔
267
            except Exception as e:
×
268
                self._databases = saved
×
269
                raise e
×
270

271
    def _getDatabases(self):
4✔
272
        """
273
        Property target used to get the databases list.
274
        """
275
        return self._databases
4✔
276

277
    user = property(_getUser, _setUser, None, "User to execute backup as.")
4✔
278
    compressMode = property(_getCompressMode, _setCompressMode, None, "Compress mode to be used for backed-up files.")
4✔
279
    all = property(_getAll, _setAll, None, "Indicates whether to back up all databases.")
4✔
280
    databases = property(_getDatabases, _setDatabases, None, "List of databases to back up.")
4✔
281

282

283
########################################################################
284
# LocalConfig class definition
285
########################################################################
286

287

288
@total_ordering
4✔
289
class LocalConfig:
4✔
290
    """
291
    Class representing this extension's configuration document.
292

293
    This is not a general-purpose configuration object like the main Cedar
294
    Backup configuration object.  Instead, it just knows how to parse and emit
295
    PostgreSQL-specific configuration values.  Third parties who need to read and
296
    write configuration related to this extension should access it through the
297
    constructor, ``validate`` and ``addConfig`` methods.
298

299
    *Note:* Lists within this class are "unordered" for equality comparisons.
300

301
    """
302

303
    def __init__(self, xmlData=None, xmlPath=None, validate=True):
4✔
304
        """
305
        Initializes a configuration object.
306

307
        If you initialize the object without passing either ``xmlData`` or
308
        ``xmlPath`` then configuration will be empty and will be invalid until it
309
        is filled in properly.
310

311
        No reference to the original XML data or original path is saved off by
312
        this class.  Once the data has been parsed (successfully or not) this
313
        original information is discarded.
314

315
        Unless the ``validate`` argument is ``False``, the :any:`LocalConfig.validate`
316
        method will be called (with its default arguments) against configuration
317
        after successfully parsing any passed-in XML.  Keep in mind that even if
318
        ``validate`` is ``False``, it might not be possible to parse the passed-in
319
        XML document if lower-level validations fail.
320

321
        *Note:* It is strongly suggested that the ``validate`` option always be set
322
        to ``True`` (the default) unless there is a specific need to read in
323
        invalid configuration from disk.
324

325
        Args:
326
           xmlData (String data): XML data representing configuration
327
           xmlPath (Absolute path to a file on disk): Path to an XML file on disk
328
           validate (Boolean true/false): Validate the document after parsing it
329
        Raises:
330
           ValueError: If both ``xmlData`` and ``xmlPath`` are passed-in
331
           ValueError: If the XML data in ``xmlData`` or ``xmlPath`` cannot be parsed
332
           ValueError: If the parsed configuration document is not valid
333
        """
334
        self._postgresql = None
4✔
335
        self.postgresql = None
4✔
336
        if xmlData is not None and xmlPath is not None:
4✔
337
            raise ValueError("Use either xmlData or xmlPath, but not both.")
4✔
338
        if xmlData is not None:
4✔
339
            self._parseXmlData(xmlData)
4✔
340
            if validate:
4✔
341
                self.validate()
4✔
342
        elif xmlPath is not None:
4✔
343
            with open(xmlPath) as f:
4✔
344
                xmlData = f.read()
4✔
345
            self._parseXmlData(xmlData)
4✔
346
            if validate:
4✔
347
                self.validate()
4✔
348

349
    def __repr__(self):
4✔
350
        """
351
        Official string representation for class instance.
352
        """
353
        return "LocalConfig(%s)" % (self.postgresql)
4✔
354

355
    def __str__(self):
4✔
356
        """
357
        Informal string representation for class instance.
358
        """
359
        return self.__repr__()
4✔
360

361
    def __eq__(self, other):
4✔
362
        """Equals operator, iplemented in terms of original Python 2 compare operator."""
363
        return self.__cmp__(other) == 0
4✔
364

365
    def __lt__(self, other):
4✔
366
        """Less-than operator, iplemented in terms of original Python 2 compare operator."""
367
        return self.__cmp__(other) < 0
4✔
368

369
    def __gt__(self, other):
4✔
370
        """Greater-than operator, iplemented in terms of original Python 2 compare operator."""
371
        return self.__cmp__(other) > 0
4✔
372

373
    def __cmp__(self, other):
4✔
374
        """
375
        Original Python 2 comparison operator.
376
        Lists within this class are "unordered" for equality comparisons.
377
        Args:
378
           other: Other object to compare to
379
        Returns:
380
            -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other
381
        """
382
        if other is None:
4✔
383
            return 1
×
384
        if self.postgresql != other.postgresql:
4✔
385
            if self.postgresql < other.postgresql:
4✔
386
                return -1
4✔
387
            else:
388
                return 1
×
389
        return 0
4✔
390

391
    def _setPostgresql(self, value):
4✔
392
        """
393
        Property target used to set the postgresql configuration value.
394
        If not ``None``, the value must be a ``PostgresqlConfig`` object.
395
        Raises:
396
           ValueError: If the value is not a ``PostgresqlConfig``
397
        """
398
        if value is None:
4✔
399
            self._postgresql = None
4✔
400
        else:
401
            if not isinstance(value, PostgresqlConfig):
4✔
402
                raise ValueError("Value must be a ``PostgresqlConfig`` object.")
4✔
403
            self._postgresql = value
4✔
404

405
    def _getPostgresql(self):
4✔
406
        """
407
        Property target used to get the postgresql configuration value.
408
        """
409
        return self._postgresql
4✔
410

411
    postgresql = property(
4✔
412
        _getPostgresql, _setPostgresql, None, "Postgresql configuration in terms of a ``PostgresqlConfig`` object."
413
    )
414

415
    def validate(self):
4✔
416
        """
417
        Validates configuration represented by the object.
418

419
        The compress mode must be filled in.  Then, if the 'all' flag
420
        *is* set, no databases are allowed, and if the 'all' flag is
421
        *not* set, at least one database is required.
422

423
        Raises:
424
           ValueError: If one of the validations fails
425
        """
426
        if self.postgresql is None:
4✔
427
            raise ValueError("PostgreSQL section is required.")
4✔
428
        if self.postgresql.compressMode is None:
4✔
429
            raise ValueError("Compress mode value is required.")
4✔
430
        if self.postgresql.all:
4✔
431
            if self.postgresql.databases is not None and self.postgresql.databases != []:
4✔
432
                raise ValueError("Databases cannot be specified if 'all' flag is set.")
4✔
433
        else:
434
            if self.postgresql.databases is None or len(self.postgresql.databases) < 1:
4✔
435
                raise ValueError("At least one PostgreSQL database must be indicated if 'all' flag is not set.")
4✔
436

437
    def addConfig(self, xmlDom, parentNode):
4✔
438
        """
439
        Adds a <postgresql> configuration section as the next child of a parent.
440

441
        Third parties should use this function to write configuration related to
442
        this extension.
443

444
        We add the following fields to the document::
445

446
           user           //cb_config/postgresql/user
447
           compressMode   //cb_config/postgresql/compress_mode
448
           all            //cb_config/postgresql/all
449

450
        We also add groups of the following items, one list element per
451
        item::
452

453
           database       //cb_config/postgresql/database
454

455
        Args:
456
           xmlDom: DOM tree as from ``impl.createDocument()``
457
           parentNode: Parent that the section should be appended to
458
        """
459
        if self.postgresql is not None:
4✔
460
            sectionNode = addContainerNode(xmlDom, parentNode, "postgresql")
4✔
461
            addStringNode(xmlDom, sectionNode, "user", self.postgresql.user)
4✔
462
            addStringNode(xmlDom, sectionNode, "compress_mode", self.postgresql.compressMode)
4✔
463
            addBooleanNode(xmlDom, sectionNode, "all", self.postgresql.all)
4✔
464
            if self.postgresql.databases is not None:
4✔
465
                for database in self.postgresql.databases:
4✔
466
                    addStringNode(xmlDom, sectionNode, "database", database)
4✔
467

468
    def _parseXmlData(self, xmlData):
4✔
469
        """
470
        Internal method to parse an XML string into the object.
471

472
        This method parses the XML document into a DOM tree (``xmlDom``) and then
473
        calls a static method to parse the postgresql configuration section.
474

475
        Args:
476
           xmlData (String data): XML data to be parsed
477
        Raises:
478
           ValueError: If the XML cannot be successfully parsed
479
        """
480
        (_, parentNode) = createInputDom(xmlData)
4✔
481
        self._postgresql = LocalConfig._parsePostgresql(parentNode)
4✔
482

483
    @staticmethod
4✔
484
    def _parsePostgresql(parent):
4✔
485
        """
486
        Parses a postgresql configuration section.
487

488
        We read the following fields::
489

490
           user           //cb_config/postgresql/user
491
           compressMode   //cb_config/postgresql/compress_mode
492
           all            //cb_config/postgresql/all
493

494
        We also read groups of the following item, one list element per
495
        item::
496

497
           databases      //cb_config/postgresql/database
498

499
        Args:
500
           parent: Parent node to search beneath
501

502
        Returns:
503
            ``PostgresqlConfig`` object or ``None`` if the section does not exist
504
        Raises:
505
           ValueError: If some filled-in value is invalid
506
        """
507
        postgresql = None
4✔
508
        section = readFirstChild(parent, "postgresql")
4✔
509
        if section is not None:
4✔
510
            postgresql = PostgresqlConfig()
4✔
511
            postgresql.user = readString(section, "user")
4✔
512
            postgresql.compressMode = readString(section, "compress_mode")
4✔
513
            postgresql.all = readBoolean(section, "all")
4✔
514
            postgresql.databases = readStringList(section, "database")
4✔
515
        return postgresql
4✔
516

517

518
########################################################################
519
# Public functions
520
########################################################################
521

522
###########################
523
# executeAction() function
524
###########################
525

526

527
def executeAction(configPath, options, config):  # noqa: ARG001
4✔
528
    """
529
    Executes the PostgreSQL backup action.
530

531
    Args:
532
       configPath (String representing a path on disk): Path to configuration file on disk
533
       options (Options object): Program command-line options
534
       config (Config object): Program configuration
535
    Raises:
536
       ValueError: Under many generic error conditions
537
       IOError: If a backup could not be written for some reason
538
    """
539
    logger.debug("Executing PostgreSQL extended action.")
×
540
    if config.options is None or config.collect is None:
×
541
        raise ValueError("Cedar Backup configuration is not properly filled in.")
×
542
    local = LocalConfig(xmlPath=configPath)
×
543
    if local.postgresql.all:
×
544
        logger.info("Backing up all databases.")
×
545
        _backupDatabase(
×
546
            config.collect.targetDir,
547
            local.postgresql.compressMode,
548
            local.postgresql.user,
549
            config.options.backupUser,
550
            config.options.backupGroup,
551
            None,
552
        )
553
    if local.postgresql.databases is not None and local.postgresql.databases != []:
×
554
        logger.debug("Backing up %d individual databases.", len(local.postgresql.databases))
×
555
        for database in local.postgresql.databases:
×
556
            logger.info("Backing up database [%s].", database)
×
557
            _backupDatabase(
×
558
                config.collect.targetDir,
559
                local.postgresql.compressMode,
560
                local.postgresql.user,
561
                config.options.backupUser,
562
                config.options.backupGroup,
563
                database,
564
            )
565
    logger.info("Executed the PostgreSQL extended action successfully.")
×
566

567

568
def _backupDatabase(targetDir, compressMode, user, backupUser, backupGroup, database=None):
4✔
569
    """
570
    Backs up an individual PostgreSQL database, or all databases.
571

572
    This internal method wraps the public method and adds some functionality,
573
    like figuring out a filename, etc.
574

575
    Args:
576
       targetDir:  Directory into which backups should be written
577
       compressMode: Compress mode to be used for backed-up files
578
       user: User to use for connecting to the database
579
       backupUser: User to own resulting file
580
       backupGroup: Group to own resulting file
581
       database: Name of database, or ``None`` for all databases
582

583
    Returns:
584
        Name of the generated backup file
585

586
    Raises:
587
       ValueError: If some value is missing or invalid
588
       IOError: If there is a problem executing the PostgreSQL dump
589
    """
590
    (outputFile, filename) = _getOutputFile(targetDir, database, compressMode)
×
591
    with outputFile:
×
592
        backupDatabase(user, outputFile, database)
×
593
    if not os.path.exists(filename):
×
NEW
594
        raise OSError("Dump file [%s] does not seem to exist after backup completed." % filename)
×
595
    changeOwnership(filename, backupUser, backupGroup)
×
596

597

598
def _getOutputFile(targetDir, database, compressMode):
4✔
599
    """
600
    Opens the output file used for saving the PostgreSQL dump.
601

602
    The filename is either ``"postgresqldump.txt"`` or
603
    ``"postgresqldump-<database>.txt"``.  The ``".gz"`` or ``".bz2"`` extension is
604
    added if ``compress`` is ``True``.
605

606
    Args:
607
       targetDir: Target directory to write file in
608
       database: Name of the database (if any)
609
       compressMode: Compress mode to be used for backed-up files
610

611
    Returns:
612
        Tuple of (Output file object, filename), file opened in binary mode for use with executeCommand()
613
    """
614
    if database is None:
×
615
        filename = pathJoin(targetDir, "postgresqldump.txt")
×
616
    else:
617
        filename = pathJoin(targetDir, "postgresqldump-%s.txt" % database)
×
618
    if compressMode == "gzip":
×
619
        filename = "%s.gz" % filename
×
620
        outputFile = GzipFile(filename, "wb")
×
621
    elif compressMode == "bzip2":
×
622
        filename = "%s.bz2" % filename
×
623
        outputFile = BZ2File(filename, "wb")
×
624
    else:
NEW
625
        outputFile = open(filename, "wb")  # noqa: SIM115
×
626
    logger.debug("PostgreSQL dump file will be [%s].", filename)
×
627
    return (outputFile, filename)
×
628

629

630
############################
631
# backupDatabase() function
632
############################
633

634

635
def backupDatabase(user, backupFile, database=None):
4✔
636
    """
637
    Backs up an individual PostgreSQL database, or all databases.
638

639
    This function backs up either a named local PostgreSQL database or all local
640
    PostgreSQL databases, using the passed in user for connectivity.
641
    This is *always* a full backup.  There is no facility for incremental
642
    backups.
643

644
    The backup data will be written into the passed-in back file.  Normally,
645
    this would be an object as returned from ``open``, but it is possible to
646
    use something like a ``GzipFile`` to write compressed output.  The caller is
647
    responsible for closing the passed-in backup file.
648

649
    *Note:* Typically, you would use the ``root`` user to back up all databases.
650

651
    Args:
652
       user (String representing PostgreSQL username): User to use for connecting to the database
653
       backupFile (Python file object as from ``open`` or ``file``): File use for writing backup
654
       database (String representing database name, or ``None`` for all databases): Name of the database to be backed up
655
    Raises:
656
       ValueError: If some value is missing or invalid
657
       IOError: If there is a problem executing the PostgreSQL dump
658
    """
659
    args = []
×
660
    if user is not None:
×
661
        args.append("-U")
×
662
        args.append(user)
×
663

664
    if database is None:
×
665
        command = resolveCommand(POSTGRESQLDUMPALL_COMMAND)
×
666
    else:
667
        command = resolveCommand(POSTGRESQLDUMP_COMMAND)
×
668
        args.append(database)
×
669

670
    result = executeCommand(command, args, returnOutput=False, ignoreStderr=True, doNotLog=True, outputFile=backupFile)[0]
×
671
    if result != 0:
×
672
        if database is None:
×
NEW
673
            raise OSError("Error [%d] executing PostgreSQL database dump for all databases." % result)
×
674
        else:
NEW
675
            raise OSError("Error [%d] executing PostgreSQL database dump for database [%s]." % (result, database))
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc