mirror of
https://github.com/OpenSolo/OpenSolo.git
synced 2025-04-29 22:24:32 +02:00
174 lines
6.0 KiB
Python
Executable File
174 lines
6.0 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
import subprocess
|
|
import time
|
|
import string
|
|
import os
|
|
import re
|
|
|
|
actually_sync_logs_to_artoo = False
|
|
|
|
class RecentLogLinker:
|
|
'''RecentLogLinker manages links to the most recent logs. These links are used by the app to retrieve the most recent logs
|
|
'''
|
|
def __init__(self, directory,link_dir):
|
|
self.directory = directory #location of dataflashlogs
|
|
self.symlink_directory = link_dir #loction of links to the dataflashlogs
|
|
# if you reduce this number, you're going to have to ensure we
|
|
# remove all old stale RECENT links somehow!
|
|
self.num_recent_links_to_create = 10
|
|
|
|
# directly copied from LogPruner
|
|
def lognum(self, filename):
|
|
base = filename.partition('.')
|
|
return int(base[0])
|
|
|
|
# directly copied from LogPruner
|
|
def ordered_list_of_log_filepaths(self):
|
|
binfiles_regex = re.compile("[0-9]+.BIN")
|
|
files = [ f for f in os.listdir(self.directory) if (binfiles_regex.match(f) and os.path.isfile(os.path.join(self.directory, f))) ]
|
|
files.sort(key=self.lognum)
|
|
return [ os.path.join(self.directory, f) for f in files ]
|
|
|
|
def update_links(self):
|
|
filepaths = self.ordered_list_of_log_filepaths()
|
|
#print("Filepaths: " + str(filepaths))
|
|
for i in range(0, self.num_recent_links_to_create):
|
|
if len(filepaths) == 0:
|
|
break
|
|
source = filepaths.pop()
|
|
if i == 0:
|
|
link_name = "RECENT.BIN"
|
|
else:
|
|
link_name = "RECENT-{0}.BIN".format(i)
|
|
|
|
link_path = os.path.join(self.symlink_directory, link_name)
|
|
if os.path.exists(link_path):
|
|
existing_source = None
|
|
try:
|
|
existing_source = os.readlink(link_path)
|
|
except OSError as e:
|
|
os.unlink(link_path)
|
|
if existing_source is not None:
|
|
#print("Leaving link alone")
|
|
if existing_source == source:
|
|
continue
|
|
os.unlink(link_path)
|
|
|
|
try:
|
|
os.symlink(source, link_path)
|
|
except OSError as e:
|
|
print "Failed to link ({0} to {1}): {2}".format(source, link_path, e.strerror)
|
|
break
|
|
|
|
class LogPruner:
|
|
'''LogPruner removes log files (e.g. 45.BIN) from the supplied
|
|
directory until only the suppied number of logs remain. Files are
|
|
removed in ascending numerical order
|
|
'''
|
|
def __init__(self, directory, max_files_remaining):
|
|
self.directory = directory
|
|
self.max_files_remaining = max_files_remaining
|
|
|
|
# directly copied to RecentLogLinker
|
|
def lognum(self, filename):
|
|
base = filename.partition('.')
|
|
return int(base[0])
|
|
|
|
# directly copied to RecentLogLinker
|
|
def ordered_list_of_log_filepaths(self):
|
|
binfiles_regex = re.compile("[0-9]+.BIN")
|
|
files = [ f for f in os.listdir(self.directory) if (binfiles_regex.match(f) and os.path.isfile(os.path.join(self.directory, f))) ]
|
|
files.sort(key=self.lognum)
|
|
return [ os.path.join(self.directory, f) for f in files ]
|
|
|
|
def run(self):
|
|
if not os.path.exists(self.directory):
|
|
print("Directory does not exist: {0}".format(self.directory))
|
|
return
|
|
ordered_list = self.ordered_list_of_log_filepaths()
|
|
while len(ordered_list) > self.max_files_remaining:
|
|
to_remove = ordered_list.pop(0)
|
|
try:
|
|
os.unlink(to_remove)
|
|
except IOError as e:
|
|
print "Failed to remove ({0}): {1}".format(to_remove, e.strerror)
|
|
break
|
|
|
|
class LogSyncer:
|
|
'''
|
|
an instance of LogSyncer loops over rsync'ing dataflash logs from
|
|
the log data directory on solo onto Artoo
|
|
'''
|
|
def __init__(self, src, dest, ssh_id_path, rate=100):
|
|
self.rsyncSrc = src
|
|
self.rsyncDest = dest
|
|
self.ssh_id_path = ssh_id_path
|
|
self.rate = rate
|
|
self.rsyncPartialDest = '.'.join((self.rsyncDest,'part'))
|
|
if not os.path.exists(src):
|
|
os.makedirs(src)
|
|
|
|
def run(self):
|
|
# quick sanity checks here. --delete is a very, very big hammer
|
|
if len(self.rsyncSrc) < 10:
|
|
print("short rsync src")
|
|
sys.exit(1)
|
|
if len(self.rsyncDest) < 10:
|
|
print("short rsync dest")
|
|
sys.exit(1)
|
|
|
|
recentloglinker = RecentLogLinker(src)
|
|
|
|
while True:
|
|
recentloglinker.update_links()
|
|
|
|
cmd = [ 'ionice', '-c', '3', 'nice',
|
|
"rsync", "-a", "--size-only", "--progress",
|
|
'-e', 'ssh -o StrictHostKeyChecking=no -i "%s"' % self.ssh_id_path,
|
|
"--partial",
|
|
"--delete",
|
|
'--bwlimit=%s' % (self.rate,),
|
|
self.rsyncSrc, self.rsyncDest]
|
|
# print "cmd: %s" % (' '.join(cmd))
|
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
stdout,stderr = p.communicate()
|
|
rc = p.returncode
|
|
if rc != 0:
|
|
print "stderr: (%s)" % stderr
|
|
time.sleep(1);
|
|
continue
|
|
|
|
# print "stdout: (%s)" % stdout
|
|
|
|
timeout = 10
|
|
if string.find(stdout,"xfr") != -1:
|
|
timeout = 1
|
|
time.sleep(timeout);
|
|
|
|
# TODO: get artoo's address from config
|
|
# TODO: get source and dest addresses from config
|
|
src = "/log/dataflash/"
|
|
dest = "root@10.1.1.1:/log/solo/dataflash"
|
|
link_dir = "/log/" #location of symlinks for app to retrieve
|
|
ssh_id_path = "/home/root/.ssh/id_rsa-mav-df-xfer"
|
|
max_log_files=50
|
|
|
|
p = LogPruner(src, max_log_files)
|
|
p.run()
|
|
|
|
if actually_sync_logs_to_artoo:
|
|
l = LogSyncer(src, dest, ssh_id_path)
|
|
l.run()
|
|
else:
|
|
# we still update the links on SoloLink so the app can fetch them
|
|
recentloglinker = RecentLogLinker(src,link_dir)
|
|
|
|
while True:
|
|
recentloglinker.update_links()
|
|
time.sleep(10);
|
|
|
|
# neither reached nor necessary if the LogSyncer runs
|
|
while True:
|
|
time.sleep(86400)
|