def unarchive(archive_fpath, dest_folder, logger):
    """ extracts a supported archive to a specified folder """

    supported_extensions = (".zip", ".tar", ".tar.bz2", ".tar.gz", ".tar.xz")
    if sum([1 for ext in supported_extensions
            if archive_fpath.endswith(ext)]) == 0:
        raise NotImplementedError(
            "Archive format extraction not supported: {}".format(
                archive_fpath))

    if archive_fpath.endswith(".zip"):
        unzip_archive(archive_fpath, dest_folder)
        return

    if sys.platform == "win32":
        # 7z does not natively support uncompressing tar.xx in one step
        if re.match(r".*\.tar\.(bz2|gz|xz)$", archive_fpath):
            win_unarchive_compressed_tar_pipe(archive_fpath, dest_folder,
                                              logger)
            return

        command = [szip_exe, "x", "-o{}".format(dest_folder), archive_fpath]
    else:
        tar_exe = "/usr/bin/tar" if sys.platform == "darwin" else "/bin/tar"
        # using -o and -m as exfat dont support mod times and ownership is different
        command = [
            tar_exe, "-C", dest_folder, "-x", "-m", "-o", "-f", archive_fpath
        ]

    subprocess_pretty_check_call(command, logger)
def win_unarchive_compressed_tar(archive_fpath, dest_folder, logger):
    """ uncompress tar.[bz2|gz] on windows using two passes """

    # uncompress first
    subprocess_pretty_check_call(
        [szip_exe, "x", "-o{}".format(dest_folder), archive_fpath], logger)

    # retrieve extracted tar fpath
    tar_fname = [
        fname for fname in os.listdir(dest_folder) if fname.endswith(".tar")
    ][-1]
    tar_fpath = os.path.join(dest_folder, tar_fname)

    # untar
    subprocess_pretty_check_call(
        [szip_exe, "x", "-ttar", "-o{}".format(dest_folder), tar_fpath],
        logger)
    # remove tar
    os.remove(tar_fpath)
Beispiel #3
0
def mount_data_partition(image_fpath, logger):
    """ mount the QEMU image's 3rd part and return its mount point/drive """

    target_dev = get_virtual_device(image_fpath, logger)

    if sys.platform == "linux" and bool(os.getenv("NO_UDISKS", False)):
        # create a mount point in /tmp
        mount_point = tempfile.mkdtemp()

        try:
            subprocess_pretty_check_call(
                [mount_exe, "-t", "exfat", target_dev, mount_point], logger)
        except Exception:
            # ensure we release the loop device on mount failure
            unmount_data_partition(mount_point, target_dev, logger)
            raise
        return mount_point, target_dev

    elif sys.platform == "linux":
        # mount the loop-device (udisksctl sets the mount point)
        udisks_mount_ret, udisks_mount = subprocess_pretty_call(
            [udisksctl_exe, "mount", "--block-device", target_dev, udisks_nou],
            logger,
            check=False,
            decode=True,
        )
        udisks_mount = udisks_mount[0].strip()

        if udisks_mount_ret != 0 and "AlreadyMounted" in udisks_mount:
            # was automatically mounted (gnome default)
            mount_point = re.search(r"at `(\/media\/.*)'\.$",
                                    udisks_mount).groups()[0]
        elif udisks_mount_ret == 0:
            # udisksctl always mounts under /media/
            mount_point = re.search(r"at (\/media\/.+)\.$",
                                    udisks_mount).groups()[0]
        else:
            release_virtual_device(target_dev,
                                   logger)  # release loop if attached
            raise OSError("failed to mount {}".format(target_dev))

        return mount_point, target_dev

    elif sys.platform == "darwin":
        target_part = "{dev}s3".format(dev=target_dev)

        # create a mount point in /tmp
        mount_point = tempfile.mkdtemp()
        try:
            subprocess_pretty_check_call(
                [mount_exe, "-t", "exfat", target_part, mount_point], logger)
        except Exception:
            # ensure we release the loop device on mount failure
            unmount_data_partition(mount_point, target_dev, logger)
            raise
        return mount_point, target_dev

    elif sys.platform == "win32":
        mount_point = "{}\\".format(target_dev)

        # mount into the specified drive
        subprocess_pretty_check_call(
            [
                imdisk_exe,
                "-a",
                "-f",
                image_fpath,
                "-o",
                "rw",
                "-t",
                "file",
                "-v",
                "3",
                "-m",
                target_dev,
            ],
            logger,
        )
        return mount_point, target_dev
Beispiel #4
0
def format_data_partition(image_fpath, logger):
    """ format the QEMU image's 3rd part in exfat on host """

    target_dev = get_virtual_device(image_fpath, logger)

    if sys.platform == "linux":

        # make sure it's not mounted (gnome automounts)
        if bool(os.getenv("NO_UDISKS", False)):
            subprocess_pretty_call([umount_exe, target_dev], logger)
        else:
            subprocess_pretty_call(
                [
                    udisksctl_exe, "unmount", "--block-device", target_dev,
                    udisks_nou
                ],
                logger,
            )

        # change mode via elevation if we can't format it
        previous_mode = None
        if not can_write_on(target_dev):
            previous_mode = allow_write_on(target_dev, logger)

        # format the data partition
        try:
            subprocess_pretty_check_call(
                [mkfs_exe, "-n", data_partition_label, target_dev], logger)
        finally:
            # remove write rights we just added
            if previous_mode:
                restore_mode(target_dev, previous_mode, logger)

            # ensure we release the loop device on mount failure
            unmount_data_partition(None, target_dev, logger)

    elif sys.platform == "darwin":
        target_part = "{dev}s3".format(dev=target_dev)

        try:
            subprocess_pretty_check_call(
                [
                    diskutil_exe,
                    "eraseVolume",
                    "exfat",
                    data_partition_label,
                    target_part,
                ],
                logger,
            )
        finally:
            # ensure we release the loop device on mount failure
            unmount_data_partition(None, target_dev, logger)

    elif sys.platform == "win32":
        # mount into specified path AND format
        try:
            subprocess_pretty_check_call(
                [
                    imdisk_exe,
                    "-a",
                    "-f",
                    image_fpath,
                    "-o",
                    "rw",
                    "-t",
                    "file",
                    "-v",
                    "3",
                    "-p",
                    "/fs:exfat /V:{} /q /y".format(data_partition_label),
                    "-m",
                    target_dev,
                ],
                logger,
            )
        finally:
            # ensure we release the loop device on mount failure
            unmount_data_partition(None, target_dev, logger)
Beispiel #5
0
def run_installation(name,
                     timezone,
                     language,
                     wifi_pwd,
                     admin_account,
                     kalite,
                     aflatoun,
                     wikifundi,
                     edupi,
                     zim_install,
                     size,
                     logger,
                     cancel_event,
                     sd_card,
                     favicon,
                     logo,
                     css,
                     done_callback=None,
                     build_dir="."):

    try:
        # Prepare SD Card
        if sd_card:
            if sys.platform == "linux":
                #TODO restore sd_card mod
                subprocess_pretty_check_call(
                    ["pkexec", "chmod", "-c", "o+w", sd_card], logger)
            elif sys.platform == "darwin":
                #TODO restore sd_card mod
                subprocess_pretty_check_call([
                    "osascript", "-e",
                    "do shell script \"diskutil unmountDisk {0} && chmod -v o+w {0}\" with administrator privileges"
                    .format(sd_card)
                ], logger)
            elif sys.platform == "win32":
                matches = re.findall(r"\\\\.\\PHYSICALDRIVE(\d*)", sd_card)
                if len(matches) != 1:
                    raise ValueError(
                        "Error while getting physical drive number")
                device_number = matches[0]

                r, w = os.pipe()
                os.write(w,
                         str.encode("select disk {}\n".format(device_number)))
                os.write(w, b"clean\n")
                os.close(w)
                logger.std(
                    "diskpart select disk {} and clean".format(device_number))
                subprocess_pretty_check_call(["diskpart"], logger, stdin=r)

        # set image names
        today = datetime.today().strftime('%Y_%m_%d-%H_%M_%S')

        image_final_path = os.path.join(build_dir,
                                        "pibox-{}.img".format(today))
        image_building_path = os.path.join(
            build_dir, "pibox-{}.BUILDING.img".format(today))
        image_error_path = os.path.join(build_dir,
                                        "pibox-{}.ERROR.img".format(today))

        # Download Raspbian
        logger.step("Download Raspbian-lite image")
        hook = ReportHook(logger.raw_std).reporthook
        (zip_filename, _) = urllib.request.urlretrieve(data.raspbian_url,
                                                       reporthook=hook)
        with ZipFile(zip_filename) as zipFile:
            logger.std("extract " + data.raspbian_zip_path)
            extraction = zipFile.extract(data.raspbian_zip_path, build_dir)
            shutil.move(extraction, image_building_path)
        os.remove(zip_filename)

        # Instance emulator
        emulator = qemu.Emulator(data.vexpress_boot_kernel,
                                 data.vexpress_boot_dtb, image_building_path,
                                 logger)

        # Resize image
        if size < emulator.get_image_size():
            logger.err("cannot decrease image size")
            raise ValueError("cannot decrease image size")

        emulator.resize_image(size)

        # Run emulation
        with emulator.run(cancel_event) as emulation:
            # Resize filesystem
            emulation.resize_fs()

            emulation.exec_cmd(
                "sudo sed -i s/mirrordirector/archive/ /etc/apt/sources.list")

            ansiblecube_emulation_path = "/var/lib/ansible/local"
            emulation.exec_cmd("sudo mkdir --mode 0755 -p /var/lib/ansible/")
            emulation.put_dir(data.ansiblecube_path,
                              ansiblecube_emulation_path)

            # Run ansiblecube
            logger.step("Run ansiblecube")
            ansiblecube.run(machine=emulation,
                            name=name,
                            timezone=timezone,
                            wifi_pwd=wifi_pwd,
                            kalite=kalite,
                            wikifundi=wikifundi,
                            edupi=edupi,
                            aflatoun=aflatoun,
                            ansiblecube_path=ansiblecube_emulation_path,
                            zim_install=zim_install,
                            admin_account=admin_account)

            # Write ideascube configuration
            with open(data.pibox_ideascube_conf, "r") as f:
                pibox_ideascube_conf = f.read()

            pibox_ideascube_conf_fmt = pibox_ideascube_conf.replace(
                "'", "'\\''")
            pibox_conf_path = "/opt/venvs/ideascube/lib/python3.4/site-packages/ideascube/conf/pibox.py"
            emulation.exec_cmd(
                "sudo sh -c 'cat > {} <<END_OF_CMD3267\n{}\nEND_OF_CMD3267'".
                format(pibox_conf_path, pibox_ideascube_conf_fmt))
            emulation.exec_cmd(
                "sudo chown ideascube:ideascube {}".format(pibox_conf_path))

            extra_app_cards = []
            if kalite != None:
                extra_app_cards.append('khanacademy')

            custom_cards = []
            if aflatoun == True:
                custom_cards.append({
                    'category': 'learn',
                    'url': 'http://aflatoun.koombook.lan',
                    'title': 'Aflatoun',
                    'description':
                    'Social and Financial Education for Children and Young People',
                    'fa': 'book',
                    'is_staff': False
                })
            if wikifundi != None:
                if "en" in wikifundi:
                    custom_cards.append({
                        'category': 'create',
                        'url': 'http://en.wikifundi.koombook.lan',
                        'title': 'Wikifundi',
                        'description':
                        'Offline editable environment that provides a similar experience to editing Wikipedia online',
                        'fa': 'wikipedia-w',
                        'is_staff': False
                    })
                if "fr" in wikifundi:
                    custom_cards.append({
                        'category': 'create',
                        'url': 'http://fr.wikifundi.koombook.lan',
                        'title': 'Wikifundi',
                        'description':
                        'Environnement qui vous permet de créer des articles Wikipédia hors-ligne',
                        'fa': 'wikipedia-w',
                        'is_staff': False
                    })
            if edupi == True:
                custom_cards.append({
                    'category': 'manage',
                    'url': 'http://edupi.koombook.lan',
                    'title': 'Edupi',
                    'description': 'Content management application',
                    'fa': 'folder',
                    'is_staff': False
                })

            kb_conf = (
                "from .pibox import *  # pragma: no flakes\n\n"
                "EXTRA_APP_CARDS = {extra_app_cards}\n\n"
                "CUSTOM_CARDS = {custom_cards}\n\n"
                "LANGUAGE_CODE = '{language}'\n\n"
                "LANGUAGES = [('{language}', '{language_name}')]\n").format(
                    extra_app_cards=extra_app_cards,
                    custom_cards=custom_cards,
                    language=language,
                    language_name=dict(data.ideascube_languages)[language])

            kb_conf_fmt = kb_conf.replace("'", "'\\''")
            kb_conf_path = "/opt/venvs/ideascube/lib/python3.4/site-packages/ideascube/conf/kb.py"
            emulation.exec_cmd(
                "sudo sh -c 'cat > {} <<END_OF_CMD3267\n{}\nEND_OF_CMD3267'".
                format(kb_conf_path, kb_conf_fmt))
            emulation.exec_cmd(
                "sudo chown ideascube:ideascube {}".format(kb_conf_path))

            if logo is not None:
                logo_emulation_path = "/usr/share/ideascube/static/branding/header-logo.png"
                emulation.put_file(logo, logo_emulation_path)
                emulation.exec_cmd("sudo chown ideascube:ideascube {}".format(
                    logo_emulation_path))

            if favicon is not None:
                favicon_emulation_path = "/usr/share/ideascube/static/branding/favicon.png"
                emulation.put_file(favicon, favicon_emulation_path)
                emulation.exec_cmd("sudo chown ideascube:ideascube {}".format(
                    favicon_emulation_path))

            if css is not None:
                css_emulation_path = "/usr/share/ideascube/static/branding/style.css"
                emulation.put_file(css, css_emulation_path)
                emulation.exec_cmd("sudo chown ideascube:ideascube {}".format(
                    css_emulation_path))

        # Write image to SD Card
        if sd_card:
            emulator.copy_image(sd_card)

    except Exception as e:
        # Set final image filename
        if os.path.isfile(image_building_path):
            os.rename(image_building_path, image_error_path)

        logger.step("Failed")
        logger.err(str(e))
        error = e
    else:
        # Set final image filename
        os.rename(image_building_path, image_final_path)

        logger.step("Done")
        error = None

    if done_callback:
        done_callback(error)

    return error