blob: 02243388ba77d10265a2865aa5958ed64f29c927 [file] [log] [blame]
# Copyright 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Check release or test OS image version on internal storage.
Description
-----------
This test checks if Chrome OS test image or release image version in
'/etc/lsb-release' are greater than or equal to the value of argument
``min_version``. If the version is too old and argument ``reimage`` is set to
True, download and apply either remote netboot firmware or cros_payload
components from factory server.
Note when use_netboot is specified, the DUT will reboot into netboot firmware
and start network image installation process to re-image.
Test Procedure
--------------
1. This test will first check if test image (when ``check_release_image`` is
False) or release image (when ``check_release_image`` is True) version >=
``min_version``. If so, this test will pass.
2. If ``reimage`` is set to False, this test will fail.
3. If ``require_space`` is set to True, this test will wait for the user presses
spacebar.
4. If ``use_netboot`` is set to True, this test will try to download
netboot firmware from factory server and flash into AP firmware. Otherwise,
download and install the selected component using ``cros_payload`` command.
If the needed components are not available on factory server, this test will
fail.
5. If ``use_netboot`` is set to True, The DUT will then reboot into netboot
firmware and start network image installation process to reimage.
Dependency
----------
- If argument ``reimage`` is set to True, factory server must be set up and be
ready for network image installation.
- If argument ``use_netboot`` is set to True, netboot firmware must be
available on factory server.
Examples
--------
To check test image version is greater than or equal to 9876.5.4, add this in
test list::
{
"pytest_name": "check_image_version",
"args": {
"min_version": "9876.5.4",
"reimage": false
}
}
Reimage if release image version is older than 9876.5.4 by flashing netboot
firmware and make pressing spacebar not needed::
{
"pytest_name": "check_image_version",
"args": {
"min_version": "9876.5.4",
"check_release_image": true,
"require_space": false
}
}
Reimage if test image version is greater than or equal to 9876.5.2012_12_21_2359
(loose format version) by flashing netboot firmware image on factory server::
{
"pytest_name": "check_image_version",
"args": {
"min_version": "9876.5.2012_12_21_2359",
"loose_version": true,
}
}
Reimage if release image is older than the release_image version on factory
server using cros_payload::
{
"pytest_name": "check_image_version",
"args": {
"check_release_image": true,
"use_netboot": false,
}
}
"""
from distutils import version
import logging
import os
from cros.factory.device import device_utils
from cros.factory.test.i18n import _
from cros.factory.test import test_case
from cros.factory.test import test_ui
from cros.factory.testlog import testlog
from cros.factory.test.utils import deploy_utils
from cros.factory.test.utils import update_utils
from cros.factory.utils.arg_utils import Arg
class CheckImageVersionTest(test_case.TestCase):
ARGS = [
Arg('min_version', str, (
'Minimum allowed test or release image version.'
'None to follow the version on factory server.'), default=None),
Arg('loose_version', bool, 'Allow any version number representation.',
default=False),
Arg('reimage', bool, 'True to re-image when image version mismatch.',
default=True),
Arg('require_space', bool,
'True to require a space key press before re-imaging.', default=True),
Arg('check_release_image', bool,
'True to check release image instead of test image.', default=False),
Arg('verify_rootfs', bool,
'True to verify rootfs before install image.',
default=True),
Arg('use_netboot', bool,
'True to image with netboot, otherwise cros_payload.', default=True)]
ui_class = test_ui.ScrollableLogUI
def setUp(self):
self.dut = device_utils.CreateDUTInterface()
def WaitNetworkReady(self):
while not self.dut.status.eth_on:
self.ui.SetInstruction(_('Please connect to ethernet.'))
self.Sleep(0.5)
def runTest(self):
# If this test stop unexpectedly during installing new image, we need to
# check image version and verify Root FS to ensure the DUT is successfully
# installed or not. The partition of Root FS is the last one to be written,
# so the image version from lsb-release and the verification of Root FS can
# ensure the result of installation.
if self.CheckImageVersion() and self.VerifyRootFs():
return
if not self.args.reimage:
self.FailTask('Image version is incorrect. Please re-image this device.')
self.WaitNetworkReady()
if self.args.require_space:
self.ui.SetInstruction(
_('Image version is incorrect. Press space to re-image.'))
self.ui.WaitKeysOnce(test_ui.SPACE_KEY)
if self.args.use_netboot:
component = update_utils.COMPONENTS.netboot_firmware
destination = None
callback = self.NetbootCallback
else:
self.assertTrue(
self.args.check_release_image, 'Only release_image is supported.')
component = update_utils.COMPONENTS.release_image
destination = self.dut.partitions.rootdev
callback = None
self.ReImage(component, destination, callback)
def NetbootCallback(self, component, destination, url):
# TODO(hungte) Should we merge this with flash_netboot.py?
del url # Unused.
fw_path = os.path.join(destination, component)
self.ui.SetInstruction(_('Flashing {component}...', component=component))
try:
if self.dut.link.IsLocal():
self.ui.PipeProcessOutputToUI(
['flash_netboot', '-y', '-i', fw_path, '--no-reboot'])
else:
with self.dut.temp.TempFile() as temp_file:
self.dut.link.Push(fw_path, temp_file)
factory_par = deploy_utils.CreateFactoryTools(self.dut)
factory_par.CheckCall(
['flash_netboot', '-y', '-i', temp_file, '--no-reboot'], log=True)
self.dut.CheckCall(['reboot'], log=True)
except Exception:
self.FailTask('Error flashing netboot firmware!')
else:
self.FailTask('Incorrect image version, DUT is rebooting to reimage.')
def ReImage(self, component, destination, callback):
updater = update_utils.Updater(
component, spawn=self.ui.PipeProcessOutputToUI)
if not updater.IsUpdateAvailable():
self.FailTask('%s not available on factory server.' % component)
self.ui.SetInstruction(_('Updating {component}....', component=component))
updater.PerformUpdate(destination=destination, callback=callback)
def CheckImageVersion(self):
if self.args.min_version is None:
# TODO(hungte) In future if we find it useful to reflash netboot for
# updating test image, we can add test_image to update_utils and enable
# fetching version here.
self.assertTrue(
self.args.check_release_image,
'Empty min_version only allowed for check_release_image.')
self.WaitNetworkReady()
updater = update_utils.Updater(update_utils.COMPONENTS.release_image)
# The 'release_image' component in cros_payload is using
# CHROMEOS_RELEASE_DESCRIPTION for version string so we have to strip it
# in future when supporting "re-image if remote is different".
self.args.min_version = updater.GetUpdateVersion().split()[0]
if not self.args.min_version:
self.FailTask('Release image not available on factory server.')
if self.args.check_release_image:
ver = self.dut.info.release_image_version
name = 'release_image'
else:
ver = self.dut.info.factory_image_version
name = 'test_image'
if ver is None:
logging.warning('Can\'t find current version')
return False
testlog.LogParam(name=name, value=ver)
expected = self.args.min_version
version_format = (version.LooseVersion if self.args.loose_version else
version.StrictVersion)
logging.info('Using version format: %r', version_format.__name__)
logging.info('current version: %r, expected: %r', ver, expected)
# TODO(hungte) In future we may want 'exact' match more than min_version.
return version_format(ver) >= version_format(expected)
def VerifyRootFs(self):
if self.args.check_release_image and self.args.verify_rootfs:
factory_tool = deploy_utils.CreateFactoryTools(self.dut)
exit_code = factory_tool.Call(
['gooftool', 'verify_rootfs', '--release_rootfs',
self.dut.partitions.RELEASE_ROOTFS.path])
return exit_code == 0
return True