summaryrefslogtreecommitdiff
path: root/lorrycontroller/workingstate.py
blob: b8dc751301260ad4506f4c5964269c800cbed271 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# Copyright (C) 2013  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 json
import os
import logging
import string

class LorryFileRunner(object):
    def __init__(self, mgr, lorryname):
        self.mgr = mgr
        self.lorryname = lorryname
        self.lorryfile = os.path.join(self.mgr.workdir,
                                      self._esc(lorryname) + ".lorry")

    def _esc(self, name):
        valid_chars = string.digits + string.letters + '%_'
        transl = lambda x: x if x in valid_chars else '_'
        return ''.join([transl(x) for x in name])

    def __enter__(self):
        lorry_obj = { self.lorryname:
                      self.mgr.lorry_state[self.lorryname]['lorry'] }
        with open(self.lorryfile, "w") as fh:
            json.dump(lorry_obj, fh)
            fh.write("\n")
        return self

    def __exit__(self, exctype, excvalue, exctraceback):
        os.unlink(self.lorryfile)

    def run_lorry(self, *args):
        cmdargs = list(args)
        cmdargs.append(self.lorryfile)
        conf_uuid = self.mgr.lorry_state[self.lorryname]['conf']
        conf = self.mgr.app.conf.configs[conf_uuid]
        cmdargs.append("--tarball=%s" % conf['tarball'])
        exit, out, err = self.mgr.app.maybe_runcmd(cmdargs)
        if exit == 0:
            logging.debug("Lorry of %s succeeded: %s" % (self.lorryname, out))
            self.mgr.lorry_state[self.lorryname]['result'] = "OK"
        else:
            logging.warn("Lorry of %s failed: %s" % (self.lorryname, err))
            self.mgr.lorry_state[self.lorryname]['result'] = err

class WorkingStateManager(object):
    '''Manage the working state of lorry-controller'''

    def __init__(self, app):
        self.app = app
        self.workdir = os.path.join(self.app.settings['work-area'], 'work')

    def __enter__(self):
        self._load_state()
        return self

    def __exit__(self, exctype, excvalue, exctraceback):
        self.purge_dead_troves()
        if not self.app.settings['dry-run']:
            self.save_state()
        else:
            logging.debug("DRY-RUN: Not saving state again")

    def purge_dead_troves(self):
        old_trove_count = len(self.trove_state.keys())
        all_troves = self.trove_state
        self.trove_state = {}
        new_trove_count = 0
        for uuid, trove in all_troves.iteritems():
            self.trove_state[uuid] = trove
            new_trove_count += 1
        if old_trove_count != new_trove_count:
            trove_diff = old_trove_count - new_trove_count
            logging.info("Purged %d dead trove entr%s from the state file" % (
                    trove_diff, ("y" if trove_diff == 1 else "ies")))

    def _load_state(self):
        self.lorry_state_file = os.path.join(self.workdir,
                                             "last-lorry-state.json")
        self.trove_state_file = os.path.join(self.workdir,
                                             "last-trove-state.json")
        if os.path.exists(self.lorry_state_file):
            logging.info("Loading lorry state file: %s" %
                         self.lorry_state_file)
            with open(self.lorry_state_file, "r") as fh:
                self.lorry_state = json.load(fh)
        else:
            self.lorry_state = dict()

        if os.path.exists(self.trove_state_file):
            logging.info("Loading trove state file: %s" %
                         self.trove_state_file)
            with open(self.trove_state_file, "r") as fh:
                self.trove_state = json.load(fh)
        else:
            self.trove_state = dict()

    def save_state(self):
        logging.info("Serialising lorry state: %s" % self.lorry_state_file)
        with open(self.lorry_state_file, "w") as fh:
            json.dump(self.lorry_state, fh, sort_keys=True, indent=4)
            fh.write("\n")
        logging.info("Serialising trove state: %s" % self.trove_state_file)
        with open(self.trove_state_file, "w") as fh:
            json.dump(self.trove_state, fh, sort_keys=True, indent=4)
            fh.write("\n")

    def get_trove(self, troveuuid):
        if troveuuid not in self.trove_state:
            self.trove_state[troveuuid] = {}
        return self.trove_state[troveuuid]

    def runner(self, lorryname):
        return LorryFileRunner(self, lorryname)