diff options
Diffstat (limited to 'lorrycontroller/status.py')
-rw-r--r-- | lorrycontroller/status.py | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/lorrycontroller/status.py b/lorrycontroller/status.py new file mode 100644 index 0000000..5e011d5 --- /dev/null +++ b/lorrycontroller/status.py @@ -0,0 +1,162 @@ +# Copyright (C) 2014 Codethink Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +import logging +import os +import time + +import bottle + +import lorrycontroller + + +class StatusRenderer(object): + + '''Helper class for rendering service status as JSON/HTML''' + + def get_status_as_dict(self, statedb, work_directory): + now = statedb.get_current_time() + status = { + 'running_queue': statedb.get_running_queue(), + 'timestamp': + time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime(now)), + 'run_queue': self.get_run_queue(statedb), + 'troves': self.get_troves(statedb), + 'warning_msg': '', + 'max_jobs': self.get_max_jobs(statedb), + } + status.update(self.get_free_disk_space(work_directory)) + return status + + def render_status_as_html(self, template, status): + return bottle.template(template, **status) + + def write_status_as_html(self, template, status, filename): + html = self.render_status_as_html(template, status) + try: + with open(filename, 'w') as f: + f.write(html) + except (OSError, IOError) as e: + status['warning_msg'] = ( + 'ERROR WRITING STATUS HTML TO DISK: %s' % str(e)) + + def get_free_disk_space(self, dirname): + result = os.statvfs(dirname) + free_bytes = result.f_bavail * result.f_bsize + return { + 'disk_free': free_bytes, + 'disk_free_mib': free_bytes / 1024**2, + 'disk_free_gib': free_bytes / 1024**3, + } + + def get_run_queue(self, statedb): + lorries = statedb.get_all_lorries_info() + now = statedb.get_current_time() + for lorry in lorries: + due = lorry['last_run'] + lorry['interval'] + lorry['interval_nice'] = self.format_secs_nicely(lorry['interval']) + lorry['due_nice'] = self.format_due_nicely(due, now) + return lorries + + def format_due_nicely(self, due, now): + now = int(now) + if due <= now: + return 'now' + else: + nice = self.format_secs_nicely(due - now) + return 'in %s' % nice + + def format_secs_nicely(self, secs): + if secs <= 0: + return 'now' + + result = [] + + hours = secs / 3600 + secs %= 3600 + mins = secs / 60 + secs %= 60 + + if hours > 0: + result.append('%d h' % hours) + if mins > 0: + result.append('%d min' % mins) + elif mins > 0: + result.append('%d min' % mins) + if secs > 0: + result.append('%d s' % secs) + else: + result.append('%d s' % secs) + + return ' '.join(result) + + def get_troves(self, statedb): + troves = [] + for trovehost in statedb.get_troves(): + trove_info = statedb.get_trove_info(trovehost) + + trove_info['ls_interval_nice'] = self.format_secs_nicely( + trove_info['ls_interval']) + + ls_due = trove_info['ls_last_run'] + trove_info['ls_interval'] + now = int(statedb.get_current_time()) + trove_info['ls_due_nice'] = self.format_due_nicely(ls_due, now) + + troves.append(trove_info) + return troves + + def get_max_jobs(self, statedb): + max_jobs = statedb.get_max_jobs() + if max_jobs is None: + return 'unlimited' + return max_jobs + + +class Status(lorrycontroller.LorryControllerRoute): + + http_method = 'GET' + path = '/1.0/status' + + def run(self, **kwargs): + logging.info('%s %s called', self.http_method, self.path) + renderer = StatusRenderer() + statedb = self.open_statedb() + status = renderer.get_status_as_dict( + statedb, self.app_settings['statedb']) + renderer.write_status_as_html( + self._templates['status'], + status, + self.app_settings['status-html']) + return status + + +class StatusHTML(lorrycontroller.LorryControllerRoute): + + http_method = 'GET' + path = '/1.0/status-html' + + def run(self, **kwargs): + logging.info('%s %s called', self.http_method, self.path) + renderer = StatusRenderer() + statedb = self.open_statedb() + status = renderer.get_status_as_dict( + statedb, self.app_settings['statedb']) + renderer.write_status_as_html( + self._templates['status'], + status, + self.app_settings['status-html']) + return renderer.render_status_as_html( + self._templates['status'], status) |