Example #1
0
    def _qemu_preexec_nw(self):
        # This is called by subprocess.Popen after spawning to run
        # qemu from tt_qemu.power_on_do() right before spawning Qemu
        # for us.
        #
        # See doc block on top of this class and on :class:`vlan_pci`
        # for why file descriptor 0.
        #
        # We will find out which is the index of the TAP device
        # assigned to this, created on _image_power_on_pre and open
        # file desctriptor 0 to it, then leave it open for Qemu to
        # tap into it.
        count = 0
        for ic_name, ic_kws in self.tags.get('interconnects', {}).iteritems():
            if not 'ipv4_addr' in ic_kws and not 'ipv6_addr' in ic_kws:
                continue
            if count > 0:
                raise NotImplementedError(
                    "QEMU Networking cannot implement "
                    "multiple networks at this time "
                    "(when trying to connec to %s)" % ic_name)
            kws = dict(ic_kws)
            kws.update(self.kws)
            kws['ic_name'] = ic_name
            if not commonl.if_present("_b%s" % ic_name):
                self.log.warning("network %s powered off? networking "
                                 "disabled" % ic_name)
                # If the network is not powered up, skip it
                # FIXME: replace with it calling vlan_pci.something()
                # that brings up the basic interface (_bICNAME) so
                # that once we power the network, it works
                continue

            tapindex = commonl.if_index("t%(ic_name)s%(id)s" % kws)
            assign = 0
            # Need to wait for udev to reconfigure this for us to have
            # access; this is done by a udev rule installed
            # (/usr/lib/udev/rules.d/80-ttbd.rules)
            tapdevname = "/dev/tap%d" % tapindex
            count = 1
            top = 4
            while not os.access(tapdevname, os.R_OK | os.W_OK):
                if count >= top:
                    msg = "%s: timed out waiting for udev to set " \
                        "permissions in /usr/lib/udev/rules.d/80-ttbd.rules" \
                        % tapdevname
                    self.log.error(msg)
                    raise RuntimeError(msg)
                time.sleep(0.25)
                count += 1
            fd = os.open("/dev/tap%d" % tapindex, os.O_RDWR, 0)
            if fd != assign:
                # there, reassign it to fd @assign
                os.dup2(fd, assign)
                os.close(fd)
                # leave fd assign open for QEMU!
            count += 1
Example #2
0
    def _qemu_preexec_nw(self):
        # This is called by subprocess.Popen after spawning to run
        # qemu from tt_qemu.power_on_do() right before spawning Qemu
        # for us.
        #
        # See doc block on top of this class and on :class:`vlan_pci`
        # for why file descriptor 0.
        #
        # We will find out which is the index of the TAP device
        # assigned to this, created on _image_power_on_pre and open
        # file desctriptor 0 to it, then leave it open for Qemu to
        # tap into it.
        count = 0
        for ic_name, ic_kws in list(self.tags.get('interconnects', {}).items()):
            if not 'ipv4_addr' in ic_kws and not 'ipv6_addr' in ic_kws:
                continue
            if count > 0:
                raise NotImplementedError(
                    "QEMU Networking cannot implement "
                    "multiple networks at this time "
                    "(when trying to connec to %s)" % ic_name)
            kws = dict(ic_kws)
            kws.update(self.kws)
            kws['ic_name'] = ic_name
            if not commonl.if_present("_b%s" % ic_name):
                self.log.warning("network %s powered off? networking "
                                 "disabled" % ic_name)
                # If the network is not powered up, skip it
                # FIXME: replace with it calling vlan_pci.something()
                # that brings up the basic interface (_bICNAME) so
                # that once we power the network, it works
                continue

            tapindex = commonl.if_index("t%(ic_name)s%(id)s" % kws)
            assign = 0
            # Need to wait for udev to reconfigure this for us to have
            # access; this is done by a udev rule installed
            # (/usr/lib/udev/rules.d/80-ttbd.rules)
            tapdevname = "/dev/tap%d" % tapindex
            count = 1
            top = 4
            while not os.access(tapdevname, os.R_OK | os.W_OK):
                if count >= top:
                    msg = "%s: timed out waiting for udev to set " \
                        "permissions in /usr/lib/udev/rules.d/80-ttbd.rules" \
                        % tapdevname
                    self.log.error(msg)
                    raise RuntimeError(msg)
                time.sleep(0.25)
                count += 1
            fd = os.open("/dev/tap%d" % tapindex, os.O_RDWR, 0)
            if fd != assign:
                # there, reassign it to fd @assign
                os.dup2(fd, assign)
                os.close(fd)
                # leave fd assign open for QEMU!
            count += 1
Example #3
0
    def on(self, target, component):
        # Start QEMU
        #
        # We first assign a port for GDB debugging, if someone takes
        # it before we do, start will fail and paranoid mode (see
        # above) will restart it.
        base = ttbl.config.tcp_port_range[0]
        top = ttbl.config.tcp_port_range[1]
        if base < 5900:  # we need ports >= 5900 for VNC
            base = 5900
        tcp_port_base = commonl.tcp_port_assigner(2, port_range=(base, top))
        target.fsdb.set("qemu-gdb-tcp-port", "%s" % tcp_port_base)
        # This might not be used at all, but we allocate and declare
        # it in case the implementation will use it; allocating in a
        # higher layer makes it more complicated.
        # this one is the port number based on 5900 (VNC default 0)
        if top < 5900:
            logging.warning(
                "ttbl.config.tcp_port_range %s doesn't include ports "
                "between above 5900, needed for VNC services. "
                "QEMU targets needing VNC support will fail to start "
                "complaining about 'vnc-port' not defined",
                ttbl.config.tcp_port_range)
        else:
            # set this for general information; the VNC screenshotter
            # also uses it
            target.fsdb.set("vnc-host", "localhost")
            target.fsdb.set("vnc-port", "%s" % (tcp_port_base + 1 - 5900))
            # this one is the raw port number
            target.fsdb.set("vnc-tcp-port", "%s" % (tcp_port_base + 1))

        self.cmdline_extra = []
        image_keys = target.fsdb.keys("qemu-image-*")
        #
        # Images interface: flash() below has been used to set images
        # to run, this will feed them into QEMU
        #
        if 'qemu-image-bios' in image_keys:
            self.cmdline_extra += ["-bios", "%(qemu-image-bios)s"]
        if 'qemu-image-kernel' in image_keys:
            self.cmdline_extra += ["-kernel", "%(qemu-image-kernel)s"]
        if 'qemu-image-kernel-args' in image_keys:
            self.cmdline_extra += ["-append", "%(qemu-image-kernel-args)s"]
        if 'qemu-image-initrd' in image_keys:
            self.cmdline_extra += ["-initrd", "%(qemu-image-initrd)s"]

        #
        # Network support bits--add command line options to connect
        # this VM to the networks the target declares
        #
        # For each network we are connected to, there must be a
        # network_tap_pc power rail controller that sets up the
        # network devices for us before starting QEMU.
        #
        # We look at it to setup the QEMU side of things as extra
        # command line options.
        #
        # - network name/icname: comes from the tuntap-ICNAME property
        #   created by the network_tap_pc power rail controller that
        #   the configuration must put on before the qemu power rail
        #   controller.
        #
        # - model: comes from what HW it has to emulate; each arch has a
        #   default or you can set for all with property qemu-model or
        #   qemu-model-ICNAME (for that specifc interconnect).
        #
        #   It has to be a valid QEMU model or QEMU will reject it
        #   with an error. You can find valid models with::
        #
        #     qemu-system-ARCH -net nic,model=help
        #
        # Zephyr, e.g., supports a few: lan9118 stellaris e1000 (from)::
        #
        #   $ grep --color -nH --null -rie ETH_NIC_MODEL
        #   drivers/ethernet/Kconfig.smsc911x:13:config ETH_NIC_MODEL$
        #   drivers/ethernet/Kconfig.stellaris:15:config ETH_NIC_MODEL$
        #   drivers/ethernet/Kconfig.e1000:16:config ETH_NIC_MODEL$
        fds = []
        try:
            for tap, if_name in target.fsdb.get_as_dict(
                    "tuntap-*").iteritems():
                # @tap is tuntap-NETWORK, the property set by
                # powe rail component network_tap_pc.
                _, ic_name = tap.split("-", 1)
                mac_addr = target.tags['interconnects'][ic_name]['mac_addr']
                model = commonl.name_make_safe(
                    target.fsdb.get(
                        "qemu-model-" + ic_name,
                        target.fsdb.get("qemu-model", self.nic_model)))

                # There is no way to cahole QEMU to say: hey, we
                # already have a tap interface configured for you, use
                # it...other than to open it and pass the file
                # descriptor to it.
                # So this finds the /dev/tap* file which matches the
                # interface created with tuntap- (class
                # network_tap_pc), opens it and passes the descriptor
                # along to qemu, then closes the FD, since we don't
                # need it.
                tapindex = commonl.if_index(if_name)
                # Need to wait for udev to reconfigure this for us to have
                # access; this is done by a udev rule installed
                # (/usr/lib/udev/rules.d/80-ttbd.rules)
                tapdevname = "/dev/tap%d" % tapindex
                ts0 = time.time()
                ts = ts0
                timeout = 5
                fd = None
                while ts - ts0 < timeout:
                    try:
                        fd = os.open(tapdevname, os.O_RDWR, 0)
                        fds.append(fd)
                        break
                    except OSError as e:
                        target.log.error(
                            "%s: couldn't open %s after"
                            " +%.1f/%.1fs, retrying: %s", ic_name, tapdevname,
                            ts - ts0, timeout, e)
                        time.sleep(0.25)
                        ts = time.time()
                else:
                    raise RuntimeError(
                        "%s: timedout (%.1fs) opening /dev/tap%d's;"
                        " udev's /usr/lib/udev/rules.d/80-ttbd.rules"
                        " didn't set permissions?" %
                        (ic_name, timeout, tapindex))

                self.cmdline_extra += [
                    "-nic",
                    "tap,fd=%d,id=%s,model=%s,mac=%s,br=_b%s" \
                    % (fd, ic_name, model, mac_addr, ic_name )
                ]

            if not fds:
                # No tuntap network attachments -> no network
                # support. Cap it.
                self.cmdline_extra += ['-nic', 'none']

            #
            # Ok, so do actually start
            #
            # if the debug port is snatched before we start by someone
            # else, this will fail and we'll have to retry--this is
            # why the power component is set to paranoid mode, so the
            # power interface automatically retries it if it fails.
            ttbl.power.daemon_c.on(self, target, component)

            # Those consoles? update their generational counts so the
            # clients can know they restarted
            if hasattr(target, "console"):
                for console in target.console.impls.keys():
                    ttbl.console.generation_set(target, console)

        finally:
            for fd in fds:  # close fds we opened for ...
                os.close(fd)  # ... networking, unneeded now
        #
        # Debugging interface--if the target is not in debugging mode,
        # tell QEMU to start right away
        #
        if target.fsdb.get("debug") == None:
            self.debug_resume(target, component)