blob: 12d2828497bd40b75f22fd251da7780b80831098 [file] [log] [blame]
Max Rasskazov26787df2015-06-05 14:47:27 +03001#-*- coding: utf-8 -*-
2
3import datetime
4import os
5
6import utils
7
8from rsync_remote import RsyncRemote
9from utils import singleton
10
11
12@singleton
13class TimeStamp(object):
14 def __init__(self):
15 self.now = datetime.datetime.utcnow()
16 self.staging_snapshot_stamp_format = r'%Y-%m-%d-%H%M%S'
17 self.staging_snapshot_stamp_regexp = \
18 r'[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}'
19 self.staging_snapshot_stamp = \
20 self.now.strftime(self.staging_snapshot_stamp_format)
21
22 def __str__(self):
23 return self.staging_snapshot_stamp
24
25
26class RsyncVersioned(RsyncRemote):
27 # retry and other function with mirror
28 # add all the needed directory functions here, like mkdir, ls, rm etc
29 # possible check that rsync url is exists
30 def __init__(self,
31 rsync_url,
Max Rasskazov01271622015-06-17 02:35:09 +030032 snapshot_dir='snapshots',
33 latest_successful_postfix='latest',
Max Rasskazov26787df2015-06-05 14:47:27 +030034 save_latest_days=14,
35 init_directory_structure=True,
36 ):
37 super(RsyncVersioned, self).__init__(rsync_url)
38 self.logger = utils.logger.getChild('RsyncVersioned.' + rsync_url)
39 self.timestamp = TimeStamp()
40 self.logger.info('Using timestamp {}'.format(self.timestamp))
Max Rasskazov46cc0732015-06-05 19:23:24 +030041 self.snapshot_dir = self.url.a_dir(snapshot_dir)
Max Rasskazov26787df2015-06-05 14:47:27 +030042 self.latest_successful_postfix = latest_successful_postfix
43 self.save_latest_days = save_latest_days
44
Max Rasskazov26787df2015-06-05 14:47:27 +030045 if init_directory_structure is True:
46 self.init_directory_structure()
47
48 def init_directory_structure(self):
49 # TODO: self.rsyncRemote.mkdir
Max Rasskazov01271622015-06-17 02:35:09 +030050 if self.url.url_type != 'path':
51 server_root = RsyncRemote(self.url.root)
52 return server_root.mkdir(
53 self.url.a_dir(self.url.path, self.snapshot_dir)
54 )
Max Rasskazov26787df2015-06-05 14:47:27 +030055
56 def push(self, source, repo_name, extra=None):
Max Rasskazov46cc0732015-06-05 19:23:24 +030057 latest_path = self.url.a_file(
Max Rasskazov26787df2015-06-05 14:47:27 +030058 self.snapshot_dir,
Max Rasskazov46cc0732015-06-05 19:23:24 +030059 '{}-{}'.format(self.url.a_file(repo_name),
Max Rasskazov26787df2015-06-05 14:47:27 +030060 self.latest_successful_postfix)
61 )
Max Rasskazov46cc0732015-06-05 19:23:24 +030062 snapshot_name = self.url.a_file(
63 '{}-{}'.format(self.url.a_file(repo_name), self.timestamp)
Max Rasskazov26787df2015-06-05 14:47:27 +030064 )
Max Rasskazov46cc0732015-06-05 19:23:24 +030065 repo_path = self.url.a_file(self.snapshot_dir, snapshot_name)
Max Rasskazov26787df2015-06-05 14:47:27 +030066
Max Rasskazov854399e2015-06-05 16:35:17 +030067 extra = '--link-dest={}'.format(
Max Rasskazov46cc0732015-06-05 19:23:24 +030068 self.url.a_file(self.url.path, latest_path)
Max Rasskazov854399e2015-06-05 16:35:17 +030069 )
Max Rasskazov26787df2015-06-05 14:47:27 +030070 result = super(RsyncVersioned, self).push(source, repo_path, extra)
Max Rasskazov17f4a6c2015-06-17 02:40:59 +030071 self.symlink(repo_name, repo_path)
72 self.symlink(latest_path, snapshot_name)
Max Rasskazov452138b2015-06-17 02:37:34 +030073 self._remove_old_snapshots(repo_name)
Max Rasskazov26787df2015-06-05 14:47:27 +030074 return result
Max Rasskazov452138b2015-06-17 02:37:34 +030075
76 def _remove_old_snapshots(self, repo_name, save_latest_days=None):
77 if save_latest_days is None:
78 save_latest_days = self.save_latest_days
79 if save_latest_days is None or save_latest_days is False:
80 # delete all snapshots
81 self.logger.info('Deletion all of the old snapshots '
82 '(save_latest_days == {})'
83 ''.format(save_latest_days))
84 save_latest_days = -1
85 elif save_latest_days == 0:
86 # skipping deletion
87 self.logger.info('Skip deletion of old snapshots '
88 '(save_latest_days == {})'
89 ''.format(save_latest_days))
90 return
91 else:
92 # delete snapshots older than
93 self.logger.info('Deletion all of the unlinked snapshots older '
94 'than {0} days (save_latest_days == {0})'
95 ''.format(save_latest_days))
96 warn_date = \
97 self.timestamp.now - datetime.timedelta(days=save_latest_days)
98 warn_date = datetime.datetime.combine(warn_date, datetime.time(0))
99 snapshots = self.ls_dirs(
100 self.url.a_dir(self.snapshot_dir),
101 pattern=r'^{}-{}$'.format(
102 repo_name,
103 self.timestamp.staging_snapshot_stamp_regexp
104 )
105 )
106 links = self.ls_symlinks(self.url.a_dir())
107 links += self.ls_symlinks(self.url.a_dir(self.snapshot_dir))
108 for s in snapshots:
109 s_date = datetime.datetime.strptime(
110 s,
111 '{}-{}'.format(repo_name,
112 self.timestamp.staging_snapshot_stamp_format)
113 )
114 s_date = datetime.datetime.combine(s_date, datetime.time(0))
115 s_path = self.url.a_dir(self.snapshot_dir, s)
116 if s_date < warn_date:
117 s_links = [_[0] for _ in links
118 if _[1] == s
119 or _[1].endswith('/{}'.format(s))
120 ]
121 if not s_links:
122 self.rmdir(s_path)
123 else:
124 self.logger.info('Skip deletion of "{}" because there are '
125 'symlinks found: {}'.format(s, s_links))
126 else:
127 self.logger.info('Skip deletion of "{}" because it newer than '
128 '{} days'.format(s, save_latest_days))