Merge branch 'master' of git.qubes-os.org:/var/lib/qubes/git/marmarek/core
This commit is contained in:
commit
30c744a716
@ -377,7 +377,7 @@ class QubesVm(object):
|
|||||||
self._set_netvm(new_netvm)
|
self._set_netvm(new_netvm)
|
||||||
|
|
||||||
def _set_netvm(self, new_netvm):
|
def _set_netvm(self, new_netvm):
|
||||||
if self.is_running() and not new_netvm.is_running():
|
if self.is_running() and new_netvm is not None and not new_netvm.is_running():
|
||||||
raise QubesException("Cannot dynamically attach to stopped NetVM")
|
raise QubesException("Cannot dynamically attach to stopped NetVM")
|
||||||
if self.netvm is not None:
|
if self.netvm is not None:
|
||||||
self.netvm.connected_vms.pop(self.qid)
|
self.netvm.connected_vms.pop(self.qid)
|
||||||
|
@ -93,6 +93,8 @@ def print_stdout(text):
|
|||||||
def print_stderr(text):
|
def print_stderr(text):
|
||||||
print >> sys.stderr, (text)
|
print >> sys.stderr, (text)
|
||||||
|
|
||||||
|
###### Block devices ########
|
||||||
|
|
||||||
def block_devid_to_name(devid):
|
def block_devid_to_name(devid):
|
||||||
major = devid / 256
|
major = devid / 256
|
||||||
minor = devid % 256
|
minor = devid % 256
|
||||||
@ -203,10 +205,11 @@ def block_list(vm = None, system_disks = False):
|
|||||||
else:
|
else:
|
||||||
vm_list = xs.ls('', '/local/domain')
|
vm_list = xs.ls('', '/local/domain')
|
||||||
|
|
||||||
|
xs_trans = xs.transaction_start()
|
||||||
devices_list = {}
|
devices_list = {}
|
||||||
for xid in vm_list:
|
for xid in vm_list:
|
||||||
vm_name = xs.read('', '/local/domain/%s/name' % xid)
|
vm_name = xs.read(xs_trans, '/local/domain/%s/name' % xid)
|
||||||
vm_devices = xs.ls('', '/local/domain/%s/qubes-block-devices' % xid)
|
vm_devices = xs.ls(xs_trans, '/local/domain/%s/qubes-block-devices' % xid)
|
||||||
if vm_devices is None:
|
if vm_devices is None:
|
||||||
continue
|
continue
|
||||||
for device in vm_devices:
|
for device in vm_devices:
|
||||||
@ -215,9 +218,9 @@ def block_list(vm = None, system_disks = False):
|
|||||||
print >> sys.stderr, "Invalid device name in VM '%s'" % vm_name
|
print >> sys.stderr, "Invalid device name in VM '%s'" % vm_name
|
||||||
continue
|
continue
|
||||||
|
|
||||||
device_size = xs.read('', '/local/domain/%s/qubes-block-devices/%s/size' % (xid, device))
|
device_size = xs.read(xs_trans, '/local/domain/%s/qubes-block-devices/%s/size' % (xid, device))
|
||||||
device_desc = xs.read('', '/local/domain/%s/qubes-block-devices/%s/desc' % (xid, device))
|
device_desc = xs.read(xs_trans, '/local/domain/%s/qubes-block-devices/%s/desc' % (xid, device))
|
||||||
device_mode = xs.read('', '/local/domain/%s/qubes-block-devices/%s/mode' % (xid, device))
|
device_mode = 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:
|
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
|
print >> sys.stderr, "Missing field in %s device parameters" % device
|
||||||
@ -245,13 +248,16 @@ def block_list(vm = None, system_disks = False):
|
|||||||
"vm": vm_name, "device":device, "size":int(device_size),
|
"vm": vm_name, "device":device, "size":int(device_size),
|
||||||
"desc":device_desc, "mode":device_mode}
|
"desc":device_desc, "mode":device_mode}
|
||||||
|
|
||||||
|
xs.transaction_end(xs_trans)
|
||||||
return devices_list
|
return devices_list
|
||||||
|
|
||||||
def block_check_attached(backend_vm, device, backend_xid = None):
|
def block_check_attached(backend_vm, device, backend_xid = None):
|
||||||
if backend_xid is None:
|
if backend_xid is None:
|
||||||
backend_xid = backend_vm.xid
|
backend_xid = backend_vm.xid
|
||||||
vm_list = xs.ls('', '/local/domain/%d/backend/vbd' % backend_xid)
|
xs_trans = xs.transaction_start()
|
||||||
|
vm_list = xs.ls(xs_trans, '/local/domain/%d/backend/vbd' % backend_xid)
|
||||||
if vm_list is None:
|
if vm_list is None:
|
||||||
|
xs.transaction_end(xs_trans)
|
||||||
return None
|
return None
|
||||||
device_majorminor = None
|
device_majorminor = None
|
||||||
try:
|
try:
|
||||||
@ -260,10 +266,10 @@ def block_check_attached(backend_vm, device, backend_xid = None):
|
|||||||
# Unknown devices will be compared directly - perhaps it is a filename?
|
# Unknown devices will be compared directly - perhaps it is a filename?
|
||||||
pass
|
pass
|
||||||
for vm_xid in vm_list:
|
for vm_xid in vm_list:
|
||||||
for devid in xs.ls('', '/local/domain/%d/backend/vbd/%s' % (backend_xid, vm_xid)):
|
for devid in xs.ls(xs_trans, '/local/domain/%d/backend/vbd/%s' % (backend_xid, vm_xid)):
|
||||||
(tmp_major, tmp_minor) = (0, 0)
|
(tmp_major, tmp_minor) = (0, 0)
|
||||||
phys_device = xs.read('', '/local/domain/%d/backend/vbd/%s/%s/physical-device' % (backend_xid, vm_xid, devid))
|
phys_device = xs.read(xs_trans, '/local/domain/%d/backend/vbd/%s/%s/physical-device' % (backend_xid, vm_xid, devid))
|
||||||
dev_params = xs.read('', '/local/domain/%d/backend/vbd/%s/%s/params' % (backend_xid, vm_xid, devid))
|
dev_params = xs.read(xs_trans, '/local/domain/%d/backend/vbd/%s/%s/params' % (backend_xid, vm_xid, devid))
|
||||||
if phys_device and phys_device.find(':'):
|
if phys_device and phys_device.find(':'):
|
||||||
(tmp_major, tmp_minor) = phys_device.split(":")
|
(tmp_major, tmp_minor) = phys_device.split(":")
|
||||||
tmp_major = int(tmp_major, 16)
|
tmp_major = int(tmp_major, 16)
|
||||||
@ -283,10 +289,12 @@ def block_check_attached(backend_vm, device, backend_xid = None):
|
|||||||
(device_majorminor is None and dev_params == device):
|
(device_majorminor is None and dev_params == device):
|
||||||
vm_name = xl_ctx.domid_to_name(int(vm_xid))
|
vm_name = xl_ctx.domid_to_name(int(vm_xid))
|
||||||
frontend = block_devid_to_name(int(devid))
|
frontend = block_devid_to_name(int(devid))
|
||||||
|
xs.transaction_end(xs_trans)
|
||||||
return {"xid":int(vm_xid), "frontend": frontend, "devid": int(devid), "vm": vm_name}
|
return {"xid":int(vm_xid), "frontend": frontend, "devid": int(devid), "vm": vm_name}
|
||||||
|
xs.transaction_end(xs_trans)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def block_attach(vm, backend_vm, device, frontend=None, mode="w", auto_detach=False):
|
def block_attach(vm, backend_vm, device, frontend=None, mode="w", auto_detach=False, wait=True):
|
||||||
if not vm.is_running():
|
if not vm.is_running():
|
||||||
raise QubesException("VM %s not running" % vm.name)
|
raise QubesException("VM %s not running" % vm.name)
|
||||||
|
|
||||||
@ -317,6 +325,36 @@ def block_attach(vm, backend_vm, device, frontend=None, mode="w", auto_detach=Fa
|
|||||||
|
|
||||||
xl_cmd = [ '/usr/sbin/xl', 'block-attach', vm.name, backend_dev, frontend, mode, str(backend_vm.xid) ]
|
xl_cmd = [ '/usr/sbin/xl', 'block-attach', vm.name, backend_dev, frontend, mode, str(backend_vm.xid) ]
|
||||||
subprocess.check_call(xl_cmd)
|
subprocess.check_call(xl_cmd)
|
||||||
|
if wait:
|
||||||
|
be_path = '/local/domain/%d/backend/vbd/%d/%d' % (backend_vm.xid, vm.xid, block_name_to_devid(frontend))
|
||||||
|
# There is no way to use xenstore watch with a timeout, so must check in a loop
|
||||||
|
interval = 0.100
|
||||||
|
# 5sec timeout
|
||||||
|
timeout = 5/interval
|
||||||
|
while timeout > 0:
|
||||||
|
be_state = xs.read('', be_path + '/state')
|
||||||
|
hotplug_state = xs.read('', be_path + '/hotplug-status')
|
||||||
|
if be_state is None:
|
||||||
|
raise QubesException("Backend device disappeared, something weird happend")
|
||||||
|
elif int(be_state) == 4:
|
||||||
|
# Ok
|
||||||
|
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)))
|
||||||
|
if error is 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')
|
||||||
|
if hotplug_error:
|
||||||
|
raise QubesException("Error while connecting block device: " + hotplug_error)
|
||||||
|
else:
|
||||||
|
raise QubesException("Unknown hotplug error while connecting block device")
|
||||||
|
time.sleep(interval)
|
||||||
|
timeout -= interval
|
||||||
|
raise QubesException("Timeout while waiting for block defice connection")
|
||||||
|
|
||||||
def block_detach(vm, frontend = "xvdi", vm_xid = None):
|
def block_detach(vm, frontend = "xvdi", vm_xid = None):
|
||||||
# Get XID if not provided already
|
# Get XID if not provided already
|
||||||
@ -334,6 +372,121 @@ def block_detach(vm, frontend = "xvdi", vm_xid = None):
|
|||||||
xl_cmd = [ '/usr/sbin/xl', 'block-detach', str(vm_xid), str(frontend)]
|
xl_cmd = [ '/usr/sbin/xl', 'block-detach', str(vm_xid), str(frontend)]
|
||||||
subprocess.check_call(xl_cmd)
|
subprocess.check_call(xl_cmd)
|
||||||
|
|
||||||
|
def block_detach_all(vm, vm_xid = None):
|
||||||
|
""" Detach all non-system devices"""
|
||||||
|
# Get XID if not provided already
|
||||||
|
if vm_xid is None:
|
||||||
|
if not vm.is_running():
|
||||||
|
raise QubesException("VM %s not running" % vm.name)
|
||||||
|
# FIXME: potential race
|
||||||
|
vm_xid = vm.xid
|
||||||
|
|
||||||
|
xs_trans = xs.transaction_start()
|
||||||
|
devices = 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))
|
||||||
|
assert be_path is not None
|
||||||
|
be_params = xs.read(xs_trans, be_path + '/params')
|
||||||
|
if be_path.startswith('/local/domain/0/') and be_params is not None and be_params.startswith(qubes_base_dir):
|
||||||
|
# system disk
|
||||||
|
continue
|
||||||
|
devices_to_detach.append(devid)
|
||||||
|
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)
|
||||||
|
|
||||||
|
####### QubesWatch ######
|
||||||
|
|
||||||
|
def only_in_first_list(l1, l2):
|
||||||
|
ret=[]
|
||||||
|
for i in l1:
|
||||||
|
if not i in l2:
|
||||||
|
ret.append(i)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
class QubesWatch(object):
|
||||||
|
class WatchType(object):
|
||||||
|
def __init__(self, fn, param):
|
||||||
|
self.fn = fn
|
||||||
|
self.param = param
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.xs = xen.lowlevel.xs.xs()
|
||||||
|
self.watch_tokens_block = {}
|
||||||
|
self.watch_tokens_vbd = {}
|
||||||
|
self.block_callback = None
|
||||||
|
self.domain_callback = None
|
||||||
|
self.xs.watch('@introduceDomain', QubesWatch.WatchType(self.domain_list_changed, None))
|
||||||
|
self.xs.watch('@releaseDomain', QubesWatch.WatchType(self.domain_list_changed, None))
|
||||||
|
|
||||||
|
def setup_block_watch(self, callback):
|
||||||
|
old_block_callback = self.block_callback
|
||||||
|
self.block_callback = callback
|
||||||
|
if old_block_callback is not None and callback is None:
|
||||||
|
# remove watches
|
||||||
|
self.update_watches_vbd([])
|
||||||
|
self.update_watches_block([])
|
||||||
|
else:
|
||||||
|
# possibly add watches
|
||||||
|
self.domain_list_changed(None)
|
||||||
|
|
||||||
|
def setup_domain_watch(self, callback):
|
||||||
|
self.domain_callback = callback
|
||||||
|
|
||||||
|
def get_block_key(self, xid):
|
||||||
|
return '/local/domain/%s/qubes-block-devices' % xid
|
||||||
|
|
||||||
|
def get_vbd_key(self, xid):
|
||||||
|
return '/local/domain/%s/device/vbd' % xid
|
||||||
|
|
||||||
|
def update_watches_block(self, xid_list):
|
||||||
|
for i in only_in_first_list(xid_list, self.watch_tokens_block.keys()):
|
||||||
|
#new domain has been created
|
||||||
|
watch = QubesWatch.WatchType(self.block_callback, i)
|
||||||
|
self.watch_tokens_block[i] = watch
|
||||||
|
self.xs.watch(self.get_block_key(i), watch)
|
||||||
|
for i in only_in_first_list(self.watch_tokens_block.keys(), xid_list):
|
||||||
|
#domain destroyed
|
||||||
|
self.xs.unwatch(self.get_block_key(i), self.watch_tokens_block[i])
|
||||||
|
self.watch_tokens_block.pop(i)
|
||||||
|
|
||||||
|
def update_watches_vbd(self, xid_list):
|
||||||
|
for i in only_in_first_list(xid_list, self.watch_tokens_vbd.keys()):
|
||||||
|
#new domain has been created
|
||||||
|
watch = QubesWatch.WatchType(self.block_callback, i)
|
||||||
|
self.watch_tokens_vbd[i] = watch
|
||||||
|
self.xs.watch(self.get_vbd_key(i), watch)
|
||||||
|
for i in only_in_first_list(self.watch_tokens_vbd.keys(), xid_list):
|
||||||
|
#domain destroyed
|
||||||
|
self.xs.unwatch(self.get_vbd_key(i), self.watch_tokens_vbd[i])
|
||||||
|
self.watch_tokens_vbd.pop(i)
|
||||||
|
|
||||||
|
def domain_list_changed(self, param):
|
||||||
|
curr = self.xs.ls('', '/local/domain')
|
||||||
|
if curr == None:
|
||||||
|
return
|
||||||
|
if self.domain_callback:
|
||||||
|
self.domain_callback()
|
||||||
|
if self.block_callback:
|
||||||
|
self.update_watches_block(curr)
|
||||||
|
self.update_watches_vbd(curr)
|
||||||
|
|
||||||
|
def watch_single(self):
|
||||||
|
result = self.xs.read_watch()
|
||||||
|
token = result[1]
|
||||||
|
token.fn(token.param)
|
||||||
|
|
||||||
|
def watch_loop(self):
|
||||||
|
while True:
|
||||||
|
self.watch_single()
|
||||||
|
|
||||||
|
######## Backups #########
|
||||||
|
|
||||||
def get_disk_usage(file_or_dir):
|
def get_disk_usage(file_or_dir):
|
||||||
if not os.path.exists(file_or_dir):
|
if not os.path.exists(file_or_dir):
|
||||||
return 0
|
return 0
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
from qubes.qubes import QubesVmCollection
|
from qubes.qubes import QubesVmCollection, QubesException
|
||||||
from qubes.qubesutils import block_list,block_attach,block_detach,block_check_attached
|
from qubes.qubesutils import block_list,block_attach,block_detach,block_detach_all,block_check_attached
|
||||||
from qubes.qubesutils import kbytes_to_kmg, bytes_to_kmg
|
from qubes.qubesutils import kbytes_to_kmg, bytes_to_kmg
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -94,7 +94,11 @@ def main():
|
|||||||
else:
|
else:
|
||||||
kwargs['mode'] = dev['mode']
|
kwargs['mode'] = dev['mode']
|
||||||
kwargs['auto_detach'] = options.auto_detach
|
kwargs['auto_detach'] = options.auto_detach
|
||||||
block_attach(vm, backend_vm, dev['device'], **kwargs)
|
try:
|
||||||
|
block_attach(vm, backend_vm, dev['device'], **kwargs)
|
||||||
|
except QubesException as e:
|
||||||
|
print >> sys.stderr, "ERROR: %s" % str(e)
|
||||||
|
sys.exit(1)
|
||||||
elif options.do_detach:
|
elif options.do_detach:
|
||||||
if (len (args) < 1):
|
if (len (args) < 1):
|
||||||
parser.error ("You must provide device or vm name!")
|
parser.error ("You must provide device or vm name!")
|
||||||
@ -104,7 +108,9 @@ def main():
|
|||||||
kwargs = {}
|
kwargs = {}
|
||||||
if options.frontend:
|
if options.frontend:
|
||||||
kwargs['frontend'] = options.frontend
|
kwargs['frontend'] = options.frontend
|
||||||
block_detach(vm, **kwargs)
|
block_detach(vm, **kwargs)
|
||||||
|
else:
|
||||||
|
block_detach_all(vm)
|
||||||
else:
|
else:
|
||||||
# Maybe device?
|
# Maybe device?
|
||||||
dev_list = block_list()
|
dev_list = block_list()
|
||||||
|
@ -1 +1,2 @@
|
|||||||
modprobe evtchn 2>/dev/null || modprobe xen-evtchn
|
modprobe evtchn 2>/dev/null || modprobe xen-evtchn
|
||||||
|
modprobe xen-blkback 2> /dev/null || modprobe blkbk
|
||||||
|
Loading…
Reference in New Issue
Block a user