Improved transaction implementation and implemented creating of additional symlinks during trsync

Change-Id: I203ab33d50ad85a68d727dc17f55ccbbe160f194
diff --git a/trsync.py b/trsync.py
index 88e5b20..6498d44 100644
--- a/trsync.py
+++ b/trsync.py
@@ -60,12 +60,16 @@
                 self.url.a_dir(self.url.path, self.snapshot_dir)
             )
 
-    def push(self, source, repo_name, extra=None):
+    def push(self, source, repo_name, symlinks=[], extra=None):
         latest_path = self.url.a_file(
             self.snapshot_dir,
             '{}-{}'.format(self.url.a_file(repo_name),
                            self.latest_successful_postfix)
         )
+
+        symlinks = list(symlinks)
+        symlinks.insert(0, latest_path)
+
         snapshot_name = self.url.a_file(
             '{}-{}'.format(self.url.a_file(repo_name), self.timestamp)
         )
@@ -77,7 +81,6 @@
 
         # TODO: retry on base class!!!!!!!!!!!!!!!
         # TODO: locking - symlink dir-timestamp.lock -> dir-timestamp
-        # TODO: write status file with symlink info
         # TODO: split transaction run (push or pull), and
         # commit/rollback functions. transaction must has possibility to
         # rollback after commit for implementation of working with pool
@@ -98,48 +101,36 @@
         try:
             # start transaction
             result = super(TRsync, self).push(source, repo_path, extra)
-            transaction.append('repo_dir_created')
+            transaction.append(lambda p=repo_path: self.rmdir(p))
             self.logger.info('{}'.format(result))
 
-            try:
-                old_latest_path_symlink_target = \
-                    [_[1] for _ in self.ls_symlinks(latest_path)][0]
-                self.logger.info('Previous {} -> {}'
-                                 ''.format(latest_path,
-                                           old_latest_path_symlink_target))
-                status = 'updated'
-            except:
-                status = 'created'
-            self.symlink(latest_path, snapshot_name)
-            transaction.append('symlink_latest_path_{}'.format(status))
-
-            self._remove_old_snapshots(repo_name)
-            transaction.append('old_snapshots_deleted')
+            for symlink in symlinks:
+                try:
+                    tgt = [_[1] for _ in self.ls_symlinks(symlink)][0]
+                    self.logger.info('Previous {} -> {}'.format(symlink, tgt))
+                    undo = lambda l=symlink, t=tgt: self.symlink(l, t)
+                except:
+                    undo = lambda l=symlink: self.rmfile(l)
+                # TODO: implement detection of target relative symlink
+                if symlink.startswith(self.snapshot_dir):
+                    self.symlink(symlink, snapshot_name)
+                else:
+                    self.symlink(symlink, repo_path)
+                transaction.append(undo)
 
         except RuntimeError:
+            self.logger.error("Rollback transaction because some of sync"
+                              "operation failed")
+            [_() for _ in reversed(transaction)]
+            raise
+
+        try:
             # deleting of old snapshots ignored when assessing the transaction
             # only warning
-            if 'old_snapshots_deleted' not in transaction:
-                self.logger.warn("Old snapshots are not deleted. Ignore. "
-                                 "May be next time.")
-                transaction.append('old_snapshots_deleted')
-
-            if len(transaction) < 4:
-                # rollback transaction if some of sync operations failed
-
-                if 'symlink_latest_path_updated' in transaction:
-                    self.logger.info('Restoring symlink {} -> {}'
-                                     ''.format(latest_path,
-                                               old_latest_path_symlink_target))
-                    self.symlink(latest_path, old_latest_path_symlink_target)
-                elif 'symlink_latest_path_created' in transaction:
-                    self.logger.info('Deleting symlink {}'.format(latest_path))
-                    self.rmfile(latest_path)
-
-                if 'repo_dir_created' in transaction:
-                    self.logger.info('Removing snapshot {}'.format(repo_path))
-                    self.rmdir(repo_path)
-                raise
+            self._remove_old_snapshots(repo_name)
+        except RuntimeError:
+            self.logger.warn("Old snapshots are not deleted. Ignore. "
+                             "May be next time.")
 
         return result