From fd52f70015e473524fecb20d685d8ef8a065ce1f Mon Sep 17 00:00:00 2001 From: Marcel Otte Date: Fri, 22 Feb 2019 21:26:01 +0100 Subject: [PATCH] Working on it ... --- backive/backive_service | 5 +++ backive/backive_udev | 5 +++ backive/config/config.py | 22 +++++++++-- backive/config/schema.yml | 77 +++++++++++++++++++++++++++++++++++++++ doc/architecture.puml | 59 ++++++++++++++++++++++++++++++ setup.cfg | 9 +++++ setup.py | 21 +++++++++-- 7 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 backive/backive_service create mode 100644 backive/backive_udev create mode 100644 backive/config/schema.yml create mode 100644 doc/architecture.puml diff --git a/backive/backive_service b/backive/backive_service new file mode 100644 index 0000000..7ec659f --- /dev/null +++ b/backive/backive_service @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +""" +Service startup script. +""" diff --git a/backive/backive_udev b/backive/backive_udev new file mode 100644 index 0000000..b41c127 --- /dev/null +++ b/backive/backive_udev @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +""" +Callable script for udev rules. +""" diff --git a/backive/config/config.py b/backive/config/config.py index 520ae32..9474090 100644 --- a/backive/config/config.py +++ b/backive/config/config.py @@ -2,6 +2,7 @@ import os import pwd from ruamel.yaml import YAML import logging +import jsonschema from backive.core.backup import Backup from backive.core.device import Device @@ -11,6 +12,11 @@ class Config: def __init__(self): self._config = dict() + self._schema = dict() + file_path = os.path.realpath(__file__) + schema_path = os.path.join(file_path, "schema.yml") + with open(schema_path, "r") as stream: + self._schema = YAML().load(stream) def find_config(self): # who are we? @@ -26,6 +32,7 @@ class Config: with open(config_file, "r") as cfg: self._config = YAML().load(cfg) + jsonschema.validate(self._config, self._schema) except Exception as e: logging.error(e) @@ -55,7 +62,16 @@ class Config: ) return backups - def get_globals(self): - if self._config.get("defaults", None): - return self._config.get("defaults") + def get_device_backups(self, device): + uuid = device + device_name = self._config.get("devices").get(uuid).get("name") + backups = [] + for backup in self.get_backups(): + if backup.target == uuid or backup.target == device_name: + backups.append(backup) + return backups + + def get_preferences(self): + if self._config.get("preferences", None): + return self._config.get("preferences") return {} diff --git a/backive/config/schema.yml b/backive/config/schema.yml new file mode 100644 index 0000000..b1e9af2 --- /dev/null +++ b/backive/config/schema.yml @@ -0,0 +1,77 @@ +### backive config schema +# +# + + +definitions: + device_section: + type: object + patternProperties: + "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$": + type: object + properties: + name: + type: string + mountname: + type: string + required: + - mountname + additionalProperties: false + backup_section: + type: object + patternProperties: + "^[a-zA-Z0-9_-]+$": + type: object + properties: + user: + type: string + from: + type: string + from_remote: + type: object + properties: + user: + type: string + password: + type: string + ssh_key_path: + type: string + to: + type: string + target_device: + type: string + frequency: + type: string + scripts: + type: array + items: + type: object + properties: + target: + enum: [ "local", "remote" ] + script: + type: string + additionalProperties: false + tool: + type: object + required: + - from + - to + - target_device + additionalProperties: false + preferences_section: + type: object + properties: + mount_root: + type: string + pattern: "^(/[^/]+)+$" + additionalProperties: false + +type: object +properties: + devices: + "$ref": "#:/definitions/device_section" + backups: + "$ref": "#:/definitions/backup_section" + preferences: + "$ref": "#:/definitions/preferences_section" diff --git a/doc/architecture.puml b/doc/architecture.puml new file mode 100644 index 0000000..1268693 --- /dev/null +++ b/doc/architecture.puml @@ -0,0 +1,59 @@ +@startuml "Overall architecture" + +actor User +control udev +agent backive_udev +interface unix_socket +agent backive_service +file config +storage HDD_in_dockingstation + +User --> HDD_in_dockingstation : 1. plugs in + +udev <--> HDD_in_dockingstation : 2. recognizes hardware change + +udev --> backive_udev : 3. calls with data of the HDD + +backive_udev --> unix_socket : 4. delivers data to interface + +backive_service <--> unix_socket : listens on and awaits data + +backive_service <-- config + +backive_service ==> HDD_in_dockingstation : 5. writes data like configured in config + +@enduml + +@startuml "Class architecture" + +class backive_udev << (S,#FF0000) Script >> + +class backive_service << (S,#FF0000) Script >> + +class Service +note left +Runs known Backup instances based +on data provided by the EventInterface +end note + +class EventInterface + +class Config + +class Backup + +class Device + +class Tool + +backive_service --> Service : provides startup to +EventInterface --* Service +Config --* Service +Backup --* Service +Device --* Backup +Tool --* Backup + +Config ..> Backup : generates Backup objects +backive_udev ..> EventInterface : sends data through unix socket + +@enduml \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index e69de29..92d8dea 100644 --- a/setup.cfg +++ b/setup.cfg @@ -0,0 +1,9 @@ +[aliases] +test=pytest + +[tool:pytest] +addopts = --verbose --cov=backive --pep8 --cov-report term-missing --pylint --pylint-error-types=E --pylint-jobs=4 +python_files = tests/test_*.py + +[build] +executable = /usr/bin/env python3 diff --git a/setup.py b/setup.py index f1fb221..118cd31 100644 --- a/setup.py +++ b/setup.py @@ -9,13 +9,28 @@ setup_info = dict( version=VERSION, author="Marcel M. Otte", author_email="qwc+backive@mmo.to", - url="tbd", - description="", + url="https://github.com/qwc/backive", + description="Service for automatic backup of data to disks provided in hot-swap (SATA docking station)", license="BSD", classifiers=[ ], + scripts=[], packages=find_packages(), - + setup_requires=[ + "setuptools>=40.4.3", + "pytest>=3.8.2", + "pytest_runner>=4.2", + ], + install_requires=[ + "jsonschema==2.6.0" + ], + tests_require=[ + "pytest_cov>=2.6.0", + "pytest_pylint>=0.12.3", + "pytest-pep8>=1.0.6", + "pylint>=2.1.1", + "coverage>=4.5.1", + ] ) setup(**setup_info)