Compare commits
176 commits
Author | SHA1 | Date | |
---|---|---|---|
|
abd53ebf24 | ||
|
d8364d9b73 | ||
|
061c65ecc0 | ||
|
80333cb51b | ||
|
b08c94efa5 | ||
|
805eb2cafa | ||
|
493952de22 | ||
|
d8ad755f1a | ||
|
7218cffa10 | ||
|
8e2a6fd86f | ||
|
16fbb1c1dd | ||
|
2561e4df43 | ||
|
f846bd7361 | ||
|
d4c7fd5122 | ||
|
07c1af1e77 | ||
|
b938ad0659 | ||
|
6789f12830 | ||
|
4bb06a7a6a | ||
|
18ae852937 | ||
|
337d4cda1d | ||
|
651d677224 | ||
|
13c57aecab | ||
|
698e6856c5 | ||
|
3f6aa6e720 | ||
|
bc9ea030d9 | ||
|
065c21d5b4 | ||
|
3c0ecd3c00 | ||
|
93a4c0e808 | ||
|
620a2e7b3c | ||
|
fcf46e4f44 | ||
|
4d2f1ddafb | ||
|
2204a06506 | ||
|
d55f65a0e2 | ||
|
901ccd03a0 | ||
|
e0e8ad1e3b | ||
|
2d251e3c5f | ||
|
6f7bce46c7 | ||
|
0950b2f6f6 | ||
|
e66336e15f | ||
|
55ff46d44a | ||
|
0980100739 | ||
|
a177193d11 | ||
|
382df796a5 | ||
|
57de374578 | ||
|
e731ef0c4b | ||
|
29bbc50d25 | ||
|
66be3f197f | ||
|
b225c4c3b1 | ||
|
32e5157e48 | ||
|
65368a7604 | ||
|
bc535d91ac | ||
|
367af70475 | ||
|
248008d4d1 | ||
|
47d4b84658 | ||
|
0540a24251 | ||
|
677c1da0e9 | ||
|
ab3f724c0d | ||
|
2547ade6d6 | ||
|
7e3102048e | ||
|
1061d26488 | ||
|
40882559d6 | ||
|
ae266befb5 | ||
|
91e4f6a2a1 | ||
|
dcd7228ed2 | ||
|
fb7e2247ca | ||
|
bbf83c38b7 | ||
|
0c31e02083 | ||
|
653653cda0 | ||
|
4e4f58a8ad | ||
|
15535f5b06 | ||
|
b12b894540 | ||
|
5a9d695c7c | ||
|
92a9541379 | ||
|
a774b639ea | ||
|
5de37c8eb7 | ||
|
349c488e56 | ||
|
e317591454 | ||
|
cb06825b0b | ||
|
9ff389cbcf | ||
|
ebbca02db5 | ||
|
838ada399c | ||
|
35ffad0ee1 | ||
|
64144d9462 | ||
|
5d22960dab | ||
|
11a8b7e32d | ||
|
323146d6c3 | ||
|
7a0d25d1f9 | ||
|
9d0554eff7 | ||
|
d0210b3c44 | ||
|
59e67661c1 | ||
|
28782ad36e | ||
|
75ac3fccc8 | ||
|
d9d278a20c | ||
|
b7f05aba21 | ||
|
b84ce3c64e | ||
|
b9a3822225 | ||
|
a0665db5ca | ||
|
19df924f6d | ||
|
ece9f9034a | ||
|
c1ff6cef63 | ||
|
3ce7c2f666 | ||
|
58498c802c | ||
|
bb2cf0583a | ||
|
f861410597 | ||
|
03f5268ab8 | ||
|
a5d049d4cc | ||
|
cda8e3c98b | ||
|
478c61a69f | ||
|
00e70ff628 | ||
|
064aa55dea | ||
|
8213e85e5c | ||
|
0c43d05b5c | ||
|
c4e7bc22b6 | ||
|
1cb22f5f07 | ||
|
a88d8d2be8 | ||
|
02c5fa3d53 | ||
|
c2c2b41e86 | ||
|
3d80ac96a1 | ||
|
211171e449 | ||
|
c41f80bec0 | ||
|
c906aa6a2f | ||
|
406f669eed | ||
|
89c03c8e6f | ||
|
b846435587 | ||
|
36d877bb06 | ||
|
87d9d41a5a | ||
|
f51b8c4e8d | ||
|
c7f84aa50c | ||
|
776a4b8638 | ||
|
85a72fd134 | ||
|
9d618b4bfa | ||
|
6d65224cd3 | ||
|
118335de2d | ||
|
0e2eb24850 | ||
|
828a243745 | ||
|
4b788a99d8 | ||
|
1bcdb8fba5 | ||
|
fa8ce4fc3f | ||
|
608325c828 | ||
|
56ece49b96 | ||
|
d1065c9fa6 | ||
|
fe822ee0d9 | ||
|
47a91a80e5 | ||
|
ea5622c61d | ||
|
8036aaa5c0 | ||
|
d6179bddbc | ||
|
0dc6a75fda | ||
|
58babd5c37 | ||
|
b2c0e004d2 | ||
|
bf01adb0fd | ||
|
aafa5862f8 | ||
|
de5c7ab5ea | ||
|
5e4718da08 | ||
|
433e90393d | ||
|
1238902427 | ||
|
bd7c5d5c0a | ||
|
648c25483b | ||
|
620dc6c88b | ||
|
74a9df55e1 | ||
|
b151ef1145 | ||
|
65a461bd1b | ||
|
bc59b64a42 | ||
|
dabb5de436 | ||
|
e2c73395f9 | ||
|
7ae7d607a7 | ||
|
7b7618c5ee | ||
|
60a335eae4 | ||
|
679a995b27 | ||
|
cfb77b8e08 | ||
|
4c71294378 | ||
|
a87da27fbb | ||
|
33dc368f38 | ||
|
01eb3aa5e2 | ||
|
02cb243278 | ||
|
4b09be0c6a | ||
|
1556a7f1b4 |
20
.Jenkinsfile
20
.Jenkinsfile
|
@ -1,15 +1,24 @@
|
||||||
pipeline {
|
pipeline {
|
||||||
agent { label 'sbuild' }
|
agent { label 'docker' }
|
||||||
stages {
|
stages {
|
||||||
stage('Build Debian package') {
|
stage('Build Debian package') {
|
||||||
|
agent {
|
||||||
|
docker {
|
||||||
|
image 'evolix/gbp:bullseye'
|
||||||
|
args '-u root --privileged'
|
||||||
|
}
|
||||||
|
}
|
||||||
when {
|
when {
|
||||||
branch 'debian'
|
branch 'debian'
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
sh 'gbp buildpackage'
|
sh 'mk-build-deps --install --remove debian/control'
|
||||||
|
sh 'rm -rf {source,*.gz,*.bz2,*.xz,*.deb,*.dsc,*.changes,*.buildinfo,lintian.txt,.git}'
|
||||||
|
sh "gbp clone --debian-branch=$GIT_BRANCH $GIT_URL source"
|
||||||
|
sh 'cd source && git checkout $GIT_BRANCH && gbp buildpackage -us -uc'
|
||||||
}
|
}
|
||||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'build-area/*.gz,build-area/*.bz2,build-area/*.xz,build-area/*.deb,build-area/*.dsc,build-area/*.changes,build-area/*.buildinfo,build-area/*.build,build-area/lintian.txt'
|
archiveArtifacts allowEmptyArchive: true, artifacts: '*.gz,*.bz2,*.xz,*.deb,*.dsc,*.changes,*.buildinfo,lintian.txt'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +28,10 @@ pipeline {
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
script {
|
script {
|
||||||
sh 'rsync -avP build-area/bkctld*.deb build-area/bkctld*.changes build-area/bkctld*.buildinfo pub.evolix.org:/srv/upload/'
|
sh 'echo Dummy line to remove once something actually happens.'
|
||||||
|
/* No crendentials yet.
|
||||||
|
sh 'rsync -avP bkctld* droneci@pub.evolix.net:/home/droneci/bkctld/'
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
35
.drone.yml
Normal file
35
.drone.yml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
kind: pipeline
|
||||||
|
name: default
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch
|
||||||
|
image: alpine/git
|
||||||
|
commands:
|
||||||
|
- git fetch --tags
|
||||||
|
|
||||||
|
- name: build debian package
|
||||||
|
image: evolix/gbp:bullseye
|
||||||
|
branches:
|
||||||
|
- debian
|
||||||
|
commands:
|
||||||
|
- mk-build-deps --install --remove debian/control
|
||||||
|
- git clean --force
|
||||||
|
- gbp buildpackage -us -uc
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- debian
|
||||||
|
|
||||||
|
- name: upload debian package
|
||||||
|
image: drillster/drone-rsync
|
||||||
|
settings:
|
||||||
|
hosts: ["pub.evolix.net"]
|
||||||
|
port: 22
|
||||||
|
user: droneci
|
||||||
|
key:
|
||||||
|
from_secret: drone_private_key
|
||||||
|
target: /home/droneci/bkctld/
|
||||||
|
source: ../bkctld*
|
||||||
|
delete: true
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- debian
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
*.swp
|
|
||||||
.vagrant
|
|
20
README.md
20
README.md
|
@ -1,20 +0,0 @@
|
||||||
EvoBackup
|
|
||||||
=========
|
|
||||||
|
|
||||||
EvoBackup is a combination of tools to manage backups on Evolix servers.
|
|
||||||
|
|
||||||
## The client side
|
|
||||||
|
|
||||||
_What you install on the servers you want to backup._
|
|
||||||
|
|
||||||
There is a backup script (usually executed by cron or similar), a utility script and some libraries.
|
|
||||||
|
|
||||||
More information in the [client README](/evolix/evobackup/src/branch/master/client/README.md).
|
|
||||||
|
|
||||||
## The server side
|
|
||||||
|
|
||||||
_What you install on the servers that store the backups._
|
|
||||||
|
|
||||||
This is also known as `bkctld` : a program to manage SSH servers in chroots to isolate backup destinations, daily copies and data retention.
|
|
||||||
|
|
||||||
More information in the [server README](/evolix/evobackup/src/branch/master/server/README.md).
|
|
|
@ -1,13 +1,8 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
This project does not follow semantic versioning.
|
|
||||||
The **major** part of the version is the year
|
|
||||||
The **minor** part changes is the month
|
|
||||||
The **patch** part changes is incremented if multiple releases happen the same month
|
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
@ -15,63 +10,6 @@ The **patch** part changes is incremented if multiple releases happen the same m
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
### Security
|
|
||||||
|
|
||||||
## [24.05.1] - 2022-05-14
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* client: fix shell syntax error
|
|
||||||
|
|
||||||
## [24.05] - 2022-05-02
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* evobackupctl: update LIBDIR when copying the template
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* evobackupctl: simplify the program path retrieval
|
|
||||||
|
|
||||||
## [24.04.1] - 2022-04-30
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* evobackupctl: quote ARGS variable for options parsing.
|
|
||||||
|
|
||||||
## [24.04] - 2022-04-29
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
* Vagrant definition for manual tests
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* split functions into libraries
|
|
||||||
* add evobackupctl script
|
|
||||||
* change the "zzz_evobackup" script to a template, easy to copy with evobackupctl
|
|
||||||
* use env-based shebang for shell scripts
|
|
||||||
* use $TMPDIR if available
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
* update-evobackup-canary is managed by ansible-roles.git
|
|
||||||
* deployment by Ansible is managed elsewhere (now in evolix-private.git, later in ansible-roles.git)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
* don't exit the whole program if a sync task can't be done
|
|
||||||
|
|
||||||
## [22.12] - 2022-12-27
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
* Use --dump-dir instead of --backup-dir to suppress dump-server-state warning
|
* Use --dump-dir instead of --backup-dir to suppress dump-server-state warning
|
||||||
* Do not use rsync compression
|
* Do not use rsync compression
|
||||||
* Replace rsync option --verbose by --itemize-changes
|
* Replace rsync option --verbose by --itemize-changes
|
||||||
|
@ -80,19 +18,17 @@ The **patch** part changes is incremented if multiple releases happen the same m
|
||||||
* Add AGPL License and README
|
* Add AGPL License and README
|
||||||
* Script now depends on Bash
|
* Script now depends on Bash
|
||||||
* tolerate absence of mtr or traceroute
|
* tolerate absence of mtr or traceroute
|
||||||
* Only one loop for all Redis instances
|
|
||||||
* remodel how we build the rsync command
|
### Deprecated
|
||||||
* use sub shells instead of moving around
|
|
||||||
* Separate Rsync for the canary file if the main Rsync has finished without errors
|
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
* No more fallback if dump-server-state is missing
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Make start_time and stop_time compatible with OpenBSD
|
* Make start_time and stop_time compatible with OpenBSD
|
||||||
|
|
||||||
## [22.03] - 2022-04-03
|
### Security
|
||||||
|
|
||||||
|
## [22.03]
|
||||||
|
|
||||||
Split client and server parts of the project
|
Split client and server parts of the project
|
||||||
|
|
167
client/README.md
167
client/README.md
|
@ -1,166 +1,3 @@
|
||||||
EvoBackup — the client side
|
Pour l'installation de `zzz_evobackup`, voir <https://intra.evolix.net/Installation_jail_backup_Evolix#installation-du-client-evobackup>
|
||||||
===========================
|
|
||||||
|
|
||||||
_What you install on the servers you want to backup._
|
Pour `update-evobackup-canary`, voir <https://intra.evolix.net/OutilsInternes/update-evobackup-canary>
|
||||||
|
|
||||||
## Design
|
|
||||||
|
|
||||||
### backup script
|
|
||||||
|
|
||||||
The tip of the iceberg is a script (often called `zzz_evobackup` because it is executed by cron at the very end of the _daily_ tasks list).
|
|
||||||
|
|
||||||
This is where you setup **what**, **how** and **where** to backup on remote server(s).
|
|
||||||
|
|
||||||
There are 2 main phases in the backup :
|
|
||||||
|
|
||||||
1. **local tasks**: everything you want to do locally (save state information, dump databases…).
|
|
||||||
2. **sync tasks**: which data (including what has been prepared in the local phase) you want to send to remote servers.
|
|
||||||
|
|
||||||
### libraries
|
|
||||||
|
|
||||||
The vast majority of the logic is in libraries, to help maintaining them without having to modify the backup script.
|
|
||||||
|
|
||||||
They contain mostly _dump_ functions that are called from the backup script.
|
|
||||||
|
|
||||||
Those functions contain a lot of code for logging, options parsing, error management, and some code specific to the dump task.
|
|
||||||
|
|
||||||
### utility script
|
|
||||||
|
|
||||||
A scripts named `evobackupctl` helps initializing a backup jail on remote servers or install the backup script where you want.
|
|
||||||
|
|
||||||
## Install and update
|
|
||||||
|
|
||||||
To install, copy these files :
|
|
||||||
|
|
||||||
* `lib/*` → `/usr/local/lib/evobackup`
|
|
||||||
* `bin/evobackupctl` → `/usr/local/bin/evobackupctl`
|
|
||||||
|
|
||||||
To update, simply overwrite them, since their content should (must?) not be customized locally.
|
|
||||||
|
|
||||||
There is also an [evobackup-client](https://gitea.evolix.org/evolix/ansible-roles/src/branch/unstable/evobackup-client) Ansible role to do this (and some other stuff) automatically.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### backup script
|
|
||||||
|
|
||||||
#### minimal configuration
|
|
||||||
|
|
||||||
##### mail notifications
|
|
||||||
|
|
||||||
The absolute minimum you must do is set the `MAIL` variable to the email address you want to be notified at.
|
|
||||||
|
|
||||||
##### sync tasks
|
|
||||||
|
|
||||||
If you want to sync files to a remote server, you have to set the `SERVERS` variable with at least one host and port.
|
|
||||||
Beware that the default `evolix-system` _sync_ doesn't sync `/home`, `/srv`…
|
|
||||||
|
|
||||||
If you want to sync files to multiple groups of servers, you can add as many _sync_ sections as you want.
|
|
||||||
A _sync_ section must contain something like this :
|
|
||||||
|
|
||||||
~~~bash
|
|
||||||
# The name of the "sync" (visible in logs)
|
|
||||||
SYNC_NAME="evolix-system"
|
|
||||||
# List of servers
|
|
||||||
SERVERS=(
|
|
||||||
host1:port1
|
|
||||||
host2:port2
|
|
||||||
)
|
|
||||||
# List of paths to include in the sync
|
|
||||||
RSYNC_INCLUDES=(
|
|
||||||
"${rsync_default_includes[@]}"
|
|
||||||
/etc
|
|
||||||
/root
|
|
||||||
/var
|
|
||||||
)
|
|
||||||
# List of paths to exclude from the sync
|
|
||||||
RSYNC_EXCLUDES=(
|
|
||||||
"${rsync_default_excludes[@]}"
|
|
||||||
)
|
|
||||||
# Actual sync command
|
|
||||||
sync "${SYNC_NAME}" "SERVERS[@]" "RSYNC_INCLUDES[@]" "RSYNC_EXCLUDES[@]"
|
|
||||||
~~~
|
|
||||||
|
|
||||||
##### local tasks
|
|
||||||
|
|
||||||
By default, the `local_tasks()` function only:
|
|
||||||
|
|
||||||
* executes [dump-server-state](https://gitea.evolix.org/evolix/dump-server-state) to put have a saved copy of a lot of information about the server
|
|
||||||
* saves a traceroute to some key network endpoints (using the `dump_traceroute()` function)
|
|
||||||
|
|
||||||
You can enable (by uncommenting) as many _dump_ functions as you want.
|
|
||||||
|
|
||||||
### advanced customization
|
|
||||||
|
|
||||||
Since this is a shell script, you can add any bash-compatible code you want.
|
|
||||||
If you do so you should read the libraries code to make sure that you don't overwrite existing functions.
|
|
||||||
|
|
||||||
##### sync tasks
|
|
||||||
|
|
||||||
If you don't want to sync files to any remote servers, you can simply replace the content of the `sync_tasks()` function by a no-op command (`:`).
|
|
||||||
|
|
||||||
`RSYNC_INCLUDES` and `RSYNC_EXCLUDES` refer to `${rsync_default_includes[@]}` and `${rsync_default_excludes[@]}` (defined in the `main.sh` library) to simplify the configuration. If you want to precisely customize the lists you can remove them and add you own.
|
|
||||||
|
|
||||||
##### local tasks
|
|
||||||
|
|
||||||
Existing _dump_ functions (as defined in libraries) are usable as-is, but you can also create your own local custom functions.
|
|
||||||
You have to define them in the backup script (or in a file that you source from the backup script).
|
|
||||||
You should prefix their name with `dump_` base your customization on the `dump_custom()` (documented in the backup script) to keep the boilerplate code (for logging, error management…).
|
|
||||||
|
|
||||||
You can customize some values inside the `setup_custom()`, like the server's hostname, the notification mail subject…
|
|
||||||
|
|
||||||
If you want to source libraries from a different path, you can change the `LIBDIR` variable at the end of the backup script.
|
|
||||||
|
|
||||||
### utility tool
|
|
||||||
|
|
||||||
The command is `evobackupctl`.
|
|
||||||
|
|
||||||
~~~
|
|
||||||
# evobackupctl --help
|
|
||||||
evobackupctl helps managing evobackup scripts
|
|
||||||
|
|
||||||
Options
|
|
||||||
-h, --help print this message and exit
|
|
||||||
-V, --version print version and exit
|
|
||||||
--jail-init-commands print jail init commands
|
|
||||||
--copy-template=PATH copy the backup template to PATH
|
|
||||||
|
|
||||||
# evobackupctl --version
|
|
||||||
evobackupctl version 24.04
|
|
||||||
|
|
||||||
Copyright 2024 Evolix <info@evolix.fr>,
|
|
||||||
Jérémy Lecour <jlecour@evolix.fr>.
|
|
||||||
|
|
||||||
evobackupctl comes with ABSOLUTELY NO WARRANTY. This is free software,
|
|
||||||
and you are welcome to redistribute it under certain conditions.
|
|
||||||
See the GNU General Public License v3.0 for details.
|
|
||||||
~~~
|
|
||||||
|
|
||||||
#### jail init commands
|
|
||||||
|
|
||||||
It prints a list of commands you can execute on remote backup servers to configure a backup "jail".
|
|
||||||
|
|
||||||
~~~
|
|
||||||
# evobackupctl --jail-init-commands
|
|
||||||
Copy-paste those lines on backup server(s) :
|
|
||||||
----------
|
|
||||||
SERVER_NAME=example-hostname
|
|
||||||
SERVER_IP=203.0.113.1
|
|
||||||
echo 'ssh-ed25519 xxxxxx root@example-hostname' > /root/${SERVER_NAME}.pub
|
|
||||||
bkctld init ${SERVER_NAME}
|
|
||||||
bkctld key ${SERVER_NAME} /root/${SERVER_NAME}.pub
|
|
||||||
bkctld ip ${SERVER_NAME} ${SERVER_IP}
|
|
||||||
bkctld start ${SERVER_NAME}
|
|
||||||
bkctld status ${SERVER_NAME}
|
|
||||||
grep --quiet --extended-regexp "^\s?NODE=" /etc/default/bkctld && bkctld sync ${SERVER_NAME}
|
|
||||||
----------
|
|
||||||
~~~
|
|
||||||
|
|
||||||
#### copy-template
|
|
||||||
|
|
||||||
It copies the backups script template to the path of your choice, nothing more.
|
|
||||||
|
|
||||||
~~~
|
|
||||||
# evobackupctl --copy-template /etc/cron.daily/zzz_evobackup
|
|
||||||
New evobackup script has been saved to '/etc/cron.daily/zzz_evobackup'.
|
|
||||||
Remember to customize it (mail notifications, backup servers…).
|
|
||||||
~~~
|
|
49
client/Vagrantfile
vendored
49
client/Vagrantfile
vendored
|
@ -1,49 +0,0 @@
|
||||||
# -*- mode: ruby -*-
|
|
||||||
# vi: set ft=ruby :
|
|
||||||
|
|
||||||
# Load ~/.VagrantFile if exist, permit local config provider
|
|
||||||
vagrantfile = File.join(Dir.home, ".VagrantFile")
|
|
||||||
load File.expand_path(vagrantfile) if File.exist?(vagrantfile)
|
|
||||||
|
|
||||||
Vagrant.configure("2") do |config|
|
|
||||||
# Run "vagrant rsync-auto" to sync after each change
|
|
||||||
config.vm.synced_folder ".", "/vagrant", type: "rsync", disabled: true
|
|
||||||
config.vm.synced_folder "bin", "/usr/local/bin", type: "rsync"
|
|
||||||
config.vm.synced_folder "lib", "/usr/local/lib/evobackup", type: "rsync"
|
|
||||||
|
|
||||||
config.ssh.shell = "/bin/sh"
|
|
||||||
|
|
||||||
config.vm.provider :libvirt do |libvirt|
|
|
||||||
# libvirt.storage :file, :size => '10G', :device => 'vdb'
|
|
||||||
libvirt.memory = 1024
|
|
||||||
libvirt.cpus = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
config_script = <<~SCRIPT
|
|
||||||
set -e
|
|
||||||
sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/; s/# fr_FR.UTF-8 UTF-8/fr_FR.UTF-8 UTF-8/' /etc/locale.gen && \
|
|
||||||
echo 'LANG="fr_FR.UTF-8"'>/etc/default/locale && \
|
|
||||||
dpkg-reconfigure --frontend=noninteractive locales && \
|
|
||||||
update-locale LANG=fr_FR.UTF-8
|
|
||||||
exit 0
|
|
||||||
SCRIPT
|
|
||||||
|
|
||||||
[
|
|
||||||
{version: "buster"},
|
|
||||||
{version: "bullseye"},
|
|
||||||
{version: "bookworm"}
|
|
||||||
].each do |i|
|
|
||||||
config.vm.define(i[:version].to_s) do |node|
|
|
||||||
node.vm.hostname = "evobackup-#{i[:version]}"
|
|
||||||
node.vm.box = "debian/#{i[:version]}64"
|
|
||||||
|
|
||||||
node.vm.provision "config", type: "shell", inline: config_script
|
|
||||||
|
|
||||||
node.vm.provision :ansible do |ansible|
|
|
||||||
ansible.playbook = "vagrant.yml"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -1,156 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# shellcheck disable=SC2155
|
|
||||||
readonly PROGPATH=$(readlink -m "${0}")
|
|
||||||
readonly PROGNAME=$(basename "${PROGPATH}")
|
|
||||||
# shellcheck disable=SC2124
|
|
||||||
readonly ARGS=$@
|
|
||||||
|
|
||||||
# Change this to wherever you install the libraries
|
|
||||||
readonly LIBDIR="/usr/local/lib/evobackup"
|
|
||||||
|
|
||||||
source "${LIBDIR}/main.sh"
|
|
||||||
|
|
||||||
show_version() {
|
|
||||||
cat <<END
|
|
||||||
${PROGNAME} version ${VERSION}
|
|
||||||
|
|
||||||
Copyright 2024 Evolix <info@evolix.fr>,
|
|
||||||
Jérémy Lecour <jlecour@evolix.fr>.
|
|
||||||
|
|
||||||
${PROGNAME} comes with ABSOLUTELY NO WARRANTY. This is free software,
|
|
||||||
and you are welcome to redistribute it under certain conditions.
|
|
||||||
See the GNU General Public License v3.0 for details.
|
|
||||||
END
|
|
||||||
}
|
|
||||||
show_help() {
|
|
||||||
cat <<END
|
|
||||||
${PROGNAME} helps managing evobackup scripts
|
|
||||||
|
|
||||||
Options
|
|
||||||
-h, --help print this message and exit
|
|
||||||
-V, --version print version and exit
|
|
||||||
--jail-init-commands print jail init commands
|
|
||||||
--copy-template=PATH copy the backup template to PATH
|
|
||||||
END
|
|
||||||
}
|
|
||||||
|
|
||||||
jail_init_commands() {
|
|
||||||
if [ ! -f /root/.ssh/id_ed25519.pub ]; then
|
|
||||||
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ''
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
SSH_KEY=$(cat /root/.ssh/id_ed25519.pub)
|
|
||||||
SERVER_NAME=$(hostname -s)
|
|
||||||
if [ "$(uname -s)" = "OpenBSD" ]; then
|
|
||||||
SERVER_IP=$(ifconfig egress | grep "inet " | head -1 | awk '{ print $2}')
|
|
||||||
else
|
|
||||||
SERVER_IP=$(curl -4 https://ifconfig.me 2> /dev/null || hostname -I | awk '{ print $1}')
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Copy-paste those lines on backup server(s) :"
|
|
||||||
echo "----------"
|
|
||||||
echo "SERVER_NAME=${SERVER_NAME}"
|
|
||||||
echo "SERVER_IP=${SERVER_IP}"
|
|
||||||
echo "echo '${SSH_KEY}' > /root/\${SERVER_NAME}.pub"
|
|
||||||
echo "bkctld init \${SERVER_NAME}"
|
|
||||||
echo "bkctld key \${SERVER_NAME} /root/\${SERVER_NAME}.pub"
|
|
||||||
echo "bkctld ip \${SERVER_NAME} \${SERVER_IP}"
|
|
||||||
echo "bkctld start \${SERVER_NAME}"
|
|
||||||
echo "bkctld status \${SERVER_NAME}"
|
|
||||||
echo "grep --quiet --extended-regexp \"^\\s?NODE=\" /etc/default/bkctld && bkctld sync \${SERVER_NAME}"
|
|
||||||
echo "----------"
|
|
||||||
}
|
|
||||||
|
|
||||||
copy_template() {
|
|
||||||
dest_path=${1}
|
|
||||||
dest_dir="$(dirname "${dest_path}")"
|
|
||||||
|
|
||||||
if [ -e "${dest_path}" ]; then
|
|
||||||
printf "Path for new evobackup script '%s' already exists.\n" "${dest_path}" >&2
|
|
||||||
exit 1
|
|
||||||
elif [ ! -e "${dest_dir}" ]; then
|
|
||||||
printf "Parent directory '%s' doesn't exist. Create it first.\n" "${dest_dir}" >&2
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
if cp "${LIBDIR}/zzz_evobackup.sh" "${dest_path}"; then
|
|
||||||
chmod 750 "${dest_path}"
|
|
||||||
|
|
||||||
# Insert metadata about the template
|
|
||||||
sed -i "s|@COMMAND@|${PROGPATH} ${ARGS}|" "${dest_path}"
|
|
||||||
sed -i "s|@DATE@|$(date --iso-8601=seconds)|" "${dest_path}"
|
|
||||||
sed -i "s|@VERSION@|${VERSION}|" "${dest_path}"
|
|
||||||
|
|
||||||
# Make sure that the library directory is correct
|
|
||||||
sed -i "s|^LIBDIR=.\+|LIBDIR=\"${LIBDIR}\"|" "${dest_path}"
|
|
||||||
|
|
||||||
printf "New evobackup script has been saved to '%s'.\n" "${dest_path}"
|
|
||||||
printf "Remember to customize it (mail notifications, backup servers…).\n"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
# If no argument is provided, print help and exit
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ -z "${ARGS}" ]; then
|
|
||||||
show_help
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
-V|--version)
|
|
||||||
show_version
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
-h|--help)
|
|
||||||
show_help
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
--jail-init-commands)
|
|
||||||
jail_init_commands
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
--copy-template)
|
|
||||||
# copy-template option, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
copy_template "${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
printf "'%s' requires a non-empty option argument.\n" "--copy-template" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--copy-template=?*)
|
|
||||||
# copy-template option, with value separated by =
|
|
||||||
copy_template "${1#*=}"
|
|
||||||
;;
|
|
||||||
--copy-template=)
|
|
||||||
# copy-template option, without value
|
|
||||||
printf "'%s' requires a non-empty option argument.\n" "--copy-template" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
printf "unknown option '%s'.\n" "${1}" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
main ${ARGS}
|
|
|
@ -1,301 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# shellcheck disable=SC2034,SC2317,SC2155
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Snapshot Elasticsearch data
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# --protocol=<http|https> (default: http)
|
|
||||||
# --cacert=[String] (default: <none>)
|
|
||||||
# path to the CA certificate to use when using https
|
|
||||||
# --host=[String] (default: localhost)
|
|
||||||
# --port=[Integer] (default: 9200)
|
|
||||||
# --user=[String] (default: <none>)
|
|
||||||
# --password=[String] (default: <none>)
|
|
||||||
# --repository=[String] (default: snaprepo)
|
|
||||||
# --snapshot=[String] (default: snapshot.daily)
|
|
||||||
#######################################################################
|
|
||||||
dump_elasticsearch() {
|
|
||||||
local option_protocol="http"
|
|
||||||
local option_cacert=""
|
|
||||||
local option_host="localhost"
|
|
||||||
local option_port="9200"
|
|
||||||
local option_user=""
|
|
||||||
local option_password=""
|
|
||||||
local option_repository="snaprepo"
|
|
||||||
local option_snapshot="snapshot.daily"
|
|
||||||
local option_others=""
|
|
||||||
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
--protocol)
|
|
||||||
# protocol options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_protocol="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--protocol=?*)
|
|
||||||
# protocol options, with value separated by =
|
|
||||||
option_protocol="${1#*=}"
|
|
||||||
;;
|
|
||||||
--protocol=)
|
|
||||||
# protocol options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--protocol' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--cacert)
|
|
||||||
# cacert options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_cacert="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--cacert' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--cacert=?*)
|
|
||||||
# cacert options, with value separated by =
|
|
||||||
option_cacert="${1#*=}"
|
|
||||||
;;
|
|
||||||
--cacert=)
|
|
||||||
# cacert options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--cacert' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--host)
|
|
||||||
# host options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_host="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--host=?*)
|
|
||||||
# host options, with value separated by =
|
|
||||||
option_host="${1#*=}"
|
|
||||||
;;
|
|
||||||
--host=)
|
|
||||||
# host options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--host' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--port)
|
|
||||||
# port options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_port="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--port=?*)
|
|
||||||
# port options, with value separated by =
|
|
||||||
option_port="${1#*=}"
|
|
||||||
;;
|
|
||||||
--port=)
|
|
||||||
# port options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--user)
|
|
||||||
# user options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_user="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--user=?*)
|
|
||||||
# user options, with value separated by =
|
|
||||||
option_user="${1#*=}"
|
|
||||||
;;
|
|
||||||
--user=)
|
|
||||||
# user options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--password)
|
|
||||||
# password options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_password="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--password=?*)
|
|
||||||
# password options, with value separated by =
|
|
||||||
option_password="${1#*=}"
|
|
||||||
;;
|
|
||||||
--password=)
|
|
||||||
# password options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--repository)
|
|
||||||
# repository options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_repository="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--repository=?*)
|
|
||||||
# repository options, with value separated by =
|
|
||||||
option_repository="${1#*=}"
|
|
||||||
;;
|
|
||||||
--repository=)
|
|
||||||
# repository options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--repository' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--snapshot)
|
|
||||||
# snapshot options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_snapshot="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--snapshot=?*)
|
|
||||||
# snapshot options, with value separated by =
|
|
||||||
option_snapshot="${1#*=}"
|
|
||||||
;;
|
|
||||||
--snapshot=)
|
|
||||||
# snapshot options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--snapshot' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
option_others=${*}
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
# Use the default Elasticsearch CA certificate when using HTTPS, if not specified directly
|
|
||||||
local default_cacert="/etc/elasticsearch/certs/http_ca.crt"
|
|
||||||
if [ "${option_protocol}" = "https" ] && [ -z "${option_cacert}" ] && [ -f "${default_cacert}" ]; then
|
|
||||||
option_cacert="${default_cacert}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
local errors_dir="${ERRORS_DIR}/elasticsearch-${option_repository}-${option_snapshot}"
|
|
||||||
rm -rf "${errors_dir}"
|
|
||||||
mkdir -p "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${errors_dir}"
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${option_snapshot}"
|
|
||||||
|
|
||||||
## Take a snapshot as a backup.
|
|
||||||
## Warning: You need to have a path.repo configured.
|
|
||||||
## See: https://wiki.evolix.org/HowtoElasticsearch#snapshots-et-sauvegardes
|
|
||||||
|
|
||||||
local base_url="${option_protocol}://${option_host}:${option_port}"
|
|
||||||
local repository_url="${base_url}/_snapshot/${option_repository}"
|
|
||||||
local snapshot_url="${repository_url}/${option_snapshot}"
|
|
||||||
|
|
||||||
# Verify snapshot repository
|
|
||||||
|
|
||||||
local error_file="${errors_dir}/verify.err"
|
|
||||||
|
|
||||||
declare -a connect_options
|
|
||||||
connect_options=()
|
|
||||||
if [ -n "${option_cacert}" ]; then
|
|
||||||
connect_options+=(--cacert "${option_cacert}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_user}" ] || [ -n "${option_password}" ]; then
|
|
||||||
local connect_options+=("--user ${option_user}:${option_password}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_others}" ]; then
|
|
||||||
# word splitting is deliberate here
|
|
||||||
# shellcheck disable=SC2206
|
|
||||||
connect_options+=(${option_others})
|
|
||||||
fi
|
|
||||||
# Add the http return code at the end of the output
|
|
||||||
connect_options+=(--write-out '%{http_code}\n')
|
|
||||||
connect_options+=(--silent)
|
|
||||||
|
|
||||||
declare -a dump_options
|
|
||||||
dump_options=()
|
|
||||||
dump_options+=(--request POST)
|
|
||||||
|
|
||||||
dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${repository_url}/_verify?pretty"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd} > "${error_file}"
|
|
||||||
|
|
||||||
# test if the last line of the log file is "200"
|
|
||||||
tail -n 1 "${error_file}" | grep --quiet "^200$" "${error_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: repository verification returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
|
|
||||||
# Delete snapshot
|
|
||||||
|
|
||||||
declare -a dump_options
|
|
||||||
dump_options=()
|
|
||||||
dump_options+=(--request DELETE)
|
|
||||||
|
|
||||||
dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${snapshot_url}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd} > /dev/null
|
|
||||||
|
|
||||||
# Create snapshot
|
|
||||||
|
|
||||||
local error_file="${errors_dir}/create.err"
|
|
||||||
|
|
||||||
declare -a dump_options
|
|
||||||
dump_options=()
|
|
||||||
dump_options+=(--request PUT)
|
|
||||||
|
|
||||||
dump_cmd="curl ${connect_options[*]} ${dump_options[*]} ${snapshot_url}?wait_for_completion=true"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd} > "${error_file}"
|
|
||||||
|
|
||||||
# test if the last line of the log file is "200"
|
|
||||||
tail -n 1 "${error_file}" | grep --quiet "^200$" "${error_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: curl returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${option_snapshot}"
|
|
||||||
}
|
|
|
@ -1,559 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# shellcheck disable=SC2034,SC2317,SC2155
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Dump LDAP files (config, data, all)
|
|
||||||
#
|
|
||||||
# Arguments: <none>
|
|
||||||
#######################################################################
|
|
||||||
dump_ldap() {
|
|
||||||
## OpenLDAP : example with slapcat
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/ldap"
|
|
||||||
rm -rf "${dump_dir}"
|
|
||||||
mkdir -p "${dump_dir}"
|
|
||||||
chmod 700 "${dump_dir}"
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${FUNCNAME[0]} to ${dump_dir}"
|
|
||||||
|
|
||||||
dump_cmd="slapcat -n 0 -l ${dump_dir}/config.bak"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd}
|
|
||||||
|
|
||||||
dump_cmd="slapcat -n 1 -l ${dump_dir}/data.bak"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd}
|
|
||||||
|
|
||||||
dump_cmd="slapcat -l ${dump_dir}/all.bak"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd}
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${FUNCNAME[0]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Copy dump file of Redis instances
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# --instances=[Integer] (default: all)
|
|
||||||
#######################################################################
|
|
||||||
dump_redis() {
|
|
||||||
all_instances=$(find /var/lib/ -mindepth 1 -maxdepth 1 '(' -type d -o -type l ')' -name 'redis*')
|
|
||||||
|
|
||||||
local option_instances=""
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
--instances)
|
|
||||||
# instances options, with key and value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
if [ "${2}" == "all" ]; then
|
|
||||||
read -a option_instances <<< "${all_instances}"
|
|
||||||
else
|
|
||||||
IFS="," read -a option_instances <<< "${2}"
|
|
||||||
fi
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--instances' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--instances=?*)
|
|
||||||
# instances options, with key and value separated by =
|
|
||||||
if [ "${1#*=}" == "all" ]; then
|
|
||||||
read -a option_instances <<< "${all_instances}"
|
|
||||||
else
|
|
||||||
IFS="," read -a option_instances <<< "${1#*=}"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--instances=)
|
|
||||||
# instances options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--instances' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
for instance in "${option_instances[@]}"; do
|
|
||||||
name=$(basename "${instance}")
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/${name}"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
if [ -f "${instance}/dump.rdb" ]; then
|
|
||||||
local error_file="${errors_dir}/${name}.err"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
|
|
||||||
|
|
||||||
# Copy the Redis database
|
|
||||||
dump_cmd="cp -a ${instance}/dump.rdb ${dump_dir}/dump.rdb"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd} 2> "${error_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: cp ${instance}/dump.rdb to ${dump_dir} returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Compress the Redis database
|
|
||||||
dump_cmd="gzip ${dump_dir}/dump.rdb"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd}
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: gzip ${dump_dir}/dump.rdb returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '${instance}/dump.rdb' not found."
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Dump all collections of a MongoDB database
|
|
||||||
# using a custom authentication, instead of /etc/mysql/debian.cnf
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# --port=[String] (default: <blank>)
|
|
||||||
# --user=[String] (default: <blank>)
|
|
||||||
# --password=[String] (default: <blank>)
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
# Other options after -- are passed as-is to mongodump
|
|
||||||
#
|
|
||||||
# don't forget to create use with read-only access
|
|
||||||
# > use admin
|
|
||||||
# > db.createUser( { user: "mongobackup", pwd: "PASS", roles: [ "backup", ] } )
|
|
||||||
#######################################################################
|
|
||||||
dump_mongodb() {
|
|
||||||
local option_port=""
|
|
||||||
local option_user=""
|
|
||||||
local option_password=""
|
|
||||||
local option_dump_label=""
|
|
||||||
local option_others=""
|
|
||||||
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
--port)
|
|
||||||
# port options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_port="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--port=?*)
|
|
||||||
# port options, with value separated by =
|
|
||||||
option_port="${1#*=}"
|
|
||||||
;;
|
|
||||||
--port=)
|
|
||||||
# port options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--user)
|
|
||||||
# user options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_user="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--user=?*)
|
|
||||||
# user options, with value separated by =
|
|
||||||
option_user="${1#*=}"
|
|
||||||
;;
|
|
||||||
--user=)
|
|
||||||
# user options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--password)
|
|
||||||
# password options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_password="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--password=?*)
|
|
||||||
# password options, with value separated by =
|
|
||||||
option_password="${1#*=}"
|
|
||||||
;;
|
|
||||||
--password=)
|
|
||||||
# password options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--dump-label)
|
|
||||||
# dump-label options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_dump_label="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--dump-label=?*)
|
|
||||||
# dump-label options, with value separated by =
|
|
||||||
option_dump_label="${1#*=}"
|
|
||||||
;;
|
|
||||||
--dump-label=)
|
|
||||||
# dump-label options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
option_others=${*}
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "${option_dump_label}" ]; then
|
|
||||||
if [ -n "${option_port}" ]; then
|
|
||||||
option_dump_label="${option_port}"
|
|
||||||
else
|
|
||||||
option_dump_label="default"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/mongodb-${option_dump_label}"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
local error_file="${errors_dir}.err"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
|
|
||||||
|
|
||||||
declare -a dump_options
|
|
||||||
dump_options=()
|
|
||||||
if [ -n "${option_port}" ]; then
|
|
||||||
dump_options+=(--port="${option_port}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_user}" ]; then
|
|
||||||
dump_options+=(--username="${option_user}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_password}" ]; then
|
|
||||||
dump_options+=(--password="${option_password}")
|
|
||||||
fi
|
|
||||||
dump_options+=(--out="${dump_dir}/")
|
|
||||||
if [ -n "${option_others}" ]; then
|
|
||||||
# word splitting is deliberate here
|
|
||||||
# shellcheck disable=SC2206
|
|
||||||
dump_options+=(${option_others})
|
|
||||||
fi
|
|
||||||
|
|
||||||
dump_cmd="mongodump ${dump_options[*]}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd} > /dev/null"
|
|
||||||
${dump_cmd} 2> "${error_file}" > /dev/null
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mongodump to ${dump_dir} returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - stop ${FUNCNAME[0]}: ${dump_dir}"
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Dump RAID configuration
|
|
||||||
#
|
|
||||||
# Arguments: <none>
|
|
||||||
#######################################################################
|
|
||||||
dump_raid_config() {
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/raid"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
if command -v megacli > /dev/null; then
|
|
||||||
local error_file="${errors_dir}/megacli.cfg"
|
|
||||||
local dump_file="${dump_dir}/megacli.err"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
dump_cmd="megacli -CfgSave -f ${dump_file} -a0"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd} 2> "${error_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: megacli to ${dump_file} returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
elif command -v perccli > /dev/null; then
|
|
||||||
local error_file="${errors_dir}/perccli.cfg"
|
|
||||||
local dump_file="${dump_dir}/perccli.err"
|
|
||||||
# log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
# TODO: find out what the correct command is
|
|
||||||
|
|
||||||
# dump_cmd="perccli XXXX"
|
|
||||||
# log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
# ${dump_cmd} 2> ${error_file}
|
|
||||||
|
|
||||||
# local last_rc=$?
|
|
||||||
# # shellcheck disable=SC2086
|
|
||||||
# if [ ${last_rc} -ne 0 ]; then
|
|
||||||
# log_error "LOCAL_TASKS - ${FUNCNAME[0]}: perccli to ${dump_file} returned an error ${last_rc}" "${error_file}"
|
|
||||||
# GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
# else
|
|
||||||
# rm -f "${error_file}"
|
|
||||||
# fi
|
|
||||||
# log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
else
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: 'megacli' and 'perccli' not found, unable to dump RAID configuration"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Save some traceroute/mtr results
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# --targets=[IP,HOST] (default: <none>)
|
|
||||||
#######################################################################
|
|
||||||
dump_traceroute() {
|
|
||||||
local option_targets=""
|
|
||||||
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
--targets)
|
|
||||||
# targets options, with key and value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
IFS="," read -a option_targets <<< "${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--targets' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--targets=?*)
|
|
||||||
# targets options, with key and value separated by =
|
|
||||||
IFS="," read -a option_targets <<< "${1#*=}"
|
|
||||||
;;
|
|
||||||
--targets=)
|
|
||||||
# targets options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--targets' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/traceroute"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
|
|
||||||
mtr_bin=$(command -v mtr)
|
|
||||||
if [ -n "${mtr_bin}" ]; then
|
|
||||||
for target in "${option_targets[@]}"; do
|
|
||||||
local dump_file="${dump_dir}/mtr-${target}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
${mtr_bin} -r "${target}" > "${dump_file}"
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
traceroute_bin=$(command -v traceroute)
|
|
||||||
if [ -n "${traceroute_bin}" ]; then
|
|
||||||
for target in "${option_targets[@]}"; do
|
|
||||||
local dump_file="${dump_dir}/traceroute-${target}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
${traceroute_bin} -n "${target}" > "${dump_file}" 2>&1
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Save many system information, using dump_server_state
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# any option for dump-server-state (except --dump-dir) is usable
|
|
||||||
# (default: --all)
|
|
||||||
#######################################################################
|
|
||||||
dump_server_state() {
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/server-state"
|
|
||||||
rm -rf "${dump_dir}"
|
|
||||||
# Do not create the directory
|
|
||||||
# mkdir -p -m 700 "${dump_dir}"
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
|
|
||||||
|
|
||||||
# pass all options
|
|
||||||
read -a options <<< "${@}"
|
|
||||||
# if no option is given, use "--all" as fallback
|
|
||||||
if [ ${#options[@]} -le 0 ]; then
|
|
||||||
options=(--all)
|
|
||||||
fi
|
|
||||||
# add "--dump-dir" in case it is missing (as it should)
|
|
||||||
options+=(--dump-dir "${dump_dir}")
|
|
||||||
|
|
||||||
dump_server_state_bin=$(command -v dump-server-state)
|
|
||||||
if [ -z "${dump_server_state_bin}" ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: dump-server-state is missing"
|
|
||||||
rc=1
|
|
||||||
else
|
|
||||||
dump_cmd="${dump_server_state_bin} ${options[*]}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd}
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: dump-server-state returned an error ${last_rc}, check ${dump_dir}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Save RabbitMQ data
|
|
||||||
#
|
|
||||||
# Arguments: <none>
|
|
||||||
#
|
|
||||||
# Warning: This has been poorly tested
|
|
||||||
#######################################################################
|
|
||||||
dump_rabbitmq() {
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/rabbitmq"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
local error_file="${errors_dir}.err"
|
|
||||||
local dump_file="${dump_dir}/config"
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
dump_cmd="rabbitmqadmin export ${dump_file}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd} 2> "${error_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Save Files ACL on various partitions.
|
|
||||||
#
|
|
||||||
# Arguments: <none>
|
|
||||||
#######################################################################
|
|
||||||
dump_facl() {
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/facl"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
|
|
||||||
|
|
||||||
dump_cmd="getfacl -R /etc > ${dump_dir}/etc.txt"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd}
|
|
||||||
|
|
||||||
dump_cmd="getfacl -R /home > ${dump_dir}/home.txt"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd}
|
|
||||||
|
|
||||||
dump_cmd="getfacl -R /usr > ${dump_dir}/usr.txt"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd}
|
|
||||||
|
|
||||||
dump_cmd="getfacl -R /var > ${dump_dir}/var.txt"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd}
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
|
|
||||||
}
|
|
|
@ -1,1551 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# shellcheck disable=SC2034,SC2317,SC2155
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Dump complete summary of an instance (using pt-mysql-summary)
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# --port=[Integer] (default: <blank>)
|
|
||||||
# --socket=[String] (default: <blank>)
|
|
||||||
# --user=[String] (default: <blank>)
|
|
||||||
# --password=[String] (default: <blank>)
|
|
||||||
# --defaults-file=[String] (default: <blank>)
|
|
||||||
# --defaults-extra-file=[String] (default: <blank>)
|
|
||||||
# --defaults-group-suffix=[String] (default: <blank>)
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
#######################################################################
|
|
||||||
dump_mysql_summary() {
|
|
||||||
local option_port=""
|
|
||||||
local option_socket=""
|
|
||||||
local option_defaults_file=""
|
|
||||||
local option_defaults_extra_file=""
|
|
||||||
local option_defaults_group_suffix=""
|
|
||||||
local option_user=""
|
|
||||||
local option_password=""
|
|
||||||
local option_dump_label=""
|
|
||||||
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
--defaults-file)
|
|
||||||
# defaults-file options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_file="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-file=?*)
|
|
||||||
# defaults-file options, with value separated by =
|
|
||||||
option_defaults_file="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-file=)
|
|
||||||
# defaults-file options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--defaults-extra-file)
|
|
||||||
# defaults-file options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_extra_file="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-extra-file=?*)
|
|
||||||
# defaults-extra-file options, with value separated by =
|
|
||||||
option_defaults_extra_file="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-extra-file=)
|
|
||||||
# defaults-extra-file options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix)
|
|
||||||
# defaults-group-suffix options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_group_suffix="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix=?*)
|
|
||||||
# defaults-group-suffix options, with value separated by =
|
|
||||||
option_defaults_group_suffix="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix=)
|
|
||||||
# defaults-group-suffix options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--port)
|
|
||||||
# port options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_port="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--port=?*)
|
|
||||||
# port options, with value separated by =
|
|
||||||
option_port="${1#*=}"
|
|
||||||
;;
|
|
||||||
--port=)
|
|
||||||
# port options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--socket)
|
|
||||||
# socket options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_socket="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--socket=?*)
|
|
||||||
# socket options, with value separated by =
|
|
||||||
option_socket="${1#*=}"
|
|
||||||
;;
|
|
||||||
--socket=)
|
|
||||||
# socket options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--user)
|
|
||||||
# user options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_user="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--user=?*)
|
|
||||||
# user options, with value separated by =
|
|
||||||
option_user="${1#*=}"
|
|
||||||
;;
|
|
||||||
--user=)
|
|
||||||
# user options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--password)
|
|
||||||
# password options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_password="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--password=?*)
|
|
||||||
# password options, with value separated by =
|
|
||||||
option_password="${1#*=}"
|
|
||||||
;;
|
|
||||||
--password=)
|
|
||||||
# password options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--dump-label)
|
|
||||||
# dump-label options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_dump_label="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--dump-label=?*)
|
|
||||||
# dump-label options, with value separated by =
|
|
||||||
option_dump_label="${1#*=}"
|
|
||||||
;;
|
|
||||||
--dump-label=)
|
|
||||||
# dump-label options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unkwnown option (ignored): '${1}'"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "${option_dump_label}" ]; then
|
|
||||||
if [ -n "${option_defaults_group_suffix}" ]; then
|
|
||||||
option_dump_label="${option_defaults_group_suffix}"
|
|
||||||
elif [ -n "${option_port}" ]; then
|
|
||||||
option_dump_label="${option_port}"
|
|
||||||
elif [ -n "${option_socket}" ]; then
|
|
||||||
option_dump_label=$(path_to_str "${option_socket}")
|
|
||||||
else
|
|
||||||
option_dump_label="default"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-summary"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
## Dump all grants (requires 'percona-toolkit' package)
|
|
||||||
if command -v pt-mysql-summary > /dev/null; then
|
|
||||||
local error_file="${errors_dir}/mysql-summary.err"
|
|
||||||
local dump_file="${dump_dir}/mysql-summary.out"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
## Connection options
|
|
||||||
declare -a connect_options
|
|
||||||
connect_options=()
|
|
||||||
if [ -n "${option_defaults_file}" ]; then
|
|
||||||
connect_options+=(--defaults-file="${option_defaults_file}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_defaults_extra_file}" ]; then
|
|
||||||
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_defaults_group_suffix}" ]; then
|
|
||||||
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_port}" ]; then
|
|
||||||
connect_options+=(--protocol=tcp)
|
|
||||||
connect_options+=(--port="${option_port}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_socket}" ]; then
|
|
||||||
connect_options+=(--protocol=socket)
|
|
||||||
connect_options+=(--socket="${option_socket}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_user}" ]; then
|
|
||||||
connect_options+=(--user="${option_user}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_password}" ]; then
|
|
||||||
connect_options+=(--password="${option_password}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
declare -a options
|
|
||||||
options=()
|
|
||||||
options+=(--sleep=0)
|
|
||||||
|
|
||||||
dump_cmd="pt-mysql-summary ${options[*]} -- ${connect_options[*]}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd} 2> "${error_file}" > "${dump_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pt-mysql-summary to ${dump_file} returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
else
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: 'pt-mysql-summary' not found, unable to dump summary"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Dump grants of an instance
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# --port=[Integer] (default: <blank>)
|
|
||||||
# --socket=[String] (default: <blank>)
|
|
||||||
# --user=[String] (default: <blank>)
|
|
||||||
# --password=[String] (default: <blank>)
|
|
||||||
# --defaults-file=[String] (default: <blank>)
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
#######################################################################
|
|
||||||
dump_mysql_grants() {
|
|
||||||
local option_port=""
|
|
||||||
local option_socket=""
|
|
||||||
local option_defaults_file=""
|
|
||||||
local option_user=""
|
|
||||||
local option_password=""
|
|
||||||
local option_dump_label=""
|
|
||||||
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
--defaults-file)
|
|
||||||
# defaults-file options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_file="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-file=?*)
|
|
||||||
# defaults-file options, with value separated by =
|
|
||||||
option_defaults_file="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-file=)
|
|
||||||
# defaults-file options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--port)
|
|
||||||
# port options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_port="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--port=?*)
|
|
||||||
# port options, with value separated by =
|
|
||||||
option_port="${1#*=}"
|
|
||||||
;;
|
|
||||||
--port=)
|
|
||||||
# port options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--socket)
|
|
||||||
# socket options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_socket="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--socket=?*)
|
|
||||||
# socket options, with value separated by =
|
|
||||||
option_socket="${1#*=}"
|
|
||||||
;;
|
|
||||||
--socket=)
|
|
||||||
# socket options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--user)
|
|
||||||
# user options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_user="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--user=?*)
|
|
||||||
# user options, with value separated by =
|
|
||||||
option_user="${1#*=}"
|
|
||||||
;;
|
|
||||||
--user=)
|
|
||||||
# user options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--password)
|
|
||||||
# password options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_password="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--password=?*)
|
|
||||||
# password options, with value separated by =
|
|
||||||
option_password="${1#*=}"
|
|
||||||
;;
|
|
||||||
--password=)
|
|
||||||
# password options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--dump-label)
|
|
||||||
# dump-label options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_dump_label="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--dump-label=?*)
|
|
||||||
# dump-label options, with value separated by =
|
|
||||||
option_dump_label="${1#*=}"
|
|
||||||
;;
|
|
||||||
--dump-label=)
|
|
||||||
# dump-label options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "${option_dump_label}" ]; then
|
|
||||||
if [ -n "${option_port}" ]; then
|
|
||||||
option_dump_label="${option_port}"
|
|
||||||
elif [ -n "${option_socket}" ]; then
|
|
||||||
option_dump_label=$(path_to_str "${option_socket}")
|
|
||||||
else
|
|
||||||
option_dump_label="default"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-grants"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
## Dump all grants (requires 'percona-toolkit' package)
|
|
||||||
if command -v pt-show-grants > /dev/null; then
|
|
||||||
local error_file="${errors_dir}/all_grants.err"
|
|
||||||
local dump_file="${dump_dir}/all_grants.sql"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
declare -a options
|
|
||||||
options=()
|
|
||||||
if [ -n "${option_defaults_file}" ]; then
|
|
||||||
options+=(--defaults-file="${option_defaults_file}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_port}" ]; then
|
|
||||||
options+=(--port="${option_port}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_socket}" ]; then
|
|
||||||
options+=(--socket="${option_socket}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_user}" ]; then
|
|
||||||
options+=(--user="${option_user}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_password}" ]; then
|
|
||||||
options+=(--password="${option_password}")
|
|
||||||
fi
|
|
||||||
options+=(--flush)
|
|
||||||
options+=(--no-header)
|
|
||||||
|
|
||||||
dump_cmd="pt-show-grants ${options[*]}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd} 2> "${error_file}" > "${dump_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pt-show-grants to ${dump_file} returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
else
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: 'pt-show-grants' not found, unable to dump grants"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Dump a single compressed file of all databases of an instance
|
|
||||||
# and a file containing only the schema.
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# --masterdata (default: <absent>)
|
|
||||||
# --port=[Integer] (default: <blank>)
|
|
||||||
# --socket=[String] (default: <blank>)
|
|
||||||
# --user=[String] (default: <blank>)
|
|
||||||
# --password=[String] (default: <blank>)
|
|
||||||
# --defaults-file=[String] (default: <blank>)
|
|
||||||
# --defaults-extra-file=[String] (default: <blank>)
|
|
||||||
# --defaults-group-suffix=[String] (default: <blank>)
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
|
|
||||||
# Other options after -- are passed as-is to mysqldump
|
|
||||||
#######################################################################
|
|
||||||
dump_mysql_global() {
|
|
||||||
local option_masterdata=""
|
|
||||||
local option_port=""
|
|
||||||
local option_socket=""
|
|
||||||
local option_defaults_file=""
|
|
||||||
local option_defaults_extra_file=""
|
|
||||||
local option_defaults_group_suffix=""
|
|
||||||
local option_user=""
|
|
||||||
local option_password=""
|
|
||||||
local option_dump_label=""
|
|
||||||
local option_compress=""
|
|
||||||
local option_others=""
|
|
||||||
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
--masterdata)
|
|
||||||
option_masterdata="--masterdata"
|
|
||||||
;;
|
|
||||||
--defaults-file)
|
|
||||||
# defaults-file options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_file="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-file=?*)
|
|
||||||
# defaults-file options, with value separated by =
|
|
||||||
option_defaults_file="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-file=)
|
|
||||||
# defaults-file options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--defaults-extra-file)
|
|
||||||
# defaults-file options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_extra_file="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-extra-file=?*)
|
|
||||||
# defaults-extra-file options, with value separated by =
|
|
||||||
option_defaults_extra_file="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-extra-file=)
|
|
||||||
# defaults-extra-file options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix)
|
|
||||||
# defaults-group-suffix options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_group_suffix="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix=?*)
|
|
||||||
# defaults-group-suffix options, with value separated by =
|
|
||||||
option_defaults_group_suffix="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix=)
|
|
||||||
# defaults-group-suffix options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--port)
|
|
||||||
# port options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_port="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--port=?*)
|
|
||||||
# port options, with value separated by =
|
|
||||||
option_port="${1#*=}"
|
|
||||||
;;
|
|
||||||
--port=)
|
|
||||||
# port options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--socket)
|
|
||||||
# socket options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_socket="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--socket=?*)
|
|
||||||
# socket options, with value separated by =
|
|
||||||
option_socket="${1#*=}"
|
|
||||||
;;
|
|
||||||
--socket=)
|
|
||||||
# socket options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--user)
|
|
||||||
# user options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_user="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--user=?*)
|
|
||||||
# user options, with value separated by =
|
|
||||||
option_user="${1#*=}"
|
|
||||||
;;
|
|
||||||
--user=)
|
|
||||||
# user options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--password)
|
|
||||||
# password options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_password="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--password=?*)
|
|
||||||
# password options, with value separated by =
|
|
||||||
option_password="${1#*=}"
|
|
||||||
;;
|
|
||||||
--password=)
|
|
||||||
# password options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--dump-label)
|
|
||||||
# dump-label options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_dump_label="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--dump-label=?*)
|
|
||||||
# dump-label options, with value separated by =
|
|
||||||
option_dump_label="${1#*=}"
|
|
||||||
;;
|
|
||||||
--dump-label=)
|
|
||||||
# dump-label options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--compress)
|
|
||||||
# compress options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_compress="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--compress=?*)
|
|
||||||
# compress options, with value separated by =
|
|
||||||
option_compress="${1#*=}"
|
|
||||||
;;
|
|
||||||
--compress=)
|
|
||||||
# compress options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
option_others=${*}
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
case "${option_compress}" in
|
|
||||||
none)
|
|
||||||
compress_cmd="cat"
|
|
||||||
dump_ext=""
|
|
||||||
;;
|
|
||||||
bzip2|bz|bz2)
|
|
||||||
compress_cmd="bzip2 --best"
|
|
||||||
dump_ext=".bz"
|
|
||||||
;;
|
|
||||||
xz)
|
|
||||||
compress_cmd="xz --best"
|
|
||||||
dump_ext=".xz"
|
|
||||||
;;
|
|
||||||
pigz)
|
|
||||||
compress_cmd="pigz --best"
|
|
||||||
dump_ext=".gz"
|
|
||||||
;;
|
|
||||||
gz|gzip|*)
|
|
||||||
compress_cmd="gzip --best"
|
|
||||||
dump_ext=".gz"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -z "${option_dump_label}" ]; then
|
|
||||||
if [ -n "${option_defaults_group_suffix}" ]; then
|
|
||||||
option_dump_label="${option_defaults_group_suffix}"
|
|
||||||
elif [ -n "${option_port}" ]; then
|
|
||||||
option_dump_label="${option_port}"
|
|
||||||
elif [ -n "${option_socket}" ]; then
|
|
||||||
option_dump_label=$(path_to_str "${option_socket}")
|
|
||||||
else
|
|
||||||
option_dump_label="default"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Connection options
|
|
||||||
declare -a connect_options
|
|
||||||
connect_options=()
|
|
||||||
if [ -n "${option_defaults_file}" ]; then
|
|
||||||
connect_options+=(--defaults-file="${option_defaults_file}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_defaults_extra_file}" ]; then
|
|
||||||
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_defaults_group_suffix}" ]; then
|
|
||||||
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_port}" ]; then
|
|
||||||
connect_options+=(--protocol=tcp)
|
|
||||||
connect_options+=(--port="${option_port}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_socket}" ]; then
|
|
||||||
connect_options+=(--protocol=socket)
|
|
||||||
connect_options+=(--socket="${option_socket}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_user}" ]; then
|
|
||||||
connect_options+=(--user="${option_user}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_password}" ]; then
|
|
||||||
connect_options+=(--password="${option_password}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Global all databases in one file
|
|
||||||
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
local error_file="${errors_dir}/mysqldump.err"
|
|
||||||
local dump_file="${dump_dir}/mysqldump.sql${dump_ext}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
declare -a dump_options
|
|
||||||
dump_options=()
|
|
||||||
dump_options+=(--opt)
|
|
||||||
dump_options+=(--force)
|
|
||||||
dump_options+=(--events)
|
|
||||||
dump_options+=(--hex-blob)
|
|
||||||
dump_options+=(--all-databases)
|
|
||||||
if [ -n "${option_masterdata}" ]; then
|
|
||||||
dump_options+=("${option_masterdata}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_others}" ]; then
|
|
||||||
# word splitting is deliberate here
|
|
||||||
# shellcheck disable=SC2206
|
|
||||||
dump_options+=(${option_others})
|
|
||||||
fi
|
|
||||||
|
|
||||||
## WARNING : logging and executing the command must be separate
|
|
||||||
## because otherwise Bash would interpret | and > as strings and not syntax.
|
|
||||||
|
|
||||||
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]} 2> ${error_file} | ${compress_cmd} > ${dump_file}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
mysqldump "${connect_options[@]}" "${dump_options[@]}" 2> "${error_file}" | ${compress_cmd} > "${dump_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
|
|
||||||
|
|
||||||
## Schema only (no data) for each databases
|
|
||||||
|
|
||||||
local error_file="${errors_dir}/mysqldump.schema.err"
|
|
||||||
local dump_file="${dump_dir}/mysqldump.schema.sql"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
declare -a dump_options
|
|
||||||
dump_options=()
|
|
||||||
dump_options+=(--force)
|
|
||||||
dump_options+=(--no-data)
|
|
||||||
dump_options+=(--all-databases)
|
|
||||||
if [ -n "${option_others}" ]; then
|
|
||||||
# word splitting is deliberate here
|
|
||||||
# shellcheck disable=SC2206
|
|
||||||
dump_options+=(${option_others})
|
|
||||||
fi
|
|
||||||
|
|
||||||
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd} 2> "${error_file}" > "${dump_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Dump a file of each databases of an instance
|
|
||||||
# and a file containing only the schema.
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# --port=[Integer] (default: <blank>)
|
|
||||||
# --socket=[String] (default: <blank>)
|
|
||||||
# --user=[String] (default: <blank>)
|
|
||||||
# --password=[String] (default: <blank>)
|
|
||||||
# --defaults-file=[String] (default: <blank>)
|
|
||||||
# --defaults-extra-file=[String] (default: <blank>)
|
|
||||||
# --defaults-group-suffix=[String] (default: <blank>)
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
|
|
||||||
# Other options after -- are passed as-is to mysqldump
|
|
||||||
#######################################################################
|
|
||||||
dump_mysql_per_base() {
|
|
||||||
local option_port=""
|
|
||||||
local option_socket=""
|
|
||||||
local option_defaults_file=""
|
|
||||||
local option_defaults_extra_file=""
|
|
||||||
local option_defaults_group_suffix=""
|
|
||||||
local option_user=""
|
|
||||||
local option_password=""
|
|
||||||
local option_dump_label=""
|
|
||||||
local option_compress=""
|
|
||||||
local option_others=""
|
|
||||||
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
--defaults-file)
|
|
||||||
# defaults-file options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_file="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-file=?*)
|
|
||||||
# defaults-file options, with value separated by =
|
|
||||||
option_defaults_file="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-file=)
|
|
||||||
# defaults-file options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--defaults-extra-file)
|
|
||||||
# defaults-file options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_extra_file="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-extra-file=?*)
|
|
||||||
# defaults-extra-file options, with value separated by =
|
|
||||||
option_defaults_extra_file="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-extra-file=)
|
|
||||||
# defaults-extra-file options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix)
|
|
||||||
# defaults-group-suffix options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_group_suffix="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix=?*)
|
|
||||||
# defaults-group-suffix options, with value separated by =
|
|
||||||
option_defaults_group_suffix="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix=)
|
|
||||||
# defaults-group-suffix options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--port)
|
|
||||||
# port options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_port="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--port=?*)
|
|
||||||
# port options, with value separated by =
|
|
||||||
option_port="${1#*=}"
|
|
||||||
;;
|
|
||||||
--port=)
|
|
||||||
# port options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--socket)
|
|
||||||
# socket options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_socket="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--socket=?*)
|
|
||||||
# socket options, with value separated by =
|
|
||||||
option_socket="${1#*=}"
|
|
||||||
;;
|
|
||||||
--socket=)
|
|
||||||
# socket options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--user)
|
|
||||||
# user options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_user="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--user=?*)
|
|
||||||
# user options, with value separated by =
|
|
||||||
option_user="${1#*=}"
|
|
||||||
;;
|
|
||||||
--user=)
|
|
||||||
# user options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--password)
|
|
||||||
# password options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_password="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--password=?*)
|
|
||||||
# password options, with value separated by =
|
|
||||||
option_password="${1#*=}"
|
|
||||||
;;
|
|
||||||
--password=)
|
|
||||||
# password options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--dump-label)
|
|
||||||
# dump-label options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_dump_label="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--dump-label=?*)
|
|
||||||
# dump-label options, with value separated by =
|
|
||||||
option_dump_label="${1#*=}"
|
|
||||||
;;
|
|
||||||
--dump-label=)
|
|
||||||
# dump-label options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--compress)
|
|
||||||
# compress options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_compress="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--compress=?*)
|
|
||||||
# compress options, with value separated by =
|
|
||||||
option_compress="${1#*=}"
|
|
||||||
;;
|
|
||||||
--compress=)
|
|
||||||
# compress options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
option_others=${*}
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
case "${option_compress}" in
|
|
||||||
none)
|
|
||||||
compress_cmd="cat"
|
|
||||||
dump_ext=""
|
|
||||||
;;
|
|
||||||
bzip2|bz|bz2)
|
|
||||||
compress_cmd="bzip2 --best"
|
|
||||||
dump_ext=".bz"
|
|
||||||
;;
|
|
||||||
xz)
|
|
||||||
compress_cmd="xz --best"
|
|
||||||
dump_ext=".xz"
|
|
||||||
;;
|
|
||||||
pigz)
|
|
||||||
compress_cmd="pigz --best"
|
|
||||||
dump_ext=".gz"
|
|
||||||
;;
|
|
||||||
gz|gzip|*)
|
|
||||||
compress_cmd="gzip --best"
|
|
||||||
dump_ext=".gz"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -z "${option_dump_label}" ]; then
|
|
||||||
if [ -n "${option_defaults_group_suffix}" ]; then
|
|
||||||
option_dump_label="${option_defaults_group_suffix}"
|
|
||||||
elif [ -n "${option_port}" ]; then
|
|
||||||
option_dump_label="${option_port}"
|
|
||||||
elif [ -n "${option_socket}" ]; then
|
|
||||||
option_dump_label=$(path_to_str "${option_socket}")
|
|
||||||
else
|
|
||||||
option_dump_label="default"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Connection options
|
|
||||||
declare -a connect_options
|
|
||||||
connect_options=()
|
|
||||||
if [ -n "${option_defaults_file}" ]; then
|
|
||||||
connect_options+=(--defaults-file="${option_defaults_file}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_defaults_extra_file}" ]; then
|
|
||||||
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_defaults_group_suffix}" ]; then
|
|
||||||
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_port}" ]; then
|
|
||||||
connect_options+=(--protocol=tcp)
|
|
||||||
connect_options+=(--port="${option_port}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_socket}" ]; then
|
|
||||||
connect_options+=(--protocol=socket)
|
|
||||||
connect_options+=(--socket="${option_socket}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_user}" ]; then
|
|
||||||
connect_options+=(--user="${option_user}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_password}" ]; then
|
|
||||||
connect_options+=(--password="${option_password}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-per-base"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
databases=$(mysql "${connect_options[@]}" --execute="show databases" --silent --skip-column-names \
|
|
||||||
| grep --extended-regexp --invert-match "^(Database|information_schema|performance_schema|sys)")
|
|
||||||
|
|
||||||
for database in ${databases}; do
|
|
||||||
local error_file="${errors_dir}/${database}.err"
|
|
||||||
local dump_file="${dump_dir}/${database}.sql${dump_ext}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
declare -a dump_options
|
|
||||||
dump_options=()
|
|
||||||
dump_options+=(--opt)
|
|
||||||
dump_options+=(--force)
|
|
||||||
dump_options+=(--events)
|
|
||||||
dump_options+=(--hex-blob)
|
|
||||||
dump_options+=(--databases "${database}")
|
|
||||||
if [ -n "${option_others}" ]; then
|
|
||||||
# word splitting is deliberate here
|
|
||||||
# shellcheck disable=SC2206
|
|
||||||
dump_options+=(${option_others})
|
|
||||||
fi
|
|
||||||
|
|
||||||
## WARNING : logging and executing the command must be separate
|
|
||||||
## because otherwise Bash would interpret | and > as strings and not syntax.
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump ${connect_options[*]} ${dump_options[*]} | ${compress_cmd} > ${dump_file}"
|
|
||||||
mysqldump "${connect_options[@]}" "${dump_options[@]}" 2> "${error_file}" | ${compress_cmd} > "${dump_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
|
|
||||||
|
|
||||||
## Schema only (no data) for each databases
|
|
||||||
|
|
||||||
local error_file="${errors_dir}/${database}.schema.err"
|
|
||||||
local dump_file="${dump_dir}/${database}.schema.sql"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
declare -a dump_options
|
|
||||||
dump_options=()
|
|
||||||
dump_options+=(--force)
|
|
||||||
dump_options+=(--no-data)
|
|
||||||
dump_options+=(--databases "${database}")
|
|
||||||
if [ -n "${option_others}" ]; then
|
|
||||||
# word splitting is deliberate here
|
|
||||||
# shellcheck disable=SC2206
|
|
||||||
dump_options+=(${option_others})
|
|
||||||
fi
|
|
||||||
|
|
||||||
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd} 2> "${error_file}" > "${dump_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Dump "tabs style" separate schema/data for each database of an instance
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# --port=[Integer] (default: <blank>)
|
|
||||||
# --socket=[String] (default: <blank>)
|
|
||||||
# --user=[String] (default: <blank>)
|
|
||||||
# --password=[String] (default: <blank>)
|
|
||||||
# --defaults-file=[String] (default: <blank>)
|
|
||||||
# --defaults-extra-file=[String] (default: <blank>)
|
|
||||||
# --defaults-group-suffix=[String] (default: <blank>)
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
|
|
||||||
# Other options after -- are passed as-is to mysqldump
|
|
||||||
#######################################################################
|
|
||||||
dump_mysql_tabs() {
|
|
||||||
local option_port=""
|
|
||||||
local option_socket=""
|
|
||||||
local option_defaults_file=""
|
|
||||||
local option_defaults_extra_file=""
|
|
||||||
local option_defaults_group_suffix=""
|
|
||||||
local option_user=""
|
|
||||||
local option_password=""
|
|
||||||
local option_dump_label=""
|
|
||||||
local option_compress=""
|
|
||||||
local option_others=""
|
|
||||||
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
--defaults-file)
|
|
||||||
# defaults-file options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_file="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-file=?*)
|
|
||||||
# defaults-file options, with value separated by =
|
|
||||||
option_defaults_file="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-file=)
|
|
||||||
# defaults-file options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--defaults-extra-file)
|
|
||||||
# defaults-file options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_extra_file="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-extra-file=?*)
|
|
||||||
# defaults-extra-file options, with value separated by =
|
|
||||||
option_defaults_extra_file="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-extra-file=)
|
|
||||||
# defaults-extra-file options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-extra-file' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix)
|
|
||||||
# defaults-group-suffix options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_defaults_group_suffix="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix=?*)
|
|
||||||
# defaults-group-suffix options, with value separated by =
|
|
||||||
option_defaults_group_suffix="${1#*=}"
|
|
||||||
;;
|
|
||||||
--defaults-group-suffix=)
|
|
||||||
# defaults-group-suffix options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--defaults-group-suffix' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--port)
|
|
||||||
# port options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_port="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--port=?*)
|
|
||||||
# port options, with value separated by =
|
|
||||||
option_port="${1#*=}"
|
|
||||||
;;
|
|
||||||
--port=)
|
|
||||||
# port options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--port' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--socket)
|
|
||||||
# socket options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_socket="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--socket=?*)
|
|
||||||
# socket options, with value separated by =
|
|
||||||
option_socket="${1#*=}"
|
|
||||||
;;
|
|
||||||
--socket=)
|
|
||||||
# socket options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--socket' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--user)
|
|
||||||
# user options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_user="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--user=?*)
|
|
||||||
# user options, with value separated by =
|
|
||||||
option_user="${1#*=}"
|
|
||||||
;;
|
|
||||||
--user=)
|
|
||||||
# user options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--user' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--password)
|
|
||||||
# password options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_password="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--password=?*)
|
|
||||||
# password options, with value separated by =
|
|
||||||
option_password="${1#*=}"
|
|
||||||
;;
|
|
||||||
--password=)
|
|
||||||
# password options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--password' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--dump-label)
|
|
||||||
# dump-label options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_dump_label="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--dump-label=?*)
|
|
||||||
# dump-label options, with value separated by =
|
|
||||||
option_dump_label="${1#*=}"
|
|
||||||
;;
|
|
||||||
--dump-label=)
|
|
||||||
# dump-label options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--compress)
|
|
||||||
# compress options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_compress="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--compress=?*)
|
|
||||||
# compress options, with value separated by =
|
|
||||||
option_compress="${1#*=}"
|
|
||||||
;;
|
|
||||||
--compress=)
|
|
||||||
# compress options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
option_others=${*}
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
case "${option_compress}" in
|
|
||||||
none)
|
|
||||||
compress_cmd="cat"
|
|
||||||
dump_ext=""
|
|
||||||
;;
|
|
||||||
bzip2|bz|bz2)
|
|
||||||
compress_cmd="bzip2 --best"
|
|
||||||
dump_ext=".bz"
|
|
||||||
;;
|
|
||||||
xz)
|
|
||||||
compress_cmd="xz --best"
|
|
||||||
dump_ext=".xz"
|
|
||||||
;;
|
|
||||||
pigz)
|
|
||||||
compress_cmd="pigz --best"
|
|
||||||
dump_ext=".gz"
|
|
||||||
;;
|
|
||||||
gz|gzip|*)
|
|
||||||
compress_cmd="gzip --best"
|
|
||||||
dump_ext=".gz"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -z "${option_dump_label}" ]; then
|
|
||||||
if [ -n "${option_defaults_group_suffix}" ]; then
|
|
||||||
option_dump_label="${option_defaults_group_suffix}"
|
|
||||||
elif [ -n "${option_port}" ]; then
|
|
||||||
option_dump_label="${option_port}"
|
|
||||||
elif [ -n "${option_socket}" ]; then
|
|
||||||
option_dump_label=$(path_to_str "${option_socket}")
|
|
||||||
else
|
|
||||||
option_dump_label="default"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Connection options
|
|
||||||
declare -a connect_options
|
|
||||||
connect_options=()
|
|
||||||
if [ -n "${option_defaults_file}" ]; then
|
|
||||||
connect_options+=(--defaults-file="${option_defaults_file}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_defaults_extra_file}" ]; then
|
|
||||||
connect_options+=(--defaults-extra-file="${option_defaults_extra_file}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_defaults_group_suffix}" ]; then
|
|
||||||
connect_options+=(--defaults-group-suffix="${option_defaults_group_suffix}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_port}" ]; then
|
|
||||||
connect_options+=(--protocol=tcp)
|
|
||||||
connect_options+=(--port="${option_port}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_socket}" ]; then
|
|
||||||
connect_options+=(--protocol=socket)
|
|
||||||
connect_options+=(--socket="${option_socket}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_user}" ]; then
|
|
||||||
connect_options+=(--user="${option_user}")
|
|
||||||
fi
|
|
||||||
if [ -n "${option_password}" ]; then
|
|
||||||
connect_options+=(--password="${option_password}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
databases=$(mysql "${connect_options[@]}" --execute="show databases" --silent --skip-column-names \
|
|
||||||
| grep --extended-regexp --invert-match "^(Database|information_schema|performance_schema|sys)")
|
|
||||||
|
|
||||||
for database in ${databases}; do
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/mysql-${option_dump_label}-tabs/${database}"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
chown -RL mysql "${dump_dir}"
|
|
||||||
|
|
||||||
local error_file="${errors_dir}.err"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_dir}"
|
|
||||||
|
|
||||||
declare -a dump_options
|
|
||||||
dump_options=()
|
|
||||||
dump_options+=(--force)
|
|
||||||
dump_options+=(--quote-names)
|
|
||||||
dump_options+=(--opt)
|
|
||||||
dump_options+=(--events)
|
|
||||||
dump_options+=(--hex-blob)
|
|
||||||
dump_options+=(--skip-comments)
|
|
||||||
dump_options+=(--fields-enclosed-by='\"')
|
|
||||||
dump_options+=(--fields-terminated-by=',')
|
|
||||||
dump_options+=(--tab="${dump_dir}")
|
|
||||||
if [ -n "${option_others}" ]; then
|
|
||||||
# word splitting is deliberate here
|
|
||||||
# shellcheck disable=SC2206
|
|
||||||
dump_options+=(${option_others})
|
|
||||||
fi
|
|
||||||
dump_options+=("${database}")
|
|
||||||
|
|
||||||
dump_cmd="mysqldump ${connect_options[*]} ${dump_options[*]}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd} 2> "${error_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: mysqldump to ${dump_dir} returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_dir}"
|
|
||||||
done
|
|
||||||
}
|
|
|
@ -1,343 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# shellcheck disable=SC2034,SC2317,SC2155
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Dump a single file of all PostgreSQL databases
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
# --compress=<gzip|pigz|bzip2|xz|none> (default: "gzip")
|
|
||||||
# Other options after -- are passed as-is to pg_dump
|
|
||||||
#######################################################################
|
|
||||||
dump_postgresql_global() {
|
|
||||||
local option_dump_label=""
|
|
||||||
local option_compress=""
|
|
||||||
local option_others=""
|
|
||||||
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
--dump-label)
|
|
||||||
# dump-label options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_dump_label="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--dump-label=?*)
|
|
||||||
# dump-label options, with value separated by =
|
|
||||||
option_dump_label="${1#*=}"
|
|
||||||
;;
|
|
||||||
--dump-label=)
|
|
||||||
# dump-label options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--compress)
|
|
||||||
# compress options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_compress="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--compress=?*)
|
|
||||||
# compress options, with value separated by =
|
|
||||||
option_compress="${1#*=}"
|
|
||||||
;;
|
|
||||||
--compress=)
|
|
||||||
# compress options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
option_others=${*}
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
case "${option_compress}" in
|
|
||||||
none)
|
|
||||||
compress_cmd="cat"
|
|
||||||
dump_ext=""
|
|
||||||
;;
|
|
||||||
bzip2|bz|bz2)
|
|
||||||
compress_cmd="bzip2 --best"
|
|
||||||
dump_ext=".bz"
|
|
||||||
;;
|
|
||||||
xz)
|
|
||||||
compress_cmd="xz --best"
|
|
||||||
dump_ext=".xz"
|
|
||||||
;;
|
|
||||||
pigz)
|
|
||||||
compress_cmd="pigz --best"
|
|
||||||
dump_ext=".gz"
|
|
||||||
;;
|
|
||||||
gz|gzip|*)
|
|
||||||
compress_cmd="gzip --best"
|
|
||||||
dump_ext=".gz"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -z "${option_dump_label}" ]; then
|
|
||||||
if [ -n "${option_defaults_group_suffix}" ]; then
|
|
||||||
option_dump_label="${option_defaults_group_suffix}"
|
|
||||||
elif [ -n "${option_port}" ]; then
|
|
||||||
option_dump_label="${option_port}"
|
|
||||||
elif [ -n "${option_socket}" ]; then
|
|
||||||
option_dump_label=$(path_to_str "${option_socket}")
|
|
||||||
else
|
|
||||||
option_dump_label="default"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-${option_dump_label}-global"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
## example with pg_dumpall and with compression
|
|
||||||
local error_file="${errors_dir}/pg_dumpall.err"
|
|
||||||
local dump_file="${dump_dir}/pg_dumpall.sql${dump_ext}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
declare -a dump_options
|
|
||||||
dump_options=()
|
|
||||||
if [ -n "${option_others}" ]; then
|
|
||||||
# word splitting is deliberate here
|
|
||||||
# shellcheck disable=SC2206
|
|
||||||
dump_options+=(${option_others})
|
|
||||||
fi
|
|
||||||
|
|
||||||
dump_cmd="(sudo -u postgres pg_dumpall ${dump_options[*]}) 2> ${error_file} | ${compress_cmd} > ${dump_file}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd}
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dumpall to ${dump_file} returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
|
|
||||||
## example with pg_dumpall and without compression
|
|
||||||
## WARNING: you need space in ~postgres
|
|
||||||
# local error_file="${errors_dir}/pg_dumpall.err"
|
|
||||||
# local dump_file="${dump_dir}/pg_dumpall.sql"
|
|
||||||
# log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
#
|
|
||||||
# (su - postgres -c "pg_dumpall > ~/pg.dump.bak") 2> "${error_file}"
|
|
||||||
# mv ~postgres/pg.dump.bak "${dump_file}"
|
|
||||||
#
|
|
||||||
# log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Dump a compressed file per database
|
|
||||||
#
|
|
||||||
# Arguments: <none>
|
|
||||||
#######################################################################
|
|
||||||
dump_postgresql_per_base() {
|
|
||||||
local option_dump_label=""
|
|
||||||
local option_compress=""
|
|
||||||
local option_others=""
|
|
||||||
|
|
||||||
# Parse options, based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
|
||||||
while :; do
|
|
||||||
case ${1:-''} in
|
|
||||||
--dump-label)
|
|
||||||
# dump-label options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_dump_label="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--dump-label=?*)
|
|
||||||
# dump-label options, with value separated by =
|
|
||||||
option_dump_label="${1#*=}"
|
|
||||||
;;
|
|
||||||
--dump-label=)
|
|
||||||
# dump-label options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--dump-label' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--compress)
|
|
||||||
# compress options, with value separated by space
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
option_compress="${2}"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
--compress=?*)
|
|
||||||
# compress options, with value separated by =
|
|
||||||
option_compress="${1#*=}"
|
|
||||||
;;
|
|
||||||
--compress=)
|
|
||||||
# compress options, without value
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: '--compress' requires a non-empty option argument."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
# End of all options.
|
|
||||||
shift
|
|
||||||
option_others=${*}
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
-?*|[[:alnum:]]*)
|
|
||||||
# ignore unknown options
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: unknown option '${1}' (ignored)"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Default case: If no more options then break out of the loop.
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
case "${option_compress}" in
|
|
||||||
none)
|
|
||||||
compress_cmd="cat"
|
|
||||||
dump_ext=""
|
|
||||||
;;
|
|
||||||
bzip2|bz|bz2)
|
|
||||||
compress_cmd="bzip2 --best"
|
|
||||||
dump_ext=".bz"
|
|
||||||
;;
|
|
||||||
xz)
|
|
||||||
compress_cmd="xz --best"
|
|
||||||
dump_ext=".xz"
|
|
||||||
;;
|
|
||||||
pigz)
|
|
||||||
compress_cmd="pigz --best"
|
|
||||||
dump_ext=".gz"
|
|
||||||
;;
|
|
||||||
gz|gzip|*)
|
|
||||||
compress_cmd="gzip --best"
|
|
||||||
dump_ext=".gz"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -z "${option_dump_label}" ]; then
|
|
||||||
if [ -n "${option_defaults_group_suffix}" ]; then
|
|
||||||
option_dump_label="${option_defaults_group_suffix}"
|
|
||||||
elif [ -n "${option_port}" ]; then
|
|
||||||
option_dump_label="${option_port}"
|
|
||||||
elif [ -n "${option_socket}" ]; then
|
|
||||||
option_dump_label=$(path_to_str "${option_socket}")
|
|
||||||
else
|
|
||||||
option_dump_label="default"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-${option_dump_label}-per-base"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
(
|
|
||||||
# shellcheck disable=SC2164
|
|
||||||
cd /var/lib/postgresql
|
|
||||||
databases=$(sudo -u postgres psql -U postgres -lt | awk -F \| '{print $1}' | grep -v "template.*")
|
|
||||||
for database in ${databases} ; do
|
|
||||||
local error_file="${errors_dir}/${database}.err"
|
|
||||||
local dump_file="${dump_dir}/${database}.sql${dump_ext}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
declare -a dump_options
|
|
||||||
dump_options=()
|
|
||||||
dump_options+=(--create)
|
|
||||||
dump_options+=(-U postgres)
|
|
||||||
dump_options+=(-d "${database}")
|
|
||||||
if [ -n "${option_others}" ]; then
|
|
||||||
# word splitting is deliberate here
|
|
||||||
# shellcheck disable=SC2206
|
|
||||||
dump_options+=(${option_others})
|
|
||||||
fi
|
|
||||||
|
|
||||||
dump_cmd="(sudo -u postgres /usr/bin/pg_dump ${dump_options[*]}) 2> ${error_file} | ${compress_cmd} > ${dump_file}"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
${dump_cmd}
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
done
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Dump a compressed file per database
|
|
||||||
#
|
|
||||||
# Arguments: <none>
|
|
||||||
#
|
|
||||||
# TODO: add arguments to include/exclude tables
|
|
||||||
#######################################################################
|
|
||||||
dump_postgresql_filtered() {
|
|
||||||
local dump_dir="${LOCAL_BACKUP_DIR}/postgresql-filtered"
|
|
||||||
local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
mkdir -p "${dump_dir}" "${errors_dir}"
|
|
||||||
# No need to change recursively, the top directory is enough
|
|
||||||
chmod 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
|
|
||||||
local error_file="${errors_dir}/pg-backup.err"
|
|
||||||
local dump_file="${dump_dir}/pg-backup.tar"
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
|
|
||||||
## example with all tables from MYBASE excepts TABLE1 and TABLE2
|
|
||||||
# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f "${dump_file}" -t 'TABLE1' -t 'TABLE2' MYBASE 2> "${error_file}"
|
|
||||||
|
|
||||||
## example with only TABLE1 and TABLE2 from MYBASE
|
|
||||||
# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f "${dump_file}" -T 'TABLE1' -T 'TABLE2' MYBASE 2> "${error_file}"
|
|
||||||
|
|
||||||
local last_rc=$?
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
if [ ${last_rc} -ne 0 ]; then
|
|
||||||
log_error "LOCAL_TASKS - ${FUNCNAME[0]}: pg_dump to ${dump_file} returned an error ${last_rc}" "${error_file}"
|
|
||||||
GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
else
|
|
||||||
rm -f "${error_file}"
|
|
||||||
fi
|
|
||||||
log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
}
|
|
|
@ -1,466 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# shellcheck disable=SC2034,SC2317
|
|
||||||
|
|
||||||
readonly VERSION="24.05.1"
|
|
||||||
|
|
||||||
# set all programs to C language (english)
|
|
||||||
export LC_ALL=C
|
|
||||||
|
|
||||||
# If expansion is attempted on an unset variable or parameter, the shell prints an
|
|
||||||
# error message, and, if not interactive, exits with a non-zero status.
|
|
||||||
set -o nounset
|
|
||||||
# The pipeline's return status is the value of the last (rightmost) command
|
|
||||||
# to exit with a non-zero status, or zero if all commands exit successfully.
|
|
||||||
set -o pipefail
|
|
||||||
# Enable trace mode if called with environment variable TRACE=1
|
|
||||||
if [[ "${TRACE-0}" == "1" ]]; then
|
|
||||||
set -o xtrace
|
|
||||||
fi
|
|
||||||
|
|
||||||
source "${LIBDIR}/utilities.sh"
|
|
||||||
source "${LIBDIR}/dump/elasticsearch.sh"
|
|
||||||
source "${LIBDIR}/dump/mysql.sh"
|
|
||||||
source "${LIBDIR}/dump/postgresql.sh"
|
|
||||||
source "${LIBDIR}/dump/misc.sh"
|
|
||||||
|
|
||||||
# Called from main, it is wrapping the local_tasks function defined in the real script
|
|
||||||
local_tasks_wrapper() {
|
|
||||||
log "START LOCAL_TASKS"
|
|
||||||
|
|
||||||
# Remove old log directories (recursively)
|
|
||||||
find "${LOCAL_BACKUP_DIR}/" -type d -name "${PROGNAME}.errors-*" -ctime +30 -exec rm -rf \;
|
|
||||||
|
|
||||||
local_tasks_type="$(type -t local_tasks)"
|
|
||||||
if [ "${local_tasks_type}" = "function" ]; then
|
|
||||||
local_tasks
|
|
||||||
else
|
|
||||||
log_error "There is no 'local_tasks' function to execute"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# TODO: check if this is still needed
|
|
||||||
# print_error_files_content
|
|
||||||
|
|
||||||
log "STOP LOCAL_TASKS"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Called from main, it is wrapping the sync_tasks function defined in the real script
|
|
||||||
sync_tasks_wrapper() {
|
|
||||||
declare -a SERVERS # Indexed array for server/port values
|
|
||||||
declare -a RSYNC_INCLUDES # Indexed array for includes
|
|
||||||
declare -a RSYNC_EXCLUDES # Indexed array for excludes
|
|
||||||
|
|
||||||
case "${SYSTEM}" in
|
|
||||||
linux)
|
|
||||||
# NOTE: remember to single-quote paths if they contain globs (*)
|
|
||||||
# and you want to defer expansion
|
|
||||||
declare -a rsync_default_includes=(
|
|
||||||
/bin
|
|
||||||
/boot
|
|
||||||
/lib
|
|
||||||
/opt
|
|
||||||
/sbin
|
|
||||||
/usr
|
|
||||||
)
|
|
||||||
;;
|
|
||||||
*bsd)
|
|
||||||
# NOTE: remember to single-quote paths if they contain globs (*)
|
|
||||||
# and you want to defer expansion
|
|
||||||
declare -a rsync_default_includes=(
|
|
||||||
/bin
|
|
||||||
/bsd
|
|
||||||
/sbin
|
|
||||||
/usr
|
|
||||||
)
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unknown system '${SYSTEM}'" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
if [ -f "${CANARY_FILE}" ]; then
|
|
||||||
rsync_default_includes+=("${CANARY_FILE}")
|
|
||||||
fi
|
|
||||||
readonly rsync_default_includes
|
|
||||||
|
|
||||||
# NOTE: remember to single-quote paths if they contain globs (*)
|
|
||||||
# and you want to defer expansion
|
|
||||||
declare -a rsync_default_excludes=(
|
|
||||||
/dev
|
|
||||||
/proc
|
|
||||||
/run
|
|
||||||
/sys
|
|
||||||
/tmp
|
|
||||||
/usr/doc
|
|
||||||
/usr/obj
|
|
||||||
/usr/share/doc
|
|
||||||
/usr/src
|
|
||||||
/var/apt
|
|
||||||
/var/cache
|
|
||||||
'/var/db/munin/*.tmp'
|
|
||||||
/var/lib/amavis/amavisd.sock
|
|
||||||
/var/lib/amavis/tmp
|
|
||||||
/var/lib/amavis/virusmails
|
|
||||||
'/var/lib/clamav/*.tmp'
|
|
||||||
/var/lib/elasticsearch
|
|
||||||
/var/lib/metche
|
|
||||||
/var/lib/mongodb
|
|
||||||
'/var/lib/munin/*tmp*'
|
|
||||||
/var/lib/mysql
|
|
||||||
/var/lib/php/sessions
|
|
||||||
/var/lib/php5
|
|
||||||
/var/lib/postgres
|
|
||||||
/var/lib/postgresql
|
|
||||||
/var/lib/sympa
|
|
||||||
/var/lock
|
|
||||||
/var/run
|
|
||||||
/var/spool/postfix
|
|
||||||
/var/spool/smtpd
|
|
||||||
/var/spool/squid
|
|
||||||
/var/state
|
|
||||||
/var/tmp
|
|
||||||
lost+found
|
|
||||||
'.nfs.*'
|
|
||||||
'lxc/*/rootfs/tmp'
|
|
||||||
'lxc/*/rootfs/usr/doc'
|
|
||||||
'lxc/*/rootfs/usr/obj'
|
|
||||||
'lxc/*/rootfs/usr/share/doc'
|
|
||||||
'lxc/*/rootfs/usr/src'
|
|
||||||
'lxc/*/rootfs/var/apt'
|
|
||||||
'lxc/*/rootfs/var/cache'
|
|
||||||
'lxc/*/rootfs/var/lib/php5'
|
|
||||||
'lxc/*/rootfs/var/lib/php/sessions'
|
|
||||||
'lxc/*/rootfs/var/lock'
|
|
||||||
'lxc/*/rootfs/var/run'
|
|
||||||
'lxc/*/rootfs/var/state'
|
|
||||||
'lxc/*/rootfs/var/tmp'
|
|
||||||
/home/mysqltmp
|
|
||||||
)
|
|
||||||
readonly rsync_default_excludes
|
|
||||||
|
|
||||||
sync_tasks_type="$(type -t sync_tasks)"
|
|
||||||
if [ "${sync_tasks_type}" = "function" ]; then
|
|
||||||
sync_tasks
|
|
||||||
else
|
|
||||||
log_error "There is no 'sync_tasks' function to execute"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
sync() {
|
|
||||||
local sync_name=${1}
|
|
||||||
local -a rsync_servers=("${!2}")
|
|
||||||
local -a rsync_includes=("${!3}")
|
|
||||||
local -a rsync_excludes=("${!4}")
|
|
||||||
|
|
||||||
## Initialize variable to store SSH connection errors
|
|
||||||
declare -a SSH_ERRORS=()
|
|
||||||
|
|
||||||
log "START SYNC_TASKS - sync=${sync_name}"
|
|
||||||
|
|
||||||
# echo "### sync ###"
|
|
||||||
|
|
||||||
# for server in "${rsync_servers[@]}"; do
|
|
||||||
# echo "server: ${server}"
|
|
||||||
# done
|
|
||||||
|
|
||||||
# for include in "${rsync_includes[@]}"; do
|
|
||||||
# echo "include: ${include}"
|
|
||||||
# done
|
|
||||||
|
|
||||||
# for exclude in "${rsync_excludes[@]}"; do
|
|
||||||
# echo "exclude: ${exclude}"
|
|
||||||
# done
|
|
||||||
|
|
||||||
local -i n=0
|
|
||||||
local server=""
|
|
||||||
if [ "${SERVERS_FALLBACK}" = "1" ]; then
|
|
||||||
# We try to find a suitable server
|
|
||||||
while :; do
|
|
||||||
server=$(pick_server ${n} "${sync_name}")
|
|
||||||
rc=$?
|
|
||||||
if [ ${rc} != 0 ]; then
|
|
||||||
GLOBAL_RC=${E_NOSRVAVAIL}
|
|
||||||
log "STOP SYNC_TASKS - sync=${sync_name}'"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test_server "${server}"; then
|
|
||||||
break
|
|
||||||
else
|
|
||||||
server=""
|
|
||||||
n=$(( n + 1 ))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
# we force the server
|
|
||||||
server=$(pick_server "${n}" "${sync_name}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
rsync_server=$(echo "${server}" | cut -d':' -f1)
|
|
||||||
rsync_port=$(echo "${server}" | cut -d':' -f2)
|
|
||||||
|
|
||||||
log "SYNC_TASKS - sync=${sync_name}: use ${server}"
|
|
||||||
|
|
||||||
# Rsync complete log file for the current run
|
|
||||||
RSYNC_LOGFILE="/var/log/${PROGNAME}.${sync_name}.rsync.log"
|
|
||||||
# Rsync stats for the current run
|
|
||||||
RSYNC_STATSFILE="/var/log/${PROGNAME}.${sync_name}.rsync-stats.log"
|
|
||||||
|
|
||||||
# reset Rsync log file
|
|
||||||
if [ -n "$(command -v truncate)" ]; then
|
|
||||||
truncate -s 0 "${RSYNC_LOGFILE}"
|
|
||||||
truncate -s 0 "${RSYNC_STATSFILE}"
|
|
||||||
else
|
|
||||||
printf "" > "${RSYNC_LOGFILE}"
|
|
||||||
printf "" > "${RSYNC_STATSFILE}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Initialize variable here, we need it later
|
|
||||||
local -a mtree_files=()
|
|
||||||
|
|
||||||
if [ "${MTREE_ENABLED}" = "1" ]; then
|
|
||||||
mtree_bin=$(command -v mtree)
|
|
||||||
|
|
||||||
if [ -n "${mtree_bin}" ]; then
|
|
||||||
# Dump filesystem stats with mtree
|
|
||||||
log "SYNC_TASKS - sync=${sync_name}: start mtree"
|
|
||||||
|
|
||||||
# Loop over Rsync includes
|
|
||||||
for i in "${!rsync_includes[@]}"; do
|
|
||||||
include="${rsync_includes[i]}"
|
|
||||||
|
|
||||||
if [ -d "${include}" ]; then
|
|
||||||
# … but exclude for mtree what will be excluded by Rsync
|
|
||||||
mtree_excludes_file="$(mktemp --tmpdir "${PROGNAME}.${sync_name}.mtree-excludes.XXXXXX")"
|
|
||||||
add_to_temp_files "${mtree_excludes_file}"
|
|
||||||
|
|
||||||
for j in "${!rsync_excludes[@]}"; do
|
|
||||||
echo "${rsync_excludes[j]}" | grep -E "^([^/]|${include})" | sed -e "s|^${include}|.|" >> "${mtree_excludes_file}"
|
|
||||||
done
|
|
||||||
|
|
||||||
mtree_file="/var/log/evobackup.$(basename "${include}").mtree"
|
|
||||||
add_to_temp_files "${mtree_file}"
|
|
||||||
|
|
||||||
${mtree_bin} -x -c -p "${include}" -X "${mtree_excludes_file}" > "${mtree_file}"
|
|
||||||
mtree_files+=("${mtree_file}")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "${#mtree_files[@]}" -le 0 ]; then
|
|
||||||
log_error "SYNC_TASKS - ${sync_name}: ERROR: mtree didn't produce any file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "SYNC_TASKS - sync=${sync_name}: stop mtree (files: ${mtree_files[*]})"
|
|
||||||
else
|
|
||||||
log "SYNC_TASKS - sync=${sync_name}: skip mtree (missing)"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log "SYNC_TASKS - sync=${sync_name}: skip mtree (disabled)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
rsync_bin=$(command -v rsync)
|
|
||||||
# Build the final Rsync command
|
|
||||||
|
|
||||||
# Rsync main options
|
|
||||||
rsync_main_args=()
|
|
||||||
rsync_main_args+=(--archive)
|
|
||||||
rsync_main_args+=(--itemize-changes)
|
|
||||||
rsync_main_args+=(--quiet)
|
|
||||||
rsync_main_args+=(--stats)
|
|
||||||
rsync_main_args+=(--human-readable)
|
|
||||||
rsync_main_args+=(--relative)
|
|
||||||
rsync_main_args+=(--partial)
|
|
||||||
rsync_main_args+=(--delete)
|
|
||||||
rsync_main_args+=(--delete-excluded)
|
|
||||||
rsync_main_args+=(--force)
|
|
||||||
rsync_main_args+=(--ignore-errors)
|
|
||||||
rsync_main_args+=(--log-file "${RSYNC_LOGFILE}")
|
|
||||||
rsync_main_args+=(--rsh "ssh -p ${rsync_port} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'")
|
|
||||||
|
|
||||||
# Rsync excludes
|
|
||||||
for i in "${!rsync_excludes[@]}"; do
|
|
||||||
rsync_main_args+=(--exclude "${rsync_excludes[i]}")
|
|
||||||
done
|
|
||||||
|
|
||||||
# Rsync local sources
|
|
||||||
rsync_main_args+=("${rsync_includes[@]}")
|
|
||||||
|
|
||||||
# Rsync remote destination
|
|
||||||
rsync_main_args+=("root@${rsync_server}:${REMOTE_BACKUP_DIR}/")
|
|
||||||
|
|
||||||
# … log it
|
|
||||||
log "SYNC_TASKS - sync=${sync_name}: Rsync main command : ${rsync_bin} ${rsync_main_args[*]}"
|
|
||||||
|
|
||||||
# … execute it
|
|
||||||
${rsync_bin} "${rsync_main_args[@]}"
|
|
||||||
|
|
||||||
rsync_main_rc=$?
|
|
||||||
|
|
||||||
# Copy last lines of rsync log to the main log
|
|
||||||
tail -n 30 "${RSYNC_LOGFILE}" >> "${LOGFILE}"
|
|
||||||
# Copy Rsync stats to special file
|
|
||||||
tail -n 30 "${RSYNC_LOGFILE}" | grep --invert-match --extended-regexp " [\<\>ch\.\*]\S{10} " > "${RSYNC_STATSFILE}"
|
|
||||||
|
|
||||||
# We ignore rc=24 (vanished files)
|
|
||||||
if [ ${rsync_main_rc} -ne 0 ] && [ ${rsync_main_rc} -ne 24 ]; then
|
|
||||||
log_error "SYNC_TASKS - sync=${sync_name}: Rsync main command returned an error ${rsync_main_rc}" "${LOGFILE}"
|
|
||||||
GLOBAL_RC=${E_SYNCFAILED}
|
|
||||||
else
|
|
||||||
# Build the report Rsync command
|
|
||||||
local -a rsync_report_args
|
|
||||||
|
|
||||||
rsync_report_args=()
|
|
||||||
|
|
||||||
# Rsync options
|
|
||||||
rsync_report_args+=(--rsh "ssh -p ${rsync_port} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'")
|
|
||||||
|
|
||||||
# Rsync local sources
|
|
||||||
if [ "${#mtree_files[@]}" -gt 0 ]; then
|
|
||||||
# send mtree files if there is any
|
|
||||||
rsync_report_args+=("${mtree_files[@]}")
|
|
||||||
fi
|
|
||||||
if [ -f "${RSYNC_LOGFILE}" ]; then
|
|
||||||
# send rsync full log file if it exists
|
|
||||||
rsync_report_args+=("${RSYNC_LOGFILE}")
|
|
||||||
fi
|
|
||||||
if [ -f "${RSYNC_STATSFILE}" ]; then
|
|
||||||
# send rsync stats log file if it exists
|
|
||||||
rsync_report_args+=("${RSYNC_STATSFILE}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Rsync remote destination
|
|
||||||
rsync_report_args+=("root@${rsync_server}:${REMOTE_LOG_DIR}/")
|
|
||||||
|
|
||||||
# … log it
|
|
||||||
log "SYNC_TASKS - sync=${sync_name}: Rsync report command : ${rsync_bin} ${rsync_report_args[*]}"
|
|
||||||
|
|
||||||
# … execute it
|
|
||||||
${rsync_bin} "${rsync_report_args[@]}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "STOP SYNC_TASKS - sync=${sync_name}"
|
|
||||||
}
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
# Default return-code (0 == succes)
|
|
||||||
GLOBAL_RC=0
|
|
||||||
|
|
||||||
# Possible error codes
|
|
||||||
readonly E_NOSRVAVAIL=21 # No server is available
|
|
||||||
readonly E_SYNCFAILED=20 # Failed sync task
|
|
||||||
readonly E_DUMPFAILED=10 # Failed dump task
|
|
||||||
|
|
||||||
# explicit PATH
|
|
||||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
|
||||||
|
|
||||||
# System name (linux, openbsd…)
|
|
||||||
: "${SYSTEM:=$(uname | tr '[:upper:]' '[:lower:]')}"
|
|
||||||
|
|
||||||
# Hostname (for logs and notifications)
|
|
||||||
: "${HOSTNAME:=$(hostname)}"
|
|
||||||
|
|
||||||
# Store pid in a file named after this program's name
|
|
||||||
: "${PROGNAME:=$(basename "$0")}"
|
|
||||||
: "${PIDFILE:="/var/run/${PROGNAME}.pid"}"
|
|
||||||
|
|
||||||
# Customize the log path if you want multiple scripts to have separate log files
|
|
||||||
: "${LOGFILE:="/var/log/evobackup.log"}"
|
|
||||||
|
|
||||||
# Canary file to update before executing tasks
|
|
||||||
: "${CANARY_FILE:="/zzz_evobackup_canary"}"
|
|
||||||
|
|
||||||
# Date format for log messages
|
|
||||||
: "${DATE_FORMAT:="%Y-%m-%d %H:%M:%S"}"
|
|
||||||
|
|
||||||
# Should we fallback on other servers when the first one is unreachable?
|
|
||||||
: "${SERVERS_FALLBACK:=1}"
|
|
||||||
# timeout (in seconds) for SSH connections
|
|
||||||
: "${SSH_CONNECT_TIMEOUT:=90}"
|
|
||||||
|
|
||||||
: "${LOCAL_BACKUP_DIR:="/home/backup"}"
|
|
||||||
# shellcheck disable=SC2174
|
|
||||||
mkdir -p -m 700 "${LOCAL_BACKUP_DIR}"
|
|
||||||
|
|
||||||
: "${ERRORS_DIR:="${LOCAL_BACKUP_DIR}/${PROGNAME}.errors-${START_TIME}"}"
|
|
||||||
# shellcheck disable=SC2174
|
|
||||||
mkdir -p -m 700 "${ERRORS_DIR}"
|
|
||||||
|
|
||||||
# Backup directory on remote server
|
|
||||||
: "${REMOTE_BACKUP_DIR:="/var/backup"}"
|
|
||||||
# Log directory in remote server
|
|
||||||
: "${REMOTE_LOG_DIR:="/var/log"}"
|
|
||||||
|
|
||||||
# Email address for notifications
|
|
||||||
: "${MAIL:="root"}"
|
|
||||||
|
|
||||||
# Email subject for notifications
|
|
||||||
: "${MAIL_SUBJECT:="[info] EvoBackup - Client ${HOSTNAME}"}"
|
|
||||||
|
|
||||||
# Enable/disable local tasks (default: enabled)
|
|
||||||
: "${LOCAL_TASKS:=1}"
|
|
||||||
# Enable/disable sync tasks (default: enabled)
|
|
||||||
: "${SYNC_TASKS:=1}"
|
|
||||||
|
|
||||||
# Enable/disable mtree (default: enabled)
|
|
||||||
: "${MTREE_ENABLED:=1}"
|
|
||||||
|
|
||||||
# If "setup_custom" exists and is a function, let's call it
|
|
||||||
setup_custom_type="$(type -t setup_custom)"
|
|
||||||
if [ "${setup_custom_type}" = "function" ]; then
|
|
||||||
setup_custom
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Force umask
|
|
||||||
umask 077
|
|
||||||
|
|
||||||
# Initialize a list of temporary files
|
|
||||||
declare -a TEMP_FILES=()
|
|
||||||
# Any file in this list will be deleted when the program exits
|
|
||||||
trap "cleanup" EXIT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
run_evobackup() {
|
|
||||||
# Start timer
|
|
||||||
START_EPOCH=$(/bin/date +%s)
|
|
||||||
START_TIME=$(/bin/date +"%Y%m%d%H%M%S")
|
|
||||||
|
|
||||||
# Configure variables and environment
|
|
||||||
setup
|
|
||||||
|
|
||||||
log "START GLOBAL - VERSION=${VERSION} LOCAL_TASKS=${LOCAL_TASKS} SYNC_TASKS=${SYNC_TASKS}"
|
|
||||||
|
|
||||||
# /!\ Only one backup processus can run at the sametime /!\
|
|
||||||
# Based on PID file, kill any running process before continuing
|
|
||||||
enforce_single_process "${PIDFILE}"
|
|
||||||
|
|
||||||
# Update canary to keep track of each run
|
|
||||||
update-evobackup-canary --who "${PROGNAME}" --file "${CANARY_FILE}"
|
|
||||||
|
|
||||||
if [ "${LOCAL_TASKS}" = "1" ]; then
|
|
||||||
local_tasks_wrapper
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${SYNC_TASKS}" = "1" ]; then
|
|
||||||
sync_tasks_wrapper
|
|
||||||
fi
|
|
||||||
|
|
||||||
STOP_EPOCH=$(/bin/date +%s)
|
|
||||||
|
|
||||||
case "${SYSTEM}" in
|
|
||||||
*bsd)
|
|
||||||
start_time=$(/bin/date -f "%s" -j "${START_EPOCH}" +"${DATE_FORMAT}")
|
|
||||||
stop_time=$(/bin/date -f "%s" -j "${STOP_EPOCH}" +"${DATE_FORMAT}")
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
start_time=$(/bin/date --date="@${START_EPOCH}" +"${DATE_FORMAT}")
|
|
||||||
stop_time=$(/bin/date --date="@${STOP_EPOCH}" +"${DATE_FORMAT}")
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
duration=$(( STOP_EPOCH - START_EPOCH ))
|
|
||||||
|
|
||||||
log "STOP GLOBAL - start='${start_time}' stop='${stop_time}' duration=${duration}s"
|
|
||||||
|
|
||||||
send_mail
|
|
||||||
|
|
||||||
exit ${GLOBAL_RC}
|
|
||||||
}
|
|
|
@ -1,143 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Output a message to the log file
|
|
||||||
log() {
|
|
||||||
local msg="${1:-$(cat /dev/stdin)}"
|
|
||||||
local pid=$$
|
|
||||||
|
|
||||||
printf "[%s] %s[%s]: %s\\n" \
|
|
||||||
"$(/bin/date +"${DATE_FORMAT}")" "${PROGNAME}" "${pid}" "${msg}" \
|
|
||||||
>> "${LOGFILE}"
|
|
||||||
}
|
|
||||||
log_error() {
|
|
||||||
local error_msg=${1}
|
|
||||||
local error_file=${2:-""}
|
|
||||||
|
|
||||||
if [ -n "${error_file}" ] && [ -f "${error_file}" ]; then
|
|
||||||
printf "\n### %s\n" "${error_msg}" >&2
|
|
||||||
# shellcheck disable=SC2046
|
|
||||||
if [ $(wc -l "${error_file}" | cut -d " " -f 1) -gt 30 ]; then
|
|
||||||
printf "~~~{%s (tail -30)}\n" "${error_file}" >&2
|
|
||||||
tail -n 30 "${error_file}" >&2
|
|
||||||
else
|
|
||||||
printf "~~~{%s}\n" "${error_file}" >&2
|
|
||||||
cat "${error_file}" >&2
|
|
||||||
fi
|
|
||||||
printf "~~~\n" >&2
|
|
||||||
|
|
||||||
log "${error_msg}, check ${error_file}"
|
|
||||||
else
|
|
||||||
printf "\n### %s\n" "${error_msg}" >&2
|
|
||||||
|
|
||||||
log "${error_msg}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
}
|
|
||||||
add_to_temp_files() {
|
|
||||||
TEMP_FILES+=("${1}")
|
|
||||||
}
|
|
||||||
# Remove all temporary file created during the execution
|
|
||||||
cleanup() {
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
rm -f "${TEMP_FILES[@]}"
|
|
||||||
find "${ERRORS_DIR}" -type d -empty -delete
|
|
||||||
}
|
|
||||||
enforce_single_process() {
|
|
||||||
local pidfile=$1
|
|
||||||
|
|
||||||
if [ -e "${pidfile}" ]; then
|
|
||||||
pid=$(cat "${pidfile}")
|
|
||||||
# Does process still exist?
|
|
||||||
if kill -0 "${pid}" 2> /dev/null; then
|
|
||||||
# Killing the childs of evobackup.
|
|
||||||
for ppid in $(pgrep -P "${pid}"); do
|
|
||||||
kill -9 "${ppid}";
|
|
||||||
done
|
|
||||||
# Then kill the main PID.
|
|
||||||
kill -9 "${pid}"
|
|
||||||
printf "%s is still running (PID %s). Process has been killed" "$0" "${pid}\\n" >&2
|
|
||||||
else
|
|
||||||
rm -f "${pidfile}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
add_to_temp_files "${pidfile}"
|
|
||||||
|
|
||||||
echo "$$" > "${pidfile}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Build the error directory (inside ERRORS_DIR) based on the dump directory path
|
|
||||||
errors_dir_from_dump_dir() {
|
|
||||||
local dump_dir=$1
|
|
||||||
local relative_path=$(realpath --relative-to="${LOCAL_BACKUP_DIR}" "${dump_dir}")
|
|
||||||
|
|
||||||
# return absolute path
|
|
||||||
realpath --canonicalize-missing "${ERRORS_DIR}/${relative_path}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Call test_server with "HOST:PORT" string
|
|
||||||
# It will return with 0 if the server is reachable.
|
|
||||||
# It will return with 1 and a message on stderr if not.
|
|
||||||
test_server() {
|
|
||||||
local item=$1
|
|
||||||
# split HOST and PORT from the input string
|
|
||||||
local host=$(echo "${item}" | cut -d':' -f1)
|
|
||||||
local port=$(echo "${item}" | cut -d':' -f2)
|
|
||||||
|
|
||||||
local new_error
|
|
||||||
|
|
||||||
# Test if the server is accepting connections
|
|
||||||
ssh -q -o "ConnectTimeout ${SSH_CONNECT_TIMEOUT}" "${host}" -p "${port}" -t "exit"
|
|
||||||
# shellcheck disable=SC2181
|
|
||||||
if [ $? = 0 ]; then
|
|
||||||
# SSH connection is OK
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
# SSH connection failed
|
|
||||||
new_error=$(printf "Failed to connect to \`%s' within %s seconds" "${item}" "${SSH_CONNECT_TIMEOUT}")
|
|
||||||
log "${new_error}"
|
|
||||||
SSH_ERRORS+=("${new_error}")
|
|
||||||
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Call pick_server with an optional positive integer to get the nth server in the list.
|
|
||||||
pick_server() {
|
|
||||||
local -i increment=${1:-0}
|
|
||||||
local -i list_length=${#SERVERS[@]}
|
|
||||||
local sync_name=${2:-""}
|
|
||||||
|
|
||||||
if (( increment >= list_length )); then
|
|
||||||
# We've reached the end of the list
|
|
||||||
new_error="No more server available"
|
|
||||||
new_error="${new_error} for sync '${sync_name}'"
|
|
||||||
log "${new_error}"
|
|
||||||
SSH_ERRORS+=("${new_error}")
|
|
||||||
|
|
||||||
# Log errors to stderr
|
|
||||||
for i in "${!SSH_ERRORS[@]}"; do
|
|
||||||
printf "%s\n" "${SSH_ERRORS[i]}" >&2
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Extract the day of month, without leading 0 (which would give an octal based number)
|
|
||||||
today=$(/bin/date +%e)
|
|
||||||
# A salt is useful to randomize the starting point in the list
|
|
||||||
# but stay identical each time it's called for a server (based on hostname).
|
|
||||||
salt=$(hostname | cksum | cut -d' ' -f1)
|
|
||||||
# Pick an integer between 0 and the length of the SERVERS list
|
|
||||||
# It changes each day
|
|
||||||
n=$(( (today + salt + increment) % list_length ))
|
|
||||||
|
|
||||||
echo "${SERVERS[n]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
send_mail() {
|
|
||||||
tail -20 "${LOGFILE}" | mail -s "${MAIL_SUBJECT}" "${MAIL}"
|
|
||||||
}
|
|
||||||
|
|
||||||
path_to_str() {
|
|
||||||
echo "${1}" | sed -e 's|^/||; s|/$||; s|/|:|g'
|
|
||||||
}
|
|
|
@ -1,326 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
# Evobackup client
|
|
||||||
# See https://gitea.evolix.org/evolix/evobackup
|
|
||||||
#
|
|
||||||
# This is a generated backup script made by:
|
|
||||||
# command: @COMMAND@
|
|
||||||
# version: @VERSION@
|
|
||||||
# date: @DATE@
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
#
|
|
||||||
# You must configure the MAIL variable to receive notifications.
|
|
||||||
#
|
|
||||||
# There is some optional configuration that you can do
|
|
||||||
# at the end of this script.
|
|
||||||
#
|
|
||||||
#######################################################################
|
|
||||||
|
|
||||||
# Email adress for notifications
|
|
||||||
MAIL=__NOTIFICATION_MAIL__
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
#
|
|
||||||
# The "sync_tasks" function will be called by the "run_evobackup" function.
|
|
||||||
#
|
|
||||||
# You can customize the variables:
|
|
||||||
# * "SYNC_NAME" (String)
|
|
||||||
# * "SERVERS" (Array of HOST:PORT)
|
|
||||||
# * "RSYNC_INCLUDES" (Array of paths to include)
|
|
||||||
# * "RSYNC_EXCLUDES" (Array of paths to exclude)
|
|
||||||
#
|
|
||||||
# WARNING: remember to single-quote paths if they contain globs (*)
|
|
||||||
# and you want to pass them as-is to Rsync.
|
|
||||||
#
|
|
||||||
# The "sync" function can be called multiple times
|
|
||||||
# with a different set of variables.
|
|
||||||
# That way you can to sync to various destinations.
|
|
||||||
#
|
|
||||||
# Default includes/excludes are defined in the "main" library,
|
|
||||||
# referenced at this end of this file.
|
|
||||||
#
|
|
||||||
#######################################################################
|
|
||||||
|
|
||||||
# shellcheck disable=SC2034
|
|
||||||
sync_tasks() {
|
|
||||||
|
|
||||||
########## System-only backup (to Evolix servers) #################
|
|
||||||
|
|
||||||
SYNC_NAME="evolix-system"
|
|
||||||
SERVERS=(
|
|
||||||
__SRV0_HOST__:__SRV0_PORT__
|
|
||||||
__SRV1_HOST__:__SRV1_PORT__
|
|
||||||
)
|
|
||||||
RSYNC_INCLUDES=(
|
|
||||||
"${rsync_default_includes[@]}"
|
|
||||||
/etc
|
|
||||||
/root
|
|
||||||
/var
|
|
||||||
)
|
|
||||||
RSYNC_EXCLUDES=(
|
|
||||||
"${rsync_default_excludes[@]}"
|
|
||||||
)
|
|
||||||
sync "${SYNC_NAME}" "SERVERS[@]" "RSYNC_INCLUDES[@]" "RSYNC_EXCLUDES[@]"
|
|
||||||
|
|
||||||
|
|
||||||
########## Full backup (to client servers) ########################
|
|
||||||
|
|
||||||
### SYNC_NAME="client-full"
|
|
||||||
### SERVERS=(
|
|
||||||
### client-backup00.evolix.net:2221
|
|
||||||
### client-backup01.evolix.net:2221
|
|
||||||
### )
|
|
||||||
### RSYNC_INCLUDES=(
|
|
||||||
### "${rsync_default_includes[@]}"
|
|
||||||
### /etc
|
|
||||||
### /root
|
|
||||||
### /var
|
|
||||||
### /home
|
|
||||||
### /srv
|
|
||||||
### )
|
|
||||||
### RSYNC_EXCLUDES=(
|
|
||||||
### "${rsync_default_excludes[@]}"
|
|
||||||
### )
|
|
||||||
### sync "${SYNC_NAME}" "SERVERS[@]" "RSYNC_INCLUDES[@]" "RSYNC_EXCLUDES[@]"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
#
|
|
||||||
# The "local_tasks" function will be called by the "run_evobackup" function.
|
|
||||||
#
|
|
||||||
# You can call any available "dump_xxx" function
|
|
||||||
# (usually installed at /usr/local/lib/evobackup/dump-*.sh)
|
|
||||||
#
|
|
||||||
# You can also write some custom functions and call them.
|
|
||||||
# A "dump_custom" example is available further down.
|
|
||||||
#
|
|
||||||
#######################################################################
|
|
||||||
|
|
||||||
local_tasks() {
|
|
||||||
|
|
||||||
########## Server state ###########
|
|
||||||
|
|
||||||
# Run dump-server-state to extract system information
|
|
||||||
#
|
|
||||||
# Options : any dump-server-state supported option
|
|
||||||
# (except --dump-dir that will be overwritten)
|
|
||||||
# See 'dump-server-state -h' for details.
|
|
||||||
#
|
|
||||||
dump_server_state
|
|
||||||
|
|
||||||
########## MySQL ##################
|
|
||||||
|
|
||||||
# Very common strategy for a single instance server with default configuration :
|
|
||||||
#
|
|
||||||
### dump_mysql_global; dump_mysql_grants; dump_mysql_summary
|
|
||||||
#
|
|
||||||
# See below for details regarding dump functions for MySQL/MariaDB
|
|
||||||
|
|
||||||
# Dump all databases in a single compressed file
|
|
||||||
#
|
|
||||||
# Options :
|
|
||||||
# --masterdata (default: <absent>)
|
|
||||||
# --port=[Integer] (default: <blank>)
|
|
||||||
# --socket=[String] (default: <blank>)
|
|
||||||
# --user=[String] (default: <blank>)
|
|
||||||
# --password=[String] (default: <blank>)
|
|
||||||
# --defaults-file=[String] (default: <blank>)
|
|
||||||
# --defaults-extra-file=[String] (default: <blank>)
|
|
||||||
# --defaults-group-suffix=[String] (default: <blank>)
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
#
|
|
||||||
### dump_mysql_global
|
|
||||||
|
|
||||||
# Dump each database separately, in a compressed file
|
|
||||||
#
|
|
||||||
# Options :
|
|
||||||
# --port=[Integer] (default: <blank>)
|
|
||||||
# --socket=[String] (default: <blank>)
|
|
||||||
# --user=[String] (default: <blank>)
|
|
||||||
# --password=[String] (default: <blank>)
|
|
||||||
# --defaults-file=[String] (default: <blank>)
|
|
||||||
# --defaults-extra-file=[String] (default: <blank>)
|
|
||||||
# --defaults-group-suffix=[String] (default: <blank>)
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
#
|
|
||||||
### dump_mysql_per_base
|
|
||||||
|
|
||||||
# Dump permissions of an instance (using pt-show-grants)
|
|
||||||
#
|
|
||||||
# Options :
|
|
||||||
# --port=[Integer] (default: <blank>)
|
|
||||||
# --socket=[String] (default: <blank>)
|
|
||||||
# --user=[String] (default: <blank>)
|
|
||||||
# --password=[String] (default: <blank>)
|
|
||||||
# --defaults-file=[String] (default: <blank>)
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
#
|
|
||||||
# WARNING - unsupported options :
|
|
||||||
# --defaults-extra-file
|
|
||||||
# --defaults-group-suffix
|
|
||||||
# You have to provide credentials manually
|
|
||||||
#
|
|
||||||
### dump_mysql_grants
|
|
||||||
|
|
||||||
# Dump complete summary of an instance (using pt-mysql-summary)
|
|
||||||
#
|
|
||||||
# Options :
|
|
||||||
# --port=[Integer] (default: <blank>)
|
|
||||||
# --socket=[String] (default: <blank>)
|
|
||||||
# --user=[String] (default: <blank>)
|
|
||||||
# --password=[String] (default: <blank>)
|
|
||||||
# --defaults-file=[String] (default: <blank>)
|
|
||||||
# --defaults-extra-file=[String] (default: <blank>)
|
|
||||||
# --defaults-group-suffix=[String] (default: <blank>)
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
#
|
|
||||||
### dump_mysql_summary
|
|
||||||
|
|
||||||
# Dump each table in separate schema/data files
|
|
||||||
#
|
|
||||||
# Options :
|
|
||||||
# --port=[Integer] (default: <blank>)
|
|
||||||
# --socket=[String] (default: <blank>)
|
|
||||||
# --user=[String] (default: <blank>)
|
|
||||||
# --password=[String] (default: <blank>)
|
|
||||||
# --defaults-file=[String] (default: <blank>)
|
|
||||||
# --defaults-extra-file=[String] (default: <blank>)
|
|
||||||
# --defaults-group-suffix=[String] (default: <blank>)
|
|
||||||
# --dump-label=[String] (default: "default")
|
|
||||||
# used as suffix of the dump dir to differenciate multiple instances
|
|
||||||
#
|
|
||||||
### dump_mysql_tabs
|
|
||||||
|
|
||||||
########## PostgreSQL #############
|
|
||||||
|
|
||||||
# Dump all databases in a single file (compressed or not)
|
|
||||||
#
|
|
||||||
### dump_postgresql_global
|
|
||||||
|
|
||||||
# Dump a specific databse with only some tables, or all but some tables (must be configured)
|
|
||||||
#
|
|
||||||
### dump_postgresql_filtered
|
|
||||||
|
|
||||||
# Dump each database separately, in a compressed file
|
|
||||||
#
|
|
||||||
### dump_postgresql_per_base
|
|
||||||
|
|
||||||
########## MongoDB ################
|
|
||||||
|
|
||||||
### dump_mongodb [--user=foo] [--password=123456789]
|
|
||||||
|
|
||||||
########## Redis ##################
|
|
||||||
|
|
||||||
# Copy data file for all instances
|
|
||||||
#
|
|
||||||
### dump_redis [--instances=<all|instance1|instance2>]
|
|
||||||
|
|
||||||
########## Elasticsearch ##########
|
|
||||||
|
|
||||||
# Snapshot data for a single-node cluster
|
|
||||||
#
|
|
||||||
### dump_elasticsearch_snapshot_singlenode [--protocol=http] [--host=localhost] [--port=9200] [--user=foo] [--password=123456789] [--repository=snaprepo] [--snapshot=snapshot.daily]
|
|
||||||
|
|
||||||
# Snapshot data for a multi-node cluster
|
|
||||||
#
|
|
||||||
### dump_elasticsearch_snapshot_multinode [--protocol=http] [--host=localhost] [--port=9200] [--user=foo] [--password=123456789] [--repository=snaprepo] [--snapshot=snapshot.daily] [--nfs-server=192.168.2.1]
|
|
||||||
|
|
||||||
########## RabbitMQ ###############
|
|
||||||
|
|
||||||
### dump_rabbitmq
|
|
||||||
|
|
||||||
########## MegaCli ################
|
|
||||||
|
|
||||||
# Copy RAID config
|
|
||||||
#
|
|
||||||
### dump_megacli_config
|
|
||||||
|
|
||||||
# Dump file access control lists
|
|
||||||
#
|
|
||||||
### dump_facl
|
|
||||||
|
|
||||||
########## OpenLDAP ###############
|
|
||||||
|
|
||||||
### dump_ldap
|
|
||||||
|
|
||||||
########## Network ################
|
|
||||||
|
|
||||||
# Dump network routes with mtr and traceroute (warning: could be long with aggressive firewalls)
|
|
||||||
#
|
|
||||||
### dump_traceroute --targets=host_or_ip[,host_or_ip]
|
|
||||||
dump_traceroute --targets=8.8.8.8,www.evolix.fr,travaux.evolix.net
|
|
||||||
|
|
||||||
# No-op, in case nothing is enabled
|
|
||||||
:
|
|
||||||
}
|
|
||||||
|
|
||||||
# This is an example for a custom dump function
|
|
||||||
# Uncomment, customize and call it from the "local_tasks" function
|
|
||||||
### dump_custom() {
|
|
||||||
### # Set dump and errors directories and files
|
|
||||||
### local dump_dir="${LOCAL_BACKUP_DIR}/custom"
|
|
||||||
### local dump_file="${dump_dir}/dump.gz"
|
|
||||||
### local errors_dir=$(errors_dir_from_dump_dir "${dump_dir}")
|
|
||||||
### local error_file="${errors_dir}/dump.err"
|
|
||||||
###
|
|
||||||
### # Reset dump and errors directories
|
|
||||||
### rm -rf "${dump_dir}" "${errors_dir}"
|
|
||||||
### # shellcheck disable=SC2174
|
|
||||||
### mkdir -p -m 700 "${dump_dir}" "${errors_dir}"
|
|
||||||
###
|
|
||||||
### # Log the start of the function
|
|
||||||
### log "LOCAL_TASKS - ${FUNCNAME[0]}: start ${dump_file}"
|
|
||||||
###
|
|
||||||
### # Prepare the dump command (errors go to the error file and the data to the dump file)
|
|
||||||
### dump_cmd="my-dump-command 2> ${error_file} > ${dump_file}"
|
|
||||||
### log "LOCAL_TASKS - ${FUNCNAME[0]}: ${dump_cmd}"
|
|
||||||
###
|
|
||||||
### # Execute the dump command
|
|
||||||
### ${dump_cmd}
|
|
||||||
###
|
|
||||||
### # Check result and deal with potential errors
|
|
||||||
### local last_rc=$?
|
|
||||||
### # shellcheck disable=SC2086
|
|
||||||
### if [ ${last_rc} -ne 0 ]; then
|
|
||||||
### log_error "LOCAL_TASKS - ${FUNCNAME[0]}: my-dump-command to ${dump_file} returned an error ${last_rc}" "${error_file}"
|
|
||||||
### GLOBAL_RC=${E_DUMPFAILED}
|
|
||||||
### else
|
|
||||||
### rm -f "${error_file}"
|
|
||||||
### fi
|
|
||||||
###
|
|
||||||
### # Log the end of the function
|
|
||||||
### log "LOCAL_TASKS - ${FUNCNAME[0]}: stop ${dump_file}"
|
|
||||||
### }
|
|
||||||
|
|
||||||
########## Optional configuration #####################################
|
|
||||||
|
|
||||||
setup_custom() {
|
|
||||||
# System name ("linux" and "openbsd" currently supported)
|
|
||||||
### SYSTEM="$(uname)"
|
|
||||||
|
|
||||||
# Host name for logs and notifications
|
|
||||||
### HOSTNAME="$(hostname)"
|
|
||||||
|
|
||||||
# Email subject for notifications
|
|
||||||
### MAIL_SUBJECT="[info] EvoBackup - Client ${HOSTNAME}"
|
|
||||||
|
|
||||||
# No-op in case nothing is executed
|
|
||||||
:
|
|
||||||
}
|
|
||||||
|
|
||||||
########## Libraries ##################################################
|
|
||||||
|
|
||||||
# Change this to wherever you install the libraries
|
|
||||||
LIBDIR="/usr/local/lib/evobackup"
|
|
||||||
|
|
||||||
source "${LIBDIR}/main.sh"
|
|
||||||
|
|
||||||
########## Let's go! ##################################################
|
|
||||||
|
|
||||||
run_evobackup
|
|
129
client/update-evobackup-canary
Normal file
129
client/update-evobackup-canary
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
PROGNAME="update-evobackup-canary"
|
||||||
|
REPOSITORY="https://gitea.evolix.org/evolix/evobackup"
|
||||||
|
|
||||||
|
VERSION="22.06"
|
||||||
|
readonly VERSION
|
||||||
|
|
||||||
|
# base functions
|
||||||
|
|
||||||
|
show_version() {
|
||||||
|
cat <<END
|
||||||
|
${PROGNAME} version ${VERSION}
|
||||||
|
|
||||||
|
Copyright 2022 Evolix <info@evolix.fr>,
|
||||||
|
Jérémy Lecour <jlecour@evolix.fr>,
|
||||||
|
and others.
|
||||||
|
|
||||||
|
${REPOSITORY}
|
||||||
|
|
||||||
|
${PROGNAME} comes with ABSOLUTELY NO WARRANTY. This is free software,
|
||||||
|
and you are welcome to redistribute it under certain conditions.
|
||||||
|
See the GNU General Public License v3.0 for details.
|
||||||
|
END
|
||||||
|
}
|
||||||
|
show_help() {
|
||||||
|
cat <<END
|
||||||
|
${PROGNAME} is updating a canary file for evobackup.
|
||||||
|
|
||||||
|
Usage: ${PROGNAME} [OPTIONS]
|
||||||
|
|
||||||
|
Main options
|
||||||
|
-w, --who who has updated the file (default: logname())
|
||||||
|
-f, --file path of the canary file (default: /zzz_evobackup_canary)
|
||||||
|
-V, --version print version and exit
|
||||||
|
-h, --help print this message and exit
|
||||||
|
END
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
if [ -z "${who:-}" ]; then
|
||||||
|
who=$(logname)
|
||||||
|
fi
|
||||||
|
if [ -z "${canary_file:-}" ]; then
|
||||||
|
canary_file="/zzz_evobackup_canary"
|
||||||
|
fi
|
||||||
|
# This option is supported both on OpenBSD which does not use GNU date and on Debian
|
||||||
|
date=$(date "+%FT%T%z")
|
||||||
|
|
||||||
|
printf "%s %s\n" "${date}" "${who}" >> "${canary_file}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# parse options
|
||||||
|
# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
||||||
|
while :; do
|
||||||
|
case $1 in
|
||||||
|
-h|-\?|--help)
|
||||||
|
show_help
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-V|--version)
|
||||||
|
show_version
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
-w|--who)
|
||||||
|
# with value separated by space
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
who=$2
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
printf 'ERROR: "-w|--who" requires a non-empty option argument.\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--who=?*)
|
||||||
|
# with value speparated by =
|
||||||
|
who=${1#*=}
|
||||||
|
;;
|
||||||
|
--who=)
|
||||||
|
# without value
|
||||||
|
printf 'ERROR: "--who" requires a non-empty option argument.\n' >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
-f|--file)
|
||||||
|
# with value separated by space
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
canary_file=$2
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
printf 'ERROR: "-f|--file" requires a non-empty option argument.\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--file=?*)
|
||||||
|
# with value speparated by =
|
||||||
|
canary_file=${1#*=}
|
||||||
|
;;
|
||||||
|
--file=)
|
||||||
|
# without value
|
||||||
|
printf 'ERROR: "--file" requires a non-empty option argument.\n' >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
--)
|
||||||
|
# End of all options.
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
-?*)
|
||||||
|
# ignore unknown options
|
||||||
|
printf 'WARN: Unknown option : %s\n' "$1" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Default case: If no more options then break out of the loop.
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
export LC_ALL=C
|
||||||
|
|
||||||
|
set -u
|
||||||
|
|
||||||
|
main
|
|
@ -1,76 +0,0 @@
|
||||||
# To be used through "vagrant up" or "vagrant provision".
|
|
||||||
---
|
|
||||||
- hosts: bookworm,bullseye,buster
|
|
||||||
gather_facts: yes
|
|
||||||
become: yes
|
|
||||||
|
|
||||||
vars_files:
|
|
||||||
- '~/GIT/evolix-private/vars/evolinux-secrets.yml'
|
|
||||||
|
|
||||||
vars:
|
|
||||||
evolinux_hostname: "localhost"
|
|
||||||
evolinux_domain: "localdomain.tld"
|
|
||||||
evomaintenance_alert_email: "evomaintenance-{{ evolinux_hostname }}@evolix.fr"
|
|
||||||
evomaintenance_install_vendor: True
|
|
||||||
client_number: "XXX"
|
|
||||||
monitoring_mode: "everytime"
|
|
||||||
evocheck_force_install: "local"
|
|
||||||
evoadmin_host: "evoadmin.{{ evolinux_hostname }}.evolix.eu"
|
|
||||||
evoadmin_contact_email: root@localhost
|
|
||||||
postfix_slow_transport_include: True
|
|
||||||
|
|
||||||
evolinux_ssh_allow_current_user: True
|
|
||||||
|
|
||||||
minifirewall_additional_trusted_ips: ["192.168.0.0/16", "10.0.0.0/8"]
|
|
||||||
minifirewall_http_sites: ["0.0.0.0/0"]
|
|
||||||
|
|
||||||
packweb_enable_evoadmin_vhost: True
|
|
||||||
packweb_phpmyadmin_suffix: "uE34swx9"
|
|
||||||
|
|
||||||
evolinux_apt_include: True
|
|
||||||
evolinux_etcgit_include: True
|
|
||||||
evolinux_hostname_include: True
|
|
||||||
evolinux_kernel_include: True
|
|
||||||
evolinux_fstab_include: True
|
|
||||||
evolinux_packages_include: True
|
|
||||||
evolinux_system_include: True
|
|
||||||
evolinux_evomaintenance_include: True
|
|
||||||
evolinux_ssh_include: True
|
|
||||||
evolinux_users_include: False
|
|
||||||
evolinux_root_include: True
|
|
||||||
evolinux_postfix_include: True
|
|
||||||
evolinux_logs_include: True
|
|
||||||
evolinux_default_www_include: True
|
|
||||||
evolinux_hardware_include: True
|
|
||||||
evolinux_provider_online_include: False
|
|
||||||
evolinux_provider_orange_fce_include: False
|
|
||||||
evolinux_log2mail_include: True
|
|
||||||
evolinux_minifirewall_include: True
|
|
||||||
evolinux_munin_include: True
|
|
||||||
evolinux_nagios_nrpe_include: True
|
|
||||||
evolinux_fail2ban_include: False
|
|
||||||
mysql_custom_datadir: '/home/mysql'
|
|
||||||
mysql_custom_tmpdir: '/home/tmpmysql'
|
|
||||||
mysql_custom_logdir: '/home/mysql-logs'
|
|
||||||
# evolinux_apt_public_sources: False
|
|
||||||
apt_upgrade: True
|
|
||||||
|
|
||||||
# TODO Try to to make it work without the following line
|
|
||||||
# packweb_multiphp_versions:
|
|
||||||
# - php74
|
|
||||||
# - php82
|
|
||||||
|
|
||||||
# autosysadmin_config:
|
|
||||||
# repair_http: "on"
|
|
||||||
# repair_mysql: off
|
|
||||||
# repair_all: 'off'
|
|
||||||
|
|
||||||
roles:
|
|
||||||
- mysql
|
|
||||||
# - evolinux-base
|
|
||||||
# # - evolinux-users
|
|
||||||
# - ./ansible/roles/autosysadmin-agent
|
|
||||||
# - packweb-apache
|
|
||||||
# # - redis
|
|
||||||
# - { role: redis, redis_instance_name: foo, redis_port: 6380 }
|
|
||||||
# - { role: redis, redis_instance_name: bar, redis_port: 6381 }
|
|
626
client/zzz_evobackup
Executable file
626
client/zzz_evobackup
Executable file
|
@ -0,0 +1,626 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Script Evobackup client
|
||||||
|
# See https://gitea.evolix.org/evolix/evobackup
|
||||||
|
#
|
||||||
|
# Authors: Evolix <info@evolix.fr>,
|
||||||
|
# Gregory Colpart <reg@evolix.fr>,
|
||||||
|
# Romain Dessort <rdessort@evolix.fr>,
|
||||||
|
# Benoit Série <bserie@evolix.fr>,
|
||||||
|
# Tristan Pilat <tpilat@evolix.fr>,
|
||||||
|
# Victor Laborie <vlaborie@evolix.fr>,
|
||||||
|
# Jérémy Lecour <jlecour@evolix.fr>
|
||||||
|
# and others.
|
||||||
|
#
|
||||||
|
# Licence: AGPLv3
|
||||||
|
#
|
||||||
|
# /!\ DON'T FORGET TO SET "MAIL" and "SERVERS" VARIABLES
|
||||||
|
|
||||||
|
##### Configuration ###################################################
|
||||||
|
|
||||||
|
VERSION="22.05"
|
||||||
|
|
||||||
|
# email adress for notifications
|
||||||
|
MAIL=jdoe@example.com
|
||||||
|
|
||||||
|
# list of hosts (hostname or IP) and SSH port for Rsync
|
||||||
|
SERVERS="node0.backup.example.com:2XXX node1.backup.example.com:2XXX"
|
||||||
|
|
||||||
|
# explicit PATH
|
||||||
|
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
||||||
|
|
||||||
|
# Should we fallback on other servers when the first one is unreachable?
|
||||||
|
SERVERS_FALLBACK=${SERVERS_FALLBACK:-1}
|
||||||
|
|
||||||
|
# timeout (in seconds) for SSH connections
|
||||||
|
SSH_CONNECT_TIMEOUT=${SSH_CONNECT_TIMEOUT:-90}
|
||||||
|
|
||||||
|
# We use /home/backup : feel free to use your own dir
|
||||||
|
LOCAL_BACKUP_DIR="/home/backup"
|
||||||
|
|
||||||
|
# You can set "linux" or "bsd" manually or let it choose automatically
|
||||||
|
SYSTEM=$(uname | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
# Store pid in a file named after this program's name
|
||||||
|
PROGNAME=$(basename "$0")
|
||||||
|
PIDFILE="/var/run/${PROGNAME}.pid"
|
||||||
|
|
||||||
|
# Customize the log path if you have multiple scripts and with separate logs
|
||||||
|
LOGFILE="/var/log/evobackup.log"
|
||||||
|
|
||||||
|
HOSTNAME=$(hostname)
|
||||||
|
|
||||||
|
DATE_FORMAT="%Y-%m-%d %H:%M:%S"
|
||||||
|
|
||||||
|
# Enable/disable local tasks (default: enabled)
|
||||||
|
: "${LOCAL_TASKS:=1}"
|
||||||
|
# Enable/disable sync tasks (default: enabled)
|
||||||
|
: "${SYNC_TASKS:=1}"
|
||||||
|
|
||||||
|
##### SETUP AND FUNCTIONS #############################################
|
||||||
|
|
||||||
|
local_tasks() {
|
||||||
|
log "START LOCAL_TASKS"
|
||||||
|
|
||||||
|
# You can comment or uncomment sections below to customize the backup
|
||||||
|
|
||||||
|
## OpenLDAP : example with slapcat
|
||||||
|
# slapcat -n 0 -l ${LOCAL_BACKUP_DIR}/config.ldap.bak
|
||||||
|
# slapcat -n 1 -l ${LOCAL_BACKUP_DIR}/data.ldap.bak
|
||||||
|
# slapcat -l ${LOCAL_BACKUP_DIR}/ldap.bak
|
||||||
|
|
||||||
|
## MySQL
|
||||||
|
|
||||||
|
## Purge previous dumps
|
||||||
|
# rm -f ${LOCAL_BACKUP_DIR}/mysql.*.gz
|
||||||
|
# rm -rf ${LOCAL_BACKUP_DIR}/mysql
|
||||||
|
# rm -rf ${LOCAL_BACKUP_DIR}/mysqlhotcopy
|
||||||
|
# rm -rf /home/mysqldump
|
||||||
|
# rm -f ${LOCAL_BACKUP_DIR}/*.err ${LOCAL_BACKUP_DIR}/**/*.err
|
||||||
|
|
||||||
|
## example with global and compressed mysqldump
|
||||||
|
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 \
|
||||||
|
# --opt --all-databases --force --events --hex-blob 2> ${LOCAL_BACKUP_DIR}/mysql.bak.err | gzip --best > ${LOCAL_BACKUP_DIR}/mysql.bak.gz
|
||||||
|
# last_rc=$?
|
||||||
|
# if [ ${last_rc} -ne 0 ]; then
|
||||||
|
# error "mysqldump (global compressed) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/mysql.bak.err"
|
||||||
|
# rc=101
|
||||||
|
# fi
|
||||||
|
|
||||||
|
## example with compressed SQL dump (with data) for each databases
|
||||||
|
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/
|
||||||
|
# for i in $(mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 -e 'show databases' -s --skip-column-names \
|
||||||
|
# | egrep -v "^(Database|information_schema|performance_schema|sys)"); do
|
||||||
|
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 --events --hex-blob $i 2> ${LOCAL_BACKUP_DIR}/${i}.err | gzip --best > ${LOCAL_BACKUP_DIR}/mysql/${i}.sql.gz
|
||||||
|
# last_rc=$?
|
||||||
|
# if [ ${last_rc} -ne 0 ]; then
|
||||||
|
# error "mysqldump (${i} compressed) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/${i}.err"
|
||||||
|
# rc=102
|
||||||
|
# fi
|
||||||
|
# done
|
||||||
|
|
||||||
|
## Dump all grants (requires 'percona-toolkit' package)
|
||||||
|
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/
|
||||||
|
# pt-show-grants --flush --no-header 2> ${LOCAL_BACKUP_DIR}/mysql/all_grants.err > ${LOCAL_BACKUP_DIR}/mysql/all_grants.sql
|
||||||
|
# last_rc=$?
|
||||||
|
# if [ ${last_rc} -ne 0 ]; then
|
||||||
|
# error "pt-show-grants returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/mysql/all_grants.err"
|
||||||
|
# rc=103
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# Dump all variables
|
||||||
|
# mysql -A -e"SHOW GLOBAL VARIABLES;" 2> ${LOCAL_BACKUP_DIR}/MySQLCurrentSettings.err > ${LOCAL_BACKUP_DIR}/MySQLCurrentSettings.txt
|
||||||
|
# last_rc=$?
|
||||||
|
# if [ ${last_rc} -ne 0 ]; then
|
||||||
|
# error "mysql (variables) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/MySQLCurrentSettings.err"
|
||||||
|
# rc=104
|
||||||
|
# fi
|
||||||
|
|
||||||
|
## example with SQL dump (schema only, no data) for each databases
|
||||||
|
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/
|
||||||
|
# for i in $(mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 -e 'show databases' -s --skip-column-names \
|
||||||
|
# | egrep -v "^(Database|information_schema|performance_schema|sys)"); do
|
||||||
|
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 --no-data --databases $i 2> ${LOCAL_BACKUP_DIR}/${i}.schema.err > ${LOCAL_BACKUP_DIR}/mysql/${i}.schema.sql
|
||||||
|
# last_rc=$?
|
||||||
|
# if [ ${last_rc} -ne 0 ]; then
|
||||||
|
# error "mysqldump (${i} schema) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/${i}.schema.err"
|
||||||
|
# rc=105
|
||||||
|
# fi
|
||||||
|
# done
|
||||||
|
|
||||||
|
## example with *one* uncompressed SQL dump for *one* database (MYBASE)
|
||||||
|
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysql/MYBASE
|
||||||
|
# chown -RL mysql ${LOCAL_BACKUP_DIR}/mysql/
|
||||||
|
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -Q \
|
||||||
|
# --opt --events --hex-blob --skip-comments -T ${LOCAL_BACKUP_DIR}/mysql/MYBASE MYBASE 2> ${LOCAL_BACKUP_DIR}/mysql/MYBASE.err
|
||||||
|
# last_rc=$?
|
||||||
|
# if [ ${last_rc} -ne 0 ]; then
|
||||||
|
# error "mysqldump (MYBASE) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/mysql/MYBASE.err"
|
||||||
|
# rc=106
|
||||||
|
# fi
|
||||||
|
|
||||||
|
## example with two dumps for each table (.sql/.txt) for all databases
|
||||||
|
# for i in $(echo SHOW DATABASES | mysql --defaults-extra-file=/etc/mysql/debian.cnf -P 3306 \
|
||||||
|
# | egrep -v "^(Database|information_schema|performance_schema|sys)" ); do
|
||||||
|
# mkdir -p -m 700 /home/mysqldump/$i ; chown -RL mysql /home/mysqldump
|
||||||
|
# mysqldump --defaults-extra-file=/etc/mysql/debian.cnf --force -P 3306 -Q --opt --events --hex-blob --skip-comments \
|
||||||
|
# --fields-enclosed-by='\"' --fields-terminated-by=',' -T /home/mysqldump/$i $i 2> /home/mysqldump/$i.err"
|
||||||
|
# last_rc=$?
|
||||||
|
# if [ ${last_rc} -ne 0 ]; then
|
||||||
|
# error "mysqldump (${i} files) returned an error ${last_rc}, check /home/mysqldump/$i.err"
|
||||||
|
# rc=107
|
||||||
|
# fi
|
||||||
|
# done
|
||||||
|
|
||||||
|
## example with mysqlhotcopy
|
||||||
|
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mysqlhotcopy/
|
||||||
|
# mysqlhotcopy MYBASE ${LOCAL_BACKUP_DIR}/mysqlhotcopy/ 2> ${LOCAL_BACKUP_DIR}/mysqlhotcopy/MYBASE.err
|
||||||
|
# last_rc=$?
|
||||||
|
# if [ ${last_rc} -ne 0 ]; then
|
||||||
|
# error "mysqlhotcopy returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/mysqlhotcopy/MYBASE.err"
|
||||||
|
# rc=108
|
||||||
|
# fi
|
||||||
|
|
||||||
|
## example for multiples MySQL instances
|
||||||
|
# mysqladminpasswd=$(grep -m1 'password = .*' /root/.my.cnf|cut -d" " -f3)
|
||||||
|
# grep -E "^port\s*=\s*\d*" /etc/mysql/my.cnf |while read instance; do
|
||||||
|
# instance=$(echo "$instance"|awk '{ print $3 }')
|
||||||
|
# if [ "$instance" != "3306" ]
|
||||||
|
# then
|
||||||
|
# mysqldump -P $instance --opt --all-databases --hex-blob -u mysqladmin -p$mysqladminpasswd 2> ${LOCAL_BACKUP_DIR}/mysql.${instance}.err | gzip --best > ${LOCAL_BACKUP_DIR}/mysql.${instance}.bak.gz
|
||||||
|
# last_rc=$?
|
||||||
|
# if [ ${last_rc} -ne 0 ]; then
|
||||||
|
# error "mysqldump (instance ${instance}) returned an error ${last_rc}, check ${LOCAL_BACKUP_DIR}/mysql.${instance}.err"
|
||||||
|
# rc=107
|
||||||
|
# fi
|
||||||
|
# fi
|
||||||
|
# done
|
||||||
|
|
||||||
|
## PostgreSQL
|
||||||
|
|
||||||
|
## Purge previous dumps
|
||||||
|
# rm -rf ${LOCAL_BACKUP_DIR}/pg.*.gz
|
||||||
|
# rm -rf ${LOCAL_BACKUP_DIR}/pg-backup.tar
|
||||||
|
# rm -rf ${LOCAL_BACKUP_DIR}/postgresql/*
|
||||||
|
|
||||||
|
## example with pg_dumpall (warning: you need space in ~postgres)
|
||||||
|
# su - postgres -c "pg_dumpall > ~/pg.dump.bak"
|
||||||
|
# mv ~postgres/pg.dump.bak ${LOCAL_BACKUP_DIR}/
|
||||||
|
|
||||||
|
## another method with gzip directly piped
|
||||||
|
# cd /var/lib/postgresql
|
||||||
|
# sudo -u postgres pg_dumpall | gzip > ${LOCAL_BACKUP_DIR}/pg.dump.bak.gz
|
||||||
|
# cd - > /dev/null
|
||||||
|
|
||||||
|
## example with all tables from MYBASE excepts TABLE1 and TABLE2
|
||||||
|
# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f ${LOCAL_BACKUP_DIR}/pg-backup.tar -t 'TABLE1' -t 'TABLE2' MYBASE
|
||||||
|
|
||||||
|
## example with only TABLE1 and TABLE2 from MYBASE
|
||||||
|
# pg_dump -p 5432 -h 127.0.0.1 -U USER --clean -F t --inserts -f ${LOCAL_BACKUP_DIR}/pg-backup.tar -T 'TABLE1' -T 'TABLE2' MYBASE
|
||||||
|
|
||||||
|
## example with compressed PostgreSQL dump for each databases
|
||||||
|
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/postgresql
|
||||||
|
# chown postgres:postgres ${LOCAL_BACKUP_DIR}/postgresql
|
||||||
|
# cd /var/lib/postgresql
|
||||||
|
# dbs=$(sudo -u postgres psql -U postgres -lt | awk -F\| '{print $1}' |grep -v template*)
|
||||||
|
# for databases in $dbs ; do sudo -u postgres /usr/bin/pg_dump --create -s -U postgres -d $databases | gzip --best -c > ${LOCAL_BACKUP_DIR}/postgresql/$databases.sql.gz ; done
|
||||||
|
# cd - > /dev/null
|
||||||
|
|
||||||
|
## MongoDB
|
||||||
|
|
||||||
|
## don't forget to create use with read-only access
|
||||||
|
## > use admin
|
||||||
|
## > db.createUser( { user: "mongobackup", pwd: "PASS", roles: [ "backup", ] } )
|
||||||
|
## Purge previous dumps
|
||||||
|
# rm -rf ${LOCAL_BACKUP_DIR}/mongodump/
|
||||||
|
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/mongodump/
|
||||||
|
# mongodump --quiet -u mongobackup -pPASS -o ${LOCAL_BACKUP_DIR}/mongodump/
|
||||||
|
# if [ $? -ne 0 ]; then
|
||||||
|
# echo "Error with mongodump!"
|
||||||
|
# fi
|
||||||
|
|
||||||
|
## Redis
|
||||||
|
|
||||||
|
## Purge previous dumps
|
||||||
|
# rm -rf ${LOCAL_BACKUP_DIR}/redis/
|
||||||
|
# rm -rf ${LOCAL_BACKUP_DIR}/redis-*
|
||||||
|
## example with copy .rdb file
|
||||||
|
## for the default instance :
|
||||||
|
# mkdir -p -m 700 ${LOCAL_BACKUP_DIR}/redis/
|
||||||
|
# cp /var/lib/redis/dump.rdb ${LOCAL_BACKUP_DIR}/redis/
|
||||||
|
## for multiple instances :
|
||||||
|
# for instance in $(ls -d /var/lib/redis-*); do
|
||||||
|
# name=$(basename $instance)
|
||||||
|
# mkdir -p ${LOCAL_BACKUP_DIR}/${name}
|
||||||
|
# cp -a ${instance}/dump.rdb ${LOCAL_BACKUP_DIR}/${name}
|
||||||
|
# done
|
||||||
|
|
||||||
|
## ElasticSearch
|
||||||
|
|
||||||
|
## Take a snapshot as a backup.
|
||||||
|
## Warning: You need to have a path.repo configured.
|
||||||
|
## See: https://wiki.evolix.org/HowtoElasticsearch#snapshots-et-sauvegardes
|
||||||
|
# curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/snapshot.daily" >> "${LOGFILE}"
|
||||||
|
# curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot.daily?wait_for_completion=true" >> "${LOGFILE}"
|
||||||
|
## Clustered version here
|
||||||
|
## It basically the same thing except that you need to check that NFS is mounted
|
||||||
|
# if ss | grep ':nfs' | grep -q 'ip\.add\.res\.s1' && ss | grep ':nfs' | grep -q 'ip\.add\.res\.s2'
|
||||||
|
# then
|
||||||
|
# curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/snapshot.daily" >> "${LOGFILE}"
|
||||||
|
# curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot.daily?wait_for_completion=true" >> "${LOGFILE}"
|
||||||
|
# else
|
||||||
|
# echo 'Cannot make a snapshot of elasticsearch, at least one node is not mounting the repository.'
|
||||||
|
# fi
|
||||||
|
## If you need to keep older snapshot, for example the last 10 daily snapshots, replace the XDELETE and XPUT lines by :
|
||||||
|
# for snapshot in $(curl -s -XGET "localhost:9200/_snapshot/snaprepo/_all?pretty=true" | grep -Eo 'snapshot_[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -n -10); do
|
||||||
|
# curl -s -XDELETE "localhost:9200/_snapshot/snaprepo/${snapshot}" | grep -v -Fx '{"acknowledged":true}'
|
||||||
|
# done
|
||||||
|
# date=$(/bin/date +%F)
|
||||||
|
# curl -s -XPUT "localhost:9200/_snapshot/snaprepo/snapshot_${date}?wait_for_completion=true" >> "${LOGFILE}"
|
||||||
|
|
||||||
|
## RabbitMQ
|
||||||
|
|
||||||
|
## export config
|
||||||
|
#rabbitmqadmin export ${LOCAL_BACKUP_DIR}/rabbitmq.config >> "${LOGFILE}"
|
||||||
|
|
||||||
|
## MegaCli config
|
||||||
|
|
||||||
|
#megacli -CfgSave -f ${LOCAL_BACKUP_DIR}/megacli_conf.dump -a0 >/dev/null
|
||||||
|
|
||||||
|
## Dump network routes with mtr and traceroute (warning: could be long with aggressive firewalls)
|
||||||
|
network_targets="8.8.8.8 www.evolix.fr travaux.evolix.net"
|
||||||
|
mtr_bin=$(command -v mtr)
|
||||||
|
if [ -n "${mtr_bin}" ]; then
|
||||||
|
for addr in ${network_targets}; do
|
||||||
|
${mtr_bin} -r "${addr}" > "${LOCAL_BACKUP_DIR}/mtr-${addr}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
traceroute_bin=$(command -v traceroute)
|
||||||
|
if [ -n "${traceroute_bin}" ]; then
|
||||||
|
for addr in ${network_targets}; do
|
||||||
|
${traceroute_bin} -n "${addr}" > "${LOCAL_BACKUP_DIR}/traceroute-${addr}" 2>&1
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
server_state_dir="${LOCAL_BACKUP_DIR}/server-state"
|
||||||
|
|
||||||
|
dump_server_state_bin=$(command -v dump-server-state)
|
||||||
|
|
||||||
|
if [ "${SYSTEM}" = "linux" ]; then
|
||||||
|
if [ -n "${dump_server_state_bin}" ]; then
|
||||||
|
${dump_server_state_bin} --all --force --dump-dir "${server_state_dir}"
|
||||||
|
last_rc=$?
|
||||||
|
if [ ${last_rc} -ne 0 ]; then
|
||||||
|
error "dump-server-state returned an error ${last_rc}, check ${server_state_dir}"
|
||||||
|
rc=1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
mkdir -p "${server_state_dir}"
|
||||||
|
|
||||||
|
## Dump system and kernel versions
|
||||||
|
uname -a > ${server_state_dir}/uname.txt
|
||||||
|
|
||||||
|
## Dump process with ps
|
||||||
|
ps auwwx > ${server_state_dir}/ps.txt
|
||||||
|
|
||||||
|
## Dump network connections with ss
|
||||||
|
ss -taupen > ${server_state_dir}/netstat.txt
|
||||||
|
|
||||||
|
## List Debian packages
|
||||||
|
dpkg -l > ${server_state_dir}/packages
|
||||||
|
dpkg --get-selections > ${server_state_dir}/packages.getselections
|
||||||
|
apt-cache dumpavail > ${server_state_dir}/packages.available
|
||||||
|
|
||||||
|
## Dump iptables
|
||||||
|
if [ -x /sbin/iptables ]; then
|
||||||
|
{ /sbin/iptables -L -n -v; /sbin/iptables -t filter -L -n -v; } > ${server_state_dir}/iptables.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
## Dump findmnt(8) output
|
||||||
|
FINDMNT_BIN=$(command -v findmnt)
|
||||||
|
if [ -x "${FINDMNT_BIN}" ]; then
|
||||||
|
${FINDMNT_BIN} > ${server_state_dir}/findmnt.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
## Dump MBR / table partitions
|
||||||
|
disks=$(lsblk -l | grep disk | grep -v -E '(drbd|fd[0-9]+)' | awk '{print $1}')
|
||||||
|
for disk in ${disks}; do
|
||||||
|
dd if="/dev/${disk}" of="${server_state_dir}/MBR-${disk}" bs=512 count=1 2>&1 | grep -Ev "(records in|records out|512 bytes)"
|
||||||
|
fdisk -l "/dev/${disk}" > "${server_state_dir}/partitions-${disk}" 2>&1
|
||||||
|
done
|
||||||
|
cat ${server_state_dir}/partitions-* > ${server_state_dir}/partitions
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -n "${dump_server_state_bin}" ]; then
|
||||||
|
${dump_server_state_bin} --all --force --dump-dir "${server_state_dir}"
|
||||||
|
last_rc=$?
|
||||||
|
if [ ${last_rc} -ne 0 ]; then
|
||||||
|
error "dump-server-state returned an error ${last_rc}, check ${server_state_dir}"
|
||||||
|
rc=1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
mkdir -p "${server_state_dir}"
|
||||||
|
|
||||||
|
## Dump system and kernel versions
|
||||||
|
uname -a > ${server_state_dir}/uname
|
||||||
|
|
||||||
|
## Dump process with ps
|
||||||
|
ps auwwx > ${server_state_dir}/ps.out
|
||||||
|
|
||||||
|
## Dump network connections with fstat
|
||||||
|
fstat | head -1 > ${server_state_dir}/netstat.out
|
||||||
|
fstat | grep internet >> ${server_state_dir}/netstat.out
|
||||||
|
|
||||||
|
## List OpenBSD packages
|
||||||
|
pkg_info -m > ${server_state_dir}/packages
|
||||||
|
|
||||||
|
## Dump MBR / table partitions
|
||||||
|
disklabel sd0 > ${server_state_dir}/partitions
|
||||||
|
|
||||||
|
## Dump pf infos
|
||||||
|
pfctl -sa > ${server_state_dir}/pfctl-sa.txt
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
## Dump rights
|
||||||
|
#getfacl -R /var > ${server_state_dir}/rights-var.txt
|
||||||
|
#getfacl -R /etc > ${server_state_dir}/rights-etc.txt
|
||||||
|
#getfacl -R /usr > ${server_state_dir}/rights-usr.txt
|
||||||
|
#getfacl -R /home > ${server_state_dir}/rights-home.txt
|
||||||
|
|
||||||
|
log "STOP LOCAL_TASKS"
|
||||||
|
}
|
||||||
|
sync_tasks() {
|
||||||
|
n=0
|
||||||
|
server=""
|
||||||
|
if [ "${SERVERS_FALLBACK}" = "1" ]; then
|
||||||
|
# We try to find a suitable server
|
||||||
|
while :; do
|
||||||
|
server=$(pick_server "${n}")
|
||||||
|
test $? = 0 || exit 2
|
||||||
|
|
||||||
|
if test_server "${server}"; then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
server=""
|
||||||
|
n=$(( n + 1 ))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
# we force the server
|
||||||
|
server=$(pick_server "${n}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
SSH_SERVER=$(echo "${server}" | cut -d':' -f1)
|
||||||
|
SSH_PORT=$(echo "${server}" | cut -d':' -f2)
|
||||||
|
|
||||||
|
if [ "${SYSTEM}" = "linux" ]; then
|
||||||
|
rep="/bin /boot /lib /opt /sbin /usr"
|
||||||
|
else
|
||||||
|
rep="/bsd /bin /sbin /usr"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "START SYNC_TASKS - server=${server}"
|
||||||
|
|
||||||
|
update-evobackup-canary --who "${PROGNAME}"
|
||||||
|
|
||||||
|
# Remote shell command
|
||||||
|
RSH_COMMAND="ssh -p ${SSH_PORT} -o 'ConnectTimeout ${SSH_CONNECT_TIMEOUT}'"
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
# /!\ WARNING /!\ WARNING /!\ WARNING /!\ WARNING /!\ WARNING /!\ #
|
||||||
|
###################################################################
|
||||||
|
# DO NOT USE COMMENTS in rsync lines #
|
||||||
|
# DO NOT ADD WHITESPACES AFTER \ in rsync lines #
|
||||||
|
# It breaks the command and destroys data #
|
||||||
|
# => Only remove (or add) lines. #
|
||||||
|
###################################################################
|
||||||
|
|
||||||
|
# ignore check because we want it to split the different arguments to $rep
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
rsync --archive \
|
||||||
|
--itemize-changes --stats --human-readable \
|
||||||
|
--relative --partial \
|
||||||
|
--delete --delete-excluded --force --ignore-errors \
|
||||||
|
--exclude "dev" \
|
||||||
|
--exclude "lost+found" \
|
||||||
|
--exclude ".nfs.*" \
|
||||||
|
--exclude "/usr/doc" \
|
||||||
|
--exclude "/usr/obj" \
|
||||||
|
--exclude "/usr/share/doc" \
|
||||||
|
--exclude "/usr/src" \
|
||||||
|
--exclude "/var/apt" \
|
||||||
|
--exclude "/var/cache" \
|
||||||
|
--exclude "/var/lib/amavis/amavisd.sock" \
|
||||||
|
--exclude "/var/lib/amavis/tmp" \
|
||||||
|
--exclude "/var/lib/clamav/*.tmp" \
|
||||||
|
--exclude "/var/lib/elasticsearch" \
|
||||||
|
--exclude "/var/lib/metche" \
|
||||||
|
--exclude "/var/lib/munin/*tmp*" \
|
||||||
|
--exclude "/var/db/munin/*.tmp" \
|
||||||
|
--exclude "/var/lib/mongodb" \
|
||||||
|
--exclude "/var/lib/mysql" \
|
||||||
|
--exclude "/var/lib/php5" \
|
||||||
|
--exclude "/var/lib/php/sessions" \
|
||||||
|
--exclude "/var/lib/postgres" \
|
||||||
|
--exclude "/var/lib/postgresql" \
|
||||||
|
--exclude "/var/lib/sympa" \
|
||||||
|
--exclude "/var/lock" \
|
||||||
|
--exclude "/var/run" \
|
||||||
|
--exclude "/var/spool/postfix" \
|
||||||
|
--exclude "/var/spool/smtpd" \
|
||||||
|
--exclude "/var/spool/squid" \
|
||||||
|
--exclude "/var/state" \
|
||||||
|
--exclude "/var/tmp" \
|
||||||
|
--exclude "lxc/*/rootfs/tmp" \
|
||||||
|
--exclude "lxc/*/rootfs/usr/doc" \
|
||||||
|
--exclude "lxc/*/rootfs/usr/obj" \
|
||||||
|
--exclude "lxc/*/rootfs/usr/share/doc" \
|
||||||
|
--exclude "lxc/*/rootfs/usr/src" \
|
||||||
|
--exclude "lxc/*/rootfs/var/apt" \
|
||||||
|
--exclude "lxc/*/rootfs/var/cache" \
|
||||||
|
--exclude "lxc/*/rootfs/var/lib/php5" \
|
||||||
|
--exclude "lxc/*/rootfs/var/lib/php/sessions" \
|
||||||
|
--exclude "lxc/*/rootfs/var/lock" \
|
||||||
|
--exclude "lxc/*/rootfs/var/log" \
|
||||||
|
--exclude "lxc/*/rootfs/var/run" \
|
||||||
|
--exclude "lxc/*/rootfs/var/state" \
|
||||||
|
--exclude "lxc/*/rootfs/var/tmp" \
|
||||||
|
--exclude "/home/mysqltmp" \
|
||||||
|
${rep} \
|
||||||
|
/etc \
|
||||||
|
/root \
|
||||||
|
/var \
|
||||||
|
/home \
|
||||||
|
/zzz_evobackup_canary \
|
||||||
|
-e "${RSH_COMMAND}" \
|
||||||
|
"root@${SSH_SERVER}:/var/backup/" \
|
||||||
|
| tail -30 >> "${LOGFILE}"
|
||||||
|
|
||||||
|
rsync_rc=$?
|
||||||
|
if [ ${rsync_rc} -ne 0 ]; then
|
||||||
|
error "rsync returned an error ${rsync_rc}, check ${LOGFILE}"
|
||||||
|
rc=201
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "STOP SYNC_TASKS - server=${server}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Call test_server with "HOST:PORT" string
|
||||||
|
# It will return with 0 if the server is reachable.
|
||||||
|
# It will return with 1 and a message on stderr if not.
|
||||||
|
test_server() {
|
||||||
|
item=$1
|
||||||
|
# split HOST and PORT from the input string
|
||||||
|
host=$(echo "${item}" | cut -d':' -f1)
|
||||||
|
port=$(echo "${item}" | cut -d':' -f2)
|
||||||
|
|
||||||
|
# Test if the server is accepting connections
|
||||||
|
ssh -q -o "ConnectTimeout ${SSH_CONNECT_TIMEOUT}" "${host}" -p "${port}" -t "exit"
|
||||||
|
# shellcheck disable=SC2181
|
||||||
|
if [ $? = 0 ]; then
|
||||||
|
# SSH connection is OK
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
# SSH connection failed
|
||||||
|
new_error=$(printf "Failed to connect to \`%s' within %s seconds" "${item}" "${SSH_CONNECT_TIMEOUT}")
|
||||||
|
log "${new_error}"
|
||||||
|
SERVERS_SSH_ERRORS=$(printf "%s\\n%s" "${SERVERS_SSH_ERRORS}" "${new_error}" | sed -e '/^$/d')
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
# Call pick_server with an optional positive integer to get the nth server in the list.
|
||||||
|
pick_server() {
|
||||||
|
increment=${1:-0}
|
||||||
|
list_length=$(echo "${SERVERS}" | wc -w)
|
||||||
|
|
||||||
|
if [ "${increment}" -ge "${list_length}" ]; then
|
||||||
|
# We've reached the end of the list
|
||||||
|
new_error="No more server available"
|
||||||
|
log "${new_error}"
|
||||||
|
SERVERS_SSH_ERRORS=$(printf "%s\\n%s" "${SERVERS_SSH_ERRORS}" "${new_error}" | sed -e '/^$/d')
|
||||||
|
|
||||||
|
# Log errors to stderr
|
||||||
|
printf "%s\\n" "${SERVERS_SSH_ERRORS}" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract the day of month, without leading 0 (which would give an octal based number)
|
||||||
|
today=$(/bin/date +%e)
|
||||||
|
# A salt is useful to randomize the starting point in the list
|
||||||
|
# but stay identical each time it's called for a server (based on hostname).
|
||||||
|
salt=$(hostname | cksum | cut -d' ' -f1)
|
||||||
|
# Pick an integer between 0 and the length of the SERVERS list
|
||||||
|
# It changes each day
|
||||||
|
item=$(( (today + salt + increment) % list_length ))
|
||||||
|
# cut starts counting fields at 1, not 0.
|
||||||
|
field=$(( item + 1 ))
|
||||||
|
|
||||||
|
echo "${SERVERS}" | cut -d' ' -f${field}
|
||||||
|
}
|
||||||
|
log() {
|
||||||
|
msg="${1:-$(cat /dev/stdin)}"
|
||||||
|
pid=$$
|
||||||
|
printf "[%s] %s[%s]: %s\\n" \
|
||||||
|
"$(/bin/date +"${DATE_FORMAT}")" "${PROGNAME}" "${pid}" "${msg}" \
|
||||||
|
>> "${LOGFILE}"
|
||||||
|
}
|
||||||
|
error() {
|
||||||
|
msg="${1:-$(cat /dev/stdin)}"
|
||||||
|
pid=$$
|
||||||
|
printf "[%s] %s[%s]: %s\\n" \
|
||||||
|
"$(/bin/date +"${DATE_FORMAT}")" "${PROGNAME}" "${pid}" "${msg}" \
|
||||||
|
>&2
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
START_EPOCH=$(/bin/date +%s)
|
||||||
|
log "START GLOBAL - VERSION=${VERSION} LOCAL_TASKS=${LOCAL_TASKS} SYNC_TASKS=${SYNC_TASKS}"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2174
|
||||||
|
mkdir -p -m 700 ${LOCAL_BACKUP_DIR}
|
||||||
|
|
||||||
|
## Force umask
|
||||||
|
umask 077
|
||||||
|
|
||||||
|
## Initialize variable to store SSH connection errors
|
||||||
|
SERVERS_SSH_ERRORS=""
|
||||||
|
|
||||||
|
## Verify other evobackup process and kill if needed
|
||||||
|
if [ -e "${PIDFILE}" ]; then
|
||||||
|
pid=$(cat "${PIDFILE}")
|
||||||
|
# Does process still exist ?
|
||||||
|
if kill -0 "${pid}" 2> /dev/null; then
|
||||||
|
# Killing the childs of evobackup.
|
||||||
|
for ppid in $(pgrep -P "${pid}"); do
|
||||||
|
kill -9 "${ppid}";
|
||||||
|
done
|
||||||
|
# Then kill the main PID.
|
||||||
|
kill -9 "${pid}"
|
||||||
|
printf "%s is still running (PID %s). Process has been killed" "$0" "${pid}\\n" >&2
|
||||||
|
else
|
||||||
|
rm -f "${PIDFILE}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo "$$" > "${PIDFILE}"
|
||||||
|
# shellcheck disable=SC2064
|
||||||
|
trap "rm -f ${PIDFILE}" EXIT
|
||||||
|
|
||||||
|
if [ "${LOCAL_TASKS}" = "1" ]; then
|
||||||
|
local_tasks
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${SYNC_TASKS}" = "1" ]; then
|
||||||
|
sync_tasks
|
||||||
|
fi
|
||||||
|
|
||||||
|
STOP_EPOCH=$(/bin/date +%s)
|
||||||
|
|
||||||
|
if [ "${SYSTEM}" = "openbsd" ]; then
|
||||||
|
start_time=$(/bin/date -f "%s" -j "${START_EPOCH}" +"${DATE_FORMAT}")
|
||||||
|
stop_time=$(/bin/date -f "%s" -j "${STOP_EPOCH}" +"${DATE_FORMAT}")
|
||||||
|
else
|
||||||
|
start_time=$(/bin/date --date="@${START_EPOCH}" +"${DATE_FORMAT}")
|
||||||
|
stop_time=$(/bin/date --date="@${STOP_EPOCH}" +"${DATE_FORMAT}")
|
||||||
|
fi
|
||||||
|
duration=$(( STOP_EPOCH - START_EPOCH ))
|
||||||
|
|
||||||
|
log "STOP GLOBAL - start='${start_time}' stop='${stop_time}' duration=${duration}s"
|
||||||
|
|
||||||
|
tail -20 "${LOGFILE}" | mail -s "[info] EvoBackup - Client ${HOSTNAME}" ${MAIL}
|
||||||
|
}
|
||||||
|
|
||||||
|
# set all programs to C language (english)
|
||||||
|
export LC_ALL=C
|
||||||
|
|
||||||
|
# Error on unassigned variable
|
||||||
|
set -u
|
||||||
|
|
||||||
|
# Default return-code (0 == succes)
|
||||||
|
rc=0
|
||||||
|
|
||||||
|
# execute main funciton
|
||||||
|
main
|
||||||
|
|
||||||
|
exit ${rc}
|
1
debian/bkctld.default
vendored
Symbolic link
1
debian/bkctld.default
vendored
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../server/bkctld.conf
|
386
debian/changelog
vendored
Normal file
386
debian/changelog
vendored
Normal file
|
@ -0,0 +1,386 @@
|
||||||
|
bkctld (22.11-1) stable; urgency=medium
|
||||||
|
|
||||||
|
[ Jeremy Lecour ]
|
||||||
|
* check-setup: get minifirewall version from internal variable
|
||||||
|
* check-setup: use findmnt with mountpoint instead of target
|
||||||
|
* check-setup: check minifirewall version only if minifirewall is present
|
||||||
|
* bkctld-check-canary: new subcommand to check canary files and content
|
||||||
|
* bkctld-stats: filter active jails and columnize the output
|
||||||
|
* bkctld check-canary: add tests
|
||||||
|
|
||||||
|
[ William Hirigoyen ]
|
||||||
|
* Add --no-header option for status command.
|
||||||
|
|
||||||
|
-- Jeremy Lecour <dprevot+git@evolix.fr> Mon, 28 Nov 2022 15:30:52 +0100
|
||||||
|
|
||||||
|
bkctld (22.07-1) stable; urgency=medium
|
||||||
|
|
||||||
|
[ David Prevot ]
|
||||||
|
* Drop link to internal doc
|
||||||
|
* CI: Drop .git directory that was not present during first build
|
||||||
|
|
||||||
|
[ Jeremy Lecour ]
|
||||||
|
* Update debian/control for new maintainer and uploaders
|
||||||
|
* server: release 22.07
|
||||||
|
* Use upstream tarball instead of http checkout
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@evolix.fr> Fri, 22 Jul 2022 13:37:22 +0200
|
||||||
|
|
||||||
|
bkctld (22.06-1) stable; urgency=medium
|
||||||
|
|
||||||
|
* Server: release 22.06
|
||||||
|
* Include CI upstream
|
||||||
|
|
||||||
|
-- David Prévot <dprevot@evolix.fr> Tue, 28 Jun 2022 16:34:14 +0200
|
||||||
|
|
||||||
|
bkctld (22.04-1) stable; urgency=medium
|
||||||
|
|
||||||
|
* Upstream release 22.04
|
||||||
|
|
||||||
|
[ David Prévot ]
|
||||||
|
* debian/control:
|
||||||
|
+ Bump debhelper from deprecated 9 to 13.
|
||||||
|
+ Set debhelper-compat version in Build-Depends.
|
||||||
|
+ Set Rules-Requires-Root: no.
|
||||||
|
+ No need to depend on an essential package
|
||||||
|
+ Document Homepage
|
||||||
|
+ Update Standards-Version to 4.5.1
|
||||||
|
* debian/copyright:
|
||||||
|
+ Use secure copyright file specification URI.
|
||||||
|
+ Update
|
||||||
|
* debian/{docs,install}: Install doc as such
|
||||||
|
* debian/postinst: Add #DEBHELPER# token
|
||||||
|
* debian/changelog: Fix bogus mail host
|
||||||
|
|
||||||
|
-- David Prévot <dprevot@evolix.fr> Tue, 03 May 2022 16:51:53 +0200
|
||||||
|
|
||||||
|
bkctld (2.12.0-2) stable; urgency=low
|
||||||
|
|
||||||
|
* Package rebuild
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Wed, 03 Nov 2021 10:12:36 +0100
|
||||||
|
|
||||||
|
bkctld (2.12.0-1) stable; urgency=low
|
||||||
|
|
||||||
|
* Upstream release 2.12.0
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Tue, 02 Nov 2021 17:35:09 +0100
|
||||||
|
|
||||||
|
bkctld (2.11.1-1) stable; urgency=medium
|
||||||
|
|
||||||
|
* Upstream release 2.11.1
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Wed, 30 Jun 2021 18:15:21 +0200
|
||||||
|
|
||||||
|
bkctld (2.11.0-1) stable; urgency=medium
|
||||||
|
|
||||||
|
* Upstream release 2.11.0
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Wed, 30 Jun 2021 15:24:18 +0200
|
||||||
|
|
||||||
|
bkctld (2.10.0-1) stable; urgency=low
|
||||||
|
|
||||||
|
* Upstream release 2.10.0
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Tue, 29 Jun 2021 17:26:29 +0200
|
||||||
|
|
||||||
|
bkctld (2.9.0-2) stable; urgency=low
|
||||||
|
|
||||||
|
* Fix postinst script
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Mon, 22 Feb 2021 12:56:12 +0100
|
||||||
|
|
||||||
|
bkctld (2.9.0-1) stable; urgency=low
|
||||||
|
|
||||||
|
* Upstream release 2.9.0
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Mon, 22 Feb 2021 12:16:41 +0100
|
||||||
|
|
||||||
|
bkctld (2.8.0-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Upstream release 2.8.0
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Sat, 28 Nov 2020 10:48:06 +0100
|
||||||
|
|
||||||
|
bkctld (2.7.1-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Upstream release 2.7.1
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Sat, 28 Nov 2020 08:43:55 +0100
|
||||||
|
|
||||||
|
bkctld (2.7.0-1) unstable; urgency=high
|
||||||
|
|
||||||
|
* Upstrem release 2.7.0
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Fri, 13 Nov 2020 15:51:39 +0100
|
||||||
|
|
||||||
|
bkctld (2.6.0-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Upstream release 2.6.0
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Wed, 07 Oct 2020 20:51:52 +0200
|
||||||
|
|
||||||
|
bkctld (2.5.1-1) unstable; urgency=high
|
||||||
|
|
||||||
|
* Upstream release 2.5.1
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Wed, 07 Oct 2020 18:35:34 +0200
|
||||||
|
|
||||||
|
bkctld (2.5.0-1) unstable; urgency=high
|
||||||
|
|
||||||
|
* Upstream release 2.5.0
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@synopsis.evolix.net> Fri, 25 Sep 2020 14:14:15 +0200
|
||||||
|
|
||||||
|
bkctld (2.4.1-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Upstream release 2.4.1
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@evolix.fr> Fri, 28 Aug 2020 10:25:10 +0200
|
||||||
|
|
||||||
|
bkctld (2.4.0-1.1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Upstream release 2.4.0
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@evolix.fr> Wed, 19 Aug 2020 14:08:39 +0200
|
||||||
|
|
||||||
|
bkctld (2.3.3-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Upstream release 2.3.3
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@evolix.fr> Thu, 28 May 2020 10:26:53 +0200
|
||||||
|
|
||||||
|
bkctld (2.3.2-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Upstream release 2.3.2
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@evolix.fr> Sun, 03 May 2020 11:18:27 +0200
|
||||||
|
|
||||||
|
bkctld (2.3.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Upstream release 2.3.1
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@evolix.fr> Wed, 22 Apr 2020 01:18:22 +0200
|
||||||
|
|
||||||
|
bkctld (2.3.0-2) unstable; urgency=low
|
||||||
|
|
||||||
|
* Attempt to clean repository
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@evolix.fr> Mon, 20 Apr 2020 08:57:13 +0200
|
||||||
|
|
||||||
|
bkctld (2.3.0-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Upstream release 2.3.0
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@evolix.fr> Mon, 20 Apr 2020 08:31:49 +0200
|
||||||
|
|
||||||
|
bkctld (2.2.2-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Upstream release 2.2.2
|
||||||
|
|
||||||
|
-- Jeremy Lecour <jlecour@evolix.fr> Sun, 19 Apr 2020 10:09:29 +0200
|
||||||
|
|
||||||
|
bkctld (2.2.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Upstream release 2.2.1
|
||||||
|
|
||||||
|
-- jlecour <jlecour@evolix.fr> Sat, 18 Apr 2020 10:35:30 +0200
|
||||||
|
|
||||||
|
bkctld (2.2.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Big refactoring of the source code
|
||||||
|
|
||||||
|
-- jlecour <jlecour@evolix.fr> Fri, 17 Apr 2020 23:44:19 +0200
|
||||||
|
|
||||||
|
bkctld (2.1.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Do not run inc / rm in background when not using btrfs
|
||||||
|
|
||||||
|
-- vlaborie <vlaborie@evolix.fr> Mon, 09 Mar 2020 16:09:19 +0100
|
||||||
|
|
||||||
|
bkctld (2.1.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Add lock for bkctld inc / rm when not using btrfs
|
||||||
|
|
||||||
|
-- vlaborie <vlaborie@evolix.fr> Thu, 05 Mar 2020 14:51:19 +0100
|
||||||
|
|
||||||
|
bkctld (2.0.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Do not unnecessary use is-on and reload in ip/port/key
|
||||||
|
|
||||||
|
-- vlaborie <vlaborie@evolix.fr> Wed, 05 Feb 2020 15:00:21 +0100
|
||||||
|
|
||||||
|
bkctld (2.0.0-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Split bkctld into multiples scripts
|
||||||
|
* Drop Debian Jessie support
|
||||||
|
* Parallelize bkctld <subcommand> all
|
||||||
|
* Subcommand list are now dynamic in bash completion
|
||||||
|
* Transform bkctld SysVinit script into systemd oneshot service
|
||||||
|
* Do not create dirs in bkctld script
|
||||||
|
* Use mktemp for keepfile and rm it after usage
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Mon, 18 Feb 2019 11:39:45 +0100
|
||||||
|
|
||||||
|
bkctld (1.7.1-1) unstable; urgency=high
|
||||||
|
|
||||||
|
* btrfs is now in /bin
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Tue, 04 Dec 2018 16:15:42 +0100
|
||||||
|
|
||||||
|
bkctld (1.7.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Check if disk is mounted in bkctld check
|
||||||
|
* Add dependency to cryptsetup
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Thu, 29 Nov 2018 17:06:04 +0100
|
||||||
|
|
||||||
|
bkctld (1.6.2-1) unstable; urgency=high
|
||||||
|
|
||||||
|
* Use absolute PATH for btrfs command
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Wed, 04 Jul 2018 14:50:35 +0200
|
||||||
|
|
||||||
|
bkctld (1.6.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Add incs info to stats
|
||||||
|
* Fix last update date of stats index file
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Wed, 13 Jun 2018 18:23:09 +0200
|
||||||
|
|
||||||
|
bkctld (1.6.0-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Add stats subcommands
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Wed, 13 Jun 2018 15:29:18 +0200
|
||||||
|
|
||||||
|
bkctld (1.5.2-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Better bkctld check output
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Wed, 30 May 2018 18:35:19 +0200
|
||||||
|
|
||||||
|
bkctld (1.5.1-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Merge check_nrpe into bkctld check subcommand
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Wed, 30 May 2018 16:42:59 +0200
|
||||||
|
|
||||||
|
bkctld (1.5.0-2) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Use pandoc for generate man page
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Tue, 29 May 2018 18:30:29 +0200
|
||||||
|
|
||||||
|
bkctld (1.5.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Refactoring of logging and notification
|
||||||
|
* Force variables definition and add a set -u
|
||||||
|
* Reload when update params
|
||||||
|
* Sanitisation / Refactoring
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Thu, 29 Mar 2018 18:16:10 +0200
|
||||||
|
|
||||||
|
bkctld (1.4.9-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Fix bkctld logging on non interactive usage
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Mon, 04 Dec 2017 11:51:07 +0100
|
||||||
|
|
||||||
|
bkctld (1.4.8-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Update of zzz_evobackup sample script
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Mon, 04 Dec 2017 10:58:11 +0100
|
||||||
|
|
||||||
|
bkctld (1.4.7-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Update lastlog even on non interactive ssh (eg. with rsync)
|
||||||
|
* Clean jail before creating it
|
||||||
|
* Use touch instead of lastlog in sshrc
|
||||||
|
* Fix jail cleaning when not running
|
||||||
|
* Add mount check when starting jail
|
||||||
|
* Use -e instead of -f when checking for Keyfile
|
||||||
|
* Fix /var/run to /run symlink
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Wed, 30 Aug 2017 17:31:04 +0200
|
||||||
|
|
||||||
|
bkctld (1.4.4-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Posix Compatibility and shellcheck cleaning
|
||||||
|
* Fix jail's sshd logging
|
||||||
|
* Simpler and more secure jail
|
||||||
|
* Use sh instead of bash and permit SSH keys only
|
||||||
|
* Set UseDNS to no in jail's config
|
||||||
|
* Add a basic nrpe check and a licence file
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Sat, 05 Aug 2017 15:56:13 -0400
|
||||||
|
|
||||||
|
bkctld (1.3-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Convert to non native package
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Sat, 05 Aug 2017 15:51:35 -0400
|
||||||
|
|
||||||
|
bkctld (1.3) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Fix btrfs support/detection
|
||||||
|
* Btrfs snapshot (incs) are now readonly
|
||||||
|
* Fix bashism (if [[ -> if [)
|
||||||
|
* Add chroot dependency to README
|
||||||
|
* Use English for var names
|
||||||
|
* Improve packages list backup
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Mon, 17 Jul 2017 11:40:30 +0100
|
||||||
|
|
||||||
|
bkctld (1.2.4) unstable; urgency=low
|
||||||
|
|
||||||
|
* Delete firewall rules on bkctld remove
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Fri, 31 Mar 2017 14:56:00 +0100
|
||||||
|
|
||||||
|
bkctld (1.2.3) unstable; urgency=low
|
||||||
|
|
||||||
|
* Don't automatically purge incs with bkctld remove (cp version)
|
||||||
|
* Use lazy umount (wait for sshd stop)
|
||||||
|
* Update man page
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Fri, 24 Mar 2017 12:22:00 +0100
|
||||||
|
|
||||||
|
bkctld (1.2.1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Add local dir for surcharge default templates files
|
||||||
|
* Add uname in client sample script
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Fri, 17 Feb 2017 13:29:00 +0100
|
||||||
|
|
||||||
|
bkctld (1.1.1) unstable; urgency=low
|
||||||
|
|
||||||
|
* print all ip on bkctld status
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Fri, 10 Feb 2017 10:43:44 +0100
|
||||||
|
|
||||||
|
bkctld (1.1) unstable; urgency=high
|
||||||
|
|
||||||
|
* Add bash completion
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Thu, 02 Feb 2017 18:24:50 +0100
|
||||||
|
|
||||||
|
bkctld (1.0.2) unstable; urgency=high
|
||||||
|
|
||||||
|
* Fix bkctld sync
|
||||||
|
* Make minifirewall silent
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Thu, 02 Feb 2017 10:48:30 +0100
|
||||||
|
|
||||||
|
bkctld (1.0.1) unstable; urgency=high
|
||||||
|
|
||||||
|
* Use absolute path for btrfs command (fix cron use)
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Fri, 27 Jan 2017 11:47:20 +0100
|
||||||
|
|
||||||
|
bkctld (1.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Initial release
|
||||||
|
|
||||||
|
-- Victor Laborie <vlaborie@evolix.fr> Wed, 11 Jan 2017 16:51:48 +0100
|
24
debian/control
vendored
Normal file
24
debian/control
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
Source: bkctld
|
||||||
|
Section: utils
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Evolix <info@evolix.fr>
|
||||||
|
Uploaders: Jérémy Lecour <jlecour@evolix.fr>, David Prévot <dprevot@evolix.fr>
|
||||||
|
Build-Depends: debhelper-compat (= 13), dh-exec
|
||||||
|
Standards-Version: 4.5.1
|
||||||
|
Rules-Requires-Root: no
|
||||||
|
Homepage: https://gitea.evolix.org/evolix/evobackup
|
||||||
|
|
||||||
|
Package: bkctld
|
||||||
|
Architecture: all
|
||||||
|
Depends: cryptsetup,
|
||||||
|
duc-nox | duc,
|
||||||
|
lsb-base,
|
||||||
|
lsof,
|
||||||
|
openssh-server,
|
||||||
|
procps,
|
||||||
|
rsync,
|
||||||
|
${misc:Depends}
|
||||||
|
Recommends: btrfs-progs
|
||||||
|
Description: Tool for manage rsync jail for backup
|
||||||
|
bkctld is a shell script to create and manage a backup server
|
||||||
|
which will handle the backup of many servers (clients).
|
671
debian/copyright
vendored
Normal file
671
debian/copyright
vendored
Normal file
|
@ -0,0 +1,671 @@
|
||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: bkctld
|
||||||
|
Upstream-Contact: Evolix <equipe@evolix.fr>
|
||||||
|
Source: https://gitea.evolix.org/evolix/evobackup/
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2004-2022 Evolix <equipe@evolix.fr>
|
||||||
|
Victor Laborie <vlaborie@evolix.fr>,
|
||||||
|
Jérémy Lecour <jlecour@evolix.fr>
|
||||||
|
and others.
|
||||||
|
License: AGPL-3
|
||||||
|
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 19 November 2007
|
||||||
|
.
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
.
|
||||||
|
Preamble
|
||||||
|
.
|
||||||
|
The GNU Affero General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works, specifically designed to ensure
|
||||||
|
cooperation with the community in the case of network server software.
|
||||||
|
.
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
our General Public Licenses are intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users.
|
||||||
|
.
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
.
|
||||||
|
Developers that use our General Public Licenses protect your rights
|
||||||
|
with two steps: (1) assert copyright on the software, and (2) offer
|
||||||
|
you this License which gives you legal permission to copy, distribute
|
||||||
|
and/or modify the software.
|
||||||
|
.
|
||||||
|
A secondary benefit of defending all users' freedom is that
|
||||||
|
improvements made in alternate versions of the program, if they
|
||||||
|
receive widespread use, become available for other developers to
|
||||||
|
incorporate. Many developers of free software are heartened and
|
||||||
|
encouraged by the resulting cooperation. However, in the case of
|
||||||
|
software used on network servers, this result may fail to come about.
|
||||||
|
The GNU General Public License permits making a modified version and
|
||||||
|
letting the public access it on a server without ever releasing its
|
||||||
|
source code to the public.
|
||||||
|
.
|
||||||
|
The GNU Affero General Public License is designed specifically to
|
||||||
|
ensure that, in such cases, the modified source code becomes available
|
||||||
|
to the community. It requires the operator of a network server to
|
||||||
|
provide the source code of the modified version running there to the
|
||||||
|
users of that server. Therefore, public use of a modified version, on
|
||||||
|
a publicly accessible server, gives the public access to the source
|
||||||
|
code of the modified version.
|
||||||
|
.
|
||||||
|
An older license, called the Affero General Public License and
|
||||||
|
published by Affero, was designed to accomplish similar goals. This is
|
||||||
|
a different license, not a version of the Affero GPL, but Affero has
|
||||||
|
released a new version of the Affero GPL which permits relicensing under
|
||||||
|
this license.
|
||||||
|
.
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
.
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
.
|
||||||
|
0. Definitions.
|
||||||
|
.
|
||||||
|
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||||
|
.
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
.
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
.
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
.
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
.
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
.
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
.
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
.
|
||||||
|
1. Source Code.
|
||||||
|
.
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
.
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
.
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
.
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
.
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
.
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
.
|
||||||
|
2. Basic Permissions.
|
||||||
|
.
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
.
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
.
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
.
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
.
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
.
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
.
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
.
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
.
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
.
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
.
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
.
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
.
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
.
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
.
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
.
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
.
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
.
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
.
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
.
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
.
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
.
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
.
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
.
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
.
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
.
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
.
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
.
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
.
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
.
|
||||||
|
7. Additional Terms.
|
||||||
|
.
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
.
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
.
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
.
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
.
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
.
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
.
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
.
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
.
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
.
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
.
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
.
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
.
|
||||||
|
8. Termination.
|
||||||
|
.
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
.
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
.
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
.
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
.
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
.
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
.
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
.
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
.
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
.
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
.
|
||||||
|
11. Patents.
|
||||||
|
.
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
.
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
.
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
.
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
.
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
.
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
.
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
.
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
.
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
.
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
.
|
||||||
|
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||||
|
.
|
||||||
|
Notwithstanding any other provision of this License, if you modify the
|
||||||
|
Program, your modified version must prominently offer all users
|
||||||
|
interacting with it remotely through a computer network (if your version
|
||||||
|
supports such interaction) an opportunity to receive the Corresponding
|
||||||
|
Source of your version by providing access to the Corresponding Source
|
||||||
|
from a network server at no charge, through some standard or customary
|
||||||
|
means of facilitating copying of software. This Corresponding Source
|
||||||
|
shall include the Corresponding Source for any work covered by version 3
|
||||||
|
of the GNU General Public License that is incorporated pursuant to the
|
||||||
|
following paragraph.
|
||||||
|
.
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the work with which it is combined will remain governed by version
|
||||||
|
3 of the GNU General Public License.
|
||||||
|
.
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
.
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU Affero General Public License from time to time. Such new versions
|
||||||
|
will be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
.
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU Affero General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU Affero General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
.
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU Affero General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
.
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
.
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
.
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
.
|
||||||
|
16. Limitation of Liability.
|
||||||
|
.
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
.
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
.
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
.
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
.
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
.
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
.
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
.
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
.
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
.
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
.
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
.
|
||||||
|
If your software can interact with users remotely through a computer
|
||||||
|
network, you should also make sure that it provides a way for users to
|
||||||
|
get its source. For example, if your program is a web application, its
|
||||||
|
interface could display a "Source" link that leads users to an archive
|
||||||
|
of the code. There are many ways you could offer source, and different
|
||||||
|
solutions will be better for different programs; see section 13 for the
|
||||||
|
specific requirements.
|
||||||
|
.
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU AGPL, see
|
1
debian/docs
vendored
Normal file
1
debian/docs
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
client/zzz_evobackup
|
5
debian/gbp.conf
vendored
Normal file
5
debian/gbp.conf
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[DEFAULT]
|
||||||
|
debian-branch = debian
|
||||||
|
export-dir = build-area
|
||||||
|
upstream-branch = upstream/latest
|
||||||
|
upstream-vcs-tag = server/%(version%~%-)s
|
6
debian/install
vendored
Executable file
6
debian/install
vendored
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/usr/bin/dh-exec
|
||||||
|
server/bkctld usr/sbin
|
||||||
|
server/lib/* usr/lib/bkctld
|
||||||
|
server/tpl/* usr/share/bkctld
|
||||||
|
server/bkctld.service lib/systemd/system
|
||||||
|
server/bash_completion => /usr/share/bash_completion/bkctld
|
3
debian/manpages
vendored
Normal file
3
debian/manpages
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
server/bkctld.8
|
||||||
|
server/bkctld.conf.5
|
||||||
|
server/evobackup-incl.5
|
17
debian/postinst
vendored
Executable file
17
debian/postinst
vendored
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
legacy_incs_policy_tpl="/usr/local/share/bkctld/inc.tpl"
|
||||||
|
new_incs_policy_tpl="/usr/local/share/bkctld/incs_policy.tpl"
|
||||||
|
|
||||||
|
if [ -e "${legacy_incs_policy_tpl}" ]; then
|
||||||
|
if [ -e "${new_incs_policy_tpl}" ]; then
|
||||||
|
mv -f "${new_incs_policy_tpl}" "${new_incs_policy_tpl}.dpkg-new"
|
||||||
|
fi
|
||||||
|
mv -f "${legacy_incs_policy_tpl}" "${new_incs_policy_tpl}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#DEBHELPER#
|
||||||
|
|
||||||
|
exit 0
|
7
debian/rules
vendored
Executable file
7
debian/rules
vendored
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_installchangelogs:
|
||||||
|
dh_installchangelogs server/CHANGELOG.md
|
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
3.0 (quilt)
|
2
debian/source/options
vendored
Normal file
2
debian/source/options
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
tar-ignore = ".vagrant/"
|
||||||
|
tar-ignore = "build-area"
|
4
debian/watch
vendored
Normal file
4
debian/watch
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
version=4
|
||||||
|
options=uversionmangle=s/-?([^\d.]+)/~$1/i;tr/A-Z/a-z/ \
|
||||||
|
https://gitea.evolix.org/evolix/evobackup/tags \
|
||||||
|
.*/(\d.+)\.tar\.gz
|
|
@ -1,13 +1,8 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
This project does not follow semantic versioning.
|
|
||||||
The **major** part of the version is the year
|
|
||||||
The **minor** part changes is the month
|
|
||||||
The **patch** part changes is incremented if multiple releases happen the same month
|
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
@ -21,9 +16,6 @@ The **patch** part changes is incremented if multiple releases happen the same m
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Test presence of old config file before trying to delete it
|
|
||||||
* Use correct variable when detecting local sshrc template
|
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
## [22.11] - 2022-11-28
|
## [22.11] - 2022-11-28
|
||||||
|
|
|
@ -15,5 +15,3 @@
|
||||||
#LOGLEVEL=6
|
#LOGLEVEL=6
|
||||||
#NODE=''
|
#NODE=''
|
||||||
#ARCHIVESDIR='/backup/archives'
|
#ARCHIVESDIR='/backup/archives'
|
||||||
#WARNING=48
|
|
||||||
#CRITICAL=72
|
|
||||||
|
|
|
@ -11,17 +11,17 @@ Warning: `cp`-ing the files without `-n` or `-i` will replace existing files !
|
||||||
~~~
|
~~~
|
||||||
# git clone https://gitea.evolix.org/evolix/evobackup.git
|
# git clone https://gitea.evolix.org/evolix/evobackup.git
|
||||||
# cd evobackup
|
# cd evobackup
|
||||||
# cp server/bkctld /usr/local/sbin/
|
# cp bkctld /usr/local/sbin/
|
||||||
# mkdir -p /usr/local/lib/bkctld
|
# mkdir -p /usr/local/lib/bkctld
|
||||||
# cp server/lib/* /usr/local/lib/bkctld/
|
# cp lib/* /usr/local/lib/bkctld/
|
||||||
# mkdir -p /usr/local/share/bkctld
|
# mkdir -p /usr/local/share/bkctld
|
||||||
# cp server/tpl/* /usr/local/share/bkctld/
|
# cp tpl/* /usr/local/share/bkctld/
|
||||||
# cp server/bkctld.service /lib/systemd/system/
|
# cp bkctld.service /lib/systemd/system/
|
||||||
# mkdir -p /usr/local/share/doc/bkctld
|
# mkdir -p /usr/local/share/doc/bkctld
|
||||||
# cp client/zzz_evobackup /usr/local/share/doc/bkctld/
|
# cp zzz_evobackup /usr/local/share/doc/bkctld/
|
||||||
# mkdir -p /usr/local/share/bash_completion/
|
# mkdir -p /usr/local/share/bash_completion/
|
||||||
# cp server/bash_completion /usr/local/share/bash_completion/bkctld
|
# cp bash_completion /usr/local/share/bash_completion/bkctld
|
||||||
# cp server/bkctld.conf /etc/default/bkctld
|
# cp bkctld.conf /etc/default/bkctld
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
## Chroot dependencies
|
## Chroot dependencies
|
||||||
|
|
|
@ -49,7 +49,7 @@ fi
|
||||||
|
|
||||||
"${LIBDIR}/bkctld-is-on" "${jail_name}" && "${LIBDIR}/bkctld-stop" "${jail_name}"
|
"${LIBDIR}/bkctld-is-on" "${jail_name}" && "${LIBDIR}/bkctld-stop" "${jail_name}"
|
||||||
|
|
||||||
test -f "${CONFDIR}/${jail_name}" && rm -f "${CONFDIR}/${jail_name}"
|
rm -f "${CONFDIR}/${jail_name}"
|
||||||
rm -rf "$(jail_config_dir "${jail_name}")"
|
rm -rf "$(jail_config_dir "${jail_name}")"
|
||||||
|
|
||||||
btrfs_bin=$(command -v btrfs)
|
btrfs_bin=$(command -v btrfs)
|
||||||
|
|
|
@ -51,15 +51,15 @@ if dry_run; then
|
||||||
else
|
else
|
||||||
mv "${jail_path}" "${new_jail_path}"
|
mv "${jail_path}" "${new_jail_path}"
|
||||||
fi
|
fi
|
||||||
|
if dry_run; then
|
||||||
if [ -d "${incs_path}" ]; then
|
if [ -d "${incs_path}" ]; then
|
||||||
if dry_run; then
|
|
||||||
echo "[dry-run] rename ${incs_path} to ${new_incs_path}"
|
echo "[dry-run] rename ${incs_path} to ${new_incs_path}"
|
||||||
else
|
fi
|
||||||
|
else
|
||||||
|
if [ -d "${incs_path}" ]; then
|
||||||
mv "${incs_path}" "${new_incs_path}"
|
mv "${incs_path}" "${new_incs_path}"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -d "${jail_config_dir}" ]; then
|
if [ -d "${jail_config_dir}" ]; then
|
||||||
if dry_run; then
|
if dry_run; then
|
||||||
echo "[dry-run] rename ${jail_config_dir} to ${new_jail_config_dir}"
|
echo "[dry-run] rename ${jail_config_dir} to ${new_jail_config_dir}"
|
||||||
|
|
|
@ -136,7 +136,7 @@ is_btrfs() {
|
||||||
|
|
||||||
inode=$(stat --format=%i "${path}")
|
inode=$(stat --format=%i "${path}")
|
||||||
|
|
||||||
test "$inode" -eq 256
|
test $inode -eq 256
|
||||||
}
|
}
|
||||||
|
|
||||||
# Returns the list of jails found in the "jails" directory (default)
|
# Returns the list of jails found in the "jails" directory (default)
|
||||||
|
@ -252,15 +252,15 @@ relative_date() {
|
||||||
new_tmp_file() {
|
new_tmp_file() {
|
||||||
name=${1:-}
|
name=${1:-}
|
||||||
|
|
||||||
mktemp --tmpdir "bkctld.${$}.${name}.XXXXX"
|
mktemp --tmpdir=/tmp "bkctld.${$}.${name}.XXXXX"
|
||||||
}
|
}
|
||||||
new_tmp_dir() {
|
new_tmp_dir() {
|
||||||
name=${1:-}
|
name=${1:-}
|
||||||
|
|
||||||
mktemp --directory --tmpdir "bkctld.${$}.${name}.XXXXX"
|
mktemp --directory --tmpdir=/tmp "bkctld.${$}.${name}.XXXXX"
|
||||||
}
|
}
|
||||||
cleanup_tmp() {
|
cleanup_tmp() {
|
||||||
find "${TMPDIR:-/tmp}" -name "bkctld.${$}.*" -delete
|
find /tmp -name "bkctld.${$}.*" -delete
|
||||||
}
|
}
|
||||||
new_lock_file() {
|
new_lock_file() {
|
||||||
lock_file=${1:-}
|
lock_file=${1:-}
|
||||||
|
@ -294,7 +294,7 @@ setup_jail_chroot() {
|
||||||
[ -f "${LOCALTPLDIR}/passwd" ] && passwd="${LOCALTPLDIR}/passwd"
|
[ -f "${LOCALTPLDIR}/passwd" ] && passwd="${LOCALTPLDIR}/passwd"
|
||||||
[ -f "${LOCALTPLDIR}/shadow" ] && shadow="${LOCALTPLDIR}/shadow"
|
[ -f "${LOCALTPLDIR}/shadow" ] && shadow="${LOCALTPLDIR}/shadow"
|
||||||
[ -f "${LOCALTPLDIR}/group" ] && group="${LOCALTPLDIR}/group"
|
[ -f "${LOCALTPLDIR}/group" ] && group="${LOCALTPLDIR}/group"
|
||||||
[ -f "${LOCALTPLDIR}/sshrc" ] && sshrc="${LOCALTPLDIR}/sshrc"
|
[ -f "${LOCALTPLDIR}/sshrc" ] && group="${LOCALTPLDIR}/sshrc"
|
||||||
|
|
||||||
cd "${jail_path}" || error "${jail_name}: failed to change directory to ${jail_path}."
|
cd "${jail_path}" || error "${jail_name}: failed to change directory to ${jail_path}."
|
||||||
umask 077
|
umask 077
|
||||||
|
|
Loading…
Reference in a new issue