Ejemplo n.º 1
0
def update_image_successful(install_image,
                            regenerate_image_id=True,
                            signed=False,
                            skip_reboot_verification=False,
                            expected_mender_clients=1,
                            pre_upload_callback=lambda: None,
                            pre_deployment_callback=lambda: None,
                            deployment_triggered_callback=lambda: None,
                            compression_type="gzip"):
    """
        Perform a successful upgrade, and assert that deployment status/logs are correct.

        A reboot is performed, and running partitions have been swapped.
        Deployment status will be set as successful for device.
        Logs will not be retrieved, and result in 404.
    """

    previous_inactive_part = Helpers.get_passive_partition()
    with Helpers.RebootDetector() as reboot:
        deployment_id, expected_image_id = common_update_procedure(
            install_image,
            regenerate_image_id,
            signed=signed,
            pre_deployment_callback=pre_deployment_callback,
            deployment_triggered_callback=deployment_triggered_callback,
            compression_type=compression_type)
        reboot.verify_reboot_performed()

    with Helpers.RebootDetector() as reboot:
        try:
            assert Helpers.get_active_partition() == previous_inactive_part
        except AssertionError:
            logs = []
            for d in adm.get_devices():
                logs.append(deploy.get_logs(d["device_id"], deployment_id))

            pytest.fail(
                "device did not flip partitions during update, here are the device logs:\n\n %s"
                % (logs))

        deploy.check_expected_statistics(deployment_id, "success",
                                         expected_mender_clients)

        for d in adm.get_devices():
            deploy.get_logs(d["device_id"], deployment_id, expected_status=404)

        if not skip_reboot_verification:
            reboot.verify_reboot_not_performed()

    assert Helpers.yocto_id_installed_on_machine() == expected_image_id

    deploy.check_expected_status("finished", deployment_id)

    # make sure backend recognizes signed and unsigned images
    artifact_id = deploy.get_deployment(deployment_id)["artifacts"][0]
    artifact_info = deploy.get_artifact_details(artifact_id)
    assert artifact_info[
        "signed"] is signed, "image was not correct recognized as signed/unsigned"

    return deployment_id
    def test_deployed_during_network_outage(self, install_image=conftest.get_valid_image()):
        """
            Install a valid upgrade image while there is no network availability on the device
            Re-establishing the network connectivity results in the upgrade to be triggered.

            Emulate a flaky network connection, and ensure that the deployment still succeeds.
        """
        if not env.host_string:
            execute(self.test_deployed_during_network_outage,
                    hosts=get_mender_clients(),
                    install_image=install_image)
            return

        Helpers.gateway_connectivity(False)
        deployment_id, expected_yocto_id = common_update_proceduce(install_image)
        time.sleep(60)

        for i in range(5):
            time.sleep(5)
            Helpers.gateway_connectivity(i % 2 == 0)
        Helpers.gateway_connectivity(True)

        logging.info("Network stabilized")
        Helpers.verify_reboot_performed()
        deploy.check_expected_status(deployment_id, "success", len(get_mender_clients()))

        assert Helpers.yocto_id_installed_on_machine() == expected_yocto_id
    def test_update_image_id_already_installed(
        self, install_image=conftest.get_valid_image()):
        """Uploading an image with an incorrect name set results in failure and rollback."""

        if not env.host_string:
            execute(self.test_update_image_id_already_installed,
                    hosts=get_mender_clients(),
                    install_image=install_image)
            return

        previous_inactive_part = Helpers.get_passive_partition()

        deployment_id, expected_image_id = common_update_proceduce(
            install_image, True)
        Helpers.verify_reboot_performed()

        devices_accepted_id = [
            device["id"] for device in adm.get_devices_status("accepted")
        ]
        deployment_id = deploy.trigger_deployment(
            name="New valid update",
            artifact_name=expected_image_id,
            devices=devices_accepted_id)

        deploy.check_expected_status(deployment_id, "already-installed",
                                     len(get_mender_clients()))
Ejemplo n.º 4
0
def update_image_failed(install_image="broken_update.ext4"):
    """
        Perform a upgrade using a broken image (random data)
        The device will reboot, uboot will detect this is not a bootable image, and revert to the previous partition.
        The resulting upgrade will be considered a failure.
    """

    devices_accepted = get_mender_clients()
    original_image_id = Helpers.yocto_id_installed_on_machine()

    previous_active_part = Helpers.get_active_partition()
    deployment_id, _ = common_update_procedure(install_image,
                                               broken_image=True)

    Helpers.verify_reboot_performed()
    assert Helpers.get_active_partition() == previous_active_part

    deploy.check_expected_statistics(deployment_id, "failure",
                                     len(devices_accepted))

    for d in adm.get_devices():
        assert "running rollback image" in deploy.get_logs(
            d["device_id"], deployment_id)

    assert Helpers.yocto_id_installed_on_machine() == original_image_id
    Helpers.verify_reboot_not_performed()

    deploy.check_expected_status("finished", deployment_id)
    def test_update_image_successful(self,
                                     install_image=conftest.get_valid_image(),
                                     regnerate_image_id=True):
        """
            Perform a successful upgrade, and assert that deployment status/logs are correct.

            A reboot is performed, and running partitions have been swapped.
            Deployment status will be set as successful for device.
            Logs will not be retrieved, and result in 404.
        """
        if not env.host_string:
            execute(self.test_update_image_successful,
                    hosts=get_mender_clients(),
                    install_image=install_image,
                    regnerate_image_id=regnerate_image_id)
            return

        previous_inactive_part = Helpers.get_passive_partition()
        deployment_id, expected_image_id = common_update_proceduce(
            install_image, regnerate_image_id)

        Helpers.verify_reboot_performed()
        assert Helpers.get_active_partition() == previous_inactive_part
        deploy.check_expected_status(deployment_id, "success",
                                     len(get_mender_clients()))

        for d in adm.get_devices():
            deploy.get_logs(d["id"], deployment_id, expected_status=404)

        Helpers.verify_reboot_not_performed()
        assert Helpers.yocto_id_installed_on_machine() == expected_image_id
Ejemplo n.º 6
0
def update_image_failed(install_image="broken_update.ext4",
                        expected_mender_clients=1):
    """
        Perform a upgrade using a broken image (random data)
        The device will reboot, uboot will detect this is not a bootable image, and revert to the previous partition.
        The resulting upgrade will be considered a failure.
    """

    devices_accepted = get_mender_clients()
    original_image_id = Helpers.yocto_id_installed_on_machine()

    previous_active_part = Helpers.get_active_partition()
    with Helpers.RebootDetector() as reboot:
        deployment_id, _ = common_update_procedure(install_image,
                                                   broken_image=True)
        reboot.verify_reboot_performed()

    with Helpers.RebootDetector() as reboot:
        assert Helpers.get_active_partition() == previous_active_part

        deploy.check_expected_statistics(deployment_id, "failure",
                                         expected_mender_clients)

        for d in auth_v2.get_devices():
            assert "got invalid entrypoint into the state machine" in deploy.get_logs(
                d["id"], deployment_id)

        assert Helpers.yocto_id_installed_on_machine() == original_image_id
        reboot.verify_reboot_not_performed()

    deploy.check_expected_status("finished", deployment_id)
Ejemplo n.º 7
0
def common_update_procedure(install_image,
                            regenerate_image_id=True,
                            device_type=conftest.machine_name,
                            broken_image=False,
                            verify_status=True,
                            signed=False,
                            devices=None,
                            scripts=[],
                            pre_upload_callback=lambda: None,
                            pre_deployment_callback=lambda: None,
                            deployment_triggered_callback=lambda: None,
                            compression_type="gzip"):

    with artifact_lock:
        if broken_image:
            artifact_id = "broken_image_" + str(random.randint(0, 999999))
        elif regenerate_image_id:
            artifact_id = Helpers.artifact_id_randomize(install_image)
            logger.debug("randomized image id: " + artifact_id)
        else:
            artifact_id = Helpers.yocto_id_from_ext4(install_image)

        compression_arg = "--compression " + compression_type

        # create atrifact
        with tempfile.NamedTemporaryFile() as artifact_file:
            created_artifact = image.make_artifact(
                install_image,
                device_type,
                artifact_id,
                artifact_file,
                signed=signed,
                scripts=scripts,
                global_flags=compression_arg)

            if created_artifact:
                pre_upload_callback()
                deploy.upload_image(created_artifact)
                if devices is None:
                    devices = list(
                        set([
                            device["device_id"]
                            for device in adm.get_devices_status("accepted")
                        ]))
                pre_deployment_callback()
                deployment_id = deploy.trigger_deployment(
                    name="New valid update",
                    artifact_name=artifact_id,
                    devices=devices)
            else:
                logger.warn("failed to create artifact")
                pytest.fail("error creating artifact")

    deployment_triggered_callback()
    # wait until deployment is in correct state
    if verify_status:
        deploy.check_expected_status("inprogress", deployment_id)

    return deployment_id, artifact_id
Ejemplo n.º 8
0
    def test_large_update_image(self):
        """Installing an image larger than the passive/active parition size should result in a failure."""
        if not env.host_string:
            execute(self.test_large_update_image, hosts=get_mender_clients())
            return

        with Helpers.RebootDetector() as reboot:
            deployment_id, _ = common_update_procedure(install_image="large_image.dat", regenerate_image_id=False, broken_image=True)
            deploy.check_expected_statistics(deployment_id, "failure", len(get_mender_clients()))
            reboot.verify_reboot_not_performed()
            deploy.check_expected_status("finished", deployment_id)
Ejemplo n.º 9
0
 def test_artifacts_persisted(self):
     devices_to_update = list(
         set([
             device["device_id"]
             for device in adm.get_devices_status("accepted",
                                                  expected_devices=10)
         ]))
     deployment_id = deploy.trigger_deployment(
         name="artifact survived backed upgrade",
         artifact_name=self.provisioned_artifact_id,
         devices=devices_to_update)
     deploy.check_expected_status("finished", deployment_id)
Ejemplo n.º 10
0
    def test_deployments_post_upgrade(self):
        adm.get_devices_status("accepted", 10)

        # perform upgrade
        devices_to_update = list(set([device["device_id"] for device in adm.get_devices_status("accepted", expected_devices=10)]))
        deployment_id, artifact_id = common_update_procedure("core-image-full-cmdline-%s.ext4" % conftest.machine_name,
                                                             device_type="test",
                                                             devices=devices_to_update)

        deploy.check_expected_status("finished", deployment_id)
        assert deploy.get_statistics(deployment_id)["success"] == 7
        assert deploy.get_statistics(deployment_id)["failure"] == 3

        deploy.get_status("finished")
Ejemplo n.º 11
0
    def abort_deployment(self, abort_step=None, mender_performs_reboot=False):
        """
            Trigger a deployment, and cancel it within 15 seconds, make sure no deployment is performed.

            Args:
                mender_performs_reboot: if set to False, a manual reboot is performed and
                                            checks are performed.
                                        if set to True, wait until device is rebooted.
        """
        if not env.host_string:
            execute(self.abort_deployment,
                    abort_step=abort_step,
                    mender_performs_reboot=mender_performs_reboot,
                    hosts=get_mender_clients())
            return

        install_image = conftest.get_valid_image()
        expected_partition = Helpers.get_active_partition()
        expected_image_id = Helpers.yocto_id_installed_on_machine()
        with Helpers.RebootDetector() as reboot:
            deployment_id, _ = common_update_procedure(install_image,
                                                       verify_status=False)

            if abort_step is not None:
                deploy.check_expected_statistics(deployment_id, abort_step,
                                                 len(get_mender_clients()))
            deploy.abort(deployment_id)
            deploy.check_expected_statistics(deployment_id, "aborted",
                                             len(get_mender_clients()))

            # no deployment logs are sent by the client, is this expected?
            for d in auth_v2.get_devices():
                deploy.get_logs(d["id"], deployment_id, expected_status=404)

            if mender_performs_reboot:
                # If Mender performs reboot, we need to wait for it to reboot
                # back into the original filesystem.
                reboot.verify_reboot_performed(number_of_reboots=2)
            else:
                # Else we reboot ourselves, just to make sure that we have not
                # unintentionally switched to the new partition.
                reboot.verify_reboot_not_performed()
                run("( sleep 10 ; reboot ) 2>/dev/null >/dev/null &")
                reboot.verify_reboot_performed()

        assert Helpers.get_active_partition() == expected_partition
        assert Helpers.yocto_id_installed_on_machine() == expected_image_id
        deploy.check_expected_status("finished", deployment_id)
    def test_update_image_breaks_networking(self, install_image="core-image-full-cmdline-vexpress-qemu-broken-network.ext4"):
        """
            Install an image without systemd-networkd binary existing.
            The network will not function, mender will not be able to send any logs.

            The expected status is the update will rollback, and be considered a failure
        """
        if not env.host_string:
            execute(self.test_update_image_breaks_networking,
                    hosts=get_mender_clients(),
                    install_image=install_image)
            return

        deployment_id, _ = common_update_proceduce(install_image)
        Helpers.verify_reboot_performed() # since the network is broken, two reboots will be performed, and the last one will be detected
        deploy.check_expected_status(deployment_id, "failure", len(get_mender_clients()))
Ejemplo n.º 13
0
    def test_deployment_abortion_success(self):
        # maybe an acceptance test is enough for this check?

        if not env.host_string:
            execute(self.test_deployment_abortion_success,
                    hosts=get_mender_clients())
            return

        install_image = conftest.get_valid_image()
        deployment_id, _ = common_update_procedure(install_image)

        Helpers.verify_reboot_performed()

        deploy.check_expected_statistics(deployment_id, "success", len(get_mender_clients()))
        deploy.abort_finished_deployment(deployment_id)
        deploy.check_expected_statistics(deployment_id, "success", len(get_mender_clients()))
        deploy.check_expected_status("finished", deployment_id)
Ejemplo n.º 14
0
    def test_unsigned_artifact_fails_deployment(self, standard_setup_with_signed_artifact_client):
        """
            Make sure that an unsigned image fails, and is handled by the backend.
            Notice that this test needs a fresh new version of the backend, since
            we installed a signed image earlier without a verification key in mender.conf
        """
        if not env.host_string:
            execute(self.test_unsigned_artifact_fails_deployment,
                    standard_setup_with_signed_artifact_client,
                    hosts=get_mender_clients())
            return

        deployment_id, _ = common_update_procedure(install_image=conftest.get_valid_image())
        deploy.check_expected_status("finished", deployment_id)
        deploy.check_expected_statistics(deployment_id, "failure", 1)

        for d in adm.get_devices():
            assert "expecting signed artifact, but no signature file found" in \
                deploy.get_logs(d["device_id"], deployment_id)
    def abort_deployment(self, abort_step=None, mender_performs_reboot=False):
        """
            Trigger a deployment, and cancel it within 15 seconds, make sure no deployment is performed.

            Args:
                mender_performs_reboot: if set to False, a manual reboot is performed and
                                            checks are performed.
                                        if set to True, wait until device is rebooted.
        """
        if not env.host_string:
            execute(self.abort_deployment,
                    abort_step=abort_step,
                    mender_performs_reboot=mender_performs_reboot,
                    hosts=get_mender_clients())
            return

        install_image = conftest.get_valid_image()
        expected_partition = Helpers.get_active_partition()
        expected_image_id = Helpers.yocto_id_installed_on_machine()
        token = Helpers.place_reboot_token()
        deployment_id, _ = common_update_procedure(install_image,
                                                   verify_status=False)

        if abort_step is not None:
            deploy.check_expected_statistics(deployment_id, abort_step,
                                             len(get_mender_clients()))
        deploy.abort(deployment_id)
        deploy.check_expected_statistics(deployment_id, "aborted",
                                         len(get_mender_clients()))

        # no deployment logs are sent by the client, is this expected?
        for d in adm.get_devices():
            deploy.get_logs(d["device_id"], deployment_id, expected_status=404)

        if not mender_performs_reboot:
            token.verify_reboot_not_performed()
            run("( sleep 10 ; reboot ) 2>/dev/null >/dev/null &")

        token.verify_reboot_performed()

        assert Helpers.get_active_partition() == expected_partition
        assert Helpers.yocto_id_installed_on_machine() == expected_image_id
        deploy.check_expected_status("finished", deployment_id)
Ejemplo n.º 16
0
def common_update_procedure(install_image,
                            regenerate_image_id=True,
                            device_type="vexpress-qemu",
                            broken_image=False,
                            verify_status=True,
                            devices=None):

    with artifact_lock:
        if broken_image:
            artifact_id = "broken_image_" + str(random.randint(0, 999999))
        elif regenerate_image_id:
            artifact_id = Helpers.artifact_id_randomize(install_image)
            logger.debug("Randomized image id: " + artifact_id)
        else:
            artifact_id = Helpers.yocto_id_from_ext4(install_image)

        # create atrifact
        with tempfile.NamedTemporaryFile() as artifact_file:
            created_artifact = image.make_artifact(install_image, device_type,
                                                   artifact_id, artifact_file)

            if created_artifact:
                deploy.upload_image(created_artifact)
                if devices is None:
                    devices = list(
                        set([
                            device["device_id"]
                            for device in adm.get_devices_status("accepted")
                        ]))
                deployment_id = deploy.trigger_deployment(
                    name="New valid update",
                    artifact_name=artifact_id,
                    devices=devices)
            else:
                pytest.fail("error creating artifact")

        # wait until deployment is in correct state
        if verify_status:
            deploy.check_expected_status("inprogress", deployment_id)

        return deployment_id, artifact_id
Ejemplo n.º 17
0
def update_image_successful(
        install_image=conftest.get_valid_image(), regenerate_image_id=True):
    """
        Perform a successful upgrade, and assert that deployment status/logs are correct.

        A reboot is performed, and running partitions have been swapped.
        Deployment status will be set as successful for device.
        Logs will not be retrieved, and result in 404.
    """

    previous_inactive_part = Helpers.get_passive_partition()
    deployment_id, expected_image_id = common_update_procedure(
        install_image, regenerate_image_id)

    Helpers.verify_reboot_performed()

    try:
        assert Helpers.get_active_partition() == previous_inactive_part
    except AssertionError:
        logs = []
        for d in adm.get_devices():
            logs.append(deploy.get_logs(d["device_id"], deployment_id))

        pytest.fail(
            "device did not flip partitions during update, here are the device logs:\n\n %s"
            % (logs))

    deploy.check_expected_statistics(deployment_id, "success",
                                     len(get_mender_clients()))

    for d in adm.get_devices():
        deploy.get_logs(d["device_id"], deployment_id, expected_status=404)

    Helpers.verify_reboot_not_performed()
    assert Helpers.yocto_id_installed_on_machine() == expected_image_id

    deploy.check_expected_status("finished", deployment_id)
    def test_update_image_recovery(self, install_image=conftest.get_valid_image()):
        """
            Install an update, and reboot the system when we detect it's being copied over to the inactive parition.

            The test should result in a failure.
        """
        if not env.host_string:
            execute(self.test_update_image_recovery,
                    hosts=get_mender_clients(),
                    install_image=install_image)
            return

        installed_yocto_id = Helpers.yocto_id_installed_on_machine()

        inactive_part = Helpers.get_passive_partition()
        deployment_id, _ = common_update_proceduce(install_image)
        active_part = Helpers.get_active_partition()

        for i in range(60):
            time.sleep(0.5)
            with quiet():
                # make sure we are writing to the inactive partition
                output = run("fuser -mv %s" % (inactive_part))
            if output.return_code == 0:
                run("killall -s 9 mender")
                with settings(warn_only=True):
                    run("( sleep 3 ; reboot ) 2>/dev/null >/dev/null &")
                break

        logging.info("Waiting for system to finish reboot")
        Helpers.verify_reboot_performed()
        assert Helpers.get_active_partition() == active_part
        deploy.check_expected_status(deployment_id, "failure", len(get_mender_clients()))
        Helpers.verify_reboot_not_performed()

        assert Helpers.yocto_id_installed_on_machine() == installed_yocto_id