Wrap all VMM connection related object into QubesVMMConnection class

This makes easier to import right objects in submodules (only one
object). This also implement lazy connection - at first access, not at
module import, which speeds up tools, which doesn't need runtime
information (like qvm-prefs or qvm-service). In the future this will
ease migration from xenstore to QubesDB.

Also implement "offline mode" - operate on qubes.xml without connecting
to VMM - raise exception at such try.
This is needed to run tools during installation, where only minimal
set of services are started, especially no libvirt.
This commit is contained in:
Marek Marczykowski 2013-05-17 04:06:29 +02:00 committed by Marek Marczykowski-Górecki
parent 9f90106db4
commit b8c62c0279
10 changed files with 196 additions and 136 deletions

View File

@ -39,7 +39,7 @@ from qubes import qmemman_algo
import libvirt
import warnings
from qubes.qubes import xs,dry_run,libvirt_conn
from qubes.qubes import dry_run,vmm
from qubes.qubes import register_qubes_vm_class
from qubes.qubes import QubesVmCollection,QubesException,QubesHost,QubesVmLabels
from qubes.qubes import defaults,system_path,vm_files,qubes_max_qid
@ -202,6 +202,10 @@ class QubesVm(object):
attrs[prop]['save_skip'] = \
lambda prop=prop: getattr(self, prop) is None
# Can happen only if VM created in offline mode
attrs['maxmem']['save_skip'] = lambda: self.maxmem is None
attrs['vcpus']['save_skip'] = lambda: self.vcpus is None
attrs['uuid']['save_skip'] = lambda: self.uuid is None
attrs['mac']['save'] = lambda: str(self._mac)
attrs['mac']['save_skip'] = lambda: self._mac is None
@ -304,7 +308,7 @@ class QubesVm(object):
self.netvm.connected_vms[self.qid] = self
# Not in generic way to not create QubesHost() to frequently
if self.maxmem is None:
if self.maxmem is None and not vmm.offline_mode:
qubes_host = QubesHost()
total_mem_mb = qubes_host.memory_total/1024
self.maxmem = total_mem_mb/2
@ -314,7 +318,7 @@ class QubesVm(object):
self.maxmem = self.memory * 10
# By default allow use all VCPUs
if self.vcpus is None:
if self.vcpus is None and not vmm.offline_mode:
qubes_host = QubesHost()
self.vcpus = qubes_host.no_cpus
@ -648,7 +652,7 @@ class QubesVm(object):
def _update_libvirt_domain(self):
domain_config = self.create_config_file()
self._libvirt_domain = libvirt_conn.defineXML(domain_config)
self._libvirt_domain = vmm.libvirt_conn.defineXML(domain_config)
self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID())
@property
@ -658,9 +662,9 @@ class QubesVm(object):
try:
if self.uuid is not None:
self._libvirt_domain = libvirt_conn.lookupByUUID(self.uuid.bytes)
self._libvirt_domain = vmm.libvirt_conn.lookupByUUID(self.uuid.bytes)
else:
self._libvirt_domain = libvirt_conn.lookupByName(self.name)
self._libvirt_domain = vmm.libvirt_conn.lookupByName(self.name)
self.uuid = uuid.UUID(bytes=self._libvirt_domain.UUID())
except libvirt.libvirtError:
if libvirt.virGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
@ -788,7 +792,7 @@ class QubesVm(object):
# TODO
uuid = self.uuid
start_time = xs.read('', "/vm/%s/start_time" % str(uuid))
start_time = vmm.xs.read('', "/vm/%s/start_time" % str(uuid))
if start_time != '':
return datetime.datetime.fromtimestamp(float(start_time))
else:
@ -820,7 +824,7 @@ class QubesVm(object):
# FIXME
# 51712 (0xCA00) is xvda
# backend node name not available through xenapi :(
used_dmdev = xs.read('', "/local/domain/0/backend/vbd/{0}/51712/node".format(self.xid))
used_dmdev = vmm.xs.read('', "/local/domain/0/backend/vbd/{0}/51712/node".format(self.xid))
return used_dmdev != current_dmdev
@ -905,15 +909,15 @@ class QubesVm(object):
return
dev_basepath = '/local/domain/%d/device/vif' % self.xid
for dev in xs.ls('', dev_basepath):
for dev in vmm.xs.ls('', dev_basepath):
# check if backend domain is alive
backend_xid = int(xs.read('', '%s/%s/backend-id' % (dev_basepath, dev)))
if backend_xid in libvirt_conn.listDomainsID():
backend_xid = int(vmm.xs.read('', '%s/%s/backend-id' % (dev_basepath, dev)))
if backend_xid in vmm.libvirt_conn.listDomainsID():
# check if device is still active
if xs.read('', '%s/%s/state' % (dev_basepath, dev)) == '4':
if vmm.xs.read('', '%s/%s/state' % (dev_basepath, dev)) == '4':
continue
# remove dead device
xs.rm('', '%s/%s' % (dev_basepath, dev))
vmm.xs.rm('', '%s/%s' % (dev_basepath, dev))
def create_xenstore_entries(self, xid = None):
if dry_run:
@ -924,69 +928,69 @@ class QubesVm(object):
assert xid >= 0, "Invalid XID value"
domain_path = xs.get_domain_path(xid)
domain_path = vmm.xs.get_domain_path(xid)
# Set Xen Store entires with VM networking info:
xs.write('', "{0}/qubes-vm-type".format(domain_path),
vmm.xs.write('', "{0}/qubes-vm-type".format(domain_path),
self.type)
xs.write('', "{0}/qubes-vm-updateable".format(domain_path),
vmm.xs.write('', "{0}/qubes-vm-updateable".format(domain_path),
str(self.updateable))
if self.is_netvm():
xs.write('',
vmm.xs.write('',
"{0}/qubes-netvm-gateway".format(domain_path),
self.gateway)
xs.write('',
vmm.xs.write('',
"{0}/qubes-netvm-secondary-dns".format(domain_path),
self.secondary_dns)
xs.write('',
vmm.xs.write('',
"{0}/qubes-netvm-netmask".format(domain_path),
self.netmask)
xs.write('',
vmm.xs.write('',
"{0}/qubes-netvm-network".format(domain_path),
self.network)
if self.netvm is not None:
xs.write('', "{0}/qubes-ip".format(domain_path), self.ip)
xs.write('', "{0}/qubes-netmask".format(domain_path),
vmm.xs.write('', "{0}/qubes-ip".format(domain_path), self.ip)
vmm.xs.write('', "{0}/qubes-netmask".format(domain_path),
self.netvm.netmask)
xs.write('', "{0}/qubes-gateway".format(domain_path),
vmm.xs.write('', "{0}/qubes-gateway".format(domain_path),
self.netvm.gateway)
xs.write('',
vmm.xs.write('',
"{0}/qubes-secondary-dns".format(domain_path),
self.netvm.secondary_dns)
tzname = self.get_timezone()
if tzname:
xs.write('',
vmm.xs.write('',
"{0}/qubes-timezone".format(domain_path),
tzname)
for srv in self.services.keys():
# convert True/False to "1"/"0"
xs.write('', "{0}/qubes-service/{1}".format(domain_path, srv),
vmm.xs.write('', "{0}/qubes-service/{1}".format(domain_path, srv),
str(int(self.services[srv])))
xs.write('',
vmm.xs.write('',
"{0}/qubes-block-devices".format(domain_path),
'')
xs.write('',
vmm.xs.write('',
"{0}/qubes-usb-devices".format(domain_path),
'')
xs.write('', "{0}/qubes-debug-mode".format(domain_path),
vmm.xs.write('', "{0}/qubes-debug-mode".format(domain_path),
str(int(self.debug)))
# Fix permissions
xs.set_permissions('', '{0}/device'.format(domain_path),
vmm.xs.set_permissions('', '{0}/device'.format(domain_path),
[{ 'dom': xid }])
xs.set_permissions('', '{0}/memory'.format(domain_path),
vmm.xs.set_permissions('', '{0}/memory'.format(domain_path),
[{ 'dom': xid }])
xs.set_permissions('', '{0}/qubes-block-devices'.format(domain_path),
vmm.xs.set_permissions('', '{0}/qubes-block-devices'.format(domain_path),
[{ 'dom': xid }])
xs.set_permissions('', '{0}/qubes-usb-devices'.format(domain_path),
vmm.xs.set_permissions('', '{0}/qubes-usb-devices'.format(domain_path),
[{ 'dom': xid }])
# fire hooks
@ -1743,7 +1747,7 @@ class QubesVm(object):
# Bind pci devices to pciback driver
for pci in self.pcidevs:
nd = libvirt_conn.nodeDeviceLookupByName('pci_0000_' + pci.replace(':','_').replace('.','_'))
nd = vmm.libvirt_conn.nodeDeviceLookupByName('pci_0000_' + pci.replace(':','_').replace('.','_'))
nd.dettach()
self.libvirt_domain.createWithFlags(libvirt.VIR_DOMAIN_START_PAUSED)

View File

@ -25,7 +25,7 @@ import sys
import os.path
import xen.lowlevel.xs
from qubes.qubes import QubesVm,register_qubes_vm_class,xs,dry_run
from qubes.qubes import QubesVm,register_qubes_vm_class,vmm,dry_run
from qubes.qubes import defaults,system_path,vm_files
from qubes.qubes import QubesVmCollection,QubesException
@ -115,7 +115,7 @@ class QubesNetVm(QubesVm):
super(QubesNetVm, self).create_xenstore_entries(xid)
xs.write('', "/local/domain/{0}/qubes-netvm-external-ip".format(xid), '')
vmm.xs.write('', "/local/domain/{0}/qubes-netvm-external-ip".format(xid), '')
self.update_external_ip_permissions(xid)
def update_external_ip_permissions(self, xid = -1):
@ -130,7 +130,7 @@ class QubesNetVm(QubesVm):
perms.append({ 'dom': xid, 'read': True })
try:
xs.set_permissions('', '/local/domain/{0}/qubes-netvm-external-ip'.format(xid),
vmm.xs.set_permissions('', '/local/domain/{0}/qubes-netvm-external-ip'.format(xid),
perms)
except xen.lowlevel.xs.Error as e:
print >>sys.stderr, "WARNING: failed to update external IP " \

View File

@ -24,7 +24,7 @@
from qubes.qubes import QubesNetVm,register_qubes_vm_class
from qubes.qubes import defaults
from qubes.qubes import QubesException,dry_run,libvirt_conn
from qubes.qubes import QubesException,dry_run,vmm
import psutil
class QubesAdminVm(QubesNetVm):
@ -62,7 +62,7 @@ class QubesAdminVm(QubesNetVm):
return psutil.virtual_memory().total/1024
def get_mem_static_max(self):
return libvirt_conn.getInfo()[1]
return vmm.libvirt_conn.getInfo()[1]
def get_disk_usage(self, file_or_dir):
return 0

View File

@ -24,7 +24,7 @@
from datetime import datetime
from qubes.qubes import QubesNetVm,register_qubes_vm_class,xs,dry_run
from qubes.qubes import QubesNetVm,register_qubes_vm_class,vmm,dry_run
from qubes.qubes import QubesVmCollection,QubesException
yum_proxy_ip = '10.137.255.254'
@ -99,8 +99,8 @@ class QubesProxyVm(QubesNetVm):
super(QubesProxyVm, self).create_xenstore_entries(xid)
xs.write('', "/local/domain/{0}/qubes-iptables-error".format(xid), '')
xs.set_permissions('', "/local/domain/{0}/qubes-iptables-error".format(xid),
vmm.xs.write('', "/local/domain/{0}/qubes-iptables-error".format(xid), '')
vmm.xs.set_permissions('', "/local/domain/{0}/qubes-iptables-error".format(xid),
[{ 'dom': xid, 'write': True }])
self.write_iptables_xenstore_entry()
@ -112,13 +112,13 @@ class QubesProxyVm(QubesNetVm):
xid = self.get_xid()
if self.netvm is None:
xs.write('', "/local/domain/{0}/qubes-netvm-domid".format(xid), '')
vmm.xs.write('', "/local/domain/{0}/qubes-netvm-domid".format(xid), '')
else:
xs.write('', "/local/domain/{0}/qubes-netvm-domid".format(xid),
vmm.xs.write('', "/local/domain/{0}/qubes-netvm-domid".format(xid),
"{0}".format(self.netvm.get_xid()))
def write_iptables_xenstore_entry(self):
xs.rm('', "/local/domain/{0}/qubes-iptables-domainrules".format(self.get_xid()))
vmm.xs.rm('', "/local/domain/{0}/qubes-iptables-domainrules".format(self.get_xid()))
iptables = "# Generated by Qubes Core on {0}\n".format(datetime.now().ctime())
iptables += "*filter\n"
iptables += ":INPUT DROP [0:0]\n"
@ -140,7 +140,7 @@ class QubesProxyVm(QubesNetVm):
# Deny inter-VMs networking
iptables += "-A FORWARD -i vif+ -o vif+ -j DROP\n"
iptables += "COMMIT\n"
xs.write('', "/local/domain/{0}/qubes-iptables-header".format(self.get_xid()), iptables)
vmm.xs.write('', "/local/domain/{0}/qubes-iptables-header".format(self.get_xid()), iptables)
vms = [vm for vm in self.connected_vms.values()]
for vm in vms:
@ -201,12 +201,12 @@ class QubesProxyVm(QubesNetVm):
iptables += "-A FORWARD -s {0} -j {1}\n".format(ip, default_action)
iptables += "COMMIT\n"
xs.write('', "/local/domain/"+str(self.get_xid())+"/qubes-iptables-domainrules/"+str(xid), iptables)
vmm.xs.write('', "/local/domain/"+str(self.get_xid())+"/qubes-iptables-domainrules/"+str(xid), iptables)
# no need for ending -A FORWARD -j DROP, cause default action is DROP
self.write_netvm_domid_entry()
self.rules_applied = None
xs.write('', "/local/domain/{0}/qubes-iptables".format(self.get_xid()), 'reload')
vmm.xs.write('', "/local/domain/{0}/qubes-iptables".format(self.get_xid()), 'reload')
register_qubes_vm_class(QubesProxyVm)

View File

@ -28,7 +28,7 @@ import libvirt
import time
from qubes.qubes import QubesVm,QubesVmLabel,register_qubes_vm_class
from qubes.qubes import QubesDispVmLabels
from qubes.qubes import dry_run,libvirt_conn
from qubes.qubes import dry_run,vmm
from qubes.qmemman_client import QMemmanClient
class QubesDisposableVm(QubesVm):
@ -146,7 +146,7 @@ class QubesDisposableVm(QubesVm):
assert (len(self.pcidevs) == 0), "DispVM cannot have PCI devices"
print >>sys.stderr, "time=%s, calling restore" % (str(time.time()))
libvirt_conn.restoreFlags(self.disp_savefile,
vmm.libvirt_conn.restoreFlags(self.disp_savefile,
domain_config, libvirt.VIR_DOMAIN_SAVE_PAUSED)
print >>sys.stderr, "time=%s, done, getting xid" % (str(time.time()))

View File

@ -30,7 +30,8 @@ import stat
import sys
import re
import stat
from qubes.qubes import QubesVm,register_qubes_vm_class,xs,dry_run
from qubes.qubes import QubesVm,register_qubes_vm_class,vmm,dry_run
from qubes.qubes import system_path,defaults
from qubes.qubes import QubesException
@ -397,7 +398,7 @@ class QubesHVm(QubesVm):
if self.xid < 0:
return -1
stubdom_xid_str = xs.read('', '/local/domain/%d/image/device-model-domid' % self.xid)
stubdom_xid_str = vmm.xs.read('', '/local/domain/%d/image/device-model-domid' % self.xid)
if stubdom_xid_str is not None:
return int(stubdom_xid_str)
else:

View File

@ -38,7 +38,6 @@ import atexit
dry_run = False
#dry_run = True
if not dry_run:
import libvirt
import xen.lowlevel.xc
@ -108,22 +107,76 @@ qubes_max_netid = 254
class QubesException (Exception):
pass
def libvirt_error_handler(ctx, error):
pass
class QubesVMMConnection(object):
def __init__(self):
self._libvirt_conn = None
self._xs = None
self._xc = None
self._offline_mode = False
@property
def offline_mode(self):
return self._offline_mode
@offline_mode.setter
def offline_mode(self, value):
if not value and self._libvirt_conn is not None:
raise QubesException("Cannot change offline mode while already connected")
self._offline_mode = value
def _libvirt_error_handler(self, ctx, error):
pass
def init_vmm_connection(self):
if self._libvirt_conn is not None:
# Already initialized
return
if self._offline_mode:
# Do not initialize in offline mode
return
self._xc = xen.lowlevel.xc.xc()
self._xs = xen.lowlevel.xs.xs()
self._libvirt_conn = libvirt.open(defaults['libvirt_uri'])
if self._libvirt_conn == None:
raise QubesException("Failed connect to libvirt driver")
libvirt.registerErrorHandler(self._libvirt_error_handler, None)
atexit.register(self._libvirt_conn.close)
def _common_getter(self, name):
if self._offline_mode:
# Do not initialize in offline mode
raise QubesException("VMM operations disabled in offline mode")
if self._libvirt_conn is None:
self.init_vmm_connection()
return getattr(self, name)
@property
def libvirt_conn(self):
return self._common_getter('_libvirt_conn')
@property
def xs(self):
return self._common_getter('_xs')
@property
def xc(self):
return self._common_getter('_xc')
##### VMM global variable definition #####
if not dry_run:
xc = xen.lowlevel.xc.xc()
xs = xen.lowlevel.xs.xs()
libvirt_conn = libvirt.open(defaults['libvirt_uri'])
if libvirt_conn == None:
raise QubesException("Failed connect to libvirt driver")
libvirt.registerErrorHandler(libvirt_error_handler, None)
atexit.register(libvirt_conn.close)
vmm = QubesVMMConnection()
##########################################
class QubesHost(object):
def __init__(self):
(model, memory, cpus, mhz, nodes, socket, cores, threads) = libvirt_conn.getInfo()
self.physinfo = xc.physinfo()
(model, memory, cpus, mhz, nodes, socket, cores, threads) = vmm.libvirt_conn.getInfo()
self.physinfo = vmm.xc.physinfo()
self._total_mem = long(memory)*1024
self._no_cpus = cpus
@ -152,7 +205,7 @@ class QubesHost(object):
if previous is None:
previous_time = time.time()
previous = {}
info = xc.domain_getinfo(0, qubes_max_qid)
info = vmm.xc.domain_getinfo(0, qubes_max_qid)
for vm in info:
previous[vm['domid']] = {}
previous[vm['domid']]['cpu_time'] = (
@ -162,7 +215,7 @@ class QubesHost(object):
current_time = time.time()
current = {}
info = xc.domain_getinfo(0, qubes_max_qid)
info = vmm.xc.domain_getinfo(0, qubes_max_qid)
for vm in info:
current[vm['domid']] = {}
current[vm['domid']]['cpu_time'] = (

View File

@ -24,7 +24,7 @@
from qubes import QubesVm,QubesException,QubesVmCollection
from qubes import QubesVmClasses
from qubes import xs
from qubes import vmm
from qubes import system_path,vm_files
import sys
import os
@ -208,7 +208,7 @@ def block_find_unused_frontend(vm = None):
assert vm is not None
assert vm.is_running()
vbd_list = xs.ls('', '/local/domain/%d/device/vbd' % vm.xid)
vbd_list = vmm.xs.ls('', '/local/domain/%d/device/vbd' % vm.xid)
# xvd* devices
major = 202
# prefer xvdi
@ -223,22 +223,22 @@ def block_list(vm = None, system_disks = False):
desc_re = re.compile(r"^.{1,255}$")
mode_re = re.compile(r"^[rw]$")
xs_trans = xs.transaction_start()
xs_trans = vmm.xs.transaction_start()
vm_list = []
if vm is not None:
if not vm.is_running():
xs.transaction_end(xs_trans)
vmm.xs.transaction_end(xs_trans)
return []
else:
vm_list = [ str(vm.xid) ]
else:
vm_list = xs.ls(xs_trans, '/local/domain')
vm_list = vmm.xs.ls(xs_trans, '/local/domain')
devices_list = {}
for xid in vm_list:
vm_name = xs.read(xs_trans, '/local/domain/%s/name' % xid)
vm_devices = xs.ls(xs_trans, '/local/domain/%s/qubes-block-devices' % xid)
vm_name = vmm.xs.read(xs_trans, '/local/domain/%s/name' % xid)
vm_devices = vmm.xs.ls(xs_trans, '/local/domain/%s/qubes-block-devices' % xid)
if vm_devices is None:
continue
for device in vm_devices:
@ -247,9 +247,9 @@ def block_list(vm = None, system_disks = False):
print >> sys.stderr, "Invalid device name in VM '%s'" % vm_name
continue
device_size = xs.read(xs_trans, '/local/domain/%s/qubes-block-devices/%s/size' % (xid, device))
device_desc = xs.read(xs_trans, '/local/domain/%s/qubes-block-devices/%s/desc' % (xid, device))
device_mode = xs.read(xs_trans, '/local/domain/%s/qubes-block-devices/%s/mode' % (xid, device))
device_size = vmm.xs.read(xs_trans, '/local/domain/%s/qubes-block-devices/%s/size' % (xid, device))
device_desc = vmm.xs.read(xs_trans, '/local/domain/%s/qubes-block-devices/%s/desc' % (xid, device))
device_mode = vmm.xs.read(xs_trans, '/local/domain/%s/qubes-block-devices/%s/mode' % (xid, device))
if device_size is None or device_desc is None or device_mode is None:
print >> sys.stderr, "Missing field in %s device parameters" % device
@ -277,16 +277,16 @@ def block_list(vm = None, system_disks = False):
"vm": vm_name, "device":device, "size":int(device_size),
"desc":device_desc, "mode":device_mode}
xs.transaction_end(xs_trans)
vmm.xs.transaction_end(xs_trans)
return devices_list
def block_check_attached(backend_vm, device, backend_xid = None):
if backend_xid is None:
backend_xid = backend_vm.xid
xs_trans = xs.transaction_start()
vm_list = xs.ls(xs_trans, '/local/domain/%d/backend/vbd' % backend_xid)
xs_trans = vmm.xs.transaction_start()
vm_list = vmm.xs.ls(xs_trans, '/local/domain/%d/backend/vbd' % backend_xid)
if vm_list is None:
xs.transaction_end(xs_trans)
vmm.xs.transaction_end(xs_trans)
return None
device_majorminor = None
try:
@ -295,10 +295,10 @@ def block_check_attached(backend_vm, device, backend_xid = None):
# Unknown devices will be compared directly - perhaps it is a filename?
pass
for vm_xid in vm_list:
for devid in xs.ls(xs_trans, '/local/domain/%d/backend/vbd/%s' % (backend_xid, vm_xid)):
for devid in vmm.xs.ls(xs_trans, '/local/domain/%d/backend/vbd/%s' % (backend_xid, vm_xid)):
(tmp_major, tmp_minor) = (0, 0)
phys_device = xs.read(xs_trans, '/local/domain/%d/backend/vbd/%s/%s/physical-device' % (backend_xid, vm_xid, devid))
dev_params = xs.read(xs_trans, '/local/domain/%d/backend/vbd/%s/%s/params' % (backend_xid, vm_xid, devid))
phys_device = vmm.xs.read(xs_trans, '/local/domain/%d/backend/vbd/%s/%s/physical-device' % (backend_xid, vm_xid, devid))
dev_params = vmm.xs.read(xs_trans, '/local/domain/%d/backend/vbd/%s/%s/params' % (backend_xid, vm_xid, devid))
if phys_device and phys_device.find(':'):
(tmp_major, tmp_minor) = phys_device.split(":")
tmp_major = int(tmp_major, 16)
@ -319,9 +319,9 @@ def block_check_attached(backend_vm, device, backend_xid = None):
#TODO
vm_name = xl_ctx.domid_to_name(int(vm_xid))
frontend = block_devid_to_name(int(devid))
xs.transaction_end(xs_trans)
vmm.xs.transaction_end(xs_trans)
return {"xid":int(vm_xid), "frontend": frontend, "devid": int(devid), "vm": vm_name}
xs.transaction_end(xs_trans)
vmm.xs.transaction_end(xs_trans)
return None
def block_attach(vm, backend_vm, device, frontend=None, mode="w", auto_detach=False, wait=True):
@ -343,7 +343,7 @@ def do_block_attach(vm, backend_vm, device, frontend, mode, auto_detach, wait):
raise QubesException("No unused frontend found")
else:
# Check if any device attached at this frontend
if xs.read('', '/local/domain/%d/device/vbd/%d/state' % (vm.xid, block_name_to_devid(frontend))) == '4':
if vmm.xs.read('', '/local/domain/%d/device/vbd/%d/state' % (vm.xid, block_name_to_devid(frontend))) == '4':
raise QubesException("Frontend %s busy in VM %s, detach it first" % (frontend, vm.name))
# Check if this device is attached to some domain
@ -368,8 +368,8 @@ def do_block_attach(vm, backend_vm, device, frontend, mode, auto_detach, wait):
# 5sec timeout
timeout = 5/interval
while timeout > 0:
be_state = xs.read('', be_path + '/state')
hotplug_state = xs.read('', be_path + '/hotplug-status')
be_state = vmm.xs.read('', be_path + '/state')
hotplug_state = vmm.xs.read('', be_path + '/hotplug-status')
if be_state is None:
raise QubesException("Backend device disappeared, something weird happened")
elif int(be_state) == 4:
@ -377,13 +377,13 @@ def do_block_attach(vm, backend_vm, device, frontend, mode, auto_detach, wait):
return
elif int(be_state) > 4:
# Error
error = xs.read('', '/local/domain/%d/error/backend/vbd/%d/%d/error' % (backend_vm.xid, vm.xid, block_name_to_devid(frontend)))
error = vmm.xs.read('', '/local/domain/%d/error/backend/vbd/%d/%d/error' % (backend_vm.xid, vm.xid, block_name_to_devid(frontend)))
if error is not None:
raise QubesException("Error while connecting block device: " + error)
else:
raise QubesException("Unknown error while connecting block device")
elif hotplug_state == 'error':
hotplug_error = xs.read('', be_path + '/hotplug-error')
hotplug_error = vmm.xs.read('', be_path + '/hotplug-error')
if hotplug_error:
raise QubesException("Error while connecting block device: " + hotplug_error)
else:
@ -401,7 +401,7 @@ def block_detach(vm, frontend = "xvdi", vm_xid = None):
vm_xid = vm.xid
# Check if this device is really connected
if not xs.read('', '/local/domain/%d/device/vbd/%d/state' % (vm_xid, block_name_to_devid(frontend))) == '4':
if not vmm.xs.read('', '/local/domain/%d/device/vbd/%d/state' % (vm_xid, block_name_to_devid(frontend))) == '4':
# Do nothing - device already detached
return
@ -417,21 +417,21 @@ def block_detach_all(vm, vm_xid = None):
# FIXME: potential race
vm_xid = vm.xid
xs_trans = xs.transaction_start()
devices = xs.ls(xs_trans, '/local/domain/%d/device/vbd' % vm_xid)
xs_trans = vmm.xs.transaction_start()
devices = vmm.xs.ls(xs_trans, '/local/domain/%d/device/vbd' % vm_xid)
if devices is None:
return
devices_to_detach = []
for devid in devices:
# check if this is system disk
be_path = xs.read(xs_trans, '/local/domain/%d/device/vbd/%s/backend' % (vm_xid, devid))
be_path = vmm.xs.read(xs_trans, '/local/domain/%d/device/vbd/%s/backend' % (vm_xid, devid))
assert be_path is not None
be_params = xs.read(xs_trans, be_path + '/params')
be_params = vmm.xs.read(xs_trans, be_path + '/params')
if be_path.startswith('/local/domain/0/') and be_params is not None and be_params.startswith(system_path["qubes_base_dir"]):
# system disk
continue
devices_to_detach.append(devid)
xs.transaction_end(xs_trans)
vmm.xs.transaction_end(xs_trans)
for devid in devices_to_detach:
xl_cmd = [ '/usr/sbin/xl', 'block-detach', str(vm_xid), devid]
subprocess.check_call(xl_cmd)
@ -450,7 +450,7 @@ def usb_setup(backend_vm_xid, vm_xid, devid, usb_ver):
devid - id of the pvusb controller
"""
num_ports = 8
trans = xs.transaction_start()
trans = vmm.xs.transaction_start()
be_path = "/local/domain/%d/backend/vusb/%d/%d" % (backend_vm_xid, vm_xid, devid)
fe_path = "/local/domain/%d/device/vusb/%d" % (vm_xid, devid)
@ -459,35 +459,35 @@ def usb_setup(backend_vm_xid, vm_xid, devid, usb_ver):
fe_perm = [{'dom': vm_xid}, {'dom': backend_vm_xid, 'read': True} ]
# Create directories and set permissions
xs.write(trans, be_path, "")
xs.set_permissions(trans, be_path, be_perm)
vmm.xs.write(trans, be_path, "")
vmm.xs.set_permissions(trans, be_path, be_perm)
xs.write(trans, fe_path, "")
xs.set_permissions(trans, fe_path, fe_perm)
vmm.xs.write(trans, fe_path, "")
vmm.xs.set_permissions(trans, fe_path, fe_perm)
# Write backend information into the location that frontend looks for
xs.write(trans, "%s/backend-id" % fe_path, str(backend_vm_xid))
xs.write(trans, "%s/backend" % fe_path, be_path)
vmm.xs.write(trans, "%s/backend-id" % fe_path, str(backend_vm_xid))
vmm.xs.write(trans, "%s/backend" % fe_path, be_path)
# Write frontend information into the location that backend looks for
xs.write(trans, "%s/frontend-id" % be_path, str(vm_xid))
xs.write(trans, "%s/frontend" % be_path, fe_path)
vmm.xs.write(trans, "%s/frontend-id" % be_path, str(vm_xid))
vmm.xs.write(trans, "%s/frontend" % be_path, fe_path)
# Write USB Spec version field.
xs.write(trans, "%s/usb-ver" % be_path, usb_ver)
vmm.xs.write(trans, "%s/usb-ver" % be_path, usb_ver)
# Write virtual root hub field.
xs.write(trans, "%s/num-ports" % be_path, str(num_ports))
vmm.xs.write(trans, "%s/num-ports" % be_path, str(num_ports))
for port in range(1, num_ports+1):
# Set all port to disconnected state
xs.write(trans, "%s/port/%d" % (be_path, port), "")
vmm.xs.write(trans, "%s/port/%d" % (be_path, port), "")
# Set state to XenbusStateInitialising
xs.write(trans, "%s/state" % fe_path, "1")
xs.write(trans, "%s/state" % be_path, "1")
xs.write(trans, "%s/online" % be_path, "1")
vmm.xs.write(trans, "%s/state" % fe_path, "1")
vmm.xs.write(trans, "%s/state" % be_path, "1")
vmm.xs.write(trans, "%s/online" % be_path, "1")
xs.transaction_end(trans)
vmm.xs.transaction_end(trans)
def usb_decode_device_from_xs(xs_encoded_device):
""" recover actual device name (xenstore doesn't allow dot in key names, so it was translated to underscore) """
@ -512,12 +512,12 @@ def usb_list():
devices_list = {}
xs_trans = xs.transaction_start()
vm_list = xs.ls(xs_trans, '/local/domain')
xs_trans = vmm.xs.transaction_start()
vm_list = vmm.xs.ls(xs_trans, '/local/domain')
for xid in vm_list:
vm_name = xs.read(xs_trans, '/local/domain/%s/name' % xid)
vm_devices = xs.ls(xs_trans, '/local/domain/%s/qubes-usb-devices' % xid)
vm_name = vmm.xs.read(xs_trans, '/local/domain/%s/name' % xid)
vm_devices = vmm.xs.ls(xs_trans, '/local/domain/%s/qubes-usb-devices' % xid)
if vm_devices is None:
continue
# when listing devices in xenstore we get encoded names
@ -527,13 +527,13 @@ def usb_list():
print >> sys.stderr, "Invalid device id in backend VM '%s'" % vm_name
continue
device = usb_decode_device_from_xs(xs_encoded_device)
device_desc = xs.read(xs_trans, '/local/domain/%s/qubes-usb-devices/%s/desc' % (xid, xs_encoded_device))
device_desc = vmm.xs.read(xs_trans, '/local/domain/%s/qubes-usb-devices/%s/desc' % (xid, xs_encoded_device))
if not desc_re.match(device_desc):
print >> sys.stderr, "Invalid %s device desc in VM '%s'" % (device, vm_name)
continue
visible_name = "%s:%s" % (vm_name, device)
# grab version
usb_ver = xs.read(xs_trans, '/local/domain/%s/qubes-usb-devices/%s/usb-ver' % (xid, xs_encoded_device))
usb_ver = vmm.xs.read(xs_trans, '/local/domain/%s/qubes-usb-devices/%s/usb-ver' % (xid, xs_encoded_device))
if usb_ver is None or not usb_ver_re.match(usb_ver):
print >> sys.stderr, "Invalid %s device USB version in VM '%s'" % (device, vm_name)
continue
@ -542,7 +542,7 @@ def usb_list():
"desc":device_desc,
"usb_ver":usb_ver}
xs.transaction_end(xs_trans)
vmm.xs.transaction_end(xs_trans)
return devices_list
def usb_check_attached(xs_trans, backend_vm, device):
@ -559,21 +559,21 @@ def usb_check_attached(xs_trans, backend_vm, device):
"""
# sample xs content: /local/domain/0/backend/vusb/4/0/port/1 = "7-5"
attached_dev = None
vms = xs.ls(xs_trans, '/local/domain/%d/backend/vusb' % backend_vm)
vms = vmm.xs.ls(xs_trans, '/local/domain/%d/backend/vusb' % backend_vm)
if vms is None:
return None
for vm in vms:
if not vm.isdigit():
print >> sys.stderr, "Invalid VM id"
continue
frontend_devs = xs.ls(xs_trans, '/local/domain/%d/backend/vusb/%s' % (backend_vm, vm))
frontend_devs = vmm.xs.ls(xs_trans, '/local/domain/%d/backend/vusb/%s' % (backend_vm, vm))
if frontend_devs is None:
continue
for frontend_dev in frontend_devs:
if not frontend_dev.isdigit():
print >> sys.stderr, "Invalid frontend in VM %s" % vm
continue
ports = xs.ls(xs_trans, '/local/domain/%d/backend/vusb/%s/%s/port' % (backend_vm, vm, frontend_dev))
ports = vmm.xs.ls(xs_trans, '/local/domain/%d/backend/vusb/%s/%s/port' % (backend_vm, vm, frontend_dev))
if ports is None:
continue
for port in ports:
@ -581,7 +581,7 @@ def usb_check_attached(xs_trans, backend_vm, device):
if not port.isdigit():
print >> sys.stderr, "Invalid port in VM %s frontend %s" % (vm, frontend)
continue
dev = xs.read(xs_trans, '/local/domain/%d/backend/vusb/%s/%s/port/%s' % (backend_vm, vm, frontend_dev, port))
dev = vmm.xs.read(xs_trans, '/local/domain/%d/backend/vusb/%s/%s/port/%s' % (backend_vm, vm, frontend_dev, port))
if dev == "":
continue
# Sanitize device id
@ -605,7 +605,7 @@ def usb_check_attached(xs_trans, backend_vm, device):
# if len(devport) != 2:
# raise QubesException("Malformed frontend syntax, must be in device-port format")
# # FIXME:
# # return xs.read('', '/local/domain/%d/device/vusb/%d/state' % (vm.xid, frontend)) == '4'
# # return vmm.xs.read('', '/local/domain/%d/device/vusb/%d/state' % (vm.xid, frontend)) == '4'
# return False
def usb_find_unused_frontend(xs_trans, backend_vm_xid, vm_xid, usb_ver):
@ -619,7 +619,7 @@ def usb_find_unused_frontend(xs_trans, backend_vm_xid, vm_xid, usb_ver):
# If nothing found, this value will be used to derive the index of a new frontend.
last_frontend_dev = -1
frontend_devs = xs.ls(xs_trans, "/local/domain/%d/device/vusb" % vm_xid)
frontend_devs = vmm.xs.ls(xs_trans, "/local/domain/%d/device/vusb" % vm_xid)
if frontend_devs is not None:
for frontend_dev in frontend_devs:
if not frontend_dev.isdigit():
@ -627,12 +627,12 @@ def usb_find_unused_frontend(xs_trans, backend_vm_xid, vm_xid, usb_ver):
continue
frontend_dev = int(frontend_dev)
fe_path = "/local/domain/%d/device/vusb/%d" % (vm_xid, frontend_dev)
if xs.read(xs_trans, "%s/backend-id" % fe_path) == str(backend_vm_xid):
if xs.read(xs_trans, '/local/domain/%d/backend/vusb/%d/%d/usb-ver' % (backend_vm_xid, vm_xid, frontend_dev)) != usb_ver:
if vmm.xs.read(xs_trans, "%s/backend-id" % fe_path) == str(backend_vm_xid):
if vmm.xs.read(xs_trans, '/local/domain/%d/backend/vusb/%d/%d/usb-ver' % (backend_vm_xid, vm_xid, frontend_dev)) != usb_ver:
last_frontend_dev = frontend_dev
continue
# here: found an existing frontend already connected to right backend using an appropriate USB version
ports = xs.ls(xs_trans, '/local/domain/%d/backend/vusb/%d/%d/port' % (backend_vm_xid, vm_xid, frontend_dev))
ports = vmm.xs.ls(xs_trans, '/local/domain/%d/backend/vusb/%d/%d/port' % (backend_vm_xid, vm_xid, frontend_dev))
if ports is None:
print >> sys.stderr, "No ports in VM %d frontend_dev %d?" % (vm_xid, frontend_dev)
last_frontend_dev = frontend_dev
@ -643,7 +643,7 @@ def usb_find_unused_frontend(xs_trans, backend_vm_xid, vm_xid, usb_ver):
print >> sys.stderr, "Invalid port in VM %d frontend_dev %d" % (vm_xid, frontend_dev)
continue
port = int(port)
dev = xs.read(xs_trans, '/local/domain/%d/backend/vusb/%d/%s/port/%s' % (backend_vm_xid, vm_xid, frontend_dev, port))
dev = vmm.xs.read(xs_trans, '/local/domain/%d/backend/vusb/%d/%s/port/%s' % (backend_vm_xid, vm_xid, frontend_dev, port))
# Sanitize device id
if not usb_port_re.match(dev):
print >> sys.stderr, "Invalid device id in backend VM %d @ %d/%d/port/%d" % \
@ -661,12 +661,12 @@ def usb_find_unused_frontend(xs_trans, backend_vm_xid, vm_xid, usb_ver):
def usb_attach(vm, backend_vm, device, frontend=None, auto_detach=False, wait=True):
device_attach_check(vm, backend_vm, device, frontend)
xs_trans = xs.transaction_start()
xs_trans = vmm.xs.transaction_start()
xs_encoded_device = usb_encode_device_for_xs(device)
usb_ver = xs.read(xs_trans, '/local/domain/%s/qubes-usb-devices/%s/usb-ver' % (backend_vm.xid, xs_encoded_device))
usb_ver = vmm.xs.read(xs_trans, '/local/domain/%s/qubes-usb-devices/%s/usb-ver' % (backend_vm.xid, xs_encoded_device))
if usb_ver is None or not usb_ver_re.match(usb_ver):
xs.transaction_end(xs_trans)
vmm.xs.transaction_end(xs_trans)
raise QubesException("Invalid %s device USB version in VM '%s'" % (device, backend_vm.name))
if frontend is None:
@ -675,12 +675,12 @@ def usb_attach(vm, backend_vm, device, frontend=None, auto_detach=False, wait=Tr
# Check if any device attached at this frontend
#if usb_check_frontend_busy(vm, frontend):
# raise QubesException("Frontend %s busy in VM %s, detach it first" % (frontend, vm.name))
xs.transaction_end(xs_trans)
vmm.xs.transaction_end(xs_trans)
raise NotImplementedError("Explicit USB frontend specification is not implemented yet")
# Check if this device is attached to some domain
attached_vm = usb_check_attached(xs_trans, backend_vm.xid, device)
xs.transaction_end(xs_trans)
vmm.xs.transaction_end(xs_trans)
if attached_vm:
if auto_detach:

View File

@ -21,7 +21,7 @@
#
#
from qubes.qubes import QubesVmCollection
from qubes.qubes import QubesVmCollection,vmm
from qubes.qubes import QubesException
from optparse import OptionParser;
import sys
@ -56,6 +56,7 @@ def main():
print >> sys.stderr, "... or use --force-root to continue anyway."
exit(1)
vmm.offline_mode = True
qvm_collection = QubesVmCollection()
qvm_collection.lock_db_for_writing()
qvm_collection.load()

View File

@ -21,10 +21,11 @@
#
#
from qubes.qubes import QubesVmCollection
from qubes.qubes import QubesVmCollection,vmm
import sys
def main():
vmm.offline_mode = True
qvm_collection = QubesVmCollection()
if qvm_collection.check_if_storage_exists():
print >> sys.stderr, "Storage exists, not overwriting."