[poky] [RFC PATCH 4/4] meta-yocto-bsp: add a controller for EFI targets where a master image is running

Stefan Stanacar stefanx.stanacar at intel.com
Sun Mar 9 11:49:19 PDT 2014


A bit of background:
- testimage.bbclass has the ability to allow a layer to provide
it's own TEST_TARGET. OE-core has a QemuTarget and a SimpleRemoteTarget
(ssh into an already up and running machine and run tests)
- basically testimage does something like:
  - target.deploy()
  - target.start()
  - runTests()
  - target.stop()

This module assumes a running EFI (and gummiboot as bootloader) machine with
core-image-testmaster installed (or similar).

In order to use this Master Image mechanism there are some hard requirements:
 - it only works for EFI-enabled hardware with the gummiboot patch series applied (see OE-core)
 - your hardware under test has to be in DHCP-enabled network that gives it the same IP for each reboot
 - the IP address of the machine under test needs to be set in local.conf before running the tests.

One time setup:
- build core-image-testmaster with EFI_PROVIDER = "gummiboot"
- install the image on the target

Test image setup:
 - build your test image, e.g core-image-sato as you usually do, but with these in local.conf:
    EFI_PROVIDER = "gummiboot"
    IMAGE_FSTYPES += "tar.gz"
 - Now run the tests:
    INHERIT += "testimage"
    TEST_TARGET = "GenericEfi"
    TEST_TARGET_IP = "192.168.2.3"
    bitbake core-image-sato -c testimage

Other notes:
 - TEST_POWERCONTROL_CMD can be a command that runs on the host and does power cycling.
The test code passes one argument to that command: off/on and nothing more. In my case I use something like
TEST_POWERCONTROL_CMD="/my/expect/script label-for-${MACHINE}" in local.conf.
That expect script connects to the power control equiment, that has custom labels assigned for ports
(I used some variation of MACHINE).
  - if no command is defined it would use classic shutdown/reboot. This is fine as long as the machine
actually reboots, but it's useful for "simple-setup-with-one-board-on-the-desk" scenario, where
some manual interaction is okay from time to time.

[YOCTO #5614]

Signed-off-by: Stefan Stanacar <stefanx.stanacar at intel.com>
---
 meta-yocto-bsp/lib/oeqa/controllers/__init__.py    |  0
 meta-yocto-bsp/lib/oeqa/controllers/masterimage.py | 88 ++++++++++++++++++++++
 meta/lib/oeqa/runtime/ssh.py                       |  2 +
 3 files changed, 90 insertions(+)
 create mode 100644 meta-yocto-bsp/lib/oeqa/controllers/__init__.py
 create mode 100644 meta-yocto-bsp/lib/oeqa/controllers/masterimage.py

diff --git a/meta-yocto-bsp/lib/oeqa/controllers/__init__.py b/meta-yocto-bsp/lib/oeqa/controllers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/meta-yocto-bsp/lib/oeqa/controllers/masterimage.py b/meta-yocto-bsp/lib/oeqa/controllers/masterimage.py
new file mode 100644
index 0000000..f72372b
--- /dev/null
+++ b/meta-yocto-bsp/lib/oeqa/controllers/masterimage.py
@@ -0,0 +1,88 @@
+import os
+import bb
+import traceback
+import time
+
+import oeqa.targetcontrol
+import oeqa.utils.sshcontrol as sshcontrol
+import oeqa.utils.commands as commands
+
+class GenericEfi(oeqa.targetcontrol.SimpleRemoteTarget):
+
+    def __init__(self, d):
+        super(GenericEfi, self).__init__(d)
+        if "tar.gz" not in (d.getVar('IMAGE_FSTYPES', True) or "").split():
+            bb.fatal('This TEST_TARGET requires a tar.gz rootfs for deployment so please ensure that IMAGE_FSTYPES contains "tar.gz". \
+                    (adding IMAGE_FSTYPES += "tar.gz" in local.conf is one way of doing that)')
+        self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("IMAGE_LINK_NAME", True) + '.tar.gz')
+        self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("KERNEL_IMAGETYPE"))
+        if not os.path.isfile(self.rootfs) or not os.path.isfile(self.kernel):
+                bb.fatal("No rootfs or kernel found. Did you build the image?")
+        cmds = d.getVar("TEST_DEPLOY_CMDS", True)
+        if cmds:
+            self.deploy_cmds = cmds.split(",")
+        else:
+            self.deploy_cmds = [
+                'mount -L boot /boot',
+                'mkdir -p /mnt/testrootfs',
+                'mount -L testrootfs /mnt/testrootfs',
+                'rm -rf /mnt/testrootfs/*',
+                'tar xzvf ~/test-rootfs.tar.gz -C /mnt/testrootfs',
+                'cp ~/test-kernel /boot',
+                'mount -t efivarfs efivarfs /sys/firmware/efi/efivars',
+                r'printf "\x07\x00\x00\x00\x74\x00\x65\x00\x73\x00\x74\x00\x00\x00" > /sys/firmware/efi/efivars/LoaderEntryOneShot-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f'
+                ]
+        # this is the name of the command that controls the power for a board
+        # e.g: TEST_POWERCONTROL_CMD = "/home/user/myscripts/powercontrol.py ${MACHINE}"
+        self.powercontrol_cmd = d.getVar("TEST_POWERCONTROL_CMD", True) or ""
+        # master ssh connection
+        self.master = None
+
+
+    def _deploy(self):
+        # make sure we are in the right image
+        self.master.ignore_status = True
+        status = self.master.run("test -f /etc/masterimage")[0]
+        if status != 0:
+            raise Exception("Target doesn't appear to be running a master image now - no /etc/masterimage found")
+        # make sure these aren't mounted
+        self.master.run("umount /boot; umount /mnt/testrootfs")
+
+        # from now on, every deploy cmd should return 0
+        self.master.ignore_status = False
+        self.master.copy_to(self.rootfs, "~/test-rootfs.tar.gz")
+        self.master.copy_to(self.kernel, "~/test-kernel")
+        for cmd in self.deploy_cmds:
+            self.master.run(cmd)
+
+    def deploy(self):
+        super(GenericEfi, self).deploy()
+        self.master = sshcontrol.SSHControl(ip=self.ip, logfile=self.sshlog, timeout=600)
+        self.master.ignore_status = False
+        try:
+            # ssh connectivity check
+            self.master.run("uname -a")
+            self._deploy()
+        except Exception:
+            bb.fatal("Failed deploying test image: %s"  % traceback.format_exc())
+
+    def start(self, params=None):
+        if self.powercontrol_cmd:
+            self.master.run('shutdown -h now')
+            commands.runCmd(self.powercontrol_cmd + " off")
+            commands.runCmd(self.powercontrol_cmd + " on")
+        else:
+            self.master.run('reboot')
+        # assuming the reboot worked, we need to
+        # wait a bit - there are better ways than a timeout
+        # but this should works for my purpose for now
+        time.sleep(90)
+        self.connection = sshcontrol.SSHControl(self.ip, logfile=self.sshlog)
+
+    def stop(self):
+        if self.powercontrol_cmd:
+            commands.runCmd(self.powercontrol_cmd + " off")
+            commands.runCmd(self.powercontrol_cmd + " on")
+        else:
+           self.connection.run('reboot')
+        time.sleep(30)
diff --git a/meta/lib/oeqa/runtime/ssh.py b/meta/lib/oeqa/runtime/ssh.py
index 8c96020..e648660 100644
--- a/meta/lib/oeqa/runtime/ssh.py
+++ b/meta/lib/oeqa/runtime/ssh.py
@@ -14,3 +14,5 @@ class SshTest(oeRuntimeTest):
     def test_ssh(self):
         (status, output) = self.target.run('uname -a')
         self.assertEqual(status, 0, msg="SSH Test failed: %s" % output)
+        (status, output) = self.target.run('cat /etc/masterimage')
+        self.assertEqual(status, 1, msg="This isn't the right image  - /etc/masterimage shouldn't be here %s" % output)
-- 
1.8.5.3



More information about the poky mailing list