Compare commits
9 Commits
c5a1ac0551
...
aa8a68aa80
Author | SHA1 | Date | |
---|---|---|---|
aa8a68aa80 | |||
7fcc5afc64 | |||
e7e98d0b47 | |||
8479c378ee | |||
274e1e2e59 | |||
eb0bdaedbd | |||
99dc6e0abf | |||
e8ba6df102 | |||
ffd9bf3d39 |
@ -17,9 +17,8 @@ jobs:
|
||||
python-version: '3.12'
|
||||
cache: 'pip' # caching pip dependencies
|
||||
- run: pip install ruff
|
||||
- run: |
|
||||
- run: |
|
||||
ruff check .
|
||||
ruff fix .
|
||||
# - uses: stefanzweifel/git-auto-commit-action@v4
|
||||
# with:
|
||||
# commit_message: 'style fixes by ruff'
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,11 +2,13 @@
|
||||
*.swp
|
||||
*~
|
||||
*.pyc
|
||||
__pycache__/*
|
||||
/tasks.sqlite
|
||||
/tasks.sqlite-wal
|
||||
/srvinstallation
|
||||
/tasks.sqlite-shm
|
||||
.idea
|
||||
.ruff_cache/*
|
||||
/deb/builddir
|
||||
/deb/*.deb
|
||||
/lib
|
||||
|
13
.hadolint.yml
Normal file
13
.hadolint.yml
Normal file
@ -0,0 +1,13 @@
|
||||
DL3008failure-threshold: warning
|
||||
format: tty
|
||||
ignored:
|
||||
- DL3007
|
||||
override:
|
||||
error:
|
||||
- DL3015
|
||||
warning:
|
||||
- DL3015
|
||||
info:
|
||||
- DL3008
|
||||
style:
|
||||
- DL3015
|
7
.pre-commit-config.yaml
Normal file
7
.pre-commit-config.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -19,4 +19,4 @@
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
20
Dockerfile
Executable file
20
Dockerfile
Executable file
@ -0,0 +1,20 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /opt/tisbackup
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
COPY . /opt/tisbackup
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y rsync ssh cron \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& /usr/local/bin/python3.12 -m pip install --no-cache-dir -r requirements.txt \
|
||||
&& mkdir -p /var/spool/cron/crontabs \
|
||||
&& echo '59 03 * * * root /bin/bash /opt/tisbackup/backup.sh' > /etc/crontab \
|
||||
&& echo '' >> /etc/crontab \
|
||||
&& crontab /etc/crontab
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["/usr/local/bin/python3.12","/opt/tisbackup/tisbackup_gui.py"]
|
41
compose.yml
Executable file
41
compose.yml
Executable file
@ -0,0 +1,41 @@
|
||||
services:
|
||||
tisbackup_gui:
|
||||
container_name: tisbackup_gui
|
||||
image: "tisbackup:latest"
|
||||
build: .
|
||||
volumes:
|
||||
- ./config/:/etc/tis/
|
||||
- ./backup/:/backup/
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 9980:8080
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: 0.50
|
||||
memory: 512M
|
||||
reservations:
|
||||
cpus: 0.25
|
||||
memory: 128M
|
||||
tisbackup_cron:
|
||||
container_name: tisbackup_cron
|
||||
image: "tisbackup:latest"
|
||||
build: .
|
||||
volumes:
|
||||
- ./config/:/etc/tis/
|
||||
- ./ssh/:/config_ssh/
|
||||
- ./backup/:/backup/
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
restart: always
|
||||
command: "/bin/bash /opt/tisbackup/cron.sh"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: 0.50
|
||||
memory: 512M
|
||||
reservations:
|
||||
cpus: 0.25
|
||||
memory: 128M
|
13
config.py
Normal file → Executable file
13
config.py
Normal file → Executable file
@ -1,10 +1,9 @@
|
||||
import os,sys
|
||||
from huey.backends.sqlite_backend import SqliteQueue,SqliteDataStore
|
||||
from huey.api import Huey, create_task
|
||||
import os
|
||||
import sys
|
||||
|
||||
from huey.contrib.sql_huey import SqlHuey
|
||||
from huey.storage import SqliteStorage
|
||||
|
||||
tisbackup_root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__)))
|
||||
tasks_db = os.path.join(tisbackup_root_dir,"tasks.sqlite")
|
||||
queue = SqliteQueue('tisbackups',tasks_db)
|
||||
result_store = SqliteDataStore('tisbackups',tasks_db)
|
||||
huey = Huey(queue,result_store,always_eager=False)
|
||||
tasks_db = os.path.join(tisbackup_root_dir, "tasks.sqlite")
|
||||
huey = SqlHuey(name="tisbackups",filename=tasks_db,always_eager=False,storage_class=SqliteStorage)
|
||||
|
4
cron.sh
Executable file
4
cron.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
set -x
|
||||
echo "Starting cron job for TIS Backup"
|
||||
cron -f -l 2
|
@ -1,42 +1,42 @@
|
||||
## tisbackup for python3
|
||||
## tisbackup for python3
|
||||
|
||||
### Install
|
||||
|
||||
Once the deb package is created, one can use it to install tisbackup on a debian machine. The command is:
|
||||
Once the deb package is created, one can use it to install tisbackup on a debian machine. The command is:
|
||||
```
|
||||
apt install ./tis-tisbackup-1-2-0.170-deb11.deb
|
||||
```
|
||||
```
|
||||
Note that the version numbers might be different depending on the system you used to build the package.
|
||||
|
||||
Then create a directory where to backup the files from your machines. The default is ```/backup```.
|
||||
This can be changed in the configuration file ```/etc/tis/tisback-config.ini```. Usually this
|
||||
directory is mounted from a shared ressource on a NAS with great capacity.
|
||||
directory is mounted from a shared ressource on a NAS with great capacity.
|
||||
|
||||
Configure your backup jobs:
|
||||
Configure your backup jobs:
|
||||
```
|
||||
cd /etc/tis
|
||||
cp tisbackup-config.ini.sample tisbackup-config.ini
|
||||
vi tisbackup-config.ini
|
||||
```
|
||||
```
|
||||
|
||||
After this, one have to generate the public and private certificates, as root:
|
||||
After this, one have to generate the public and private certificates, as root:
|
||||
```
|
||||
cd
|
||||
ssh-keygen -t rsa -b 2048
|
||||
```
|
||||
```
|
||||
(press enter for each step)
|
||||
|
||||
Then propagate the public certificate on the machines targetted for backup:
|
||||
Then propagate the public certificate on the machines targetted for backup:
|
||||
```
|
||||
ssh-copy-id -i /root/.ssh/id_rsa.pub root@machine1
|
||||
ssh-copy-id -i /root/.ssh/id_rsa.pub root@machine2
|
||||
```
|
||||
```
|
||||
etc.
|
||||
|
||||
|
||||
Eventually modify ```/etc/cron.d/tisbackup``` for your needs.
|
||||
|
||||
Finalize the installation with:
|
||||
Finalize the installation with:
|
||||
```
|
||||
tisbackup -d backup
|
||||
systemctl start tisbackup_gui
|
||||
@ -52,5 +52,3 @@ The documentation for tisbackup is here: [tisbackup doc](https://tisbackup.readt
|
||||
dpkg --force-all --purge tis-tisbackup
|
||||
apt autoremove
|
||||
```
|
||||
|
||||
|
||||
|
@ -3,8 +3,7 @@ Version: 1-__VERSION__
|
||||
Section: base
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: unzip, ssh, rsync, python3-paramiko, python3-pyvmomi, python3-pexpect, python3-flask,python3-simplejson, python3-pip
|
||||
Depends: unzip, ssh, rsync, python3-paramiko, python3-pyvmomi, python3-pexpect, python3-flask,python3-simplejson, python3-pip
|
||||
Maintainer: Tranquil-IT <technique@tranquil.it>
|
||||
Description: TISBackup backup management
|
||||
Description: TISBackup backup management
|
||||
Homepage: https://www.tranquil.it
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VERSION_DEB=$(cat /etc/debian_version | cut -d "." -f 1)
|
||||
VERSION_DEB=$(cat /etc/debian_version | cut -d "." -f 1)
|
||||
VERSION_SHORT=$(cat ../tisbackup.py | grep "__version__" | cut -d "=" -f 2 | sed 's/"//g')
|
||||
GIT_COUNT=`git rev-list HEAD --count`
|
||||
GIT_COUNT=`git rev-list HEAD --count`
|
||||
VERSION="${VERSION_SHORT}.${GIT_COUNT}-deb${VERSION_DEB}"
|
||||
|
||||
rm -f *.deb
|
||||
@ -32,5 +32,3 @@ rsync -aP ../samples/tisbackup-config.ini.sample ./builddir/etc/tis/tisbackup-c
|
||||
chmod 755 ./builddir/opt/tisbackup/tisbackup.py
|
||||
|
||||
dpkg-deb --build builddir tis-tisbackup-1-${VERSION}.deb
|
||||
|
||||
|
||||
|
2
docs/_static/basic.css
vendored
2
docs/_static/basic.css
vendored
@ -765,4 +765,4 @@ div.math:hover a.headerlink {
|
||||
#top-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
docs/_static/css/badge_only.css
vendored
2
docs/_static/css/badge_only.css
vendored
@ -1 +1 @@
|
||||
.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}
|
||||
.fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}
|
||||
|
1418
docs/_static/css/fonts/fontawesome-webfont.svg
vendored
1418
docs/_static/css/fonts/fontawesome-webfont.svg
vendored
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 434 KiB After Width: | Height: | Size: 433 KiB |
2
docs/_static/css/theme.css
vendored
2
docs/_static/css/theme.css
vendored
File diff suppressed because one or more lines are too long
2
docs/_static/documentation_options.js
vendored
2
docs/_static/documentation_options.js
vendored
@ -9,4 +9,4 @@ var DOCUMENTATION_OPTIONS = {
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt',
|
||||
NAVIGATION_WITH_KEYS: false
|
||||
};
|
||||
};
|
||||
|
1418
docs/_static/fonts/fontawesome-webfont.svg
vendored
1418
docs/_static/fonts/fontawesome-webfont.svg
vendored
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 434 KiB After Width: | Height: | Size: 433 KiB |
2
docs/_static/js/badge_only.js
vendored
2
docs/_static/js/badge_only.js
vendored
@ -1 +1 @@
|
||||
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}});
|
||||
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}});
|
||||
|
2
docs/_static/js/html5shiv-printshiv.min.js
vendored
2
docs/_static/js/html5shiv-printshiv.min.js
vendored
@ -1,4 +1,4 @@
|
||||
/**
|
||||
* @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
|
||||
*/
|
||||
!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document);
|
||||
!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document);
|
||||
|
2
docs/_static/js/html5shiv.min.js
vendored
2
docs/_static/js/html5shiv.min.js
vendored
@ -1,4 +1,4 @@
|
||||
/**
|
||||
* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
|
||||
*/
|
||||
!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document);
|
||||
!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document);
|
||||
|
2
docs/_static/js/theme.js
vendored
2
docs/_static/js/theme.js
vendored
@ -1 +1 @@
|
||||
!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("<div class='wy-table-responsive'></div>"),n("table.docutils.footnote").wrap("<div class='wy-table-responsive footnote'></div>"),n("table.docutils.citation").wrap("<div class='wy-table-responsive citation'></div>"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n('<span class="toctree-expand"></span>'),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}t.length>0&&($(".wy-menu-vertical .current").removeClass("current"),t.addClass("current"),t.closest("li.toctree-l1").addClass("current"),t.closest("li.toctree-l1").parent().addClass("current"),t.closest("li.toctree-l1").addClass("current"),t.closest("li.toctree-l2").addClass("current"),t.closest("li.toctree-l3").addClass("current"),t.closest("li.toctree-l4").addClass("current"),t.closest("li.toctree-l5").addClass("current"),t[0].scrollIntoView())}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current"),e.siblings().find("li.current").removeClass("current"),e.find("> ul li.current").removeClass("current"),e.toggleClass("current")}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t<e.length&&!window.requestAnimationFrame;++t)window.requestAnimationFrame=window[e[t]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[e[t]+"CancelAnimationFrame"]||window[e[t]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(e,t){var i=(new Date).getTime(),o=Math.max(0,16-(i-n)),r=window.setTimeout((function(){e(i+o)}),o);return n=i+o,r}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(n){clearTimeout(n)})}()}).call(window)},function(n,e){n.exports=jQuery},function(n,e,t){}]);
|
||||
!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("<div class='wy-table-responsive'></div>"),n("table.docutils.footnote").wrap("<div class='wy-table-responsive footnote'></div>"),n("table.docutils.citation").wrap("<div class='wy-table-responsive citation'></div>"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n('<span class="toctree-expand"></span>'),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}t.length>0&&($(".wy-menu-vertical .current").removeClass("current"),t.addClass("current"),t.closest("li.toctree-l1").addClass("current"),t.closest("li.toctree-l1").parent().addClass("current"),t.closest("li.toctree-l1").addClass("current"),t.closest("li.toctree-l2").addClass("current"),t.closest("li.toctree-l3").addClass("current"),t.closest("li.toctree-l4").addClass("current"),t.closest("li.toctree-l5").addClass("current"),t[0].scrollIntoView())}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current"),e.siblings().find("li.current").removeClass("current"),e.find("> ul li.current").removeClass("current"),e.toggleClass("current")}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t<e.length&&!window.requestAnimationFrame;++t)window.requestAnimationFrame=window[e[t]+"RequestAnimationFrame"],window.cancelAnimationFrame=window[e[t]+"CancelAnimationFrame"]||window[e[t]+"CancelRequestAnimationFrame"];window.requestAnimationFrame||(window.requestAnimationFrame=function(e,t){var i=(new Date).getTime(),o=Math.max(0,16-(i-n)),r=window.setTimeout((function(){e(i+o)}),o);return n=i+o,r}),window.cancelAnimationFrame||(window.cancelAnimationFrame=function(n){clearTimeout(n)})}()}).call(window)},function(n,e){n.exports=jQuery},function(n,e,t){}]);
|
||||
|
4
docs/_static/language_data.js
vendored
4
docs/_static/language_data.js
vendored
@ -13,7 +13,7 @@
|
||||
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
|
||||
|
||||
|
||||
/* Non-minified version JS is _stemmer.js if file is provided */
|
||||
/* Non-minified version JS is _stemmer.js if file is provided */
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
@ -293,5 +293,3 @@ function splitQuery(query) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
2
docs/_static/pygments.css
vendored
2
docs/_static/pygments.css
vendored
@ -71,4 +71,4 @@ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left:
|
||||
.highlight .vg { color: #bb60d5 } /* Name.Variable.Global */
|
||||
.highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */
|
||||
.highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */
|
||||
.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
|
||||
.highlight .il { color: #208050 } /* Literal.Number.Integer.Long */
|
||||
|
@ -9,75 +9,75 @@
|
||||
<meta content="Documentation, TISBackup, configuration, backup jobs" name="keywords" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Configuring the backup jobs — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<title>Configuring the backup jobs — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/ribbon.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/theme_overrides.css" type="text/css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="_static/favicon.ico"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="_static/js/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
<script src="_static/jquery.js"></script>
|
||||
<script src="_static/underscore.js"></script>
|
||||
<script src="_static/doctools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||
|
||||
|
||||
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Using TISBackup" href="using_tisbackup.html" />
|
||||
<link rel="prev" title="Installing and configuring TISBackup on Debian" href="installing_tisbackup.html" />
|
||||
<link rel="prev" title="Installing and configuring TISBackup on Debian" href="installing_tisbackup.html" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
|
||||
|
||||
|
||||
<div class="wy-grid-for-nav">
|
||||
|
||||
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home"> TISBackup
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="version">
|
||||
1.8
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
@ -86,17 +86,17 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span class="caption-text">Presenting TISBackup</span></p>
|
||||
<ul class="current">
|
||||
<li class="toctree-l1"><a class="reference internal" href="presenting_tisbackup.html">Technical background for TISBackup</a></li>
|
||||
@ -125,29 +125,29 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="screenshots.html">Screenshots of TISBackup</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||
|
||||
|
||||
|
||||
<nav class="wy-nav-top" aria-label="top navigation">
|
||||
|
||||
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">TISBackup</a>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="wy-nav-content">
|
||||
|
||||
|
||||
<div class="rst-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -168,28 +168,28 @@
|
||||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||
|
||||
<ul class="wy-breadcrumbs">
|
||||
|
||||
|
||||
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
||||
|
||||
|
||||
<li>Configuring the backup jobs</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="_sources/configuring_tisbackup.rst.txt" rel="nofollow"> View page source</a>
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
|
||||
<section id="configuring-the-backup-jobs">
|
||||
<h1>Configuring the backup jobs<a class="headerlink" href="#configuring-the-backup-jobs" title="Permalink to this headline">¶</a></h1>
|
||||
<p id="configuring-backup-jobs">The configuration of the backups is done in an <em class="mimetype">.ini</em> file,
|
||||
@ -440,7 +440,7 @@ with read-write access only for it.</p>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
@ -456,14 +456,14 @@ with read-write access only for it.</p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
|
||||
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
@ -472,7 +472,7 @@ with read-write access only for it.</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
@ -480,11 +480,11 @@ with read-write access only for it.</p>
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-89790248-2"></script>
|
||||
@ -499,4 +499,4 @@ gtag('config', 'UA-89790248-2');
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -5,75 +5,75 @@
|
||||
<html class="writer-html5" lang="en" >
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Index — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Index — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/ribbon.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/theme_overrides.css" type="text/css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="_static/favicon.ico"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="_static/js/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
<script src="_static/jquery.js"></script>
|
||||
<script src="_static/underscore.js"></script>
|
||||
<script src="_static/doctools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||
|
||||
|
||||
|
||||
<link rel="index" title="Index" href="#" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
|
||||
|
||||
|
||||
<div class="wy-grid-for-nav">
|
||||
|
||||
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home"> TISBackup
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="version">
|
||||
1.8
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
@ -82,17 +82,17 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span class="caption-text">Presenting TISBackup</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="presenting_tisbackup.html">Technical background for TISBackup</a></li>
|
||||
@ -106,29 +106,29 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="screenshots.html">Screenshots of TISBackup</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||
|
||||
|
||||
|
||||
<nav class="wy-nav-top" aria-label="top navigation">
|
||||
|
||||
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">TISBackup</a>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="wy-nav-content">
|
||||
|
||||
|
||||
<div class="rst-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -149,36 +149,36 @@
|
||||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||
|
||||
<ul class="wy-breadcrumbs">
|
||||
|
||||
|
||||
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
||||
|
||||
|
||||
<li>Index</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
|
||||
|
||||
<h1 id="index">Index</h1>
|
||||
|
||||
<div class="genindex-jumpbox">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
|
||||
@ -190,14 +190,14 @@
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
|
||||
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
@ -206,7 +206,7 @@
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
@ -214,11 +214,11 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-89790248-2"></script>
|
||||
@ -233,4 +233,4 @@ gtag('config', 'UA-89790248-2');
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
140
docs/index.html
140
docs/index.html
@ -9,74 +9,74 @@
|
||||
<meta content="Documentation, TISBackup, introduction, welcome page, Welcome" name="keywords" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Presenting TISBackup — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<title>Presenting TISBackup — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/ribbon.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/theme_overrides.css" type="text/css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="_static/favicon.ico"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="_static/js/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
<script src="_static/jquery.js"></script>
|
||||
<script src="_static/underscore.js"></script>
|
||||
<script src="_static/doctools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||
|
||||
|
||||
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Technical background for TISBackup" href="presenting_tisbackup.html" />
|
||||
<link rel="next" title="Technical background for TISBackup" href="presenting_tisbackup.html" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
|
||||
|
||||
|
||||
<div class="wy-grid-for-nav">
|
||||
|
||||
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="#" class="icon icon-home"> TISBackup
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="version">
|
||||
1.8
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
@ -85,17 +85,17 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span class="caption-text">Presenting TISBackup</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="presenting_tisbackup.html">Technical background for TISBackup</a></li>
|
||||
@ -109,29 +109,29 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="screenshots.html">Screenshots of TISBackup</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||
|
||||
|
||||
|
||||
<nav class="wy-nav-top" aria-label="top navigation">
|
||||
|
||||
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="#">TISBackup</a>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="wy-nav-content">
|
||||
|
||||
|
||||
<div class="rst-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -152,28 +152,28 @@
|
||||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||
|
||||
<ul class="wy-breadcrumbs">
|
||||
|
||||
|
||||
<li><a href="#" class="icon icon-home"></a> »</li>
|
||||
|
||||
|
||||
<li>Presenting TISBackup</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="_sources/index.rst.txt" rel="nofollow"> View page source</a>
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
|
||||
<figure class="align-center">
|
||||
<a class="reference internal image-reference" href="_images/tisbackup_logo.png"><img alt="TISBackup Logo" src="_images/tisbackup_logo.png" style="width: 700.0px; height: 206.0px;" /></a>
|
||||
</figure>
|
||||
@ -275,7 +275,7 @@ if there is a problem during the backup.</p></li>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
@ -290,14 +290,14 @@ if there is a problem during the backup.</p></li>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
|
||||
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
@ -306,7 +306,7 @@ if there is a problem during the backup.</p></li>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
@ -314,11 +314,11 @@ if there is a problem during the backup.</p></li>
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-89790248-2"></script>
|
||||
@ -333,4 +333,4 @@ gtag('config', 'UA-89790248-2');
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -9,75 +9,75 @@
|
||||
<meta content="Documentation, TISBackup, installation, configuration" name="keywords" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Installing and configuring TISBackup on Debian — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<title>Installing and configuring TISBackup on Debian — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/ribbon.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/theme_overrides.css" type="text/css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="_static/favicon.ico"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="_static/js/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
<script src="_static/jquery.js"></script>
|
||||
<script src="_static/underscore.js"></script>
|
||||
<script src="_static/doctools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||
|
||||
|
||||
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Configuring the backup jobs" href="configuring_tisbackup.html" />
|
||||
<link rel="prev" title="Technical background for TISBackup" href="presenting_tisbackup.html" />
|
||||
<link rel="prev" title="Technical background for TISBackup" href="presenting_tisbackup.html" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
|
||||
|
||||
|
||||
<div class="wy-grid-for-nav">
|
||||
|
||||
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home"> TISBackup
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="version">
|
||||
1.8
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
@ -86,17 +86,17 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span class="caption-text">Presenting TISBackup</span></p>
|
||||
<ul class="current">
|
||||
<li class="toctree-l1"><a class="reference internal" href="presenting_tisbackup.html">Technical background for TISBackup</a></li>
|
||||
@ -123,29 +123,29 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="screenshots.html">Screenshots of TISBackup</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||
|
||||
|
||||
|
||||
<nav class="wy-nav-top" aria-label="top navigation">
|
||||
|
||||
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">TISBackup</a>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="wy-nav-content">
|
||||
|
||||
|
||||
<div class="rst-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -166,28 +166,28 @@
|
||||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||
|
||||
<ul class="wy-breadcrumbs">
|
||||
|
||||
|
||||
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
||||
|
||||
|
||||
<li>Installing and configuring TISBackup on Debian</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="_sources/installing_tisbackup.rst.txt" rel="nofollow"> View page source</a>
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
|
||||
<section id="installing-and-configuring-tisbackup-on-debian">
|
||||
<h1>Installing and configuring TISBackup on Debian<a class="headerlink" href="#installing-and-configuring-tisbackup-on-debian" title="Permalink to this headline">¶</a></h1>
|
||||
<section id="setting-up-the-gnu-linux-debian-server">
|
||||
@ -423,7 +423,7 @@ of your TISBackup server on port 8080.</p>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
@ -439,14 +439,14 @@ of your TISBackup server on port 8080.</p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
|
||||
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
@ -455,7 +455,7 @@ of your TISBackup server on port 8080.</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
@ -463,11 +463,11 @@ of your TISBackup server on port 8080.</p>
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-89790248-2"></script>
|
||||
@ -482,4 +482,4 @@ gtag('config', 'UA-89790248-2');
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -9,75 +9,75 @@
|
||||
<meta content="Documentation, TISBackup, technical background" name="keywords" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Technical background for TISBackup — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<title>Technical background for TISBackup — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/ribbon.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/theme_overrides.css" type="text/css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="_static/favicon.ico"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="_static/js/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
<script src="_static/jquery.js"></script>
|
||||
<script src="_static/underscore.js"></script>
|
||||
<script src="_static/doctools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||
|
||||
|
||||
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Installing and configuring TISBackup on Debian" href="installing_tisbackup.html" />
|
||||
<link rel="prev" title="Presenting TISBackup" href="index.html" />
|
||||
<link rel="prev" title="Presenting TISBackup" href="index.html" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
|
||||
|
||||
|
||||
<div class="wy-grid-for-nav">
|
||||
|
||||
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home"> TISBackup
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="version">
|
||||
1.8
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
@ -86,17 +86,17 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span class="caption-text">Presenting TISBackup</span></p>
|
||||
<ul class="current">
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Technical background for TISBackup</a><ul>
|
||||
@ -116,29 +116,29 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="screenshots.html">Screenshots of TISBackup</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||
|
||||
|
||||
|
||||
<nav class="wy-nav-top" aria-label="top navigation">
|
||||
|
||||
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">TISBackup</a>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="wy-nav-content">
|
||||
|
||||
|
||||
<div class="rst-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -159,28 +159,28 @@
|
||||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||
|
||||
<ul class="wy-breadcrumbs">
|
||||
|
||||
|
||||
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
||||
|
||||
|
||||
<li>Technical background for TISBackup</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="_sources/presenting_tisbackup.rst.txt" rel="nofollow"> View page source</a>
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
|
||||
<section id="technical-background-for-tisbackup">
|
||||
<h1>Technical background for TISBackup<a class="headerlink" href="#technical-background-for-tisbackup" title="Permalink to this headline">¶</a></h1>
|
||||
<p>The deduplication of this solution is based on the hardlinks
|
||||
@ -274,7 +274,7 @@ and <a class="reference internal" href="installing_tisbackup.html#base-debian-se
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
@ -290,14 +290,14 @@ and <a class="reference internal" href="installing_tisbackup.html#base-debian-se
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
|
||||
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
@ -306,7 +306,7 @@ and <a class="reference internal" href="installing_tisbackup.html#base-debian-se
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
@ -314,11 +314,11 @@ and <a class="reference internal" href="installing_tisbackup.html#base-debian-se
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-89790248-2"></script>
|
||||
@ -333,4 +333,4 @@ gtag('config', 'UA-89790248-2');
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -9,74 +9,74 @@
|
||||
<meta content="Documentation, TISBackup, screenshots" name="keywords" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Screenshots of TISBackup — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<title>Screenshots of TISBackup — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/ribbon.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/theme_overrides.css" type="text/css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="_static/favicon.ico"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="_static/js/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
<script src="_static/jquery.js"></script>
|
||||
<script src="_static/underscore.js"></script>
|
||||
<script src="_static/doctools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||
|
||||
|
||||
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="prev" title="Contacting Tranquil IT" href="tranquil-it-contacts.html" />
|
||||
<link rel="prev" title="Contacting Tranquil IT" href="tranquil-it-contacts.html" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
|
||||
|
||||
|
||||
<div class="wy-grid-for-nav">
|
||||
|
||||
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home"> TISBackup
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="version">
|
||||
1.8
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
@ -85,17 +85,17 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span class="caption-text">Presenting TISBackup</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="presenting_tisbackup.html">Technical background for TISBackup</a></li>
|
||||
@ -109,29 +109,29 @@
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Screenshots of TISBackup</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||
|
||||
|
||||
|
||||
<nav class="wy-nav-top" aria-label="top navigation">
|
||||
|
||||
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">TISBackup</a>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="wy-nav-content">
|
||||
|
||||
|
||||
<div class="rst-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -152,28 +152,28 @@
|
||||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||
|
||||
<ul class="wy-breadcrumbs">
|
||||
|
||||
|
||||
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
||||
|
||||
|
||||
<li>Screenshots of TISBackup</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="_sources/screenshots.rst.txt" rel="nofollow"> View page source</a>
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
|
||||
<section id="screenshots-of-tisbackup">
|
||||
<h1>Screenshots of TISBackup<a class="headerlink" href="#screenshots-of-tisbackup" title="Permalink to this headline">¶</a></h1>
|
||||
<figure class="align-center" id="id1">
|
||||
@ -222,7 +222,7 @@
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
@ -237,14 +237,14 @@
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
|
||||
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
@ -253,7 +253,7 @@
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
@ -261,11 +261,11 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-89790248-2"></script>
|
||||
@ -280,4 +280,4 @@ gtag('config', 'UA-89790248-2');
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
148
docs/search.html
148
docs/search.html
@ -4,78 +4,78 @@
|
||||
<html class="writer-html5" lang="en" >
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Search — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Search — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/ribbon.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/theme_overrides.css" type="text/css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="_static/favicon.ico"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="_static/js/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
<script src="_static/jquery.js"></script>
|
||||
<script src="_static/underscore.js"></script>
|
||||
<script src="_static/doctools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/searchtools.js"></script>
|
||||
<script type="text/javascript" src="_static/language_data.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="#" />
|
||||
<link rel="search" title="Search" href="#" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
|
||||
|
||||
|
||||
<div class="wy-grid-for-nav">
|
||||
|
||||
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home"> TISBackup
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="version">
|
||||
1.8
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="#" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
@ -84,17 +84,17 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span class="caption-text">Presenting TISBackup</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="presenting_tisbackup.html">Technical background for TISBackup</a></li>
|
||||
@ -108,29 +108,29 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="screenshots.html">Screenshots of TISBackup</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||
|
||||
|
||||
|
||||
<nav class="wy-nav-top" aria-label="top navigation">
|
||||
|
||||
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">TISBackup</a>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="wy-nav-content">
|
||||
|
||||
|
||||
<div class="rst-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -151,24 +151,24 @@
|
||||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||
|
||||
<ul class="wy-breadcrumbs">
|
||||
|
||||
|
||||
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
||||
|
||||
|
||||
<li>Search</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
|
||||
<noscript>
|
||||
<div id="fallback" class="admonition warning">
|
||||
<p class="last">
|
||||
@ -177,13 +177,13 @@
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
|
||||
|
||||
<div id="search-results">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
|
||||
@ -195,14 +195,14 @@
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
|
||||
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
@ -211,7 +211,7 @@
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
@ -219,17 +219,17 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function() { Search.loadIndex("searchindex.js"); });
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/javascript" id="searchindexloader"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-89790248-2"></script>
|
||||
@ -245,4 +245,4 @@ gtag('config', 'UA-89790248-2');
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
File diff suppressed because one or more lines are too long
@ -9,75 +9,75 @@
|
||||
<meta content="TISBackup, documentation, website, editor, Twitter, official website" name="keywords" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Contacting Tranquil IT — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<title>Contacting Tranquil IT — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/ribbon.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/theme_overrides.css" type="text/css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="_static/favicon.ico"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="_static/js/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
<script src="_static/jquery.js"></script>
|
||||
<script src="_static/underscore.js"></script>
|
||||
<script src="_static/doctools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||
|
||||
|
||||
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Screenshots of TISBackup" href="screenshots.html" />
|
||||
<link rel="prev" title="Using TISBackup" href="using_tisbackup.html" />
|
||||
<link rel="prev" title="Using TISBackup" href="using_tisbackup.html" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
|
||||
|
||||
|
||||
<div class="wy-grid-for-nav">
|
||||
|
||||
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home"> TISBackup
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="version">
|
||||
1.8
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
@ -86,17 +86,17 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span class="caption-text">Presenting TISBackup</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="presenting_tisbackup.html">Technical background for TISBackup</a></li>
|
||||
@ -110,29 +110,29 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="screenshots.html">Screenshots of TISBackup</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||
|
||||
|
||||
|
||||
<nav class="wy-nav-top" aria-label="top navigation">
|
||||
|
||||
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">TISBackup</a>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="wy-nav-content">
|
||||
|
||||
|
||||
<div class="rst-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -153,28 +153,28 @@
|
||||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||
|
||||
<ul class="wy-breadcrumbs">
|
||||
|
||||
|
||||
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
||||
|
||||
|
||||
<li>Contacting Tranquil IT</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="_sources/tranquil-it-contacts.rst.txt" rel="nofollow"> View page source</a>
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
|
||||
<section id="contacting-tranquil-it">
|
||||
<span id="contact-tranquil-it"></span><h1>Contacting Tranquil IT<a class="headerlink" href="#contacting-tranquil-it" title="Permalink to this headline">¶</a></h1>
|
||||
<ul class="simple">
|
||||
@ -185,7 +185,7 @@
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
@ -201,14 +201,14 @@
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
|
||||
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
@ -217,7 +217,7 @@
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
@ -225,11 +225,11 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-89790248-2"></script>
|
||||
@ -244,4 +244,4 @@ gtag('config', 'UA-89790248-2');
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -9,75 +9,75 @@
|
||||
<meta content="Documentation, TISBackup, usage, options, exporting" name="keywords" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Using TISBackup — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<title>Using TISBackup — TISBackup 1.8.2 documentation</title>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/custom.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/css/ribbon.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/theme_overrides.css" type="text/css" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<link rel="shortcut icon" href="_static/favicon.ico"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="_static/js/html5shiv.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
<script src="_static/jquery.js"></script>
|
||||
<script src="_static/underscore.js"></script>
|
||||
<script src="_static/doctools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
|
||||
|
||||
<script type="text/javascript" src="_static/js/theme.js"></script>
|
||||
|
||||
|
||||
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Contacting Tranquil IT" href="tranquil-it-contacts.html" />
|
||||
<link rel="prev" title="Configuring the backup jobs" href="configuring_tisbackup.html" />
|
||||
<link rel="prev" title="Configuring the backup jobs" href="configuring_tisbackup.html" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
|
||||
|
||||
|
||||
<div class="wy-grid-for-nav">
|
||||
|
||||
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home"> TISBackup
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="version">
|
||||
1.8
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" />
|
||||
@ -86,17 +86,17 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p><span class="caption-text">Presenting TISBackup</span></p>
|
||||
<ul class="current">
|
||||
<li class="toctree-l1"><a class="reference internal" href="presenting_tisbackup.html">Technical background for TISBackup</a></li>
|
||||
@ -113,29 +113,29 @@
|
||||
<li class="toctree-l1"><a class="reference internal" href="screenshots.html">Screenshots of TISBackup</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
|
||||
|
||||
|
||||
|
||||
<nav class="wy-nav-top" aria-label="top navigation">
|
||||
|
||||
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">TISBackup</a>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="wy-nav-content">
|
||||
|
||||
|
||||
<div class="rst-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -156,28 +156,28 @@
|
||||
<div role="navigation" aria-label="breadcrumbs navigation">
|
||||
|
||||
<ul class="wy-breadcrumbs">
|
||||
|
||||
|
||||
<li><a href="index.html" class="icon icon-home"></a> »</li>
|
||||
|
||||
|
||||
<li>Using TISBackup</li>
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="_sources/using_tisbackup.rst.txt" rel="nofollow"> View page source</a>
|
||||
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
|
||||
<section id="using-tisbackup">
|
||||
<h1>Using TISBackup<a class="headerlink" href="#using-tisbackup" title="Permalink to this headline">¶</a></h1>
|
||||
<p id="id1">As seen in the <a class="reference internal" href="installing_tisbackup.html#install-tisbackup-debian"><span class="std std-ref">section on installing TISbackup</span></a>,
|
||||
@ -291,7 +291,7 @@ e2label /dev/xvdc1 tisbackup
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
|
||||
@ -307,14 +307,14 @@ e2label /dev/xvdc1 tisbackup
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
|
||||
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
@ -323,7 +323,7 @@ e2label /dev/xvdc1 tisbackup
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(function () {
|
||||
@ -331,11 +331,11 @@ e2label /dev/xvdc1 tisbackup
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-89790248-2"></script>
|
||||
@ -350,4 +350,4 @@ gtag('config', 'UA-89790248-2');
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
7
entrypoint.sh
Executable file
7
entrypoint.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
env >> /etc/environment
|
||||
|
||||
# execute CMD
|
||||
echo "$@"
|
||||
exec "$@"
|
@ -55,11 +55,12 @@
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
import gettext
|
||||
import six.moves.xmlrpc_client as xmlrpclib
|
||||
import six.moves.http_client as httplib
|
||||
import socket
|
||||
import sys
|
||||
|
||||
import six.moves.http_client as httplib
|
||||
import six.moves.xmlrpc_client as xmlrpclib
|
||||
|
||||
translation = gettext.translation('xen-xm', fallback = True)
|
||||
|
||||
API_VERSION_1_1 = '1.1'
|
||||
|
@ -15,4 +15,3 @@
|
||||
# along with TISBackup. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
sys.stderr = open('/dev/null') # Silence silly warnings from paramiko
|
||||
import paramiko
|
||||
@ -32,6 +33,7 @@ sys.stderr = sys.__stderr__
|
||||
|
||||
from libtisbackup.common import *
|
||||
|
||||
|
||||
class backup_mysql(backup_generic):
|
||||
"""Backup a mysql database as gzipped sql file through ssh"""
|
||||
type = 'mysql+ssh'
|
||||
|
@ -18,16 +18,17 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import datetime
|
||||
import os
|
||||
|
||||
from .common import *
|
||||
|
||||
|
||||
class backup_null(backup_generic):
|
||||
"""Null backup to register servers which don't need any backups
|
||||
"""Null backup to register servers which don't need any backups
|
||||
but we still want to know they are taken in account"""
|
||||
type = 'null'
|
||||
|
||||
|
||||
required_params = ['type','server_name','backup_name']
|
||||
optional_params = []
|
||||
|
||||
@ -43,9 +44,8 @@ class backup_null(backup_generic):
|
||||
return {}
|
||||
def checknagios(self,maxage_hours=30):
|
||||
return (nagiosStateOk,"No backups needs to be performed")
|
||||
|
||||
|
||||
register_driver(backup_null)
|
||||
|
||||
if __name__=='__main__':
|
||||
pass
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
import sys
|
||||
|
||||
try:
|
||||
sys.stderr = open('/dev/null') # Silence silly warnings from paramiko
|
||||
import paramiko
|
||||
@ -27,15 +28,17 @@ except ImportError as e:
|
||||
|
||||
sys.stderr = sys.__stderr__
|
||||
|
||||
import datetime
|
||||
import base64
|
||||
import datetime
|
||||
import os
|
||||
from libtisbackup.common import *
|
||||
import re
|
||||
|
||||
from libtisbackup.common import *
|
||||
|
||||
|
||||
class backup_oracle(backup_generic):
|
||||
"""Backup a oracle database as zipped file through ssh"""
|
||||
type = 'oracle+ssh'
|
||||
type = 'oracle+ssh'
|
||||
required_params = backup_generic.required_params + ['db_name','private_key', 'userid']
|
||||
optional_params = ['username', 'remote_backup_dir', 'ignore_error_oracle_code']
|
||||
db_name=''
|
||||
@ -44,7 +47,7 @@ class backup_oracle(backup_generic):
|
||||
ignore_error_oracle_code = [ ]
|
||||
|
||||
def do_backup(self,stats):
|
||||
|
||||
|
||||
self.logger.debug('[%s] Connecting to %s with user %s and key %s',self.backup_name,self.server_name,self.username,self.private_key)
|
||||
try:
|
||||
mykey = paramiko.RSAKey.from_private_key_file(self.private_key)
|
||||
@ -54,8 +57,8 @@ class backup_oracle(backup_generic):
|
||||
|
||||
self.ssh = paramiko.SSHClient()
|
||||
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
self.ssh.connect(self.server_name,username=self.username,pkey = mykey,port=self.ssh_port)
|
||||
|
||||
self.ssh.connect(self.server_name,username=self.username,pkey = mykey,port=self.ssh_port)
|
||||
|
||||
t = datetime.datetime.now()
|
||||
self.backup_start_date = t.strftime('%Y%m%d-%Hh%Mm%S')
|
||||
dumpfile= self.remote_backup_dir + '/' + self.db_name + '_' + self.backup_start_date+'.dmp'
|
||||
@ -68,10 +71,10 @@ class backup_oracle(backup_generic):
|
||||
else:
|
||||
print(('mkdir "%s"' % self.dest_dir))
|
||||
else:
|
||||
raise Exception('backup destination directory already exists : %s' % self.dest_dir)
|
||||
raise Exception('backup destination directory already exists : %s' % self.dest_dir)
|
||||
# dump db
|
||||
stats['status']='Dumping'
|
||||
cmd = "exp '%s' file='%s' grants=y log='%s'"% (self.userid,dumpfile, dumplog)
|
||||
cmd = "exp '%s' file='%s' grants=y log='%s'"% (self.userid,dumpfile, dumplog)
|
||||
self.logger.debug('[%s] Dump DB : %s',self.backup_name,cmd)
|
||||
if not self.dry_run:
|
||||
(error_code,output) = ssh_exec(cmd,ssh=self.ssh)
|
||||
@ -94,7 +97,7 @@ class backup_oracle(backup_generic):
|
||||
|
||||
# zip the file
|
||||
stats['status']='Zipping'
|
||||
cmd = 'gzip %s' % dumpfile
|
||||
cmd = 'gzip %s' % dumpfile
|
||||
self.logger.debug('[%s] Compress backup : %s',self.backup_name,cmd)
|
||||
if not self.dry_run:
|
||||
(error_code,output) = ssh_exec(cmd,ssh=self.ssh)
|
||||
@ -117,7 +120,7 @@ class backup_oracle(backup_generic):
|
||||
stats['total_files_count']=1
|
||||
stats['written_files_count']=1
|
||||
stats['total_bytes']=os.stat(localpath).st_size
|
||||
stats['written_bytes']=os.stat(localpath).st_size
|
||||
stats['written_bytes']=os.stat(localpath).st_size
|
||||
stats['log']='gzip dump of DB %s:%s (%d bytes) to %s' % (self.server_name,self.db_name, stats['written_bytes'], localpath)
|
||||
stats['backup_location'] = self.dest_dir
|
||||
stats['status']='RMTemp'
|
||||
@ -131,7 +134,7 @@ class backup_oracle(backup_generic):
|
||||
|
||||
filelist = os.listdir(self.backup_dir)
|
||||
filelist.sort()
|
||||
p = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}$')
|
||||
p = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}$')
|
||||
for item in filelist:
|
||||
if p.match(item):
|
||||
dir_name = os.path.join(self.backup_dir,item)
|
||||
|
@ -18,6 +18,7 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
import sys
|
||||
|
||||
try:
|
||||
sys.stderr = open('/dev/null') # Silence silly warnings from paramiko
|
||||
import paramiko
|
||||
@ -29,6 +30,7 @@ sys.stderr = sys.__stderr__
|
||||
|
||||
from .common import *
|
||||
|
||||
|
||||
class backup_pgsql(backup_generic):
|
||||
"""Backup a postgresql database as gzipped sql file through ssh"""
|
||||
type = 'pgsql+ssh'
|
||||
|
@ -18,19 +18,19 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import datetime
|
||||
from libtisbackup.common import *
|
||||
import time
|
||||
import logging
|
||||
import re
|
||||
import os
|
||||
import os.path
|
||||
import datetime
|
||||
import re
|
||||
import time
|
||||
|
||||
from libtisbackup.common import *
|
||||
|
||||
|
||||
class backup_rsync(backup_generic):
|
||||
"""Backup a directory on remote server with rsync and rsync protocol (requires running remote rsync daemon)"""
|
||||
type = 'rsync'
|
||||
type = 'rsync'
|
||||
required_params = backup_generic.required_params + ['remote_user','remote_dir','rsync_module','password_file']
|
||||
optional_params = backup_generic.optional_params + ['compressionlevel','compression','bwlimit','exclude_list','protect_args','overload_args']
|
||||
|
||||
@ -46,7 +46,7 @@ class backup_rsync(backup_generic):
|
||||
overload_args = None
|
||||
compressionlevel = 0
|
||||
|
||||
|
||||
|
||||
|
||||
def read_config(self,iniconf):
|
||||
assert(isinstance(iniconf,ConfigParser))
|
||||
@ -54,7 +54,7 @@ class backup_rsync(backup_generic):
|
||||
if not self.bwlimit and iniconf.has_option('global','bw_limit'):
|
||||
self.bwlimit = iniconf.getint('global','bw_limit')
|
||||
if not self.compressionlevel and iniconf.has_option('global','compression_level'):
|
||||
self.compressionlevel = iniconf.getint('global','compression_level')
|
||||
self.compressionlevel = iniconf.getint('global','compression_level')
|
||||
|
||||
def do_backup(self,stats):
|
||||
if not self.set_lock():
|
||||
@ -216,7 +216,7 @@ class backup_rsync(backup_generic):
|
||||
raise
|
||||
|
||||
|
||||
finally:
|
||||
finally:
|
||||
self.remove_lock()
|
||||
|
||||
def get_latest_backup(self,current):
|
||||
@ -225,8 +225,8 @@ class backup_rsync(backup_generic):
|
||||
filelist.sort()
|
||||
filelist.reverse()
|
||||
full = ''
|
||||
r_full = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}$')
|
||||
r_partial = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}.rsync$')
|
||||
r_full = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}$')
|
||||
r_partial = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}.rsync$')
|
||||
# we take all latest partials younger than the latest full and the latest full
|
||||
for item in filelist:
|
||||
if r_partial.match(item) and item<current:
|
||||
@ -245,7 +245,7 @@ class backup_rsync(backup_generic):
|
||||
|
||||
filelist = os.listdir(self.backup_dir)
|
||||
filelist.sort()
|
||||
p = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}$')
|
||||
p = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}$')
|
||||
for item in filelist:
|
||||
if p.match(item):
|
||||
dir_name = os.path.join(self.backup_dir,item)
|
||||
@ -288,7 +288,7 @@ class backup_rsync(backup_generic):
|
||||
return False
|
||||
else:
|
||||
self.logger.info("[" + self.backup_name + "] incorrrect lock file : no pid line")
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
def set_lock(self):
|
||||
@ -317,7 +317,7 @@ class backup_rsync(backup_generic):
|
||||
|
||||
class backup_rsync_ssh(backup_rsync):
|
||||
"""Backup a directory on remote server with rsync and ssh protocol (requires rsync software on remote host)"""
|
||||
type = 'rsync+ssh'
|
||||
type = 'rsync+ssh'
|
||||
required_params = backup_generic.required_params + ['remote_user','remote_dir','private_key']
|
||||
optional_params = backup_generic.optional_params + ['compression','bwlimit','ssh_port','exclude_list','protect_args','overload_args', 'cipher_spec']
|
||||
cipher_spec = ''
|
||||
@ -341,4 +341,3 @@ if __name__=='__main__':
|
||||
b.read_config(cp)
|
||||
b.process_backup()
|
||||
print((b.checknagios()))
|
||||
|
||||
|
@ -18,20 +18,19 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import datetime
|
||||
from .common import *
|
||||
import time
|
||||
import logging
|
||||
import re
|
||||
import os
|
||||
import os.path
|
||||
import datetime
|
||||
import re
|
||||
import time
|
||||
|
||||
from .common import *
|
||||
|
||||
|
||||
class backup_rsync_btrfs(backup_generic):
|
||||
"""Backup a directory on remote server with rsync and btrfs protocol (requires running remote rsync daemon)"""
|
||||
type = 'rsync+btrfs'
|
||||
type = 'rsync+btrfs'
|
||||
required_params = backup_generic.required_params + ['remote_user','remote_dir','rsync_module','password_file']
|
||||
optional_params = backup_generic.optional_params + ['compressionlevel','compression','bwlimit','exclude_list','protect_args','overload_args']
|
||||
|
||||
@ -47,7 +46,7 @@ class backup_rsync_btrfs(backup_generic):
|
||||
overload_args = None
|
||||
compressionlevel = 0
|
||||
|
||||
|
||||
|
||||
|
||||
def read_config(self,iniconf):
|
||||
assert(isinstance(iniconf,ConfigParser))
|
||||
@ -55,7 +54,7 @@ class backup_rsync_btrfs(backup_generic):
|
||||
if not self.bwlimit and iniconf.has_option('global','bw_limit'):
|
||||
self.bwlimit = iniconf.getint('global','bw_limit')
|
||||
if not self.compressionlevel and iniconf.has_option('global','compression_level'):
|
||||
self.compressionlevel = iniconf.getint('global','compression_level')
|
||||
self.compressionlevel = iniconf.getint('global','compression_level')
|
||||
|
||||
def do_backup(self,stats):
|
||||
if not self.set_lock():
|
||||
@ -74,7 +73,7 @@ class backup_rsync_btrfs(backup_generic):
|
||||
returncode = process.returncode
|
||||
if (returncode != 0):
|
||||
self.logger.error("[" + self.backup_name + "] shell program exited with error code: %s"%log)
|
||||
raise Exception("[" + self.backup_name + "] shell program exited with error code " + str(returncode), cmd)
|
||||
raise Exception("[" + self.backup_name + "] shell program exited with error code " + str(returncode), cmd)
|
||||
else:
|
||||
self.logger.info("[" + self.backup_name + "] create btrs volume: %s"%dest_dir)
|
||||
else:
|
||||
@ -235,7 +234,7 @@ class backup_rsync_btrfs(backup_generic):
|
||||
raise
|
||||
|
||||
|
||||
finally:
|
||||
finally:
|
||||
self.remove_lock()
|
||||
|
||||
def get_latest_backup(self,current):
|
||||
@ -244,8 +243,8 @@ class backup_rsync_btrfs(backup_generic):
|
||||
filelist.sort()
|
||||
filelist.reverse()
|
||||
full = ''
|
||||
r_full = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}$')
|
||||
r_partial = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}.rsync$')
|
||||
r_full = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}$')
|
||||
r_partial = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}.rsync$')
|
||||
# we take all latest partials younger than the latest full and the latest full
|
||||
for item in filelist:
|
||||
if r_partial.match(item) and item<current:
|
||||
@ -263,7 +262,7 @@ class backup_rsync_btrfs(backup_generic):
|
||||
|
||||
filelist = os.listdir(self.backup_dir)
|
||||
filelist.sort()
|
||||
p = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}$')
|
||||
p = re.compile('^\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}$')
|
||||
for item in filelist:
|
||||
if p.match(item):
|
||||
dir_name = os.path.join(self.backup_dir,item)
|
||||
@ -306,7 +305,7 @@ class backup_rsync_btrfs(backup_generic):
|
||||
return False
|
||||
else:
|
||||
self.logger.info("[" + self.backup_name + "] incorrrect lock file : no pid line")
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
def set_lock(self):
|
||||
@ -335,7 +334,7 @@ class backup_rsync_btrfs(backup_generic):
|
||||
|
||||
class backup_rsync__btrfs_ssh(backup_rsync_btrfs):
|
||||
"""Backup a directory on remote server with rsync,ssh and btrfs protocol (requires rsync software on remote host)"""
|
||||
type = 'rsync+btrfs+ssh'
|
||||
type = 'rsync+btrfs+ssh'
|
||||
required_params = backup_generic.required_params + ['remote_user','remote_dir','private_key']
|
||||
optional_params = backup_generic.optional_params + ['compression','bwlimit','ssh_port','exclude_list','protect_args','overload_args','cipher_spec']
|
||||
cipher_spec = ''
|
||||
@ -359,4 +358,3 @@ if __name__=='__main__':
|
||||
b.read_config(cp)
|
||||
b.process_backup()
|
||||
print((b.checknagios()))
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
sys.stderr = open('/dev/null') # Silence silly warnings from paramiko
|
||||
import paramiko
|
||||
@ -32,6 +33,7 @@ sys.stderr = sys.__stderr__
|
||||
|
||||
from .common import *
|
||||
|
||||
|
||||
class backup_samba4(backup_generic):
|
||||
"""Backup a samba4 databases as gzipped tdbs file through ssh"""
|
||||
type = 'samba4'
|
||||
@ -163,4 +165,4 @@ class backup_samba4(backup_generic):
|
||||
self.logger.info('Skipping %s, already registered',dir_name)
|
||||
|
||||
|
||||
register_driver(backup_samba4)
|
||||
register_driver(backup_samba4)
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
sys.stderr = open('/dev/null') # Silence silly warnings from paramiko
|
||||
import paramiko
|
||||
@ -30,11 +31,13 @@ except ImportError as e:
|
||||
|
||||
sys.stderr = sys.__stderr__
|
||||
|
||||
import datetime
|
||||
import base64
|
||||
import datetime
|
||||
import os
|
||||
|
||||
from .common import *
|
||||
|
||||
|
||||
class backup_sqlserver(backup_generic):
|
||||
"""Backup a SQLSERVER database as gzipped sql file through ssh"""
|
||||
type = 'sqlserver+ssh'
|
||||
|
@ -18,23 +18,26 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import datetime
|
||||
from .common import *
|
||||
from . import XenAPI
|
||||
import time
|
||||
import logging
|
||||
import re
|
||||
import os.path
|
||||
import datetime
|
||||
import select
|
||||
import urllib.request, urllib.error, urllib.parse, urllib.request, urllib.parse, urllib.error
|
||||
import base64
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import select
|
||||
import socket
|
||||
import requests
|
||||
import pexpect
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from stat import *
|
||||
|
||||
import pexpect
|
||||
import requests
|
||||
|
||||
from . import XenAPI
|
||||
from .common import *
|
||||
|
||||
|
||||
class backup_switch(backup_generic):
|
||||
"""Backup a startup-config on a switch"""
|
||||
@ -149,7 +152,7 @@ class backup_switch(backup_generic):
|
||||
else:
|
||||
child.sendline(self.switch_user)
|
||||
child.expect(".*#")
|
||||
|
||||
|
||||
child.sendline( "terminal datadump")
|
||||
child.expect("#")
|
||||
child.sendline( "show startup-config")
|
||||
@ -259,4 +262,3 @@ if __name__=='__main__':
|
||||
cp.read('/opt/tisbackup/configtest.ini')
|
||||
b = backup_xva()
|
||||
b.read_config(cp)
|
||||
|
||||
|
@ -18,25 +18,25 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
from .common import *
|
||||
import pyVmomi
|
||||
from pyVmomi import vim
|
||||
from pyVmomi import vmodl
|
||||
from pyVim.connect import SmartConnect, Disconnect
|
||||
|
||||
from datetime import datetime, date, timedelta
|
||||
import atexit
|
||||
import getpass
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
import pyVmomi
|
||||
import requests
|
||||
from pyVim.connect import Disconnect, SmartConnect
|
||||
from pyVmomi import vim, vmodl
|
||||
# Disable HTTPS verification warnings.
|
||||
from requests.packages import urllib3
|
||||
|
||||
from .common import *
|
||||
|
||||
urllib3.disable_warnings()
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import tarfile
|
||||
import re
|
||||
import time
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from stat import *
|
||||
|
||||
|
||||
@ -59,15 +59,15 @@ class backup_vmdk(backup_generic):
|
||||
cookie_text = " " + cookie_value + "; $" + cookie_path
|
||||
# Make a cookie
|
||||
cookie = dict()
|
||||
cookie[cookie_name] = cookie_text
|
||||
cookie[cookie_name] = cookie_text
|
||||
return cookie
|
||||
|
||||
|
||||
def download_file(self,url, local_filename, cookie, headers):
|
||||
r = requests.get(url, stream=True, headers=headers,cookies=cookie,verify=False)
|
||||
with open(local_filename, 'wb') as f:
|
||||
for chunk in r.iter_content(chunk_size=1024*1024*64):
|
||||
if chunk:
|
||||
for chunk in r.iter_content(chunk_size=1024*1024*64):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
f.flush()
|
||||
return local_filename
|
||||
@ -78,7 +78,7 @@ class backup_vmdk(backup_generic):
|
||||
try:
|
||||
infos = HttpNfcLease.info
|
||||
device_urls = infos.deviceUrl
|
||||
vmdks = []
|
||||
vmdks = []
|
||||
for device_url in device_urls:
|
||||
deviceId = device_url.key
|
||||
deviceUrlStr = device_url.url
|
||||
@ -89,7 +89,7 @@ class backup_vmdk(backup_generic):
|
||||
cookie = self.make_compatible_cookie(si._stub.cookie)
|
||||
headers = {'Content-Type': 'application/octet-stream'}
|
||||
self.logger.debug("[%s] exporting disk: %s" %(self.server_name,diskFileName))
|
||||
|
||||
|
||||
self.download_file(diskUrlStr, diskFileName, cookie, headers)
|
||||
vmdks.append({"filename":diskFileName,"id":deviceId})
|
||||
finally:
|
||||
@ -99,8 +99,8 @@ class backup_vmdk(backup_generic):
|
||||
|
||||
def create_ovf(self,vm,vmdks):
|
||||
ovfDescParams = vim.OvfManager.CreateDescriptorParams()
|
||||
ovf = si.content.ovfManager.CreateDescriptor(vm, ovfDescParams)
|
||||
root = ET.fromstring(ovf.ovfDescriptor)
|
||||
ovf = si.content.ovfManager.CreateDescriptor(vm, ovfDescParams)
|
||||
root = ET.fromstring(ovf.ovfDescriptor)
|
||||
new_id = list(root[0][1].attrib.values())[0][1:3]
|
||||
ovfFiles = []
|
||||
for vmdk in vmdks:
|
||||
@ -109,14 +109,14 @@ class backup_vmdk(backup_generic):
|
||||
ovfFiles.append(vim.OvfManager.OvfFile(size=os.path.getsize(vmdk['filename']), path=vmdk['filename'], deviceId=id))
|
||||
|
||||
ovfDescParams = vim.OvfManager.CreateDescriptorParams()
|
||||
ovfDescParams.ovfFiles = ovfFiles;
|
||||
ovfDescParams.ovfFiles = ovfFiles;
|
||||
|
||||
ovf = si.content.ovfManager.CreateDescriptor(vm, ovfDescParams)
|
||||
ovf = si.content.ovfManager.CreateDescriptor(vm, ovfDescParams)
|
||||
ovf_filename = vm.name+".ovf"
|
||||
self.logger.debug("[%s] creating ovf file: %s" %(self.server_name,ovf_filename))
|
||||
|
||||
with open(ovf_filename, "w") as f:
|
||||
f.write(ovf.ovfDescriptor)
|
||||
|
||||
with open(ovf_filename, "w") as f:
|
||||
f.write(ovf.ovfDescriptor)
|
||||
return ovf_filename
|
||||
|
||||
def create_ova(self,vm, vmdks, ovf_filename):
|
||||
@ -131,9 +131,9 @@ class backup_vmdk(backup_generic):
|
||||
|
||||
def clone_vm(self,vm):
|
||||
task = self.wait_task(vm.CreateSnapshot_Task(name="backup",description="Automatic backup "+datetime.now().strftime("%Y-%m-%d %H:%M:%s"),memory=False,quiesce=True))
|
||||
snapshot=task.info.result
|
||||
snapshot=task.info.result
|
||||
prefix_vmclone = self.prefix_clone
|
||||
clone_name = prefix_vmclone + vm.name
|
||||
clone_name = prefix_vmclone + vm.name
|
||||
datastore = '[%s]' % vm.datastore[0].name
|
||||
|
||||
|
||||
@ -146,16 +146,16 @@ class backup_vmdk(backup_generic):
|
||||
config = vim.vm.ConfigSpec(name=clone_name, memoryMB=vm.summary.config.memorySizeMB, numCPUs=vm.summary.config.numCpu, files=vmx_file)
|
||||
|
||||
hosts = datacenter.hostFolder.childEntity
|
||||
resource_pool = hosts[0].resourcePool
|
||||
resource_pool = hosts[0].resourcePool
|
||||
|
||||
self.wait_task(vmFolder.CreateVM_Task(config=config,pool=resource_pool))
|
||||
self.wait_task(vmFolder.CreateVM_Task(config=config,pool=resource_pool))
|
||||
|
||||
new_vm = [x for x in vmFolder.childEntity if x.name == clone_name][0]
|
||||
|
||||
controller = vim.vm.device.VirtualDeviceSpec()
|
||||
controller.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
|
||||
controller.device = vim.vm.device.VirtualLsiLogicController(busNumber=0,sharedBus='noSharing')
|
||||
controller.device.key = 0
|
||||
controller.device = vim.vm.device.VirtualLsiLogicController(busNumber=0,sharedBus='noSharing')
|
||||
controller.device.key = 0
|
||||
i=0
|
||||
|
||||
vm_devices = []
|
||||
@ -189,7 +189,7 @@ class backup_vmdk(backup_generic):
|
||||
|
||||
|
||||
|
||||
vm_devices.append(controller)
|
||||
vm_devices.append(controller)
|
||||
|
||||
config.deviceChange=vm_devices
|
||||
self.wait_task(new_vm.ReconfigVM_Task(config))
|
||||
@ -198,7 +198,7 @@ class backup_vmdk(backup_generic):
|
||||
def wait_task(self,task):
|
||||
while task.info.state in ["queued", "running"]:
|
||||
time.sleep(2)
|
||||
self.logger.debug("[%s] %s",self.server_name,task.info.descriptionId)
|
||||
self.logger.debug("[%s] %s",self.server_name,task.info.descriptionId)
|
||||
return task
|
||||
|
||||
|
||||
@ -213,7 +213,7 @@ class backup_vmdk(backup_generic):
|
||||
else:
|
||||
print('mkdir "%s"' % dest_dir)
|
||||
else:
|
||||
raise Exception('backup destination directory already exists : %s' % dest_dir)
|
||||
raise Exception('backup destination directory already exists : %s' % dest_dir)
|
||||
os.chdir(dest_dir)
|
||||
user_esx, password_esx, null = open(self.password_file).read().split('\n')
|
||||
|
||||
@ -237,27 +237,27 @@ class backup_vmdk(backup_generic):
|
||||
if vm.name == self.server_name:
|
||||
vm_is_off = vm.summary.runtime.powerState == "poweredOff"
|
||||
if str2bool(self.halt_vm):
|
||||
vm.ShutdownGuest()
|
||||
vm.ShutdownGuest()
|
||||
vm_is_off = True
|
||||
|
||||
|
||||
if vm_is_off:
|
||||
vmdks = self.export_vmdks(vm)
|
||||
ovf_filename = self.create_ovf(vm, vmdks)
|
||||
ovf_filename = self.create_ovf(vm, vmdks)
|
||||
else:
|
||||
new_vm = self.clone_vm(vm)
|
||||
vmdks = self.export_vmdks(new_vm)
|
||||
ovf_filename = self.create_ovf(vm, vmdks)
|
||||
self.wait_task(new_vm.Destroy_Task())
|
||||
|
||||
self.wait_task(new_vm.Destroy_Task())
|
||||
|
||||
if str2bool(self.create_ovafile):
|
||||
ova_filename = self.create_ova(vm, vmdks, ovf_filename)
|
||||
|
||||
ova_filename = self.create_ova(vm, vmdks, ovf_filename)
|
||||
|
||||
if str2bool(self.halt_vm):
|
||||
vm.PowerOnVM()
|
||||
|
||||
vm.PowerOnVM()
|
||||
|
||||
|
||||
if os.path.exists(dest_dir):
|
||||
for file in os.listdir(dest_dir):
|
||||
for file in os.listdir(dest_dir):
|
||||
stats['written_bytes'] += os.stat(file)[ST_SIZE]
|
||||
stats['total_files_count'] += 1
|
||||
stats['written_files_count'] += 1
|
||||
@ -266,10 +266,10 @@ class backup_vmdk(backup_generic):
|
||||
stats['written_bytes'] = 0
|
||||
|
||||
stats['backup_location'] = dest_dir
|
||||
|
||||
|
||||
stats['log']='XVA backup from %s OK, %d bytes written' % (self.server_name,stats['written_bytes'])
|
||||
stats['status']='OK'
|
||||
|
||||
stats['status']='OK'
|
||||
|
||||
|
||||
except BaseException as e:
|
||||
stats['status']='ERROR'
|
||||
@ -279,4 +279,3 @@ class backup_vmdk(backup_generic):
|
||||
|
||||
|
||||
register_driver(backup_vmdk)
|
||||
|
||||
|
@ -20,12 +20,14 @@
|
||||
|
||||
|
||||
|
||||
from .common import *
|
||||
import paramiko
|
||||
|
||||
from .common import *
|
||||
|
||||
|
||||
class backup_xcp_metadata(backup_generic):
|
||||
"""Backup metatdata of a xcp pool using xe pool-dump-database"""
|
||||
type = 'xcp-dump-metadata'
|
||||
type = 'xcp-dump-metadata'
|
||||
required_params = ['type','server_name','private_key','backup_name']
|
||||
|
||||
def do_backup(self,stats):
|
||||
@ -58,7 +60,7 @@ class backup_xcp_metadata(backup_generic):
|
||||
stats['total_files_count']=1
|
||||
stats['written_files_count']=1
|
||||
stats['total_bytes']=os.stat(localpath).st_size
|
||||
stats['written_bytes']=os.stat(localpath).st_size
|
||||
stats['written_bytes']=os.stat(localpath).st_size
|
||||
stats['log']='gzip dump of DB %s:%s (%d bytes) to %s' % (self.server_name,'xcp metadata dump', stats['written_bytes'], localpath)
|
||||
stats['backup_location'] = localpath
|
||||
stats['status']='OK'
|
||||
@ -72,7 +74,7 @@ class backup_xcp_metadata(backup_generic):
|
||||
|
||||
filelist = os.listdir(self.backup_dir)
|
||||
filelist.sort()
|
||||
p = re.compile('^%s-(?P<date>\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}).dump.gz$' % self.server_name)
|
||||
p = re.compile('^%s-(?P<date>\d{8,8}-\d{2,2}h\d{2,2}m\d{2,2}).dump.gz$' % self.server_name)
|
||||
for item in filelist:
|
||||
sr = p.match(item)
|
||||
if sr:
|
||||
@ -82,7 +84,7 @@ class backup_xcp_metadata(backup_generic):
|
||||
self.logger.info('Registering %s from %s',file_name,fileisodate(file_name))
|
||||
size_bytes = int(os.popen('du -sb "%s"' % file_name).read().split('\t')[0])
|
||||
self.logger.debug(' Size in bytes : %i',size_bytes)
|
||||
if not self.dry_run:
|
||||
if not self.dry_run:
|
||||
self.dbstat.add(self.backup_name,self.server_name,'',\
|
||||
backup_start=start,backup_end=fileisodate(file_name),status='OK',total_bytes=size_bytes,backup_location=file_name)
|
||||
else:
|
||||
|
@ -18,20 +18,23 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import logging
|
||||
import re
|
||||
import os
|
||||
import datetime
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import socket
|
||||
import tarfile
|
||||
import hashlib
|
||||
from stat import *
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import ssl
|
||||
import tarfile
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from stat import *
|
||||
|
||||
import requests
|
||||
|
||||
from .common import *
|
||||
from . import XenAPI
|
||||
from .common import *
|
||||
|
||||
if hasattr(ssl, '_create_unverified_context'):
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
|
@ -18,19 +18,19 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
import logging
|
||||
import datetime
|
||||
import time
|
||||
from iniparse import ConfigParser
|
||||
import sqlite3
|
||||
import shutil
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
|
||||
import shutil
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from iniparse import ConfigParser
|
||||
|
||||
try:
|
||||
sys.stderr = open('/dev/null') # Silence silly warnings from paramiko
|
||||
|
@ -18,23 +18,25 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import datetime
|
||||
from .common import *
|
||||
from . import XenAPI
|
||||
import time
|
||||
import logging
|
||||
import re
|
||||
import os.path
|
||||
import os
|
||||
import datetime
|
||||
import select
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
import base64
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import select
|
||||
import socket
|
||||
import ssl
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from stat import *
|
||||
import ssl
|
||||
if hasattr(ssl, '_create_unverified_context'):
|
||||
|
||||
from . import XenAPI
|
||||
from .common import *
|
||||
|
||||
if hasattr(ssl, '_create_unverified_context'):
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
|
||||
|
||||
@ -44,23 +46,23 @@ class copy_vm_xcp(backup_generic):
|
||||
|
||||
required_params = backup_generic.required_params + ['server_name','storage_name','password_file','vm_name','network_name']
|
||||
optional_params = backup_generic.optional_params + ['start_vm','max_copies', 'delete_snapshot', 'halt_vm']
|
||||
|
||||
|
||||
start_vm = "no"
|
||||
max_copies = 1
|
||||
halt_vm = "no"
|
||||
halt_vm = "no"
|
||||
delete_snapshot = "yes"
|
||||
|
||||
|
||||
def read_config(self,iniconf):
|
||||
assert(isinstance(iniconf,ConfigParser))
|
||||
backup_generic.read_config(self,iniconf)
|
||||
backup_generic.read_config(self,iniconf)
|
||||
if self.start_vm in 'no' and iniconf.has_option('global','start_vm'):
|
||||
self.start_vm = iniconf.get('global','start_vm')
|
||||
self.start_vm = iniconf.get('global','start_vm')
|
||||
if self.max_copies == 1 and iniconf.has_option('global','max_copies'):
|
||||
self.max_copies = iniconf.getint('global','max_copies')
|
||||
self.max_copies = iniconf.getint('global','max_copies')
|
||||
if self.delete_snapshot == "yes" and iniconf.has_option('global','delete_snapshot'):
|
||||
self.delete_snapshot = iniconf.get('global','delete_snapshot')
|
||||
|
||||
|
||||
|
||||
def copy_vm_to_sr(self, vm_name, storage_name, dry_run, delete_snapshot="yes"):
|
||||
user_xen, password_xen, null = open(self.password_file).read().split('\n')
|
||||
session = XenAPI.Session('https://'+self.server_name)
|
||||
@ -68,25 +70,25 @@ class copy_vm_xcp(backup_generic):
|
||||
session.login_with_password(user_xen,password_xen)
|
||||
except XenAPI.Failure as error:
|
||||
msg,ip = error.details
|
||||
|
||||
|
||||
if msg == 'HOST_IS_SLAVE':
|
||||
server_name = ip
|
||||
session = XenAPI.Session('https://'+server_name)
|
||||
session.login_with_password(user_xen,password_xen)
|
||||
|
||||
|
||||
session.login_with_password(user_xen,password_xen)
|
||||
|
||||
|
||||
self.logger.debug("[%s] VM (%s) to backup in storage: %s",self.backup_name,vm_name,storage_name)
|
||||
now = datetime.datetime.now()
|
||||
|
||||
|
||||
#get storage opaqueRef
|
||||
try:
|
||||
storage = session.xenapi.SR.get_by_name_label(storage_name)[0]
|
||||
except IndexError as error:
|
||||
result = (1,"error get SR opaqueref %s"%(error))
|
||||
return result
|
||||
|
||||
|
||||
#get vm to copy opaqueRef
|
||||
|
||||
|
||||
#get vm to copy opaqueRef
|
||||
try:
|
||||
vm = session.xenapi.VM.get_by_name_label(vm_name)[0]
|
||||
except IndexError as error:
|
||||
@ -99,96 +101,96 @@ class copy_vm_xcp(backup_generic):
|
||||
except IndexError as error:
|
||||
result = (1, "error get VM network opaqueref %s" % (error))
|
||||
return result
|
||||
|
||||
|
||||
if str2bool(self.halt_vm):
|
||||
status_vm = session.xenapi.VM.get_power_state(vm)
|
||||
status_vm = session.xenapi.VM.get_power_state(vm)
|
||||
self.logger.debug("[%s] Status of VM: %s",self.backup_name,status_vm)
|
||||
if status_vm == "Running":
|
||||
self.logger.debug("[%s] Shutdown in progress",self.backup_name)
|
||||
if dry_run:
|
||||
print("session.xenapi.VM.clean_shutdown(vm)")
|
||||
print("session.xenapi.VM.clean_shutdown(vm)")
|
||||
else:
|
||||
session.xenapi.VM.clean_shutdown(vm)
|
||||
session.xenapi.VM.clean_shutdown(vm)
|
||||
snapshot = vm
|
||||
else:
|
||||
#do the snapshot
|
||||
#do the snapshot
|
||||
self.logger.debug("[%s] Snapshot in progress",self.backup_name)
|
||||
try:
|
||||
snapshot = session.xenapi.VM.snapshot(vm,"tisbackup-%s"%(vm_name))
|
||||
except XenAPI.Failure as error:
|
||||
result = (1,"error when snapshot %s"%(error))
|
||||
return result
|
||||
|
||||
|
||||
#get snapshot opaqueRef
|
||||
|
||||
|
||||
#get snapshot opaqueRef
|
||||
snapshot = session.xenapi.VM.get_by_name_label("tisbackup-%s"%(vm_name))[0]
|
||||
session.xenapi.VM.set_name_description(snapshot,"snapshot created by tisbackup on : %s"%(now.strftime("%Y-%m-%d %H:%M")))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
vm_backup_name = "zzz-%s-"%(vm_name)
|
||||
|
||||
|
||||
|
||||
|
||||
#Check if old backup exit
|
||||
list_backups = []
|
||||
for vm_ref in session.xenapi.VM.get_all():
|
||||
name_lablel = session.xenapi.VM.get_name_label(vm_ref)
|
||||
if vm_backup_name in name_lablel:
|
||||
list_backups.append(name_lablel)
|
||||
|
||||
|
||||
list_backups.sort()
|
||||
|
||||
|
||||
if len(list_backups) >= 1:
|
||||
|
||||
|
||||
# Shutting last backup if started
|
||||
last_backup_vm = session.xenapi.VM.get_by_name_label(list_backups[-1])[0]
|
||||
if not "Halted" in session.xenapi.VM.get_power_state(last_backup_vm):
|
||||
self.logger.debug("[%s] Shutting down last backup vm : %s", self.backup_name, list_backups[-1] )
|
||||
session.xenapi.VM.hard_shutdown(last_backup_vm)
|
||||
|
||||
|
||||
# Delete oldest backup if exist
|
||||
if len(list_backups) >= int(self.max_copies):
|
||||
for i in range(len(list_backups)-int(self.max_copies)+1):
|
||||
oldest_backup_vm = session.xenapi.VM.get_by_name_label(list_backups[i])[0]
|
||||
if not "Halted" in session.xenapi.VM.get_power_state(oldest_backup_vm):
|
||||
self.logger.debug("[%s] Shutting down old vm : %s", self.backup_name, list_backups[i] )
|
||||
self.logger.debug("[%s] Shutting down old vm : %s", self.backup_name, list_backups[i] )
|
||||
session.xenapi.VM.hard_shutdown(oldest_backup_vm)
|
||||
|
||||
|
||||
try:
|
||||
self.logger.debug("[%s] Deleting old vm : %s", self.backup_name, list_backups[i])
|
||||
for vbd in session.xenapi.VM.get_VBDs(oldest_backup_vm):
|
||||
if session.xenapi.VBD.get_type(vbd) == 'CD'and session.xenapi.VBD.get_record(vbd)['empty'] == False:
|
||||
session.xenapi.VBD.eject(vbd)
|
||||
session.xenapi.VBD.eject(vbd)
|
||||
else:
|
||||
vdi = session.xenapi.VBD.get_VDI(vbd)
|
||||
if not 'NULL' in vdi:
|
||||
session.xenapi.VDI.destroy(vdi)
|
||||
|
||||
session.xenapi.VM.destroy(oldest_backup_vm)
|
||||
|
||||
session.xenapi.VM.destroy(oldest_backup_vm)
|
||||
except XenAPI.Failure as error:
|
||||
result = (1,"error when destroy old backup vm %s"%(error))
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
self.logger.debug("[%s] Copy %s in progress on %s",self.backup_name,vm_name,storage_name)
|
||||
try:
|
||||
backup_vm = session.xenapi.VM.copy(snapshot,vm_backup_name+now.strftime("%Y-%m-%d %H:%M"),storage)
|
||||
except XenAPI.Failure as error:
|
||||
result = (1,"error when copy %s"%(error))
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
# define VM as a template
|
||||
session.xenapi.VM.set_is_a_template(backup_vm,False)
|
||||
|
||||
|
||||
#change the network of the new VM
|
||||
try:
|
||||
vifDestroy = session.xenapi.VM.get_VIFs(backup_vm)
|
||||
except IndexError as error:
|
||||
result = (1,"error get VIF opaqueref %s"%(error))
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
for i in vifDestroy:
|
||||
vifRecord = session.xenapi.VIF.get_record(i)
|
||||
session.xenapi.VIF.destroy(i)
|
||||
@ -216,13 +218,13 @@ class copy_vm_xcp(backup_generic):
|
||||
except Exception as error:
|
||||
result = (1,error)
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
if self.start_vm in ['true', '1', 't', 'y', 'yes', 'oui']:
|
||||
session.xenapi.VM.start(backup_vm,False,True)
|
||||
|
||||
|
||||
session.xenapi.VM.set_name_description(backup_vm,"snapshot created by tisbackup on : %s"%(now.strftime("%Y-%m-%d %H:%M")))
|
||||
|
||||
|
||||
size_backup = 0
|
||||
for vbd in session.xenapi.VM.get_VBDs(backup_vm):
|
||||
if session.xenapi.VBD.get_type(vbd) == 'CD' and session.xenapi.VBD.get_record(vbd)['empty'] == False:
|
||||
@ -231,11 +233,11 @@ class copy_vm_xcp(backup_generic):
|
||||
vdi = session.xenapi.VBD.get_VDI(vbd)
|
||||
if not 'NULL' in vdi:
|
||||
size_backup = size_backup + int(session.xenapi.VDI.get_record(vdi)['physical_utilisation'])
|
||||
|
||||
|
||||
result = (0,size_backup)
|
||||
if self.delete_snapshot == 'no':
|
||||
return result
|
||||
|
||||
|
||||
#Disable automatic boot
|
||||
if 'auto_poweron' in session.xenapi.VM.get_other_config(backup_vm):
|
||||
session.xenapi.VM.remove_from_other_config(backup_vm, "auto_poweron")
|
||||
@ -258,25 +260,25 @@ class copy_vm_xcp(backup_generic):
|
||||
if status_vm == "Running":
|
||||
self.logger.debug("[%s] Starting in progress",self.backup_name)
|
||||
if dry_run:
|
||||
print("session.xenapi.VM.start(vm,False,True)")
|
||||
print("session.xenapi.VM.start(vm,False,True)")
|
||||
else:
|
||||
session.xenapi.VM.start(vm,False,True)
|
||||
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
def do_backup(self,stats):
|
||||
try:
|
||||
timestamp = int(time.time())
|
||||
cmd = self.copy_vm_to_sr(self.vm_name, self.storage_name, self.dry_run, delete_snapshot=self.delete_snapshot)
|
||||
|
||||
|
||||
if cmd[0] == 0:
|
||||
timeExec = int(time.time()) - timestamp
|
||||
stats['log']='copy of %s to an other storage OK' % (self.backup_name)
|
||||
stats['status']='OK'
|
||||
stats['total_files_count'] = 1
|
||||
stats['total_bytes'] = cmd[1]
|
||||
|
||||
|
||||
stats['backup_location'] = self.storage_name
|
||||
else:
|
||||
stats['status']='ERROR'
|
||||
|
@ -3,18 +3,16 @@
|
||||
# Copyright (c) 2007 Tim Lauridsen <tla@rasmil.dk>
|
||||
# All Rights Reserved. See LICENSE-PSF & LICENSE for details.
|
||||
|
||||
from .ini import INIConfig, change_comment_syntax
|
||||
from .compat import ConfigParser, RawConfigParser, SafeConfigParser
|
||||
from .config import BasicConfig, ConfigNamespace
|
||||
from .compat import RawConfigParser, ConfigParser, SafeConfigParser
|
||||
from .configparser import (DEFAULTSECT, MAX_INTERPOLATION_DEPTH,
|
||||
DuplicateSectionError, InterpolationDepthError,
|
||||
InterpolationMissingOptionError,
|
||||
InterpolationSyntaxError, NoOptionError,
|
||||
NoSectionError)
|
||||
from .ini import INIConfig, change_comment_syntax
|
||||
from .utils import tidy
|
||||
|
||||
from .configparser import DuplicateSectionError, \
|
||||
NoSectionError, NoOptionError, \
|
||||
InterpolationMissingOptionError, \
|
||||
InterpolationDepthError, \
|
||||
InterpolationSyntaxError, \
|
||||
DEFAULTSECT, MAX_INTERPOLATION_DEPTH
|
||||
|
||||
__all__ = [
|
||||
'BasicConfig', 'ConfigNamespace',
|
||||
'INIConfig', 'tidy', 'change_comment_syntax',
|
||||
|
@ -12,21 +12,18 @@ The underlying INIConfig object can be accessed as cfg.data
|
||||
"""
|
||||
|
||||
import re
|
||||
from .configparser import DuplicateSectionError, \
|
||||
NoSectionError, NoOptionError, \
|
||||
InterpolationMissingOptionError, \
|
||||
InterpolationDepthError, \
|
||||
InterpolationSyntaxError, \
|
||||
DEFAULTSECT, MAX_INTERPOLATION_DEPTH
|
||||
|
||||
# These are imported only for compatiability.
|
||||
# The code below does not reference them directly.
|
||||
from .configparser import Error, InterpolationError, \
|
||||
MissingSectionHeaderError, ParsingError
|
||||
|
||||
import six
|
||||
|
||||
from . import ini
|
||||
# These are imported only for compatiability.
|
||||
# The code below does not reference them directly.
|
||||
from .configparser import (DEFAULTSECT, MAX_INTERPOLATION_DEPTH,
|
||||
DuplicateSectionError, Error,
|
||||
InterpolationDepthError, InterpolationError,
|
||||
InterpolationMissingOptionError,
|
||||
InterpolationSyntaxError, MissingSectionHeaderError,
|
||||
NoOptionError, NoSectionError, ParsingError)
|
||||
|
||||
|
||||
class RawConfigParser(object):
|
||||
|
@ -1,6 +1,6 @@
|
||||
try:
|
||||
from ConfigParser import *
|
||||
# not all objects get imported with __all__
|
||||
from ConfigParser import *
|
||||
from ConfigParser import Error, InterpolationMissingOptionError
|
||||
except ImportError:
|
||||
from configparser import *
|
||||
|
@ -42,11 +42,11 @@ Example:
|
||||
# Backward-compatiable with ConfigParser
|
||||
|
||||
import re
|
||||
from .configparser import DEFAULTSECT, ParsingError, MissingSectionHeaderError
|
||||
|
||||
import six
|
||||
|
||||
from . import config
|
||||
from .configparser import DEFAULTSECT, MissingSectionHeaderError, ParsingError
|
||||
|
||||
|
||||
class LineType(object):
|
||||
|
@ -1,5 +1,5 @@
|
||||
from . import compat
|
||||
from .ini import LineContainer, EmptyLine
|
||||
from .ini import EmptyLine, LineContainer
|
||||
|
||||
|
||||
def tidy(cfg):
|
||||
|
10
pyproject.toml
Normal file
10
pyproject.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[tool.black]
|
||||
line-length = 140
|
||||
|
||||
[tool.ruff]
|
||||
# Allow lines to be as long as 120.
|
||||
line-length = 140
|
||||
indent-width = 4
|
||||
|
||||
[tool.ruff.lint]
|
||||
ignore = ["F401","F403","F405","E402"]
|
7
requirements.txt
Normal file → Executable file
7
requirements.txt
Normal file → Executable file
@ -1,3 +1,10 @@
|
||||
six
|
||||
requests
|
||||
paramiko
|
||||
pexpect
|
||||
flask
|
||||
simplejson
|
||||
huey
|
||||
iniparse
|
||||
redis
|
||||
peewee
|
||||
|
@ -7,9 +7,8 @@ mkdir -p BUILD RPMS
|
||||
|
||||
|
||||
|
||||
VERSION=`git rev-list HEAD --count`
|
||||
VERSION=`git rev-list HEAD --count`
|
||||
echo $VERSION > __VERSION__
|
||||
|
||||
rpmbuild -bb --buildroot $PWD/builddir -v --clean tis-tisbackup.spec
|
||||
cp RPMS/*/*.rpm .
|
||||
|
||||
|
@ -15,7 +15,7 @@ Source0: ../
|
||||
Prefix: /
|
||||
|
||||
%if "%{rhel}" == "8"
|
||||
Requires: unzip rsync python3-paramiko python3-pyvmomi nfs-utils python3-flask python3-simplejson autofs python3-pexpect
|
||||
Requires: unzip rsync python3-paramiko python3-pyvmomi nfs-utils python3-flask python3-simplejson autofs python3-pexpect
|
||||
%endif
|
||||
%if "%{rhel}" == "7"
|
||||
Requires: unzip rsync python36-paramiko python3-pyvmomi nfs-utils python3-flask python3-simplejson autofs pexpect
|
||||
|
@ -14,5 +14,3 @@ else
|
||||
sleep 3
|
||||
fi
|
||||
echo $(date +%Y-%m-%d\ %H:%M:%S) : Fin Export TISBackup sur Disque USB : $target >> /var/log/tisbackup.log
|
||||
|
||||
|
||||
|
@ -20,7 +20,7 @@ maximum_backup_age=30
|
||||
;type=rsync+ssh
|
||||
;server_name=srvzimbra
|
||||
;remote_dir=/
|
||||
;exclude_list="/proc/**","/sys/**","/dev/**"
|
||||
;exclude_list="/proc/**","/sys/**","/dev/**"
|
||||
;private_key=/root/.ssh/id_rsa
|
||||
;ssh_port = 22
|
||||
|
||||
@ -95,4 +95,3 @@ maximum_backup_age=30
|
||||
;type=xcp-dump-metadata
|
||||
;server_name=srvxen1
|
||||
;private_key=/root/.ssh/id_rsa
|
||||
|
||||
|
@ -18,4 +18,3 @@ password_file=/home/homes/ssamson/tisbackup-pra/xen_passwd
|
||||
network_name=net-test
|
||||
#start_vm=no
|
||||
#max_copies=3
|
||||
|
||||
|
@ -4,4 +4,3 @@
|
||||
# m h dom mon dow user command
|
||||
30 22 * * * root /opt/tisbackup/tisbackup.py -c /etc/tis/tisbackup-config.ini backup >> /var/log/tisbackup.log 2>&1
|
||||
30 12 * * * root /opt/tisbackup/tisbackup.py -c /etc/tis/tisbackup-config.ini cleanup >> /var/log/tisbackup.log 2>&1
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
[general]
|
||||
config_tisbackup= /etc/tis/tisbackup-config.ini
|
||||
sections=
|
||||
sections=
|
||||
ADMIN_EMAIL=technique@tranquil-it-systems.fr
|
||||
base_config_dir= /etc/tis/
|
||||
|
@ -95,4 +95,3 @@ case "$1" in
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
[Unit]
|
||||
Description=tisbackup
|
||||
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/python3 /opt/tisbackup/tisbackup_gui.py
|
||||
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
@ -95,4 +95,3 @@ case "$1" in
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
[Unit]
|
||||
Description=tisbackup
|
||||
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=huey_consumer.py -n tisbackup_gui.huey
|
||||
WorkingDirectory=/opt/tisbackup
|
||||
WorkingDirectory=/opt/tisbackup
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
2
static/js/bootstrap.js
vendored
2
static/js/bootstrap.js
vendored
File diff suppressed because one or more lines are too long
3825
static/js/jquery.dataTables.js
vendored
3825
static/js/jquery.dataTables.js
vendored
File diff suppressed because it is too large
Load Diff
2
static/js/jquery.js
vendored
2
static/js/jquery.js
vendored
File diff suppressed because one or more lines are too long
@ -2,4 +2,4 @@ $(document).ready(function () {
|
||||
$('[data-toggle="offcanvas"]').click(function () {
|
||||
$('.row-offcanvas').toggleClass('active')
|
||||
});
|
||||
});
|
||||
});
|
||||
|
2
static/styles/bootstrap.css
vendored
2
static/styles/bootstrap.css
vendored
File diff suppressed because one or more lines are too long
6
tasks.py
6
tasks.py
@ -1,6 +1,8 @@
|
||||
from huey import RedisHuey
|
||||
import os
|
||||
import logging
|
||||
import os
|
||||
|
||||
from huey import RedisHuey
|
||||
|
||||
from tisbackup import tis_backup
|
||||
|
||||
huey = RedisHuey('tisbackup', host='localhost')
|
||||
|
@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if backup_list['rsync_list']|count != 0 %}
|
||||
|
@ -9,9 +9,9 @@
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-success fade in">
|
||||
<a href="#" class="close" data-dismiss="alert">×</a>
|
||||
<strong>Success!</strong> {{ message }}
|
||||
<strong>Success!</strong> {{ message }}
|
||||
</div>
|
||||
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
@ -19,7 +19,7 @@
|
||||
<p>
|
||||
<div class="alert alert-danger fade in"><strong>Error</strong>: {{ error }}</div>
|
||||
<div class="alert alert-warning"><strong>Notice</strong>: {{ info }}</div>
|
||||
|
||||
|
||||
<h4>Also, you can contact your <a href="mailto:{{ email }}?Subject=TISBACKUP%20Export"> System Administrator</a> for more details </h4>
|
||||
</p>
|
||||
{% elif not start %}
|
||||
@ -31,28 +31,28 @@
|
||||
$("#backup").submit();
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
$('#selectall').click(function(event) { //on click
|
||||
if(this.checked) { // check select status
|
||||
$('.checkbox1').each(function() { //loop through each checkbox
|
||||
this.checked = true; //select all checkboxes with class "checkbox1"
|
||||
this.checked = true; //select all checkboxes with class "checkbox1"
|
||||
});
|
||||
}else{
|
||||
$('.checkbox1').each(function() { //loop through each checkbox
|
||||
this.checked = false; //deselect all checkboxes with class "checkbox1"
|
||||
});
|
||||
this.checked = false; //deselect all checkboxes with class "checkbox1"
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<form id="backup" action='/export_backup'>
|
||||
<p> Select backups to save : <br/>
|
||||
<p> Select backups to save : <br/>
|
||||
<div class="row">
|
||||
<div class="checkbox"><label><input type="checkbox" class="checkbox1" id="selectall" checked>Select all</label></div>
|
||||
<div class="checkbox"><label><input type="checkbox" class="checkbox1" id="selectall" checked>Select all</label></div>
|
||||
{% for entry in sections|sort %}
|
||||
<div class="col-xs-6 col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="checkbox"><label><input type="checkbox" name="sections" class="checkbox1" value="{{entry}}" checked>{{entry}}</label></div>
|
||||
<div class="checkbox"><label><input type="checkbox" name="sections" class="checkbox1" value="{{entry}}" checked>{{entry}}</label></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
@ -65,7 +65,7 @@
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
{% else %}
|
||||
<h2 class="title">Backups is running: </h2>
|
||||
<table id="table" class='table'>
|
||||
@ -79,7 +79,7 @@
|
||||
</table>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
//Refresh periode in seconds
|
||||
var refresh = 5;
|
||||
@ -97,7 +97,7 @@ function status(){
|
||||
done = false;
|
||||
}else{
|
||||
$('tbody').append('<td>'+val.status+'</td>');
|
||||
|
||||
|
||||
done = data.finish;
|
||||
}
|
||||
$('#table-design').append('</tr>');
|
||||
|
@ -24,7 +24,7 @@
|
||||
"aTargets": [ 0 ],
|
||||
"mData": "backup_start",
|
||||
"mRender": function ( data, type, full ) {
|
||||
var d = new Date(data);
|
||||
var d = new Date(data);
|
||||
return d.getFullYear()+"/"+(d.getMonth()+1)+"/"+d.getDate()+" "+d.toLocaleTimeString();
|
||||
}
|
||||
},
|
||||
@ -32,7 +32,7 @@
|
||||
"aTargets": [ 1 ],
|
||||
"mData": "backup_start",
|
||||
"mRender": function ( data, type, full ) {
|
||||
var d = new Date(data);
|
||||
var d = new Date(data);
|
||||
return d.getFullYear()+"/"+(d.getMonth()+1)+"/"+d.getDate()+" "+d.toLocaleTimeString();
|
||||
}
|
||||
},
|
||||
@ -79,7 +79,7 @@
|
||||
$('#inputDatabaseName').keyup(function () { delay(function(){ oTable.fnLengthChange($('#inputDatabaseName').val() ); }, 300 )});
|
||||
$(".dataTables_length").remove()
|
||||
var nb_row = GetURLParameter('row');
|
||||
if (nb_row ){
|
||||
if (nb_row ){
|
||||
oTable.fnLengthChange( nb_row) ;
|
||||
$('#inputDatabaseName').val(nb_row);
|
||||
}
|
||||
@ -96,7 +96,7 @@
|
||||
{
|
||||
/* Get the DataTables object again - this is not a recreation, just a get of the object */
|
||||
var oTable = $('#table-design').dataTable();
|
||||
|
||||
|
||||
var bVis = oTable.fnSettings().aoColumns[iCol].bVisible;
|
||||
oTable.fnSetColumnVis( iCol, bVis ? false : true );
|
||||
}
|
||||
@ -111,10 +111,10 @@
|
||||
{
|
||||
var sPageURL = window.location.search.substring(1);
|
||||
var sURLVariables = sPageURL.split('&');
|
||||
for (var i = 0; i < sURLVariables.length; i++)
|
||||
for (var i = 0; i < sURLVariables.length; i++)
|
||||
{
|
||||
var sParameterName = sURLVariables[i].split('=');
|
||||
if (sParameterName[0] == sParam)
|
||||
if (sParameterName[0] == sParam)
|
||||
{
|
||||
return sParameterName[1];
|
||||
}
|
||||
@ -135,7 +135,7 @@
|
||||
{
|
||||
oSettings._iDisplayLength = iDisplay;
|
||||
oSettings.oApi._fnCalculateEnd( oSettings );
|
||||
|
||||
|
||||
/* If we have space to show extra rows (backing up from the end point - then do so */
|
||||
if ( oSettings._iDisplayEnd == oSettings.aiDisplay.length )
|
||||
{
|
||||
@ -145,14 +145,14 @@
|
||||
oSettings._iDisplayStart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( oSettings._iDisplayLength == -1 )
|
||||
{
|
||||
oSettings._iDisplayStart = 0;
|
||||
}
|
||||
|
||||
|
||||
oSettings.oApi._fnDraw( oSettings );
|
||||
|
||||
|
||||
if ( oSettings.aanFeatures.l )
|
||||
{
|
||||
$('select', oSettings.aanFeatures.l).val( iDisplay );
|
||||
@ -179,7 +179,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
@ -204,7 +204,7 @@
|
||||
<div class="checkbox"><label><input type="checkbox" onclick="fnShowHide( 4 );" checked> Backup duration</label></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-xs-6 col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="checkbox"><label><input type="checkbox" onclick="fnShowHide( 6 );"checked> Written bytes</label></div>
|
||||
@ -213,9 +213,9 @@
|
||||
<div class="checkbox"><label><input type="checkbox" onclick="fnShowHide( 9 );"/> Total bytes</label></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-xs-6 col-md-4">
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<div class="checkbox"><label><input type="checkbox" onclick="fnShowHide( 10 );"> Backup location</label></div>
|
||||
<div class="checkbox"><label><input type="checkbox" onclick="fnShowHide( 11);">Description </label></div>
|
||||
<div class="checkbox"><label><input type="checkbox" onclick="fnShowHide( 12);"> Log</label></div>
|
||||
@ -225,7 +225,7 @@
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -55,7 +55,7 @@
|
||||
{% endblock %}
|
||||
|
||||
</div><!--/row-->
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
<footer>
|
||||
@ -80,7 +80,7 @@
|
||||
if ( data.configs.length > 1){
|
||||
$('#choix_conf').removeClass('hidden');
|
||||
$("#choix_conf").show();
|
||||
|
||||
|
||||
$.each(data.configs, function(key,val){
|
||||
if (key == data.config_number)
|
||||
$('#choix_conf').append('<option vaulue="'+key+'" selected>'+val+'</option>');
|
||||
@ -89,7 +89,7 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$( "#choix_conf" ).change(function() {
|
||||
$.get( "/config_number/"+this.selectedIndex, function( data ) {location.reload();});
|
||||
});
|
||||
@ -98,4 +98,3 @@
|
||||
</script>
|
||||
|
||||
</body></html>
|
||||
|
||||
|
311
tisbackup.py
311
tisbackup.py
@ -18,41 +18,43 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
import datetime
|
||||
import subprocess
|
||||
import os,sys
|
||||
import os
|
||||
import sys
|
||||
from os.path import isfile, join
|
||||
|
||||
tisbackup_root_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.insert(0,os.path.join(tisbackup_root_dir,'lib'))
|
||||
sys.path.insert(0,os.path.join(tisbackup_root_dir,'libtisbackup'))
|
||||
sys.path.insert(0, os.path.join(tisbackup_root_dir, "lib"))
|
||||
sys.path.insert(0, os.path.join(tisbackup_root_dir, "libtisbackup"))
|
||||
|
||||
from iniparse import ini,ConfigParser
|
||||
from optparse import OptionParser
|
||||
import re
|
||||
import getopt
|
||||
import os.path
|
||||
import logging
|
||||
import errno
|
||||
from libtisbackup.common import *
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
from optparse import OptionParser
|
||||
|
||||
from iniparse import ConfigParser, ini
|
||||
|
||||
from libtisbackup.backup_mysql import backup_mysql
|
||||
from libtisbackup.backup_rsync import backup_rsync
|
||||
from libtisbackup.backup_rsync import backup_rsync_ssh
|
||||
#from libtisbackup.backup_oracle import backup_oracle
|
||||
from libtisbackup.backup_rsync_btrfs import backup_rsync_btrfs
|
||||
from libtisbackup.backup_rsync_btrfs import backup_rsync__btrfs_ssh
|
||||
from libtisbackup.backup_pgsql import backup_pgsql
|
||||
from libtisbackup.backup_xva import backup_xva
|
||||
#from libtisbackup.backup_vmdk import backup_vmdk
|
||||
#from libtisbackup.backup_switch import backup_switch
|
||||
|
||||
# from libtisbackup.backup_vmdk import backup_vmdk
|
||||
# from libtisbackup.backup_switch import backup_switch
|
||||
from libtisbackup.backup_null import backup_null
|
||||
from libtisbackup.backup_xcp_metadata import backup_xcp_metadata
|
||||
from libtisbackup.copy_vm_xcp import copy_vm_xcp
|
||||
#from libtisbackup.backup_sqlserver import backup_sqlserver
|
||||
from libtisbackup.backup_pgsql import backup_pgsql
|
||||
from libtisbackup.backup_rsync import backup_rsync, backup_rsync_ssh
|
||||
|
||||
# from libtisbackup.backup_oracle import backup_oracle
|
||||
from libtisbackup.backup_rsync_btrfs import backup_rsync__btrfs_ssh, backup_rsync_btrfs
|
||||
|
||||
# from libtisbackup.backup_sqlserver import backup_sqlserver
|
||||
from libtisbackup.backup_samba4 import backup_samba4
|
||||
from libtisbackup.backup_xcp_metadata import backup_xcp_metadata
|
||||
from libtisbackup.backup_xva import backup_xva
|
||||
from libtisbackup.common import *
|
||||
from libtisbackup.copy_vm_xcp import copy_vm_xcp
|
||||
|
||||
__version__="2.0"
|
||||
__version__ = "2.0"
|
||||
|
||||
usage="""\
|
||||
usage = """\
|
||||
%prog -c configfile action
|
||||
|
||||
TIS Files Backup system.
|
||||
@ -67,52 +69,75 @@ action is either :
|
||||
exportbackup : copy lastest OK backups from local to location defned by --exportdir parameter
|
||||
register_existing : scan backup directories and add missing backups to database"""
|
||||
|
||||
version="VERSION"
|
||||
version = "VERSION"
|
||||
|
||||
parser = OptionParser(usage=usage, version="%prog " + version)
|
||||
parser.add_option(
|
||||
"-c", "--config", dest="config", default="/etc/tis/tisbackup-config.ini", help="Config file full path (default: %default)"
|
||||
)
|
||||
parser.add_option("-d", "--dry-run", dest="dry_run", default=False, action="store_true", help="Dry run (default: %default)")
|
||||
parser.add_option("-v", "--verbose", dest="verbose", default=False, action="store_true", help="More information (default: %default)")
|
||||
parser.add_option(
|
||||
"-s", "--sections", dest="sections", default="", help="Comma separated list of sections (backups) to process (default: All)"
|
||||
)
|
||||
parser.add_option(
|
||||
"-l",
|
||||
"--loglevel",
|
||||
dest="loglevel",
|
||||
default="info",
|
||||
type="choice",
|
||||
choices=["debug", "warning", "info", "error", "critical"],
|
||||
metavar="LOGLEVEL",
|
||||
help="Loglevel (default: %default)",
|
||||
)
|
||||
parser.add_option("-n", "--len", dest="statscount", default=30, type="int", help="Number of lines to list for dumpstat (default: %default)")
|
||||
parser.add_option(
|
||||
"-b",
|
||||
"--backupdir",
|
||||
dest="backup_base_dir",
|
||||
default="",
|
||||
help="Base directory for all backups (default: [global] backup_base_dir in config file)",
|
||||
)
|
||||
parser.add_option(
|
||||
"-x", "--exportdir", dest="exportdir", default="", help="Directory where to export latest backups with exportbackup (nodefault)"
|
||||
)
|
||||
|
||||
parser=OptionParser(usage=usage,version="%prog " + version)
|
||||
parser.add_option("-c","--config", dest="config", default='/etc/tis/tisbackup-config.ini', help="Config file full path (default: %default)")
|
||||
parser.add_option("-d","--dry-run", dest="dry_run", default=False, action='store_true', help="Dry run (default: %default)")
|
||||
parser.add_option("-v","--verbose", dest="verbose", default=False, action='store_true', help="More information (default: %default)")
|
||||
parser.add_option("-s","--sections", dest="sections", default='', help="Comma separated list of sections (backups) to process (default: All)")
|
||||
parser.add_option("-l","--loglevel", dest="loglevel", default='info', type='choice', choices=['debug','warning','info','error','critical'], metavar='LOGLEVEL',help="Loglevel (default: %default)")
|
||||
parser.add_option("-n","--len", dest="statscount", default=30, type='int', help="Number of lines to list for dumpstat (default: %default)")
|
||||
parser.add_option("-b","--backupdir", dest="backup_base_dir", default='', help="Base directory for all backups (default: [global] backup_base_dir in config file)")
|
||||
parser.add_option("-x","--exportdir", dest="exportdir", default='', help="Directory where to export latest backups with exportbackup (nodefault)")
|
||||
|
||||
class tis_backup:
|
||||
logger = logging.getLogger('tisbackup')
|
||||
logger = logging.getLogger("tisbackup")
|
||||
|
||||
def __init__(self,dry_run=False,verbose=False,backup_base_dir=''):
|
||||
def __init__(self, dry_run=False, verbose=False, backup_base_dir=""):
|
||||
self.dry_run = dry_run
|
||||
self.verbose = verbose
|
||||
self.backup_base_dir = backup_base_dir
|
||||
self.backup_base_dir = ''
|
||||
self.backup_base_dir = ""
|
||||
self.backup_list = []
|
||||
self.dry_run = dry_run
|
||||
self.verbose=False
|
||||
self.verbose = False
|
||||
|
||||
def read_ini_file(self,filename):
|
||||
def read_ini_file(self, filename):
|
||||
ini.change_comment_syntax()
|
||||
cp = ConfigParser()
|
||||
cp.read(filename)
|
||||
|
||||
if not self.backup_base_dir:
|
||||
self.backup_base_dir = cp.get('global','backup_base_dir')
|
||||
self.backup_base_dir = cp.get("global", "backup_base_dir")
|
||||
if not os.path.isdir(self.backup_base_dir):
|
||||
self.logger.info('Creating backup directory %s' % self.backup_base_dir)
|
||||
self.logger.info("Creating backup directory %s" % self.backup_base_dir)
|
||||
os.makedirs(self.backup_base_dir)
|
||||
|
||||
self.logger.debug("backup directory : " + self.backup_base_dir)
|
||||
self.dbstat = BackupStat(os.path.join(self.backup_base_dir,'log','tisbackup.sqlite'))
|
||||
self.dbstat = BackupStat(os.path.join(self.backup_base_dir, "log", "tisbackup.sqlite"))
|
||||
|
||||
for section in cp.sections():
|
||||
if (section != 'global'):
|
||||
if section != "global":
|
||||
self.logger.debug("reading backup config " + section)
|
||||
backup_item = None
|
||||
type = cp.get(section,'type')
|
||||
type = cp.get(section, "type")
|
||||
|
||||
backup_item = backup_drivers[type](backup_name=section,
|
||||
backup_dir=os.path.join(self.backup_base_dir,section),dbstat=self.dbstat,dry_run=self.dry_run)
|
||||
backup_item = backup_drivers[type](
|
||||
backup_name=section, backup_dir=os.path.join(self.backup_base_dir, section), dbstat=self.dbstat, dry_run=self.dry_run
|
||||
)
|
||||
backup_item.read_config(cp)
|
||||
backup_item.verbose = self.verbose
|
||||
|
||||
@ -122,35 +147,34 @@ class tis_backup:
|
||||
# TODO socket.gethostbyaddr('64.236.16.20')
|
||||
# TODO limit backup to one backup on the command line
|
||||
|
||||
|
||||
def checknagios(self,sections=[]):
|
||||
def checknagios(self, sections=[]):
|
||||
try:
|
||||
if not sections:
|
||||
sections = [backup_item.backup_name for backup_item in self.backup_list]
|
||||
|
||||
self.logger.debug('Start of check nagios for %s' % (','.join(sections),))
|
||||
self.logger.debug("Start of check nagios for %s" % (",".join(sections),))
|
||||
try:
|
||||
worst_nagiosstatus = None
|
||||
ok = []
|
||||
warning = []
|
||||
critical = []
|
||||
unknown = []
|
||||
nagiosoutput = ''
|
||||
nagiosoutput = ""
|
||||
for backup_item in self.backup_list:
|
||||
if not sections or backup_item.backup_name in sections:
|
||||
(nagiosstatus,log) = backup_item.checknagios()
|
||||
(nagiosstatus, log) = backup_item.checknagios()
|
||||
if nagiosstatus == nagiosStateCritical:
|
||||
critical.append((backup_item.backup_name,log))
|
||||
elif nagiosstatus == nagiosStateWarning :
|
||||
warning.append((backup_item.backup_name,log))
|
||||
critical.append((backup_item.backup_name, log))
|
||||
elif nagiosstatus == nagiosStateWarning:
|
||||
warning.append((backup_item.backup_name, log))
|
||||
elif nagiosstatus == nagiosStateOk:
|
||||
ok.append((backup_item.backup_name,log))
|
||||
ok.append((backup_item.backup_name, log))
|
||||
else:
|
||||
unknown.append((backup_item.backup_name,log))
|
||||
self.logger.debug('[%s] nagios:"%i" log: %s',backup_item.backup_name,nagiosstatus,log)
|
||||
unknown.append((backup_item.backup_name, log))
|
||||
self.logger.debug('[%s] nagios:"%i" log: %s', backup_item.backup_name, nagiosstatus, log)
|
||||
|
||||
if not ok and not critical and not unknown and not warning:
|
||||
self.logger.debug('Nothing processed')
|
||||
self.logger.debug("Nothing processed")
|
||||
worst_nagiosstatus = nagiosStateUnknown
|
||||
nagiosoutput = 'UNKNOWN : Unknown backup sections "%s"' % sections
|
||||
|
||||
@ -159,156 +183,154 @@ class tis_backup:
|
||||
if unknown:
|
||||
if not worst_nagiosstatus:
|
||||
worst_nagiosstatus = nagiosStateUnknown
|
||||
nagiosoutput = 'UNKNOWN status backups %s' % (','.join([b[0] for b in unknown]))
|
||||
nagiosoutput = "UNKNOWN status backups %s" % (",".join([b[0] for b in unknown]))
|
||||
globallog.extend(unknown)
|
||||
|
||||
if critical:
|
||||
if not worst_nagiosstatus:
|
||||
worst_nagiosstatus = nagiosStateCritical
|
||||
nagiosoutput = 'CRITICAL backups %s' % (','.join([b[0] for b in critical]))
|
||||
nagiosoutput = "CRITICAL backups %s" % (",".join([b[0] for b in critical]))
|
||||
globallog.extend(critical)
|
||||
|
||||
if warning:
|
||||
if not worst_nagiosstatus:
|
||||
worst_nagiosstatus = nagiosStateWarning
|
||||
nagiosoutput = 'WARNING backups %s' % (','.join([b[0] for b in warning]))
|
||||
nagiosoutput = "WARNING backups %s" % (",".join([b[0] for b in warning]))
|
||||
globallog.extend(warning)
|
||||
|
||||
if ok:
|
||||
if not worst_nagiosstatus:
|
||||
worst_nagiosstatus = nagiosStateOk
|
||||
nagiosoutput = 'OK backups %s' % (','.join([b[0] for b in ok]))
|
||||
nagiosoutput = "OK backups %s" % (",".join([b[0] for b in ok]))
|
||||
globallog.extend(ok)
|
||||
|
||||
if worst_nagiosstatus == nagiosStateOk:
|
||||
nagiosoutput = 'ALL backups OK %s' % (','.join(sections))
|
||||
|
||||
nagiosoutput = "ALL backups OK %s" % (",".join(sections))
|
||||
|
||||
except BaseException as e:
|
||||
worst_nagiosstatus = nagiosStateCritical
|
||||
nagiosoutput = 'EXCEPTION',"Critical : %s" % str(e)
|
||||
nagiosoutput = "EXCEPTION", "Critical : %s" % str(e)
|
||||
raise
|
||||
|
||||
finally:
|
||||
self.logger.debug('worst nagios status :"%i"',worst_nagiosstatus)
|
||||
print('%s (tisbackup V%s)' %(nagiosoutput,version))
|
||||
print('\n'.join(["[%s]:%s" % (l[0],l[1]) for l in globallog]))
|
||||
self.logger.debug('worst nagios status :"%i"', worst_nagiosstatus)
|
||||
print("%s (tisbackup V%s)" % (nagiosoutput, version))
|
||||
print("\n".join(["[%s]:%s" % (log_elem[0], log_elem[1]) for log_elem in globallog]))
|
||||
sys.exit(worst_nagiosstatus)
|
||||
|
||||
def process_backup(self,sections=[]):
|
||||
def process_backup(self, sections=[]):
|
||||
processed = []
|
||||
errors = []
|
||||
if not sections:
|
||||
sections = [backup_item.backup_name for backup_item in self.backup_list]
|
||||
|
||||
self.logger.info('Processing backup for %s' % (','.join(sections)) )
|
||||
self.logger.info("Processing backup for %s" % (",".join(sections)))
|
||||
for backup_item in self.backup_list:
|
||||
if not sections or backup_item.backup_name in sections:
|
||||
try:
|
||||
assert(isinstance(backup_item,backup_generic))
|
||||
self.logger.info('Processing [%s]',(backup_item.backup_name))
|
||||
assert isinstance(backup_item, backup_generic)
|
||||
self.logger.info("Processing [%s]", (backup_item.backup_name))
|
||||
stats = backup_item.process_backup()
|
||||
processed.append((backup_item.backup_name,stats))
|
||||
processed.append((backup_item.backup_name, stats))
|
||||
except BaseException as e:
|
||||
self.logger.critical('Backup [%s] processed with error : %s',backup_item.backup_name,e)
|
||||
errors.append((backup_item.backup_name,str(e)))
|
||||
self.logger.critical("Backup [%s] processed with error : %s", backup_item.backup_name, e)
|
||||
errors.append((backup_item.backup_name, str(e)))
|
||||
if not processed and not errors:
|
||||
self.logger.critical('No backup properly finished or processed')
|
||||
self.logger.critical("No backup properly finished or processed")
|
||||
else:
|
||||
if processed:
|
||||
self.logger.info('Backup processed : %s' , ",".join([b[0] for b in processed]))
|
||||
self.logger.info("Backup processed : %s", ",".join([b[0] for b in processed]))
|
||||
if errors:
|
||||
self.logger.error('Backup processed with errors: %s' , ",".join([b[0] for b in errors]))
|
||||
self.logger.error("Backup processed with errors: %s", ",".join([b[0] for b in errors]))
|
||||
|
||||
def export_backups(self,sections=[],exportdir=''):
|
||||
def export_backups(self, sections=[], exportdir=""):
|
||||
processed = []
|
||||
errors = []
|
||||
if not sections:
|
||||
sections = [backup_item.backup_name for backup_item in self.backup_list]
|
||||
|
||||
self.logger.info('Exporting OK backups for %s to %s' % (','.join(sections),exportdir) )
|
||||
self.logger.info("Exporting OK backups for %s to %s" % (",".join(sections), exportdir))
|
||||
|
||||
for backup_item in self.backup_list:
|
||||
if backup_item.backup_name in sections:
|
||||
try:
|
||||
assert(isinstance(backup_item,backup_generic))
|
||||
self.logger.info('Processing [%s]',(backup_item.backup_name))
|
||||
assert isinstance(backup_item, backup_generic)
|
||||
self.logger.info("Processing [%s]", (backup_item.backup_name))
|
||||
stats = backup_item.export_latestbackup(destdir=exportdir)
|
||||
processed.append((backup_item.backup_name,stats))
|
||||
processed.append((backup_item.backup_name, stats))
|
||||
except BaseException as e:
|
||||
self.logger.critical('Export Backup [%s] processed with error : %s',backup_item.backup_name,e)
|
||||
errors.append((backup_item.backup_name,str(e)))
|
||||
self.logger.critical("Export Backup [%s] processed with error : %s", backup_item.backup_name, e)
|
||||
errors.append((backup_item.backup_name, str(e)))
|
||||
if not processed and not errors:
|
||||
self.logger.critical('No export backup properly finished or processed')
|
||||
self.logger.critical("No export backup properly finished or processed")
|
||||
else:
|
||||
if processed:
|
||||
self.logger.info('Export Backups processed : %s' , ",".join([b[0] for b in processed]))
|
||||
self.logger.info("Export Backups processed : %s", ",".join([b[0] for b in processed]))
|
||||
if errors:
|
||||
self.logger.error('Export Backups processed with errors: %s' , ",".join([b[0] for b in errors]))
|
||||
self.logger.error("Export Backups processed with errors: %s", ",".join([b[0] for b in errors]))
|
||||
|
||||
def retry_failed_backups(self,maxage_hours=30):
|
||||
def retry_failed_backups(self, maxage_hours=30):
|
||||
processed = []
|
||||
errors = []
|
||||
|
||||
# before mindate, backup is too old
|
||||
mindate = datetime2isodate((datetime.datetime.now() - datetime.timedelta(hours=maxage_hours)))
|
||||
failed_backups = self.dbstat.query("""\
|
||||
failed_backups = self.dbstat.query(
|
||||
"""\
|
||||
select distinct backup_name as bname
|
||||
from stats
|
||||
where status="OK" and backup_start>=?""",(mindate,))
|
||||
|
||||
|
||||
defined_backups = list(map(lambda f:f.backup_name, [ x for x in self.backup_list if not isinstance(x, backup_null) ]))
|
||||
failed_backups_names = set(defined_backups) - set([b['bname'] for b in failed_backups if b['bname'] in defined_backups])
|
||||
where status="OK" and backup_start>=?""",
|
||||
(mindate,),
|
||||
)
|
||||
|
||||
defined_backups = list(map(lambda f: f.backup_name, [x for x in self.backup_list if not isinstance(x, backup_null)]))
|
||||
failed_backups_names = set(defined_backups) - set([b["bname"] for b in failed_backups if b["bname"] in defined_backups])
|
||||
|
||||
if failed_backups_names:
|
||||
self.logger.info('Processing backup for %s',','.join(failed_backups_names))
|
||||
self.logger.info("Processing backup for %s", ",".join(failed_backups_names))
|
||||
for backup_item in self.backup_list:
|
||||
if backup_item.backup_name in failed_backups_names:
|
||||
try:
|
||||
assert(isinstance(backup_item,backup_generic))
|
||||
self.logger.info('Processing [%s]',(backup_item.backup_name))
|
||||
assert isinstance(backup_item, backup_generic)
|
||||
self.logger.info("Processing [%s]", (backup_item.backup_name))
|
||||
stats = backup_item.process_backup()
|
||||
processed.append((backup_item.backup_name,stats))
|
||||
processed.append((backup_item.backup_name, stats))
|
||||
except BaseException as e:
|
||||
self.logger.critical('Backup [%s] not processed, error : %s',backup_item.backup_name,e)
|
||||
errors.append((backup_item.backup_name,str(e)))
|
||||
self.logger.critical("Backup [%s] not processed, error : %s", backup_item.backup_name, e)
|
||||
errors.append((backup_item.backup_name, str(e)))
|
||||
if not processed and not errors:
|
||||
self.logger.critical('No backup properly finished or processed')
|
||||
self.logger.critical("No backup properly finished or processed")
|
||||
else:
|
||||
if processed:
|
||||
self.logger.info('Backup processed : %s' , ",".join([b[0] for b in errors]))
|
||||
self.logger.info("Backup processed : %s", ",".join([b[0] for b in errors]))
|
||||
if errors:
|
||||
self.logger.error('Backup processed with errors: %s' , ",".join([b[0] for b in errors]))
|
||||
self.logger.error("Backup processed with errors: %s", ",".join([b[0] for b in errors]))
|
||||
else:
|
||||
self.logger.info('No recent failed backups found in database')
|
||||
self.logger.info("No recent failed backups found in database")
|
||||
|
||||
|
||||
def cleanup_backup_section(self,sections = []):
|
||||
log = ''
|
||||
def cleanup_backup_section(self, sections=[]):
|
||||
processed = False
|
||||
if not sections:
|
||||
sections = [backup_item.backup_name for backup_item in self.backup_list]
|
||||
|
||||
self.logger.info('Processing cleanup for %s' % (','.join(sections)) )
|
||||
self.logger.info("Processing cleanup for %s" % (",".join(sections)))
|
||||
for backup_item in self.backup_list:
|
||||
if backup_item.backup_name in sections:
|
||||
try:
|
||||
assert(isinstance(backup_item,backup_generic))
|
||||
self.logger.info('Processing cleanup of [%s]',(backup_item.backup_name))
|
||||
assert isinstance(backup_item, backup_generic)
|
||||
self.logger.info("Processing cleanup of [%s]", (backup_item.backup_name))
|
||||
backup_item.cleanup_backup()
|
||||
processed = True
|
||||
except BaseException as e:
|
||||
self.logger.critical('Cleanup of [%s] not processed, error : %s',backup_item.backup_name,e)
|
||||
self.logger.critical("Cleanup of [%s] not processed, error : %s", backup_item.backup_name, e)
|
||||
if not processed:
|
||||
self.logger.critical('No cleanup properly finished or processed')
|
||||
self.logger.critical("No cleanup properly finished or processed")
|
||||
|
||||
def register_existingbackups(self,sections = []):
|
||||
def register_existingbackups(self, sections=[]):
|
||||
if not sections:
|
||||
sections = [backup_item.backup_name for backup_item in self.backup_list]
|
||||
|
||||
self.logger.info('Append existing backups to database...')
|
||||
self.logger.info("Append existing backups to database...")
|
||||
for backup_item in self.backup_list:
|
||||
if backup_item.backup_name in sections:
|
||||
backup_item.register_existingbackups()
|
||||
@ -316,26 +338,26 @@ class tis_backup:
|
||||
def html_report(self):
|
||||
for backup_item in self.backup_list:
|
||||
if not section or section == backup_item.backup_name:
|
||||
assert(isinstance(backup_item,backup_generic))
|
||||
assert isinstance(backup_item, backup_generic)
|
||||
if not maxage_hours:
|
||||
maxage_hours = backup_item.maximum_backup_age
|
||||
(nagiosstatus,log) = backup_item.checknagios(maxage_hours=maxage_hours)
|
||||
globallog.append('[%s] %s' % (backup_item.backup_name,log))
|
||||
self.logger.debug('[%s] nagios:"%i" log: %s',backup_item.backup_name,nagiosstatus,log)
|
||||
processed = True
|
||||
if nagiosstatus >= worst_nagiosstatus:
|
||||
worst_nagiosstatus = nagiosstatus
|
||||
(nagiosstatus, log) = backup_item.checknagios(maxage_hours=maxage_hours)
|
||||
globallog.append("[%s] %s" % (backup_item.backup_name, log))
|
||||
self.logger.debug('[%s] nagios:"%i" log: %s', backup_item.backup_name, nagiosstatus, log)
|
||||
# processed = True
|
||||
# if nagiosstatus >= worst_nagiosstatus:
|
||||
# worst_nagiosstatus = nagiosstatus
|
||||
|
||||
|
||||
def main():
|
||||
(options,args)=parser.parse_args()
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if len(args) != 1:
|
||||
print("ERROR : You must provide one action to perform")
|
||||
parser.print_usage()
|
||||
sys.exit(2)
|
||||
|
||||
backup_start_date = datetime.datetime.now().strftime('%Y%m%d-%Hh%Mm%S')
|
||||
backup_start_date = datetime.datetime.now().strftime("%Y%m%d-%Hh%Mm%S")
|
||||
|
||||
# options
|
||||
action = args[0]
|
||||
@ -344,23 +366,23 @@ def main():
|
||||
print(backup_drivers[t].get_help())
|
||||
sys.exit(0)
|
||||
|
||||
config_file =options.config
|
||||
config_file = options.config
|
||||
dry_run = options.dry_run
|
||||
verbose = options.verbose
|
||||
|
||||
loglevel = options.loglevel
|
||||
|
||||
# setup Logger
|
||||
logger = logging.getLogger('tisbackup')
|
||||
logger = logging.getLogger("tisbackup")
|
||||
hdlr = logging.StreamHandler()
|
||||
hdlr.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
|
||||
hdlr.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
|
||||
logger.addHandler(hdlr)
|
||||
|
||||
# set loglevel
|
||||
if loglevel in ('debug','warning','info','error','critical'):
|
||||
if loglevel in ("debug", "warning", "info", "error", "critical"):
|
||||
numeric_level = getattr(logging, loglevel.upper(), None)
|
||||
if not isinstance(numeric_level, int):
|
||||
raise ValueError('Invalid log level: %s' % loglevel)
|
||||
raise ValueError("Invalid log level: %s" % loglevel)
|
||||
logger.setLevel(numeric_level)
|
||||
|
||||
# Config file
|
||||
@ -371,36 +393,36 @@ def main():
|
||||
cp = ConfigParser()
|
||||
cp.read(config_file)
|
||||
|
||||
backup_base_dir = options.backup_base_dir or cp.get('global','backup_base_dir')
|
||||
log_dir = os.path.join(backup_base_dir,'log')
|
||||
backup_base_dir = options.backup_base_dir or cp.get("global", "backup_base_dir")
|
||||
log_dir = os.path.join(backup_base_dir, "log")
|
||||
if not os.path.exists(log_dir):
|
||||
os.makedirs(log_dir)
|
||||
|
||||
# if we run the nagios check, we don't create log file, everything is piped to stdout
|
||||
if action!='checknagios':
|
||||
if action != "checknagios":
|
||||
try:
|
||||
hdlr = logging.FileHandler(os.path.join(log_dir,'tisbackup_%s.log' % (backup_start_date)))
|
||||
hdlr.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
|
||||
hdlr = logging.FileHandler(os.path.join(log_dir, "tisbackup_%s.log" % (backup_start_date)))
|
||||
hdlr.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
|
||||
logger.addHandler(hdlr)
|
||||
except IOError as e:
|
||||
if action == 'cleanup' and e.errno == errno.ENOSPC:
|
||||
if action == "cleanup" and e.errno == errno.ENOSPC:
|
||||
logger.warning("No space left on device, disabling file logging.")
|
||||
else:
|
||||
raise e
|
||||
|
||||
# Main
|
||||
backup = tis_backup(dry_run=dry_run,verbose=verbose,backup_base_dir=backup_base_dir)
|
||||
backup = tis_backup(dry_run=dry_run, verbose=verbose, backup_base_dir=backup_base_dir)
|
||||
backup.read_ini_file(config_file)
|
||||
|
||||
backup_sections = options.sections.split(',') if options.sections else []
|
||||
backup_sections = options.sections.split(",") if options.sections else []
|
||||
|
||||
all_sections = [backup_item.backup_name for backup_item in backup.backup_list]
|
||||
if not backup_sections:
|
||||
backup_sections = all_sections
|
||||
else:
|
||||
for b in backup_sections:
|
||||
if not b in all_sections:
|
||||
raise Exception('Section %s is not defined in config file' % b)
|
||||
if b not in all_sections:
|
||||
raise Exception("Section %s is not defined in config file" % b)
|
||||
|
||||
if dry_run:
|
||||
logger.warning("WARNING : DRY RUN, nothing will be done, just printing on screen...")
|
||||
@ -409,23 +431,22 @@ def main():
|
||||
backup.process_backup(backup_sections)
|
||||
elif action == "exportbackup":
|
||||
if not options.exportdir:
|
||||
raise Exception('No export directory supplied dor exportbackup action')
|
||||
backup.export_backups(backup_sections,options.exportdir)
|
||||
raise Exception("No export directory supplied dor exportbackup action")
|
||||
backup.export_backups(backup_sections, options.exportdir)
|
||||
elif action == "cleanup":
|
||||
backup.cleanup_backup_section(backup_sections)
|
||||
elif action == "checknagios":
|
||||
backup.checknagios(backup_sections)
|
||||
elif action == "dumpstat":
|
||||
for s in backup_sections:
|
||||
backup.dbstat.last_backups(s,count=options.statscount)
|
||||
backup.dbstat.last_backups(s, count=options.statscount)
|
||||
elif action == "retryfailed":
|
||||
backup.retry_failed_backups()
|
||||
elif action == "register_existing":
|
||||
backup.register_existingbackups(backup_sections)
|
||||
|
||||
|
||||
else:
|
||||
logger.error('Unhandled action "%s", quitting...',action)
|
||||
logger.error('Unhandled action "%s", quitting...', action)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
404
tisbackup_gui.py
404
tisbackup_gui.py
@ -17,89 +17,89 @@
|
||||
# along with TISBackup. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
import os,sys
|
||||
import os
|
||||
import sys
|
||||
from os.path import isfile, join
|
||||
|
||||
tisbackup_root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__)))
|
||||
sys.path.append(os.path.join(tisbackup_root_dir,'lib'))
|
||||
sys.path.append(os.path.join(tisbackup_root_dir,'libtisbackup'))
|
||||
sys.path.append(os.path.join(tisbackup_root_dir, "lib"))
|
||||
sys.path.append(os.path.join(tisbackup_root_dir, "libtisbackup"))
|
||||
|
||||
|
||||
from shutil import *
|
||||
from iniparse import ConfigParser,RawConfigParser
|
||||
from libtisbackup.common import *
|
||||
import time
|
||||
from flask import request, Flask, session, g, appcontext_pushed, redirect, url_for, abort, render_template, flash, jsonify, Response
|
||||
from urllib.parse import urlparse
|
||||
import json
|
||||
import glob
|
||||
import time
|
||||
|
||||
from config import huey
|
||||
from tasks import run_export_backup, get_task, set_task
|
||||
|
||||
from tisbackup import tis_backup
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
from shutil import *
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from flask import Flask, Response, abort, appcontext_pushed, flash, g, jsonify, redirect, render_template, request, session, url_for
|
||||
from iniparse import ConfigParser, RawConfigParser
|
||||
|
||||
from config import huey
|
||||
from libtisbackup.common import *
|
||||
from tasks import get_task, run_export_backup, set_task
|
||||
from tisbackup import tis_backup
|
||||
|
||||
cp = ConfigParser()
|
||||
cp.read("/etc/tis/tisbackup_gui.ini")
|
||||
|
||||
CONFIG = cp.get('general','config_tisbackup').split(",")
|
||||
SECTIONS = cp.get('general','sections')
|
||||
ADMIN_EMAIL = cp.get('general','ADMIN_EMAIL')
|
||||
BASE_DIR = cp.get('general','base_config_dir')
|
||||
CONFIG = cp.get("general", "config_tisbackup").split(",")
|
||||
SECTIONS = cp.get("general", "sections")
|
||||
ADMIN_EMAIL = cp.get("general", "ADMIN_EMAIL")
|
||||
BASE_DIR = cp.get("general", "base_config_dir")
|
||||
|
||||
tisbackup_config_file= CONFIG[0]
|
||||
config_number=0
|
||||
tisbackup_config_file = CONFIG[0]
|
||||
config_number = 0
|
||||
|
||||
cp = ConfigParser()
|
||||
cp.read(tisbackup_config_file)
|
||||
backup_base_dir = cp.get('global','backup_base_dir')
|
||||
dbstat = BackupStat(os.path.join(backup_base_dir,'log','tisbackup.sqlite'))
|
||||
backup_base_dir = cp.get("global", "backup_base_dir")
|
||||
dbstat = BackupStat(os.path.join(backup_base_dir, "log", "tisbackup.sqlite"))
|
||||
mindate = None
|
||||
error = None
|
||||
info = None
|
||||
app = Flask(__name__)
|
||||
app.secret_key = 'fsiqefiuqsefARZ4Zfesfe34234dfzefzfe'
|
||||
app.config['PROPAGATE_EXCEPTIONS'] = True
|
||||
app.secret_key = "fsiqefiuqsefARZ4Zfesfe34234dfzefzfe"
|
||||
app.config["PROPAGATE_EXCEPTIONS"] = True
|
||||
|
||||
tasks_db = os.path.join(tisbackup_root_dir,"tasks.sqlite")
|
||||
tasks_db = os.path.join(tisbackup_root_dir, "tasks.sqlite")
|
||||
|
||||
|
||||
def read_all_configs(base_dir):
|
||||
raw_configs = []
|
||||
list_config = []
|
||||
config_base_dir = base_dir
|
||||
|
||||
# config_base_dir = base_dir
|
||||
|
||||
for file in os.listdir(base_dir):
|
||||
if isfile(join(base_dir,file)):
|
||||
raw_configs.append(join(base_dir,file))
|
||||
|
||||
if isfile(join(base_dir, file)):
|
||||
raw_configs.append(join(base_dir, file))
|
||||
|
||||
for elem in raw_configs:
|
||||
line = open(elem).readline()
|
||||
if 'global' in line:
|
||||
if "global" in line:
|
||||
list_config.append(elem)
|
||||
|
||||
backup_dict = {}
|
||||
backup_dict['rsync_ssh_list'] = []
|
||||
backup_dict['rsync_btrfs_list'] = []
|
||||
backup_dict['rsync_list'] = []
|
||||
backup_dict['null_list'] = []
|
||||
backup_dict['pgsql_list'] = []
|
||||
backup_dict['mysql_list'] = []
|
||||
#backup_dict['sqlserver_list'] = []
|
||||
backup_dict['xva_list'] = []
|
||||
backup_dict['metadata_list'] = []
|
||||
#backup_dict['switch_list'] = []
|
||||
#backup_dict['oracle_list'] = []
|
||||
backup_dict["rsync_ssh_list"] = []
|
||||
backup_dict["rsync_btrfs_list"] = []
|
||||
backup_dict["rsync_list"] = []
|
||||
backup_dict["null_list"] = []
|
||||
backup_dict["pgsql_list"] = []
|
||||
backup_dict["mysql_list"] = []
|
||||
# backup_dict['sqlserver_list'] = []
|
||||
backup_dict["xva_list"] = []
|
||||
backup_dict["metadata_list"] = []
|
||||
# backup_dict['switch_list'] = []
|
||||
# backup_dict['oracle_list'] = []
|
||||
|
||||
result = []
|
||||
cp = ConfigParser()
|
||||
for config_file in list_config:
|
||||
cp.read(config_file)
|
||||
|
||||
backup_base_dir = cp.get('global', 'backup_base_dir')
|
||||
backup_base_dir = cp.get("global", "backup_base_dir")
|
||||
backup = tis_backup(backup_base_dir=backup_base_dir)
|
||||
backup.read_ini_file(config_file)
|
||||
|
||||
@ -110,11 +110,12 @@ def read_all_configs(base_dir):
|
||||
backup_sections = all_sections
|
||||
else:
|
||||
for b in backup_sections:
|
||||
if not b in all_sections:
|
||||
raise Exception('Section %s is not defined in config file' % b)
|
||||
if b not in all_sections:
|
||||
raise Exception("Section %s is not defined in config file" % b)
|
||||
|
||||
if not backup_sections:
|
||||
sections = [backup_item.backup_name for backup_item in backup.backup_list]
|
||||
# never used..
|
||||
# if not backup_sections:
|
||||
# sections = [backup_item.backup_name for backup_item in backup.backup_list]
|
||||
|
||||
for backup_item in backup.backup_list:
|
||||
if backup_item.backup_name in backup_sections:
|
||||
@ -125,35 +126,28 @@ def read_all_configs(base_dir):
|
||||
result.append(b)
|
||||
|
||||
for row in result:
|
||||
backup_name = row['backup_name']
|
||||
server_name = row['server_name']
|
||||
backup_type = row['type']
|
||||
backup_name = row["backup_name"]
|
||||
server_name = row["server_name"]
|
||||
backup_type = row["type"]
|
||||
if backup_type == "xcp-dump-metadata":
|
||||
backup_dict['metadata_list'].append(
|
||||
[server_name, backup_name, backup_type, ""])
|
||||
backup_dict["metadata_list"].append([server_name, backup_name, backup_type, ""])
|
||||
if backup_type == "rsync+ssh":
|
||||
remote_dir = row['remote_dir']
|
||||
backup_dict['rsync_ssh_list'].append(
|
||||
[server_name, backup_name, backup_type, remote_dir])
|
||||
remote_dir = row["remote_dir"]
|
||||
backup_dict["rsync_ssh_list"].append([server_name, backup_name, backup_type, remote_dir])
|
||||
if backup_type == "rsync+btrfs+ssh":
|
||||
remote_dir = row['remote_dir']
|
||||
backup_dict['rsync_btrfs_list'].append(
|
||||
[server_name, backup_name, backup_type, remote_dir])
|
||||
remote_dir = row["remote_dir"]
|
||||
backup_dict["rsync_btrfs_list"].append([server_name, backup_name, backup_type, remote_dir])
|
||||
if backup_type == "rsync":
|
||||
remote_dir = row['remote_dir']
|
||||
backup_dict['rsync_list'].append(
|
||||
[server_name, backup_name, backup_type, remote_dir])
|
||||
remote_dir = row["remote_dir"]
|
||||
backup_dict["rsync_list"].append([server_name, backup_name, backup_type, remote_dir])
|
||||
if backup_type == "null":
|
||||
backup_dict['null_list'].append(
|
||||
[server_name, backup_name, backup_type, ""])
|
||||
backup_dict["null_list"].append([server_name, backup_name, backup_type, ""])
|
||||
if backup_type == "pgsql+ssh":
|
||||
db_name = row['db_name'] if len(row['db_name']) > 0 else '*'
|
||||
backup_dict['pgsql_list'].append(
|
||||
[server_name, backup_name, backup_type, db_name])
|
||||
db_name = row["db_name"] if len(row["db_name"]) > 0 else "*"
|
||||
backup_dict["pgsql_list"].append([server_name, backup_name, backup_type, db_name])
|
||||
if backup_type == "mysql+ssh":
|
||||
db_name = row['db_name'] if len(row['db_name']) > 0 else '*'
|
||||
backup_dict['mysql_list'].append(
|
||||
[server_name, backup_name, backup_type, db_name])
|
||||
db_name = row["db_name"] if len(row["db_name"]) > 0 else "*"
|
||||
backup_dict["mysql_list"].append([server_name, backup_name, backup_type, db_name])
|
||||
# if backup_type == "sqlserver+ssh":
|
||||
# db_name = row['db_name']
|
||||
# backup_dict['sqlserver_list'].append(
|
||||
@ -163,12 +157,11 @@ def read_all_configs(base_dir):
|
||||
# backup_dict['oracle_list'].append(
|
||||
# [server_name, backup_name, backup_type, db_name])
|
||||
if backup_type == "xen-xva":
|
||||
backup_dict['xva_list'].append(
|
||||
[server_name, backup_name, backup_type, ""])
|
||||
backup_dict["xva_list"].append([server_name, backup_name, backup_type, ""])
|
||||
# if backup_type == "switch":
|
||||
# backup_dict['switch_list'].append(
|
||||
# [server_name, backup_name, backup_type, ""])
|
||||
|
||||
|
||||
return backup_dict
|
||||
|
||||
|
||||
@ -177,7 +170,7 @@ def read_config():
|
||||
cp = ConfigParser()
|
||||
cp.read(config_file)
|
||||
|
||||
backup_base_dir = cp.get('global','backup_base_dir')
|
||||
backup_base_dir = cp.get("global", "backup_base_dir")
|
||||
backup = tis_backup(backup_base_dir=backup_base_dir)
|
||||
backup.read_ini_file(config_file)
|
||||
|
||||
@ -188,56 +181,58 @@ def read_config():
|
||||
backup_sections = all_sections
|
||||
else:
|
||||
for b in backup_sections:
|
||||
if not b in all_sections:
|
||||
raise Exception('Section %s is not defined in config file' % b)
|
||||
if b not in all_sections:
|
||||
raise Exception("Section %s is not defined in config file" % b)
|
||||
|
||||
result = []
|
||||
if not backup_sections:
|
||||
sections = [backup_item.backup_name for backup_item in backup.backup_list]
|
||||
|
||||
# not used ...
|
||||
# if not backup_sections:
|
||||
# sections = [backup_item.backup_name for backup_item in backup.backup_list]
|
||||
|
||||
for backup_item in backup.backup_list:
|
||||
if backup_item.backup_name in backup_sections:
|
||||
b = {}
|
||||
for attrib_name in backup_item.required_params+backup_item.optional_params:
|
||||
if hasattr(backup_item,attrib_name):
|
||||
b[attrib_name] = getattr(backup_item,attrib_name)
|
||||
for attrib_name in backup_item.required_params + backup_item.optional_params:
|
||||
if hasattr(backup_item, attrib_name):
|
||||
b[attrib_name] = getattr(backup_item, attrib_name)
|
||||
result.append(b)
|
||||
|
||||
backup_dict = {}
|
||||
backup_dict['rsync_ssh_list'] = []
|
||||
backup_dict['rsync_btrfs_list'] = []
|
||||
backup_dict['rsync_list'] = []
|
||||
backup_dict['null_list'] = []
|
||||
backup_dict['pgsql_list'] = []
|
||||
backup_dict['mysql_list'] = []
|
||||
#backup_dict['sqlserver_list'] = []
|
||||
backup_dict['xva_list'] = []
|
||||
backup_dict['metadata_list'] = []
|
||||
#backup_dict['switch_list'] = []
|
||||
#backup_dict['oracle_list'] = []
|
||||
backup_dict["rsync_ssh_list"] = []
|
||||
backup_dict["rsync_btrfs_list"] = []
|
||||
backup_dict["rsync_list"] = []
|
||||
backup_dict["null_list"] = []
|
||||
backup_dict["pgsql_list"] = []
|
||||
backup_dict["mysql_list"] = []
|
||||
# backup_dict['sqlserver_list'] = []
|
||||
backup_dict["xva_list"] = []
|
||||
backup_dict["metadata_list"] = []
|
||||
# backup_dict['switch_list'] = []
|
||||
# backup_dict['oracle_list'] = []
|
||||
for row in result:
|
||||
backup_name = row['backup_name']
|
||||
server_name = row['server_name']
|
||||
backup_type = row['type']
|
||||
backup_name = row["backup_name"]
|
||||
server_name = row["server_name"]
|
||||
backup_type = row["type"]
|
||||
if backup_type == "xcp-dump-metadata":
|
||||
backup_dict['metadata_list'].append([server_name, backup_name, backup_type, ""])
|
||||
backup_dict["metadata_list"].append([server_name, backup_name, backup_type, ""])
|
||||
if backup_type == "rsync+ssh":
|
||||
remote_dir = row['remote_dir']
|
||||
backup_dict['rsync_ssh_list'].append([server_name, backup_name, backup_type,remote_dir])
|
||||
remote_dir = row["remote_dir"]
|
||||
backup_dict["rsync_ssh_list"].append([server_name, backup_name, backup_type, remote_dir])
|
||||
if backup_type == "rsync+btrfs+ssh":
|
||||
remote_dir = row['remote_dir']
|
||||
backup_dict['rsync_btrfs_list'].append([server_name, backup_name, backup_type,remote_dir])
|
||||
remote_dir = row["remote_dir"]
|
||||
backup_dict["rsync_btrfs_list"].append([server_name, backup_name, backup_type, remote_dir])
|
||||
if backup_type == "rsync":
|
||||
remote_dir = row['remote_dir']
|
||||
backup_dict['rsync_list'].append([server_name, backup_name, backup_type,remote_dir])
|
||||
remote_dir = row["remote_dir"]
|
||||
backup_dict["rsync_list"].append([server_name, backup_name, backup_type, remote_dir])
|
||||
if backup_type == "null":
|
||||
backup_dict['null_list'].append([server_name, backup_name, backup_type, ""])
|
||||
backup_dict["null_list"].append([server_name, backup_name, backup_type, ""])
|
||||
if backup_type == "pgsql+ssh":
|
||||
db_name = row['db_name'] if len(row['db_name']) > 0 else '*'
|
||||
backup_dict['pgsql_list'].append([server_name, backup_name, backup_type, db_name])
|
||||
db_name = row["db_name"] if len(row["db_name"]) > 0 else "*"
|
||||
backup_dict["pgsql_list"].append([server_name, backup_name, backup_type, db_name])
|
||||
if backup_type == "mysql+ssh":
|
||||
db_name = row['db_name'] if len(row['db_name']) > 0 else '*'
|
||||
backup_dict['mysql_list'].append([server_name, backup_name, backup_type, db_name])
|
||||
db_name = row["db_name"] if len(row["db_name"]) > 0 else "*"
|
||||
backup_dict["mysql_list"].append([server_name, backup_name, backup_type, db_name])
|
||||
# if backup_type == "sqlserver+ssh":
|
||||
# db_name = row['db_name']
|
||||
# backup_dict['sqlserver_list'].append([server_name, backup_name, backup_type, db_name])
|
||||
@ -245,49 +240,68 @@ def read_config():
|
||||
# db_name = row['db_name']
|
||||
# backup_dict['oracle_list'].append([server_name, backup_name, backup_type, db_name])
|
||||
if backup_type == "xen-xva":
|
||||
backup_dict['xva_list'].append([server_name, backup_name, backup_type, ""])
|
||||
backup_dict["xva_list"].append([server_name, backup_name, backup_type, ""])
|
||||
# if backup_type == "switch":
|
||||
# backup_dict['switch_list'].append([server_name, backup_name, backup_type, ""])
|
||||
return backup_dict
|
||||
|
||||
@app.route('/')
|
||||
|
||||
@app.route("/")
|
||||
def backup_all():
|
||||
backup_dict = read_config()
|
||||
return render_template('backups.html', backup_list = backup_dict)
|
||||
return render_template("backups.html", backup_list=backup_dict)
|
||||
|
||||
|
||||
@app.route('/config_number/')
|
||||
@app.route('/config_number/<int:id>')
|
||||
@app.route("/config_number/")
|
||||
@app.route("/config_number/<int:id>")
|
||||
def set_config_number(id=None):
|
||||
if id != None and len(CONFIG) > id:
|
||||
if id is not None and len(CONFIG) > id:
|
||||
global config_number
|
||||
config_number=id
|
||||
config_number = id
|
||||
read_config()
|
||||
return jsonify(configs=CONFIG,config_number=config_number)
|
||||
return jsonify(configs=CONFIG, config_number=config_number)
|
||||
|
||||
|
||||
@app.route('/all_json')
|
||||
@app.route("/all_json")
|
||||
def backup_all_json():
|
||||
backup_dict = read_all_configs(BASE_DIR)
|
||||
return json.dumps(backup_dict['rsync_list']+backup_dict['rsync_btrfs_list']+backup_dict['rsync_ssh_list']+backup_dict['pgsql_list']+backup_dict['mysql_list']+backup_dict['xva_list']+backup_dict['null_list']+backup_dict['metadata_list'])
|
||||
#+ backup_dict['switch_list'])+backup_dict['sqlserver_list']
|
||||
return json.dumps(
|
||||
backup_dict["rsync_list"]
|
||||
+ backup_dict["rsync_btrfs_list"]
|
||||
+ backup_dict["rsync_ssh_list"]
|
||||
+ backup_dict["pgsql_list"]
|
||||
+ backup_dict["mysql_list"]
|
||||
+ backup_dict["xva_list"]
|
||||
+ backup_dict["null_list"]
|
||||
+ backup_dict["metadata_list"]
|
||||
)
|
||||
# + backup_dict['switch_list'])+backup_dict['sqlserver_list']
|
||||
|
||||
|
||||
@app.route('/json')
|
||||
@app.route("/json")
|
||||
def backup_json():
|
||||
backup_dict = read_config()
|
||||
return json.dumps(backup_dict['rsync_list']+backup_dict['rsync_btrfs_list']+backup_dict['rsync_ssh_list']+backup_dict['pgsql_list']+backup_dict['mysql_list']+backup_dict['xva_list']+backup_dict['null_list']+backup_dict['metadata_list'])
|
||||
#+ backup_dict['switch_list'])+backup_dict['sqlserver_list']
|
||||
return json.dumps(
|
||||
backup_dict["rsync_list"]
|
||||
+ backup_dict["rsync_btrfs_list"]
|
||||
+ backup_dict["rsync_ssh_list"]
|
||||
+ backup_dict["pgsql_list"]
|
||||
+ backup_dict["mysql_list"]
|
||||
+ backup_dict["xva_list"]
|
||||
+ backup_dict["null_list"]
|
||||
+ backup_dict["metadata_list"]
|
||||
)
|
||||
# + backup_dict['switch_list'])+backup_dict['sqlserver_list']
|
||||
|
||||
|
||||
def check_usb_disk():
|
||||
"""This method returns the mounts point of FIRST external disk"""
|
||||
# disk_name = []
|
||||
# disk_name = []
|
||||
usb_disk_list = []
|
||||
for name in glob.glob('/dev/sd[a-z]'):
|
||||
for name in glob.glob("/dev/sd[a-z]"):
|
||||
for line in os.popen("udevadm info -q env -n %s" % name):
|
||||
if re.match("ID_PATH=.*usb.*", line):
|
||||
usb_disk_list += [ name ]
|
||||
usb_disk_list += [name]
|
||||
|
||||
if len(usb_disk_list) == 0:
|
||||
raise_error("Cannot find any external usb disk", "You should plug the usb hard drive into the server")
|
||||
@ -296,20 +310,23 @@ def check_usb_disk():
|
||||
|
||||
usb_partition_list = []
|
||||
for usb_disk in usb_disk_list:
|
||||
cmd = "udevadm info -q path -n %s" % usb_disk + '1'
|
||||
output = os.popen(cmd).read()
|
||||
cmd = "udevadm info -q path -n %s" % usb_disk + "1"
|
||||
output = os.popen(cmd).read()
|
||||
print("cmd : " + cmd)
|
||||
print("output : " + output)
|
||||
|
||||
if '/devices/pci' in output:
|
||||
#flash("partition found: %s1" % usb_disk)
|
||||
if "/devices/pci" in output:
|
||||
# flash("partition found: %s1" % usb_disk)
|
||||
usb_partition_list.append(usb_disk + "1")
|
||||
|
||||
print(usb_partition_list)
|
||||
|
||||
if len(usb_partition_list) ==0:
|
||||
raise_error("The drive %s has no partition" % (usb_disk_list[0] ), "You should initialize the usb drive and format an ext4 partition with TISBACKUP label")
|
||||
return ""
|
||||
if len(usb_partition_list) == 0:
|
||||
raise_error(
|
||||
"The drive %s has no partition" % (usb_disk_list[0]),
|
||||
"You should initialize the usb drive and format an ext4 partition with TISBACKUP label",
|
||||
)
|
||||
return ""
|
||||
|
||||
tisbackup_partition_list = []
|
||||
for usb_partition in usb_partition_list:
|
||||
@ -317,133 +334,139 @@ def check_usb_disk():
|
||||
flash("tisbackup backup partition found: %s" % usb_partition)
|
||||
tisbackup_partition_list.append(usb_partition)
|
||||
|
||||
print(tisbackup_partition_list)
|
||||
print(tisbackup_partition_list)
|
||||
|
||||
if len(tisbackup_partition_list) ==0:
|
||||
raise_error("No tisbackup partition exist on disk %s" % (usb_disk_list[0] ), "You should initialize the usb drive and format an ext4 partition with TISBACKUP label")
|
||||
if len(tisbackup_partition_list) == 0:
|
||||
raise_error(
|
||||
"No tisbackup partition exist on disk %s" % (usb_disk_list[0]),
|
||||
"You should initialize the usb drive and format an ext4 partition with TISBACKUP label",
|
||||
)
|
||||
return ""
|
||||
|
||||
if len(tisbackup_partition_list) > 1:
|
||||
if len(tisbackup_partition_list) > 1:
|
||||
raise_error("There are many usb disk", "You should plug remove one of them")
|
||||
return ""
|
||||
|
||||
|
||||
return tisbackup_partition_list[0]
|
||||
|
||||
|
||||
def check_already_mount(partition_name,refresh):
|
||||
with open('/proc/mounts') as f:
|
||||
def check_already_mount(partition_name, refresh):
|
||||
with open("/proc/mounts") as f:
|
||||
mount_point = ""
|
||||
for line in f.readlines():
|
||||
if line.startswith(partition_name):
|
||||
mount_point = line.split(' ')[1]
|
||||
mount_point = line.split(" ")[1]
|
||||
if not refresh:
|
||||
run_command("/bin/umount %s" % mount_point)
|
||||
os.rmdir(mount_point)
|
||||
os.rmdir(mount_point)
|
||||
return mount_point
|
||||
|
||||
|
||||
def run_command(cmd, info=""):
|
||||
flash("Executing: %s"% cmd)
|
||||
from subprocess import CalledProcessError, check_output
|
||||
result =""
|
||||
flash("Executing: %s" % cmd)
|
||||
from subprocess import CalledProcessError, check_output
|
||||
|
||||
result = ""
|
||||
try:
|
||||
result = check_output(cmd, stderr=subprocess.STDOUT,shell=True)
|
||||
except CalledProcessError as e:
|
||||
raise_error(result,info)
|
||||
result = check_output(cmd, stderr=subprocess.STDOUT, shell=True)
|
||||
except CalledProcessError:
|
||||
raise_error(result, info)
|
||||
return result
|
||||
|
||||
def check_mount_disk(partition_name, refresh):
|
||||
|
||||
mount_point = check_already_mount(partition_name, refresh)
|
||||
if not refresh:
|
||||
|
||||
|
||||
mount_point = "/mnt/TISBACKUP-" +str(time.time())
|
||||
def check_mount_disk(partition_name, refresh):
|
||||
|
||||
mount_point = check_already_mount(partition_name, refresh)
|
||||
if not refresh:
|
||||
|
||||
mount_point = "/mnt/TISBACKUP-" + str(time.time())
|
||||
os.mkdir(mount_point)
|
||||
flash("must mount " + partition_name )
|
||||
flash("must mount " + partition_name)
|
||||
cmd = "mount %s %s" % (partition_name, mount_point)
|
||||
if run_command(cmd,"You should manualy mount the usb drive") != "":
|
||||
if run_command(cmd, "You should manualy mount the usb drive") != "":
|
||||
flash("Remove directory: %s" % mount_point)
|
||||
os.rmdir(mount_point)
|
||||
return ""
|
||||
os.rmdir(mount_point)
|
||||
return ""
|
||||
|
||||
return mount_point
|
||||
|
||||
@app.route('/status.json')
|
||||
|
||||
@app.route("/status.json")
|
||||
def export_backup_status():
|
||||
exports = dbstat.query('select * from stats where TYPE="EXPORT" and backup_start>="%s"' % mindate)
|
||||
error = ""
|
||||
finish=not runnings_backups()
|
||||
if get_task() != None and finish:
|
||||
finish = not runnings_backups()
|
||||
if get_task() is not None and finish:
|
||||
status = get_task().get()
|
||||
if status != "ok":
|
||||
error = "Export failing with error: "+status
|
||||
error = "Export failing with error: " + status
|
||||
|
||||
return jsonify(data=exports, finish=finish, error=error)
|
||||
|
||||
|
||||
return jsonify(data=exports,finish=finish,error=error)
|
||||
|
||||
def runnings_backups():
|
||||
task = get_task()
|
||||
is_runnig = (task != None)
|
||||
finish = ( is_runnig and task.get() != None)
|
||||
task = get_task()
|
||||
is_runnig = task is not None
|
||||
finish = is_runnig and task.get() is not None
|
||||
return is_runnig and not finish
|
||||
|
||||
|
||||
@app.route('/backups.json')
|
||||
@app.route("/backups.json")
|
||||
def last_backup_json():
|
||||
exports = dbstat.query('select * from stats where TYPE="BACKUP" ORDER BY backup_start DESC ')
|
||||
return Response(response=json.dumps(exports),
|
||||
status=200,
|
||||
mimetype="application/json")
|
||||
return Response(response=json.dumps(exports), status=200, mimetype="application/json")
|
||||
|
||||
|
||||
@app.route('/last_backups')
|
||||
@app.route("/last_backups")
|
||||
def last_backup():
|
||||
exports = dbstat.query('select * from stats where TYPE="BACKUP" ORDER BY backup_start DESC LIMIT 20 ')
|
||||
return render_template("last_backups.html", backups=exports)
|
||||
|
||||
|
||||
@app.route('/export_backup')
|
||||
@app.route("/export_backup")
|
||||
def export_backup():
|
||||
|
||||
|
||||
raise_error("", "")
|
||||
backup_dict = read_config()
|
||||
sections = []
|
||||
backup_sections = []
|
||||
for backup_types in backup_dict:
|
||||
for backup_types in backup_dict:
|
||||
if backup_types == "null_list":
|
||||
continue
|
||||
for section in backup_dict[backup_types]:
|
||||
#if section.count > 0:
|
||||
# if section.count > 0:
|
||||
if len(section) > 0:
|
||||
sections.append(section[1])
|
||||
|
||||
noJobs = (not runnings_backups())
|
||||
noJobs = not runnings_backups()
|
||||
if "start" in list(request.args.keys()) or not noJobs:
|
||||
start=True
|
||||
start = True
|
||||
if "sections" in list(request.args.keys()):
|
||||
backup_sections = request.args.getlist('sections')
|
||||
|
||||
backup_sections = request.args.getlist("sections")
|
||||
|
||||
else:
|
||||
start=False
|
||||
start = False
|
||||
cp.read(tisbackup_config_file)
|
||||
|
||||
partition_name = check_usb_disk()
|
||||
if partition_name:
|
||||
if noJobs:
|
||||
mount_point = check_mount_disk( partition_name, False)
|
||||
mount_point = check_mount_disk(partition_name, False)
|
||||
else:
|
||||
mount_point = check_mount_disk( partition_name, True)
|
||||
mount_point = check_mount_disk(partition_name, True)
|
||||
if noJobs:
|
||||
global mindate
|
||||
mindate = datetime2isodate(datetime.datetime.now())
|
||||
global mindate
|
||||
mindate = datetime2isodate(datetime.datetime.now())
|
||||
if not error and start:
|
||||
print(tisbackup_config_file)
|
||||
task = run_export_backup(base=backup_base_dir, config_file=CONFIG[config_number], mount_point=mount_point, backup_sections=",".join([str(x) for x in backup_sections]))
|
||||
task = run_export_backup(
|
||||
base=backup_base_dir,
|
||||
config_file=CONFIG[config_number],
|
||||
mount_point=mount_point,
|
||||
backup_sections=",".join([str(x) for x in backup_sections]),
|
||||
)
|
||||
set_task(task)
|
||||
|
||||
|
||||
|
||||
return render_template("export_backup.html", error=error, start=start, info=info, email=ADMIN_EMAIL, sections=sections)
|
||||
|
||||
|
||||
@ -453,9 +476,10 @@ def raise_error(strError, strInfo):
|
||||
info = strInfo
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == "__main__":
|
||||
read_config()
|
||||
from os import environ
|
||||
if 'WINGDB_ACTIVE' in environ:
|
||||
|
||||
if "WINGDB_ACTIVE" in environ:
|
||||
app.debug = False
|
||||
app.run(host= '0.0.0.0',port=8080)
|
||||
app.run(host="0.0.0.0", port=8080)
|
||||
|
Loading…
Reference in New Issue
Block a user