Module pandare.qcows

Module for fetching generic PANDA images and managing their metadata.

Expand source code
#!/usr/bin/env python3
'''
Module for fetching generic PANDA images and managing their metadata.
'''

import logging
from os import path, remove, makedirs
from sys import argv
from subprocess import check_call
from collections import namedtuple
from shlex import split as shlex_split

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

VM_DIR = path.join(path.expanduser("~"), ".panda")

class Image(namedtuple('Image', ['arch', 'os', 'prompt', 'cdrom', 'snapshot', 'url', 'extra_files', 'qcow', 'default_mem', 'extra_args'])):
    '''
    The Image class stores information about a supported PANDA image

    Args:
        arch (str): Arch for the given architecture.
        os (str): an os string we can pass to panda with -os
        prompt (regex): a regex to detect a bash prompt after loading the snapshot and sending commands
        cdrom (str): name to use for cd-drive when inserting an ISO via monitor
        qcow (str): optional name to save qcow as
        url (str): url to download the qcow (e.g. https:// website.com/yourqcow.qcow2)
        default_mem (str): memory to use for the root snapshot (e.g. 1G)
        extra_files (list): other files (assumed to be in same directory on server) that we also need
        extra_args (list): Extra arguments to pass to PANDA (e.g. ['-display', 'none'])
    '''

Image.__new__.__defaults__ = (None,) * len(Image._fields)

SUPPORTED_IMAGES = {
    # Debian: support for 4 arches on Wheezy
    'i386_wheezy': Image(
            arch = 'i386',
            os="linux-32-debian:3.2.0-4-686-pae",
            prompt=rb"root@debian-i386:.*# ",
            qcow="wheezy_panda2.qcow2", # Backwards compatability
            cdrom="ide1-cd0",
            snapshot="root",
            default_mem='128M',
            url="https://panda-re.mit.edu/qcows/linux/debian/7.3/x86/debian_7.3_x86.qcow",
            extra_args="-display none"),

    'x86_64_wheezy': Image(
            arch='x86_64',
            os="linux-64-debian:3.2.0-4-amd64",
            prompt=rb"root@debian-amd64:.*# ",
            qcow="wheezy_x64.qcow2",# Backwards compatability 
            cdrom="ide1-cd0",
            snapshot="root",
            default_mem='128M',
            url="https://panda-re.mit.edu/qcows/linux/debian/7.3/x86_64/debian_7.3_x86_64.qcow",
            extra_args="-display none"),

    'ppc_wheezy': Image(
            arch='ppc',
            os="linux-64-debian:3.2.0-4-ppc-pae",
            prompt=rb"root@debian-powerpc:.*# ",
            qcow="ppc_wheezy.qcow2",# Backwards compatability 
            cdrom="ide1-cd0",
            default_mem='128M',
            snapshot="root",
            url="https://panda-re.mit.edu/qcows/linux/debian/7.3/ppc/debian_7.3_ppc.qcow",
            extra_args="-display none"),

    'arm_wheezy': Image(
            arch='arm',
            os="linux-32-debian:3.2.0-4-versatile-arm",
            prompt=rb"root@debian-armel:.*# ",
            qcow="arm_wheezy.qcow",# Backwards compatability 
            cdrom="scsi0-cd2",
            default_mem='128M',
            snapshot="root",
            url="https://panda-re.mit.edu/qcows/linux/debian/7.3/arm/debian_7.3_arm.qcow",
            extra_files=['vmlinuz-3.2.0-4-versatile', 'initrd.img-3.2.0-4-versatile'],
            extra_args='-display none -M versatilepb -append "root=/dev/sda1" -kernel {DOT_DIR}/vmlinuz-3.2.0-4-versatile -initrd {DOT_DIR}/initrd.img-3.2.0-4-versatile'.format(DOT_DIR=VM_DIR)),

    'aarch64_focal': Image(
            arch='aarch64',
            os="linux-64-ubuntu:5.4.0-58-generic-arm64",
            prompt=rb"root@ubuntu-panda:.*# ",
            #cdrom="scsi0-cd2", # No idea what this should be
            default_mem='1G',
            snapshot="root",
            url="https://panda-re.mit.edu/qcows/linux/ubuntu/2004/aarch64/ubuntu20_04-aarch64.qcow",
            extra_files=['ubuntu20_04-aarch64-flash0.qcow'],
            extra_args='-nographic -machine virt -cpu cortex-a57 -drive file={DOT_DIR}/ubuntu20_04-aarch64-flash0.qcow,if=pflash,readonly=on'.format(DOT_DIR=VM_DIR)),

    'mips64': Image(
            arch='mips64',
            os="linux-64-debian:4.14.0-3-5kc-malta", # XXX: NO OSI
            prompt=rb"root@debian-buster-mips:.*# ",
            cdrom="ide1-cd0", # not sure
            snapshot="root",
            url="https://panda-re.mit.edu/qcows/linux/debian/10/mips64/debian-buster-mips.qcow2",
            default_mem='2g',
            extra_files=['vmlinux-4.14.0-3-5kc-malta.mips.buster', 'initrd.img-4.14.0-3-5kc-malta.mips.buster'],
            extra_args='-M malta -cpu MIPS64R2-generic -append "root=/dev/vda console=ttyS0 mem=2048m net.ifnames=0 nokaslr" -netdev user,id=user.0 -device virtio-net,netdev=user.0 -device usb-kbd -device usb-tablet -kernel {DOT_DIR}/vmlinux-4.14.0-3-5kc-malta.mips.buster -initrd {DOT_DIR}/initrd.img-4.14.0-3-5kc-malta.mips.buster -nographic'.format(DOT_DIR=VM_DIR)),

    'mips_wheezy': Image(
            arch='mips',
            os="linux-32-debian:3.2.0-4-4kc-malta",
            prompt=rb"root@debian-mips:.*# ",
            cdrom="ide1-cd0",
            snapshot="root",
            url="https://panda-re.mit.edu/qcows/linux/debian/7.3/mips/debian_7.3_mips.qcow",
            default_mem='1g',
            extra_files=['vmlinux-3.2.0-4-4kc-malta'],
            extra_args='-M malta -kernel {DOT_DIR}/vmlinux-3.2.0-4-4kc-malta -append "root=/dev/sda1" -nographic'.format(DOT_DIR=VM_DIR)),

    'mipsel_wheezy':  Image(
            arch='mipsel',
            os = "linux-32-debian:3.2.0-4-4kc-malta",
            prompt=rb"root@debian-mipsel:.*# ",
            cdrom="ide1-cd0",
            snapshot="root",
            default_mem='1g',
            url="https://panda-re.mit.edu/qcows/linux/debian/7.3/mipsel/debian_7.3_mipsel.qcow",
            extra_files=['vmlinux-3.2.0-4-4kc-malta.mipsel',],
            extra_args='-M malta -kernel {DOT_DIR}/vmlinux-3.2.0-4-4kc-malta.mipsel -append "root=/dev/sda1" -nographic'.format(DOT_DIR=VM_DIR)),

    'mips_buildroot5':  Image(
            arch='mips',
            os = "linux-32-buildroot:5.10.7-4kc-malta",
            prompt=rb"# ",
            cdrom="ide1-cd0",
            snapshot="root",
            default_mem='1g',
            url="https://panda-re.mit.edu/qcows/linux/buildroot/5.10/mips/mips32_buildroot.qcow",
            extra_files=['mips32_vmlinux-5.10.7-4kc-malta',],
            extra_args='-M malta -kernel {DOT_DIR}/mips32_vmlinux-5.10.7-4kc-malta -net nic,model=pcnet -net user -append "root=/dev/hda" -nographic'.format(DOT_DIR=VM_DIR)),


    'mipsel_buildroot5':  Image(
            arch='mipsel',
            os = "linux-32-buildroot:5.10.7-4kc-malta-el",
            prompt=rb"# ",
            cdrom="ide1-cd0",
            snapshot="root",
            default_mem='1g',
            url="https://panda-re.mit.edu/qcows/linux/buildroot/5.10/mipsel/mipsel32_buildroot.qcow",
            extra_files=['mipsel32_vmlinux-5.10.7-4kc-malta-el',],
            extra_args='-M malta -kernel {DOT_DIR}/mipsel32_vmlinux-5.10.7-4kc-malta-el -net nic,model=pcnet -net user -append "root=/dev/hda" -nographic'.format(DOT_DIR=VM_DIR)),


    # Ubuntu: x86/x86_64 support for 16.04, x86_64 support for 18.04
    'i386_ubuntu_1604': Image(
            arch = 'i386',
            os="linux-32-ubuntu:4.4.200-170-generic", # Version.c is 200 but name is 4.4.0. Not sure why
            prompt=rb"root@instance-1:.*#",
            cdrom="ide1-cd0",
            snapshot="root",
            default_mem='1024',
            url="https://panda-re.mit.edu/qcows/linux/ubuntu/1604/x86/ubuntu_1604_x86.qcow",
            extra_args="-display none"),

    #'x86_64_ubuntu_1604': Image( # XXX: This one is broken
    #        arch='x86_64',
    #        os="linux-64-ubuntu:4.4.0-180-pae",
    #        prompt=rb"root@instance-1:.*#",
    #        cdrom="ide1-cd0",
    #        snapshot="root",
    #        default_mem='1024',
    #        url="https://panda-re.mit.edu/qcows/linux/ubuntu/1604/x86_64/ubuntu_1604_x86_64.qcow",
    #        extra_files=['xenial-server-cloudimg-amd64-disk1.img',],
    #        extra_args="-display none"),

    'x86_64_ubuntu_1804': Image(
            arch='x86_64',
            os="linux-64-ubuntu:4.15.0-72-generic-noaslr-nokaslr",
            prompt=rb"root@ubuntu:.*#",
            cdrom="ide1-cd0",
            snapshot="root",
            default_mem='1024',
            url="https://panda-re.mit.edu/qcows/linux/ubuntu/1804/x86_64/bionic-server-cloudimg-amd64-noaslr-nokaslr.qcow2",
            extra_args="-display none"),
}
"""
Dictionary of `Image` objects by name.
Generic values (underlying OS version may change) include:
    x86_64
    i386
    ppc
    arm
    aarch64
    mips
    mipsel
    mips64

You may also specify an exact arch/OS combination from the following exist:
    x86_64_ubuntu_1804
    i386_ubuntu_1604
    ppc_wheezy
    arm_wheezy
    aarch64 _focal
    mips_wheezy
    mips_buildroot5
    mipsel_wheezy
    mipsel_buildroot5
    mips64
""" # TODO: autogenerate values here

# Default values
SUPPORTED_IMAGES['x86_64']  = SUPPORTED_IMAGES['x86_64_ubuntu_1804']
SUPPORTED_IMAGES['i386']    = SUPPORTED_IMAGES['i386_ubuntu_1604']
SUPPORTED_IMAGES['ppc']     = SUPPORTED_IMAGES['ppc_wheezy']
SUPPORTED_IMAGES['arm']     = SUPPORTED_IMAGES['arm_wheezy']
SUPPORTED_IMAGES['aarch64'] = SUPPORTED_IMAGES['aarch64_focal']
SUPPORTED_IMAGES['mips']    = SUPPORTED_IMAGES['mips_wheezy']
SUPPORTED_IMAGES['mipsel']  = SUPPORTED_IMAGES['mipsel_wheezy']
SUPPORTED_IMAGES['mips64']    = SUPPORTED_IMAGES['mips64']

class Qcows():
    '''
    Helper library for managing qcows on your filesystem.
    Given an architecture, it can download a qcow from `panda.mit.edu` to `~/.panda/` and then use that.
    Alternatively, if a path to a qcow is provided, it can just use that.
    A qcow loaded by architecture can then be queried to get the name of the root snapshot or prompt.
    '''

    @staticmethod
    def get_qcow_info(name=None):
        '''
        Get information about supported image as specified by name.

        Args:
            name (str): String idenfifying a qcow supported
                
        Returns:
            Image: Instance of the Image class for a qcow
        '''
        if name is None:
            logger.warning("No qcow name provided. Defaulting to i386")
            name = "i386"

        if path.isfile(name):
            raise RuntimeError("TODO: can't automatically determine system info from custom qcows. Use one of: {}".format(", ".join(SUPPORTED_IMAGES.keys())))

        name = name.lower() # Case insensitive. Assumes supported_arches keys are lowercase
        if name not in SUPPORTED_IMAGES.keys():
            raise RuntimeError("Architecture {} is not in list of supported names: {}".format(name, ", ".join(SUPPORTED_IMAGES.keys())))

        r = SUPPORTED_IMAGES[name]
        # Move properties in .arch to being in the main object
        return r

    @staticmethod
    def get_qcow(name=None):
        '''
        Given a generic name of a qcow in `pandare.qcows.SUPPORTED_IMAGES` or a path to a qcow, return the path. Defaults to i386

        Args:
            name (str): generic name or path to qcow
                
        Returns:
            string: Path to qcow
        '''
        if name is None:
            logger.warning("No qcow name provided. Defaulting to i386")
            name = "i386"

        if path.isfile(name):
            logger.debug("Provided qcow name appears to be a path, returning it directly: %s", name)
            return name

        name = name.lower() # Case insensitive. Assumes supported_images keys are lowercase
        if name not in SUPPORTED_IMAGES.keys():
            raise RuntimeError("Architecture {} is not in list of supported names: {}".format(name, ", ".join(SUPPORTED_IMAGES.keys())))

        image_data = SUPPORTED_IMAGES[name]
        qc = image_data.qcow
        if not qc: # Default, get name from url
            qc = image_data.url.split("/")[-1]
        qcow_path = path.join(VM_DIR,qc)
        makedirs(VM_DIR, exist_ok=True)

        if not path.isfile(qcow_path):
            print("\nQcow {} doesn't exist. Downloading from https://panda-re.mit.edu. Thanks MIT!\n".format(qc))
            try:
                check_call(["wget", "--quiet", image_data.url, "-O", qcow_path])
                for extra_file in image_data.extra_files or []:
                    extra_file_path = path.join(VM_DIR, extra_file)
                    url = image_data.url[:image_data.url.rfind("/")] + "/" + extra_file # Truncate url to last /, then add extra_file
                    check_call(["wget", "--quiet", url, "-O", extra_file_path])
            except Exception as e:
                logger.info("Download failed, deleting partial file(s): %s", qcow_path)
                remove(qcow_path)
                for extra_file in image_data.extra_files or []:
                    try:
                        remove(path.join(VM_DIR, extra_file))
                    except: # Extra files might not exist
                        pass
                raise e # Reraise
            logger.debug("Downloaded %s to %s", qc, qcow_path)
        return qcow_path

    @staticmethod
    def qcow_from_arg(idx=1):
        '''
        Given an index into argv, call get_qcow with that arg if it exists, else with None

        Args:
            idx (int): an index into argv
                
        Returns:
            string: Path to qcow
        '''
        if len(argv) > idx:
            return Qcows.get_qcow(argv[idx])
        else:
            return Qcows.get_qcow()

    @staticmethod
    def cli(target):
        from .panda import Panda
        q = Qcows.get_qcow_info(target)
        qcow = Qcows.get_qcow(target)
        arch = q.arch
        build_dir = Panda._find_build_dir(arch)
        panda_args = [build_dir + f"/{arch}-softmmu/panda-system-{arch}"]
        biospath = path.realpath(path.join(build_dir, "pc-bios"))
        panda_args.extend(["-L", biospath])

        if arch == 'mips64':
            panda_args.extend(["-drive", f"file={qcow},if=virtio"])
        else:
            panda_args.append(qcow)

        panda_args.extend(['-m', q.default_mem])

        if q.extra_args:
            extra_args = shlex_split(q.extra_args)
            for x in extra_args:
                if " " in x:
                    panda_args.append(repr(x))
                else:
                    panda_args.append(x)

        panda_args.extend(['-loadvm', q.snapshot])

        ret = " ".join(panda_args)

        if "-display none" in ret:
            ret = ret.replace("-display none", "-nographic")
        return ret

if __name__ == "__main__":
    from sys import argv, stdout
    valid_names = ", ".join(SUPPORTED_IMAGES.keys())

    if len(argv) != 2 or argv[1] not in SUPPORTED_IMAGES:
        raise ValueError(f"USAGE: {argv[0]}: target_arch where name is one of " + valid_names)

    cmd = Qcows.cli(argv[1])
    if stdout.isatty():
        print(f"\nRun panda for {argv[1]} with:\n{cmd}")
    else:
        print(cmd)

Global variables

var SUPPORTED_IMAGES

Dictionary of Image objects by name. Generic values (underlying OS version may change) include: x86_64 i386 ppc arm aarch64 mips mipsel mips64

You may also specify an exact arch/OS combination from the following exist: x86_64_ubuntu_1804 i386_ubuntu_1604 ppc_wheezy arm_wheezy aarch64 _focal mips_wheezy mips_buildroot5 mipsel_wheezy mipsel_buildroot5 mips64

Classes

class Image (arch=None, os=None, prompt=None, cdrom=None, snapshot=None, url=None, extra_files=None, qcow=None, default_mem=None, extra_args=None)

The Image class stores information about a supported PANDA image

Args

arch : str
Arch for the given architecture.
os : str
an os string we can pass to panda with -os
prompt : regex
a regex to detect a bash prompt after loading the snapshot and sending commands
cdrom : str
name to use for cd-drive when inserting an ISO via monitor
qcow : str
optional name to save qcow as
url : str
url to download the qcow (e.g. https:// website.com/yourqcow.qcow2)
default_mem : str
memory to use for the root snapshot (e.g. 1G)
extra_files : list
other files (assumed to be in same directory on server) that we also need
extra_args : list
Extra arguments to pass to PANDA (e.g. ['-display', 'none'])
Expand source code
class Image(namedtuple('Image', ['arch', 'os', 'prompt', 'cdrom', 'snapshot', 'url', 'extra_files', 'qcow', 'default_mem', 'extra_args'])):
    '''
    The Image class stores information about a supported PANDA image

    Args:
        arch (str): Arch for the given architecture.
        os (str): an os string we can pass to panda with -os
        prompt (regex): a regex to detect a bash prompt after loading the snapshot and sending commands
        cdrom (str): name to use for cd-drive when inserting an ISO via monitor
        qcow (str): optional name to save qcow as
        url (str): url to download the qcow (e.g. https:// website.com/yourqcow.qcow2)
        default_mem (str): memory to use for the root snapshot (e.g. 1G)
        extra_files (list): other files (assumed to be in same directory on server) that we also need
        extra_args (list): Extra arguments to pass to PANDA (e.g. ['-display', 'none'])
    '''

Ancestors

  • builtins.tuple
class Qcows

Helper library for managing qcows on your filesystem. Given an architecture, it can download a qcow from panda.mit.edu to ~/.panda/ and then use that. Alternatively, if a path to a qcow is provided, it can just use that. A qcow loaded by architecture can then be queried to get the name of the root snapshot or prompt.

Expand source code
class Qcows():
    '''
    Helper library for managing qcows on your filesystem.
    Given an architecture, it can download a qcow from `panda.mit.edu` to `~/.panda/` and then use that.
    Alternatively, if a path to a qcow is provided, it can just use that.
    A qcow loaded by architecture can then be queried to get the name of the root snapshot or prompt.
    '''

    @staticmethod
    def get_qcow_info(name=None):
        '''
        Get information about supported image as specified by name.

        Args:
            name (str): String idenfifying a qcow supported
                
        Returns:
            Image: Instance of the Image class for a qcow
        '''
        if name is None:
            logger.warning("No qcow name provided. Defaulting to i386")
            name = "i386"

        if path.isfile(name):
            raise RuntimeError("TODO: can't automatically determine system info from custom qcows. Use one of: {}".format(", ".join(SUPPORTED_IMAGES.keys())))

        name = name.lower() # Case insensitive. Assumes supported_arches keys are lowercase
        if name not in SUPPORTED_IMAGES.keys():
            raise RuntimeError("Architecture {} is not in list of supported names: {}".format(name, ", ".join(SUPPORTED_IMAGES.keys())))

        r = SUPPORTED_IMAGES[name]
        # Move properties in .arch to being in the main object
        return r

    @staticmethod
    def get_qcow(name=None):
        '''
        Given a generic name of a qcow in `pandare.qcows.SUPPORTED_IMAGES` or a path to a qcow, return the path. Defaults to i386

        Args:
            name (str): generic name or path to qcow
                
        Returns:
            string: Path to qcow
        '''
        if name is None:
            logger.warning("No qcow name provided. Defaulting to i386")
            name = "i386"

        if path.isfile(name):
            logger.debug("Provided qcow name appears to be a path, returning it directly: %s", name)
            return name

        name = name.lower() # Case insensitive. Assumes supported_images keys are lowercase
        if name not in SUPPORTED_IMAGES.keys():
            raise RuntimeError("Architecture {} is not in list of supported names: {}".format(name, ", ".join(SUPPORTED_IMAGES.keys())))

        image_data = SUPPORTED_IMAGES[name]
        qc = image_data.qcow
        if not qc: # Default, get name from url
            qc = image_data.url.split("/")[-1]
        qcow_path = path.join(VM_DIR,qc)
        makedirs(VM_DIR, exist_ok=True)

        if not path.isfile(qcow_path):
            print("\nQcow {} doesn't exist. Downloading from https://panda-re.mit.edu. Thanks MIT!\n".format(qc))
            try:
                check_call(["wget", "--quiet", image_data.url, "-O", qcow_path])
                for extra_file in image_data.extra_files or []:
                    extra_file_path = path.join(VM_DIR, extra_file)
                    url = image_data.url[:image_data.url.rfind("/")] + "/" + extra_file # Truncate url to last /, then add extra_file
                    check_call(["wget", "--quiet", url, "-O", extra_file_path])
            except Exception as e:
                logger.info("Download failed, deleting partial file(s): %s", qcow_path)
                remove(qcow_path)
                for extra_file in image_data.extra_files or []:
                    try:
                        remove(path.join(VM_DIR, extra_file))
                    except: # Extra files might not exist
                        pass
                raise e # Reraise
            logger.debug("Downloaded %s to %s", qc, qcow_path)
        return qcow_path

    @staticmethod
    def qcow_from_arg(idx=1):
        '''
        Given an index into argv, call get_qcow with that arg if it exists, else with None

        Args:
            idx (int): an index into argv
                
        Returns:
            string: Path to qcow
        '''
        if len(argv) > idx:
            return Qcows.get_qcow(argv[idx])
        else:
            return Qcows.get_qcow()

    @staticmethod
    def cli(target):
        from .panda import Panda
        q = Qcows.get_qcow_info(target)
        qcow = Qcows.get_qcow(target)
        arch = q.arch
        build_dir = Panda._find_build_dir(arch)
        panda_args = [build_dir + f"/{arch}-softmmu/panda-system-{arch}"]
        biospath = path.realpath(path.join(build_dir, "pc-bios"))
        panda_args.extend(["-L", biospath])

        if arch == 'mips64':
            panda_args.extend(["-drive", f"file={qcow},if=virtio"])
        else:
            panda_args.append(qcow)

        panda_args.extend(['-m', q.default_mem])

        if q.extra_args:
            extra_args = shlex_split(q.extra_args)
            for x in extra_args:
                if " " in x:
                    panda_args.append(repr(x))
                else:
                    panda_args.append(x)

        panda_args.extend(['-loadvm', q.snapshot])

        ret = " ".join(panda_args)

        if "-display none" in ret:
            ret = ret.replace("-display none", "-nographic")
        return ret

Static methods

def cli(target)
Expand source code
@staticmethod
def cli(target):
    from .panda import Panda
    q = Qcows.get_qcow_info(target)
    qcow = Qcows.get_qcow(target)
    arch = q.arch
    build_dir = Panda._find_build_dir(arch)
    panda_args = [build_dir + f"/{arch}-softmmu/panda-system-{arch}"]
    biospath = path.realpath(path.join(build_dir, "pc-bios"))
    panda_args.extend(["-L", biospath])

    if arch == 'mips64':
        panda_args.extend(["-drive", f"file={qcow},if=virtio"])
    else:
        panda_args.append(qcow)

    panda_args.extend(['-m', q.default_mem])

    if q.extra_args:
        extra_args = shlex_split(q.extra_args)
        for x in extra_args:
            if " " in x:
                panda_args.append(repr(x))
            else:
                panda_args.append(x)

    panda_args.extend(['-loadvm', q.snapshot])

    ret = " ".join(panda_args)

    if "-display none" in ret:
        ret = ret.replace("-display none", "-nographic")
    return ret
def get_qcow(name=None)

Given a generic name of a qcow in SUPPORTED_IMAGES or a path to a qcow, return the path. Defaults to i386

Args

name : str
generic name or path to qcow

Returns

string
Path to qcow
Expand source code
@staticmethod
def get_qcow(name=None):
    '''
    Given a generic name of a qcow in `pandare.qcows.SUPPORTED_IMAGES` or a path to a qcow, return the path. Defaults to i386

    Args:
        name (str): generic name or path to qcow
            
    Returns:
        string: Path to qcow
    '''
    if name is None:
        logger.warning("No qcow name provided. Defaulting to i386")
        name = "i386"

    if path.isfile(name):
        logger.debug("Provided qcow name appears to be a path, returning it directly: %s", name)
        return name

    name = name.lower() # Case insensitive. Assumes supported_images keys are lowercase
    if name not in SUPPORTED_IMAGES.keys():
        raise RuntimeError("Architecture {} is not in list of supported names: {}".format(name, ", ".join(SUPPORTED_IMAGES.keys())))

    image_data = SUPPORTED_IMAGES[name]
    qc = image_data.qcow
    if not qc: # Default, get name from url
        qc = image_data.url.split("/")[-1]
    qcow_path = path.join(VM_DIR,qc)
    makedirs(VM_DIR, exist_ok=True)

    if not path.isfile(qcow_path):
        print("\nQcow {} doesn't exist. Downloading from https://panda-re.mit.edu. Thanks MIT!\n".format(qc))
        try:
            check_call(["wget", "--quiet", image_data.url, "-O", qcow_path])
            for extra_file in image_data.extra_files or []:
                extra_file_path = path.join(VM_DIR, extra_file)
                url = image_data.url[:image_data.url.rfind("/")] + "/" + extra_file # Truncate url to last /, then add extra_file
                check_call(["wget", "--quiet", url, "-O", extra_file_path])
        except Exception as e:
            logger.info("Download failed, deleting partial file(s): %s", qcow_path)
            remove(qcow_path)
            for extra_file in image_data.extra_files or []:
                try:
                    remove(path.join(VM_DIR, extra_file))
                except: # Extra files might not exist
                    pass
            raise e # Reraise
        logger.debug("Downloaded %s to %s", qc, qcow_path)
    return qcow_path
def get_qcow_info(name=None)

Get information about supported image as specified by name.

Args

name : str
String idenfifying a qcow supported

Returns

Image
Instance of the Image class for a qcow
Expand source code
@staticmethod
def get_qcow_info(name=None):
    '''
    Get information about supported image as specified by name.

    Args:
        name (str): String idenfifying a qcow supported
            
    Returns:
        Image: Instance of the Image class for a qcow
    '''
    if name is None:
        logger.warning("No qcow name provided. Defaulting to i386")
        name = "i386"

    if path.isfile(name):
        raise RuntimeError("TODO: can't automatically determine system info from custom qcows. Use one of: {}".format(", ".join(SUPPORTED_IMAGES.keys())))

    name = name.lower() # Case insensitive. Assumes supported_arches keys are lowercase
    if name not in SUPPORTED_IMAGES.keys():
        raise RuntimeError("Architecture {} is not in list of supported names: {}".format(name, ", ".join(SUPPORTED_IMAGES.keys())))

    r = SUPPORTED_IMAGES[name]
    # Move properties in .arch to being in the main object
    return r
def qcow_from_arg(idx=1)

Given an index into argv, call get_qcow with that arg if it exists, else with None

Args

idx : int
an index into argv

Returns

string
Path to qcow
Expand source code
@staticmethod
def qcow_from_arg(idx=1):
    '''
    Given an index into argv, call get_qcow with that arg if it exists, else with None

    Args:
        idx (int): an index into argv
            
    Returns:
        string: Path to qcow
    '''
    if len(argv) > idx:
        return Qcows.get_qcow(argv[idx])
    else:
        return Qcows.get_qcow()