blob: a7f9a5e1ca5835718eb125217675d08b087ca51b [file] [log] [blame]
Joshua Hesketh0ddd6382013-07-26 10:33:36 +10001# Copyright ....
2
3import git
4import logging
5import os
6import select
7import subprocess
8import time
9
10
11class GitRepository(object):
12
13 """ Manage a git repository for our uses """
Joshua Hesketh363d0042013-07-26 11:44:07 +100014 log = logging.getLogger("lib.utils.GitRepository")
Joshua Hesketh0ddd6382013-07-26 10:33:36 +100015
16 def __init__(self, remote_url, local_path):
17 self.remote_url = remote_url
18 self.local_path = local_path
19 self._ensure_cloned()
20
21 self.repo = git.Repo(self.local_path)
22
23 def fetch(self, ref):
24 # The git.remote.fetch method may read in git progress info and
25 # interpret it improperly causing an AssertionError. Because the
26 # data was fetched properly subsequent fetches don't seem to fail.
27 # So try again if an AssertionError is caught.
28 origin = self.repo.remotes.origin
29 self.log.debug("Fetching %s from %s" % (ref, origin))
30
31 try:
32 origin.fetch(ref)
33 except AssertionError:
34 origin.fetch(ref)
35
36 def checkout(self, ref):
37 self.log.debug("Checking out %s" % ref)
38 return self.repo.git.checkout(ref)
39
40 def _ensure_cloned(self):
41 if not os.path.exists(self.local_path):
42 self.log.debug("Cloning from %s to %s" % (self.remote_url,
43 self.local_path))
44 git.Repo.clone_from(self.remote_url, self.local_path)
45
Joshua Hesketh0ddd6382013-07-26 10:33:36 +100046
47def execute_to_log(cmd, logfile, timeout=-1,
48 watch_logs=[
49 ('[syslog]', '/var/log/syslog'),
50 ('[sqlslo]', '/var/log/mysql/slow-queries.log'),
51 ('[sqlerr]', '/var/log/mysql/error.log')
52 ],
53 heartbeat=True
54 ):
55 """ Executes a command and logs the STDOUT/STDERR and output of any
56 supplied watch_logs from logs into a new logfile
57
58 watch_logs is a list of tuples with (name,file) """
59
60 if not os.path.isdir(os.path.dirname(logfile)):
61 os.makedirs(os.path.dirname(logfile))
62
63 logger = logging.getLogger('execute_to_log')
64 log_hanlder = logging.FileHandler(logfile)
65 log_formatter = logging.Formatter('%(asctime)s %(message)s')
66 log_hanlder.setFormatter(log_formatter)
67 logger.addHandler(log_hanlder)
68
69 descriptors = {}
70
71 for watch_file in watch_logs:
72 fd = os.open(watch_file[1], os.O_RDONLY)
73 os.lseek(fd, 0, os.SEEK_END)
74 descriptors[fd] = dict(
75 name=watch_file[0],
Joshua Hesketh1ab465f2013-07-26 13:57:28 +100076 poll=select.POLLIN,
77 lines=''
Joshua Hesketh0ddd6382013-07-26 10:33:36 +100078 )
79
80 cmd += ' 2>&1'
81 start_time = time.time()
82 p = subprocess.Popen(
83 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
84
85 descriptors[p.stdout.fileno()] = dict(
Joshua Hesketh1ab465f2013-07-26 13:57:28 +100086 name='[output]',
Joshua Hesketh09b2f7f2013-07-29 09:05:58 +100087 poll=(select.POLLIN | select.POLLHUP),
88 lines=''
Joshua Hesketh0ddd6382013-07-26 10:33:36 +100089 )
90
91 poll_obj = select.poll()
92 for fd, descriptor in descriptors.items():
93 poll_obj.register(fd, descriptor['poll'])
94
95 last_heartbeat = time.time()
96
Joshua Hesketh1ab465f2013-07-26 13:57:28 +100097 def process(fd):
98 """ Write the fd to log """
99 descriptors[fd]['lines'] += os.read(fd, 1024 * 1024)
100 # Avoid partial lines by only processing input with breaks
Joshua Hesketh09b2f7f2013-07-29 09:05:58 +1000101 if descriptors[fd]['lines'].find('\n') != -1:
Joshua Hesketh1ab465f2013-07-26 13:57:28 +1000102 elems = descriptors[fd]['lines'].split('\n')
103 # Take all but the partial line
104 for l in elems[:-1]:
105 if len(l) > 0:
106 l = '%s %s' % (descriptors[fd]['name'], l)
107 logger.info(l)
108 last_heartbeat = time.time()
109 # Place the partial line back into lines to be processed
110 descriptors[fd]['lines'] = elems[-1]
111
Joshua Hesketh0ddd6382013-07-26 10:33:36 +1000112 while p.poll() is None:
113 if timeout > 0 and time.time() - start_time > timeout:
114 # Append to logfile
115 logger.info("[timeout]")
116 os.kill(p.pid, 9)
117
118 for fd, flag in poll_obj.poll(0):
Joshua Hesketh1ab465f2013-07-26 13:57:28 +1000119 process(fd)
Joshua Hesketh0ddd6382013-07-26 10:33:36 +1000120
121 if time.time() - last_heartbeat > 30:
122 # Append to logfile
123 logger.info("[heartbeat]")
124 last_heartbeat = time.time()
125
Joshua Hesketh1ab465f2013-07-26 13:57:28 +1000126 # Do one last write to get the remaining lines
127 for fd, flag in poll_obj.poll(0):
128 process(fd)
129
Joshua Hesketh363d0042013-07-26 11:44:07 +1000130 logger.info('[script exit code = %d]' % p.returncode)
Joshua Hesketh926502f2013-07-31 11:56:40 +1000131
132def push_file(local_file):
133 """ Push a log file to a server. Returns the public URL """
134 pass