예제 #1
0
    def test(self):
        """Dumb test for privops.utils.storeState.

    This test is pretty dumb. There are no branches or anything in
    storeState, and if the code doesn't throw exceptions, it's roughly
    guaranteed to work."""
        self.networks = [('debmarshal-0', 500, '10.100.1.1')]

        lock_file = self.mox.CreateMock(file)
        self.mox.StubOutWithMock(debmarshal.utils, 'acquireLock')
        debmarshal.utils.acquireLock('debmarshal-networks', fcntl.LOCK_EX).\
            AndReturn(lock_file)

        self.open = self.mox.CreateMockAnything()
        utils.open = self.open

        net_file = self.mox.CreateMock(file)
        self.open('/var/run/debmarshal-networks', 'w').AndReturn(net_file)
        pickle.dump(self.networks, net_file)

        self.mox.ReplayAll()

        utils.storeState(self.networks, 'debmarshal-networks')

        self.mox.VerifyAll()

        del utils.open
예제 #2
0
    def destroyNetwork(self, name, _debmarshal_sender=None):
        """Destroy a debmarshal network.

    destroyNetwork uses the state recorded by createNetwork to verify
    that the user who created a network is the only one who can
    destroy it (except for root).

    Args:
      name: The name of the network returned from createNetwork

    Raises:
      debmarshal.errors.NetworkNotFound: The specified network name
        does not exist.
      debmarshal.errors.AccessDenied: The specified network is not
        owned by the user calling destroyNetwork.
    """
        utils.caller = _debmarshal_sender

        virt_con = libvirt.open('qemu:///system')

        nets = networks.loadNetworkState(virt_con)
        if name not in nets:
            raise errors.NetworkNotFound("Network %s does not exist." % name)

        if utils.getCaller() not in (0, nets[name]):
            raise errors.AccessDenied("Network %s not owned by UID %d." %
                                      (name, utils.getCaller()))

        virt_con.networkLookupByName(name).destroy()
        virt_con.networkLookupByName(name).undefine()

        del nets[name]
        utils.storeState(nets, 'debmarshal-networks')

        utils.caller = None
예제 #3
0
  def destroyDomain(self, name, hypervisor, _debmarshal_sender=None):
    """Destroy a debmarshal domain.

    destroyDomain uses the state recorded by createDomain to verify
    ownership of the domain.

    Domains can be destroyed by the user that created them, or by
    root.

    Args:
      name: The name of the domain to destroy.
      hypervisor: The hypervisor for this domain.
    """
    utils.caller = _debmarshal_sender

    hyper_class = hypervisors.base.hypervisors[hypervisor]
    virt_con = hyper_class.open()

    doms = domains.loadDomainState()
    if (name, hypervisor) not in doms:
      raise errors.DomainNotFound("Domain %s does not exist." % name)

    if utils.getCaller() not in (0, doms[(name, hypervisor)]):
      raise errors.AccessDenied("Domain %s is not owned by UID %d." %
                                (name, utils.getCaller()))

    virt_dom = virt_con.lookupByName(name)
    virt_dom.destroy()

    del doms[(name, hypervisor)]
    utils.storeState(doms, 'debmarshal-domains')

    utils.caller = None
예제 #4
0
  def destroyNetwork(self, name, _debmarshal_sender=None):
    """Destroy a debmarshal network.

    destroyNetwork uses the state recorded by createNetwork to verify
    that the user who created a network is the only one who can
    destroy it (except for root).

    Args:
      name: The name of the network returned from createNetwork

    Raises:
      debmarshal.errors.NetworkNotFound: The specified network name
        does not exist.
      debmarshal.errors.AccessDenied: The specified network is not
        owned by the user calling destroyNetwork.
    """
    utils.caller = _debmarshal_sender

    virt_con = libvirt.open('qemu:///system')

    nets = networks.loadNetworkState(virt_con)
    if name not in nets:
      raise errors.NetworkNotFound("Network %s does not exist." % name)

    if utils.getCaller() not in (0, nets[name]):
      raise errors.AccessDenied("Network %s not owned by UID %d." % (name, utils.getCaller()))

    virt_con.networkLookupByName(name).destroy()
    virt_con.networkLookupByName(name).undefine()

    del nets[name]
    utils.storeState(nets, 'debmarshal-networks')

    utils.caller = None
예제 #5
0
    def destroyDomain(self, name, hypervisor, _debmarshal_sender=None):
        """Destroy a debmarshal domain.

    destroyDomain uses the state recorded by createDomain to verify
    ownership of the domain.

    Domains can be destroyed by the user that created them, or by
    root.

    Args:
      name: The name of the domain to destroy.
      hypervisor: The hypervisor for this domain.
    """
        utils.caller = _debmarshal_sender

        hyper_class = hypervisors.base.hypervisors[hypervisor]
        virt_con = hyper_class.open()

        doms = domains.loadDomainState()
        if (name, hypervisor) not in doms:
            raise errors.DomainNotFound("Domain %s does not exist." % name)

        if utils.getCaller() not in (0, doms[(name, hypervisor)]):
            raise errors.AccessDenied("Domain %s is not owned by UID %d." %
                                      (name, utils.getCaller()))

        virt_dom = virt_con.lookupByName(name)
        virt_dom.destroy()

        del doms[(name, hypervisor)]
        utils.storeState(doms, 'debmarshal-domains')

        utils.caller = None
예제 #6
0
  def test(self):
    """Dumb test for privops.utils.storeState.

    This test is pretty dumb. There are no branches or anything in
    storeState, and if the code doesn't throw exceptions, it's roughly
    guaranteed to work."""
    self.networks = [('debmarshal-0', 500, '10.100.1.1')]

    lock_file = self.mox.CreateMock(file)
    self.mox.StubOutWithMock(debmarshal.utils, 'acquireLock')
    debmarshal.utils.acquireLock('debmarshal-networks', fcntl.LOCK_EX).\
        AndReturn(lock_file)

    self.open = self.mox.CreateMockAnything()
    utils.open = self.open

    net_file = self.mox.CreateMock(file)
    self.open('/var/run/debmarshal-networks', 'w').AndReturn(net_file)
    pickle.dump(self.networks, net_file)

    self.mox.ReplayAll()

    utils.storeState(self.networks, 'debmarshal-networks')

    self.mox.VerifyAll()

    del utils.open
예제 #7
0
  def testStoreSuccess(self):
    """Test createNetwork when everything goes right"""
    self.mox.StubOutWithMock(utils, 'storeState')
    self.networks[self.name] = 1000
    utils.storeState(self.networks, 'debmarshal-networks')

    self.mox.ReplayAll()

    self.assertEqual(privops.Privops().createNetwork(self.hosts),
                     (self.name, self.gateway, '255.255.255.0', self.host_dict))
예제 #8
0
    def testStoreSuccess(self):
        """Test createNetwork when everything goes right"""
        self.mox.StubOutWithMock(utils, 'storeState')
        self.networks[self.name] = 1000
        utils.storeState(self.networks, 'debmarshal-networks')

        self.mox.ReplayAll()

        self.assertEqual(
            privops.Privops().createNetwork(self.hosts),
            (self.name, self.gateway, '255.255.255.0', self.host_dict))
예제 #9
0
    def test(self):
        """Test privops.domains.createNetwork.

    With all of the functionality pulled into helper functions,
    createNetwork doesn't actually do all that much work.
    """
        name = 'debmarshal-12'
        memory = '128M'
        disks = ['/home/ebroder/root.img']
        net = 'debmarshal-0'
        mac = '00:11:22:33:44:55'

        self.mox.StubOutWithMock(utils, 'getCaller')
        utils.getCaller().MultipleTimes().AndReturn(500)
        self.mox.StubOutWithMock(debmarshal.utils, 'acquireLock')
        debmarshal.utils.acquireLock('debmarshal-domlist', fcntl.LOCK_EX)

        self.mox.StubOutWithMock(hypervisors.qemu.QEMU, 'open')
        qemu_con = self.mox.CreateMock(libvirt.virConnect)
        hypervisors.qemu.QEMU.open().AndReturn(qemu_con)

        self.mox.StubOutWithMock(domains, '_validateNetwork')
        domains._validateNetwork(net, qemu_con)

        self.mox.StubOutWithMock(domains, '_validatePath')
        for d in disks:
            domains._validatePath(d, os.R_OK | os.W_OK)

        self.mox.StubOutWithMock(domains, '_findUnusedName')
        domains._findUnusedName(qemu_con).AndReturn(name)

        self.mox.StubOutWithMock(hypervisors.qemu.QEMU, 'domainXMLString')
        hypervisors.qemu.QEMU.domainXMLString(
            mox.IgnoreArg()).AndReturn('<fake_xml/>')

        self.mox.StubOutWithMock(domains, 'loadDomainState')
        domains.loadDomainState().AndReturn({('debmarshal-1', 'qemu'): 500})

        self.mox.StubOutWithMock(utils, 'storeState')
        utils.storeState({
            ('debmarshal-1', 'qemu'): 500,
            (name, 'qemu'): 500
        }, 'debmarshal-domains')

        qemu_con.createLinux('<fake_xml/>', 0)

        self.mox.ReplayAll()

        self.assertEqual(
            privops.Privops().createDomain(memory, disks, net, mac, 'qemu',
                                           'x86_64', {}), name)
예제 #10
0
  def test(self):
    """Test privops.domains.createNetwork.

    With all of the functionality pulled into helper functions,
    createNetwork doesn't actually do all that much work.
    """
    name = 'debmarshal-12'
    memory = '128M'
    disks = ['/home/ebroder/root.img']
    net = 'debmarshal-0'
    mac = '00:11:22:33:44:55'

    self.mox.StubOutWithMock(utils, 'getCaller')
    utils.getCaller().MultipleTimes().AndReturn(500)
    self.mox.StubOutWithMock(debmarshal.utils, 'acquireLock')
    debmarshal.utils.acquireLock('debmarshal-domlist', fcntl.LOCK_EX)

    self.mox.StubOutWithMock(hypervisors.qemu.QEMU, 'open')
    qemu_con = self.mox.CreateMock(libvirt.virConnect)
    hypervisors.qemu.QEMU.open().AndReturn(qemu_con)

    self.mox.StubOutWithMock(domains, '_validateNetwork')
    domains._validateNetwork(net, qemu_con)

    self.mox.StubOutWithMock(domains, '_validatePath')
    for d in disks:
      domains._validatePath(d, os.R_OK | os.W_OK)

    self.mox.StubOutWithMock(domains, '_findUnusedName')
    domains._findUnusedName(qemu_con).AndReturn(name)

    self.mox.StubOutWithMock(hypervisors.qemu.QEMU, 'domainXMLString')
    hypervisors.qemu.QEMU.domainXMLString(mox.IgnoreArg()).AndReturn(
      '<fake_xml/>')

    self.mox.StubOutWithMock(domains, 'loadDomainState')
    domains.loadDomainState().AndReturn({
        ('debmarshal-1', 'qemu'): 500})

    self.mox.StubOutWithMock(utils, 'storeState')
    utils.storeState({
      ('debmarshal-1', 'qemu'): 500,
      (name, 'qemu'): 500}, 'debmarshal-domains')

    qemu_con.createLinux('<fake_xml/>', 0)

    self.mox.ReplayAll()

    self.assertEqual(privops.Privops().createDomain(
      memory, disks, net, mac, 'qemu', 'x86_64', {}), name)
예제 #11
0
  def testSuccess(self):
    """Test that destroyDomain can succeed."""
    self.mox.StubOutWithMock(utils, 'getCaller')
    utils.getCaller().MultipleTimes().AndReturn(500)

    virt_dom = self.mox.CreateMock(libvirt.virDomain)
    self.virt_con.lookupByName('debmarshal-0').AndReturn(virt_dom)
    virt_dom.destroy()

    self.mox.StubOutWithMock(utils, 'storeState')
    del self.domains[('debmarshal-0', 'qemu')]
    utils.storeState(self.domains, 'debmarshal-domains')

    self.mox.ReplayAll()

    privops.Privops().destroyDomain('debmarshal-0', 'qemu')
예제 #12
0
    def testSuccess(self):
        """Test that destroyDomain can succeed."""
        self.mox.StubOutWithMock(utils, 'getCaller')
        utils.getCaller().MultipleTimes().AndReturn(500)

        virt_dom = self.mox.CreateMock(libvirt.virDomain)
        self.virt_con.lookupByName('debmarshal-0').AndReturn(virt_dom)
        virt_dom.destroy()

        self.mox.StubOutWithMock(utils, 'storeState')
        del self.domains[('debmarshal-0', 'qemu')]
        utils.storeState(self.domains, 'debmarshal-domains')

        self.mox.ReplayAll()

        privops.Privops().destroyDomain('debmarshal-0', 'qemu')
예제 #13
0
  def testStoreFailure(self):
    """Test that the network is destroyed if state about it can't be
    stored"""
    self.mox.StubOutWithMock(utils, 'storeState')
    self.networks[self.name] = 1000
    utils.storeState(self.networks, 'debmarshal-networks').\
                     AndRaise(Exception("Error!"))

    self.virt_con.networkLookupByName(self.name).MultipleTimes().AndReturn(
        self.virt_net)
    self.virt_net.destroy()
    self.virt_net.undefine()

    self.mox.ReplayAll()

    self.assertRaises(Exception, privops.Privops().createNetwork,
                      self.hosts)
예제 #14
0
    def testStoreFailure(self):
        """Test that the network is destroyed if state about it can't be
    stored"""
        self.mox.StubOutWithMock(utils, 'storeState')
        self.networks[self.name] = 1000
        utils.storeState(self.networks, 'debmarshal-networks').\
                         AndRaise(Exception("Error!"))

        self.virt_con.networkLookupByName(self.name).MultipleTimes().AndReturn(
            self.virt_net)
        self.virt_net.destroy()
        self.virt_net.undefine()

        self.mox.ReplayAll()

        self.assertRaises(Exception,
                          privops.Privops().createNetwork, self.hosts)
예제 #15
0
    def testSuccess(self):
        """Test that destroyNetwork will actually delete an existing
    network owned by the right user."""
        self.mox.StubOutWithMock(utils, 'getCaller')
        utils.getCaller().MultipleTimes().AndReturn(501)

        virt_net = self.mox.CreateMock(libvirt.virNetwork)
        self.virt_con.networkLookupByName(
            'debmarshal-0').MultipleTimes().AndReturn(virt_net)
        virt_net.destroy()
        virt_net.undefine()

        self.mox.StubOutWithMock(utils, 'storeState')
        del self.networks['debmarshal-0']
        utils.storeState(self.networks, 'debmarshal-networks')

        self.mox.ReplayAll()

        privops.Privops().destroyNetwork('debmarshal-0')
예제 #16
0
  def testSuccess(self):
    """Test that destroyNetwork will actually delete an existing
    network owned by the right user."""
    self.mox.StubOutWithMock(utils, 'getCaller')
    utils.getCaller().MultipleTimes().AndReturn(501)

    virt_net = self.mox.CreateMock(libvirt.virNetwork)
    self.virt_con.networkLookupByName('debmarshal-0').MultipleTimes().AndReturn(
        virt_net)
    virt_net.destroy()
    virt_net.undefine()

    self.mox.StubOutWithMock(utils, 'storeState')
    del self.networks['debmarshal-0']
    utils.storeState(self.networks, 'debmarshal-networks')

    self.mox.ReplayAll()

    privops.Privops().destroyNetwork('debmarshal-0')
예제 #17
0
    def createNetwork(self, hosts, _debmarshal_sender=None):
        """All of the networking config you need for a debmarshal test rig.

    createNetwork creates an isolated virtual network within
    libvirt. It picks an IP address space that is as-yet unused
    (within debmarshal), and assigns that to the network. It then
    allocates IP addresses and MAC addresses for each of the hostnames
    listed in hosts.

    createNetwork tracks which users created which networks, and
    debmarshal will only allow the user that created a network to
    attach VMs to it or destroy it.

    Args:
      hosts: A list of hostnames that will eventually be attached to
        this network

    Returns:
      A 4-tuple containing:
        Network name: This is used to reference the newly created
          network in the future. It is unique across the local
          workstation
        Gateway: The network address. Also the DNS server, if that
          information isn't being grabbed over DHCP
        Netmask: The netmask for the network
        VMs: A dict mapping hostnames in hosts to (IP address, MAC
          address), as assigned by createNetwork
    """
        utils.caller = _debmarshal_sender

        # First, input validation. Everything in hosts should be a valid
        # hostname
        for h in hosts:
            networks._validateHostname(h)

        # We don't really care which particular libvirt driver we connect
        # to, because they all share the same networking
        # config. libvirt.open() is supposed to take None to indicate a
        # default, but it doesn't seem to work, so we pass in what's
        # supposed to be the default for root.
        virt_con = libvirt.open('qemu:///system')

        net_name = networks._findUnusedName()
        net_gateway, net_mask = networks._findUnusedNetwork(len(hosts))

        net_hosts = {}
        host_addr = ip.IP(net_gateway) + 1
        for host in hosts:
            # Use the virtinst package's MAC address generator because it's
            # easier than writing another one for ourselves.
            #
            # This does mean that the MAC addresses are allocated from
            # Xensource's OUI, but whatever
            mac = virtinst.util.randomMAC()
            net_hosts[host] = (host_addr.ip_ext, mac)
            host_addr += 1

        xml = networks._genNetworkXML(net_name, net_gateway, net_mask,
                                      net_hosts)
        virt_net = virt_con.networkDefineXML(xml)
        virt_net.create()

        try:
            nets = networks.loadNetworkState(virt_con)
            nets[net_name] = utils.getCaller()
            utils.storeState(nets, 'debmarshal-networks')
        except:
            virt_con.networkLookupByName(net_name).destroy()
            virt_con.networkLookupByName(net_name).undefine()
            raise

        utils.caller = None

        return (net_name, net_gateway, net_mask, net_hosts)
예제 #18
0
    def createDomain(self,
                     memory,
                     disks,
                     network,
                     mac,
                     hypervisor,
                     arch,
                     extra,
                     _debmarshal_sender=None):
        """Create a virtual machine domain.

    createDomain creates a domain for a virtual machine used as part
    of a debmarshal test and boots it up.

    We do no validation or accounting of memory allocations from the
    privileged side. Since debmarshal is intended to be run on
    single-user machines, the worst case scenario is a DoS of
    yourself.

    When supported by the hypervisor, createDomain can also pass in a
    hypervisor, initrd, and command line, bypassing the BIOS.

    Args:
      memory: str containing the amount of memory to be allocated to
        the new domain. This should include a suffix such as 'G' or
        'M' to indicate the units of the amount.
      disks: list of strs of paths to disk images, in the order that
        they should be attached to the guest. All disk images must be
        owned by the user calling createDomain.
      network: The name of the network to attach this VM to. The
        netwok must have been created using
        debmarshal.privops.createNetwork by the user calling
        createDomain.
      mac: The MAC address of the new VM.
      hypervisor: What hypervisor to use to start this VM. While it's
        possible to mix hypervisors amongst the domains for a single
        test suite, it is the caller's responsibility to keep track of
        that when destroyDomain is called later. Currently only qemu
        is supported.
      arch: The CPU architecture for the VM, or an empty string if the
        architecture should be the same as that of the host.

      extra: A dict of optional extra configuration arguments, e.g.:
        kernel: The kernel to boot with, if requested. The kernel
          should be readable by the user calling createDomain.
        initrd: The initrd to boot with, if requested. The initrd
          should also be readable by the user calling createDomain
        cmdline: Additional command-line arguments to pass to the
          kernel.

    Returns:
      The name of the new domain.
    """
        utils.caller = _debmarshal_sender

        hyper_class = hypervisors.base.hypervisors[hypervisor]
        virt_con = hyper_class.open()

        domains._validateNetwork(network, virt_con)
        for d in disks:
            domains._validatePath(d, os.R_OK | os.W_OK)

        allowed_extras = set(
            ['kernel', 'initrd', 'cmdline', 'on_poweroff', 'on_reboot'])
        if set(extra) - allowed_extras != set():
            raise errors.InvalidInput("Invalid extra parameter specified.")

        if 'kernel' in extra:
            if 'initrd' not in extra:
                raise errors.InvalidInput(
                    "Must specify both a kernel and initrd, or neither.")

            domains._validatePath(extra['kernel'], os.R_OK)
            domains._validatePath(extra['initrd'], os.R_OK)

        for opt in ['on_poweroff', 'on_reboot']:
            if opt in extra and extra[opt] not in ('destroy', 'restart'):
                raise errors.InvalidInput(
                    "%s must be one of 'destroy' or 'restart'" % opt)

        name = domains._findUnusedName(virt_con)
        memory = debmarshal.utils.parseKBytes(memory)

        vm_params = vm.VM(name=name,
                          memory=memory,
                          disks=disks,
                          network=network,
                          mac=mac,
                          arch=arch,
                          extra=extra)

        dom_xml = hyper_class.domainXMLString(vm_params)

        # The new domain is intentionally recorded to the statefile before
        # starting the VM, because it's much worse to have a running VM we
        # don't know about than to have state on a VM that doesn't
        # actually exist (loadDomainState already handles the latter
        # case).
        doms = domains.loadDomainState()
        doms[(name, hypervisor)] = utils.getCaller()
        utils.storeState(doms, 'debmarshal-domains')

        virt_con.createLinux(dom_xml, 0)

        utils.caller = None

        return name
예제 #19
0
  def createNetwork(self, hosts, _debmarshal_sender=None):
    """All of the networking config you need for a debmarshal test rig.

    createNetwork creates an isolated virtual network within
    libvirt. It picks an IP address space that is as-yet unused
    (within debmarshal), and assigns that to the network. It then
    allocates IP addresses and MAC addresses for each of the hostnames
    listed in hosts.

    createNetwork tracks which users created which networks, and
    debmarshal will only allow the user that created a network to
    attach VMs to it or destroy it.

    Args:
      hosts: A list of hostnames that will eventually be attached to
        this network

    Returns:
      A 4-tuple containing:
        Network name: This is used to reference the newly created
          network in the future. It is unique across the local
          workstation
        Gateway: The network address. Also the DNS server, if that
          information isn't being grabbed over DHCP
        Netmask: The netmask for the network
        VMs: A dict mapping hostnames in hosts to (IP address, MAC
          address), as assigned by createNetwork
    """
    utils.caller = _debmarshal_sender

    # First, input validation. Everything in hosts should be a valid
    # hostname
    for h in hosts:
      networks._validateHostname(h)

    # We don't really care which particular libvirt driver we connect
    # to, because they all share the same networking
    # config. libvirt.open() is supposed to take None to indicate a
    # default, but it doesn't seem to work, so we pass in what's
    # supposed to be the default for root.
    virt_con = libvirt.open('qemu:///system')

    net_name = networks._findUnusedName()
    net_gateway, net_mask = networks._findUnusedNetwork(len(hosts))

    net_hosts = {}
    host_addr = ip.IP(net_gateway) + 1
    for host in hosts:
      # Use the virtinst package's MAC address generator because it's
      # easier than writing another one for ourselves.
      #
      # This does mean that the MAC addresses are allocated from
      # Xensource's OUI, but whatever
      mac = virtinst.util.randomMAC()
      net_hosts[host] = (host_addr.ip_ext, mac)
      host_addr += 1

    xml = networks._genNetworkXML(net_name, net_gateway, net_mask, net_hosts)
    virt_net = virt_con.networkDefineXML(xml)
    virt_net.create()

    try:
      nets = networks.loadNetworkState(virt_con)
      nets[net_name] = utils.getCaller()
      utils.storeState(nets, 'debmarshal-networks')
    except:
      virt_con.networkLookupByName(net_name).destroy()
      virt_con.networkLookupByName(net_name).undefine()
      raise

    utils.caller = None

    return (net_name, net_gateway, net_mask, net_hosts)
예제 #20
0
  def createDomain(self, memory, disks, network, mac, hypervisor, arch,
                   extra,
                   _debmarshal_sender=None):
    """Create a virtual machine domain.

    createDomain creates a domain for a virtual machine used as part
    of a debmarshal test and boots it up.

    We do no validation or accounting of memory allocations from the
    privileged side. Since debmarshal is intended to be run on
    single-user machines, the worst case scenario is a DoS of
    yourself.

    When supported by the hypervisor, createDomain can also pass in a
    hypervisor, initrd, and command line, bypassing the BIOS.

    Args:
      memory: str containing the amount of memory to be allocated to
        the new domain. This should include a suffix such as 'G' or
        'M' to indicate the units of the amount.
      disks: list of strs of paths to disk images, in the order that
        they should be attached to the guest. All disk images must be
        owned by the user calling createDomain.
      network: The name of the network to attach this VM to. The
        netwok must have been created using
        debmarshal.privops.createNetwork by the user calling
        createDomain.
      mac: The MAC address of the new VM.
      hypervisor: What hypervisor to use to start this VM. While it's
        possible to mix hypervisors amongst the domains for a single
        test suite, it is the caller's responsibility to keep track of
        that when destroyDomain is called later. Currently only qemu
        is supported.
      arch: The CPU architecture for the VM, or an empty string if the
        architecture should be the same as that of the host.

      extra: A dict of optional extra configuration arguments, e.g.:
        kernel: The kernel to boot with, if requested. The kernel
          should be readable by the user calling createDomain.
        initrd: The initrd to boot with, if requested. The initrd
          should also be readable by the user calling createDomain
        cmdline: Additional command-line arguments to pass to the
          kernel.

    Returns:
      The name of the new domain.
    """
    utils.caller = _debmarshal_sender

    hyper_class = hypervisors.base.hypervisors[hypervisor]
    virt_con = hyper_class.open()

    domains._validateNetwork(network, virt_con)
    for d in disks:
      domains._validatePath(d, os.R_OK | os.W_OK)

    allowed_extras = set(['kernel', 'initrd', 'cmdline',
                          'on_poweroff', 'on_reboot'])
    if set(extra) - allowed_extras != set():
      raise errors.InvalidInput("Invalid extra parameter specified.")

    if 'kernel' in extra:
      if 'initrd' not in extra:
        raise errors.InvalidInput(
          "Must specify both a kernel and initrd, or neither.")

      domains._validatePath(extra['kernel'], os.R_OK)
      domains._validatePath(extra['initrd'], os.R_OK)

    for opt in ['on_poweroff', 'on_reboot']:
      if opt in extra and extra[opt] not in ('destroy', 'restart'):
        raise errors.InvalidInput(
          "%s must be one of 'destroy' or 'restart'" % opt)

    name = domains._findUnusedName(virt_con)
    memory = debmarshal.utils.parseKBytes(memory)

    vm_params = vm.VM(name=name,
                      memory=memory,
                      disks=disks,
                      network=network,
                      mac=mac,
                      arch=arch,
                      extra=extra)

    dom_xml = hyper_class.domainXMLString(vm_params)

    # The new domain is intentionally recorded to the statefile before
    # starting the VM, because it's much worse to have a running VM we
    # don't know about than to have state on a VM that doesn't
    # actually exist (loadDomainState already handles the latter
    # case).
    doms = domains.loadDomainState()
    doms[(name, hypervisor)] = utils.getCaller()
    utils.storeState(doms, 'debmarshal-domains')

    virt_con.createLinux(dom_xml, 0)

    utils.caller = None

    return name