Merge branch 'qdb-watch'
* qdb-watch: tests: add qdb_watch test ext/block: make use of QubesDB watch vm: add API for watching changes in QubesDB vm: optimize imports api/admin: don't send internal events in admin.Events Add explanation why admin.vm.volume.Import is a custom script Follow change of qubesdb path return type Rename vm.qdb to vm.untrusted_qdb
This commit is contained in:
commit
639fa26079
@ -1,4 +1,27 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This Admin API call is implemented as a custom script, instead of dumb
|
||||
# passthrough to qubesd because it may get huge amount of data (whole root.img
|
||||
# for example). qubesd cannot handle it because:
|
||||
# 1. It loads the whole payload into memory, before even start looking at it
|
||||
# (and later, do not allow to modify/append it).
|
||||
# 2. There is 64kB limit on payload size that qubesd can handle (because of
|
||||
# point 1).
|
||||
# 3. Performance reasons (qubesd is not optimized for performance, passing
|
||||
# such large data stream through it would take ages).
|
||||
#
|
||||
# The whole admin.vm.volume.Import consists of:
|
||||
# 1. Permissions checks, getting a path from appropriate storage pool (done
|
||||
# by qubesd)
|
||||
# 2. Actual data import (done by this script, using dd)
|
||||
# 3. Report final result, produce final response to the caller (done by
|
||||
# qubesd)
|
||||
#
|
||||
# This way we do not pass all the data through qubesd, but still can
|
||||
# control the process from there in a meaningful way. Note that the last
|
||||
# part (second call to qubesd) may perform all kind of verification (like
|
||||
# a signature check on the data, or so) and can also prevent VM from
|
||||
# starting (hooking also domain-pre-start event) from not verified image.
|
||||
|
||||
set -e
|
||||
|
||||
|
@ -51,8 +51,18 @@ class QubesMgmtEventsDispatcher(object):
|
||||
self.send_event = send_event
|
||||
|
||||
def vm_handler(self, subject, event, **kwargs):
|
||||
# do not send internal events
|
||||
if event.startswith('admin-permission:'):
|
||||
return
|
||||
if event.startswith('device-get:'):
|
||||
return
|
||||
if event.startswith('device-list:'):
|
||||
return
|
||||
if event.startswith('device-list-attached:'):
|
||||
return
|
||||
if event in ('domain-is-fully-usable',):
|
||||
return
|
||||
|
||||
if not list(qubes.api.apply_filters([(subject, event, kwargs)],
|
||||
self.filters)):
|
||||
return
|
||||
|
@ -51,10 +51,9 @@ class QubesMiscAPI(qubes.api.AbstractQubesAPI):
|
||||
|
||||
prefix = '/features-request/'
|
||||
|
||||
keys = [key.decode('ascii', errors='strict')
|
||||
for key in self.src.qdb.list(prefix)]
|
||||
keys = self.src.untrusted_qdb.list(prefix)
|
||||
untrusted_features = {key[len(prefix):]:
|
||||
self.src.qdb.read(key).decode('ascii', errors='strict')
|
||||
self.src.untrusted_qdb.read(key).decode('ascii', errors='strict')
|
||||
for key in keys}
|
||||
|
||||
safe_set = string.ascii_letters + string.digits
|
||||
@ -79,7 +78,8 @@ class QubesMiscAPI(qubes.api.AbstractQubesAPI):
|
||||
safe_set = string.ascii_letters + string.digits
|
||||
expected_features = ('qrexec', 'gui', 'default-user')
|
||||
for feature in expected_features:
|
||||
untrusted_value = self.src.qdb.read('/qubes-tools/' + feature)
|
||||
untrusted_value = self.src.untrusted_qdb.read(
|
||||
'/qubes-tools/' + feature)
|
||||
if untrusted_value:
|
||||
untrusted_value = untrusted_value.decode('ascii',
|
||||
errors='strict')
|
||||
|
@ -44,11 +44,17 @@ Such extension should provide:
|
||||
domain of given identifier
|
||||
- handle `device-list-attached:class` event - list currently attached
|
||||
devices to this domain
|
||||
- fire `device-list-change:class` event when device list change is detected
|
||||
(new/removed device)
|
||||
|
||||
Note that device-listing event handlers can not be asynchronous. This for
|
||||
example means you can not call qrexec service there. This is intentional to
|
||||
keep device listing operation cheap. You need to design the extension to take
|
||||
this into account (for example by using QubesDB).
|
||||
|
||||
Extension may use QubesDB watch API (QubesVM.watch_qdb_path(path), then handle
|
||||
`domain-qdb-change:path`) to detect changes and fire
|
||||
`device-list-change:class` event.
|
||||
'''
|
||||
import asyncio
|
||||
|
||||
|
@ -56,7 +56,7 @@ class BlockDevice(qubes.devices.DeviceInfo):
|
||||
return self.ident
|
||||
safe_set = {ord(c) for c in
|
||||
string.ascii_letters + string.digits + '()+,-.:=_/ '}
|
||||
untrusted_desc = self.backend_domain.qdb.read(
|
||||
untrusted_desc = self.backend_domain.untrusted_qdb.read(
|
||||
'/qubes-block-devices/{}/desc'.format(self.ident))
|
||||
desc = ''.join((chr(c) if c in safe_set else '_')
|
||||
for c in untrusted_desc)
|
||||
@ -69,7 +69,7 @@ class BlockDevice(qubes.devices.DeviceInfo):
|
||||
if self._mode is None:
|
||||
if not self.backend_domain.is_running():
|
||||
return 'w'
|
||||
untrusted_mode = self.backend_domain.qdb.read(
|
||||
untrusted_mode = self.backend_domain.untrusted_qdb.read(
|
||||
'/qubes-block-devices/{}/mode'.format(self.ident))
|
||||
if untrusted_mode is None:
|
||||
self._mode = 'w'
|
||||
@ -87,7 +87,7 @@ class BlockDevice(qubes.devices.DeviceInfo):
|
||||
if self._size is None:
|
||||
if not self.backend_domain.is_running():
|
||||
return None
|
||||
untrusted_size = self.backend_domain.qdb.read(
|
||||
untrusted_size = self.backend_domain.untrusted_qdb.read(
|
||||
'/qubes-block-devices/{}/size'.format(self.ident))
|
||||
if untrusted_size is None:
|
||||
self._size = 0
|
||||
@ -106,6 +106,18 @@ class BlockDevice(qubes.devices.DeviceInfo):
|
||||
|
||||
|
||||
class BlockDeviceExtension(qubes.ext.Extension):
|
||||
@qubes.ext.handler('domain-init', 'domain-load')
|
||||
def on_domain_init_load(self, vm, event):
|
||||
'''Initialize watching for changes'''
|
||||
# pylint: disable=unused-argument,no-self-use
|
||||
vm.watch_qdb_path('/qubes-block-devices')
|
||||
|
||||
@qubes.ext.handler('domain-qdb-change:/qubes-block-devices')
|
||||
def on_qdb_change(self, vm, event, path):
|
||||
'''A change in QubesDB means a change in device list'''
|
||||
# pylint: disable=unused-argument,no-self-use
|
||||
vm.fire_event('device-list-change:block')
|
||||
|
||||
def device_get(self, vm, ident):
|
||||
# pylint: disable=no-self-use
|
||||
'''Read information about device from QubesDB
|
||||
@ -114,7 +126,7 @@ class BlockDeviceExtension(qubes.ext.Extension):
|
||||
:param ident: device identifier
|
||||
:returns BlockDevice'''
|
||||
|
||||
untrusted_qubes_device_attrs = vm.qdb.list(
|
||||
untrusted_qubes_device_attrs = vm.untrusted_qdb.list(
|
||||
'/qubes-block-devices/{}/'.format(ident))
|
||||
if not untrusted_qubes_device_attrs:
|
||||
return None
|
||||
@ -124,12 +136,11 @@ class BlockDeviceExtension(qubes.ext.Extension):
|
||||
def on_device_list_block(self, vm, event):
|
||||
# pylint: disable=unused-argument,no-self-use
|
||||
|
||||
safe_set = {ord(c) for c in
|
||||
string.ascii_letters + string.digits}
|
||||
safe_set = string.ascii_letters + string.digits
|
||||
if not vm.is_running():
|
||||
return
|
||||
untrusted_qubes_devices = vm.qdb.list('/qubes-block-devices/')
|
||||
untrusted_idents = set(untrusted_path.split(b'/', 3)[2]
|
||||
untrusted_qubes_devices = vm.untrusted_qdb.list('/qubes-block-devices/')
|
||||
untrusted_idents = set(untrusted_path.split('/', 3)[2]
|
||||
for untrusted_path in untrusted_qubes_devices)
|
||||
for untrusted_ident in untrusted_idents:
|
||||
if not all(c in safe_set for c in untrusted_ident):
|
||||
@ -138,7 +149,7 @@ class BlockDeviceExtension(qubes.ext.Extension):
|
||||
vm.log.warning(msg % vm.name)
|
||||
continue
|
||||
|
||||
ident = untrusted_ident.decode('ascii', errors='strict')
|
||||
ident = untrusted_ident
|
||||
|
||||
device_info = self.device_get(vm, ident)
|
||||
if device_info:
|
||||
|
@ -60,9 +60,9 @@ class R3Compatibility(qubes.ext.Extension):
|
||||
vmtype = 'NetVM'
|
||||
else:
|
||||
vmtype = 'AppVM'
|
||||
vm.qdb.write('/qubes-vm-type', vmtype)
|
||||
vm.untrusted_qdb.write('/qubes-vm-type', vmtype)
|
||||
|
||||
vm.qdb.write("/qubes-iptables-error", '')
|
||||
vm.untrusted_qdb.write("/qubes-iptables-error", '')
|
||||
self.write_iptables_qubesdb_entry(vm)
|
||||
|
||||
self.write_services(vm)
|
||||
@ -81,7 +81,7 @@ class R3Compatibility(qubes.ext.Extension):
|
||||
|
||||
def write_iptables_qubesdb_entry(self, firewallvm):
|
||||
# pylint: disable=no-self-use
|
||||
firewallvm.qdb.rm("/qubes-iptables-domainrules/")
|
||||
firewallvm.untrusted_qdb.rm("/qubes-iptables-domainrules/")
|
||||
iptables = "# Generated by Qubes Core on {0}\n".format(
|
||||
datetime.datetime.now().ctime())
|
||||
iptables += "*filter\n"
|
||||
@ -102,7 +102,7 @@ class R3Compatibility(qubes.ext.Extension):
|
||||
# Deny inter-VMs networking
|
||||
iptables += "-A FORWARD -i vif+ -o vif+ -j DROP\n"
|
||||
iptables += "COMMIT\n"
|
||||
firewallvm.qdb.write("/qubes-iptables-header", iptables)
|
||||
firewallvm.untrusted_qdb.write("/qubes-iptables-header", iptables)
|
||||
|
||||
for vm in firewallvm.connected_vms:
|
||||
iptables = "*filter\n"
|
||||
@ -154,11 +154,12 @@ class R3Compatibility(qubes.ext.Extension):
|
||||
iptables += '-A FORWARD -s {0} -j {1}\n'.format(ip,
|
||||
str(conf.policy).upper())
|
||||
iptables += 'COMMIT\n'
|
||||
firewallvm.qdb.write('/qubes-iptables-domainrules/' + str(xid),
|
||||
firewallvm.untrusted_qdb.write(
|
||||
'/qubes-iptables-domainrules/' + str(xid),
|
||||
iptables)
|
||||
# no need for ending -A FORWARD -j DROP, cause default action is DROP
|
||||
|
||||
firewallvm.qdb.write('/qubes-iptables', 'reload')
|
||||
firewallvm.untrusted_qdb.write('/qubes-iptables', 'reload')
|
||||
|
||||
def write_services(self, vm):
|
||||
for feature, value in vm.features.items():
|
||||
@ -166,8 +167,9 @@ class R3Compatibility(qubes.ext.Extension):
|
||||
if service is None:
|
||||
continue
|
||||
# forcefully convert to '0' or '1'
|
||||
vm.qdb.write('/qubes-service/{}'.format(service),
|
||||
vm.untrusted_qdb.write('/qubes-service/{}'.format(service),
|
||||
str(int(bool(value))))
|
||||
if 'updates-proxy-setup' in vm.features.keys():
|
||||
vm.qdb.write('/qubes-service/{}'.format('yum-proxy-setup'),
|
||||
vm.untrusted_qdb.write(
|
||||
'/qubes-service/{}'.format('yum-proxy-setup'),
|
||||
str(int(bool(vm.features['updates-proxy-setup']))))
|
||||
|
@ -419,7 +419,8 @@ class Storage(object):
|
||||
# trigger watches to update device status
|
||||
# FIXME: this should be removed once libvirt will report such
|
||||
# events itself
|
||||
# self.vm.qdb.write('/qubes-block-devices', '') ← do we need this?
|
||||
# self.vm.untrusted_qdb.write('/qubes-block-devices', '')
|
||||
# ← do we need this?
|
||||
|
||||
def _is_already_attached(self, volume):
|
||||
''' Checks if the given volume is already attached '''
|
||||
|
@ -37,9 +37,10 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
|
||||
|
||||
def configure_qdb(self, entries):
|
||||
self.src.configure_mock(**{
|
||||
'qdb.read.side_effect': (lambda path: entries.get(path, None)),
|
||||
'qdb.list.side_effect': (lambda path:
|
||||
sorted(map(str.encode, entries.keys()))),
|
||||
'untrusted_qdb.read.side_effect': (
|
||||
lambda path: entries.get(path, None)),
|
||||
'untrusted_qdb.list.side_effect': (
|
||||
lambda path: sorted(entries.keys())),
|
||||
})
|
||||
|
||||
def call_mgmt_func(self, method, arg=b'', payload=b''):
|
||||
@ -64,10 +65,10 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
|
||||
mock.call.save()
|
||||
])
|
||||
self.assertEqual(self.src.mock_calls, [
|
||||
mock.call.qdb.list('/features-request/'),
|
||||
mock.call.qdb.read('/features-request/feature1'),
|
||||
mock.call.qdb.read('/features-request/feature2'),
|
||||
mock.call.qdb.read('/features-request/feature3'),
|
||||
mock.call.untrusted_qdb.list('/features-request/'),
|
||||
mock.call.untrusted_qdb.read('/features-request/feature1'),
|
||||
mock.call.untrusted_qdb.read('/features-request/feature2'),
|
||||
mock.call.untrusted_qdb.read('/features-request/feature3'),
|
||||
mock.call.fire_event('features-request', untrusted_features={
|
||||
'feature1': '1', 'feature2': '', 'feature3': 'other'})
|
||||
])
|
||||
@ -80,7 +81,7 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
|
||||
mock.call.save()
|
||||
])
|
||||
self.assertEqual(self.src.mock_calls, [
|
||||
mock.call.qdb.list('/features-request/'),
|
||||
mock.call.untrusted_qdb.list('/features-request/'),
|
||||
mock.call.fire_event('features-request', untrusted_features={})
|
||||
])
|
||||
|
||||
@ -93,8 +94,8 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
|
||||
self.call_mgmt_func(b'qubes.FeaturesRequest')
|
||||
self.assertEqual(self.app.mock_calls, [])
|
||||
self.assertEqual(self.src.mock_calls, [
|
||||
mock.call.qdb.list('/features-request/'),
|
||||
mock.call.qdb.read('/features-request/feature1'),
|
||||
mock.call.untrusted_qdb.list('/features-request/'),
|
||||
mock.call.untrusted_qdb.read('/features-request/feature1'),
|
||||
])
|
||||
|
||||
def test_003_features_request_invalid2(self):
|
||||
@ -106,8 +107,8 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
|
||||
self.call_mgmt_func(b'qubes.FeaturesRequest')
|
||||
self.assertEqual(self.app.mock_calls, [])
|
||||
self.assertEqual(self.src.mock_calls, [
|
||||
mock.call.qdb.list('/features-request/'),
|
||||
mock.call.qdb.read('/features-request/feature1'),
|
||||
mock.call.untrusted_qdb.list('/features-request/'),
|
||||
mock.call.untrusted_qdb.read('/features-request/feature1'),
|
||||
])
|
||||
|
||||
def test_010_notify_tools(self):
|
||||
@ -125,9 +126,9 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
|
||||
mock.call.save()
|
||||
])
|
||||
self.assertEqual(self.src.mock_calls, [
|
||||
mock.call.qdb.read('/qubes-tools/qrexec'),
|
||||
mock.call.qdb.read('/qubes-tools/gui'),
|
||||
mock.call.qdb.read('/qubes-tools/default-user'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/qrexec'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/gui'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/default-user'),
|
||||
mock.call.fire_event('features-request', untrusted_features={
|
||||
'gui': '1',
|
||||
'default-user': 'user',
|
||||
@ -146,9 +147,9 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
|
||||
response = self.call_mgmt_func(b'qubes.NotifyTools')
|
||||
self.assertIsNone(response)
|
||||
self.assertEqual(self.src.mock_calls, [
|
||||
mock.call.qdb.read('/qubes-tools/qrexec'),
|
||||
mock.call.qdb.read('/qubes-tools/gui'),
|
||||
mock.call.qdb.read('/qubes-tools/default-user'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/qrexec'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/gui'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/default-user'),
|
||||
mock.call.fire_event('features-request', untrusted_features={
|
||||
'gui': '1',
|
||||
'default-user': 'user',
|
||||
@ -169,7 +170,7 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
|
||||
self.call_mgmt_func(b'qubes.NotifyTools')
|
||||
self.assertEqual(self.app.mock_calls, [])
|
||||
self.assertEqual(self.src.mock_calls, [
|
||||
mock.call.qdb.read('/qubes-tools/qrexec'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/qrexec'),
|
||||
])
|
||||
|
||||
def test_016_notify_tools_invalid_value_gui(self):
|
||||
@ -185,8 +186,8 @@ class TC_00_API_Misc(qubes.tests.QubesTestCase):
|
||||
self.call_mgmt_func(b'qubes.NotifyTools')
|
||||
self.assertEqual(self.app.mock_calls, [])
|
||||
self.assertEqual(self.src.mock_calls, [
|
||||
mock.call.qdb.read('/qubes-tools/qrexec'),
|
||||
mock.call.qdb.read('/qubes-tools/gui'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/qrexec'),
|
||||
mock.call.untrusted_qdb.read('/qubes-tools/gui'),
|
||||
])
|
||||
|
||||
def test_020_notify_updates_standalone(self):
|
||||
|
@ -94,13 +94,9 @@ class TestQubesDB(object):
|
||||
self._data = data
|
||||
|
||||
def read(self, key):
|
||||
if isinstance(key, str):
|
||||
key = key.encode()
|
||||
return self._data.get(key, None)
|
||||
|
||||
def list(self, prefix):
|
||||
if isinstance(prefix, str):
|
||||
prefix = prefix.encode()
|
||||
return [key for key in self._data if key.startswith(prefix)]
|
||||
|
||||
|
||||
@ -120,7 +116,7 @@ class TestApp(object):
|
||||
class TestVM(object):
|
||||
def __init__(self, qdb, domain_xml=None, running=True, name='test-vm'):
|
||||
self.name = name
|
||||
self.qdb = TestQubesDB(qdb)
|
||||
self.untrusted_qdb = TestQubesDB(qdb)
|
||||
self.libvirt_domain = mock.Mock()
|
||||
self.is_running = lambda: running
|
||||
self.log = mock.Mock()
|
||||
@ -143,10 +139,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_000_device_get(self):
|
||||
vm = TestVM({
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'w',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'w',
|
||||
})
|
||||
device_info = self.ext.device_get(vm, 'sda')
|
||||
self.assertIsInstance(device_info, qubes.ext.block.BlockDevice)
|
||||
@ -161,10 +157,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_001_device_get_other_node(self):
|
||||
vm = TestVM({
|
||||
b'/qubes-block-devices/mapper_dmroot': b'',
|
||||
b'/qubes-block-devices/mapper_dmroot/desc': b'Test device',
|
||||
b'/qubes-block-devices/mapper_dmroot/size': b'1024000',
|
||||
b'/qubes-block-devices/mapper_dmroot/mode': b'w',
|
||||
'/qubes-block-devices/mapper_dmroot': b'',
|
||||
'/qubes-block-devices/mapper_dmroot/desc': b'Test device',
|
||||
'/qubes-block-devices/mapper_dmroot/size': b'1024000',
|
||||
'/qubes-block-devices/mapper_dmroot/mode': b'w',
|
||||
})
|
||||
device_info = self.ext.device_get(vm, 'mapper_dmroot')
|
||||
self.assertIsInstance(device_info, qubes.ext.block.BlockDevice)
|
||||
@ -179,20 +175,20 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_002_device_get_invalid_desc(self):
|
||||
vm = TestVM({
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device<>za\xc4\x87abc',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'w',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device<>za\xc4\x87abc',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'w',
|
||||
})
|
||||
device_info = self.ext.device_get(vm, 'sda')
|
||||
self.assertEqual(device_info.description, 'Test device__za__abc')
|
||||
|
||||
def test_003_device_get_invalid_size(self):
|
||||
vm = TestVM({
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000abc',
|
||||
b'/qubes-block-devices/sda/mode': b'w',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000abc',
|
||||
'/qubes-block-devices/sda/mode': b'w',
|
||||
})
|
||||
device_info = self.ext.device_get(vm, 'sda')
|
||||
self.assertEqual(device_info.size, 0)
|
||||
@ -200,10 +196,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_004_device_get_invalid_mode(self):
|
||||
vm = TestVM({
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'abc',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'abc',
|
||||
})
|
||||
device_info = self.ext.device_get(vm, 'sda')
|
||||
self.assertEqual(device_info.mode, 'w')
|
||||
@ -211,24 +207,24 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_005_device_get_none(self):
|
||||
vm = TestVM({
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'w',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'w',
|
||||
})
|
||||
device_info = self.ext.device_get(vm, 'sdb')
|
||||
self.assertIsNone(device_info)
|
||||
|
||||
def test_010_devices_list(self):
|
||||
vm = TestVM({
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'w',
|
||||
b'/qubes-block-devices/sdb': b'',
|
||||
b'/qubes-block-devices/sdb/desc': b'Test device2',
|
||||
b'/qubes-block-devices/sdb/size': b'2048000',
|
||||
b'/qubes-block-devices/sdb/mode': b'r',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'w',
|
||||
'/qubes-block-devices/sdb': b'',
|
||||
'/qubes-block-devices/sdb/desc': b'Test device2',
|
||||
'/qubes-block-devices/sdb/size': b'2048000',
|
||||
'/qubes-block-devices/sdb/mode': b'r',
|
||||
})
|
||||
devices = sorted(list(self.ext.on_device_list_block(vm, '')))
|
||||
self.assertEqual(len(devices), 2)
|
||||
@ -250,9 +246,9 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_012_devices_list_invalid_ident(self):
|
||||
vm = TestVM({
|
||||
b'/qubes-block-devices/invalid ident': b'',
|
||||
b'/qubes-block-devices/invalid+ident': b'',
|
||||
b'/qubes-block-devices/invalid#': b'',
|
||||
'/qubes-block-devices/invalid ident': b'',
|
||||
'/qubes-block-devices/invalid+ident': b'',
|
||||
'/qubes-block-devices/invalid#': b'',
|
||||
})
|
||||
devices = sorted(list(self.ext.on_device_list_block(vm, '')))
|
||||
self.assertEqual(len(devices), 0)
|
||||
@ -334,10 +330,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_040_attach(self):
|
||||
back_vm = TestVM(name='sys-usb', qdb={
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'w',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'w',
|
||||
})
|
||||
vm = TestVM({}, domain_xml=domain_xml_template.format(''))
|
||||
dev = qubes.ext.block.BlockDevice(back_vm, 'sda')
|
||||
@ -353,10 +349,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_041_attach_frontend(self):
|
||||
back_vm = TestVM(name='sys-usb', qdb={
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'w',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'w',
|
||||
})
|
||||
vm = TestVM({}, domain_xml=domain_xml_template.format(''))
|
||||
dev = qubes.ext.block.BlockDevice(back_vm, 'sda')
|
||||
@ -373,10 +369,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_042_attach_read_only(self):
|
||||
back_vm = TestVM(name='sys-usb', qdb={
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'w',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'w',
|
||||
})
|
||||
vm = TestVM({}, domain_xml=domain_xml_template.format(''))
|
||||
dev = qubes.ext.block.BlockDevice(back_vm, 'sda')
|
||||
@ -394,10 +390,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_043_attach_invalid_option(self):
|
||||
back_vm = TestVM(name='sys-usb', qdb={
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'w',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'w',
|
||||
})
|
||||
vm = TestVM({}, domain_xml=domain_xml_template.format(''))
|
||||
dev = qubes.ext.block.BlockDevice(back_vm, 'sda')
|
||||
@ -408,10 +404,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_044_attach_invalid_option2(self):
|
||||
back_vm = TestVM(name='sys-usb', qdb={
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'w',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'w',
|
||||
})
|
||||
vm = TestVM({}, domain_xml=domain_xml_template.format(''))
|
||||
dev = qubes.ext.block.BlockDevice(back_vm, 'sda')
|
||||
@ -422,10 +418,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_045_attach_backend_not_running(self):
|
||||
back_vm = TestVM(name='sys-usb', running=False, qdb={
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'w',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'w',
|
||||
})
|
||||
vm = TestVM({}, domain_xml=domain_xml_template.format(''))
|
||||
dev = qubes.ext.block.BlockDevice(back_vm, 'sda')
|
||||
@ -435,10 +431,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_046_attach_ro_dev_rw(self):
|
||||
back_vm = TestVM(name='sys-usb', qdb={
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'r',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'r',
|
||||
})
|
||||
vm = TestVM({}, domain_xml=domain_xml_template.format(''))
|
||||
dev = qubes.ext.block.BlockDevice(back_vm, 'sda')
|
||||
@ -449,10 +445,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_047_attach_read_only_auto(self):
|
||||
back_vm = TestVM(name='sys-usb', qdb={
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'r',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'r',
|
||||
})
|
||||
vm = TestVM({}, domain_xml=domain_xml_template.format(''))
|
||||
dev = qubes.ext.block.BlockDevice(back_vm, 'sda')
|
||||
@ -469,10 +465,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_050_detach(self):
|
||||
back_vm = TestVM(name='sys-usb', qdb={
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'r',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'r',
|
||||
})
|
||||
device_xml = (
|
||||
'<disk type="block" device="disk">\n'
|
||||
@ -491,10 +487,10 @@ class TC_00_Block(qubes.tests.QubesTestCase):
|
||||
|
||||
def test_051_detach_not_attached(self):
|
||||
back_vm = TestVM(name='sys-usb', qdb={
|
||||
b'/qubes-block-devices/sda': b'',
|
||||
b'/qubes-block-devices/sda/desc': b'Test device',
|
||||
b'/qubes-block-devices/sda/size': b'1024000',
|
||||
b'/qubes-block-devices/sda/mode': b'r',
|
||||
'/qubes-block-devices/sda': b'',
|
||||
'/qubes-block-devices/sda/desc': b'Test device',
|
||||
'/qubes-block-devices/sda/size': b'1024000',
|
||||
'/qubes-block-devices/sda/mode': b'r',
|
||||
})
|
||||
device_xml = (
|
||||
'<disk type="block" device="disk">\n'
|
||||
|
@ -65,6 +65,20 @@ class TC_00_Basic(qubes.tests.SystemTestCase):
|
||||
with self.assertNotRaises(qubes.exc.QubesException):
|
||||
vm.storage.verify()
|
||||
|
||||
def test_040_qdb_watch(self):
|
||||
flag = set()
|
||||
|
||||
def handler(vm, event, path):
|
||||
if path == '/test-watch-path':
|
||||
flag.add(True)
|
||||
|
||||
vm = self.app.domains[0]
|
||||
vm.watch_qdb_path('/test-watch-path')
|
||||
vm.add_handler('domain-qdb-change:/test-watch-path', handler)
|
||||
self.assertFalse(flag)
|
||||
vm.untrusted_qdb.write('/test-watch-path', 'test-value')
|
||||
self.loop.run_until_complete(asyncio.sleep(0.1))
|
||||
self.assertTrue(flag)
|
||||
|
||||
class TC_01_Properties(qubes.tests.SystemTestCase):
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
|
@ -24,14 +24,9 @@
|
||||
'''Qubes Virtual Machines
|
||||
|
||||
'''
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import asyncio
|
||||
import re
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import xml.parsers.expat
|
||||
|
||||
import lxml.etree
|
||||
|
||||
@ -271,6 +266,8 @@ class BaseVM(qubes.PropertyHolder):
|
||||
# self.app must be set before super().__init__, because some property
|
||||
# setters need working .app attribute
|
||||
#: mother :py:class:`qubes.Qubes` object
|
||||
self._qdb_watch_paths = set()
|
||||
self._qdb_connection_watch = None
|
||||
self.app = app
|
||||
|
||||
super(BaseVM, self).__init__(xml, **kwargs)
|
||||
@ -393,6 +390,55 @@ class BaseVM(qubes.PropertyHolder):
|
||||
]).render(vm=self)
|
||||
return domain_config
|
||||
|
||||
def watch_qdb_path(self, path):
|
||||
'''Add a QubesDB path to be watched.
|
||||
|
||||
Each change to the path will cause `domain-qdb-change:path` event to be
|
||||
fired.
|
||||
You can call this method for example in response to
|
||||
`domain-init` and `domain-load` events.
|
||||
'''
|
||||
|
||||
if path not in self._qdb_watch_paths:
|
||||
self._qdb_watch_paths.add(path)
|
||||
if self._qdb_connection_watch:
|
||||
self._qdb_connection_watch.watch(path)
|
||||
|
||||
def _qdb_watch_reader(self, loop):
|
||||
'''Callback when self._qdb_connection_watch.watch_fd() FD is
|
||||
readable.
|
||||
|
||||
Read reported event (watched path change) and fire appropriate event.
|
||||
'''
|
||||
import qubesdb # pylint: disable=import-error
|
||||
try:
|
||||
path = self._qdb_connection_watch.read_watch()
|
||||
for watched_path in self._qdb_watch_paths:
|
||||
if watched_path == path or (
|
||||
watched_path.endswith('/') and
|
||||
path.startswith(watched_path)):
|
||||
self.fire_event('domain-qdb-change:' + watched_path,
|
||||
path=path)
|
||||
except qubesdb.DisconnectedError:
|
||||
loop.remove_reader(self._qdb_connection_watch.watch_fd())
|
||||
self._qdb_connection_watch.close()
|
||||
self._qdb_connection_watch = None
|
||||
|
||||
def start_qdb_watch(self, name, loop=None):
|
||||
'''Start watching QubesDB
|
||||
|
||||
Calling this method in appropriate time is responsibility of child
|
||||
class.
|
||||
'''
|
||||
import qubesdb # pylint: disable=import-error
|
||||
self._qdb_connection_watch = qubesdb.QubesDB(name)
|
||||
if loop is None:
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.add_reader(self._qdb_connection_watch.watch_fd(),
|
||||
self._qdb_watch_reader, loop)
|
||||
for path in self._qdb_watch_paths:
|
||||
self._qdb_connection_watch.watch(path)
|
||||
|
||||
|
||||
class VMProperty(qubes.property):
|
||||
'''Property that is referring to a VM
|
||||
|
@ -55,6 +55,9 @@ class AdminVM(qubes.vm.BaseVM):
|
||||
self._qdb_connection = None
|
||||
self._libvirt_domain = None
|
||||
|
||||
if not self.app.vmm.offline_mode:
|
||||
self.start_qdb_watch('dom0')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@ -167,7 +170,7 @@ class AdminVM(qubes.vm.BaseVM):
|
||||
return None
|
||||
|
||||
@property
|
||||
def qdb(self):
|
||||
def untrusted_qdb(self):
|
||||
'''QubesDB handle for this domain.'''
|
||||
if self._qdb_connection is None:
|
||||
import qubesdb # pylint: disable=import-error
|
||||
|
@ -291,12 +291,12 @@ class NetVMMixin(qubes.events.Emitter):
|
||||
base_dir = '/qubes-firewall/' + vm.ip + '/'
|
||||
# remove old entries if any (but don't touch base empty entry - it
|
||||
# would trigger reload right away
|
||||
self.qdb.rm(base_dir)
|
||||
self.untrusted_qdb.rm(base_dir)
|
||||
# write new rules
|
||||
for key, value in vm.firewall.qdb_entries(addr_family=4).items():
|
||||
self.qdb.write(base_dir + key, value)
|
||||
self.untrusted_qdb.write(base_dir + key, value)
|
||||
# signal its done
|
||||
self.qdb.write(base_dir[:-1], '')
|
||||
self.untrusted_qdb.write(base_dir[:-1], '')
|
||||
|
||||
def set_mapped_ip_info_for_vm(self, vm):
|
||||
'''
|
||||
@ -307,14 +307,15 @@ class NetVMMixin(qubes.events.Emitter):
|
||||
# add info about remapped IPs (VM IP hidden from the VM itself)
|
||||
mapped_ip_base = '/mapped-ip/{}'.format(vm.ip)
|
||||
if vm.visible_ip:
|
||||
self.qdb.write(mapped_ip_base + '/visible-ip', vm.visible_ip)
|
||||
self.untrusted_qdb.write(mapped_ip_base + '/visible-ip',
|
||||
vm.visible_ip)
|
||||
else:
|
||||
self.qdb.rm(mapped_ip_base + '/visible-ip')
|
||||
self.untrusted_qdb.rm(mapped_ip_base + '/visible-ip')
|
||||
if vm.visible_gateway:
|
||||
self.qdb.write(mapped_ip_base + '/visible-gateway',
|
||||
self.untrusted_qdb.write(mapped_ip_base + '/visible-gateway',
|
||||
vm.visible_gateway)
|
||||
else:
|
||||
self.qdb.rm(mapped_ip_base + '/visible-gateway')
|
||||
self.untrusted_qdb.rm(mapped_ip_base + '/visible-gateway')
|
||||
|
||||
|
||||
@qubes.events.handler('property-del:netvm')
|
||||
|
@ -26,6 +26,8 @@ from __future__ import absolute_import
|
||||
import asyncio
|
||||
import base64
|
||||
import datetime
|
||||
import errno
|
||||
import grp
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
@ -34,11 +36,8 @@ import subprocess
|
||||
import uuid
|
||||
import warnings
|
||||
|
||||
import grp
|
||||
|
||||
import errno
|
||||
import lxml
|
||||
import libvirt # pylint: disable=import-error
|
||||
import lxml
|
||||
|
||||
import qubes
|
||||
import qubes.config
|
||||
@ -249,6 +248,18 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
||||
|
||||
This event is a good place to add your custom entries to the qdb.
|
||||
|
||||
.. event:: domain-qdb-change:watched-path (subject, event, path)
|
||||
|
||||
Fired when watched QubesDB entry is changed. See
|
||||
:py:meth:`watch_qdb_path`. *watched-path* part of event name is
|
||||
what path was registered for watching, *path* in event argument
|
||||
is what actually have changed (which may be different if watching a
|
||||
directory, i.e. a path with `/` at the end).
|
||||
|
||||
:param subject: Event emitter (the qube object)
|
||||
:param event: Event name (``'domain-qdb-change'``)
|
||||
:param path: changed QubesDB path
|
||||
|
||||
.. event:: backup-get-files (subject, event)
|
||||
|
||||
Collects additional file to be included in a backup.
|
||||
@ -590,7 +601,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
||||
yield block_dev
|
||||
|
||||
@property
|
||||
def qdb(self):
|
||||
def untrusted_qdb(self):
|
||||
'''QubesDB handle for this domain.'''
|
||||
if self._qdb_connection is None:
|
||||
if self.is_running():
|
||||
@ -727,6 +738,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
||||
if self.storage is None:
|
||||
self.storage = qubes.storage.Storage(self)
|
||||
|
||||
if not self.app.vmm.offline_mode and self.is_running():
|
||||
self.start_qdb_watch(self.name)
|
||||
|
||||
@qubes.events.handler('property-set:label')
|
||||
def on_property_set_label(self, event, name, newvalue, oldvalue=None):
|
||||
# pylint: disable=unused-argument
|
||||
@ -1714,53 +1728,53 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
||||
'''
|
||||
# pylint: disable=no-member
|
||||
|
||||
self.qdb.write('/name', self.name)
|
||||
self.qdb.write('/type', self.__class__.__name__)
|
||||
self.qdb.write('/qubes-vm-updateable', str(self.updateable))
|
||||
self.qdb.write('/qubes-vm-persistence',
|
||||
self.untrusted_qdb.write('/name', self.name)
|
||||
self.untrusted_qdb.write('/type', self.__class__.__name__)
|
||||
self.untrusted_qdb.write('/qubes-vm-updateable', str(self.updateable))
|
||||
self.untrusted_qdb.write('/qubes-vm-persistence',
|
||||
'full' if self.updateable else 'rw-only')
|
||||
self.qdb.write('/qubes-debug-mode', str(int(self.debug)))
|
||||
self.untrusted_qdb.write('/qubes-debug-mode', str(int(self.debug)))
|
||||
try:
|
||||
self.qdb.write('/qubes-base-template', self.template.name)
|
||||
self.untrusted_qdb.write('/qubes-base-template', self.template.name)
|
||||
except AttributeError:
|
||||
self.qdb.write('/qubes-base-template', '')
|
||||
self.untrusted_qdb.write('/qubes-base-template', '')
|
||||
|
||||
self.qdb.write('/qubes-random-seed',
|
||||
self.untrusted_qdb.write('/qubes-random-seed',
|
||||
base64.b64encode(qubes.utils.urandom(64)))
|
||||
|
||||
if self.provides_network:
|
||||
# '/qubes-netvm-network' value is only checked for being non empty
|
||||
self.qdb.write('/qubes-netvm-network', self.gateway)
|
||||
self.qdb.write('/qubes-netvm-gateway', self.gateway)
|
||||
self.qdb.write('/qubes-netvm-netmask', self.netmask)
|
||||
self.untrusted_qdb.write('/qubes-netvm-network', self.gateway)
|
||||
self.untrusted_qdb.write('/qubes-netvm-gateway', self.gateway)
|
||||
self.untrusted_qdb.write('/qubes-netvm-netmask', self.netmask)
|
||||
|
||||
for i, addr in zip(('primary', 'secondary'), self.dns):
|
||||
self.qdb.write('/qubes-netvm-{}-dns'.format(i), addr)
|
||||
self.untrusted_qdb.write('/qubes-netvm-{}-dns'.format(i), addr)
|
||||
|
||||
if self.netvm is not None:
|
||||
self.qdb.write('/qubes-ip', self.visible_ip)
|
||||
self.qdb.write('/qubes-netmask', self.visible_netmask)
|
||||
self.qdb.write('/qubes-gateway', self.visible_gateway)
|
||||
self.untrusted_qdb.write('/qubes-ip', self.visible_ip)
|
||||
self.untrusted_qdb.write('/qubes-netmask', self.visible_netmask)
|
||||
self.untrusted_qdb.write('/qubes-gateway', self.visible_gateway)
|
||||
|
||||
for i, addr in zip(('primary', 'secondary'), self.dns):
|
||||
self.qdb.write('/qubes-{}-dns'.format(i), addr)
|
||||
self.untrusted_qdb.write('/qubes-{}-dns'.format(i), addr)
|
||||
|
||||
|
||||
tzname = qubes.utils.get_timezone()
|
||||
if tzname:
|
||||
self.qdb.write('/qubes-timezone', tzname)
|
||||
self.untrusted_qdb.write('/qubes-timezone', tzname)
|
||||
|
||||
for feature, value in self.features.items():
|
||||
if not feature.startswith('service.'):
|
||||
continue
|
||||
service = feature[len('service.'):]
|
||||
# forcefully convert to '0' or '1'
|
||||
self.qdb.write('/qubes-service/{}'.format(service),
|
||||
self.untrusted_qdb.write('/qubes-service/{}'.format(service),
|
||||
str(int(bool(value))))
|
||||
|
||||
self.qdb.write('/qubes-block-devices', '')
|
||||
self.untrusted_qdb.write('/qubes-block-devices', '')
|
||||
|
||||
self.qdb.write('/qubes-usb-devices', '')
|
||||
self.untrusted_qdb.write('/qubes-usb-devices', '')
|
||||
|
||||
# TODO: Currently the whole qmemman is quite Xen-specific, so stay with
|
||||
# xenstore for it until decided otherwise
|
||||
@ -1771,6 +1785,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
||||
|
||||
self.fire_event('domain-qdb-create')
|
||||
|
||||
self.start_qdb_watch(self.name)
|
||||
|
||||
# TODO async; update this in constructor
|
||||
def _update_libvirt_domain(self):
|
||||
'''Re-initialise :py:attr:`libvirt_domain`.'''
|
||||
|
Loading…
Reference in New Issue
Block a user