summaryrefslogtreecommitdiffstats
path: root/scripts/qemucommand.py
blob: 9b23c54d3a7695e181e1bbe20ebed08d1f3cf290 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
from os.path import exists, isdir, join, realpath, abspath
from os import listdir
import random
import socket
from shutil import copyfile
from subprocess import check_output

EXTENSIONS = {
    'intel-corei7-64': 'wic',
    'qemux86-64': 'ota-ext4'
}


def find_local_port(start_port):
    """"
    Find the next free TCP port after 'start_port'.
    """

    for port in range(start_port, start_port + 10):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.bind(('', port))
            return port
        except socket.error:
            print("Skipping port %d" % port)
        finally:
            s.close()
    raise Exception("Could not find a free TCP port")


def random_mac():
    """Return a random Ethernet MAC address
    @link https://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml#ethernet-numbers-2
    """
    head = "ca:fe:"
    hex_digits = '0123456789abcdef'
    tail = ':'.join([random.choice(hex_digits) + random.choice(hex_digits) for _ in range(4)])
    return head + tail


class QemuCommand(object):
    def __init__(self, args):
        self.dry_run = args.dry_run
        self.overlay = args.overlay
        if args.machine:
            self.machine = args.machine
        else:
            if not isdir(args.dir):
                raise ValueError("Directory %s does not exist, please specify a --machine or a valid images directory" % args.dir)
            machines = listdir(args.dir)
            if len(machines) == 1:
                self.machine = machines[0]
            else:
                raise ValueError("Could not autodetect machine type. More than one entry in %s. Maybe --machine qemux86-64?" % args.dir)

        # If using an overlay with U-Boot, copy the rom when we create the
        # overlay so that we can keep it around just in case.
        if args.efi:
            self.bios = 'OVMF.fd'
        else:
            uboot_path = abspath(join(args.dir, self.machine, 'u-boot-qemux86-64.rom'))
            if self.overlay:
                new_uboot_path = self.overlay + '.u-boot.rom'
                if not exists(self.overlay):
                    if not exists(uboot_path):
                        raise ValueError("U-Boot image %s does not exist" % uboot_path)
                    if not exists(new_uboot_path):
                        if self.dry_run:
                            print("cp %s %s" % (uboot_path, new_uboot_path))
                        else:
                            copyfile(uboot_path, new_uboot_path)
                uboot_path = new_uboot_path
            if not exists(uboot_path) and not (self.dry_run and not exists(self.overlay)):
                raise ValueError("U-Boot image %s does not exist" % uboot_path)
            self.bios = uboot_path

        # If using an overlay, we need to keep the "backing" image around, as
        # bitbake will often clean it up, and the overlay silently depends on
        # the hardcoded path. The easiest solution is to keep the file and use
        # a relative path to it.
        if exists(args.imagename):
            image = realpath(args.imagename)
        else:
            ext = EXTENSIONS.get(self.machine, 'wic')
            image = join(args.dir, self.machine, '%s-%s.%s' % (args.imagename, self.machine, ext))
        if self.overlay:
            new_image_path = self.overlay + '.img'
            if not exists(self.overlay):
                if not exists(image):
                    raise ValueError("OS image %s does not exist" % image)
                if not exists(new_image_path):
                    if self.dry_run:
                        print("cp %s %s" % (image, new_image_path))
                    else:
                        copyfile(image, new_image_path)
            self.image = new_image_path
        else:
            self.image = realpath(image)
        if not exists(self.image) and not (self.dry_run and not exists(self.overlay)):
            raise ValueError("OS image %s does not exist" % self.image)

        if args.mac:
            self.mac_address = args.mac
        else:
            self.mac_address = random_mac()
        self.serial_port = find_local_port(8990)
        self.ssh_port = find_local_port(2222)
        if args.mem:
            self.mem = args.mem
        else:
            self.mem = "1G"
        if args.kvm is None:
            # Autodetect KVM using 'kvm-ok'
            try:
                check_output(['kvm-ok'])
                self.kvm = True
            except Exception:
                self.kvm = False
        else:
            self.kvm = args.kvm
        self.gui = not args.no_gui
        self.gdb = args.gdb
        self.pcap = args.pcap
        self.secondary_network = args.secondary_network

    def command_line(self):
        netuser = 'user,hostfwd=tcp:0.0.0.0:%d-:22,restrict=off' % self.ssh_port
        if self.gdb:
            netuser += ',hostfwd=tcp:0.0.0.0:2159-:2159'
        cmdline = [
            "qemu-system-x86_64",
            "-bios", self.bios
        ]
        if not self.overlay:
            cmdline += ["-drive", "file=%s,if=ide,format=raw,snapshot=on" % self.image]
        cmdline += [
            "-serial", "tcp:127.0.0.1:%d,server,nowait" % self.serial_port,
            "-m", self.mem,
            "-object", "rng-random,id=rng0,filename=/dev/urandom",
            "-device", "virtio-rng-pci,rng=rng0",
            "-net", netuser,
            "-net", "nic,macaddr=%s" % self.mac_address
        ]
        if self.pcap:
            cmdline += ['-net', 'dump,file=' + self.pcap]
        if self.secondary_network:
            cmdline += [
                '-netdev', 'socket,id=vlan1,mcast=230.0.0.1:1234,localaddr=127.0.0.1',
                '-device', 'e1000,netdev=vlan1,mac='+random_mac(),
            ]
        if self.gui:
            cmdline += [
                    "-usb",
                    "-device", "usb-tablet",
                    "-show-cursor",
                    "-vga", "std"
            ]
        else:
            cmdline += [
                    "-nographic",
                    "-monitor", "null",
            ]
        if self.kvm:
            cmdline += ['-enable-kvm', '-cpu', 'host']
        else:
            cmdline += ['-cpu', 'Haswell']
        if self.overlay:
            cmdline.append(self.overlay)
        return cmdline

    def img_command_line(self):
        cmdline = [
            "qemu-img", "create",
            "-o", "backing_file=%s" % self.image,
            "-f", "qcow2",
            self.overlay]
        return cmdline