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)
def block_list(vm = None, system_disks = False): device_re = re.compile(r"^[a-z0-9]{1,12}$") # FIXME: any better idea of desc_re? desc_re = re.compile(r"^.{1,255}$") mode_re = re.compile(r"^[rw]$") vm_list = [] if vm is not None: if not vm.is_running(): return [] else: vm_list = [ str(vm.xid) ] else: vm_list = xs.ls('', '/local/domain') xs_trans = xs.transaction_start() 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) if vm_devices is None: continue for device in vm_devices: # Sanitize device name if not device_re.match(device): 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)) 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 continue if not device_size.isdigit(): print >> sys.stderr, "Invalid %s device size in VM '%s'" % (device, vm_name) continue if not desc_re.match(device_desc): print >> sys.stderr, "Invalid %s device desc in VM '%s'" % (device, vm_name) continue if not mode_re.match(device_mode): print >> sys.stderr, "Invalid %s device mode in VM '%s'" % (device, vm_name) continue # Check if we know major number for this device; attach will work without this, but detach and check_attached don't if block_name_to_majorminor(device) == (0, 0): print >> sys.stderr, "Unsupported device %s:%s" % (vm_name, device) continue if not system_disks: if xid == '0' and device_desc.startswith(qubes_base_dir): continue visible_name = "%s:%s" % (vm_name, device) devices_list[visible_name] = {"name": visible_name, "xid":int(xid), "vm": vm_name, "device":device, "size":int(device_size), "desc":device_desc, "mode":device_mode} 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) if vm_list is None: xs.transaction_end(xs_trans) return None device_majorminor = None try: device_majorminor = block_name_to_majorminor(device) except: # 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)): (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)) if phys_device and phys_device.find(':'): (tmp_major, tmp_minor) = phys_device.split(":") tmp_major = int(tmp_major, 16) tmp_minor = int(tmp_minor, 16) else: # perhaps not ready yet - check params if not dev_params: # Skip not-phy devices continue elif not dev_params.startswith('/dev/'): # will compare params directly pass else: (tmp_major, tmp_minor) = block_name_to_majorminor(dev_params.lstrip('/dev/')) if (device_majorminor and (tmp_major, tmp_minor) == device_majorminor) or \ (device_majorminor is None and dev_params == device): vm_name = xl_ctx.domid_to_name(int(vm_xid)) 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} xs.transaction_end(xs_trans) return None
def block_detach(vm, frontend = "xvdi", vm_xid = None): # 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 # 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': # Do nothing - device already detached return xl_cmd = [ '/usr/sbin/xl', 'block-detach', str(vm_xid), str(frontend)] subprocess.check_call(xl_cmd)
def block_attach(vm, backend_vm, device, frontend=None, mode="w", auto_detach=False, wait=True): if not vm.is_running(): raise QubesException("VM %s not running" % vm.name) if not backend_vm.is_running(): raise QubesException("VM %s not running" % backend_vm.name) if frontend is None: frontend = block_find_unused_frontend(vm) if frontend is None: 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': raise QubesException("Frontend %s busy in VM %s, detach it first" % (frontend, vm.name)) # Check if this device is attached to some domain attached_vm = block_check_attached(backend_vm, device) if attached_vm: if auto_detach: block_detach(None, attached_vm['devid'], vm_xid=attached_vm['xid']) else: raise QubesException("Device %s from %s already connected to VM %s as %s" % (device, backend_vm.name, attached_vm['vm'], attached_vm['frontend'])) if device.startswith('/'): backend_dev = 'script:file:' + device else: backend_dev = 'phy:/dev/' + device xl_cmd = [ '/usr/sbin/xl', 'block-attach', vm.name, backend_dev, frontend, mode, str(backend_vm.xid) ] 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 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') 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")