J'ai créé un daemon qui écoute sur un socket unix et qui attend des commandes. Ces commandes sont définies par des workers, appelés dans une classe workers. La classe workers permet d'encapsuler un système de sauvegarde/restauration en entrée/sortie des différents workers suivant l'état renvoyer par lesdits worker. Certains workers héritent d'une classe BckObservable
permettant d'appeler une fonction pour sauvegarder certain fichier spécifique qui ne sont pas présent dans la liste des fichiers de base à sauvegarder. J'ai utilisé le design pattern observer pour faire ça. L'instance de mon objet backup doit être le même durant tout le traitement du worker et c'est là que j'ai un soucis. En effet, pour un de mes worker j'ai besoin de d'avoir accès à mon objet backup non pas dans mon worker mais dans un objet appelé dans le worker, sachant que je ne passe pas mon objet backup à mes workers.
Avec les noms de classe pour correspondre au code que je vais mettre à la fin ça donnerait ceci : Pouvoir déclencher un evènement dans SystemConf()
via l'observateur BckObservable()
. Le soucis c'est que si je veux ajouter un observateur sur SystemConf() il faudrait que je passe mon objet Backup()
(Déclarer dans le __enter__
de Worker()
) dans l'objet ServerConfiguration()
, ce que je souhaite éviter pour centraliser le backup dans ma classe Workers()
Et bien sûr voilà un code d'exemple sur le fonctionnement du bousin:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from __future__ import absolute_import
import os
import shutil
from collections import defaultdict
# Implémentation du design pattern observer
class BckObservable(object):
def __init__(self):
self.observers = defaultdict(list)
def add_bck_observer(self, obs, event):
if not hasattr(obs, 'specific_save'):
raise ValueError("First argument must be object with specific_save method")
self.observers[event].append(obs)
def specific_backup(self, event, path):
for obs in self.observers[event]:
obs.specific_save(path)
# Classe de backup
class Backup(object):
def __init__(self):
self.backup_path = '/opt/backup'
self._files_backup_path = os.path.join(self.backup_path, 'files')
self.base_backup_path = ['/etc/shinken', '/var/lib/shinken']
# Fonction lancer dès qu'il y a besoin de sauvegarde, représenter par la variable need_save dans Workers
def save(self):
self._save_files(self.base_backup_path)
# Fonction lancer de manière indépendante via l'observer
def specific_save(self, path):
self._save_files(path)
# Copy les fichier dans le répertoire de backup
def _save_files(self, save_paths):
if isinstance(save_paths, unicode) or isinstance(save_paths, str):
save_paths = [save_paths]
if not isinstance(save_paths, list):
raise TypeError('Paths must be a list')
for path in save_paths:
backup_path = os.path.join(self._files_backup_path, path.lstrip('/'))
shutil.copy(path, backup_path)
# Fonction lancer en cas d'erreur dans le process pour restaurer les fichiers à leur état initial
def restore(self):
for root, dirs, files in os.walk(self._files_backup_path):
backup_path = os.path.join(root, files)
restore_path = backup_path.replace(self._files_backup_path, '')
shutil.copy(backup_path, restore_path)
class Workers(object):
# need_save est un booléen permettant de savoir si il y a besoin de sauvegarde
# worker est la classe à lancer, pour les besoin de l'exemple worker sera toujours pareil
def __init__(self, need_save, *data):
self.need_save = need_save
self.worker = ServerConfiguration(*data)
# Lance la sauvegarde si besoin
def __enter__(self):
if self.need_save:
self.backup = Backup()
self.backup.save()
# Lance la restauration si besoin
def __exit__(self, exc_type, exc_val, exc_tb):
if all((exc_type, exc_val, exc_tb)):
if self.need_save:
self.backup.restore()
# Ajoute un observer Backup() sur l'event specific_backup
# Appel la méthode call() du worker
def call_worker(self):
if hasattr(self.worker, 'add_bck_observer'):
self.worker.add_bck_observer(self.backup, 'specific_backup')
self.worker.call()
class ServerConfiguration(BckObservable):
def __init__(self, data):
super(ServerConfiguration, self).__init__()
self.data = data
# Appel une classe de traitement
def call(self):
sc = SystemConf()
sc.call()
class SystemConf(BckObservable):
# Lancement de la fonction specific_backup -> ne fonctionne pas
# Il n'y a pas d'observer sur cette classe, il est sur le parent
def __init__(self):
super(SystemConf, self).__init__()
paths_list = ['/etc/network/interfaces', '/etc/hosts']
self.specific_backup('specific_backup', paths_list)
def call(self):
print "SystemConf traitement"
if __name__ == '__main__':
wk = Workers(True, 'Some data')
with wk:
wk.call_worker()