minor tweaks to code and config. Logging improved etc
[qa-tools:meego-ai-flasher.git] / customflasher.py
1 # -*- coding: utf-8 -*-
2 # ***** BEGIN LICENCE BLOCK *****
3 # This file is part of OTS
4 #
5 # Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
6 #
7 # Contact: Mikko Makinen <mikko.al.makinen@nokia.com>
8 #
9 # This library is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public License
11 # version 2.1 as published by the Free Software Foundation.
12 #
13 # This library is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 # 02110-1301 USA
22 # ***** END LICENCE BLOCK *****
23
24 # Modified from the defaultflasher.py by Sampo Saaristo
25 # pylint: disable-msg=W0511
26 # TODO: 
27 #  - needs refactoring
28 # FIXME:
29 #  - looks like python written by c-programmer (that's what it is:) 
30 #  - doc strings
31 #
32 # The flash sequence is as follows. The commands are read from configuration
33 # file /etc/meego_ai_flasher.conf. Commands left empty in configuration file 
34 # are silently skipped, only the flash_command is mandatory.
35 #
36 # +-----------------------------+
37 # |    image_prepare_command    |-> InvalidImage
38 # +-----------------------------+
39 #             |
40 #             v
41 # +-----------------------------+
42 # |     power_cycle_command     |-> FlashFailed
43 # +-----------------------------+
44 #             |
45 #             v
46 # +-----------------------------+
47 # |     pre_flash_command       |-> FlashFailed
48 # +-----------------------------+
49 #             |
50 #             v
51 # +-----------------------------+
52 # | pre_flash_conn_test_command |-> ConnectionTestFailed 
53 # +-----------------------------+
54 #             |
55 #             v
56 # +-----------------------------+
57 # |        flash_command        |-> FlashFailed
58 # +-----------------------------+
59 #             |
60 #             v
61 # +-----------------------------+
62 # |   post_flash_command        |-> FlashFailed
63 # +-----------------------------+
64 #             |
65 #             v
66 # +-----------------------------+
67 # |     conn_test_1_command     |-> ConnectionTestFailed
68 # +-----------------------------+
69 #             |
70 #             v
71 # +-----------------------------+
72 # |     conn_test_2_command     |-> ConnectionTestFailed
73 # +-----------------------------+
74 #             |
75 #             v
76 # +-----------------------------+
77 # |wait_system_testable_command |-> ConnectionTestFailed
78 # +-----------------------------+
79 #             |
80 #             v
81 #          SUCCESS
82
83 """
84 This module includes PofC flasher 
85 """
86 import logging
87 import sys
88 from optparse import OptionParser
89 from ots.worker.command import Command, SoftTimeoutException, \
90                                 HardTimeoutException, CommandFailed
91 from ots.worker.conductor.helpers import parse_config
92
93 CONFIG_FILE = "/etc/meego_ai_flasher.conf"
94  
95 class FlashFailed(Exception):
96     """Flash Failed exception"""
97     pass
98
99 class InvalidImage(Exception):
100     """Invalid Image exception"""
101     pass
102
103 class InvalidConfig(Exception):
104     """Invalid configuration exception"""
105     pass
106
107 class ConnectionTestFailed(Exception):
108     """Connection test failed exception"""
109     pass
110
111 # pylint: disable-msg=R0903
112 class SoftwareUpdater(object):
113     """ Default class for SoftwareUpdater """
114
115     def __init__(self, flasher=None):
116         super(SoftwareUpdater, self).__init__()
117         self.log = logging.getLogger("conductor")
118         self._parse_config()
119
120     def _parse_config (self):
121         self.config = parse_config (CONFIG_FILE, "flasher")
122         dut = self.config['dut']
123         if (dut is ""):
124             raise InvalidConfig
125         self.config = parse_config (CONFIG_FILE, dut)
126          
127     def _image_prepare (self, image_path, content_image_path, \
128                         command_str, timeout):
129         """ Do some preparations for the image """
130         if (command_str is ""):
131             return
132         self.log.info ("image-prepare (timeout %d)" % float (timeout))
133         self.log.debug("Image prepare command %s" % command_str)
134         try:
135             command = Command(command_str % image_path , timeout)
136             command.execute()
137         except CommandFailed:
138             self.log.warning("image prepare  failed")
139             raise FlashFailed 
140         except (SoftTimeoutException, HardTimeoutException):
141             self.log.warning("image prepare timeout")
142             raise FlashFailed 
143
144     def _power_cycle (self, command_str, timeout):
145         """ Do some preparations for the image """
146         if (command_str is ""):
147             return
148         self.log.info ("power-cycle (timeout %d)" % float (timeout))
149         self.log.debug("power cycle command %s" % command_str)
150         try:
151             command = Command(command_str, timeout)
152             command.execute()
153         except CommandFailed:
154             self.log.warning("power cycle failed")
155             raise FlashFailed 
156         except (SoftTimeoutException, HardTimeoutException):
157             self.log.warning("power cycle timeout")
158             raise FlashFailed 
159
160     def _pre_flash (self, command_str, timeout):
161         """ Execute pre flash command """
162         if (command_str is ""):
163             return
164         self.log.info ("pre-flash (timeout %d)" % float (timeout))
165         self.log.debug("Pre flash command %s " % command_str)
166         try:
167             command = Command(command_str, timeout)
168             command.execute()
169         except CommandFailed:
170             self.log.warning("pre flash command failed")
171             raise FlashFailed 
172         except (SoftTimeoutException, HardTimeoutException):
173             self.log.warning("pre flash command timeout")
174             raise FlashFailed 
175
176     def _post_flash (self, command_str, timeout):
177         """ Execute post flash command """
178         if (command_str is ""):
179             return
180         self.log.info ("post-flash (timeout %d)" % float (timeout))
181         self.log.debug("Post flash command %s" % command_str)
182         try:
183             command = Command(command_str, timeout)
184             command.execute()
185         except CommandFailed:
186             self.log.warning("post flash command failed")
187             raise FlashFailed 
188         except (SoftTimeoutException, HardTimeoutException):
189             self.log.warning("post flash command timeout")
190             raise FlashFailed 
191
192     def _conn_test (self, command_str, timeout):
193         """ Check connection to device """
194         if (command_str is ""):
195             return
196         self.log.info("performing connection check - timeout %d" % \
197                       float (timeout))
198         try:
199             command = Command(command_str, timeout)
200             command.execute_with_retries(10, 0, 1)
201         except CommandFailed:
202             self.log.warning("connection check failed")
203             raise FlashFailed 
204         except (SoftTimeoutException, HardTimeoutException):
205             self.log.warning("connection check timeout")
206             raise FlashFailed 
207         self.log.info("...OK")
208
209
210     # pylint: disable-msg=W0613
211     def _flash (self, image_path, content_image_path, command_str, timeout):
212         """
213         Performs the actual flashing
214
215         @type image_path: C{string}
216         @param image: Absolute path of image file
217
218         @type content_image_path: C{string}
219         @param content_image_path: Absolute path of Device content image file
220         """
221         if (command_str is ""):
222             self.log.error ("No flash command")
223             raise FlashFailed
224         try:
225             self.log.info("Flashing.. timeout %d", float (timeout))
226             command = Command(command_str % image_path, timeout)
227             command.execute()
228         except CommandFailed:
229             self.log.warning("flash command failed!")
230             raise FlashFailed 
231         except (SoftTimeoutException, HardTimeoutException):
232             self.log.warning("flash command timeout")
233             raise FlashFailed 
234         self.log.info("...OK")
235
236     def _wait_system_testable (self, command_str, timeout):
237         """ dbus signal or something """
238         if (command_str is ""):
239             return
240         self.log.info("waiting device to settle %d" % \
241                       float (timeout))
242         try:
243             command = Command(command_str, timeout)
244             command.execute()
245         except CommandFailed:
246             self.log.warning("wait system testable failed")
247             raise ConnectionTestFailed 
248         except (SoftTimeoutException, HardTimeoutException):
249             self.log.warning("wait system testable timeout")
250             raise ConnectionTestFailed 
251         self.log.info("...OK")
252
253
254     def flash(self, image_path, content_image_path):
255         """
256         Call this method to start flashing.
257
258         @type image_path: C{string}
259         @param image: Absolute path of image file
260
261         @type content_image_path: C{string}
262         @param content_image_path: Absolute path of Device content image file
263         """
264         self.log.warning("custom flasher for n900/netbook autoinstall still "\
265                          "PofC level")
266         self._image_prepare (image_path, content_image_path, \
267                              self.config['image_prepare_command'], \
268                              self.config['image_prepare_timeout'])
269         self._power_cycle (self.config['power_cycle_command'], \
270                            self.config['power_cycle_timeout'])
271         self._pre_flash (self.config['pre_flash_command'], \
272                          self.config['pre_flash_timeout'])
273         self._conn_test (self.config['pre_flash_conn_test_command'], \
274                          self.config['pre_flash_conn_test_timeout'])
275         self._flash (image_path, content_image_path, \
276                      self.config['flash_command'], \
277                      self.config['flash_timeout'])
278         self._post_flash (self.config['post_flash_command'], \
279                           self.config['post_flash_timeout'])
280         self._conn_test (self.config['conn_test_1_command'], \
281                          self.config['conn_test_1_timeout'])
282         self._conn_test (self.config['conn_test_2_command'], \
283                          self.config['conn_test_2_timeout'])
284         self._wait_system_testable \
285             (self.config['wait_system_testable_command'], \
286              self.config['wait_system_testable_timeout'])
287
288 def main():
289     """Main function for commandline use - mostly for testing"""
290
291     usage = "usage: %prog [options] \n" + \
292             "example: " + \
293             "%prog -f image_file.bin -c content_image_file.bin"
294                 
295     parser = OptionParser(usage=usage)
296     parser.add_option("-f", "--image-file", dest="image", 
297                       action="store", type="string",
298                       help="Path to main image file", 
299                       metavar="IMAGE")
300     
301     parser.add_option("-c", "--content-image", dest="content_image", 
302                       action="store", type="string",
303                       help="Path to content image file", 
304                       metavar="CONTENTIMAGE")
305     # pylint: disable-msg=W0612
306     (options, args) = parser.parse_args()
307
308     if not (options.image or options.content_image):
309         parser.print_help()
310         sys.exit(-1)
311
312     log_format = '%(asctime)s %(levelname)s %(message)s'
313     logging.basicConfig(level=logging.DEBUG, format=log_format, \
314                         stream=sys.stdout)
315     log = logging.getLogger("conductor")
316     log.info("here goes ..") 
317     try:
318         flasher = SoftwareUpdater()
319         flasher.flash(options.image, options.content_image)
320         log.info("Flashing completed!")
321     except FlashFailed:
322         log.error("Flashing failed!")
323     except ConnectionTestFailed:
324         log.error("Connection test failed!")
325     except InvalidImage:
326         log.error("Flashing failed: Invalid image file.")
327     except InvalidConfig:
328         log.error("Flashing failed: Invalid configuration file.")
329
330 if __name__ == '__main__':
331     main()