Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
ipsecondary_test.py
1 #!/usr/bin/env python3
2 
3 import argparse
4 import logging
5 import time
6 
7 from os import getcwd, chdir, path
8 
9 from test_fixtures import with_aktualizr, with_uptane_backend, KeyStore, with_secondary, with_treehub,\
10  with_sysroot, with_director, TestRunner
11 
12 logger = logging.getLogger("IPSecondaryTest")
13 
14 
15 # The following is a test suit intended for IP Secondary integration testing
16 @with_uptane_backend()
17 @with_secondary(start=True)
18 @with_aktualizr(start=False, output_logs=False)
19 def test_secondary_update_if_secondary_starts_first(uptane_repo, secondary, aktualizr, **kwargs):
20  '''Test Secondary update if Secondary is booted before Primary'''
21 
22  # add a new image to the repo in order to update the secondary with it
23  secondary_image_filename = "secondary_image_filename_001.img"
24  secondary_image_hash = uptane_repo.add_image(id=secondary.id, image_filename=secondary_image_filename)
25 
26  logger.debug("Trying to update ECU {} with the image {}".
27  format(secondary.id, (secondary_image_hash, secondary_image_filename)))
28 
29  with aktualizr:
30  # run aktualizr once, secondary has been already running
31  aktualizr.wait_for_completion()
32 
33  test_result = secondary_image_hash == aktualizr.get_current_image_info(secondary.id)
34  logger.debug("Update result: {}".format("success" if test_result else "failed"))
35  return test_result
36 
37 
38 @with_uptane_backend()
39 @with_secondary(start=False)
40 @with_aktualizr(start=True, output_logs=False)
41 def test_secondary_update_if_primary_starts_first(uptane_repo, secondary, aktualizr, **kwargs):
42  '''Test Secondary update if Secondary is booted after Primary'''
43 
44  # add a new image to the repo in order to update the secondary with it
45  secondary_image_filename = "secondary_image_filename_001.img"
46  secondary_image_hash = uptane_repo.add_image(id=secondary.id, image_filename=secondary_image_filename)
47 
48  logger.debug("Trying to update ECU {} with the image {}".
49  format(secondary.id, (secondary_image_hash, secondary_image_filename)))
50  with secondary:
51  # start secondary, aktualizr has been already started in 'once' mode
52  aktualizr.wait_for_completion()
53 
54  test_result = secondary_image_hash == aktualizr.get_current_image_info(secondary.id)
55  logger.debug("Update result: {}".format("success" if test_result else "failed"))
56  return test_result
57 
58 
59 @with_uptane_backend()
60 @with_director()
61 @with_secondary(start=False)
62 @with_aktualizr(start=False, output_logs=False)
63 def test_secondary_update(uptane_repo, secondary, aktualizr, director, **kwargs):
64  '''Test Secondary update if a boot order of Secondary and Primary is undefined'''
65 
66  test_result = True
67  # add a new image to the repo in order to update the secondary with it
68  secondary_image_filename = "secondary_image_filename.img"
69  secondary_image_hash = uptane_repo.add_image(id=secondary.id, image_filename=secondary_image_filename)
70 
71  logger.debug("Trying to update ECU {} with the image {}".
72  format(secondary.id, (secondary_image_hash, secondary_image_filename)))
73 
74  # start Secondary and Aktualizr processes, aktualizr is started in 'once' mode
75  with secondary, aktualizr:
76  aktualizr.wait_for_completion()
77 
78  if not director.get_install_result():
79  logger.error("Installation result is not successful")
80  return False
81 
82  # check currently installed hash
83  if secondary_image_hash != aktualizr.get_current_image_info(secondary.id):
84  logger.error("Target image hash doesn't match the currently installed hash")
85  return False
86 
87  # check updated file
88  update_file = path.join(secondary.storage_dir.name, "firmware.txt")
89  if not path.exists(update_file):
90  logger.error("Expected updated file does not exist: {}".format(update_file))
91  return False
92 
93  if secondary_image_filename != director.get_ecu_manifest_filepath(secondary.id[1]):
94  logger.error("Target name doesn't match a filepath value of the reported manifest: {}".format(director.get_manifest()))
95  return False
96 
97  return True
98 
99 
100 @with_treehub()
101 @with_uptane_backend()
102 @with_director()
103 @with_sysroot()
104 @with_secondary(start=False)
105 @with_aktualizr(start=False, run_mode='once', output_logs=True)
106 def test_secondary_ostree_update(uptane_repo, secondary, aktualizr, treehub, sysroot, director, **kwargs):
107  """Test Secondary ostree update if a boot order of Secondary and Primary is undefined"""
108 
109  target_rev = treehub.revision
110  expected_targetname = uptane_repo.add_ostree_target(secondary.id, target_rev, "GARAGE_TARGET_NAME")
111 
112  with secondary:
113  with aktualizr:
114  aktualizr.wait_for_completion()
115 
116  pending_rev = aktualizr.get_current_pending_image_info(secondary.id)
117 
118  if pending_rev != target_rev:
119  logger.error("Pending version {} != the target one {}".format(pending_rev, target_rev))
120  return False
121 
122  sysroot.update_revision(pending_rev)
123  secondary.emulate_reboot()
124 
125  aktualizr.set_mode('full')
126  with aktualizr:
127  with secondary:
128  director.wait_for_install()
129 
130  if not director.get_install_result():
131  logger.error("Installation result is not successful")
132  return False
133 
134  installed_rev = aktualizr.get_current_image_info(secondary.id)
135 
136  if installed_rev != target_rev:
137  logger.error("Installed version {} != the target one {}".format(installed_rev, target_rev))
138  return False
139 
140  if expected_targetname != director.get_ecu_manifest_filepath(secondary.id[1]):
141  logger.error(
142  "Target name doesn't match a filepath value of the reported manifest: expected: {}, actual: {}".
143  format(expected_targetname, director.get_ecu_manifest_filepath(secondary.id[1])))
144  return False
145 
146  return True
147 
148 
149 @with_treehub()
150 @with_uptane_backend()
151 @with_director()
152 @with_sysroot()
153 @with_secondary(start=False, output_logs=False, force_reboot=True)
154 @with_aktualizr(start=False, run_mode='once', output_logs=True)
155 def test_secondary_ostree_reboot(uptane_repo, secondary, aktualizr, treehub, sysroot, director, **kwargs):
156  target_rev = treehub.revision
157  uptane_repo.add_ostree_target(secondary.id, target_rev, "GARAGE_TARGET_NAME")
158 
159  with secondary:
160  with aktualizr:
161  aktualizr.wait_for_completion()
162  secondary.wait_for_completion()
163 
164  pending_rev = aktualizr.get_current_pending_image_info(secondary.id)
165 
166  if pending_rev != target_rev:
167  logger.error("Pending version {} != the target one {}".format(pending_rev, target_rev))
168  return False
169 
170  sysroot.update_revision(pending_rev)
171 
172  aktualizr.set_mode('full')
173  with aktualizr:
174  with secondary:
175  director.wait_for_install()
176 
177  if not director.get_install_result():
178  logger.error("Installation result is not successful")
179  return False
180 
181  installed_rev = aktualizr.get_current_image_info(secondary.id)
182 
183  if installed_rev != target_rev:
184  logger.error("Installed version {} != the target one {}".format(installed_rev, target_rev))
185  return False
186 
187  return True
188 
189 
190 @with_uptane_backend()
191 @with_director()
192 @with_secondary(start=False)
193 @with_aktualizr(start=False, secondary_wait_sec=1, output_logs=False)
194 def test_secondary_install_timeout(uptane_repo, secondary, aktualizr, director, **kwargs):
195  '''Test that secondary install fails after a timeout if the secondary never connects'''
196 
197  # run aktualizr and secondary and wait until the device/aktualizr is registered
198  with aktualizr, secondary:
199  aktualizr.wait_for_completion()
200 
201  # the secondary must be registered
202  if not aktualizr.is_ecu_registered(secondary.id):
203  return False
204 
205  # make sure that the secondary is not running
206  if secondary.is_running():
207  return False
208 
209  # launch an update on secondary without it
210  secondary_image_filename = "secondary_image_filename_001.img"
211  uptane_repo.add_image(id=secondary.id, image_filename=secondary_image_filename)
212 
213  aktualizr.update_wait_timeout(0.1)
214  with aktualizr:
215  aktualizr.wait_for_completion()
216 
217  manifest = director.get_manifest()
218  result_code = manifest["signed"]["installation_report"]["report"]["result"]["code"]
219  if result_code != "INTERNAL_ERROR":
220  logger.error("Wrong result code {}".format(result_code))
221  return False
222 
223  return not director.get_install_result()
224 
225 
226 
227 @with_uptane_backend()
228 @with_secondary(start=False)
229 @with_aktualizr(start=False, output_logs=False, wait_timeout=0.1)
230 def test_primary_timeout_during_first_run(uptane_repo, secondary, aktualizr, **kwargs):
231  """Test Aktualizr's timeout of waiting for Secondaries during initial boot"""
232 
233  secondary_image_filename = "secondary_image_filename_001.img"
234  uptane_repo.add_image(id=secondary.id, image_filename=secondary_image_filename)
235 
236  logger.debug("Checking Aktualizr behaviour if it timeouts while waiting for a connection from the secondary")
237 
238  # just start the aktualizr and expect that it timeouts on waiting for a connection from the secondary
239  # so the secondary is not registered at the device and backend
240  with aktualizr:
241  aktualizr.wait_for_completion()
242 
243  info = aktualizr.get_info()
244  if info is None:
245  return False
246  not_provisioned = 'Provisioned on server: no' in info
247 
248  return not_provisioned and not aktualizr.is_ecu_registered(secondary.id)
249 
250 
251 @with_uptane_backend()
252 @with_director()
253 @with_secondary(start=False)
254 @with_aktualizr(start=False, output_logs=True)
255 def test_primary_wait_secondary_install(uptane_repo, secondary, aktualizr, director, **kwargs):
256  """Test that Primary waits for Secondary to connect before installing"""
257 
258  # provision device with a secondary
259  with secondary, aktualizr:
260  aktualizr.wait_for_completion()
261 
262  secondary_image_filename = "secondary_image_filename.img"
263  uptane_repo.add_image(id=secondary.id, image_filename=secondary_image_filename)
264 
265  with aktualizr:
266  time.sleep(10)
267  with secondary:
268  aktualizr.wait_for_completion()
269 
270  if not director.get_install_result():
271  logger.error("Installation result is not successful")
272  return False
273 
274  return True
275 
276 
277 @with_uptane_backend()
278 @with_secondary(start=False, output_logs=False)
279 @with_aktualizr(start=False, output_logs=False)
280 def test_primary_timeout_after_device_is_registered(uptane_repo, secondary, aktualizr, **kwargs):
281  '''Test Aktualizr's timeout of waiting for Secondaries after the device/aktualizr was registered at the backend'''
282 
283  # run aktualizr and secondary and wait until the device/aktualizr is registered
284  with aktualizr, secondary:
285  aktualizr.wait_for_completion()
286 
287  # the secondary must be registered
288  if not aktualizr.is_ecu_registered(secondary.id):
289  return False
290 
291  # make sure that the secondary is not running
292  if secondary.is_running():
293  return False
294 
295  # run just aktualizr, the previously registered secondary is off
296  # and check if the primary ECU is updatable if the secondary is not connected
297  primary_image_filename = "primary_image_filename_001.img"
298  primary_image_hash = uptane_repo.add_image(id=aktualizr.id, image_filename=primary_image_filename)
299 
300  # if a new image for the not-connected secondary is specified in the target
301  # then nothing is going to be updated, including the image intended for
302  # healthy primary ECU
303  # secondary_image_filename = "secondary_image_filename_001.img"
304  # secondary_image_hash = uptane_repo.add_image(id=secondary.id, image_filename=secondary_image_filename)
305 
306  aktualizr.update_wait_timeout(0.1)
307  with aktualizr:
308  aktualizr.wait_for_completion()
309 
310  return aktualizr.get_current_primary_image_info() == primary_image_hash
311 
312 
313 @with_uptane_backend()
314 @with_secondary(start=False)
315 @with_secondary(start=False, arg_name='secondary2')
316 @with_aktualizr(start=False, output_logs=True)
317 def test_primary_multiple_secondaries(uptane_repo, secondary, secondary2, aktualizr, **kwargs):
318  '''Test Aktualizr with multiple ip secondaries'''
319 
320  with aktualizr, secondary, secondary2:
321  aktualizr.wait_for_completion()
322 
323  if not aktualizr.is_ecu_registered(secondary.id) or not aktualizr.is_ecu_registered(secondary2.id):
324  return False
325 
326  return True
327  secondary_image_filename = "secondary_image_filename.img"
328  uptane_repo.add_image(id=secondary.id, image_filename=secondary_image_filename)
329  uptane_repo.add_image(id=secondary2.id, image_filename=secondary_image_filename)
330 
331  with aktualizr:
332  time.sleep(10)
333  with secondary:
334  aktualizr.wait_for_completion()
335 
336  if not director.get_install_result():
337  logger.error("Installation result is not successful")
338  return False
339 
340  return True
341 
342 
343 # test suit runner
344 if __name__ == '__main__':
345  logging.basicConfig(level=logging.INFO)
346 
347  parser = argparse.ArgumentParser(description='Test IP Secondary')
348  parser.add_argument('-b', '--build-dir', help='build directory', default='build')
349  parser.add_argument('-s', '--src-dir', help='source directory', default='.')
350  parser.add_argument('-o', '--ostree', help='ostree support', action='store_true')
351 
352  input_params = parser.parse_args()
353 
354  KeyStore.base_dir = path.abspath(input_params.src_dir)
355  initial_cwd = getcwd()
356  chdir(input_params.build_dir)
357 
358  test_suite = [
359  test_secondary_update,
360  test_secondary_update_if_secondary_starts_first,
361  test_secondary_update_if_primary_starts_first,
362  test_secondary_install_timeout,
363  test_primary_timeout_during_first_run,
364  test_primary_timeout_after_device_is_registered,
365  test_primary_multiple_secondaries,
366  ]
367 
368  if input_params.ostree:
369  test_suite += [
370  test_secondary_ostree_update,
371  test_secondary_ostree_reboot,
372  ]
373 
374  test_suite_run_result = TestRunner(test_suite).run()
375 
376  chdir(initial_cwd)
377  exit(0 if test_suite_run_result else 1)