├── .gitignore
├── AUTHORS
├── INSTALL
├── LICENSE
├── README.md
├── bin
├── pgbackman
├── pgbackman-bulk-update
├── pgbackman_alerts
├── pgbackman_control
├── pgbackman_dump
├── pgbackman_maintenance
├── pgbackman_restore
├── pgbackman_status_info
└── pgbackman_zabbix_autodiscovery
├── debian
├── changelog
├── compat
├── control
├── copyright
├── postinst
├── rules
└── source
│ └── format
├── docs
├── Makefile
├── images
│ ├── architecture.graphml
│ ├── architecture.jpg
│ ├── components.graphml
│ ├── components.jpg
│ ├── main.dia
│ ├── register_restore.graphml
│ └── register_restore.jpg
├── manual.rst
├── manual_es.rst
├── release-notes.rst
└── style.css
├── etc
├── pgbackman-alerts.service
├── pgbackman-control.service
├── pgbackman-maintenance.service
├── pgbackman.conf
├── pgbackman.log
├── pgbackman.logrotate
├── pgbackman_alerts.template
├── pgbackman_init_debian.sh
└── pgbackman_init_rh.sh
├── pgbackman
├── __init__.py
├── cli.py
├── config.py
├── database.py
├── logs.py
├── ordereddict.py
├── prettytable.py
└── version.py
├── rpm
└── pgbackman.spec
├── setup.py
├── setup2.py
├── sql
├── pgbackman.sql
├── pgbackman_2.sql
├── pgbackman_3.sql
└── pgbackman_4.sql
└── vagrant
├── Vagrantfile
├── Vagrantfile_orig
├── bootstrap.sh
├── bootstrap2.sh
├── bootstrap3.sh
├── bootstrap_centos.sh
└── bootstrap_debian.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled source #
2 | ###################
3 | *.com
4 | *.class
5 | *.dll
6 | *.exe
7 | *.o
8 | *.so
9 | *.pyc
10 |
11 | # Packages #
12 | ############
13 | # it's better to unpack these files and commit the raw source
14 | # git has its own built in compression methods
15 | *.7z
16 | *.dmg
17 | *.gz
18 | *.iso
19 | *.jar
20 | *.rar
21 | *.tar
22 | *.zip
23 |
24 | # Logs and databases #
25 | ######################
26 | *.log
27 |
28 | # OS generated files #
29 | ######################
30 | .DS_Store
31 | .DS_Store?
32 | ._*
33 | .Spotlight-V100
34 | .Trashes
35 | ehthumbs.db
36 | Thumbs.db
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | Rafael Martinez Guerrero - PostgreSQL-es
2 |
3 | rafael@postgresql.org.es
4 | http://www.postgresql.org.es/
5 | https://github.com/rafaelma/pgbackman
6 |
--------------------------------------------------------------------------------
/INSTALL:
--------------------------------------------------------------------------------
1 | Check the PgBackMan documentation:
2 | http://www.pgbackman.org/documentation.html
3 |
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PgBackMan
2 | =========
3 |
4 | PostgreSQL backup manager
5 |
6 | *NOTE: This project is no longer maintained due to lack of free time to do so.
7 | The source code is still available and it was supported up to PostgreSQL 9.6.*
8 |
9 | PgBackMan is a tool for managing PostgreSQL logical backups created
10 | with ``pg_dump`` and ``pg_dumpall``.
11 |
12 | It is designed to manage backups from thousands of databases running
13 | in multiple PostgreSQL nodes, and it supports a multiple backup server
14 | topology.
15 |
16 | PgBackMan is not a tool for managing PITR (Point in time recovery)
17 | backups. There are several other solutions that can be use for
18 | managing PITR backups, such as PITRTools, OmniPITR, and Barman.
19 |
20 | The PgBackMan code is distributed under the GNU General Public License
21 | and it is written in Python and PL/PgSQL. It has been developed and
22 | tested by members of the Database Operations Group at the Center for
23 | Information Technology at the University of Oslo.
24 |
25 |
--------------------------------------------------------------------------------
/bin/pgbackman:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) 2013-2015 Rafael Martinez Guerrero / PostgreSQL-es
4 | # rafael@postgresql.org.es / http://www.postgresql.org.es/
5 | #
6 | # Copyright (c) 2015 USIT-University of Oslo
7 | #
8 | # This file is part of Pgbackman
9 | # https://github.com/rafaelma/pgbackman
10 | #
11 | # PgBackMan is free software: you can redistribute it and/or modify
12 | # it under the terms of the GNU General Public License as published by
13 | # the Free Software Foundation, either version 3 of the License, or
14 | # (at your option) any later version.
15 | #
16 | # PgBackMan is distributed in the hope that it will be useful,
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | # GNU General Public License for more details.
20 | #
21 | # You should have received a copy of the GNU General Public License
22 | # along with Pgbackman. If not, see .
23 |
24 | import argparse
25 | from pgbackman.cli import *
26 |
27 | if __name__ == '__main__':
28 |
29 | try:
30 |
31 | #
32 | # Processing command line parameters
33 | #
34 |
35 | output_format = ''
36 | pgbackman_command = ''
37 |
38 | parser = argparse.ArgumentParser(prog=sys.argv[0], description='zabbix-cli - Zabbix client')
39 |
40 | parser.add_argument('--output', '-o', metavar='[csv|json]', choices=['csv', 'json'],
41 | required=False, dest='output_format')
42 |
43 | parser.add_argument('--command', '-C', metavar='', required=False, dest='pgbackman_command')
44 |
45 | args = parser.parse_args()
46 |
47 | if args.output_format:
48 | output_format = args.output_format
49 |
50 | if args.pgbackman_command:
51 | pgbackman_command = args.pgbackman_command
52 |
53 | #
54 | # pgbackman cli initialization
55 | #
56 |
57 | cli = PgbackmanCli()
58 |
59 | #
60 | # Processing output format
61 | #
62 |
63 | if output_format == 'csv':
64 | cli.output_format = 'csv'
65 |
66 | elif output_format == 'json':
67 | cli.output_format = 'json'
68 |
69 | else:
70 | cli.output_format = 'table'
71 |
72 | #
73 | # PgBackMan in non-interactive modus
74 | #
75 |
76 | if pgbackman_command != '':
77 |
78 | cli.execution_modus = 'non-interactive'
79 | cli.onecmd(pgbackman_command)
80 |
81 | #
82 | # PgBackMan in interactive modus (pgbackman-shell)
83 | #
84 |
85 | elif pgbackman_command == '':
86 | os.system('clear')
87 |
88 | cli.execution_modus = 'interactive'
89 |
90 | cli.check_pgbackman_database_version()
91 | cli.cmdloop()
92 |
93 | else:
94 | raise NotImplementedError
95 |
96 | except KeyboardInterrupt:
97 | print
98 | print "\nDone, thank you for using PgBackMan"
99 |
100 | sys.exit(0)
101 |
--------------------------------------------------------------------------------
/bin/pgbackman-bulk-update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) 2013-2015 Rafael Martinez Guerrero / PostgreSQL-es
4 | # rafael@postgresql.org.es / http://www.postgresql.org.es/
5 | #
6 | # Copyright (c) 2015 USIT-University of Oslo
7 | #
8 | # This file is part of Pgbackman
9 | # https://github.com/rafaelma/pgbackman
10 | #
11 | # PgBackMan is free software: you can redistribute it and/or modify
12 | # it under the terms of the GNU General Public License as published by
13 | # the Free Software Foundation, either version 3 of the License, or
14 | # (at your option) any later version.
15 | #
16 | # PgBackMan is distributed in the hope that it will be useful,
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | # GNU General Public License for more details.
20 | #
21 | # You should have received a copy of the GNU General Public License
22 | # along with Pgbackman. If not, see .
23 |
24 | import sys
25 | import os
26 | import argparse
27 | import subprocess
28 |
29 | from pgbackman.config import *
30 | from pgbackman.logs import *
31 |
32 | if __name__ == '__main__':
33 |
34 | try:
35 |
36 | intro = '\n####################################################################\n' + \
37 | 'PgBackMan bulk update \n' + \
38 | '####################################################################\n'
39 |
40 | print intro
41 |
42 | #
43 | # Process command line parameters
44 | #
45 |
46 | input_file = ''
47 |
48 | ok_count = 0
49 | error_count = 0
50 | not_supported_count = 0
51 |
52 | parser = argparse.ArgumentParser(prog=sys.argv[0])
53 | parser.add_argument('--input-file', '-f', metavar='[Filename]', required=True, help='Input file', dest='input_file')
54 |
55 | args = parser.parse_args()
56 |
57 | if args.input_file:
58 | input_file = args.input_file
59 |
60 |
61 | logs = PgbackmanLogs("pgbackman-bulk-update", "", "")
62 |
63 | logs.logger.debug('**** pgbackman-bulk-update startet. ****')
64 |
65 | # Normalized absolutized version of the pathname if
66 | # files does not include an absolute path
67 |
68 | if os.path.isabs(input_file) == False:
69 | input_file = os.path.abspath(input_file)
70 |
71 | if os.path.exists(input_file):
72 |
73 | logs.logger.info('File [%s] exists. Bulk execution of commands defined in this file started.',input_file)
74 | print '[OK] File [' + input_file + '] exists. Bulk execution of commands defined in this file started.\n'
75 |
76 | #
77 | # Processing pgbackman commands in file
78 | #
79 |
80 | try:
81 | with open(input_file,'r') as file:
82 | for line in file:
83 |
84 | line = line.strip()
85 |
86 | if line.find('#',0) == -1 and line != '':
87 |
88 | pgbackman_command = line
89 | command = 'pgbackman -o json -C "' + pgbackman_command + '"'
90 |
91 | if 'delete_' in line.lower() or \
92 | 'register_' in line.lower() or \
93 | 'update_' in line.lower():
94 |
95 | DEVNULL = open(os.devnull, 'w')
96 | proc = subprocess.Popen([command],stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
97 | proc.wait()
98 |
99 | stdout, stderr = proc.communicate()
100 |
101 | if proc.returncode == 0:
102 | logs.logger.info('PgBackMan command [%s] executed',command)
103 | print '[OK]\tPgBackMan command [' + command + '] executed'
104 |
105 | ok_count += 1
106 |
107 | else:
108 | logs.logger.error('PgBackMan command [%s] could not be executed - %s',command,str(stdout))
109 | print '[ERROR]\tPgBackMan command [' + command + '] could not be executed'
110 |
111 | error_count += 1
112 | else:
113 |
114 | logs.logger.info('PgBackMan command [%s] is not supported by pgbackman-bulk-update.',command)
115 | print '[INFO]\tPgBackMan command [' + command + '] is not supported by pgbackman-bulk-update.'
116 |
117 | not_supported_count += 1
118 |
119 | footer = '\n####################################################################\n' + \
120 | 'Total ok: ' + str(ok_count) + '\n' + \
121 | 'Total error: ' + str(error_count) + '\n' + \
122 | 'Total not supported: ' + str(not_supported_count) + '\n' + \
123 | '####################################################################'
124 |
125 | print footer
126 |
127 | if error_count > 0:
128 | print "WARNING: Check PgBackMan log file for error information"
129 | print
130 |
131 | except Exception as e:
132 |
133 | logs.logger.error('Problems using file [%s] - %s',input_file,e)
134 | print '[ERROR]\tProblems using file [' + input_file + '] - ' + str(e)
135 | sys.exit(1)
136 |
137 | else:
138 | logs.logger.info('File [%s] does not exist. Bulk execution of commands aborted.',input_file)
139 | print '[ERROR]\tFile [' + input_file + '] does not exist. Bulk execution of commands aborted'
140 |
141 | logs.logger.debug('**** pgbackman-bulk-update finished. ****')
142 |
143 | except Exception as e:
144 | print '\n[ERROR]: %s\n',e
145 |
146 | logs.logger.error('Problems running pgbackman-bulk-update - %s',e)
147 | print 'Problems running pgbackman-bulk-update - ' + str(e)
148 |
149 | sys.exit(1)
150 |
--------------------------------------------------------------------------------
/bin/pgbackman_alerts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) 2013-2015 Rafael Martinez Guerrero / PostgreSQL-es
4 | # rafael@postgresql.org.es / http://www.postgresql.org.es/
5 | #
6 | # Copyright (c) 2014-2015 USIT-University of Oslo
7 | #
8 | # This file is part of PgBackMan
9 | # https://github.com/rafaelma/pgbackman
10 | #
11 | # PgBackMan is free software: you can redistribute it and/or modify
12 | # it under the terms of the GNU General Public License as published by
13 | # the Free Software Foundation, either version 3 of the License, or
14 | # (at your option) any later version.
15 | #
16 | # PgBackMan is distributed in the hope that it will be useful,
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | # GNU General Public License for more details.
20 | #
21 | # You should have received a copy of the GNU General Public License
22 | # along with Pgbackman. If not, see .
23 |
24 | import datetime
25 | import sys
26 | import os
27 | import time
28 | import socket
29 | import signal
30 | import errno
31 | import smtplib
32 |
33 | from string import Template
34 | from pgbackman.logs import *
35 | from pgbackman.database import *
36 | from pgbackman.config import *
37 | import pgbackman.version
38 |
39 |
40 | # ############################################
41 | # Function get_alerts()
42 | # ############################################
43 |
44 | def get_alerts(conf,db,backup_server_id):
45 | """
46 | Getting alerts from this backup server
47 | """
48 |
49 | try:
50 | for alert in db.get_alerts(backup_server_id):
51 | send_alert(conf,db,alert)
52 |
53 | except psycopg2.OperationalError as e:
54 | raise e
55 | except Exception as e:
56 | logs.logger.error('Could not get alerts for this backup server - %s.',e)
57 |
58 |
59 | # ############################################
60 | # Function send_alert()
61 | # ############################################
62 |
63 | def send_alert(conf,db,alert_data):
64 | """
65 | Sending alert from this backup server
66 | """
67 |
68 | try:
69 |
70 | variables = {}
71 |
72 | variables['alert_id'] = alert_data[0]
73 | variables['registered'] = alert_data[1]
74 | variables['alert_type'] = alert_data[2]
75 | variables['ref_id'] = alert_data[3]
76 | variables['bck_id'] = alert_data[4]
77 | variables['backup_server_id'] = alert_data[5]
78 | variables['backup_server_fqdn'] = db.get_backup_server_fqdn(variables['backup_server_id'])
79 | variables['pgsql_node_id'] = alert_data[6]
80 | variables['pgsql_node_fqdn'] = db.get_pgsql_node_fqdn(variables['pgsql_node_id'])
81 | variables['dbname'] = alert_data[7]
82 | variables['execution_status'] = alert_data[8]
83 | variables['error_message'] = alert_data[9]
84 | variables['sendto'] = alert_data[10]
85 | variables['alert_sent'] = alert_data[11]
86 | variables['date'] = datetime.datetime.now()
87 | variables['pgbackman_version'] = pgbackman.version.__version__.split(':')[1]
88 |
89 | msg = ''
90 |
91 | #
92 | # If SMTP alert is not active we will not send the emails
93 | # alerts but we will update alert_sent = TRUE.
94 | #
95 | # This is done to avoid a storm of old alerts if we activate
96 | # this functionality after a period of time.
97 | #
98 |
99 | if conf.smtp_alerts == 'OFF':
100 |
101 | db.update_alert_sent(variables['alert_id'],'true')
102 | logs.logger.info('AlertID [%s] for BckID [%s] registered as sent with smtp_alerts=OFF',variables['alert_id'],variables['bck_id'])
103 |
104 | elif conf.smtp_alerts == 'ON':
105 |
106 | #
107 | # Add the From, To and user-agent headers
108 | #
109 | headers = ("From: %s\r\nTo: %s\r\nUser-agent: pgbackman_alerts (v.%s)\r\n"
110 | % (conf.smtp_from_address, variables['sendto'],variables['pgbackman_version']))
111 |
112 | #
113 | # Get the email body
114 | #
115 | body = parse_alert_template(conf,variables)
116 |
117 | #
118 | # Connect to SNMP.
119 | #
120 | # We will not use SSL when connecting via localhost, even if
121 | # smtp_ssl = ON
122 | #
123 |
124 | server = conf.smtp_server + ':' + conf.smtp_port
125 |
126 | if conf.smtp_server == 'localhost':
127 | smtp = smtplib.SMTP(server)
128 | else:
129 | server = conf.smtp_server + ':' + conf.smtp_port
130 |
131 | if conf.smtp_ssl == 'ON':
132 | smtp = smtplib.SMTP_SSL(server)
133 |
134 | elif conf.smtp_ssl == 'OFF':
135 | smtp = smtplib.SMTP(server)
136 |
137 | smtp.login(conf.smtp_user,conf.smtp_password)
138 |
139 | #
140 | # Send email
141 | #
142 |
143 | smtp.sendmail(conf.smtp_from_address, variables['sendto'], headers + body)
144 | logs.logger.info('Email alert for BckID [%s] sent to [%s]',variables['bck_id'],variables['sendto'])
145 |
146 | smtp.quit()
147 |
148 | db.update_alert_sent(variables['alert_id'],'true')
149 | logs.logger.info('AlertID [%s] for BckID [%s] registered in the database as sent with smtp_alerts=ON',variables['alert_id'],variables['bck_id'])
150 |
151 | except psycopg2.OperationalError as e:
152 | raise e
153 | except Exception as e:
154 | logs.logger.error('Problems sending alertID [%s] via SMTP - %s.',variables['alert_id'],e)
155 |
156 |
157 | # ############################################
158 | # Function parse_alert_template()
159 | # ############################################
160 |
161 | def parse_alert_template(conf,variables):
162 |
163 | try:
164 |
165 | f = open(conf.alerts_template, 'r')
166 | template = f.read()
167 |
168 | t = Template(template)
169 | body = t.safe_substitute(variables)
170 |
171 | return body
172 |
173 | except Exception as e:
174 | raise Exception("Problems parsing alert template [%s] for alarmID [%s]- %s." % (conf.alerts_template,variables['alert_id'],e))
175 |
176 |
177 | # ############################################
178 | # Function signal_handler()
179 | # ############################################
180 |
181 | def signal_handler(signum, frame):
182 | logs.logger.info('**** pgbackman_maintenance stopped. ****')
183 | sys.exit(0)
184 |
185 |
186 | # ############################################
187 | # Function check_database_connection()
188 | # ############################################
189 |
190 | def check_database_connection(db):
191 | '''Check if we can connect to the database server and the pgbackman database'''
192 |
193 | try:
194 | db.pg_connect()
195 | return True
196 | except Exception as e:
197 | return False
198 |
199 |
200 | # ############################################
201 | # Function main()
202 | # ############################################
203 |
204 | def main():
205 |
206 | conf = PgbackmanConfiguration()
207 | dsn = conf.dsn
208 |
209 | if conf.smtp_alerts == 'OFF':
210 | logs.logger.info('SMTP Alerts is not active. Check your configuration file and define smtp_alerts=ON to activate this and restart pgbackman.')
211 |
212 | #
213 | # We exit pgbackman_alerts if sending of alerts via SMTP is not
214 | # activated
215 | #
216 |
217 | logs.logger.debug('Backup server ID from config file: %s',conf.backup_server)
218 | logs.logger.debug('Backup server FQDN: %s',socket.getfqdn())
219 | logs.logger.debug('DSN: host=%s hostaddr=%s port=%s database=%s user=%s ',conf.dbhost,conf.dbhostaddr,conf.dbport,conf.dbname,conf.dbuser)
220 |
221 | db = PgbackmanDB(dsn, 'pgbackman_alerts')
222 |
223 | #
224 | # We check before starting if the database is available.
225 | # If it is not available we will wait conf.pg_connect_retry_interval
226 | # and try again
227 |
228 | check_db = check_database_connection(db)
229 |
230 | while not check_db:
231 | logs.logger.critical('The pgbackman database is not available. Waiting %s seconds before trying again',conf.pg_connect_retry_interval)
232 |
233 | time.sleep(conf.pg_connect_retry_interval)
234 | check_db = check_database_connection(db)
235 |
236 | logs.logger.debug('Database server is up and running and pgbackman database is available')
237 |
238 | #
239 | # Check backup server information
240 | #
241 |
242 | if conf.backup_server != '':
243 | backup_server_fqdn = conf.backup_server
244 | else:
245 | backup_server_fqdn = socket.getfqdn()
246 |
247 | try:
248 | backup_server_id = db.get_backup_server_id(backup_server_fqdn)
249 | logs.logger.info('Backup server [%s] is registered in pgbackman',backup_server_fqdn)
250 |
251 | except psycopg2.Error as e:
252 | logs.logger.critical('Cannot find backup server [%s] in pgbackman. Stopping pgbackman_alerts.',backup_server_fqdn)
253 | logs.logger.info('**** pgbackman_alerts stopped. ****')
254 | sys.exit(1)
255 |
256 | loop = 0
257 |
258 | while loop == 0:
259 | try:
260 | get_alerts(conf,db,backup_server_id)
261 |
262 | except psycopg2.OperationalError as e:
263 |
264 | #
265 | # If we lose the connection to the database, we will wait conf.pg_connect_retry_interval
266 | # before trying to connect again.
267 | #
268 |
269 | logs.logger.critical('Operational error: %s',e)
270 |
271 | check_db = check_database_connection(db)
272 |
273 | while not check_db:
274 | logs.logger.critical('We have lost the connection to the database. Waiting %s seconds before trying again',conf.pg_connect_retry_interval)
275 |
276 | time.sleep(conf.pg_connect_retry_interval)
277 | check_db = check_database_connection(db)
278 |
279 | # Wait for next maintenance run if in loop mode
280 | time.sleep(conf.alerts_check_interval)
281 |
282 | db.pg_close()
283 |
284 |
285 | # ############################################
286 | #
287 | # ############################################
288 |
289 | if __name__ == '__main__':
290 |
291 | logs = PgbackmanLogs("pgbackman_alerts", "", "")
292 |
293 | signal.signal(signal.SIGINT,signal_handler)
294 | signal.signal(signal.SIGTERM,signal_handler)
295 |
296 | logs.logger.info('**** pgbackman_alerts started. ****')
297 |
298 | main()
299 |
300 | logs.logger.info('**** pgbackman_alerts finished. ****')
301 |
--------------------------------------------------------------------------------
/bin/pgbackman_maintenance:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) 2013-2014 Rafael Martinez Guerrero / PostgreSQL-es
4 | # rafael@postgresql.org.es / http://www.postgresql.org.es/
5 | #
6 | # Copyright (c) 2014 USIT-University of Oslo
7 | #
8 | # This file is part of PgBackMan
9 | # https://github.com/rafaelma/pgbackman
10 | #
11 | # PgBackMan is free software: you can redistribute it and/or modify
12 | # it under the terms of the GNU General Public License as published by
13 | # the Free Software Foundation, either version 3 of the License, or
14 | # (at your option) any later version.
15 | #
16 | # PgBackMan is distributed in the hope that it will be useful,
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | # GNU General Public License for more details.
20 | #
21 | # You should have received a copy of the GNU General Public License
22 | # along with Pgbackman. If not, see .
23 |
24 | import subprocess
25 | import tempfile
26 | import datetime
27 | import sys
28 | import os
29 | import time
30 | import socket
31 | import signal
32 | import argparse
33 | import errno
34 | import shutil
35 |
36 | from pgbackman.logs import *
37 | from pgbackman.database import *
38 | from pgbackman.config import *
39 |
40 | '''
41 | This program is used by PgBackMan to run some maintenance tasks in this backup server.
42 |
43 | These are the task implemented:
44 |
45 | * Delete restore logs files when restore definitions/catalogs are deleted.
46 | * Delete backup and log files from catalog entries associated to a backup definition after
47 | this definition has been deleted with the force parameter.
48 | * Enforce retentions for backup definitions.
49 | * Enforce retentions of snapshot backups
50 | * Process all pending backup catalog log files in the server
51 | * Process all pending restore catalog log files in the server
52 |
53 | '''
54 |
55 | # ############################################
56 | # Function delete_restore_logs()
57 | # ############################################
58 |
59 | def delete_restore_logs(db,backup_server_id):
60 | '''Delete restore log files after restore definitions/catalogs are deleted'''
61 |
62 | logs.logger.debug('## Deleting restore logs after restore definitions/catalogs are deleted ##')
63 |
64 | try:
65 | for record in db.get_restore_logs_to_delete(backup_server_id):
66 |
67 | error_cnt = 0
68 |
69 | #
70 | # record[2] is the file to delete
71 | #
72 |
73 | try:
74 | os.unlink(record[2])
75 | logs.logger.debug('File: %s deleted',record[2])
76 |
77 | except OSError as e:
78 | if e.errno != errno.ENOENT:
79 | logs.logger.error('Problems deleting restore log files with DelID: %s - %s',record[0],e)
80 | error_cnt = error_cnt + 1
81 | else:
82 | pass
83 |
84 | #
85 | # We can delete the delID entry from the database only if we can
86 | # delete all the files in the delID entry without errors
87 | #
88 |
89 | if error_cnt == 0:
90 | try:
91 | db.delete_restore_logs_to_delete(record[0])
92 | logs.logger.info('Restore Log file for DelID: %s deleted',record[0])
93 |
94 | except psycopg2.OperationalError as e:
95 | raise e
96 | except Exception as e:
97 | logs.logger.error('Problems deleting restore logs to delete entry DelID: %s - %s',record[0],e)
98 |
99 | except psycopg2.OperationalError as e:
100 | raise e
101 | except Exception as e:
102 | logs.logger.error('Could not get the restore log to delete information - %s',e)
103 |
104 |
105 | # ############################################
106 | # Function delete_files_from_force_deletes()
107 | # ############################################
108 |
109 | def delete_files_from_force_deletes(db,backup_server_id):
110 | '''Delete dump and log files from force deletions of backup definitions'''
111 |
112 | logs.logger.debug('## Deleting files from forced DefID deletions ##')
113 |
114 | try:
115 | for record in db.get_catalog_entries_to_delete(backup_server_id):
116 |
117 | error_cnt = 0
118 |
119 | #
120 | # record[5...10] are the files to delete
121 | #
122 |
123 | for index in range(5,11):
124 |
125 | try:
126 |
127 | if os.path.isfile(record[index]):
128 | os.unlink(record[index])
129 | logs.logger.debug('File: %s deleted',record[index])
130 |
131 | elif os.path.isdir(record[index]):
132 | shutil.rmtree(record[index])
133 | logs.logger.debug('Directory: %s deleted',record[index])
134 |
135 | except OSError as e:
136 | if e.errno != errno.ENOENT:
137 | logs.logger.error('Problems deleting files from force deletions of DefIDs: %s',e)
138 | error_cnt = error_cnt + 1
139 | else:
140 | pass
141 |
142 |
143 | #
144 | # We can delete the delID entry from the database only if we can
145 | # delete all the files in the delID entry without errors
146 | #
147 |
148 | if error_cnt == 0:
149 | try:
150 | db.delete_catalog_entries_to_delete(record[0])
151 | logs.logger.info('Files for catalog ID: %s / DefID: %s deleted',record[3],record[2])
152 |
153 | except psycopg2.OperationalError as e:
154 | raise e
155 | except Exception as e:
156 | logs.logger.error('Problems deleting cataloginfo from force defid deletions - %s',e)
157 |
158 | except psycopg2.OperationalError as e:
159 | raise e
160 | except Exception as e:
161 | logs.logger.error('Could not get the catalog information for defid force deletions - %s',e)
162 |
163 |
164 | # ############################################
165 | # Function enforce_backup_retentions()
166 | # ############################################
167 |
168 | def enforce_backup_retentions(db,backup_server_id):
169 | '''Delete dump and log files according to retention_periods and retention_redundancies'''
170 |
171 | logs.logger.debug('## Enforce backup retentions ##')
172 |
173 | try:
174 | for record in db.get_cron_catalog_entries_to_delete_by_retention(backup_server_id):
175 |
176 | error_cnt = 0
177 |
178 | #
179 | # record[9...14] are the files to delete
180 | #
181 |
182 | for index in range(9,15):
183 |
184 | try:
185 |
186 | if os.path.isfile(record[index]):
187 | os.unlink(record[index])
188 | logs.logger.debug('File: %s deleted',record[index])
189 |
190 | elif os.path.isdir(record[index]):
191 | shutil.rmtree(record[index])
192 | logs.logger.debug('Directory: %s deleted',record[index])
193 |
194 | except OSError as e:
195 | if e.errno != errno.ENOENT:
196 | logs.logger.error('Problems deleting files from backup enforce retentions: %s',e)
197 | error_cnt = error_cnt + 1
198 | else:
199 | pass
200 |
201 | #
202 | # We can delete the BckID entry from the catalog table only if we can
203 | # delete all the files for the BckID entry without errors
204 | #
205 |
206 | if error_cnt == 0:
207 | try:
208 | db.delete_backup_catalog(record[1])
209 | logs.logger.info('Files for catalog ID: %s / DefID: %s deleted',record[1],record[2])
210 |
211 | except psycopg2.OperationalError as e:
212 | raise e
213 | except Exception as e:
214 | logs.logger.error('Problems deleting entry from backup job catalog - %s',e)
215 |
216 | except psycopg2.OperationalError as e:
217 | raise e
218 | except Exception as e:
219 | logs.logger.error('Could not get information to enforce backup file retentions - %s',e)
220 |
221 |
222 | # ############################################
223 | # Function enforce_snapshot_retentions()
224 | # ############################################
225 |
226 | def enforce_snapshot_retentions(db,backup_server_id):
227 | '''Delete dump and log snapshot files according to retention_periods'''
228 |
229 | logs.logger.debug('## Enforce snapshot retentions ##')
230 |
231 | try:
232 | for record in db.get_at_catalog_entries_to_delete_by_retention(backup_server_id):
233 |
234 | error_cnt = 0
235 |
236 | #
237 | # record[7...12] are the files to delete
238 | #
239 |
240 | for index in range(7,13):
241 |
242 | try:
243 |
244 | if os.path.isfile(record[index]):
245 | os.unlink(record[index])
246 | logs.logger.debug('File: %s deleted',record[index])
247 |
248 | elif os.path.isdir(record[index]):
249 | shutil.rmtree(record[index])
250 | logs.logger.debug('Directory: %s deleted',record[index])
251 |
252 | except OSError as e:
253 | if e.errno != errno.ENOENT:
254 | logs.logger.error('Problems deleting files from snapshot enforce retentions: %s',e)
255 | error_cnt = error_cnt + 1
256 | else:
257 | pass
258 |
259 | #
260 | # We can delete the snapshotID entry from the catalog and definition table only if we can
261 | # delete all the files for the SnapshotID entry without errors
262 | #
263 |
264 | if error_cnt == 0:
265 | try:
266 | db.delete_snapshot_definition(record[1])
267 | logs.logger.info('Files for catalog ID: %s / SnapshotID: %s deleted',record[0],record[1])
268 |
269 | except psycopg2.OperationalError as e:
270 | raise e
271 | except Exception as e:
272 | logs.logger.error('Problems deleting entry from backup job catalog - %s',e)
273 |
274 | except psycopg2.OperationalError as e:
275 | raise e
276 | except Exception as e:
277 | logs.logger.error('Could not get information to enforce snapshot file retentions - %s',e)
278 |
279 |
280 | # ##################################################
281 | # Function process_pending_backup_catalog_log_file()
282 | # ##################################################
283 |
284 | def process_pending_backup_catalog_log_file(db,backup_server_id):
285 | '''Process all pending backup catalog log files in the server '''
286 |
287 | role_list = []
288 |
289 | logs.logger.debug('## Processing pending backup catalog log files ##')
290 |
291 | try:
292 | db.pg_connect()
293 |
294 | root_backup_partition = db.get_backup_server_config_value(backup_server_id,'root_backup_partition')
295 | pending_catalog = root_backup_partition + '/pending_updates'
296 |
297 | for pending_log_file in os.listdir(pending_catalog):
298 | if pending_log_file.find('backup_jobs_pending_log_updates_nodeid') != -1:
299 | with open(pending_catalog + '/' + pending_log_file,'r') as pending_file:
300 | for line in pending_file:
301 | parameters = line.split('::')
302 |
303 | if len(parameters) == 25:
304 |
305 | #
306 | # Fix when def_id and snapshot_id are like ''. This is not a valid
307 | # integer value
308 | #
309 |
310 | def_id = parameters[0]
311 | snapshot_id = parameters[21]
312 |
313 | if def_id == '':
314 | def_id = None
315 | elif snapshot_id == '':
316 | snapshot_id = None
317 |
318 | # Generate role list
319 |
320 | role_list = parameters[22].split(' ')
321 |
322 | #
323 | # Updating the database with the information in the pending file
324 | #
325 |
326 | db.register_backup_catalog(def_id,
327 | parameters[1],
328 | parameters[2],
329 | parameters[3],
330 | parameters[4],
331 | parameters[5],
332 | parameters[6],
333 | parameters[7],
334 | parameters[8],
335 | parameters[9],
336 | parameters[10],
337 | parameters[11],
338 | parameters[12],
339 | parameters[13],
340 | parameters[14],
341 | parameters[15],
342 | parameters[16],
343 | parameters[17],
344 | parameters[18],
345 | parameters[19],
346 | parameters[20],
347 | snapshot_id,
348 | role_list,
349 | parameters[23],
350 | parameters[24].replace('\n',''))
351 |
352 | logs.logger.info('Backup job catalog for DefID: %s or snapshotID: %s in pending file %s updated in the database',def_id,snapshot_id,pending_log_file)
353 |
354 | #
355 | # Deleting the pending file if we can update the database with
356 | # the information in the file
357 | #
358 |
359 | os.unlink(pending_catalog + '/' + pending_log_file)
360 | logs.logger.info('Pending backup file: %s deleted',pending_log_file)
361 |
362 | else:
363 | logs.logger.error('Wrong format in pending backup file: %s',pending_log_file)
364 |
365 | except psycopg2.OperationalError as e:
366 | raise e
367 | except Exception as e:
368 | logs.logger.error('Problems processing pending backup files - %s',e)
369 |
370 |
371 | # ##################################################
372 | # Function process_pending_restore_catalog_log_file()
373 | # ##################################################
374 |
375 | def process_pending_restore_catalog_log_file(db,backup_server_id):
376 | '''Process all pending restore catalog log files in the server '''
377 |
378 | role_list = []
379 |
380 | logs.logger.debug('## Processing pending restore catalog log files ##')
381 |
382 | try:
383 | db.pg_connect()
384 |
385 | root_backup_partition = db.get_backup_server_config_value(backup_server_id,'root_backup_partition')
386 | pending_catalog = root_backup_partition + '/pending_updates'
387 |
388 | for pending_log_file in os.listdir(pending_catalog):
389 | if pending_log_file.find('restore_jobs_pending_log_updates_nodeid') != -1:
390 | with open(pending_catalog + '/' + pending_log_file,'r') as pending_file:
391 | for line in pending_file:
392 | parameters = line.split('::')
393 |
394 | if len(parameters) == 17:
395 |
396 | #
397 | # Updating the database with the information in the pending file
398 | #
399 |
400 | db.register_restore_catalog(parameters[0],
401 | parameters[1],
402 | parameters[2],
403 | parameters[3],
404 | parameters[4],
405 | parameters[5],
406 | parameters[6],
407 | parameters[7],
408 | parameters[8],
409 | parameters[9],
410 | parameters[10],
411 | parameters[11],
412 | parameters[12],
413 | parameters[13],
414 | parameters[14].split(' '),
415 | parameters[15],
416 | parameters[16].replace('\n',''))
417 |
418 | logs.logger.info('Restore job catalog for restoreDef: %s in pending file %s updated in the database',parameters[0],pending_log_file)
419 |
420 | #
421 | # Deleting the pending file if we can update the database with
422 | # the information in the file
423 | #
424 |
425 | os.unlink(pending_catalog + '/' + pending_log_file)
426 | logs.logger.info('Pending restore file: %s deleted',pending_log_file)
427 |
428 | else:
429 | logs.logger.error('Wrong format in pending restore file: %s',pending_log_file)
430 |
431 | except psycopg2.OperationalError as e:
432 | raise e
433 | except Exception as e:
434 | logs.logger.error('Problems processing pending restore files - %s',e)
435 |
436 |
437 |
438 | # ############################################################
439 | # Function process_backup_definitions_from_deleted_databases()
440 | # ############################################################
441 |
442 | def process_backup_definitions_from_deleted_databases(db,backup_server_id):
443 | '''
444 | This function stops backup definitions for databases that have been deleted
445 | in the PgSQL nodes running them.
446 | '''
447 |
448 | logs.logger.debug('## Processing backup definitions for deleted databases ##')
449 |
450 | #
451 | # Processing data for all PgSQL nodes with status "RUNNING"
452 | #
453 |
454 | try:
455 | for record in db.get_pgsql_nodes_list():
456 |
457 | backup_def_full_list = []
458 | backup_def_database_list = []
459 | backup_def_list_to_process = []
460 | database_list = []
461 | db_node = None
462 |
463 | pgsql_node_id = record[0]
464 | pgsql_node_fqdn = record[1]
465 |
466 | logs.logger.debug('Proccesing backup definitions for deleted databases on: %s',pgsql_node_fqdn)
467 |
468 | dsn_value = db.get_pgsql_node_dsn(pgsql_node_id)
469 | db_node = PgbackmanDB(dsn_value, 'pgbackman_maintenance')
470 |
471 | #
472 | # Get all backup definitions for this Backup server - PgSQL node
473 | #
474 | for backup_def_id in db.get_all_backup_definitions(backup_server_id,pgsql_node_id):
475 | backup_def_full_list.append(backup_def_id[0])
476 |
477 | #
478 | # Get backup definitions for databases that exist in the PgSQL_node
479 | #
480 |
481 | for database in db_node.get_pgsql_node_database_list():
482 |
483 | for backup_def_id in db.get_database_backup_definitions(backup_server_id,pgsql_node_id,database[0]):
484 | backup_def_database_list.append(backup_def_id[0])
485 |
486 | #
487 | # List of backup definitions to process
488 | #
489 |
490 | backup_def_list_to_process = set(backup_def_full_list) - set(backup_def_database_list)
491 |
492 | #
493 | # Update the backup def status to "DELETED"
494 | #
495 |
496 | for backup_def_id in backup_def_list_to_process:
497 |
498 | logs.logger.info('Updating status of bck_def: %s to DELETED',backup_def_id)
499 | db.update_backup_definition_status_to_delete(int(backup_def_id))
500 |
501 | #
502 | # Update catalog_entries_to_delete and delete def_id from backup_definition
503 | #
504 | # The files associated to the backup definition and the
505 | # backup definition entry will we deleted if the backup
506 | # definition got the DELETED status for more than the
507 | # period of time defined by the parameter
508 | # automatic_deletion_retention for the PgSQL node that was
509 | # running the deleted database.
510 | #
511 |
512 | for backup_def_id in db.get_deleted_backup_definitions_to_delete_by_retention():
513 | try:
514 | db.delete_force_backup_definition_id(backup_def_id[0])
515 | logs.logger.info('Updating catalog_entries_to_delete for DefID: %s with status DELETED',backup_def_id[0])
516 | except Exception as e:
517 | logs.logger.error('Could not update catalog_entries_to_delete for DefID: %s with status DELETED - %s',backup_def_id[0],e)
518 |
519 | except psycopg2.OperationalError as e:
520 | raise e
521 | except Exception as e:
522 | logs.logger.error('Could not process backup definitions for deleted databases - %s',e)
523 |
524 |
525 | # ############################################
526 | # Function signal_handler()
527 | # ############################################
528 |
529 | def signal_handler(signum, frame):
530 | logs.logger.info('**** pgbackman_maintenance stopped. ****')
531 | sys.exit(0)
532 |
533 |
534 | # ############################################
535 | # Function check_database_connection()
536 | # ############################################
537 |
538 | def check_database_connection(db):
539 | '''Check if we can connect to the database server and the pgbackman database'''
540 |
541 | try:
542 | db.pg_connect()
543 | return True
544 | except Exception as e:
545 | return False
546 |
547 |
548 | # ############################################
549 | # Function main()
550 | # ############################################
551 |
552 | def main():
553 |
554 | conf = PgbackmanConfiguration()
555 | dsn = conf.dsn
556 |
557 | logs.logger.debug('Backup server ID from config file: %s',conf.backup_server)
558 | logs.logger.debug('Backup server FQDN: %s',socket.getfqdn())
559 | logs.logger.debug('DSN: host=%s hostaddr=%s port=%s database=%s user=%s ',conf.dbhost,conf.dbhostaddr,conf.dbport,conf.dbname,conf.dbuser)
560 | logs.logger.debug('Maintenance interval: %s',conf.maintenance_interval)
561 |
562 | db = PgbackmanDB(dsn, 'pgbackman_maintenance')
563 |
564 | #
565 | # We check before starting if the database is available.
566 | # If it is not available we will wait conf.pg_connect_retry_interval
567 | # and try again
568 |
569 | check_db = check_database_connection(db)
570 |
571 | while not check_db:
572 | logs.logger.critical('The pgbackman database is not available. Waiting %s seconds before trying again',conf.pg_connect_retry_interval)
573 |
574 | time.sleep(conf.pg_connect_retry_interval)
575 | check_db = check_database_connection(db)
576 |
577 | logs.logger.debug('Database server is up and running and pgbackman database is available')
578 |
579 | #
580 | # Check backup server information
581 | #
582 |
583 | if conf.backup_server != '':
584 | backup_server_fqdn = conf.backup_server
585 | else:
586 | backup_server_fqdn = socket.getfqdn()
587 |
588 | try:
589 | backup_server_id = db.get_backup_server_id(backup_server_fqdn)
590 | logs.logger.info('Backup server: %s is registered in pgbackman',backup_server_fqdn)
591 |
592 | except psycopg2.Error as e:
593 | logs.logger.critical('Cannot find backup server %s in pgbackman. Stopping pgbackman2cron.',backup_server_fqdn)
594 | logs.logger.info('**** pgbackman_maintenance stopped. ****')
595 | sys.exit(1)
596 |
597 | loop = 0
598 |
599 | while loop == 0:
600 | try:
601 | delete_files_from_force_deletes(db,backup_server_id)
602 | enforce_backup_retentions(db,backup_server_id)
603 | enforce_snapshot_retentions(db,backup_server_id)
604 | delete_restore_logs(db,backup_server_id)
605 | process_pending_backup_catalog_log_file(db,backup_server_id)
606 | process_pending_restore_catalog_log_file(db,backup_server_id)
607 | process_backup_definitions_from_deleted_databases(db,backup_server_id)
608 |
609 | except psycopg2.OperationalError as e:
610 |
611 | #
612 | # If we lose the connection to the database, we will wait conf.pg_connect_retry_interval
613 | # before trying to connect again.
614 | #
615 |
616 | logs.logger.critical('Operational error: %s',e)
617 |
618 | check_db = check_database_connection(db)
619 |
620 | while not check_db:
621 | logs.logger.critical('We have lost the connection to the database. Waiting %s seconds before trying again',conf.pg_connect_retry_interval)
622 |
623 | time.sleep(conf.pg_connect_retry_interval)
624 | check_db = check_database_connection(db)
625 |
626 | if cron:
627 | loop = 1
628 | else:
629 | # Wait for next maintenance run if in loop mode
630 | time.sleep(conf.maintenance_interval)
631 |
632 | db.pg_close()
633 |
634 |
635 | # ############################################
636 | #
637 | # ############################################
638 |
639 | if __name__ == '__main__':
640 |
641 | logs = PgbackmanLogs("pgbackman_maintenance", "", "")
642 |
643 | signal.signal(signal.SIGINT,signal_handler)
644 | signal.signal(signal.SIGTERM,signal_handler)
645 |
646 | parser = argparse.ArgumentParser(prog=sys.argv[0])
647 | parser.add_argument('--cron', required=False, help='Single run to use via cron', action="store_true")
648 |
649 | args = parser.parse_args()
650 |
651 | logs.logger.info('**** pgbackman_maintenance started. ****')
652 |
653 | if args.cron:
654 | cron = True
655 | logs.logger.info('Running in cron mode')
656 | else:
657 | cron = False
658 | logs.logger.info('Running in loop mode')
659 |
660 | main()
661 |
662 | logs.logger.info('**** pgbackman_maintenance finished. ****')
663 |
--------------------------------------------------------------------------------
/bin/pgbackman_status_info:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) 2013-2015 Rafael Martinez Guerrero / PostgreSQL-es
4 | # rafael@postgresql.org.es / http://www.postgresql.org.es/
5 | #
6 | # Copyright (c) 2014-2015 USIT-University of Oslo
7 | #
8 | # This file is part of PgBackMan
9 | # https://github.com/rafaelma/pgbackman
10 | #
11 | # PgBackMan is free software: you can redistribute it and/or modify
12 | # it under the terms of the GNU General Public License as published by
13 | # the Free Software Foundation, either version 3 of the License, or
14 | # (at your option) any later version.
15 | #
16 | # PgBackMan is distributed in the hope that it will be useful,
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | # GNU General Public License for more details.
20 | #
21 | # You should have received a copy of the GNU General Public License
22 | # along with PgBck. If not, see .
23 |
24 | import sys
25 | import os
26 | import signal
27 | import argparse
28 | import json
29 |
30 | from pgbackman.database import *
31 | from pgbackman.config import *
32 | from pgbackman.logs import *
33 |
34 |
35 | '''
36 | This program is used to get some status information from
37 | pgbackman.
38 |
39 | This can be used e.g. by Zabbix to define new Items
40 |
41 | '''
42 |
43 | # ############################################
44 | # Function signal_handler()
45 | # ############################################
46 |
47 | def signal_handler(signum, frame):
48 | sys.exit(0)
49 |
50 |
51 | # ############################################
52 | # Function Main()
53 | # ############################################
54 |
55 | def get_status_info(parameter_status,backup_server_fqdn,backup_def_id):
56 |
57 | try:
58 |
59 | conf = PgbackmanConfiguration()
60 | pgbackman_dsn = conf.dsn
61 |
62 | db = PgbackmanDB(pgbackman_dsn, 'pgbackman_status_info')
63 |
64 | if backup_server_fqdn != '':
65 | backup_server_id = db.get_backup_server_id(backup_server_fqdn)
66 | else:
67 | backup_server_id = ''
68 |
69 | return_value = db.get_status_info(parameter_status,backup_server_id,backup_def_id)
70 | print return_value
71 |
72 | logs.logger.debug('Getting status information for parameter [%s]',parameter_status)
73 |
74 | except Exception as e:
75 | logs.logger.error('Problems getting status information for parameter [%s] - %s',parameter_status,e)
76 | sys.exit(1)
77 |
78 |
79 | # ############################################
80 | #
81 | # ############################################
82 |
83 | if __name__ == '__main__':
84 |
85 | signal.signal(signal.SIGINT,signal_handler)
86 | signal.signal(signal.SIGTERM,signal_handler)
87 |
88 | #
89 | # Initializing logging
90 | #
91 |
92 | logs = PgbackmanLogs("pgbackman_status_info", "", "")
93 | logs.logger.debug('**** pgbackman_status_info started. ****')
94 |
95 | parser = argparse.ArgumentParser(prog=sys.argv[0])
96 |
97 | parser.add_argument('--backup-server','-b', metavar='BACKUP-SERVER-FQDN', required=False, help='Backup server FQDN', dest='backup_server_fqdn')
98 | parser.add_argument('--backup-definition-id','-d', metavar='BACKUP-DEFINITION-ID', required=False, help='Backup definition ID', dest='backup_def_id')
99 | parser.add_argument('--parameter-status','-p', metavar='PARAMETER-STATUS', required=False, help='Parameter status', dest='parameter_status')
100 |
101 | args = parser.parse_args()
102 |
103 | if args.backup_server_fqdn:
104 | backup_server = args.backup_server_fqdn
105 | else:
106 | backup_server = ''
107 |
108 | if args.backup_def_id:
109 | backup_def_id = args.backup_def_id
110 | else:
111 | backup_def_id = '0'
112 |
113 | if args.parameter_status:
114 | parameter_status = args.parameter_status
115 | else:
116 | parameter_status = ''
117 |
118 | get_status_info(parameter_status,backup_server,backup_def_id)
119 |
120 | logs.logger.debug('**** pgbackman_status_info finished. ****')
121 |
--------------------------------------------------------------------------------
/bin/pgbackman_zabbix_autodiscovery:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) 2013-2015 Rafael Martinez Guerrero / PostgreSQL-es
4 | # rafael@postgresql.org.es / http://www.postgresql.org.es/
5 | #
6 | # Copyright (c) 2014-2015 USIT-University of Oslo
7 | #
8 | # This file is part of PgBackMan
9 | # https://github.com/rafaelma/pgbackman
10 | #
11 | # PgBackMan is free software: you can redistribute it and/or modify
12 | # it under the terms of the GNU General Public License as published by
13 | # the Free Software Foundation, either version 3 of the License, or
14 | # (at your option) any later version.
15 | #
16 | # PgBackMan is distributed in the hope that it will be useful,
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | # GNU General Public License for more details.
20 | #
21 | # You should have received a copy of the GNU General Public License
22 | # along with PgBck. If not, see .
23 |
24 | import sys
25 | import os
26 | import signal
27 | import argparse
28 | import json
29 |
30 | from pgbackman.database import *
31 | from pgbackman.config import *
32 | from pgbackman.logs import *
33 |
34 |
35 | '''
36 | This program is used to generate the JSON output that Zabbix needs
37 | in a low-level discovery rule.
38 |
39 | The JSON output will have information about the databases with a
40 | backup definition in PgBackMan. {#PGSQL_NODE} and {#DBNAME} are the
41 | macros Zabbix will use to get the data.
42 |
43 | '''
44 |
45 | # ############################################
46 | # Function signal_handler()
47 | # ############################################
48 |
49 | def signal_handler(signum, frame):
50 | sys.exit(0)
51 |
52 |
53 | # ############################################
54 | # Function Main()
55 | # ############################################
56 |
57 | def get_pgbackman_zabbix_autodiscovery(backup_server_fqdn):
58 |
59 | backup_definitions_list = []
60 |
61 | try:
62 |
63 | conf = PgbackmanConfiguration()
64 | pgbackman_dsn = conf.dsn
65 |
66 | db = PgbackmanDB(pgbackman_dsn, 'pgbackman_zabbix_autodiscovery')
67 |
68 | backup_server_id = db.get_backup_server_id(backup_server_fqdn)
69 |
70 | for def_id,pgsql_node,dbname in db.get_backup_server_bckdef_list(backup_server_id):
71 | backup_definition = {}
72 | backup_definition = {"{#DEFID}":def_id,"{#PGSQL_NODE}":pgsql_node,"{#DBNAME}":dbname}
73 |
74 | backup_definitions_list.append(backup_definition)
75 |
76 | result = {"data":backup_definitions_list}
77 | print json.dumps(result,sort_keys=True,indent=2)
78 |
79 | logs.logger.info('Zabbix autodiscovery data for backup server: [%s] delivered. (Total bckdef: %s)',backup_server_fqdn,len(backup_definitions_list))
80 |
81 | except Exception as e:
82 | logs.logger.error('Problems getting Zabbix autodiscovery data for backup server: [%s] - %s',args.backup_server_fqdn,e)
83 | sys.exit(1)
84 |
85 |
86 | # ############################################
87 | #
88 | # ############################################
89 |
90 | if __name__ == '__main__':
91 |
92 | signal.signal(signal.SIGINT,signal_handler)
93 | signal.signal(signal.SIGTERM,signal_handler)
94 |
95 | #
96 | # Initializing logging
97 | #
98 |
99 | logs = PgbackmanLogs("pgbackman_zabbix_autodiscovery", "", "")
100 | logs.logger.info('**** pgbackman_zabbix_autodiscovery started. ****')
101 |
102 | parser = argparse.ArgumentParser(prog=sys.argv[0])
103 | parser.add_argument('--backup-server','-b', metavar='BACKUP-SERVER-FQDN', required=True, help='Backup server FQDN', dest='backup_server_fqdn')
104 |
105 | args = parser.parse_args()
106 |
107 | if args.backup_server_fqdn:
108 |
109 | logs.logger.debug('Getting Zabbix autodiscovery data for backup server: [%s]',args.backup_server_fqdn)
110 | get_pgbackman_zabbix_autodiscovery(args.backup_server_fqdn)
111 |
112 | else:
113 | print('Backup server fqdn parameter not defined')
114 | logs.logger.error('Backup server fqdn parameter not defined')
115 |
116 | logs.logger.info('**** pgbackman_zabbix_autodiscovery finished. ****')
117 |
--------------------------------------------------------------------------------
/debian/changelog:
--------------------------------------------------------------------------------
1 | pgbackman (1.2.0) unstable; urgency=low
2 |
3 | * New release 1.2.0
4 |
5 | -- Rafael Martinez Guerrero Tue, 13 Jun 2017 14:14:05 +0000
6 |
7 | pgbackman (1.1.0) unstable; urgency=low
8 |
9 | * New release 1.1.0
10 |
11 | -- Rafael Martinez Guerrero Sat, 08 Oct 2015 03:07:05 +0000
12 |
13 | pgbackman (1.0.0) unstable; urgency=low
14 |
15 | * Initial release 1.0.0
16 |
17 | -- Rafael Martinez Guerrero Sat, 21 Jun 2014 13:48:05 +0000
18 |
--------------------------------------------------------------------------------
/debian/compat:
--------------------------------------------------------------------------------
1 | 9
2 |
--------------------------------------------------------------------------------
/debian/control:
--------------------------------------------------------------------------------
1 | Source: pgbackman
2 | Section: database
3 | Priority: optional
4 | Maintainer: Rafael Martinez Guerrero
5 | Build-Depends: debhelper (>= 9~)
6 | , python | python-all | python-dev | python-all-dev
7 | , python-setuptools
8 | Standards-Version: 3.9.8
9 | Homepage: http://www.pgbackman.org/
10 | Vcs-Browser: https://github.com/rafaelma/pgbackman
11 | Vcs-Git: git://github.com/rafaelma/pgbackman.git
12 | X-Python-Version: >= 2.6
13 |
14 | Package: pgbackman
15 | Architecture: all
16 | Depends: ${misc:Depends}
17 | , ${python:Depends}
18 | , python-psycopg2 (>= 2.4.0)
19 | , python-argparse
20 | , at
21 | , cron
22 | ,adduser
23 | Description: PostgreSQL Backup Manager
24 | PgBackMan is a tool for managing PostgreSQL logical backups created
25 | with pg_dump and pg_dumpall.
26 | .
27 | It is designed to manage backups from thousands of databases running
28 | in multiple PostgreSQL nodes, and it supports a multiple backup
29 | server topology.
30 | .
31 | It also manages role and database configuration information when
32 | creating a backup of a database. This information is necessary to
33 | ensure a 100% restoration of a logical backup of a database and the
34 | elements associated to it.
35 | .
36 | PgBackMan is not a tool for managing PITR (Point in time recovery)
37 | backups. There are several other solutions that can be used for
38 | managing PITR backups, such as PITRTools, OmniPITR, and Barman.
39 |
--------------------------------------------------------------------------------
/debian/copyright:
--------------------------------------------------------------------------------
1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2 |
3 | Files: *
4 | Copyright: 2013-2017, Rafael Martinez Guerrero
5 | 2014-2015, USIT / University of Oslo
6 | License: GPL-3.0+
7 |
8 | Files: pgbackman/prettytable.py
9 | Copyright: 2009-2013, Luke Maurits
10 | License: BSD-3
11 |
12 | License: GPL-3.0+
13 | This program is free software: you can redistribute it and/or modify
14 | it under the terms of the GNU General Public License as published by
15 | the Free Software Foundation, either version 3 of the License, or
16 | (at your option) any later version.
17 | .
18 | This package 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. See the
21 | GNU General Public License for more details.
22 | .
23 | You should have received a copy of the GNU General Public License
24 | along with this program. If not, see .
25 | .
26 | On Debian systems, the complete text of the GNU General
27 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
28 |
29 | License: BSD-3
30 | Redistribution and use in source and binary forms, with or without
31 | modification, are permitted provided that the following conditions
32 | are met:
33 | .
34 | 1) Redistributions of source code must retain the above copyright
35 | notice, this list of conditions and the following disclaimer.
36 | .
37 | 2) Redistributions in binary form must reproduce the above copyright
38 | notice, this list of conditions and the following disclaimer in the
39 | documentation and/or other materials provided with the distribution.
40 | .
41 | 3) Neither the name of the ORGANIZATION nor the names of its
42 | contributors may be used to endorse or promote products derived from
43 | this software without specific prior written permission.
44 | .
45 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
46 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
47 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
48 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
49 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
51 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
52 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
53 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
54 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
55 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 |
--------------------------------------------------------------------------------
/debian/postinst:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # postinst script for pgbackman
3 | #
4 | # see: dh_installdeb(1)
5 |
6 | set -e
7 |
8 | PGBACKMAN_GROUP=pgbackman
9 | PGBACKMAN_USER=pgbackman
10 | PGBACKMAN_LOGDIR=/var/log/pgbackman
11 |
12 |
13 | case "$1" in
14 | configure)
15 | if ! getent group $PGBACKMAN_GROUP > /dev/null; then
16 | groupadd -f -r $PGBACKMAN_GROUP
17 | fi
18 | if ! getent passwd $PGBACKMAN_GROUP > /dev/null; then
19 | useradd -m -N -g $PGBACKMAN_GROUP -r -d /var/lib/pgbackman -s /bin/bash -c "PostgreSQL Backup Manager" $PGBACKMAN_USER
20 | fi
21 |
22 | if [ -d "$PGBACKMAN_LOGDIR" ]
23 | then
24 | touch ${PGBACKMAN_LOGDIR}/pgbackman.log
25 | chown -R ${PGBACKMAN_USER}:${PGBACKMAN_GROUP} ${PGBACKMAN_LOGDIR}
26 | chmod -R 775 ${PGBACKMAN_LOGDIR}
27 | fi
28 |
29 | systemctl daemon-reload
30 | ;;
31 |
32 | abort-upgrade|abort-remove|abort-deconfigure)
33 | ;;
34 |
35 | *)
36 | echo "postinst called with unknown argument \`$1'" >&2
37 | exit 1
38 | ;;
39 | esac
40 |
41 | # dh_installdeb will replace this with shell code automatically
42 | # generated by other debhelper scripts.
43 |
44 | #DEBHELPER#
45 |
46 | exit 0
47 |
--------------------------------------------------------------------------------
/debian/rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 | %:
3 | dh $@ --with python2
4 |
5 |
--------------------------------------------------------------------------------
/debian/source/format:
--------------------------------------------------------------------------------
1 | 3.0 (native)
2 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | #
2 | # Makefile
3 | #
4 |
5 | VERSION="1.2.0"
6 |
7 | all: html man pdf
8 |
9 | html:
10 | rst2html -gdt --stylesheet-path=style.css manual.rst > pgbackman-manual-$(VERSION).html
11 | rst2html -gdt --stylesheet-path=style.css manual_es.rst > pgbackman-manual-$(VERSION)_es.html
12 |
13 | man:
14 | rst2man manual.rst > pgbackman-manual-$(VERSION).man
15 | rst2man manual_es.rst > pgbackman-manual-$(VERSION)_es.man
16 |
17 | pdf:
18 | cat manual.rst | sed s/":scale: 50%"/":scale: 100%"/g > manual.tmp
19 | rst2pdf --output pgbackman-manual-$(VERSION).pdf manual.tmp
20 | rm -f manual.tmp
21 | cat manual_es.rst | sed s/":scale: 50%"/":scale: 100%"/g > manual_es.tmp
22 | rst2pdf --output pgbackman-manual-$(VERSION)_es.pdf manual_es.tmp
23 | rm -f manual_es.tmp
24 |
25 | clean:
26 | rm -f pgbackman-manual-$(VERSION)*.html
27 | rm -f pgbackman-manual-$(VERSION)*.man
28 | rm -f pgbackman-manual-$(VERSION)*.pdf
29 | rm -f *~
30 |
--------------------------------------------------------------------------------
/docs/images/architecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelma/pgbackman/b0a1878abbf3aa976ea08dba64c24ccafffc6680/docs/images/architecture.jpg
--------------------------------------------------------------------------------
/docs/images/components.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelma/pgbackman/b0a1878abbf3aa976ea08dba64c24ccafffc6680/docs/images/components.jpg
--------------------------------------------------------------------------------
/docs/images/register_restore.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelma/pgbackman/b0a1878abbf3aa976ea08dba64c24ccafffc6680/docs/images/register_restore.jpg
--------------------------------------------------------------------------------
/docs/release-notes.rst:
--------------------------------------------------------------------------------
1 | =============
2 | Release Notes
3 | =============
4 |
5 | Version 1.1.0
6 | =============
7 |
8 | Date: 2015-10-28
9 |
10 | This new release implements some new features and fix some bugs from
11 | version 1.0.0.
12 |
13 | New features
14 | ------------
15 |
16 | * Add functionality to stop automatically all backup definitions for
17 | databases that have been deleted with DROP DATABASE or renamed in
18 | the PgSQL nodes running them.
19 |
20 | The affected backup definitions will get a DELETED status and all
21 | files associated to the affected backup definitions will be kept
22 | only for the period of time defined by the
23 | automatic_deletion_retention parameter for the PgSQL node that was
24 | running the deleted database.
25 |
26 | The associated files will get deleted regardless of the
27 | retention_period and retention_redundancy values for the affected
28 | backup definitions.
29 |
30 | * Possibility of pausing replication on slaves nodes when taking
31 | backups of large databases to avoid the termination of the backup
32 | process by postgreSQL.
33 |
34 | * Possibility of activating sending of alerts via SMTP when a backup,
35 | snapshot or restore terminates with an error message.
36 |
37 | * Overview of databases without backup definitions in the PgSQL nodes
38 | registered in PgBackMan.
39 |
40 | * Possibility of defining a backup definition in one run for all
41 | databases in a PgSQL node without defined backup definitions.
42 |
43 | * More parameters available when searching for backups in the catalog.
44 |
45 | * Possibility of viewing snapshots and restores in progress.
46 |
47 | * Add the possibility to define the release version of pg_dump /
48 | pg_dumpall to use when taking a backup of type snapshot.
49 |
50 | * Use PgSQL node and database information in the central PgBackMan
51 | logfile.
52 |
53 | * Automatic upgrade of the 'pgbackman' database to a new version
54 | via the PgBackMan shell.
55 |
56 |
57 | Migration to 1.1.0
58 | ------------------
59 |
60 | It is very important to check the upgrade procedure to version 1.1.0
61 | in the documentation to avoid problems and errors when and after
62 | upgrading to the new version.
63 |
64 | Check the documentation:
65 |
66 | * **[All versions]:** http://www.pgbackman.org/docs/
67 | * **[English]:** http://www.pgbackman.org/docs/pgbackman-manual-1.1.0.html
68 | * **[Español]:** http://www.pgbackman.org/docs/pgbackman-manual-1.1.0_es.html
69 |
70 |
71 | Bugfixes
72 | --------
73 |
74 | * The parameter channel_check_interval is not supported. Update
75 | pgbackman command show_pgbackman_config to not show this
76 | information.
77 |
78 | * Add dependency information for psycopg2. We need at least version
79 | 2.4 to avoid problems when executing pgbackman.
80 |
81 | * Fix problems restoring backups with several almost identical roles
82 | that include each others.
83 |
84 | * Include GRANT ... TO ... GRANTED BY .... statements in the roles
85 | backup file. We did not this if If they did not own something or had
86 | privileges in the database we were backing up
87 |
88 | * Use copytruncate with logrotate to truncate the old log file after
89 | taken a copy. We do not have functionality to tell pgbackman to
90 | close its logfile, therefor it continued writing (appending) to the
91 | previous log file forever.
92 |
93 | * Change the default pg_dump / pg_restore format to directory. We need
94 | to use this format insteed of custom if we want to have the
95 | possibility of dumping data in parallel with pg_dump.
96 |
97 | * Fix a problem when deleting a backup definition by dbname and having
98 | snapshots backups in our system. We delete data only from backup
99 | definitions and not snapshot definitions.
100 |
101 | * Standardize the use of all/* values for some parameters. It was not
102 | used consistently.
103 |
104 | * Delete /etc/pgbackman.conf as a configuration file possibility. Only
105 | /etc/pgbackman/pgbackman.log and $HOME/.pgbackman/pgbackman.conf are
106 | valid now, and the version under the home directory of the user
107 | running PgBackMan will have preference (if it exists) to the central
108 | configuration file.
109 |
110 | * Fix that command inputs with only spaces crashed the pgbackman
111 | shell.
112 |
113 |
114 | Version 1.0.0
115 | =============
116 |
117 | Date: 2014-06-26
118 |
119 | First version available to the public.
120 |
121 | Main features
122 | -------------
123 |
124 | * Central database with metadata information.
125 | * PgBackMan shell for interaction with the system.
126 | * Management of multiple backup servers.
127 | * Management of multiple PostgreSQL servers.
128 | * Management of thousands of backups dumps through a catalogue.
129 | * Manual and scheduled backups.
130 | * Management of retention policies for backups.
131 | * Fully detailed backup reports.
132 | * Multiple predefined database backup types, CLUSTER,FULL,SCHEMA,DATA.
133 | * Full backup of role information for a database.
134 | * Full backup of database configuration for a database.
135 | * Automatic definitions of backups for all databases running in a PgSQL node.
136 | * Automatic restore procedures.
137 | * Autonomous pgbackman_dump program that functions even if the central database with metadata information is not available.
138 | * Handling of error situations.
139 |
--------------------------------------------------------------------------------
/docs/style.css:
--------------------------------------------------------------------------------
1 | @import url(html4css1.css);
2 |
3 | /**
4 | * :Author: Chad Skeeters
5 | * :Contact: cskeeters@nciinc.com
6 | * Stylesheet for use with Docutils/rst2html.
7 | * Example: rst2html --stylesheet=rst2html.css README.rst doc/html/README.html
8 | */
9 |
10 | html {
11 | font-size: 100%;
12 | -webkit-text-size-adjust: 100%;
13 | -ms-text-size-adjust: 100%;
14 | }
15 |
16 | a:focus {
17 | outline: thin dotted #333;
18 | outline: 5px auto -webkit-focus-ring-color;
19 | outline-offset: -2px;
20 | }
21 |
22 | a:hover,
23 | a:active {
24 | outline: 0;
25 | }
26 |
27 | sub,
28 | sup {
29 | position: relative;
30 | font-size: 75%;
31 | line-height: 0;
32 | vertical-align: baseline;
33 | }
34 |
35 | sup {
36 | top: -0.5em;
37 | }
38 |
39 | sub {
40 | bottom: -0.25em;
41 | }
42 |
43 | img {
44 | width: auto\9;
45 | height: auto;
46 | max-width: 100%;
47 | vertical-align: middle;
48 | border: 0;
49 | -ms-interpolation-mode: bicubic;
50 | }
51 |
52 | @media print {
53 | * {
54 | color: #000 !important;
55 | text-shadow: none !important;
56 | background: transparent !important;
57 | box-shadow: none !important;
58 | }
59 |
60 | a,
61 | a:visited {
62 | text-decoration: underline;
63 | }
64 |
65 | a[href]:after {
66 | content: " (" attr(href) ")";
67 | }
68 |
69 | abbr[title]:after {
70 | content: " (" attr(title) ")";
71 | }
72 |
73 | .ir a:after,
74 | a[href^="javascript:"]:after,
75 | a[href^="#"]:after {
76 | content: "";
77 | }
78 |
79 | pre,
80 | blockquote {
81 | border: 1px solid #999;
82 | page-break-inside: avoid;
83 | }
84 |
85 | thead {
86 | display: table-header-group;
87 | }
88 |
89 | tr,
90 | img {
91 | page-break-inside: avoid;
92 | }
93 |
94 | img {
95 | max-width: 100% !important;
96 | }
97 | @ page {
98 | margin: 0.5cm;
99 | }
100 |
101 | h1 {
102 | page-break-before: always;
103 | }
104 |
105 | h1.title {
106 | page-break-before: avoid;
107 | }
108 |
109 | p,
110 | h2,
111 | h3 {
112 | orphans: 3;
113 | widows: 3;
114 | }
115 |
116 | h2,
117 | h3 {
118 | page-break-after: avoid;
119 | }
120 | }
121 |
122 | body {
123 | margin: 40px;
124 | margin-right: auto;
125 | margin-left: auto;
126 | width: 880px;
127 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
128 | font-size: 14px;
129 | line-height: 20px;
130 | color: #333333;
131 | background-color: #ffffff;
132 | }
133 |
134 | a {
135 | color: #0088cc;
136 | text-decoration: none;
137 | }
138 |
139 | a:hover,
140 | a:focus {
141 | color: #005580;
142 | text-decoration: underline;
143 | }
144 |
145 | .img-rounded {
146 | -webkit-border-radius: 6px;
147 | -moz-border-radius: 6px;
148 | border-radius: 6px;
149 | }
150 |
151 | .img-polaroid {
152 | padding: 4px;
153 | background-color: #fff;
154 | border: 1px solid #ccc;
155 | border: 1px solid rgba(0, 0, 0, 0.2);
156 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
157 | -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
158 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
159 | }
160 |
161 | p {
162 | margin: 0 0 10px;
163 | }
164 |
165 | small {
166 | font-size: 85%;
167 | }
168 |
169 | strong {
170 | font-weight: bold;
171 | }
172 |
173 | em {
174 | font-style: italic;
175 | }
176 |
177 | cite {
178 | font-style: normal;
179 | }
180 |
181 | h1,
182 | h2,
183 | h3,
184 | h4,
185 | h5,
186 | h6 {
187 | font-family: inherit;
188 | font-weight: bold;
189 | line-height: 20px;
190 | color: inherit;
191 | text-rendering: optimizelegibility;
192 | }
193 |
194 | h1 {
195 | font-size: 2em;
196 | padding-bottom: .2em;
197 | border-bottom: 1px solid grey;
198 | }
199 |
200 | h1.title {
201 | padding-bottom: 1em;
202 | border-bottom: 0px;
203 | }
204 |
205 | h2 {
206 | font-size: 1.5em;
207 | }
208 |
209 | h3 {
210 | font-size: 1.3em;
211 | font-family: Georgia, serif;
212 | font-style: italic;
213 | /*font-weight:normal;*/;
214 | }
215 |
216 | h4 {
217 | font-size: 1.3em;
218 | }
219 |
220 | h5 {
221 | font-size: 1.2em;
222 | }
223 |
224 | h6 {
225 | font-size: 1.1em;
226 | }
227 |
228 | ul,
229 | ol {
230 | padding: 0;
231 | margin: 0 0 10px 25px;
232 | }
233 |
234 | ul ul,
235 | ul ol,
236 | ol ol,
237 | ol ul {
238 | margin-bottom: 0;
239 | }
240 |
241 | li {
242 | line-height: 20px;
243 | }
244 |
245 | dl {
246 | margin-bottom: 20px;
247 | }
248 |
249 | dt,
250 | dd {
251 | line-height: 20px;
252 | }
253 |
254 | dt {
255 | font-weight: bold;
256 | }
257 |
258 | dd {
259 | margin-left: 10px;
260 | }
261 |
262 | hr {
263 | margin: 20px 0;
264 | border: 0;
265 | border-top: 1px solid #eeeeee;
266 | border-bottom: 1px solid #ffffff;
267 | }
268 |
269 | abbr[title],
270 | abbr[data-original-title] {
271 | cursor: help;
272 | border-bottom: 1px dotted #999999;
273 | }
274 |
275 | abbr.initialism {
276 | font-size: 90%;
277 | text-transform: uppercase;
278 | }
279 |
280 | blockquote {
281 | padding: 0 0 0 15px;
282 | margin: 0 0 20px;
283 | border-left: 5px solid #eeeeee;
284 | }
285 |
286 | blockquote p {
287 | margin-bottom: 0;
288 | font-size: 17.5px;
289 | font-weight: 300;
290 | line-height: 1.25;
291 | }
292 |
293 | q:before,
294 | q:after,
295 | blockquote:before,
296 | blockquote:after {
297 | content: "";
298 | }
299 |
300 | address {
301 | display: block;
302 | margin-bottom: 20px;
303 | font-style: normal;
304 | line-height: 20px;
305 | }
306 |
307 | code,
308 | pre {
309 | padding: 0 3px 2px;
310 | font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
311 | font-size: 12px;
312 | color: #333333;
313 | -webkit-border-radius: 3px;
314 | -moz-border-radius: 3px;
315 | border-radius: 3px;
316 | }
317 |
318 | code {
319 | padding: 2px 4px;
320 | color: #d14;
321 | white-space: nowrap;
322 | background-color: #f7f7f9;
323 | border: 1px solid #e1e1e8;
324 | }
325 |
326 | pre {
327 | display: block;
328 | padding: 9.5px;
329 | margin: 0 0 10px;
330 | font-size: 13px;
331 | line-height: 20px;
332 | #word-break: break-all;
333 | #word-wrap: break-word;
334 | #white-space: pre;
335 | #white-space: pre-wrap;
336 | background-color: #f5f5f5;
337 | border: 1px solid #ccc;
338 | border: 1px solid rgba(0, 0, 0, 0.15);
339 | -webkit-border-radius: 4px;
340 | -moz-border-radius: 4px;
341 | border-radius: 4px;
342 | overflow: auto;
343 | }
344 |
345 | pre.prettyprint {
346 | margin-bottom: 20px;
347 | }
348 |
349 | pre code {
350 | padding: 0;
351 | color: inherit;
352 | white-space: pre;
353 | white-space: pre-wrap;
354 | background-color: transparent;
355 | border: 0;
356 | }
357 |
358 | .pre-scrollable {
359 | max-height: 340px;
360 | overflow-y: scroll;
361 | }
362 |
363 | table {
364 | max-width: 100%;
365 | background-color: transparent;
366 | border-collapse: collapse;
367 | border-spacing: 0;
368 | }
369 |
370 | .table {
371 | width: 100%;
372 | margin-bottom: 20px;
373 | }
374 |
375 | .table th,
376 | .table td {
377 | padding: 8px;
378 | line-height: 20px;
379 | text-align: left;
380 | vertical-align: top;
381 | border-top: 1px solid #dddddd;
382 | }
383 |
384 | .table th {
385 | font-weight: bold;
386 | }
387 |
388 | .table thead th {
389 | vertical-align: bottom;
390 | }
391 |
392 | .table caption + thead tr:first-child th,
393 | .table caption + thead tr:first-child td,
394 | .table colgroup + thead tr:first-child th,
395 | .table colgroup + thead tr:first-child td,
396 | .table thead:first-child tr:first-child th,
397 | .table thead:first-child tr:first-child td {
398 | border-top: 0;
399 | }
400 |
401 | .table tbody + tbody {
402 | border-top: 2px solid #dddddd;
403 | }
404 |
405 | .table .table {
406 | background-color: #ffffff;
407 | }
408 |
409 | .table-condensed th,
410 | .table-condensed td {
411 | padding: 4px 5px;
412 | }
413 |
414 | .table-bordered {
415 | border: 1px solid #dddddd;
416 | border-collapse: separate;
417 | *border-collapse: collapse;
418 | border-left: 0;
419 | -webkit-border-radius: 4px;
420 | -moz-border-radius: 4px;
421 | border-radius: 4px;
422 | }
423 |
424 | .table-bordered th,
425 | .table-bordered td {
426 | border-left: 1px solid #dddddd;
427 | }
428 |
429 | .table-bordered caption + thead tr:first-child th,
430 | .table-bordered caption + tbody tr:first-child th,
431 | .table-bordered caption + tbody tr:first-child td,
432 | .table-bordered colgroup + thead tr:first-child th,
433 | .table-bordered colgroup + tbody tr:first-child th,
434 | .table-bordered colgroup + tbody tr:first-child td,
435 | .table-bordered thead:first-child tr:first-child th,
436 | .table-bordered tbody:first-child tr:first-child th,
437 | .table-bordered tbody:first-child tr:first-child td {
438 | border-top: 0;
439 | }
440 |
441 | .table-bordered thead:first-child tr:first-child > th:first-child,
442 | .table-bordered tbody:first-child tr:first-child > td:first-child,
443 | .table-bordered tbody:first-child tr:first-child > th:first-child {
444 | -webkit-border-top-left-radius: 4px;
445 | border-top-left-radius: 4px;
446 | -moz-border-radius-topleft: 4px;
447 | }
448 |
449 | .table-bordered thead:first-child tr:first-child > th:last-child,
450 | .table-bordered tbody:first-child tr:first-child > td:last-child,
451 | .table-bordered tbody:first-child tr:first-child > th:last-child {
452 | -webkit-border-top-right-radius: 4px;
453 | border-top-right-radius: 4px;
454 | -moz-border-radius-topright: 4px;
455 | }
456 |
457 | .table-bordered thead:last-child tr:last-child > th:first-child,
458 | .table-bordered tbody:last-child tr:last-child > td:first-child,
459 | .table-bordered tbody:last-child tr:last-child > th:first-child,
460 | .table-bordered tfoot:last-child tr:last-child > td:first-child,
461 | .table-bordered tfoot:last-child tr:last-child > th:first-child {
462 | -webkit-border-bottom-left-radius: 4px;
463 | border-bottom-left-radius: 4px;
464 | -moz-border-radius-bottomleft: 4px;
465 | }
466 |
467 | .table-bordered thead:last-child tr:last-child > th:last-child,
468 | .table-bordered tbody:last-child tr:last-child > td:last-child,
469 | .table-bordered tbody:last-child tr:last-child > th:last-child,
470 | .table-bordered tfoot:last-child tr:last-child > td:last-child,
471 | .table-bordered tfoot:last-child tr:last-child > th:last-child {
472 | -webkit-border-bottom-right-radius: 4px;
473 | border-bottom-right-radius: 4px;
474 | -moz-border-radius-bottomright: 4px;
475 | }
476 |
477 | .table-bordered tfoot + tbody:last-child tr:last-child td:first-child {
478 | -webkit-border-bottom-left-radius: 0;
479 | border-bottom-left-radius: 0;
480 | -moz-border-radius-bottomleft: 0;
481 | }
482 |
483 | .table-bordered tfoot + tbody:last-child tr:last-child td:last-child {
484 | -webkit-border-bottom-right-radius: 0;
485 | border-bottom-right-radius: 0;
486 | -moz-border-radius-bottomright: 0;
487 | }
488 |
489 | .table-bordered caption + thead tr:first-child th:first-child,
490 | .table-bordered caption + tbody tr:first-child td:first-child,
491 | .table-bordered colgroup + thead tr:first-child th:first-child,
492 | .table-bordered colgroup + tbody tr:first-child td:first-child {
493 | -webkit-border-top-left-radius: 4px;
494 | border-top-left-radius: 4px;
495 | -moz-border-radius-topleft: 4px;
496 | }
497 |
498 | .table-bordered caption + thead tr:first-child th:last-child,
499 | .table-bordered caption + tbody tr:first-child td:last-child,
500 | .table-bordered colgroup + thead tr:first-child th:last-child,
501 | .table-bordered colgroup + tbody tr:first-child td:last-child {
502 | -webkit-border-top-right-radius: 4px;
503 | border-top-right-radius: 4px;
504 | -moz-border-radius-topright: 4px;
505 | }
506 |
507 | .table-striped tbody > tr:nth-child(odd) > td,
508 | .table-striped tbody > tr:nth-child(odd) > th {
509 | background-color: #f9f9f9;
510 | }
511 |
512 | .table-hover tbody tr:hover > td,
513 | .table-hover tbody tr:hover > th {
514 | background-color: #f5f5f5;
515 | }
516 |
517 | table td[class*="span"],
518 | table th[class*="span"],
519 | .row-fluid table td[class*="span"],
520 | .row-fluid table th[class*="span"] {
521 | display: table-cell;
522 | float: none;
523 | margin-left: 0;
524 | }
525 |
526 | .hero-unit {
527 | padding: 60px;
528 | margin-bottom: 30px;
529 | font-size: 18px;
530 | font-weight: 200;
531 | line-height: 30px;
532 | color: inherit;
533 | background-color: #eeeeee;
534 | -webkit-border-radius: 6px;
535 | -moz-border-radius: 6px;
536 | border-radius: 6px;
537 | }
538 |
539 | .hero-unit h1 {
540 | margin-bottom: 0;
541 | font-size: 60px;
542 | line-height: 1;
543 | letter-spacing: -1px;
544 | color: inherit;
545 | }
546 |
547 | .hero-unit li {
548 | line-height: 30px;
549 | }
550 |
551 |
552 | /* rst2html default used to remove borders from tables and images */
553 | .borderless, table.borderless td, table.borderless th {
554 | border: 0;
555 | }
556 |
557 | table.borderless td, table.borderless th {
558 | /* Override padding for "table.docutils td" with "! important".
559 | The right padding separates the table cells. */
560 | padding: 0 0.5em 0 0 ! important;
561 | }
562 |
563 | .first {
564 | /* Override more specific margin styles with "! important". */
565 | margin-top: 0 ! important;
566 | }
567 |
568 | .last, .with-subtitle {
569 | margin-bottom: 0 ! important;
570 | }
571 |
572 | .hidden {
573 | display: none;
574 | }
575 |
576 | a.toc-backref {
577 | text-decoration: none;
578 | color: black;
579 | }
580 |
581 | blockquote.epigraph {
582 | margin: 2em 5em;
583 | }
584 |
585 | dl.docutils dd {
586 | margin-bottom: 0.5em;
587 | }
588 |
589 | object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
590 | overflow: hidden;
591 | }
592 |
593 | /* Uncomment (and remove this text!) to get bold-faced definition list terms
594 | dl.docutils dt {
595 | font-weight: bold }
596 | */
597 |
598 | div.abstract {
599 | margin: 2em 5em;
600 | }
601 |
602 | div.abstract p.topic-title {
603 | font-weight: bold;
604 | text-align: center;
605 | }
606 |
607 | div.admonition, div.attention, div.caution, div.danger, div.error,
608 | div.hint, div.important, div.note, div.tip, div.warning {
609 | margin: 2em;
610 | border: medium outset;
611 | padding: 1em;
612 | }
613 |
614 | div.note, div.warning {
615 | margin: 1.5em 0px;
616 | border: none;
617 | }
618 |
619 | div.note p.admonition-title,
620 | div.warning p.admonition-title {
621 | display: none;
622 | }
623 |
624 | /* Clearfix
625 | * http://css-tricks.com/snippets/css/clear-fix/
626 | */
627 |
628 | div.note:after,
629 | div.warning:after {
630 | content: "";
631 | display: table;
632 | clear: both;
633 | }
634 |
635 | div.note p:before,
636 | div.warning p:before {
637 | display: block;
638 | float: left;
639 | font-size: 4em;
640 | line-height: 1em;
641 | margin-right: 20px;
642 | margin-left: 0em;
643 | margin-top: -10px;
644 | content: '\0270D';
645 | /*handwriting*/;
646 | }
647 |
648 | div.warning p:before {
649 | content: '\026A0';
650 | /*warning*/;
651 | }
652 |
653 | div.admonition p.admonition-title, div.hint p.admonition-title,
654 | div.important p.admonition-title, div.note p.admonition-title,
655 | div.tip p.admonition-title {
656 | font-weight: bold;
657 | font-family: sans-serif;
658 | }
659 |
660 | div.attention p.admonition-title, div.caution p.admonition-title,
661 | div.danger p.admonition-title, div.error p.admonition-title,
662 | div.warning p.admonition-title, .code .error {
663 | color: red;
664 | font-weight: bold;
665 | font-family: sans-serif;
666 | }
667 |
668 | /* Uncomment (and remove this text!) to get reduced vertical space in
669 | compound paragraphs.
670 | div.compound .compound-first, div.compound .compound-middle {
671 | margin-bottom: 0.5em }
672 |
673 | div.compound .compound-last, div.compound .compound-middle {
674 | margin-top: 0.5em }
675 | */
676 |
677 | div.dedication {
678 | margin: 2em 5em;
679 | text-align: center;
680 | font-style: italic;
681 | }
682 |
683 | div.dedication p.topic-title {
684 | font-weight: bold;
685 | font-style: normal;
686 | }
687 |
688 | div.figure {
689 | margin-left: 2em;
690 | margin-right: 2em;
691 | }
692 |
693 | div.footer, div.header {
694 | clear: both;
695 | font-size: smaller;
696 | }
697 |
698 | div.line-block {
699 | display: block;
700 | margin-top: 1em;
701 | margin-bottom: 1em;
702 | }
703 |
704 | div.line-block div.line-block {
705 | margin-top: 0;
706 | margin-bottom: 0;
707 | margin-left: 1.5em;
708 | }
709 |
710 | div.sidebar {
711 | margin: 0 0 0.5em 1em;
712 | border: medium outset;
713 | padding: 1em;
714 | background-color: #ffffee;
715 | width: 40%;
716 | float: right;
717 | clear: right;
718 | }
719 |
720 | div.sidebar p.rubric {
721 | font-family: sans-serif;
722 | font-size: medium;
723 | }
724 |
725 | div.system-messages {
726 | margin: 5em;
727 | }
728 |
729 | div.system-messages h1 {
730 | color: red;
731 | }
732 |
733 | div.system-message {
734 | border: medium outset;
735 | padding: 1em;
736 | }
737 |
738 | div.system-message p.system-message-title {
739 | color: red;
740 | font-weight: bold;
741 | }
742 |
743 | div.topic {
744 | margin: 2em;
745 | }
746 |
747 | h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
748 | h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
749 | margin-top: 0.4em;
750 | }
751 |
752 | h1.title {
753 | text-align: center;
754 | }
755 |
756 | h2.subtitle {
757 | text-align: center;
758 | }
759 |
760 | hr.docutils {
761 | width: 75%;
762 | }
763 |
764 | img.align-left, .figure.align-left, object.align-left {
765 | clear: left;
766 | float: left;
767 | margin-right: 1em;
768 | }
769 |
770 | img.align-right, .figure.align-right, object.align-right {
771 | clear: right;
772 | float: right;
773 | margin-left: 1em;
774 | }
775 |
776 | img.align-center, .figure.align-center, object.align-center {
777 | display: block;
778 | margin-left: auto;
779 | margin-right: auto;
780 | }
781 |
782 | .align-left {
783 | text-align: left;
784 | }
785 |
786 | .align-center {
787 | clear: both;
788 | text-align: center;
789 | }
790 |
791 | .align-right {
792 | text-align: right;
793 | }
794 |
795 | /* reset inner alignment in figures */
796 | div.align-right {
797 | text-align: inherit;
798 | }
799 |
800 | /* div.align-center * { */
801 | /* text-align: left } */
802 |
803 | ol.simple, ul.simple {
804 | margin-bottom: 1em;
805 | }
806 |
807 | ol.arabic {
808 | list-style: decimal;
809 | }
810 |
811 | ol.loweralpha {
812 | list-style: lower-alpha;
813 | }
814 |
815 | ol.upperalpha {
816 | list-style: upper-alpha;
817 | }
818 |
819 | ol.lowerroman {
820 | list-style: lower-roman;
821 | }
822 |
823 | ol.upperroman {
824 | list-style: upper-roman;
825 | }
826 |
827 | p.attribution {
828 | text-align: right;
829 | margin-left: 50%;
830 | }
831 |
832 | p.caption {
833 | font-style: italic;
834 | }
835 |
836 | p.credits {
837 | font-style: italic;
838 | font-size: smaller;
839 | }
840 |
841 | p.label {
842 | white-space: nowrap;
843 | }
844 |
845 | p.rubric {
846 | font-weight: bold;
847 | font-size: larger;
848 | color: maroon;
849 | text-align: center;
850 | }
851 |
852 | p.sidebar-title {
853 | font-family: sans-serif;
854 | font-weight: bold;
855 | font-size: larger;
856 | }
857 |
858 | p.sidebar-subtitle {
859 | font-family: sans-serif;
860 | font-weight: bold;
861 | }
862 |
863 | p.topic-title {
864 | font-weight: bold;
865 | }
866 |
867 | pre.address {
868 | margin-bottom: 0;
869 | margin-top: 0;
870 | font: inherit;
871 | }
872 |
873 | pre.literal-block, pre.doctest-block, pre.math, pre.code {
874 | margin-left: 2em;
875 | margin-right: 2em;
876 | }
877 |
878 | pre.code .ln {
879 | color: grey;
880 | } /* line numbers */
881 | pre.code, code {
882 | background-color: #eeeeee;
883 | }
884 |
885 | pre.code .comment, code .comment {
886 | color: #5C6576;
887 | }
888 |
889 | pre.code .keyword, code .keyword {
890 | color: #3B0D06;
891 | font-weight: bold;
892 | }
893 |
894 | pre.code .literal.string, code .literal.string {
895 | color: #0C5404;
896 | }
897 |
898 | pre.code .name.builtin, code .name.builtin {
899 | color: #352B84;
900 | }
901 |
902 | pre.code .deleted, code .deleted {
903 | background-color: #DEB0A1;
904 | }
905 |
906 | pre.code .inserted, code .inserted {
907 | background-color: #A3D289;
908 | }
909 |
910 | span.classifier {
911 | font-family: sans-serif;
912 | font-style: oblique;
913 | }
914 |
915 | span.classifier-delimiter {
916 | font-family: sans-serif;
917 | font-weight: bold;
918 | }
919 |
920 | span.interpreted {
921 | font-family: sans-serif;
922 | }
923 |
924 | span.option {
925 | white-space: nowrap;
926 | }
927 |
928 | span.pre {
929 | white-space: pre;
930 | }
931 |
932 | span.problematic {
933 | color: red;
934 | }
935 |
936 | span.section-subtitle {
937 | /* font-size relative to parent (h1..h6 element) */
938 | font-size: 80%;
939 | }
940 |
941 | table.citation {
942 | border-left: solid 1px gray;
943 | margin-left: 1px;
944 | }
945 |
946 | table.docinfo {
947 | margin: 2em 4em;
948 | }
949 |
950 | table.docutils {
951 | margin-top: 0.5em;
952 | margin-bottom: 0.5em;
953 | }
954 |
955 | table.footnote {
956 | border-left: solid 1px black;
957 | margin-left: 1px;
958 | }
959 |
960 | table.docutils td, table.docutils th,
961 | table.docinfo td, table.docinfo th {
962 | padding-left: 0.5em;
963 | padding-right: 0.5em;
964 | vertical-align: top;
965 | }
966 |
967 | table.docutils th.field-name, table.docinfo th.docinfo-name {
968 | font-weight: bold;
969 | text-align: left;
970 | white-space: nowrap;
971 | padding-left: 0;
972 | }
973 |
974 | h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
975 | h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
976 | font-size: 100%;
977 | }
978 |
979 | ul.auto-toc {
980 | list-style-type: none;
981 | }
982 |
983 | .code .pygments-hll {
984 | background-color: #ffffcc;
985 | }
986 |
987 | .code .pygments-c {
988 | color: #60a0b0;
989 | font-style: italic;
990 | } /* Comment */
991 | .code .pygments-err {
992 | border: 1px solid #FF0000;
993 | } /* Error */
994 | .code .pygments-k {
995 | color: #007020;
996 | font-weight: bold;
997 | } /* Keyword */
998 | .code .pygments-o {
999 | color: #666666;
1000 | } /* Operator */
1001 | .code .pygments-cm {
1002 | color: #60a0b0;
1003 | font-style: italic;
1004 | } /* Comment.Multiline */
1005 | .code .pygments-cp {
1006 | color: #007020;
1007 | } /* Comment.Preproc */
1008 | .code .pygments-c1 {
1009 | color: #60a0b0;
1010 | font-style: italic;
1011 | } /* Comment.Single */
1012 | .code .pygments-cs {
1013 | color: #60a0b0;
1014 | background-color: #fff0f0;
1015 | } /* Comment.Special */
1016 | .code .pygments-gd {
1017 | color: #A00000;
1018 | } /* Generic.Deleted */
1019 | .code .pygments-ge {
1020 | font-style: italic;
1021 | } /* Generic.Emph */
1022 | .code .pygments-gr {
1023 | color: #FF0000;
1024 | } /* Generic.Error */
1025 | .code .pygments-gh {
1026 | color: #000080;
1027 | font-weight: bold;
1028 | } /* Generic.Heading */
1029 | .code .pygments-gi {
1030 | color: #00A000;
1031 | } /* Generic.Inserted */
1032 | .code .pygments-go {
1033 | color: #888888;
1034 | } /* Generic.Output */
1035 | .code .pygments-gp {
1036 | color: #c65d09;
1037 | font-weight: bold;
1038 | } /* Generic.Prompt */
1039 | .code .pygments-gs {
1040 | font-weight: bold;
1041 | } /* Generic.Strong */
1042 | .code .pygments-gu {
1043 | color: #800080;
1044 | font-weight: bold;
1045 | } /* Generic.Subheading */
1046 | .code .pygments-gt {
1047 | color: #0044DD;
1048 | } /* Generic.Traceback */
1049 | .code .pygments-kc {
1050 | color: #007020;
1051 | font-weight: bold;
1052 | } /* Keyword.Constant */
1053 | .code .pygments-kd {
1054 | color: #007020;
1055 | font-weight: bold;
1056 | } /* Keyword.Declaration */
1057 | .code .pygments-kn {
1058 | color: #007020;
1059 | font-weight: bold;
1060 | } /* Keyword.Namespace */
1061 | .code .pygments-kp {
1062 | color: #007020;
1063 | } /* Keyword.Pseudo */
1064 | .code .pygments-kr {
1065 | color: #007020;
1066 | font-weight: bold;
1067 | } /* Keyword.Reserved */
1068 | .code .pygments-kt {
1069 | color: #902000;
1070 | } /* Keyword.Type */
1071 | .code .pygments-m {
1072 | color: #40a070;
1073 | } /* Literal.Number */
1074 | .code .pygments-s {
1075 | color: #4070a0;
1076 | } /* Literal.String */
1077 | .code .pygments-na {
1078 | color: #4070a0;
1079 | } /* Name.Attribute */
1080 | .code .pygments-nb {
1081 | color: #007020;
1082 | } /* Name.Builtin */
1083 | .code .pygments-nc {
1084 | color: #0e84b5;
1085 | font-weight: bold;
1086 | } /* Name.Class */
1087 | .code .pygments-no {
1088 | color: #60add5;
1089 | } /* Name.Constant */
1090 | .code .pygments-nd {
1091 | color: #555555;
1092 | font-weight: bold;
1093 | } /* Name.Decorator */
1094 | .code .pygments-ni {
1095 | color: #d55537;
1096 | font-weight: bold;
1097 | } /* Name.Entity */
1098 | .code .pygments-ne {
1099 | color: #007020;
1100 | } /* Name.Exception */
1101 | .code .pygments-nf {
1102 | color: #06287e;
1103 | } /* Name.Function */
1104 | .code .pygments-nl {
1105 | color: #002070;
1106 | font-weight: bold;
1107 | } /* Name.Label */
1108 | .code .pygments-nn {
1109 | color: #0e84b5;
1110 | font-weight: bold;
1111 | } /* Name.Namespace */
1112 | .code .pygments-nt {
1113 | color: #062873;
1114 | font-weight: bold;
1115 | } /* Name.Tag */
1116 | .code .pygments-nv {
1117 | color: #bb60d5;
1118 | } /* Name.Variable */
1119 | .code .pygments-ow {
1120 | color: #007020;
1121 | font-weight: bold;
1122 | } /* Operator.Word */
1123 | .code .pygments-w {
1124 | color: #bbbbbb;
1125 | } /* Text.Whitespace */
1126 | .code .pygments-mf {
1127 | color: #40a070;
1128 | } /* Literal.Number.Float */
1129 | .code .pygments-mh {
1130 | color: #40a070;
1131 | } /* Literal.Number.Hex */
1132 | .code .pygments-mi {
1133 | color: #40a070;
1134 | } /* Literal.Number.Integer */
1135 | .code .pygments-mo {
1136 | color: #40a070;
1137 | } /* Literal.Number.Oct */
1138 | .code .pygments-sb {
1139 | color: #4070a0;
1140 | } /* Literal.String.Backtick */
1141 | .code .pygments-sc {
1142 | color: #4070a0;
1143 | } /* Literal.String.Char */
1144 | .code .pygments-sd {
1145 | color: #4070a0;
1146 | font-style: italic;
1147 | } /* Literal.String.Doc */
1148 | .code .pygments-s2 {
1149 | color: #4070a0;
1150 | } /* Literal.String.Double */
1151 | .code .pygments-se {
1152 | color: #4070a0;
1153 | font-weight: bold;
1154 | } /* Literal.String.Escape */
1155 | .code .pygments-sh {
1156 | color: #4070a0;
1157 | } /* Literal.String.Heredoc */
1158 | .code .pygments-si {
1159 | color: #70a0d0;
1160 | font-style: italic;
1161 | } /* Literal.String.Interpol */
1162 | .code .pygments-sx {
1163 | color: #c65d09;
1164 | } /* Literal.String.Other */
1165 | .code .pygments-sr {
1166 | color: #235388;
1167 | } /* Literal.String.Regex */
1168 | .code .pygments-s1 {
1169 | color: #4070a0;
1170 | } /* Literal.String.Single */
1171 | .code .pygments-ss {
1172 | color: #517918;
1173 | } /* Literal.String.Symbol */
1174 | .code .pygments-bp {
1175 | color: #007020;
1176 | } /* Name.Builtin.Pseudo */
1177 | .code .pygments-vc {
1178 | color: #bb60d5;
1179 | } /* Name.Variable.Class */
1180 | .code .pygments-vg {
1181 | color: #bb60d5;
1182 | } /* Name.Variable.Global */
1183 | .code .pygments-vi {
1184 | color: #bb60d5;
1185 | } /* Name.Variable.Instance */
1186 | .code .pygments-il {
1187 | color: #40a070;
1188 | } /* Literal.Number.Integer.Long */
1189 |
--------------------------------------------------------------------------------
/etc/pgbackman-alerts.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=pgbackman alerts service
3 |
4 | [Service]
5 | User=root
6 | Group=root
7 | Restart=always
8 | ExecStart=/usr/bin/pgbackman_alerts
9 |
10 | [Install]
11 | WantedBy=multi-user.target
12 |
--------------------------------------------------------------------------------
/etc/pgbackman-control.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=pgbackman control service
3 |
4 | [Service]
5 | User=root
6 | Group=root
7 | Restart=always
8 | ExecStart=/usr/bin/pgbackman_control
9 |
10 | [Install]
11 | WantedBy=multi-user.target
12 |
--------------------------------------------------------------------------------
/etc/pgbackman-maintenance.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=pgbackman maintenance service
3 |
4 | [Service]
5 | User=root
6 | Group=root
7 | Restart=always
8 | ExecStart=/usr/bin/pgbackman_maintenance
9 |
10 | [Install]
11 | WantedBy=multi-user.target
12 |
--------------------------------------------------------------------------------
/etc/pgbackman.conf:
--------------------------------------------------------------------------------
1 | ;
2 | ; Copyright (c) 2013-2015 Rafael Martinez Guerrero / PostgreSQL-es
3 | ; rafael@postgresql.org.es / http://www.postgresql.org.es/
4 | ;
5 | ; Copyright (c) 2014-2015 USIT-University of Oslo
6 | ;
7 | ; This file is part of PgBackMan
8 | ; https://github.com/rafaelma/pgbackman
9 | ;
10 | ; PgBackMan is free software: you can redistribute it and/or modify
11 | ; it under the terms of the GNU General Public License as published by
12 | ; the Free Software Foundation, either version 3 of the License, or
13 | ; (at your option) any later version.
14 | ;
15 | ; PgBackMan is distributed in the hope that it will be useful,
16 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 | ; GNU General Public License for more details.
19 | ;
20 | ; You should have received a copy of the GNU General Public License
21 | ; along with Pgbackman. If not, see .
22 | ;
23 | ;
24 | ; Configuration file for PgBackMan 1.1.0
25 | ;
26 |
27 | ; ######################
28 | ; Backup server section
29 | ; ######################
30 | [backup_server]
31 |
32 | ; If this server is a backup server used by pgbackman
33 | ; we can define the backup server FQDN here.
34 | ;
35 | ; If this parameter is not defined here, pgbackman will try to get this
36 | ; value from the system with socket.getfqdn() and db.get_backup_server_id()
37 | ;
38 | ;backup_server=pgbackup.example.org
39 |
40 |
41 | ; ###########################
42 | ; pgbackman database section
43 | ; ###########################
44 | [pgbackman_database]
45 |
46 | ; Database server fqdn running the pgbackman database
47 | ;host=dbserver.example.org
48 |
49 | ; Database server IP running the pgbackman databas
50 | ;hostaddr=127.0.0.1
51 |
52 | ; Database port used by pgbackman
53 | ; Default: 5432
54 | ;port=5432
55 |
56 | ; Database name used by pgbackman
57 | ; Default: pgbackman
58 | dbname=pgbackman
59 |
60 | ; User used to connect to the pgbackman database
61 | ; Default: pgbackman_role_rw
62 | user=pgbackman_role_rw
63 |
64 | ; Password for dbuser
65 | ;password=mypassword
66 |
67 | ; Interval in seconds to wait before we retry to (re)connect to the database
68 | ; in case the database server is not running
69 | ; Default: 10
70 | pg_connect_retry_interval=10
71 |
72 | ; Directory with PgBackMan database source files
73 | ; Default: /usr/share/pgbackman
74 | database_source_dir=/usr/share/pgbackman
75 |
76 | ; ######################
77 | ; pgbackman_dump section
78 | ; ######################
79 | [pgbackman_dump]
80 |
81 | ; Temp directory used to create temp files
82 | ; Default: /tmp
83 | tmp_dir=/tmp
84 |
85 | ; Activate pause/resume of recovery process when running a backup in a
86 | ; PgSQL node that is a slave/standby node in a replication
87 | ; installation
88 | ;
89 | ; NOTE: **Be carefull** with this parameter and understand the
90 | ; implications of activating it for a slave node.
91 | ;
92 | ; If you activate this parameter, the pgbackman_dump process will
93 | ; pause the recovery process on the slave node for the time it takes
94 | ; to generate the dump of the database.
95 | ;
96 | ; If you run multiple backups on the slave node that overlaps in time,
97 | ; the replication will be paused until the last backup is finish.
98 | ;
99 | ; This will increase the replication lag between the master and the
100 | ; slave. This could have an impact on your system e.g. in how much
101 | ; diskspace will be used on master and slave or if you would be able
102 | ; to catch again with the master in a very busy system.
103 | ;
104 | ; PgBackMan **does not check** if the replication has been paused for
105 | ; a long time or if the replication lag is too big.
106 | ;
107 | ; Default: OFF
108 | pause_recovery_process_on_slave=OFF
109 |
110 |
111 | ; ##############################
112 | ; pgbackman_maintenance section
113 | ; ##############################
114 | [pgbackman_maintenance]
115 |
116 | ; Interval in seconds to wait between maintenance runs
117 | ; Default: 70
118 | maintenance_interval=70
119 |
120 | ; ##############################
121 | ; pgbackman_alerts section
122 | ; ##############################
123 | [pgbackman_alerts]
124 |
125 | ; Activate email sending of pgbackman alerts
126 | ; Default: OFF
127 | smtp_alerts=OFF
128 |
129 | ; Interval in secons to wait between alarms checks
130 | ; Default: 300
131 | alerts_check_interval=300
132 |
133 | ; SMTP server
134 | ; Default: localhost
135 | smtp_server=localhost
136 |
137 | ; SMTP port SSL:465 / No-SSL:25
138 | ; Default: 25
139 | smtp_port=25
140 |
141 | ; SMTP via SSL
142 | ; Default: ON
143 | smtp_ssl=ON
144 |
145 | ; Username to login into the SMTP server
146 | ; Default: ''
147 | smtp_user=
148 |
149 | ; password to login into the SMTP server
150 | ; Default: ''
151 | smtp_password=
152 |
153 | ; SMTP From address
154 | ; smtp_from_address=user@example.org
155 |
156 | ; Template to use when sending alerts
157 | ; Default: /etc/pgbackman/pgbackman_alerts.template
158 | alerts_template=/etc/pgbackman/pgbackman_alerts.template
159 |
160 |
161 | ; ######################
162 | ; Logging section
163 | ; ######################
164 | [logging]
165 |
166 | ; Log level: DEBUG, INFO, WARN, ERROR, CRITICAL
167 | ; Default: ERROR
168 | log_level=INFO
169 |
170 | ; Log file used by pgbackman
171 | ; Default: /var/log/pgbackman/pgbackman.log
172 | log_file=/var/log/pgbackman/pgbackman.log
173 |
174 |
--------------------------------------------------------------------------------
/etc/pgbackman.log:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelma/pgbackman/b0a1878abbf3aa976ea08dba64c24ccafffc6680/etc/pgbackman.log
--------------------------------------------------------------------------------
/etc/pgbackman.logrotate:
--------------------------------------------------------------------------------
1 | /var/log/pgbackman/pgbackman.log {
2 | copytruncate
3 | missingok
4 | notifempty
5 | create 0600 pgbackman pgbackman
6 | weekly
7 | rotate 6
8 | }
9 |
--------------------------------------------------------------------------------
/etc/pgbackman_alerts.template:
--------------------------------------------------------------------------------
1 | Subject: [pgbackman_alerts] Last backup [BckID: $bck_id] for [DBname: $dbname] failed.
2 |
3 | -------------------------------------------------
4 | AlertID: $alert_id
5 | Registered: $registered
6 | -------------------------------------------------
7 | This backup job has been terminated with an error
8 |
9 | Backup server: $backup_server_fqdn
10 | PgSQL node: $pgsql_node_fqdn
11 | DBname: $dbname
12 |
13 | $alert_type ID: $ref_id
14 | BckID: $bck_id
15 |
16 | Execution status: $execution_status
17 | Error message: $error_message
18 |
19 | Run 'pgbackman show_backup_details $bck_id' for details.
20 | -------------------------------------------------
21 | Date: $date
22 | -------------------------------------------------
23 |
--------------------------------------------------------------------------------
/etc/pgbackman_init_debian.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | ### BEGIN INIT INFO
3 | # Provides: pgbackman
4 | # Required-Start: $network $local_fs $remote_fs
5 | # Required-Stop: $network $local_fs $remote_fs
6 | # Default-Start: 2 3 4 5
7 | # Default-Stop: 0 1 6
8 | # Short-Description: PostgreSQL Backup Manager
9 | ### END INIT INFO
10 |
11 | # Author: Rafael Martinez Guerrero
12 | # Homepage: http://www.pgbackman.org/
13 |
14 | PATH=/sbin:/usr/sbin:/bin:/usr/bin
15 | DESC=pgbackman # Introduce a short description here
16 | NAME=pgbackman # Introduce the short server's name here
17 | DAEMON_CONTROL=/usr/bin/pgbackman_control
18 | DAEMON_MAINTENANCE=/usr/bin/pgbackman_maintenance
19 | DAEMON_ALERTS=/usr/bin/pgbackman_alerts
20 | PIDFILE_CONTROL=/var/run/pgbackman_control.pid
21 | PIDFILE_MAINTENANCE=/var/run/pgbackman_maintenance.pid
22 | PIDFILE_ALERTS=/var/run/pgbackman_alerts.pid
23 | SCRIPTNAME=/etc/init.d/$NAME
24 |
25 | # Exit if the package is not installed
26 | [ -x $DAEMON_CONTROL ] || exit 0
27 | [ -x $DAEMON_MAINTENANCE ] || exit 0
28 | [ -x $DAEMON_ALERTS ] || exit 0
29 |
30 | # Read configuration variable file if it is present
31 | [ -r /etc/default/$NAME ] && . /etc/default/$NAME
32 |
33 | # Load the VERBOSE setting and other rcS variables
34 | . /lib/init/vars.sh
35 |
36 | # Define LSB log_* functions.
37 | # Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
38 | . /lib/lsb/init-functions
39 |
40 | #
41 | # Function that starts the daemon/service
42 | #
43 |
44 | do_start_control()
45 | {
46 | # Return
47 | # 0 if daemon has been started
48 | # 1 if daemon was already running
49 | # 2 if daemon could not be started
50 |
51 | start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE_CONTROL --startas $DAEMON_CONTROL
52 | RETVAL="$?"
53 |
54 | case $RETVAL in
55 | 0)
56 | return 0
57 | ;;
58 | 1)
59 | return 1
60 | ;;
61 | 2)
62 | return 2
63 | ;;
64 | esac
65 | }
66 |
67 | do_start_maintenance()
68 | {
69 | # Return
70 | # 0 if daemon has been started
71 | # 1 if daemon was already running
72 | # 2 if daemon could not be started
73 |
74 | start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE_MAINTENANCE --startas $DAEMON_MAINTENANCE
75 | RETVAL="$?"
76 |
77 | case $RETVAL in
78 | 0)
79 | return 0
80 | ;;
81 | 1)
82 | return 1
83 | ;;
84 | 2)
85 | return 2
86 | ;;
87 | esac
88 | }
89 |
90 | do_start_alerts()
91 | {
92 | # Return
93 | # 0 if daemon has been started
94 | # 1 if daemon was already running
95 | # 2 if daemon could not be started
96 |
97 | start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE_ALERTS --startas $DAEMON_ALERTS
98 | RETVAL="$?"
99 |
100 | case $RETVAL in
101 | 0)
102 | return 0
103 | ;;
104 | 1)
105 | return 1
106 | ;;
107 | 2)
108 | return 2
109 | ;;
110 | esac
111 | }
112 |
113 |
114 | #
115 | # Function that stops the daemon/service
116 | #
117 |
118 | do_stop_control()
119 | {
120 | # Return
121 | # 0 if daemon has been stopped
122 | # 1 if daemon was already stopped
123 | # 2 if daemon could not be stopped
124 |
125 | start-stop-daemon --stop --quiet --retry=10 --pidfile $PIDFILE_CONTROL
126 | RETVAL="$?"
127 |
128 | case $RETVAL in
129 | 0)
130 | rm -f $PIDFILE_CONTROL
131 | return 0
132 | ;;
133 | 1)
134 | return 1
135 | ;;
136 | 2)
137 | return 2
138 | ;;
139 | esac
140 | }
141 |
142 | do_stop_maintenance()
143 | {
144 | # Return
145 | # 0 if daemon has been stopped
146 | # 1 if daemon was already stopped
147 | # 2 if daemon could not be stopped
148 |
149 | start-stop-daemon --stop --quiet --retry=10 --pidfile $PIDFILE_MAINTENANCE
150 | RETVAL="$?"
151 |
152 | case $RETVAL in
153 | 0)
154 | rm -f $PIDFILE_MAINTENANCE
155 | return 0
156 | ;;
157 | 1)
158 | return 1
159 | ;;
160 | 2)
161 | return 2
162 | ;;
163 | esac
164 | }
165 |
166 | do_stop_alerts()
167 | {
168 | # Return
169 | # 0 if daemon has been stopped
170 | # 1 if daemon was already stopped
171 | # 2 if daemon could not be stopped
172 |
173 | start-stop-daemon --stop --quiet --retry=10 --pidfile $PIDFILE_ALERTS
174 | RETVAL="$?"
175 |
176 | case $RETVAL in
177 | 0)
178 | rm -f $PIDFILE_ALERTS
179 | return 0
180 | ;;
181 | 1)
182 | return 1
183 | ;;
184 | 2)
185 | return 2
186 | ;;
187 | esac
188 | }
189 |
190 |
191 | case "$1" in
192 | start)
193 |
194 | log_daemon_msg "Starting pgbackman control"
195 | do_start_control
196 |
197 | case "$?" in
198 | 0|1) log_end_msg 0 ;;
199 | 2) log_end_msg 1 ;;
200 | esac
201 |
202 | log_daemon_msg "Starting pgbackman maintenance"
203 | do_start_maintenance
204 |
205 | case "$?" in
206 | 0|1) log_end_msg 0 ;;
207 | 2) log_end_msg 1 ;;
208 | esac
209 |
210 | log_daemon_msg "Starting pgbackman alerts"
211 | do_start_alerts
212 |
213 | case "$?" in
214 | 0|1) log_end_msg 0 ;;
215 | 2) log_end_msg 1 ;;
216 | esac
217 | ;;
218 |
219 | stop)
220 |
221 | log_daemon_msg "Stopping pgabckman control"
222 | do_stop_control
223 | case "$?" in
224 | 0|1) log_end_msg 0 ;;
225 | 2) log_end_msg 1 ;;
226 | esac
227 |
228 | log_daemon_msg "Stopping pgbackman maintenance"
229 | do_stop_maintenance
230 | case "$?" in
231 | 0|1) log_end_msg 0 ;;
232 | 2) log_end_msg 1 ;;
233 | esac
234 |
235 | log_daemon_msg "Stopping pgbackman alerts"
236 | do_stop_alerts
237 | case "$?" in
238 | 0|1) log_end_msg 0 ;;
239 | 2) log_end_msg 1 ;;
240 | esac
241 | ;;
242 |
243 | status)
244 | status_of_proc -p "$PIDFILE_CONTROL" "$DAEMON_CONTROL" "pgbackman control"
245 | status_of_proc -p "$PIDFILE_MAINTENANCE" "$DAEMON_MAINTENANCE" "pgbackman maintenance"
246 | status_of_proc -p "$PIDFILE_ALERTS" "$DAEMON_ALERTS" "pgbackman alerts"
247 | exit 0
248 | ;;
249 |
250 | restart|force-reload)
251 |
252 | log_daemon_msg "Restarting pgbackman control"
253 | do_stop_control
254 | case "$?" in
255 | 0|1)
256 | do_start_control
257 | case "$?" in
258 | 0) log_end_msg 0 ;;
259 | 1) log_end_msg 1 ;; # Old process is still running
260 | *) log_end_msg 1 ;; # Failed to start
261 | esac
262 | ;;
263 | *)
264 | # Failed to stop
265 | log_end_msg 1
266 | ;;
267 | esac
268 |
269 | log_daemon_msg "Restarting pgbackman maintenance"
270 | do_stop_maintenance
271 | case "$?" in
272 | 0|1)
273 | do_start_maintenance
274 | case "$?" in
275 | 0) log_end_msg 0 ;;
276 | 1) log_end_msg 1 ;; # Old process is still running
277 | *) log_end_msg 1 ;; # Failed to start
278 | esac
279 | ;;
280 | *)
281 | # Failed to stop
282 | log_end_msg 1
283 | ;;
284 | esac
285 |
286 | log_daemon_msg "Restarting pgbackman alerts"
287 | do_stop_alerts
288 | case "$?" in
289 | 0|1)
290 | do_start_alerts
291 | case "$?" in
292 | 0) log_end_msg 0 ;;
293 | 1) log_end_msg 1 ;; # Old process is still running
294 | *) log_end_msg 1 ;; # Failed to start
295 | esac
296 | ;;
297 | *)
298 | # Failed to stop
299 | log_end_msg 1
300 | ;;
301 | esac
302 | ;;
303 |
304 | *)
305 | echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
306 | exit 3
307 | ;;
308 | esac
309 |
--------------------------------------------------------------------------------
/etc/pgbackman_init_rh.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # /etc/init.d/pgbackman
4 | # Init script for PgBackman
5 | #
6 | # chkconfig: 2345 95 05
7 | # description: PgBackMan - PostgreSQL Backup Manager
8 | #
9 |
10 | # Source function library.
11 | . /etc/rc.d/init.d/functions
12 |
13 | # Pull in sysconfig settings
14 | [ -f /etc/sysconfig/pgbackman ] && . /etc/sysconfig/pgbackman
15 |
16 | # Variables
17 | BINDIR="/usr/bin"
18 | PGBACKMAN_STARTUP_LOG=/var/log/pgbackman/pgbackman_startup.log
19 | PGBACKMAN_LOG=/var/log/pgbackman/pgbackman.log
20 |
21 | LOCKFILE_CONTROL="/var/lock/subsys/pgbackman_control"
22 | PIDFILE_CONTROL="/var/run/pgbackman_control.pid"
23 |
24 | LOCKFILE_MAINTENANCE="/var/lock/subsys/pgbackman_maintenance"
25 | PIDFILE_MAINTENANCE="/var/run/pgbackman_maintenance.pid"
26 |
27 | LOCKFILE_ALERTS="/var/lock/subsys/pgbackman_alerts"
28 | PIDFILE_ALERTS="/var/run/pgbackman_alerts.pid"
29 |
30 | # Exit if the scripts are not installed
31 | [ -f "$BINDIR/pgbackman_control" ] || exit 1
32 | [ -f "$BINDIR/pgbackman_maintenance" ] || exit 1
33 | [ -f "$BINDIR/pgbackman_alerts" ] || exit 1
34 |
35 | # Create logfile if it does not exist
36 | if [ ! -f "$PGBACKMAN_LOG" ]
37 | then
38 | touch "$PGBACKMAN_LOG"
39 | chown -R pgbackman:pgbackman "$PGBACKMAN_LOG"
40 | else
41 | chown -R pgbackman:pgbackman "$PGBACKMAN_LOG"
42 | fi
43 |
44 | start(){
45 | PGBACKMAN_CONTROL_START="Starting pgbackman_control service: "
46 | PGBACKMAN_MAINTENANCE_START="Starting pgbackman_maintenance service: "
47 | PGBACKMAN_ALERTS_START="Starting pgbackman_alerts service: "
48 |
49 | if [ ! -e "$PGBACKMAN_STARTUP_LOG" ]
50 | then
51 | touch "$PGBACKMAN_STARTUP_LOG" || exit 1
52 | chown pgbackman:pgbackman "$PGBACKMAN_STARTUP_LOG"
53 | chmod go-rwx "$PGBACKMAN_STARTUP_LOG"
54 | fi
55 |
56 | echo -n "$PGBACKMAN_CONTROL_START"
57 |
58 | if [ ! -e "$LOCKFILE_CONTROL" ]
59 | then
60 | $BINDIR/pgbackman_control >> "$PGBACKMAN_STARTUP_LOG" 2>&1 &
61 | RETVAL=$?
62 | PID=$!
63 | sleep 2
64 |
65 | if [ "x$PID" != "x" ]
66 | then
67 | success
68 | echo
69 |
70 | touch "$LOCKFILE_CONTROL"
71 | echo $PID > "$PIDFILE_CONTROL"
72 | else
73 | failure
74 | echo
75 |
76 | return $RETVAL
77 | fi
78 |
79 | else
80 | failure
81 | echo
82 | fi
83 |
84 | echo -n "$PGBACKMAN_MAINTENANCE_START"
85 |
86 | if [ ! -e "$LOCKFILE_MAINTENANCE" ]
87 | then
88 | $BINDIR/pgbackman_maintenance >> "$PGBACKMAN_STARTUP_LOG" 2>&1 &
89 | RETVAL=$?
90 | PID=$!
91 | sleep 2
92 |
93 | if [ "x$PID" != "x" ]
94 | then
95 | success
96 | echo
97 |
98 | touch "$LOCKFILE_MAINTENANCE"
99 | echo $PID > "$PIDFILE_MAINTENANCE"
100 | else
101 | failure
102 | echo
103 |
104 | return $RETVAL
105 | fi
106 |
107 | else
108 | failure
109 | echo
110 | fi
111 |
112 | echo -n "$PGBACKMAN_ALERTS_START"
113 |
114 | if [ ! -e "$LOCKFILE_ALERTS" ]
115 | then
116 | $BINDIR/pgbackman_alerts >> "$PGBACKMAN_STARTUP_LOG" 2>&1 &
117 | RETVAL=$?
118 | PID=$!
119 | sleep 2
120 |
121 | if [ "x$PID" != "x" ]
122 | then
123 | success
124 | echo
125 |
126 | touch "$LOCKFILE_ALERTS"
127 | echo $PID > "$PIDFILE_ALERTS"
128 | else
129 | failure
130 | echo
131 |
132 | return $RETVAL
133 | fi
134 |
135 | else
136 | failure
137 | echo
138 | fi
139 |
140 | }
141 |
142 | stop(){
143 |
144 | PGBACKMAN_CONTROL_STOP="Stopping pgbackman_control service: "
145 | PGBACKMAN_MAINTENANCE_STOP="Stopping pgbackman_maintenance service: "
146 | PGBACKMAN_ALERTS_STOP="Stopping pgbackman_alerts service: "
147 |
148 | echo -n $PGBACKMAN_CONTROL_STOP
149 |
150 | if [ -e "$LOCKFILE_CONTROL" ]
151 | then
152 | killproc -p "$PIDFILE_CONTROL" pgbackman_control
153 | RETVAL=$?
154 |
155 | if [ $RETVAL -eq 0 ]
156 | then
157 | success
158 | echo
159 |
160 | rm -f $LOCKFILE_CONTROL
161 | rm -f $PIDFILE_CONTROL
162 |
163 | else
164 | failure
165 | echo
166 | return $RETVAL
167 | fi
168 |
169 | else
170 | # not running; per LSB standards this is "ok"
171 | success
172 | echo
173 | fi
174 |
175 | echo -n $PGBACKMAN_MAINTENANCE_STOP
176 |
177 | if [ -e "$LOCKFILE_MAINTENANCE" ]
178 | then
179 | killproc -p "$PIDFILE_MAINTENANCE" pgbackman_maintenance
180 | RETVAL=$?
181 |
182 | if [ $RETVAL -eq 0 ]
183 | then
184 | success
185 | echo
186 |
187 | rm -f $LOCKFILE_MAINTENANCE
188 | rm -f $PIDFILE_MAINTENANCE
189 |
190 | else
191 | failure
192 | echo
193 |
194 | return $RETVAL
195 | fi
196 |
197 | else
198 | # not running; per LSB standards this is "ok"
199 | success
200 | echo
201 | fi
202 |
203 | echo -n $PGBACKMAN_ALERTS_STOP
204 |
205 | if [ -e "$LOCKFILE_ALERTS" ]
206 | then
207 | killproc -p "$PIDFILE_ALERTS" pgbackman_alerts
208 | RETVAL=$?
209 |
210 | if [ $RETVAL -eq 0 ]
211 | then
212 | success
213 | echo
214 |
215 | rm -f $LOCKFILE_ALERTS
216 | rm -f $PIDFILE_ALERTS
217 |
218 | else
219 | failure
220 | echo
221 |
222 | return $RETVAL
223 | fi
224 |
225 | else
226 | # not running; per LSB standards this is "ok"
227 | success
228 | echo
229 | fi
230 |
231 |
232 | }
233 |
234 | restart(){
235 | stop
236 | start
237 | }
238 |
239 | # See how we were called.
240 | case "$1" in
241 | start)
242 | start
243 | ;;
244 | stop)
245 | stop
246 | ;;
247 | status)
248 | status -p $PIDFILE_CONTROL pgbackman_control
249 | status -p $PIDFILE_MAINTENANCE pgbackman_maintenance
250 | status -p $PIDFILE_ALERTS pgbackman_alerts
251 | ;;
252 | restart)
253 | restart
254 | ;;
255 | *)
256 | echo $"Usage: $0 {start|stop|status|restart}"
257 | exit 1
258 | ;;
259 | esac
260 |
261 | exit $?
262 |
--------------------------------------------------------------------------------
/pgbackman/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rafaelma/pgbackman/b0a1878abbf3aa976ea08dba64c24ccafffc6680/pgbackman/__init__.py
--------------------------------------------------------------------------------
/pgbackman/config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) 2013-2014 Rafael Martinez Guerrero / PostgreSQL-es
4 | # rafael@postgresql.org.es / http://www.postgresql.org.es/
5 | #
6 | # Copyright (c) 2014 USIT-University of Oslo
7 | #
8 | # This file is part of PgBackMan
9 | # https://github.com/rafaelma/pgbackman
10 | #
11 | # PgBackMan is free software: you can redistribute it and/or modify
12 | # it under the terms of the GNU General Public License as published by
13 | # the Free Software Foundation, either version 3 of the License, or
14 | # (at your option) any later version.
15 | #
16 | # PgBackMan is distributed in the hope that it will be useful,
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | # GNU General Public License for more details.
20 | #
21 | # You should have received a copy of the GNU General Public License
22 | # along with Pgbackman. If not, see .
23 |
24 | import socket
25 | import os
26 | import ConfigParser
27 |
28 |
29 | class PgbackmanConfiguration():
30 | # ############################################
31 | # Constructor
32 | # ############################################
33 |
34 | def __init__(self):
35 | """ The Constructor."""
36 |
37 | self.config_file = ''
38 |
39 | # Backup server section
40 | self.backup_server = ''
41 |
42 | # pgbackman database section
43 | self.dbhost = ''
44 | self.dbhostaddr = ''
45 | self.dbport = '5432'
46 | self.dbname = 'pgbackman'
47 | self.dbuser = 'pgbackman_role_rw'
48 | self.dbpassword = ''
49 | self.dsn = ''
50 | self.pg_connect_retry_interval = 10
51 | self.database_source_dir = '/usr/share/pgbackman'
52 |
53 | # pgbackman_dump section
54 | self.tmp_dir = '/tmp'
55 | self.pause_recovery_process_on_slave = 'OFF'
56 |
57 | # pgbackman_maintenance section
58 | self.maintenance_interval = 70
59 |
60 | # pgbackman_alerts section
61 | self.smtp_alerts = 'OFF'
62 | self.alerts_check_interval = 300
63 | self.smtp_server = 'localhost'
64 | self.smtp_port = '25'
65 | self.smtp_ssl = 'ON'
66 | self.smtp_user = ''
67 | self.smtp_password = ''
68 | self.smtp_from_address = ''
69 | self.alerts_template = '/etc/pgbackman/pgbackman_alerts.template'
70 |
71 | # Logging section
72 | self.log_level = 'ERROR'
73 | self.log_file = '/var/log/pgbackman/pgbackman.log'
74 |
75 | self.set_configuration_file()
76 | self.set_configuration_parameters()
77 |
78 | # ############################################
79 | # Method
80 | # ############################################
81 |
82 | def set_configuration_file(self):
83 | """Set the pgbackman configuration file"""
84 |
85 | config_file_list = ['/etc/pgbackman/pgbackman.conf']
86 |
87 | if os.getenv('HOME') is not None:
88 | config_file_list.insert(0, os.getenv('HOME') + '/.pgbackman/pgbackman.conf')
89 |
90 | for file in config_file_list:
91 | if os.path.isfile(file):
92 | self.config_file = file
93 | break
94 |
95 | # ############################################
96 | # Method
97 | # ############################################
98 |
99 | def set_configuration_parameters(self):
100 | """Set configuration parameters"""
101 |
102 | dsn_parameters = []
103 |
104 | if self.config_file:
105 |
106 | config = ConfigParser.RawConfigParser()
107 | config.read(self.config_file)
108 |
109 | # Backup server section
110 | if config.has_option('backup_server', 'backup_server'):
111 | self.backup_server = config.get('backup_server', 'backup_server')
112 |
113 | # pgbackman database section
114 | if config.has_option('pgbackman_database', 'host'):
115 | self.dbhost = config.get('pgbackman_database', 'host')
116 |
117 | if config.has_option('pgbackman_database', 'hostaddr'):
118 | self.dbhostaddr = config.get('pgbackman_database', 'hostaddr')
119 |
120 | if config.has_option('pgbackman_database', 'port'):
121 | self.dbport = config.get('pgbackman_database', 'port')
122 |
123 | if config.has_option('pgbackman_database', 'dbname'):
124 | self.dbname = config.get('pgbackman_database', 'dbname')
125 |
126 | if config.has_option('pgbackman_database', 'user'):
127 | self.dbuser = config.get('pgbackman_database', 'user')
128 |
129 | if config.has_option('pgbackman_database', 'password'):
130 | self.dbpassword = config.get('pgbackman_database', 'password')
131 |
132 | if config.has_option('pgbackman_database', 'pg_connect_retry_interval'):
133 | self.pg_connect_retry_interval = int(config.get('pgbackman_database', 'pg_connect_retry_interval'))
134 |
135 | if config.has_option('pgbackman_database', 'database_source_dir'):
136 | self.database_source_dir = config.get('pgbackman_database', 'database_source_dir')
137 |
138 | # pgbackman_dump section
139 | if config.has_option('pgbackman_dump', 'tmp_dir'):
140 | self.tmp_dir = config.get('pgbackman_dump', 'tmp_dir')
141 |
142 | if config.has_option('pgbackman_dump', 'pause_recovery_process_on_slave'):
143 | self.pause_recovery_process_on_slave = config.get('pgbackman_dump',
144 | 'pause_recovery_process_on_slave').upper()
145 |
146 | # pgbackman_maintenance section
147 | if config.has_option('pgbackman_maintenance', 'maintenance_interval'):
148 | self.maintenance_interval = int(config.get('pgbackman_maintenance', 'maintenance_interval'))
149 |
150 | # pgbackman_alerts section
151 | if config.has_option('pgbackman_alerts', 'smtp_alerts'):
152 | self.smtp_alerts = config.get('pgbackman_alerts', 'smtp_alerts').upper()
153 |
154 | if config.has_option('pgbackman_alerts', 'alerts_check_interval'):
155 | self.alerts_check_interval = int(config.get('pgbackman_alerts', 'alerts_check_interval'))
156 |
157 | if config.has_option('pgbackman_alerts', 'smtp_server'):
158 | self.smtp_server = config.get('pgbackman_alerts', 'smtp_server')
159 |
160 | if config.has_option('pgbackman_alerts', 'smtp_port'):
161 | self.smtp_port = config.get('pgbackman_alerts', 'smtp_port')
162 |
163 | if config.has_option('pgbackman_alerts', 'smtp_ssl'):
164 | self.smtp_ssl = config.get('pgbackman_alerts', 'smtp_ssl').upper()
165 |
166 | if config.has_option('pgbackman_alerts', 'smtp_user'):
167 | self.smtp_user = config.get('pgbackman_alerts', 'smtp_user')
168 |
169 | if config.has_option('pgbackman_alerts', 'smtp_password'):
170 | self.smtp_password = config.get('pgbackman_alerts', 'smtp_password')
171 |
172 | if config.has_option('pgbackman_alerts', 'smtp_from_address'):
173 | self.smtp_from_address = config.get('pgbackman_alerts', 'smtp_from_address')
174 |
175 | if config.has_option('pgbackman_alerts', 'alerts_template'):
176 | self.alerts_template = config.get('pgbackman_alerts', 'alerts_template')
177 |
178 | # Logging section
179 | if config.has_option('logging', 'log_level'):
180 | self.log_level = config.get('logging', 'log_level').upper()
181 |
182 | if config.has_option('logging', 'log_file'):
183 | self.log_file = config.get('logging', 'log_file')
184 |
185 | # Generate the DSN string
186 |
187 | if self.dbhost != '':
188 | dsn_parameters.append('host=''' + self.dbhost + '')
189 |
190 | if self.dbhostaddr != '':
191 | dsn_parameters.append('hostaddr=''' + self.dbhostaddr + '')
192 |
193 | if self.dbport != '':
194 | dsn_parameters.append('port=''' + self.dbport + '')
195 |
196 | if self.dbname != '':
197 | dsn_parameters.append('dbname=''' + self.dbname + '')
198 |
199 | if self.dbuser != '':
200 | dsn_parameters.append('user=''' + self.dbuser + '')
201 |
202 | if self.dbpassword != '':
203 | dsn_parameters.append('password=''' + self.dbpassword + '')
204 |
205 | for parameter in dsn_parameters:
206 | self.dsn = self.dsn + parameter + ' '
207 |
--------------------------------------------------------------------------------
/pgbackman/logs.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) 2013-2014 Rafael Martinez Guerrero / PostgreSQL-es
4 | # rafael@postgresql.org.es / http://www.postgresql.org.es/
5 | #
6 | # Copyright (c) 2014 USIT-University of Oslo
7 | #
8 | # This file is part of PgBackMan
9 | # https://github.com/rafaelma/pgbackman
10 | #
11 | # PgBackMan is free software: you can redistribute it and/or modify
12 | # it under the terms of the GNU General Public License as published by
13 | # the Free Software Foundation, either version 3 of the License, or
14 | # (at your option) any later version.
15 | #
16 | # Pgbackman is distributed in the hope that it will be useful,
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | # GNU General Public License for more details.
20 | #
21 | # You should have received a copy of the GNU General Public License
22 | # along with PgBck. If not, see .
23 |
24 | import os
25 | import sys
26 | import logging
27 |
28 | from pgbackman.config import *
29 |
30 | class PgbackmanLogs(logging.Logger):
31 |
32 | # ############################################
33 | # Constructor
34 | # ############################################
35 |
36 | def __init__(self, logger_name,pgsql_node,dbname):
37 | """ The Constructor."""
38 |
39 | self.logger_name = logger_name
40 | self.pgsql_node = pgsql_node
41 | self.dbname = dbname
42 |
43 | self.conf = PgbackmanConfiguration()
44 |
45 | self.logger = logging.getLogger(logger_name)
46 | self.level = logging.getLevelName(self.conf.log_level.upper())
47 |
48 | self.logger.setLevel(self.level)
49 |
50 | try:
51 | self.fh = logging.FileHandler(self.conf.log_file)
52 | self.fh.setLevel(self.level)
53 |
54 | self.formatter = logging.Formatter("%(asctime)s [%(name)s]" + self.pgsql_node + self. dbname + "[%(process)d][%(levelname)s]: %(message)s")
55 | self.fh.setFormatter(self.formatter)
56 | self.logger.addHandler(self.fh)
57 |
58 | except Exception as e:
59 | print "ERROR: Problems with the log configuration needed by pgbackman: %s" % e
60 | sys.exit(1)
61 |
62 |
63 |
--------------------------------------------------------------------------------
/pgbackman/ordereddict.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Ref: http://code.activestate.com/recipes/576693-ordered-dictionary-for-py24/
4 | # Created by Raymond Hettinger on Wed, 18 Mar 2009 (MIT)
5 | #
6 | # We support python 2.6 and needs OrderedDict().
7 | #
8 | # Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy.
9 | # Passes Python2.7's test suite and incorporates all the latest updates.
10 |
11 | try:
12 | from thread import get_ident as _get_ident
13 | except ImportError:
14 | from dummy_thread import get_ident as _get_ident
15 |
16 | try:
17 | from _abcoll import KeysView, ValuesView, ItemsView
18 | except ImportError:
19 | pass
20 |
21 |
22 | class OrderedDict(dict):
23 | 'Dictionary that remembers insertion order'
24 | # An inherited dict maps keys to values.
25 | # The inherited dict provides __getitem__, __len__, __contains__, and get.
26 | # The remaining methods are order-aware.
27 | # Big-O running times for all methods are the same as for regular dictionaries.
28 |
29 | # The internal self.__map dictionary maps keys to links in a doubly linked list.
30 | # The circular doubly linked list starts and ends with a sentinel element.
31 | # The sentinel element never gets deleted (this simplifies the algorithm).
32 | # Each link is stored as a list of length three: [PREV, NEXT, KEY].
33 |
34 | def __init__(self, *args, **kwds):
35 | '''Initialize an ordered dictionary. Signature is the same as for
36 | regular dictionaries, but keyword arguments are not recommended
37 | because their insertion order is arbitrary.
38 |
39 | '''
40 | if len(args) > 1:
41 | raise TypeError('expected at most 1 arguments, got %d' % len(args))
42 | try:
43 | self.__root
44 | except AttributeError:
45 | self.__root = root = [] # sentinel node
46 | root[:] = [root, root, None]
47 | self.__map = {}
48 | self.__update(*args, **kwds)
49 |
50 | def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
51 | 'od.__setitem__(i, y) <==> od[i]=y'
52 | # Setting a new item creates a new link which goes at the end of the linked
53 | # list, and the inherited dictionary is updated with the new key/value pair.
54 | if key not in self:
55 | root = self.__root
56 | last = root[0]
57 | last[1] = root[0] = self.__map[key] = [last, root, key]
58 | dict_setitem(self, key, value)
59 |
60 | def __delitem__(self, key, dict_delitem=dict.__delitem__):
61 | 'od.__delitem__(y) <==> del od[y]'
62 | # Deleting an existing item uses self.__map to find the link which is
63 | # then removed by updating the links in the predecessor and successor nodes.
64 | dict_delitem(self, key)
65 | link_prev, link_next, key = self.__map.pop(key)
66 | link_prev[1] = link_next
67 | link_next[0] = link_prev
68 |
69 | def __iter__(self):
70 | 'od.__iter__() <==> iter(od)'
71 | root = self.__root
72 | curr = root[1]
73 | while curr is not root:
74 | yield curr[2]
75 | curr = curr[1]
76 |
77 | def __reversed__(self):
78 | 'od.__reversed__() <==> reversed(od)'
79 | root = self.__root
80 | curr = root[0]
81 | while curr is not root:
82 | yield curr[2]
83 | curr = curr[0]
84 |
85 | def clear(self):
86 | 'od.clear() -> None. Remove all items from od.'
87 | try:
88 | for node in self.__map.itervalues():
89 | del node[:]
90 | root = self.__root
91 | root[:] = [root, root, None]
92 | self.__map.clear()
93 | except AttributeError:
94 | pass
95 | dict.clear(self)
96 |
97 | def popitem(self, last=True):
98 | '''od.popitem() -> (k, v), return and remove a (key, value) pair.
99 | Pairs are returned in LIFO order if last is true or FIFO order if false.
100 |
101 | '''
102 | if not self:
103 | raise KeyError('dictionary is empty')
104 | root = self.__root
105 | if last:
106 | link = root[0]
107 | link_prev = link[0]
108 | link_prev[1] = root
109 | root[0] = link_prev
110 | else:
111 | link = root[1]
112 | link_next = link[1]
113 | root[1] = link_next
114 | link_next[0] = root
115 | key = link[2]
116 | del self.__map[key]
117 | value = dict.pop(self, key)
118 | return key, value
119 |
120 | # -- the following methods do not depend on the internal structure --
121 |
122 | def keys(self):
123 | 'od.keys() -> list of keys in od'
124 | return list(self)
125 |
126 | def values(self):
127 | 'od.values() -> list of values in od'
128 | return [self[key] for key in self]
129 |
130 | def items(self):
131 | 'od.items() -> list of (key, value) pairs in od'
132 | return [(key, self[key]) for key in self]
133 |
134 | def iterkeys(self):
135 | 'od.iterkeys() -> an iterator over the keys in od'
136 | return iter(self)
137 |
138 | def itervalues(self):
139 | 'od.itervalues -> an iterator over the values in od'
140 | for k in self:
141 | yield self[k]
142 |
143 | def iteritems(self):
144 | 'od.iteritems -> an iterator over the (key, value) items in od'
145 | for k in self:
146 | yield (k, self[k])
147 |
148 | def update(*args, **kwds):
149 | '''od.update(E, **F) -> None. Update od from dict/iterable E and F.
150 |
151 | If E is a dict instance, does: for k in E: od[k] = E[k]
152 | If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]
153 | Or if E is an iterable of items, does: for k, v in E: od[k] = v
154 | In either case, this is followed by: for k, v in F.items(): od[k] = v
155 |
156 | '''
157 | if len(args) > 2:
158 | raise TypeError('update() takes at most 2 positional '
159 | 'arguments (%d given)' % (len(args),))
160 | elif not args:
161 | raise TypeError('update() takes at least 1 argument (0 given)')
162 | self = args[0]
163 | # Make progressively weaker assumptions about "other"
164 | other = ()
165 | if len(args) == 2:
166 | other = args[1]
167 | if isinstance(other, dict):
168 | for key in other:
169 | self[key] = other[key]
170 | elif hasattr(other, 'keys'):
171 | for key in other.keys():
172 | self[key] = other[key]
173 | else:
174 | for key, value in other:
175 | self[key] = value
176 | for key, value in kwds.items():
177 | self[key] = value
178 |
179 | __update = update # let subclasses override update without breaking __init__
180 |
181 | __marker = object()
182 |
183 | def pop(self, key, default=__marker):
184 | '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
185 | If key is not found, d is returned if given, otherwise KeyError is raised.
186 |
187 | '''
188 | if key in self:
189 | result = self[key]
190 | del self[key]
191 | return result
192 | if default is self.__marker:
193 | raise KeyError(key)
194 | return default
195 |
196 | def setdefault(self, key, default=None):
197 | 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
198 | if key in self:
199 | return self[key]
200 | self[key] = default
201 | return default
202 |
203 | def __repr__(self, _repr_running={}):
204 | 'od.__repr__() <==> repr(od)'
205 | call_key = id(self), _get_ident()
206 | if call_key in _repr_running:
207 | return '...'
208 | _repr_running[call_key] = 1
209 | try:
210 | if not self:
211 | return '%s()' % (self.__class__.__name__,)
212 | return '%s(%r)' % (self.__class__.__name__, self.items())
213 | finally:
214 | del _repr_running[call_key]
215 |
216 | def __reduce__(self):
217 | 'Return state information for pickling'
218 | items = [[k, self[k]] for k in self]
219 | inst_dict = vars(self).copy()
220 | for k in vars(OrderedDict()):
221 | inst_dict.pop(k, None)
222 | if inst_dict:
223 | return (self.__class__, (items,), inst_dict)
224 | return self.__class__, (items,)
225 |
226 | def copy(self):
227 | 'od.copy() -> a shallow copy of od'
228 | return self.__class__(self)
229 |
230 | @classmethod
231 | def fromkeys(cls, iterable, value=None):
232 | '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
233 | and values equal to v (which defaults to None).
234 |
235 | '''
236 | d = cls()
237 | for key in iterable:
238 | d[key] = value
239 | return d
240 |
241 | def __eq__(self, other):
242 | '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
243 | while comparison to a regular mapping is order-insensitive.
244 |
245 | '''
246 | if isinstance(other, OrderedDict):
247 | return len(self)==len(other) and self.items() == other.items()
248 | return dict.__eq__(self, other)
249 |
250 | def __ne__(self, other):
251 | return not self == other
252 |
253 | # -- the following methods are only used in Python 2.7 --
254 |
255 | def viewkeys(self):
256 | "od.viewkeys() -> a set-like object providing a view on od's keys"
257 | return KeysView(self)
258 |
259 | def viewvalues(self):
260 | "od.viewvalues() -> an object providing a view on od's values"
261 | return ValuesView(self)
262 |
263 | def viewitems(self):
264 | "od.viewitems() -> a set-like object providing a view on od's items"
265 | return ItemsView(self)
266 |
--------------------------------------------------------------------------------
/pgbackman/version.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | __version__ = '3:1.2.0'
3 |
--------------------------------------------------------------------------------
/rpm/pgbackman.spec:
--------------------------------------------------------------------------------
1 | #
2 | # File: pgbackman.spec
3 | #
4 | # Autor: Rafael Martinez
5 | #
6 |
7 | %define majorversion 1.2
8 | %define minorversion 0
9 | %define pbm_owner pgbackman
10 | %define pbm_group pgbackman
11 | %{!?pybasever: %define pybasever %(python -c "import sys;print(sys.version[0:3])")}
12 | %{!?python_sitelib: %define python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
13 |
14 | Summary: PostgreSQL backup manager
15 | Name: pgbackman
16 | Version: %{majorversion}.%{minorversion}
17 | Release: 1%{?dist}
18 | License: GPLv3
19 | Group: Applications/Databases
20 | Url: http://www.pgbackman.org/
21 | Source0: %{name}-%{version}.tar.gz
22 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot-%(%{__id_u} -n)
23 | BuildArch: noarch
24 | Requires: python-psycopg2 >= 2.4.0, python-argparse, at, cronie, python-setuptools, shadow-utils, logrotate
25 |
26 | %description
27 | PgBackMan is a tool for managing PostgreSQL logical backups created
28 | with pg_dump and pg_dumpall.
29 |
30 | It is designed to manage backups from thousands of databases running
31 | in multiple PostgreSQL nodes, and it supports a multiple backup
32 | servers topology.
33 |
34 | It also manages role and database configuration information when
35 | creating a backup of a database. This information is necessary to
36 | ensure a 100% restore of a logical backup of a database and the
37 | elements associated to it.
38 |
39 | %prep
40 | %setup -n %{name}-%{version} -q
41 |
42 | %build
43 | python setup.py build
44 |
45 | %install
46 | python setup.py install -O1 --skip-build --root %{buildroot}
47 | mkdir -p %{buildroot}/var/lib/%{name}
48 | touch %{buildroot}/var/log/%{name}/%{name}.log
49 |
50 | %clean
51 | rm -rf %{buildroot}
52 |
53 | %files
54 | %defattr(-,root,root)
55 | %doc INSTALL
56 | %{python_sitelib}/%{name}-%{version}-py%{pybasever}.egg-info/
57 | %{python_sitelib}/%{name}/
58 | %{_bindir}/%{name}*
59 | %{_sysconfdir}/init.d/%{name}*
60 | %{_sysconfdir}/logrotate.d/%{name}*
61 | %{_datadir}/%{name}/*
62 | /var/log/%{name}/*
63 | %config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf
64 | %config(noreplace) %{_sysconfdir}/%{name}/%{name}_alerts.template
65 | %attr(700,%{pbm_owner},%{pbm_group}) %dir /var/lib/%{name}
66 | %attr(755,%{pbm_owner},%{pbm_group}) %dir /var/log/%{name}
67 | %attr(600,%{pbm_owner},%{pbm_group}) %ghost /var/log/%{name}/%{name}.log
68 |
69 | %pre
70 | groupadd -f -r pgbackman >/dev/null 2>&1 || :
71 | useradd -M -N -g pgbackman -r -d /var/lib/pgbackman -s /bin/bash \
72 | -c "PostgreSQL Backup Manager" pgbackman >/dev/null 2>&1 || :
73 |
74 | %changelog
75 | * Tue Jun 13 2017 - Rafael Martinez Guerrero 1.2.0-1
76 | - New release 1.0.0
77 |
78 | * Mon Jun 24 2014 - Rafael Martinez Guerrero 1.0.0-1
79 | - New release 1.0.0
80 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) 2013-2014 Rafael Martinez Guerrero / PostgreSQL-es
4 | # rafael@postgresql.org.es / http://www.postgresql.org.es/
5 | #
6 | # Copyright (c) 2014 USIT-University of Oslo
7 | #
8 | # This file is part of Pgbackman
9 | # https://github.com/rafaelma/pgbackman
10 | #
11 | # Pgbackman is free software: you can redistribute it and/or modify
12 | # it under the terms of the GNU General Public License as published by
13 | # the Free Software Foundation, either version 3 of the License, or
14 | # (at your option) any later version.
15 | #
16 | # Pgbackman is distributed in the hope that it will be useful,
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | # GNU General Public License for more details.
20 | #
21 | # You should have received a copy of the GNU General Public License
22 | # along with Pgbackman. If not, see .
23 |
24 | import subprocess
25 | import platform
26 | import shutil
27 | import sys
28 | import os
29 | import pwd
30 | import grp
31 | from setuptools import setup
32 |
33 | '''
34 | setup.py installation file
35 | '''
36 | try:
37 | pgbackman = {}
38 | with open('pgbackman/version.py', 'r') as version_file:
39 | exec (version_file.read(), pgbackman)
40 |
41 | if sys.version_info < (2, 6):
42 | raise SystemExit('ERROR: pgbackman needs at least python 2.6 to work')
43 | else:
44 | install_requires = ['psycopg2>=2.4.0','argparse']
45 |
46 | install_files = [('/etc/pgbackman', ['etc/pgbackman.conf']),
47 | ('/etc/pgbackman', ['etc/pgbackman_alerts.template']),
48 | ('/etc/logrotate.d', ['etc/pgbackman.logrotate']),
49 | ('/usr/share/pgbackman/', ['sql/pgbackman.sql']),
50 | ('/usr/share/pgbackman/', ['sql/pgbackman_2.sql']),
51 | ('/usr/share/pgbackman/', ['sql/pgbackman_3.sql'])]
52 | #
53 | # Check linux distribution and define init script
54 | #
55 |
56 | distro = platform.linux_distribution()[0]
57 |
58 | if distro in ('CentOS Linux', 'Red Hat Enterprise Linux Server', 'Red Hat Enterprise Linux Workstation', 'Fedora'):
59 |
60 | install_files.append(('/etc/init.d', ['etc/pgbackman_init_rh.sh']))
61 |
62 | elif distro in ('Ubuntu','debian'):
63 |
64 | install_files.append(('/lib/systemd/system', ['etc/pgbackman-alerts.service']))
65 | install_files.append(('/lib/systemd/system', ['etc/pgbackman-control.service']))
66 | install_files.append(('/lib/systemd/system', ['etc/pgbackman-maintenance.service']))
67 |
68 | else:
69 |
70 | install_files.append(('/etc/init.d', ['etc/pgbackman_init_rh.sh']))
71 |
72 | #
73 | # Setup
74 | #
75 |
76 | setup(name='pgbackman',
77 | version=pgbackman['__version__'].split(':')[1],
78 | description='PGBACKMAN - PostgreSQL Backup Manager',
79 | author='Rafael Martinez Guerrero',
80 | author_email='rafael@postgresql.org.es',
81 | url='http://www.pgbackman.org/',
82 | packages=['pgbackman',],
83 | scripts=['bin/pgbackman','bin/pgbackman_control','bin/pgbackman_maintenance','bin/pgbackman_dump','bin/pgbackman_restore','bin/pgbackman_zabbix_autodiscovery','bin/pgbackman_status_info','bin/pgbackman_alerts','bin/pgbackman-bulk-update'],
84 | data_files=install_files,
85 | install_requires=install_requires,
86 | platforms=['Linux'],
87 | classifiers=[
88 | 'Environment :: Console',
89 | 'Development Status :: 5 - Production/Stable',
90 | 'Topic :: System :: Archiving :: Backup',
91 | 'Topic :: Database',
92 | 'Topic :: System :: Recovery Tools',
93 | 'Intended Audience :: System Administrators',
94 | 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
95 | 'Programming Language :: Python',
96 | 'Programming Language :: Python :: 2.6',
97 | 'Programming Language :: Python :: 2.7',
98 | ],
99 | )
100 |
101 | except Exception as e:
102 | print e
103 |
--------------------------------------------------------------------------------
/setup2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # Copyright (c) 2013-2014 Rafael Martinez Guerrero / PostgreSQL-es
4 | # rafael@postgresql.org.es / http://www.postgresql.org.es/
5 | #
6 | # Copyright (c) 2014 USIT-University of Oslo
7 | #
8 | # This file is part of Pgbackman
9 | # https://github.com/rafaelma/pgbackman
10 | #
11 | # Pgbackman is free software: you can redistribute it and/or modify
12 | # it under the terms of the GNU General Public License as published by
13 | # the Free Software Foundation, either version 3 of the License, or
14 | # (at your option) any later version.
15 | #
16 | # Pgbackman is distributed in the hope that it will be useful,
17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 | # GNU General Public License for more details.
20 | #
21 | # You should have received a copy of the GNU General Public License
22 | # along with Pgbackman. If not, see .
23 |
24 | import subprocess
25 | import platform
26 | import shutil
27 | import sys
28 | import os
29 | import pwd
30 | import grp
31 | from setuptools import setup
32 |
33 | '''
34 | setup.py installation file
35 | '''
36 | try:
37 | pgbackman = {}
38 | with open('pgbackman/version.py', 'r') as version_file:
39 | exec (version_file.read(), pgbackman)
40 |
41 | if sys.version_info < (2, 6):
42 | raise SystemExit('ERROR: pgbackman needs at least python 2.6 to work')
43 | else:
44 | install_requires = ['psycopg2>=2.4.0','argparse']
45 |
46 |
47 | #
48 | # Creating pgbackman users and groups
49 | #
50 |
51 |
52 | groupadd_command = '/usr/sbin/groupadd -f -r pgbackman'
53 | proc = subprocess.Popen([groupadd_command],shell=True)
54 | proc.wait()
55 |
56 | if proc.returncode == 0:
57 | print 'Group pgbackman created'
58 |
59 | elif proc.returncode != 0:
60 | raise SystemExit('ERROR: Problems creating group pgbackman. Returncode: ' + str(proc.returncode))
61 |
62 | useradd_command = '/usr/sbin/useradd -m -N -g pgbackman -r -d /var/lib/pgbackman -s /bin/bash -c "PostgreSQL Backup Manager" pgbackman'
63 | proc = subprocess.Popen([useradd_command],shell=True)
64 | proc.wait()
65 |
66 | if proc.returncode == 0:
67 | print 'User pgbackman created'
68 |
69 | elif proc.returncode == 9:
70 | print 'User pgbackman already exists'
71 |
72 | else:
73 | raise SystemExit('ERROR: Problems creating user pgbackman. Returncode: ' + str(proc.returncode))
74 |
75 | #
76 | # Check linux distribution and define init script
77 | #
78 |
79 | distro = platform.linux_distribution()[0]
80 |
81 | if distro == 'CentOS' or distro == 'Red Hat Enterprise Linux Server' or distro == 'Red Hat Enterprise Linux Workstation' or distro == 'Fedora':
82 | init_file = 'etc/pgbackman_init_rh.sh'
83 | shutil.copy2(init_file, '/tmp/pgbackman')
84 |
85 | elif distro == 'debian' or distro == 'Ubuntu':
86 | init_file = 'etc/pgbackman_init_debian.sh'
87 | shutil.copy2(init_file, '/tmp/pgbackman')
88 |
89 | else:
90 | init_file = 'etc/pgbackman_init_rh.sh'
91 | shutil.copy2(init_file, '/tmp/pgbackman')
92 |
93 | #
94 | # Setup
95 | #
96 |
97 | setup(name='pgbackman',
98 | version=pgbackman['__version__'].split(':')[1],
99 | description='PGBACKMAN - PostgreSQL Backup Manager',
100 | author='Rafael Martinez Guerrero',
101 | author_email='rafael@postgresql.org.es',
102 | url='http://www.pgbackman.org/',
103 | packages=['pgbackman',],
104 | scripts=['bin/pgbackman','bin/pgbackman_control','bin/pgbackman_maintenance','bin/pgbackman_dump','bin/pgbackman_restore','bin/pgbackman_zabbix_autodiscovery','bin/pgbackman_status_info','bin/pgbackman_alerts','bin/pgbackman-bulk-update'],
105 | data_files=[('/etc/init.d', ['/tmp/pgbackman']),
106 | ('/etc/pgbackman', ['etc/pgbackman.conf']),
107 | ('/etc/pgbackman', ['etc/pgbackman_alerts.template']),
108 | ('/etc/logrotate.d', ['etc/pgbackman.logrotate']),
109 | ('/usr/share/pgbackman/', ['sql/pgbackman.sql']),
110 | ('/usr/share/pgbackman/', ['sql/pgbackman_2.sql']),
111 | ('/usr/share/pgbackman/', ['sql/pgbackman_3.sql']),
112 | ('/var/log/pgbackman',['README.md'])],
113 | install_requires=install_requires,
114 | platforms=['Linux'],
115 | classifiers=[
116 | 'Environment :: Console',
117 | 'Development Status :: 5 - Production/Stable',
118 | 'Topic :: System :: Archiving :: Backup',
119 | 'Topic :: Database',
120 | 'Topic :: System :: Recovery Tools',
121 | 'Intended Audience :: System Administrators',
122 | 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
123 | 'Programming Language :: Python',
124 | 'Programming Language :: Python :: 2.6',
125 | 'Programming Language :: Python :: 2.7',
126 | ],
127 | )
128 | try:
129 | root_uid = pwd.getpwnam('root').pw_uid
130 | pgbackman_uid = pwd.getpwnam('pgbackman').pw_uid
131 | pgbackman_gid = grp.getgrnam('pgbackman').gr_gid
132 |
133 | os.chown('/var/log/pgbackman',pgbackman_uid, pgbackman_gid)
134 | os.chmod('/var/log/pgbackman',01775)
135 |
136 | os.chown('/var/log/pgbackman/pgbackman.log',pgbackman_uid, pgbackman_gid)
137 | os.chmod('/var/log/pgbackman/pgbackman.log',00664)
138 |
139 | print "Privileges defined for user pgbackman"
140 |
141 | except Exception as e:
142 | print e
143 |
144 | except Exception as e:
145 | print e
146 |
--------------------------------------------------------------------------------
/sql/pgbackman_3.sql:
--------------------------------------------------------------------------------
1 | --
2 | -- PgBackMan database - Upgrade from 2:1_1_0 to 3:1_2_0
3 | --
4 | -- Copyright (c) 2013-2015 Rafael Martinez Guerrero / PostgreSQL-es
5 | -- rafael@postgresql.org.es / http://www.postgresql.org.es/
6 | --
7 | -- Copyright (c) 2015 USIT-University of Oslo
8 | --
9 | -- This file is part of PgBackMan
10 | -- https://github.com/rafaelma/pgbackman
11 | --
12 |
13 | BEGIN;
14 |
15 | --Update function update_backup_server_config with two new parameters (pgsql_bin_9_5,pgsql_bin_9_6)
16 |
17 | DROP FUNCTION update_backup_server_config(INTEGER,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT);
18 |
19 | CREATE OR REPLACE FUNCTION update_backup_server_config(INTEGER,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT) RETURNS VOID
20 | LANGUAGE plpgsql
21 | SECURITY INVOKER
22 | SET search_path = public, pg_temp
23 | AS $$
24 | DECLARE
25 | backup_server_id_ ALIAS FOR $1;
26 | pgsql_bin_9_0_ ALIAS FOR $2;
27 | pgsql_bin_9_1_ ALIAS FOR $3;
28 | pgsql_bin_9_2_ ALIAS FOR $4;
29 | pgsql_bin_9_3_ ALIAS FOR $5;
30 | pgsql_bin_9_4_ ALIAS FOR $6;
31 | pgsql_bin_9_5_ ALIAS FOR $7;
32 | pgsql_bin_9_6_ ALIAS FOR $8;
33 | root_backup_partition_ ALIAS FOR $9;
34 |
35 | server_cnt INTEGER;
36 | v_msg TEXT;
37 | v_detail TEXT;
38 | v_context TEXT;
39 | BEGIN
40 |
41 | SELECT count(*) FROM backup_server WHERE server_id = backup_server_id_ INTO server_cnt;
42 |
43 | IF server_cnt != 0 THEN
44 |
45 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_0'''
46 | USING backup_server_id_,
47 | pgsql_bin_9_0_;
48 |
49 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_1'''
50 | USING backup_server_id_,
51 | pgsql_bin_9_1_;
52 |
53 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_2'''
54 | USING backup_server_id_,
55 | pgsql_bin_9_2_;
56 |
57 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_3'''
58 | USING backup_server_id_,
59 | pgsql_bin_9_3_;
60 |
61 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_4'''
62 | USING backup_server_id_,
63 | pgsql_bin_9_4_;
64 |
65 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_5'''
66 | USING backup_server_id_,
67 | pgsql_bin_9_5_;
68 |
69 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_6'''
70 | USING backup_server_id_,
71 | pgsql_bin_9_6_;
72 |
73 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''root_backup_partition'''
74 | USING backup_server_id_,
75 | root_backup_partition_;
76 |
77 | ELSE
78 | RAISE EXCEPTION 'Backup server % does not exist',backup_server_id_;
79 | END IF;
80 |
81 | EXCEPTION WHEN others THEN
82 | GET STACKED DIAGNOSTICS
83 | v_msg = MESSAGE_TEXT,
84 | v_detail = PG_EXCEPTION_DETAIL,
85 | v_context = PG_EXCEPTION_CONTEXT;
86 | RAISE EXCEPTION E'\n----------------------------------------------\nEXCEPTION:\n----------------------------------------------\nMESSAGE: % \nDETAIL : % \n----------------------------------------------\n', v_msg, v_detail;
87 | END;
88 | $$;
89 |
90 | ALTER FUNCTION update_backup_server_config(INTEGER,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT) OWNER TO pgbackman_role_rw;
91 |
92 | -- Update view show_jobs_queue
93 |
94 | CREATE OR REPLACE VIEW show_jobs_queue AS
95 | SELECT a.id AS "JobID",
96 | date_trunc('seconds',a.registered) AS "Registered",
97 | a.backup_server_id AS "SrvID",
98 | b.hostname || '.' || b.domain_name AS "Backup server",
99 | a.pgsql_node_id AS "NodeID",
100 | c.hostname || '.' || c.domain_name AS "PgSQL node",
101 | a.is_assigned AS "Assigned"
102 | FROM job_queue a
103 | INNER JOIN backup_server b ON a.backup_server_id = b.server_id
104 | INNER JOIN pgsql_node c ON a.pgsql_node_id = c.node_id
105 | ORDER BY a.registered ASC;
106 |
107 | ALTER VIEW show_jobs_queue OWNER TO pgbackman_role_rw;
108 |
109 | -- Update server_config with a new parameter (pgsql_bin_9.5) for all Backup servers in the system
110 |
111 | INSERT INTO backup_server_config (server_id,parameter,value,description)
112 | SELECT server_id,
113 | 'pgsql_bin_9_5'::text,
114 | '/usr/pgsql-9.5/bin'::text,
115 | 'postgreSQL 9.5 bin directory'::text
116 | FROM backup_server
117 | ORDER BY server_id;
118 |
119 | -- Update server_config with a new parameter (pgsql_bin_9.6) for all Backup servers in the system
120 |
121 | INSERT INTO backup_server_config (server_id,parameter,value,description)
122 | SELECT server_id,
123 | 'pgsql_bin_9_6'::text,
124 | '/usr/pgsql-9.6/bin'::text,
125 | 'postgreSQL 9.6 bin directory'::text
126 | FROM backup_server
127 | ORDER BY server_id;
128 |
129 | -- Update backup_server_default_config with postgresql 9.5 information
130 |
131 | INSERT INTO backup_server_default_config (parameter,value,description) VALUES ('pgsql_bin_9_5','/usr/pgsql-9.5/bin','postgreSQL 9.5 bin directory');
132 |
133 | -- Update backup_server_default_config with postgresql 9.6 information
134 |
135 | INSERT INTO backup_server_default_config (parameter,value,description) VALUES ('pgsql_bin_9_6','/usr/pgsql-9.6/bin','postgreSQL 9.6 bin directory');
136 |
137 | -- Update pgbackman_version with information about version 3:1_2_0
138 |
139 | INSERT INTO pgbackman_version (version,tag) VALUES ('3','v_1_2_0');
140 |
141 |
142 | COMMIT;
143 |
--------------------------------------------------------------------------------
/sql/pgbackman_4.sql:
--------------------------------------------------------------------------------
1 | --
2 | -- PgBackMan database - Upgrade from 3:1_2_0 to 4:1_3_0
3 | --
4 | -- Copyright (c) 2013-2017 Rafael Martinez Guerrero / PostgreSQL-es
5 | -- rafael@postgresql.org.es / http://www.postgresql.org.es/
6 | --
7 | -- Copyright (c) 2015 USIT-University of Oslo
8 | --
9 | -- This file is part of PgBackMan
10 | -- https://github.com/rafaelma/pgbackman
11 | --
12 |
13 | BEGIN;
14 |
15 | --Update function update_backup_server_config with one new parameter (pgsql_bin_10)
16 |
17 | DROP FUNCTION update_backup_server_config(INTEGER,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT);
18 |
19 | CREATE OR REPLACE FUNCTION update_backup_server_config(INTEGER,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT) RETURNS VOID
20 | LANGUAGE plpgsql
21 | SECURITY INVOKER
22 | SET search_path = public, pg_temp
23 | AS $$
24 | DECLARE
25 | backup_server_id_ ALIAS FOR $1;
26 | pgsql_bin_9_0_ ALIAS FOR $2;
27 | pgsql_bin_9_1_ ALIAS FOR $3;
28 | pgsql_bin_9_2_ ALIAS FOR $4;
29 | pgsql_bin_9_3_ ALIAS FOR $5;
30 | pgsql_bin_9_4_ ALIAS FOR $6;
31 | pgsql_bin_9_5_ ALIAS FOR $7;
32 | pgsql_bin_9_6_ ALIAS FOR $8;
33 | pgsql_bin_10_ ALIAS FOR $9;
34 | root_backup_partition_ ALIAS FOR $10;
35 |
36 | server_cnt INTEGER;
37 | v_msg TEXT;
38 | v_detail TEXT;
39 | v_context TEXT;
40 | BEGIN
41 |
42 | SELECT count(*) FROM backup_server WHERE server_id = backup_server_id_ INTO server_cnt;
43 |
44 | IF server_cnt != 0 THEN
45 |
46 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_0'''
47 | USING backup_server_id_,
48 | pgsql_bin_9_0_;
49 |
50 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_1'''
51 | USING backup_server_id_,
52 | pgsql_bin_9_1_;
53 |
54 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_2'''
55 | USING backup_server_id_,
56 | pgsql_bin_9_2_;
57 |
58 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_3'''
59 | USING backup_server_id_,
60 | pgsql_bin_9_3_;
61 |
62 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_4'''
63 | USING backup_server_id_,
64 | pgsql_bin_9_4_;
65 |
66 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_5'''
67 | USING backup_server_id_,
68 | pgsql_bin_9_5_;
69 |
70 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_9_6'''
71 | USING backup_server_id_,
72 | pgsql_bin_9_6_;
73 |
74 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''pgsql_bin_10'''
75 | USING backup_server_id_,
76 | pgsql_bin_10_;
77 |
78 | EXECUTE 'UPDATE backup_server_config SET value = $2 WHERE server_id = $1 AND parameter = ''root_backup_partition'''
79 | USING backup_server_id_,
80 | root_backup_partition_;
81 |
82 | ELSE
83 | RAISE EXCEPTION 'Backup server % does not exist',backup_server_id_;
84 | END IF;
85 |
86 | EXCEPTION WHEN others THEN
87 | GET STACKED DIAGNOSTICS
88 | v_msg = MESSAGE_TEXT,
89 | v_detail = PG_EXCEPTION_DETAIL,
90 | v_context = PG_EXCEPTION_CONTEXT;
91 | RAISE EXCEPTION E'\n----------------------------------------------\nEXCEPTION:\n----------------------------------------------\nMESSAGE: % \nDETAIL : % \n----------------------------------------------\n', v_msg, v_detail;
92 | END;
93 | $$;
94 |
95 | ALTER FUNCTION update_backup_server_config(INTEGER,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT,TEXT) OWNER TO pgbackman_role_rw;
96 |
97 | -- Update server_config with a new parameter (pgsql_bin_10) for all Backup servers in the system
98 |
99 | INSERT INTO backup_server_config (server_id,parameter,value,description)
100 | SELECT server_id,
101 | 'pgsql_bin_10'::text,
102 | '/usr/pgsql-10/bin'::text,
103 | 'postgreSQL 10 bin directory'::text
104 | FROM backup_server
105 | ORDER BY server_id;
106 |
107 | -- Update backup_code with a new backup code (RDS) to take backups on RDS instances.
108 |
109 | INSERT INTO backup_code (code,description) VALUES ('RDS','Only runs a pg_dump of the data so that it does not try to access globals that postgres cannot access on RDS');
110 |
111 | -- Update backup_server_default_config with postgresql 10 information
112 |
113 | INSERT INTO backup_server_default_config (parameter,value,description) VALUES ('pgsql_bin_10','/usr/pgsql-10/bin','postgreSQL 10 bin directory');
114 |
115 | -- Update pgbackman_version with information about version 4:1_3_0
116 |
117 | INSERT INTO pgbackman_version (version,tag) VALUES ('4','v_1_3_0');
118 |
119 |
120 | COMMIT;
121 |
--------------------------------------------------------------------------------
/vagrant/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
5 | VAGRANTFILE_API_VERSION = "2"
6 |
7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
8 | # All Vagrant configuration is done here. The most common configuration
9 | # options are documented and commented below. For a complete reference,
10 | # please see the online documentation at vagrantup.com.
11 |
12 | # Every Vagrant virtual environment requires a box to build off of.
13 | #config.vm.box = "chef/centos-6.6"
14 |
15 | config.vm.define "pg-backup01" do |pgbackup1|
16 | pgbackup1.vm.box = "chef/centos-6.6"
17 | pgbackup1.vm.network "private_network", ip: "10.10.10.101"
18 | pgbackup1.vm.hostname = "pg-backup01.example.net"
19 | pgbackup1.vm.provision :shell, :path => "bootstrap.sh"
20 |
21 | pgbackup1.vm.provider "virtualbox" do |vb1|
22 | # Use VBoxManage to customize the VM. For example to change memory:
23 | vb1.customize ["modifyvm", :id, "--memory", "512"]
24 | end
25 | end
26 |
27 | config.vm.define "pg-backup02" do |pgbackup2|
28 | pgbackup2.vm.box = "chef/debian-7.4"
29 | pgbackup2.vm.network "private_network", ip: "10.10.10.102"
30 | pgbackup2.vm.hostname = "pg-backup02.example.net"
31 | pgbackup2.vm.provision :shell, :path => "bootstrap_debian.sh"
32 |
33 | pgbackup2.vm.provider "virtualbox" do |vb2|
34 | # Use VBoxManage to customize the VM. For example to change memory:
35 | vb2.customize ["modifyvm", :id, "--memory", "512"]
36 | end
37 | end
38 |
39 | config.vm.define "pg-node01" do |pgnode1|
40 | pgnode1.vm.box = "chef/centos-6.6"
41 | pgnode1.vm.network "private_network", ip: "10.10.10.103"
42 | pgnode1.vm.hostname = "pg-node01.example.net"
43 | pgnode1.vm.provision :shell, :path => "bootstrap3.sh"
44 |
45 | pgnode1.vm.provider "virtualbox" do |vb3|
46 | # Use VBoxManage to customize the VM. For example to change memory:
47 | vb3.customize ["modifyvm", :id, "--memory", "512"]
48 | end
49 | end
50 |
51 | config.vm.define "pg-node02" do |pgnode2|
52 | pgnode2.vm.box = "chef/centos-6.6"
53 | pgnode2.vm.network "private_network", ip: "10.10.10.108"
54 | pgnode2.vm.hostname = "pg-node02.example.net"
55 | pgnode2.vm.provision :shell, :path => "bootstrap3.sh"
56 |
57 | pgnode2.vm.provider "virtualbox" do |vb3|
58 | # Use VBoxManage to customize the VM. For example to change memory:
59 | vb3.customize ["modifyvm", :id, "--memory", "512"]
60 | end
61 | end
62 |
63 | config.vm.define "pgbackmandb" do |pgbackmandb|
64 | pgbackmandb.vm.box = "chef/centos-6.6"
65 | pgbackmandb.vm.network "private_network", ip: "10.10.10.104"
66 | pgbackmandb.vm.hostname = "pgbackmandb.example.net"
67 | pgbackmandb.vm.provision :shell, :path => "bootstrap2.sh"
68 |
69 | pgbackmandb.vm.provider "virtualbox" do |vb4|
70 | # Use VBoxManage to customize the VM. For example to change memory:
71 | vb4.customize ["modifyvm", :id, "--memory", "512"]
72 | end
73 | end
74 |
75 | config.vm.define "centos6" do |centos6|
76 | centos6.vm.box = "chef/centos-6.6"
77 | centos6.vm.network "private_network", ip: "10.10.10.105"
78 | centos6.vm.hostname = "centos6.example.net"
79 | centos6.vm.provision :shell, :path => "bootstrap_centos.sh"
80 |
81 | centos6.vm.provider "virtualbox" do |vb5|
82 | # Use VBoxManage to customize the VM. For example to change memory:
83 | vb5.customize ["modifyvm", :id, "--memory", "512"]
84 | end
85 | end
86 |
87 | config.vm.define "debian7" do |debian7|
88 | debian7.vm.box = "chef/debian-7.4"
89 | debian7.vm.network "private_network", ip: "10.10.10.106"
90 | debian7.vm.hostname = "debian7.example.net"
91 | debian7.vm.provision :shell, :path => "bootstrap_debian.sh"
92 |
93 | debian7.vm.provider "virtualbox" do |vb6|
94 | # Use VBoxManage to customize the VM. For example to change memory:
95 | vb6.customize ["modifyvm", :id, "--memory", "512"]
96 | end
97 | end
98 |
99 | config.vm.define "ubuntu14" do |ubuntu14|
100 | ubuntu14.vm.box = "chef/ubuntu-14.04"
101 | ubuntu14.vm.network "private_network", ip: "10.10.10.107"
102 | ubuntu14.vm.hostname = "ubuntu14.example.net"
103 | ubuntu14.vm.provision :shell, :path => "bootstrap_debian.sh"
104 |
105 | ubuntu14.vm.provider "virtualbox" do |vb7|
106 | # Use VBoxManage to customize the VM. For example to change memory:
107 | vb7.customize ["modifyvm", :id, "--memory", "512"]
108 | end
109 | end
110 |
111 | config.vm.define "centos7" do |centos7|
112 | centos7.vm.box = "centos7"
113 | centos7.vm.network "private_network", ip: "10.10.10.108"
114 | centos7.vm.hostname = "centos7.example.net"
115 | centos7.vm.provision :shell, :path => "bootstrap_centos.sh"
116 |
117 | centos7.vm.provider "virtualbox" do |vb8|
118 | # Use VBoxManage to customize the VM. For example to change memory:
119 | vb8.customize ["modifyvm", :id, "--memory", "1024"]
120 | end
121 | end
122 |
123 |
124 | # The url from where the 'config.vm.box' box will be fetched if it
125 | # doesn't already exist on the user's system.
126 | # config.vm.box_url = "http://domain.com/path/to/above.box"
127 |
128 | # Create a forwarded port mapping which allows access to a specific port
129 | # within the machine from a port on the host machine. In the example below,
130 | # accessing "localhost:8080" will access port 80 on the guest machine.
131 | # config.vm.network "forwarded_port", guest: 80, host: 8080
132 |
133 | # Create a private network, which allows host-only access to the machine
134 | # using a specific IP.
135 | #config.vm.network "private_network", ip: "10.10.10.100"
136 |
137 |
138 | # Create a public network, which generally matched to bridged network.
139 | # Bridged networks make the machine appear as another physical device on
140 | # your network.
141 | # config.vm.network "public_network"
142 |
143 | # If true, then any SSH connections made will enable agent forwarding.
144 | # Default value: false
145 | # config.ssh.forward_agent = true
146 |
147 | # Share an additional folder to the guest VM. The first argument is
148 | # the path on the host to the actual folder. The second argument is
149 | # the path on the guest to mount the folder. And the optional third
150 | # argument is a set of non-required options.
151 | # config.vm.synced_folder "../data", "/vagrant_data"
152 |
153 | # Provider-specific configuration so you can fine-tune various
154 | # backing providers for Vagrant. These expose provider-specific options.
155 | # Example for VirtualBox:
156 | #
157 | # config.vm.provider "virtualbox" do |vb|
158 | # # Don't boot with headless mode
159 | # vb.gui = true
160 | #
161 | # # Use VBoxManage to customize the VM. For example to change memory:
162 | # vb.customize ["modifyvm", :id, "--memory", "512"]
163 | # end
164 | #
165 | # View the documentation for the provider you're using for more
166 | # information on available options.
167 |
168 | # Enable provisioning with Puppet stand alone. Puppet manifests
169 | # are contained in a directory path relative to this Vagrantfile.
170 | # You will need to create the manifests directory and a manifest in
171 | # the file chef/centos-6.6.pp in the manifests_path directory.
172 | #
173 | # An example Puppet manifest to provision the message of the day:
174 | #
175 | # # group { "puppet":
176 | # # ensure => "present",
177 | # # }
178 | # #
179 | # # File { owner => 0, group => 0, mode => 0644 }
180 | # #
181 | # # file { '/etc/motd':
182 | # # content => "Welcome to your Vagrant-built virtual machine!
183 | # # Managed by Puppet.\n"
184 | # # }
185 | #
186 | # config.vm.provision "puppet" do |puppet|
187 | # puppet.manifests_path = "manifests"
188 | # puppet.manifest_file = "site.pp"
189 | # end
190 |
191 | # Enable provisioning with chef solo, specifying a cookbooks path, roles
192 | # path, and data_bags path (all relative to this Vagrantfile), and adding
193 | # some recipes and/or roles.
194 | #
195 | # config.vm.provision "chef_solo" do |chef|
196 | # chef.cookbooks_path = "../my-recipes/cookbooks"
197 | # chef.roles_path = "../my-recipes/roles"
198 | # chef.data_bags_path = "../my-recipes/data_bags"
199 | # chef.add_recipe "mysql"
200 | # chef.add_role "web"
201 | #
202 | # # You may also specify custom JSON attributes:
203 | # chef.json = { :mysql_password => "foo" }
204 | # end
205 |
206 | # Enable provisioning with chef server, specifying the chef server URL,
207 | # and the path to the validation key (relative to this Vagrantfile).
208 | #
209 | # The Opscode Platform uses HTTPS. Substitute your organization for
210 | # ORGNAME in the URL and validation key.
211 | #
212 | # If you have your own Chef Server, use the appropriate URL, which may be
213 | # HTTP instead of HTTPS depending on your configuration. Also change the
214 | # validation key to validation.pem.
215 | #
216 | # config.vm.provision "chef_client" do |chef|
217 | # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
218 | # chef.validation_key_path = "ORGNAME-validator.pem"
219 | # end
220 | #
221 | # If you're using the Opscode platform, your validator client is
222 | # ORGNAME-validator, replacing ORGNAME with your organization name.
223 | #
224 | # If you have your own Chef Server, the default validation client name is
225 | # chef-validator, unless you changed the configuration.
226 | #
227 | # chef.validation_client_name = "ORGNAME-validator"
228 | end
229 |
--------------------------------------------------------------------------------
/vagrant/Vagrantfile_orig:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
5 | VAGRANTFILE_API_VERSION = "2"
6 |
7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
8 | # All Vagrant configuration is done here. The most common configuration
9 | # options are documented and commented below. For a complete reference,
10 | # please see the online documentation at vagrantup.com.
11 |
12 | # Every Vagrant virtual environment requires a box to build off of.
13 | config.vm.box = "chef/centos-6.5"
14 |
15 | # The url from where the 'config.vm.box' box will be fetched if it
16 | # doesn't already exist on the user's system.
17 | # config.vm.box_url = "http://domain.com/path/to/above.box"
18 |
19 | # Create a forwarded port mapping which allows access to a specific port
20 | # within the machine from a port on the host machine. In the example below,
21 | # accessing "localhost:8080" will access port 80 on the guest machine.
22 | # config.vm.network "forwarded_port", guest: 80, host: 8080
23 |
24 | # Create a private network, which allows host-only access to the machine
25 | # using a specific IP.
26 | # config.vm.network "private_network", ip: "192.168.33.10"
27 |
28 | # Create a public network, which generally matched to bridged network.
29 | # Bridged networks make the machine appear as another physical device on
30 | # your network.
31 | # config.vm.network "public_network"
32 |
33 | # If true, then any SSH connections made will enable agent forwarding.
34 | # Default value: false
35 | # config.ssh.forward_agent = true
36 |
37 | # Share an additional folder to the guest VM. The first argument is
38 | # the path on the host to the actual folder. The second argument is
39 | # the path on the guest to mount the folder. And the optional third
40 | # argument is a set of non-required options.
41 | # config.vm.synced_folder "../data", "/vagrant_data"
42 |
43 | # Provider-specific configuration so you can fine-tune various
44 | # backing providers for Vagrant. These expose provider-specific options.
45 | # Example for VirtualBox:
46 | #
47 | # config.vm.provider "virtualbox" do |vb|
48 | # # Don't boot with headless mode
49 | # vb.gui = true
50 | #
51 | # # Use VBoxManage to customize the VM. For example to change memory:
52 | # vb.customize ["modifyvm", :id, "--memory", "1024"]
53 | # end
54 | #
55 | # View the documentation for the provider you're using for more
56 | # information on available options.
57 |
58 | # Enable provisioning with Puppet stand alone. Puppet manifests
59 | # are contained in a directory path relative to this Vagrantfile.
60 | # You will need to create the manifests directory and a manifest in
61 | # the file chef/centos-6.5.pp in the manifests_path directory.
62 | #
63 | # An example Puppet manifest to provision the message of the day:
64 | #
65 | # # group { "puppet":
66 | # # ensure => "present",
67 | # # }
68 | # #
69 | # # File { owner => 0, group => 0, mode => 0644 }
70 | # #
71 | # # file { '/etc/motd':
72 | # # content => "Welcome to your Vagrant-built virtual machine!
73 | # # Managed by Puppet.\n"
74 | # # }
75 | #
76 | # config.vm.provision "puppet" do |puppet|
77 | # puppet.manifests_path = "manifests"
78 | # puppet.manifest_file = "site.pp"
79 | # end
80 |
81 | # Enable provisioning with chef solo, specifying a cookbooks path, roles
82 | # path, and data_bags path (all relative to this Vagrantfile), and adding
83 | # some recipes and/or roles.
84 | #
85 | # config.vm.provision "chef_solo" do |chef|
86 | # chef.cookbooks_path = "../my-recipes/cookbooks"
87 | # chef.roles_path = "../my-recipes/roles"
88 | # chef.data_bags_path = "../my-recipes/data_bags"
89 | # chef.add_recipe "mysql"
90 | # chef.add_role "web"
91 | #
92 | # # You may also specify custom JSON attributes:
93 | # chef.json = { :mysql_password => "foo" }
94 | # end
95 |
96 | # Enable provisioning with chef server, specifying the chef server URL,
97 | # and the path to the validation key (relative to this Vagrantfile).
98 | #
99 | # The Opscode Platform uses HTTPS. Substitute your organization for
100 | # ORGNAME in the URL and validation key.
101 | #
102 | # If you have your own Chef Server, use the appropriate URL, which may be
103 | # HTTP instead of HTTPS depending on your configuration. Also change the
104 | # validation key to validation.pem.
105 | #
106 | # config.vm.provision "chef_client" do |chef|
107 | # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
108 | # chef.validation_key_path = "ORGNAME-validator.pem"
109 | # end
110 | #
111 | # If you're using the Opscode platform, your validator client is
112 | # ORGNAME-validator, replacing ORGNAME with your organization name.
113 | #
114 | # If you have your own Chef Server, the default validation client name is
115 | # chef-validator, unless you changed the configuration.
116 | #
117 | # chef.validation_client_name = "ORGNAME-validator"
118 | end
119 |
--------------------------------------------------------------------------------
/vagrant/bootstrap.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | rpm -ivh http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-centos93-9.3-1.noarch.rpm
4 | rpm -ivh http://yum.postgresql.org/9.2/redhat/rhel-6-x86_64/pgdg-centos92-9.2-6.noarch.rpm
5 | rpm -ivh http://yum.postgresql.org/9.1/redhat/rhel-6-x86_64/pgdg-centos91-9.1-4.noarch.rpm
6 | rpm -ivh http://yum.postgresql.org/9.0/redhat/rhel-6-x86_64/pgdg-centos90-9.0-5.noarch.rpm
7 |
8 | yum -y update
9 | yum -y install python-setuptools gcc postgresql93 postgresql92 postgresql91 postgresql90 python-devel python-psycopg2 python-argparse
10 | yum -y upgrade
11 |
12 | echo "10.10.10.101 pg-backup01.example.net pg-backup01" >> /etc/hosts
13 | echo "10.10.10.102 pg-backup02.example.net pg-backup02" >> /etc/hosts
14 | echo "10.10.10.103 pg-node01.example.net pg-node01" >> /etc/hosts
15 | echo "10.10.10.104 pgbackmandb.example.net pgbackmandb" >> /etc/hosts
16 | echo "10.10.10.105 pg-node02.example.net pg-node02" >> /etc/hosts
17 |
18 |
--------------------------------------------------------------------------------
/vagrant/bootstrap2.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | rpm -ivh http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-centos93-9.3-1.noarch.rpm
4 | yum -y update
5 | yum -y groupinstall "PostgreSQL Database Server 9.3 PGDG"
6 | yum -y upgrade
7 |
8 | echo "10.10.10.101 pg-backup01.example.net pg-backup01" >> /etc/hosts
9 | echo "10.10.10.102 pg-backup02.example.net pg-backup02" >> /etc/hosts
10 | echo "10.10.10.103 pg-node01.example.net pg-node01" >> /etc/hosts
11 | echo "10.10.10.104 pgbackmandb.example.net pgbackmandb" >> /etc/hosts
12 | echo "10.10.10.105 pg-node02.example.net pg-node02" >> /etc/hosts
13 |
14 | sudo /etc/init.d/postgresql-9.3 initdb
15 |
16 | echo "host pgbackman pgbackman_role_rw 10.10.10.101/32 trust" > /var/lib/pgsql/9.3/data/pg_hba.conf
17 | echo "host pgbackman pgbackman_role_rw 10.10.10.102/32 trust" >> /var/lib/pgsql/9.3/data/pg_hba.conf
18 | echo "local all all peer" >> /var/lib/pgsql/9.3/data/pg_hba.conf
19 |
20 | echo "listen_addresses = '10.10.10.104'" >> /var/lib/pgsql/9.3/data/postgresql.conf
21 |
22 | sudo /etc/init.d/postgresql-9.3 start
23 |
24 |
--------------------------------------------------------------------------------
/vagrant/bootstrap3.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | rpm -ivh http://yum.postgresql.org/9.1/redhat/rhel-6-x86_64/pgdg-centos91-9.1-4.noarch.rpm
4 | yum -y update
5 | yum -y groupinstall "PostgreSQL Database Server 9.1 PGDG"
6 | yum -y upgrade
7 |
8 | echo "10.10.10.101 pg-backup01.example.net pg-backup01" >> /etc/hosts
9 | echo "10.10.10.102 pg-backup02.example.net pg-backup02" >> /etc/hosts
10 | echo "10.10.10.103 pg-node01.example.net pg-node01" >> /etc/hosts
11 | echo "10.10.10.104 pgbackmandb.example.net pgbackmandb" >> /etc/hosts
12 | echo "10.10.10.105 pg-node02.example.net pg-node02" >> /etc/hosts
13 |
14 | sudo /etc/init.d/postgresql-9.1 initdb
15 |
16 | echo "host all postgres 10.10.10.101/32 trust" > /var/lib/pgsql/9.1/data/pg_hba.conf
17 | echo "host all postgres 10.10.10.102/32 trust" >> /var/lib/pgsql/9.1/data/pg_hba.conf
18 | echo "local all all peer" >> /var/lib/pgsql/9.1/data/pg_hba.conf
19 |
20 | echo "listen_addresses = '10.10.10.103'" >> /var/lib/pgsql/9.1/data/postgresql.conf
21 |
22 | sudo /etc/init.d/postgresql-9.1 start
23 |
24 |
--------------------------------------------------------------------------------
/vagrant/bootstrap_centos.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo "10.10.10.105 centos6.example.net centos6" >> /etc/hosts
4 | echo "10.10.10.106 debian7.example.net debian7" >> /etc/hosts
5 | echo "10.10.10.107 ubuntu14.example.net ubuntu14" >> /etc/hosts
6 | echo "10.10.10.108 centos7.example.net centos7" >> /etc/hosts
7 |
8 |
9 | yum -y update
10 | yum -y install rpm-build git make
11 | yum -y upgrade
12 |
--------------------------------------------------------------------------------
/vagrant/bootstrap_debian.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo "10.10.10.105 centos6.example.net centos6" >> /etc/hosts
4 | echo "10.10.10.106 debian7.example.net debian7" >> /etc/hosts
5 | echo "10.10.10.107 ubuntu14.example.net ubuntu14" >> /etc/hosts
6 |
7 | apt-get -y update
8 | apt-get -y install git make
9 | apt-get -y upgrade
10 |
11 |
--------------------------------------------------------------------------------