Merge branch 'my_master'
This commit is contained in:
commit
885a7682fb
|
@ -0,0 +1,33 @@
|
||||||
|
# backive
|
||||||
|
|
||||||
|
The name comes from the combination of backup and archive - silly, I know.
|
||||||
|
|
||||||
|
# Purpose
|
||||||
|
|
||||||
|
I've a long-term backup strategy where I put some labeled hard-disk in a SATA docking station and run a backup routine. When done, this hard-disk goes back into some shelf in my attic or basement. When the time is come again to update the long-term backup the same procedure starts again.
|
||||||
|
|
||||||
|
So now there are my backup routines, which are manually currently - **and that sucks.**
|
||||||
|
|
||||||
|
So what this tool or service should do is the following:
|
||||||
|
- I am able to configure based on UUIDs of the partitions which devices are allowed for backup
|
||||||
|
- I can specify as much backup items as I want, which should include:
|
||||||
|
- Backup local and remote data (Linux machine and SSH required)
|
||||||
|
- Use the best tool available for the task (rsync, duplicity, whatever)
|
||||||
|
- Even be able to "backup" without a target device (choose another path on the system)
|
||||||
|
- (optional) Be able to run pre-backup commands (for databases maybe), remote too
|
||||||
|
- The service has to be able to automatically detect the presence of a hard-disk, mounting it, running the backup routine and unmounting
|
||||||
|
- Good logging about the process
|
||||||
|
|
||||||
|
|
||||||
|
What I currently see as optional:
|
||||||
|
- Notification about the finished process (got conky running anyway, where I see the disks)
|
||||||
|
|
||||||
|
## Technical goals
|
||||||
|
|
||||||
|
- systemd service
|
||||||
|
- udev rules for notifications about new drives
|
||||||
|
- Python package, as easy to install as possible
|
||||||
|
|
||||||
|
# Current state
|
||||||
|
|
||||||
|
In the very beginning...
|
|
@ -0,0 +1,72 @@
|
||||||
|
from ruamel.yaml import YAML
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import signal
|
||||||
|
import os
|
||||||
|
import pwd
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig(format='%(asctime)s->%(name)s@%(levelname)s: %(message)s', filename=sys.stdout, level=logging.INFO)
|
||||||
|
|
||||||
|
got_signal = False
|
||||||
|
disks_by_uuid = "/dev/disk/by-uuid"
|
||||||
|
|
||||||
|
|
||||||
|
class MBD:
|
||||||
|
def __init__(self):
|
||||||
|
self._config = None
|
||||||
|
# who are we?
|
||||||
|
uid = os.getuid()
|
||||||
|
# name?
|
||||||
|
user = pwd.getpwuid(uid).pw_name
|
||||||
|
try:
|
||||||
|
if uid == 0:
|
||||||
|
config_file = "/etc/mbd.yml"
|
||||||
|
else:
|
||||||
|
config_file = os.path.join(os.path.expanduser("~"), ".mbd", "mbd.yml")
|
||||||
|
pass
|
||||||
|
|
||||||
|
with open(config_file, "r") as cfg:
|
||||||
|
self._config = YAML().load(cfg)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(e)
|
||||||
|
|
||||||
|
def get_list_of_devices(self):
|
||||||
|
if os.path.exists(disks_by_uuid):
|
||||||
|
uuids = os.listdir(disks_by_uuid)
|
||||||
|
return uuids
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_backup_list(self):
|
||||||
|
defaults = self._config.get("defaults", None)
|
||||||
|
if defaults:
|
||||||
|
mount_root = defaults.get("mount_root", "/media")
|
||||||
|
else:
|
||||||
|
mount_root = "/media"
|
||||||
|
|
||||||
|
devices = self._config.get("devices", dict())
|
||||||
|
for d in devices:
|
||||||
|
if d in self.get_list_of_devices():
|
||||||
|
# do backup to this device
|
||||||
|
# mount it
|
||||||
|
mount = os.path.join(mount_root, devices[d].get("mountname"))
|
||||||
|
os.system("mount {byuuid}/{uuid} {mount}".format(
|
||||||
|
byuuid=disks_by_uuid, uuid=d, mount=mount))
|
||||||
|
last_backup_file = os.path.join(mount, devices[d].get("backup").get("target"), ".mdb.last")
|
||||||
|
if os.path.exists(last_backup_file):
|
||||||
|
with open(last_backup_file, "r") as f:
|
||||||
|
last_backup = f.read()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def signal_handler(signum, frame):
|
||||||
|
got_signal = True
|
||||||
|
|
||||||
|
|
||||||
|
signal.signal(signal.SIGHUP, signal_handler)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pass
|
|
@ -0,0 +1,61 @@
|
||||||
|
import os
|
||||||
|
import pwd
|
||||||
|
from ruamel.yaml import YAML
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from backive.core.backup import Backup
|
||||||
|
from backive.core.device import Device
|
||||||
|
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._config = dict()
|
||||||
|
|
||||||
|
def find_config(self):
|
||||||
|
# who are we?
|
||||||
|
uid = os.getuid()
|
||||||
|
# name?
|
||||||
|
user = pwd.getpwuid(uid).pw_name
|
||||||
|
try:
|
||||||
|
if uid == 0:
|
||||||
|
config_file = "/etc/backive.yml"
|
||||||
|
else:
|
||||||
|
config_file = os.path.join(os.path.expanduser("~"), ".backive", "backive.yml")
|
||||||
|
pass
|
||||||
|
|
||||||
|
with open(config_file, "r") as cfg:
|
||||||
|
self._config = YAML().load(cfg)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(e)
|
||||||
|
|
||||||
|
def get_devices(self):
|
||||||
|
devices = []
|
||||||
|
if self._config.get("devices", None):
|
||||||
|
data = self._config.get("devices")
|
||||||
|
for device in data:
|
||||||
|
devices.append(
|
||||||
|
Device.instance(
|
||||||
|
device,
|
||||||
|
data.get(device)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return devices
|
||||||
|
|
||||||
|
def get_backups(self):
|
||||||
|
backups = []
|
||||||
|
if self._config.get("backups", None):
|
||||||
|
data = self._config.get("backups")
|
||||||
|
for name in data:
|
||||||
|
backups.append(
|
||||||
|
Backup.instance(
|
||||||
|
name,
|
||||||
|
data.get(name)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return backups
|
||||||
|
|
||||||
|
def get_globals(self):
|
||||||
|
if self._config.get("defaults", None):
|
||||||
|
return self._config.get("defaults")
|
||||||
|
return {}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class Backup:
|
||||||
|
|
||||||
|
config = {}
|
||||||
|
|
||||||
|
def __init__(self, name, cfg=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def instance(cls, name, cfg):
|
||||||
|
return Backup(name, cfg)
|
|
@ -0,0 +1,28 @@
|
||||||
|
import os
|
||||||
|
import backive.config.config as cfg
|
||||||
|
|
||||||
|
|
||||||
|
class Device:
|
||||||
|
disks_by_uuid = "/dev/disk/by-uuid"
|
||||||
|
|
||||||
|
def __init__(self, uuid, config=None):
|
||||||
|
self.uuid = uuid
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def instance(cls, uuid, config):
|
||||||
|
return Device(uuid, config)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_list(cls):
|
||||||
|
if os.path.exists(cls.disks_by_uuid):
|
||||||
|
uuids = os.listdir(cls.disks_by_uuid)
|
||||||
|
return uuids
|
||||||
|
return []
|
||||||
|
|
||||||
|
def mount(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def unmount(self):
|
||||||
|
pass
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
AVAILABLE_TOOLS = {}
|
||||||
|
|
||||||
|
def register_tool(name):
|
||||||
|
def decorator(Cls):
|
||||||
|
AVAILABLE_TOOLS.update({name: Cls})
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
class Tool:
|
||||||
|
def __init__(self, options):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def instance(cls, name, options):
|
||||||
|
if name in AVAILABLE_TOOLS:
|
||||||
|
return AVAILABLE_TOOLS.get(name)(options)
|
||||||
|
return None
|
|
@ -0,0 +1,10 @@
|
||||||
|
from backive.core.tool import AVAILABLE_TOOLS, register_tool
|
||||||
|
|
||||||
|
|
||||||
|
@register_tool("rsync")
|
||||||
|
class Rsync:
|
||||||
|
def __init__(self, options):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import setuptools
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
|
VERSION="0.1.0"
|
||||||
|
|
||||||
|
setup_info = dict(
|
||||||
|
name="backive",
|
||||||
|
version=VERSION,
|
||||||
|
author="Marcel M. Otte",
|
||||||
|
author_email="qwc+backive@mmo.to",
|
||||||
|
url="tbd",
|
||||||
|
description="",
|
||||||
|
license="BSD",
|
||||||
|
classifiers=[
|
||||||
|
],
|
||||||
|
packages=find_packages(),
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
setup(**setup_info)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue