Make backup result report function in backup service
diff --git a/staffeln/common/constants.py b/staffeln/common/constants.py
index 6de73f6..d0ec6fe 100644
--- a/staffeln/common/constants.py
+++ b/staffeln/common/constants.py
@@ -1,7 +1,5 @@
-BACKUP_COMPLETED=2
-BACKUP_WIP=1
-BACKUP_PLANNED=0
-
-BACKUP_ENABLED_KEY = 'true'
-
-DEFAULT_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
\ No newline at end of file
+BACKUP_COMPLETED=2
+BACKUP_WIP=1
+BACKUP_PLANNED=0
+
+BACKUP_ENABLED_KEY = 'true'
diff --git a/staffeln/common/notify.py b/staffeln/common/notify.py
index 19f8440..e3aabf2 100644
--- a/staffeln/common/notify.py
+++ b/staffeln/common/notify.py
@@ -4,6 +4,7 @@
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import staffeln.conf
+from staffeln.common import time as xtime
CONF = staffeln.conf.CONF
@@ -29,18 +30,22 @@
print(str(e))
return False
-def SendNotification(content, receiver=None):
+def SendBackupResultNotification(success_volume_list, failed_volume_list):
subject = "Backup result"
- html = "<h3>${CONTENT}</h3>"
- html = html.replace("${CONTENT}", content)
+ html = "<h3>${TIME}</h3>" \
+ "<h3>Success List</h3>" \
+ "<h4>${SUCCESS_VOLUME_LIST}</h4>" \
+ "<h3>Failed List</h3>" \
+ "<h4>${FAILED_VOLUME_LIST}</h4>"
- if receiver == None:
- return
- if len(receiver) == 0:
- return
+ success_volumes = '<br>'.join([str(elem) for elem in success_volume_list])
+ failed_volumes = '<br>'.join([str(elem) for elem in failed_volume_list])
+ html = html.replace("${TIME}", xtime.get_current_strtime())
+ html = html.replace("${SUCCESS_VOLUME_LIST}", success_volumes)
+ html = html.replace("${FAILED_VOLUME_LIST}", failed_volumes)
- res = sendEmail(src_email=CONF.notification.sender_email,
+ return sendEmail(src_email=CONF.notification.sender_email,
src_pwd=CONF.notification.sender_pwd,
dest_email=CONF.notification.receiver,
subject=subject,
diff --git a/staffeln/common/time.py b/staffeln/common/time.py
index 6ebdee7..0af1cb4 100644
--- a/staffeln/common/time.py
+++ b/staffeln/common/time.py
@@ -1,48 +1,55 @@
-import re
-from datetime import datetime
-from dateutil.relativedelta import relativedelta
-
-regex = re.compile(
- r'((?P<years>\d+?)y)?((?P<months>\d+?)m)?((?P<weeks>\d+?)w)?((?P<days>\d+?)d)?'
-)
-
-
-# parse_time parses timedelta string to time dict
-# input: <string> 1y2m3w5d - all values should be integer
-# output: <dict> {year: 1, month: 2, week: 3, day: 5}
-def parse_timedelta_string(time_str):
- empty_flag = True
- try:
- parts = regex.match(time_str)
- if not parts:
- return None
- parts = parts.groupdict()
- time_params = {}
- for key in parts:
- if parts[key]:
- time_params[key] = int(parts[key])
- empty_flag = False
- else:
- time_params[key] = 0
- if empty_flag: return None
- return time_params
- except:
- return None
-
-
-def timeago(years, months, weeks, days, from_date=None):
- if from_date is None:
- from_date = datetime.now()
- return from_date - relativedelta(years=years, months=months, weeks=weeks, days=days)
-
-## yearsago using Standard library
-# def yearsago(years, from_date=None):
-# if from_date is None:
-# from_date = datetime.now()
-# try:
-# return from_date.replace(year=from_date.year - years)
-# except ValueError:
-# # Must be 2/29!
-# assert from_date.month == 2 and from_date.day == 29 # can be removed
-# return from_date.replace(month=2, day=28,
-# year=from_date.year-years)
+import re
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
+
+DEFAULT_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
+
+regex = re.compile(
+ r'((?P<years>\d+?)y)?((?P<months>\d+?)m)?((?P<weeks>\d+?)w)?((?P<days>\d+?)d)?'
+)
+
+
+# parse_time parses timedelta string to time dict
+# input: <string> 1y2m3w5d - all values should be integer
+# output: <dict> {year: 1, month: 2, week: 3, day: 5}
+def parse_timedelta_string(time_str):
+ empty_flag = True
+ try:
+ parts = regex.match(time_str)
+ if not parts:
+ return None
+ parts = parts.groupdict()
+ time_params = {}
+ for key in parts:
+ if parts[key]:
+ time_params[key] = int(parts[key])
+ empty_flag = False
+ else:
+ time_params[key] = 0
+ if empty_flag: return None
+ return time_params
+ except:
+ return None
+
+
+def get_current_strtime():
+ now = datetime.datetime.now()
+ return now.strftime(DEFAULT_TIME_FORMAT)
+
+
+def timeago(years, months, weeks, days, from_date=None):
+ if from_date is None:
+ from_date = datetime.now()
+ return from_date - relativedelta(years=years, months=months, weeks=weeks, days=days)
+
+## yearsago using Standard library
+# def yearsago(years, from_date=None):
+# if from_date is None:
+# from_date = datetime.now()
+# try:
+# return from_date.replace(year=from_date.year - years)
+# except ValueError:
+# # Must be 2/29!
+# assert from_date.month == 2 and from_date.day == 29 # can be removed
+# return from_date.replace(month=2, day=28,
+# year=from_date.year-years)
diff --git a/staffeln/conductor/manager.py b/staffeln/conductor/manager.py
index 3daf47e..5997828 100755
--- a/staffeln/conductor/manager.py
+++ b/staffeln/conductor/manager.py
@@ -9,6 +9,7 @@
from staffeln.common import constants
from staffeln.conductor import backup
from staffeln.common import context
+from staffeln.common import notify
from staffeln.common import time as xtime
from staffeln.i18n import _
@@ -58,6 +59,9 @@
return False
# Manage active backup generators
+ # TODO(Alex): need to discuss
+ # Need to wait until all backups are finished?
+ # That is required to make the backup report
def _process_wip_tasks(self):
LOG.info(_("Processing WIP backup generators..."))
queues_started = backup.Backup().get_queues(
@@ -82,17 +86,25 @@
current_tasks = backup.Backup().get_queues()
backup.Backup().create_queue(current_tasks)
+ def _report_backup_result(self):
+ self.success_backup_list = []
+ self.failed_backup_list = []
+ notify.SendNotification(self.success_backup_list, self.failed_backup_list)
+
@periodics.periodic(spacing=CONF.conductor.backup_service_period, run_immediately=True)
def backup_engine(self):
LOG.info("backing... %s" % str(time.time()))
LOG.info("%s periodics" % self.name)
if self._check_quota(): return
+ # NOTE(Alex): If _process_wip_tasks() waits tiil no WIP tasks
+ # exist, no need to repeat this function before and after queue update.
self._process_wip_tasks()
- self._process_todo_tasks()
self._update_task_queue()
- self._process_wip_tasks()
self._process_todo_tasks()
+ self._process_wip_tasks()
+ self._report_backup_result()
+
class RotationManager(cotyledon.Service):
@@ -157,4 +169,4 @@
if res == None: LOG.info(_("Retention time format is invalid. "
"Follow <YEARS>y<MONTHS>m<WEEKS>w<DAYS>d."))
- return res.strftime(constants.DEFAULT_TIME_FORMAT)
+ return res.strftime(xtime.DEFAULT_TIME_FORMAT)
diff --git a/staffeln/conf/notify.py b/staffeln/conf/notify.py
index 42daefe..c292e51 100644
--- a/staffeln/conf/notify.py
+++ b/staffeln/conf/notify.py
@@ -9,11 +9,6 @@
)
email_opts = [
- cfg.StrOpt(
- "template",
- default="<h3>\${CONTENT}</h3>",
- help=_("This html template is used to email the backup result."),
- ),
cfg.ListOpt(
"receiver",
default=[],